Blog
All posts (105)
Pages:
< Previous 1–10 11–20 21–30 31–40 41–50 51–60 61–70 71–80 81–90 91–100 101–110 Next >
Ordering:
Ascending Descending
31. Cool URIs don't change
2008-06-11 15:04:31 by Martynas Jusevičius
As the W3C best practice goes, cool URIs don't change. While I was aware of it, I stumbled upon it in a real-life situation and was convinced that stable URIs are not only abstract concept, but can also be an issue in development.
In the DIY Framework resources function as real objects connected to domain objects they stand for, and have explicit URIs. They are also stored in a database where URI becomes, logically enough, a primary key. The URIs are relative to the domain, and are usually made “pretty” from the name (and possibly category, date, etc.) of the domain object, for example, Places/Bars/Retro.
That functions well on resource lookup and creation. Resource that needs to be processed is retrieved by querying the database (with the help of Propel ORM) with current request URI. URI of a new resource is concatenated of all the necessary URL-encoded parts.
However, things become complicated when a resource has a lot of child resources, which have the same URI prefix but different ending, for example, Places/Bars/Retro/Events/ or Places/Bars/Retro/Pictures/123 (and there could be hundreds of those). In the current architecture, the only relationship between these resources is the URI string. Now, if a user recognizes he/she has made a mistake, or for some other reason wants to change the name of the place from Retro to something else, it would be also logical to change the URI as well. But that would also affect all the child resources, and changing one name might involve changing URIs of all of them, which could probably mean hundreds of queries. The same goes for deleting.
I don't really see an easy way out of this situation. One of them would be simply not to allow changing names of objects or whatever that goes into URIs :) Cool URIs don't change, right? Made a mistake — create a new one. But I guess that is not too user-friendly, huh?...
The other would mean refactoring the whole resource design by making the parent-child relationship explicit and making the URIs only relative to the parent. Maybe that could be implemented using the new nested-set support in Propel. It would make it easier to get the parent resource (instead of checking their URIs), which is sometimes useful. On the other hand, it would also make resource retrieval, URI matching and constructing much more complicated compared to the trivial string matching and concatenation which is currently used.
32. Master thesis
2008-06-05 14:19:44 by Martynas Jusevičius
Me and my colleague have finally defended our master thesis titled “New Generation of Social Networks and Underlying Web Frameworks Based on Semantic Web Technologies” at IT University of Copenhagen and got 12, the top score on the danish scale. I have to say that was totally unexpected, but still feels nice :)
33. Simple recommendation system
2008-05-22 20:56:51 by Martynas Jusevičius
I have built a simple recommendation for one of the websites I work with. It is only based on social relationships and does not take into account other parameters. The basic idea is if I like an item, select other items liked by people who also like this item
and it can be used to build lists of similar items as seen on Amazon or YouTube. If we use as an example people who like movies (tables Person, Movie and many-to-many relationship PersonMovie), this can be expressed in SQL using several joins:
SELECT DISTINCT Movie.*, COUNT(Person.ID) AS PersonCount
FROM Movie
JOIN PersonMovie ON Movie.ID = PersonMovie.MovieID
JOIN Person ON PersonMovie.PersonID = Person.ID
JOIN PersonMovie AS PersonMovie1 ON Person.ID = PersonMovie1.PersonID
JOIN Movie AS Movie1 ON PersonMovie1.MovieID = Movie1.ID
WHERE Movie1.ID = 1 AND Movie.ID != Movie1.ID
GROUP BY Movie.ID
ORDER BY PersonCount DESCFirst we join movies to people and then backwards to the specific movie with ID = 1 that we want to get similar movies for. PersonCount is the relevance factor — the higher it is, the more people like the movie.
The algorithm can also be implemented in PHP using Propel ORM, for example, as a method in the Movie class:
public function getSimilarMovies(Criteria $c = null)
{
if ($c === null) $c = new Criteria();
elseif ($c instanceof Criteria) $c = clone $c;
MoviePeer::addSelectColumns($c);
$c->addAlias("PersonMovie1", PersonMoviePeer::TABLE_NAME);
$c->addAlias("Movie1", MoviePeer::TABLE_NAME);
$c->addAsColumn("PersonCount", "COUNT(Person.ID)");
$c->addJoin(MoviePeer::ID, PersonMoviePeer::MovieID);
$c->addJoin(PersonMoviePeer::PERSONID, PersonPeer::ID);
$c->addJoin(PersonPeer::ID, "PersonMovie1.PersonID");
$c->addJoin("PersonMovie1.MovieID", "Movie1.ID");
$c->add("Movie1.ID", $this->getID());
$c->add(MoviePeer::ID, $this->getID(), Criteria::NOT_EQUAL);
$c->addGroupByColumn(MoviePeer::ID);
$c->addDescendingOrderByColumn("PersonCount");
return MoviePeer::doSelect($c);
}34. Free services for Web developers
2008-05-10 11:08:16 by Martynas Jusevičius
I recently noticed, that most services needed to develop, manage, and deploy a website are free, except for hosting perhaps. Version control, project management, bug tracking — all that a developer or a small team might need is online for $0, thanks to some far-sighted providers. Most of the services have completely free accounts with some limitations, others provide free trial. And paying a few backs when you need more space or features does not feel too bad since you have been treated well.
Here is a list I personally use and can recommend (this is not an advertisement on someone's request!):
- Springloops
- Code collaboration for Web developers: SVN hosting, code browser, change reviews, FTP deployment, Basecamp integration
- Basecamp
- Project management, collaboration, and task software: projects, to-dos, milestones, writeboards
- Google Analytics
- Website statistics, visitor tracking
- Feedburner
- RSS statistics and tracking
- Dropbox
- File sharing and synchronization on different platforms
- Tick
- Time tracking
- Yammer
- Internal communication for organizations
- GetSatisfaction
- Customer support
Know more or better? Please let us know :)
35. Calculating great-circle distance in MySQL and Propel
2008-05-01 17:29:49 by Martynas Jusevičius
Eventually the simple distance formula that I have blogged about turned out to be too inaccurate, even for locations within city bounds. I needed to use a formula to calculate great-circle distance which takes into account that the Earth is a sphere. So here is the SQL query:
SELECT *,
@lat1 := (RADIANS(l1.lat)) as lat1, @lng1 := (RADIANS(l1.lng)) AS lng1,
@lat2 := (RADIANS(l2.lat)) AS lat2, @lng2 := (RADIANS(l2.lng)) AS lng2,
ACOS(SIN(@lat1) * SIN(@lat2) + COS(@lat1) * COS(@lat2) * COS(@lng2 - @lng1)) * 6371 AS Distance
FROM Location AS l1 INNER JOIN Location AS l2
WHERE l1.id = 1 AND l1.id != l2.id
ORDER BY Distance
LIMIT 10It uses variables to store temporary calculations. I'm not sure though if the @var syntax is MySQL-specific.
As custom as this query is, it still is possible to build it in an object-oriented fashion using Propel ORM and its Criteria class. It can be used for example to implement a method in a Location class to get other closest locations around it:
class Location extends BaseLocation {
{
// ...
public function getClosestLocations(Criteria $c = null)
{
if ($c === null) $c = new Criteria();
elseif ($c instanceof Criteria) $c = clone $c;
LocationPeer::addSelectColumns($c);
$c->addAlias("Location1", LocationPeer::TABLE_NAME);
$c->addAsColumn("lat", "@lat := RADIANS(Location.lat)");
$c->addAsColumn("lng", "@lng := RADIANS(Location.lng)");
$c->addAsColumn("lat1", "@lat1 := RADIANS(Location1.lat)");
$c->addAsColumn("lng1", "@lng1 := RADIANS(Location1.lng)");
$c->addAsColumn("Distance", "ACOS(SIN(@lat) * SIN(@lat1) + COS(@lat) * COS(@lat1) * COS(@lng1 - @lng)) * 6371");
$c->add(LocationPeer::ID, $this->getID(), Criteria::NOT_EQUAL);
$c->add("Location1.ID", $this->getID());
return LocationPeer::populateObjects(LocationPeer::doSelectStmt($c));
}
}36. Calculating distance in SQL
2008-04-22 21:27:53 by Martynas Jusevičius
There was a need recently to calculate distances between locations that have a latitude and longitude defined and retrieve the ones that are closest. I came up with a little SQL query, which is based on the Pythagorean theorem and simple math. It does not take into account that Earth is a sphere (in that case you would need to use something more complicated like the Haversine formula), but should be accurate enough for relatively short distances in range of tens of kilometers.
SELECT *, SQRT(POW(L2.lat - L1.lat, 2) + POW(L2.lng - L1.lng, 2)) AS Distance
FROM Location AS L1
INNER JOIN Location AS L2
WHERE L1.id = 1 AND L1.id != L2.id
ORDER BY Distance
LIMIT 10The query makes a self join on the Location table, which is assumed to contain lat and lng columns. It retrieves 10 locations closest to location which ID is 1. If you remove that condition and the LIMIT clause, you will get a table of distances between all locations.
You can also put some limit on distance as radius (in degrees), but it does not really translate into (kilo)meters using this formula.
37. kbh.dk
2008-04-18 12:04:15 by Martynas Jusevičius
I started working as a freelancer on kbh.dk. From the English description:
Kbh.dk is a social network for everybody that live in or care about Copenhagen.
We will launch a Public Beta version in May 2008. In the first phase the web site will be mainly in Danish but we’re working hard to get full internationalization as soon as possible.
The project is driven by the wish to create a socially connected metropolis. Kbh.dk will be the place where the people of Copenhagen inspire each other to use the city - together. The project is financed by the Municipality of Copenhagen.
Kbh.dk helps you to
- Find people that share your interests and connect through groups
- Promote your interests with video, photos and blogs
- Network with your friends - and use the city with them
- Invite others to the events you are organizing or following
I am responsible for the events part. Users will be able to create them, but there will also be events imported from a third-party aggregation webservice.
It seems like it is going to be fun, I'm excited :)
38. Recursion in XSLT
2008-04-09 23:52:48 by Martynas Jusevičius
XSLT, which is great for web templating, is a functional language and has no loops or mutable variables. These constructs have to be replaced with recursion and parameters. For example, if you would like to loop until a certain number and print the counter, you would have to use recursion.
Recursion in XSLT is implemented using <xsl:apply-templates> or <xsl:call-template> together with <xsl:with-param> and <xsl:param>.
The general logic is to call or apply a template with some initial parameter value, get the necessary work done inside of the template, and let it call itself with a modified parameter. Considering the loop example, it would mean calling the template with 0 as initial counter parameter, printing it out in the template, and calling the template recursively with counter increased by one with the condition that it still is smaller than the target number.
As a complete example we provide a template for converting line breaks in a string into HTML <br/> elements:
<xsl:template name="process-line-breaks">
<xsl:param name="text"/>
<xsl:variable name="break" select="' '"/> <!-- entity for line break character, might differ according to platform -->
<xsl:choose>
<xsl:when test="contains($text, $break)">
<xsl:value-of select="substring-before($text, $break)"/>
<br/>
<xsl:call-template name="process-line-breaks">
<xsl:with-param name="text" select="substring-after($text, $break)"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>The template takes a text string as a parameter and tries to locate a line break in it. If that suceeds, the string part before the break is the output, followed by a <br/>, and the template is applied recursively on the string part after the break. If there are no more breaks in the string parameter, the remaining string becomes the output and the template terminates. In each step the template is left with a smaller and smaller remainder of the original string, while the preceding part becomes template output with line breaks replaced.
The template can be applied on a text content like this:
<xsl:template match="text()" mode="process-line-breaks">
<xsl:call-template name="process-line-breaks">
<xsl:with-param name="text" select="."/>
</xsl:call-template>
</xsl:template>39. Semantic social networks
2008-04-02 14:05:45 by Martynas Jusevičius
Lately there has been a growing dissatisfaction in the user communities of social networks and other user-based websites which were built with an intention to lock and control the data that users provide. Users complain about not having control over profile data and social relationships they have established and not being able to export them in machine readable formats, which makes reuse and integration of this data virtually impossible. This resulted in several initiatives:
- Open Social Web
- Demands ownership, control, and freedom of personal information on the social web
- Social Network Portability
- Demands ability to import profile information and social network (based on Microformats)
- DataPortability
- Encourages standards-based data portability in general
- OpenID
- Promotes single digital identity
- OpenSocial
- Defines a common API for social applications across multiple websites
The main problem seems to be data portability in general, and we have already argued that it is a sweet spot for semantic technologies. However, there are several Semantic Web projects directly addressing issues of social networks:
- Friend of a Friend (FOAF)
- Aimed at creating a Web of machine-readable pages describing people, the links between them and the things they create and do
- Semantically Interlinked Online Communities (SIOC)
- Provides methods for interconnecting discussion methods such as blogs, forums and mailing lists to each other
In combination with Linked Data, FOAF would allow import of friend profiles from one website to another, and SIOC would enable structured queries over distributed user-generated content. Social networks could reuse semantic data from sites like DBpedia and be valuable sources of such data themselves. These were the main ideas in Tim Berners-Lee Giant Global Graph vision and Brad Fitzpatrick's thoughts on Social Graph.
Leigh Dodds wrote about this back in 2004, but things have advanced slowly. There exist SIOC plugins for several blog engines, and LiveJournal is publishing FOAF data, but portability still has to gain momentum, most likely by support from major sites.
40. PHP 5 features: Exceptions
2008-03-25 10:18:59 by Martynas Jusevičius
A useful new feature in PHP 5 is exception handling via the try/throw/catch paradigm.
An exception may be thrown and caught. If an exception is thrown in code surrounded by try, the following statements will not be executed, and the exception will be handled by the first matching catch block. Each try has to have at least one corresponding catch, and multiple catch blocks can be defined to handle different types of exceptions. Normal execution continues after the last catch block.
The built-in Exception class can be extended and a user-defined exception class can be implemented.
Exceptions is a powerful construct which can be used to improve code but also easily abused. PHP5 Exception Use Guidelines defines one basic rule of thumb:
Exceptions should never be used as normal program flow. If removing all exception handling logic (try-catch statements) from the program, the remaining code should represent the "One True Path" -- the flow that would be executed in the absence of errors.
This requirement is equivalent to requiring that exceptions be thrown only on error conditions, and never in normal program states.
When developing with the DIY Framework, we are mostly using exceptions to indicate form validation or non-permitted access errors. Here is an example from a SettingsResource class which handles a page of user settings, which should be accessible only when the user is logged on:
public function doPost(Request $request, Response $response)
{
$view = null;
$parent = parent::doPost($request, $response);
if ($parent != null) $view = $parent;
else
{
try
{
if ($request->getSession()->getAttribute("user") instanceof GuestUser) throw new NoPermissionException();
$view = new SettingsGeneralView($this);
// ... some other code to save submitted settings
}
catch (NoPermissionException $e)
{
$response->sendRedirect(FrontEndMapping::getHost().LoginResource::getInstance()->getURI());
}
}
return $view;
}If the user in session is a guest user (not logged on), an exception is thrown, and a redirect to the login page follows.
Pages:
< Previous 1–10 11–20 21–30 31–40 41–50 51–60 61–70 71–80 81–90 91–100 101–110 Next >
Ordering:
Ascending Descending
