A Finder Smarter Than Propel Getters
The sfPropelFinder symfony plugin keeps getting better. The addition of two new methods, relatedTo() and findLast() make things even easier than before. Let's see it with an example.
Propel is smart enough to generate getter methods for related objects when you define a foreign key in your schema. For instance, if your schema relates Comment to Article as follows:
propel:
article:
title: varchar(255)
body: longvarchar
created_at: ~
comment:
article_id: ~
author: varchar(100)
body: longvarchar
created_at: ~
Then after you build your model, the BaseArticle class will provide a getComments() method that will facilitate the retrieval of the comments related to an existing article:
$comments = $article->getComments();
But now, what if you need to get the list of comments ordered from the last posted to the first posted? The generated getter method accepts a Criteria as its first parameter, so you can write:
$c = new Criteria();
$c->addDescendingOrderByColumn(CommentPeer::CREATED_AT);
$comments = $article->getComments($c);
If you want the latest comment posted on an article, you will need to make something slightly more complicated. For the sake of the example, the code appears on the same fashion as the previous ones, but you should definitely wrap it up in a method stored in the Article class.
$c = new Criteria();
$c->addDescendingOrderByColumn(CommentPeer::CREATED_AT);
$comments = $article->getComments($c);
if(isset($comments[0])
$comment = $comments[0];
}
else
{
$comment = null;
}
// Alternative way, but not really shorter
$c = new Criteria();
$c->add(CommentPeer::ARTICLE_ID, $article->getId());
$c->addDescendingOrderByColumn(CommentPeer::CREATED_AT);
$comment = CommentPeer::doSelectOne($c);
The sfPropelFinder provides an alternative, and I believe better way of doing this. The finder object can filter results related to a given object thanks to its new relatedTo() method. This method uses the schema to guess the local and foreign columns, so you don't have to pass any other argument than the related object:
$comments = sfPropelFinder::from('Comment')->
relatedTo($article)->
find();
How is that better than $article->getComments()? It is not. But for the following examples, the interest of the relatedTo() method appears more clearly:
$comments = sfPropelFinder::from('Comment')->
relatedTo($article)->
orderByCreatedAt()->
find();
$comments = sfPropelFinder::from('Comment')->
relatedTo($article)->
findLast();
findLast() is also a recent addition to the plugin. It returns a single record, the last one, based on the creation date (or on the id if there is no creation date column).
The relatedTo() method offers the same convenient way to retrieve related objects by a single method call as Propel generated getters. But since it is embedded in the sfPropelFinder class, it allows for further manipulation of the results with a very simple API.
So if you didn't give it a try yet, checkout the sfPropelFinderPlugin from the symfony Subversion repository. You will soon see how it dramatically reduces the amount of code you write in the model.
Possibly related posts (automatically generated):
Thank you for good article. You've inspired me to use this plugin. But could you use other color for variables (not blue)? It is very difficult to read this on the dark background.
@windock: I tried to make it brighter. Only it is still not very nice... I wish there were an existing Geshi style for PHP in a dark backgroud.
The lighter blue is better, even if not perfect. I can read w/o squinting now.
If it's not too hard to pull off, it would be nice to have an optional integer parameter for the findLast() method for getting more than just a single item. I can think of lots of cases where findLast($count) would be handy.
@forkmantis:
findLast()already accepts a parameter, but it's a column name - to help the finder determine what you mean by "last" if it's not obvious.As it is,
findLast()answers a common use case, but if you can do too many things with it, I'm afraid the API will lose its "focus". Or, to make things clearer, start looking too much like the Doctrine API, where there are so many ways to do one thing that sometimes you're lost...So I'm not sure that adding a second parameter to
findLast()to allow the retrieval of more than one column is a good thing to do. And you can already do it by chainingorderBy()andfind(n).I prefer KISS to TIMTOWTDI.
@Francois Ah, OK. I guess if I'd already been using the plugin, I'd probably already know that. For what it's worth, I'll probably end up installing it as soon as can free up some time in my current project.
Hi, looks great everything about this plugin. One question: ¿ Is this compatible with propel 1.3?
Hi François,
this is an amazing contribution! I'm loving it. I do have one issue though. In my database, I name tables plural, and use the phpName attribute to get singular entities, such as: tablename customers, propel class Customer. Now, this construct fails when I use the finder to perform a join, since, in line 838 you're comparing the related table name to the phpName, and they're not equal in my case, even though they are formally connected. I'm not very familiar with the inner workings of the mapping classes, but I would assume the getRelation function should get a hold of the related table, and use the phpName attribute..
I'll play some more with it, but if you know how to quick-fix it, that would make my day
All the best, Daniel
@Neonardo: Not yet, but as soon as it hits a stable state, I have plans to port it to Propel 1.3. Should not be too difficult though, I cant think of only one method (
doFind()) as a possible compatibility problem.@Daniel: Yes, I guess I went fast when considerating name transformations, to deal primarily with the standard case.
If you manage to build up a patch, I'll apply it to the trunk. Could you open a ticket on this issue?
Hi François ! Very good class, fun and usable.
good job !
Hey François,
here is the ticket, http://trac.symfony-project.com/ticket/3883 including attached patch, which probably should be reviewed briefly.. Not an expert on propel mapping classes, hehe.. I hope this helps.
Thanks again, I'm loving this plugin (and all your other stuff anyways).
Daniel
@Daniel: By the way, this ticket was fixed a day after it's been reported