Update 29/4: Thank you Bert Oost of OostDesign for the tip on supporting different getPage configurations!

If you've ever created custom components, chances are that you have had to create accompanying snippets to actually show the data on the front-end. That can be a lot of work, and once data starts piling up you may want to add some pagination to it. This brief article will explain the requirements that getPage puts on your coding to properly paginate custom objects.

Getting the number of Results

First of all, we will need to tell getPage what the total number of results matching any filters set in place by our snippet is. In our snippet, we can easily set up the xPDOQuery object the way we normally would and take that object to get the total amount of results. No - we don't use getCollection and count() the array - that would be an insane waste of processing time. Instead we use the little jewel called modX::getCount, which is actually extended from xPDO::getCount. This will, without retrieving all the data from the database, get the total number of results for your query. Let's show an example of how we could use this - in this example we look up the amount of resources that have a class of modWeblink or modSymLink and are published:

$c = $modx->newQuery('modResource');
$c->where(array(
    'published' => 1,
    'class_key:IN' => array('modWeblink','modSymLink'), 
));

$total = $modx->getCount('modResource',$c);
echo "Found {$total} resources of class modWeblink or modSymLink that are published.";
$collection = $modx->getCollection('modResource',$c);

So that will give us a number in the $total var - but that doesn't tell getPage anything - yet! Having looked at the getPage documentation trying to figure out this part, I've overlooked the answer many times and it is surprisingly simple. The documentation tells us there's a totalVar property:

totalVar | The key of a placeholder that must contain the total number of records in the limitable collection being paged. | total

And that's our answer. We need to set a placeholder, of which the key matches the value of the totalVar property: by default that's "total", though users have the ability to change this in their getPage call, so we'll need to get the totalVar property and set a placeholder with that name. Let's update our example accordingly to set the placeholder and that's all there is to it.

$c = $modx->newQuery('modResource');
$c->where(array(
    'published' => 1,
    'class_key:IN' => array('modWeblink','modSymLink'), 
));

$total = $modx->getCount('modResource',$c);
$totalVar = $modx->getOption('totalVar', $scriptProperties, 'total');
$modx->setPlaceholder($totalVar,$total);
echo "Found {$total} resources of class modWeblink or modSymLink that are published.";
$collection = $modx->getCollection('modResource',$c);

Starting at the right offset

Secondly we will need to make sure our snippet understand which results to display - and that it passes this on to our xPDOQuery object. In this case all we need to do is lookout for two properties: limit and offset. Limit will be set by getPage to the &limit property, which indicates how many items to show at once. The offset will be calculated based on the "page" request and the limit per page.

If we would update our example, we could end up having this - which works perfect with getPage for pagination.

$c = $modx->newQuery('modResource');
$c->where(array(
    'published' => 1,
    'class_key:IN' => array('modWeblink','modSymLink'), 
));

$total = $modx->getCount('modResource',$c);
$totalVar = $modx->getOption('totalVar', $scriptProperties, 'total');
$modx->setPlaceholder($totalVar,$total);

$limit = $modx->getOption('limit',$scriptProperties,10);
$offset = $modx->getOption('offset',$scriptProperties,0);
$c->limit($limit,$offset);

$collection = $modx->getCollection('modResource',$c);

You may notice a few important things:

  • We get the total amount of results, and set the total placeholder *before* we limit the query. While the docs say it may ignore the limit property, I like to be sure.
  • We use modX::getOption() (extended from xPDO::getOption) to get the "limit" and "offset" properties from our scriptProperties array available in snippets. If it does not exist we assign default values of 10 and 0 respectively.
  • We use xPDOQuery::limit() to set the limit and offset to the query. We then pass that on to our getCollection call and be on our way to process it further and give the site visitor what they want.

Hopefully this brief article will help more developers making their snippets getPage-proof, which will make it easier for front-end developers to use one tool (getPage) for all their pagination needs. Please feel free to share your thoughts and suggestions below!

Thank you Mark!
With this info i get my snippet works with getPage in a minute.
Only one think is, i use $xpdo instead of $modx, because i use extended class.

Well, most of the methods in there are actually inherited from xPDO, so you could just change $modx to $xpdo in the snippet code. I think $modx->setPlaceholder may be the sole exception there.

In any snippet you will have access to the $modx variable, so if you're using something class based, you can pass the $modx object to it in the constructor and internallly set it (as reference) to $this->modx, and use that in the snippet instead.

Glad this info helped you get getPage running with your own snippet :)

Hi Mark,

You seem to becoming a very active member of the Modx community - very helpful for others well done. Quick question I have a custom-table / custom-search function which will obviously return varying results dependant on the search terms which I want to paginate. Do you have any recommendations about doing this ? I am guessing you need to make the db_query persistent and then pass back more variables to 'limit' the results - but I am a little unsure how to make the query persistent? Any pointers appreciated!

Sorry for the long time it took to get your comment approved - didn't get the message.

I think the problem is you'll have to make sure the pagination links take into account any custom URL params used for the search. I don't think getPage has a custom url writing feature yet (it's been requested), so it'll probably be hack-ish until that time..

Dont really understand the totalVar=`total` parameter that's supposed to be a placeholder.

Does it mean on the page I place a [[+total]]

Got the pagination to work but when I navigate to another page the navigation is not displayed.

(Another wierd thing is that the offset does not offset)

Hey, I have a list in a MIGX, how to I use page-navigation with it? I used your parseMIGXToGallery as snipped. I get al the results on the page, but after a while it's a whole list and I prefer something like 5 results per page. Thanks for help.

Comments are closed :(

While I would prefer to keep comments open indefinitely, the amount of spam that old articles attract is becoming a strain to keep up with and I can't always answer questions about ancient blog postings. If you have valuable feedback or important questions, please feel free to get in touch.