sfPropelPager and pagination
by rp
I am writing this post just after finishing 30mins of really annoying problems with sfPropelPager and implementing a pager partial, which can be re-used though out the project. The problem that I was facing was that there is no way (or at-least I don’t know off) to ensure that the current params from the browser are also passed as the user paginates from page to page.
So here I was dealing with two situations:
-
User posts in a search box (GET request) and the parameters are passed like
search/index?q=foo&p=10
Now as the results appears on the page I want the following links for my pager:
search/index?page=1&q=foo&p=10 search/index?page=2&q=foo&p=10
and so on.
-
Where I have a neat url coming from symfony already. Like.
website/show/flickr.html
In this case I want urls to appear as
website/show/flickr/1 website/show/flickr/2
and so on.
I dont claim this is the best way to do, if you think there is one, please leave a comment, however for others who have spent some time trying to figure this out, this might get them up and running quickly and then then an improvise this further. So here’s my solution.
Code form the Partial (_pager.php)
<?php
$query_string = '';
$pager_param_name = 'page';
// use this value in case you are getting your parameters from a get request
// e.g. search where you are showing results as part of submitting a get form
$query_params = $sf_request->getGetParameters();
// use this when you already have a proper fancy url in place
// and you want to get the internal rep of the query params.
// this doesnt return any query params if the intial request is sent thru
// the query string (e.g. in the above case this would return nothing after ? in the url)
$uri = sfContext::getInstance()->getRouting()->getCurrentInternalUri();
$args = explode('?', $uri);
if (isset($args[1])) {
$params = $args[1];
} else {
$params = $sf_request->getGetParameters();
$counter = 0;
$len = count($params);
$tmp = '';
foreach($params as $key => $value) {
$tmp .= $key . '=' . $value;
$counter +=1;
if ($counter < $len) {
$tmp .= '&';
}
}
$params = $tmp;
}
// fancy reg to make sure we dont reset the value for the pager param again
$query_string = eregi_replace('(&){0,1}' . $pager_param_name . '=[0-9]{1,}(&){0,1}', '', $params);
if ('' != $query_string) {
$query_string = '&' . $query_string;
}
?>
<div class="mod pager">
<div class="bd">
<?php if ($pager->haveToPaginate()): ?>
<ol>
<li><?php echo link_to('«', $route .'?' . $pager_param_name. '='.$pager->getFirstPage() . $query_string) ?></li>
<li><?php echo link_to('<', $route . '?' . $pager_param_name. '='.$pager->getPreviousPage() . $query_string) ?></li>
<?php $links = $pager->getLinks(); foreach ($links as $pagecount): ?>
<li>
<?php echo ($pagecount == $pager->getPage()) ?
'<span class="current">' . $pagecount . '</span>' :
link_to($pagecount, $route . '?' . $pager_param_name. '='.$pagecount . $query_string)
?>
</li>
<?php endforeach ?>
<li><?php echo link_to('>', $route . '?' . $pager_param_name. '='.$pager->getNextPage(). $query_string) ?></li>
<li><?php echo link_to('»', $route . '?' . $pager_param_name. '='.$pager->getLastPage() . $query_string) ?></li>
</ol>
<?php endif ?>
</div>
</div>
Code Inside the action class
public function executeShow(sfWebRequest $request)
{
$pager = UsernamePeer::getListByWebsitePager($website, $count);
$pager->setPage($this->getRequestParameter('page',1));
$pager->init();
$this->pager = $pager;
}
And then finally the template that calls the partial
<?php include_partial('global/username_list', array('pager' => $pager, 'route' => 'website/show'))?>
Interesting effort, but I would never let this partial into any project I was working on. There is too much code in the partial. Idiomatic Symfony code does not have code blocks in the templates or partials. Having overseen several Symfony projects, I’ve learned that when I see this kind of thing, it means the programmer is taking short cuts, not just with pagination, but with everything.
Possibly you may need to do a custom pager class, and store it in /lib, but it is inappropriate to have this much code in a partial.
Ouch! that was a bit harsh! but you have a valid point. I am sure i am not the only developer who would agree with this, but essentially as a developer you would re-visit your code form months/years back and be ashamed of it but IMHO is if there was learning from it? and can you spot the difference between how you would write the same code much better now. If yes then its all good.
I am glad you took a moment to leave your thoughts and I value it. This code is indeed really bad, but this again wasn’t something to be used in production but just to see how it can be used. If you know what you are doing, you can handle that code properly. This code obviously is not production ready and I assumed that for the readers.
nice example, thank you!
maybe you should have used component instead of partial to separate code from view