Eating My Own Dog Food
I spent the last three hours porting my sfSimpleBlog plugin to sfPropelFinder. While it was the occasion to polish the sfPropelFinder API and fix a bug, it was also a great pleasure to replace Propel Peer/Criteria code with finder one.
The blog plugin is up and running, the code is now much cleaner, and as a bonus, the query count has been reduced. If you want to test it, checkout the latest trunk version of the plugin (the plugin release system of the symfony project website doesn't seem to appreciate my PEAR package).
I find sfPropelFinder code to be naturally flowing. It cuts model classes sizes by 50%, it is much more readable, and makes a few custom model methods useless.
See for yourself. Here is the old PluginsfSimpleBlogPostPeer class, holding the methods required to retrieve blog posts, in the previous version:
{
public static function getRecentPager($max, $page)
{
$pager = new sfPropelPager('sfSimpleBlogPost', $max);
$c = new Criteria();
$c->add(self::IS_PUBLISHED, true);
$c->addDescendingOrderByColumn(self::CREATED_AT);
$pager->setCriteria($c);
$pager->setPage($page);
$pager->setPeerMethod('doSelectJoinAll');
$pager->init();
return $pager;
}
public static function getRecent($max = 10)
{
$c = new Criteria();
$c->add(self::IS_PUBLISHED, true);
$c->addDescendingOrderByColumn(self::CREATED_AT);
$c->setLimit($max);
return self::doSelectJoinAll($c);
}
public static function getTaggedPager($tag, $max, $page)
{
$pager = new sfPropelPager('sfSimpleBlogPost', $max);
$c = new Criteria();
$c->addJoin(sfSimpleBlogTagPeer::SF_BLOG_POST_ID, self::ID);
$c->add(sfSimpleBlogTagPeer::TAG, $tag);
$c->add(self::IS_PUBLISHED, true);
$c->addDescendingOrderByColumn(self::CREATED_AT);
$pager->setCriteria($c);
$pager->setPage($page);
$pager->setPeerMethod('doSelectJoinAll');
$pager->init();
return $pager;
}
public static function getTagged($tag, $max)
{
$c = new Criteria();
$c->addJoin(sfSimpleBlogTagPeer::SF_BLOG_POST_ID, self::ID);
$c->add(sfSimpleBlogTagPeer::TAG, $tag);
$c->add(self::IS_PUBLISHED, true);
$c->addDescendingOrderByColumn(self::CREATED_AT);
$c->setLimit($max);
return sfSimpleBlogPostPeer::doSelectJoinAll($c);
}
public static function retrieveByStrippedTitleAndDate($text, $date, $con = null)
{
if ($con === null)
{
$con = Propel::getConnection(self::DATABASE_NAME);
}
$criteria = new Criteria(sfSimpleBlogPostPeer::DATABASE_NAME);
$criteria->add(sfSimpleBlogPostPeer::STRIPPED_TITLE, $text);
if (sfConfig::get('app_sfSimpleBlog_use_date_in_url', false))
{
$criteria->add(sfSimpleBlogPostPeer::PUBLISHED_AT, $date);
}
$v = sfSimpleBlogPostPeer::doSelect($criteria, $con);
return !empty($v)> 0 ? $v[0] : null;
}
}
And here is the revised version. It is no longer a Peer class, but a Finder class extending DbFinder:
{
protected $class = 'sfSimpleBlogPost';
public function recent()
{
return $this->
with(sfConfig::get('app_sfSimpleBlog_user_class', 'sfGuardUser'))->
where('IsPublished', true)->
orderBy('CreatedAt', 'desc');
}
public function tagged($tag)
{
return $this->
join('sfSimpleBlogTag')->
where('sfSimpleBlogTag.Tag', $tag);
}
public function withNbComments()
{
return $this->
leftJoin('sfSimpleBlogComment c')->
withColumn('COUNT(sf_blog_comment.id)', 'NbComments')->
where('c.IsModerated', false)->
groupBy('c.SfBlogPostId');
}
public function findByStrippedTitleAndDate($text, $date)
{
$this->where('StrippedTitle', $text);
if (sfConfig::get('app_sfSimpleBlog_use_date_in_url', false))
{
$this->where('PublishedAt', $date);
}
return $this->findOne();
}
}
For those who followed my previous posts, you probably understand that the only thing that prevents sfSimpleBlog from working with Doctrine is the advance in the implementation of sfDoctrineFinder class. Once that is finished - and I'm progressing quite fast - sfSimpleBlogPlugin will be the first true ORM agnostic plugin.
Possibly related posts (automatically generated):
You're doing great things François. I hope the whole community is as happy as I am to see you working at such an important thing that ORM-Agnostication is.
In my opinion you're doing one of the most important part of what should be done for the sf1.2 milestone. Providing ORM layers as plugins instead of a part of the symfony core is nothing if there is no ORM abstraction layer designed for plugins (even for any projet, it's a very pleasant feature, the idea that migrating from propel to doctrine would be painless is just amazing).
Thanks a lot François, et bonne continuation ; merci pour toutes ces belles choses auxquelles tu travailles.
Thanks as usually for this great news. On the other hand I was about to set up(upload to the server) my blog system and you stop me from publishing it! XP. Now I'll implement my blog with this new system, I'll be using propel 1.3.
Thanks Again for this awesome develop.
Long Live DbFinder!!
Hi François, how a great job here!! ;). I 'd really like to know if there i any plan to include this plugin as a part of the symfony core.
Great Job! Love SFpropelFinder
This is good news. I can't wait to dive into plug-ins
Is it possible with this plug-in to do something along the lines of: select X from table AS a, table AS b ...
I looked and could not seem to see it, but it might be because I am blind
John: It is possible in sfPropelFinder with a trick (calling the
addAlias()Criteria methods on the finder), but Doctrine doesn't allow it at all if the two tables do not share a relation. So basically I can't implement it in an ORM-agnostic way...