While my earlier example illustrated the concept of automating pagination with Smarty in your Movable Type archives, I had a couple of problems with it.
First and foremost was that if someone “hacked” the URL, to submit their own value for a start range, then the links wouldn’t work right. They might start on entry number 2 instead of entry 1, and while the previous link would say 1-1 (itself a bit ugly), it would actually render entries starting with 1, but the page would biuld through the default limit or the end of the category, whichever came first.
So I played with adding a user-specified limit (with a maximum size). But it had the same problem, in that if someone specified a limit that wasn’t that of the default, it could create strange results with the links and what you would get when you clicked them. Back to the drawing board.
I figured the only way to approach this was to go with true pagination – that is, allow the user to select a page, not an entry. That page would always contain the same entries (the last page, of course, may vary, and new pages may be added). But they would not be able to start from somewhere in the middle of the page – that just wouldn’t work.
Step one is to find the total number of entries. As with my earlier example, I’m using the category count to find the total. You could use other formulas, depending on where you are using the pagination. The point is just to get a count in the right variable.
{{* total number of entries *}} {{capture assign="count"}}<$MTCategoryCount>{{/capture}}
Step two is to set a number of entries per page. I set the limit variable to the number of entries I’d like per page.
{{* entries per page *}} {{assign var="limit" value="12"}}
Step three is to take that number and figure out how many pages you will have by using that number. By using the PHP function @ceil, I don’t have to worry about fractions.
{{* number of pages *}} {{assign var="pages" value=$count/$limit|@ceil}}
Finally, you’ll need to take those values and set your MTEntries container appropriately. I used something like this:
{{assign var="lastn" value=$limit}} {{math assign="offset" equation="($p-1)*$limit"}} <MTEntries lastn="`$lastn`" offset="`$offset`">
This section takes the variable called limit that we set earlier and populates the lastn attribute of the MTEntries container. Similarly, the value of offset is used to set, appropriately enough, the offset attribute in the same container. The calculation for setting the offset variable simply takes the page number, subtracts 1 and multiplies by the value of limit. In other words, it accounts for the entries on the current page.
In the end, the calculations were a bit more complex, but I like the results a lot better. Check out the archives (you’ll need to browse individual categories to see the change) and let me know what you think.
As always, if you’re interested in something like this but are having trouble doing it yourself, let me know and we can talk rates. This is what I do, and I am available for consulting work regardless of whether you are an individual, a small company or a multinational conglomerate.
Update: Thanks once again to Brad for even more help. Couldn’t do it without him.
Comments
13 responses to “True Smarty Pagination”
Generally speaking, perhaps. But it’s going to need some work to know what else is going on there to cause the error.
You mention negative results, it’s likely that you could be running into a problem going back further than you need to, and so you need to cut the number (12 in this case) to something smaller than that so you don’t ask for more records than exist, or something along those lines – the use of the PHP max function might be useful here.
Hello, I’m trying to implement your true pagination solution, and I keep turning up this error:
[You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ‘-12’ at line 17]
After poking around, it seems that it is probably the result of a change around MySQL v4.1 that started flagging negative results where previously there were ignored incorrectly. I’m running MySQL 5.0.18.
Can you advise on how to get your code working with this version of MySQL?
Thanks and best wishes,
Frank
Hi Justin –
If you think you have it working, but aren’t getting anything, I’d suggest you do two things.
First, look at the source of your page. Is it showing anything that shouldn’t be there (for instance, the MTEntries container tags or the Smarty code)? If so, it means it isn’t processing correctly.
Second, add some output to your page so that you can see what you are getting in the variables. To use a Smarty variable, just enclose it in double braces: {{$variable}}.
I generally use HTML comments, so any output doesn’t show up on the rendered page. But that’s purely preference.
Hello–I’d really like to use this and I have one simple question. I believe I have the code correct, but NO ENTRIES are showing up. The page renders exactly as coded otherwise, except no entries show up. It’s slightly confusing. Any free help is appreciated…
I assume you’re trying to make sure that the value of the passed variable isn’t empty, is greater than zero and is less than the total number of pages, yes?
If so, you may want to consider something like this:
If it’s greater than 1, assign the smaller of the total number of pages ($pages) or x (in this case, $smarty.request.p). Otherwise, assign “1”. This code is somewhat easier to read, without having to pile on test after test.
The other reason for this code is because your code doesn’t address the issue of someone requesting the last page. If you have 10 pages of data, and someone sends p=10, your code won’t handle it and they will get the first page. You could make your test less than or equal to the total number of pages, or less than the total number of pages plus one, to accomplish the same thing.
One other difference is that if someone requests a page higher than the last (say 12 out of 10), I’ll send them to the last page, where your code will send them to the first page. This is really personal preference.
Actually, for added security you should really do this:
{{assign var=”pg” value=1}}
{{if ($smarty.get.p != null) && ($smarty.get.p > 0) && ($smarty.get.p < $pages)}}
{{assign var=”p” value=$smarty.get.p}}
{{/if}}
Here’s what I added to the code above to get previous and next links. After the assign var=”pages” line add:
{{assign var=”p” value=1}}
{{if $smarty.request.p != null}}
{{assign var=”p” value=$smarty.request.p}}
{{/if}}
Where you want your links add:
{{if $p > 1}}
<a href=”?p={{$p-1}}”>previous</a>
{{/if}}
{{if $p < $pages}}
<a href=”?p={{$p+1}}”>next</a>
{{/if}}
I hope this helps.
Hi Ken (or New, or Mex, or…) – While getting actual page numbers isn’t a trade secret or anything, it does take a few more steps. It’s possible that I’ll write those up at some point in the future, but I haven’t yet.
In the meantime, yes, this is exactly the sort of consulting that I do, and I would be happy to help get you going with true pagination if you’re in need. No sense getting ever more frustrated when a few pointers, and perhaps some help with the code, can get you headed in the right direction.
Let me know if there’s any way that I can help!
Thanks for what was for me a breakthrough on pagination. I’m stuck, though. I understand how the above works and can get the pages to break accordingly, but I can’t translate it into page numbers, links, etc., such as you have on your archives. Is that the part you want us to contact you to pay for? I’m getting frustrated!
You would have to do more work, as the Smarty pagination in this plugin is based on directly querying the database. By using this method (the one I step through), you don’t have to do that – just let MT do the work.