feat: paginate without counting the collection - #60
Merged
Merged
Conversation
`Page` now fetches one item more than fits and uses its presence for `hasMorePages()`, so a previous/next pager costs a single query. `totalCount()`, `lastPage()`, `pageCount()` and `strict()` still count, but only when called. `nextPage()` now returns `null` for a page past the end rather than the next page number.
Pagination is now documented by what your template asks for rather than as one flat API: `Simple` (previous/next, no counting) and `Full` (totals and page numbers), with strict mode under `Full` where the total is already known. The cost is described as counting the collection rather than as queries, since what that costs is up to the source - free for an array, a `COUNT` for Doctrine, a full iteration for a generator.
Strict mode counted the collection on every request to validate the page number. It now fetches the requested page first and only counts if that page came back empty - so a valid page costs nothing extra and the fallback is paid for only when it's actually needed.
Renders a previous/next or numbered pager for any `Page`, linking to the current route and keeping its query parameters. Twig is now a dev dependency so the templates are rendered in tests.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Refs #51.
Pageneeded the total to answer even basic questions -nextPage()went throughlastPage()→totalCount()→count(), so a previous/next pager paid for aCOUNTover the whole table. It now fetches one item more than fits on the page and uses that extra item's presence to answer the newhasMorePages(), discarding it before you see the items.nextPage(),previousPage()andhaveToPaginate()are all derived from it.totalCount(),lastPage()andpageCount()are unchanged - they still count, but only when called. So a previous/next pager counts nothing, and a numbered one counts once.Strict mode got the same treatment: it used to count on every request to validate the page number, and now fetches the requested page first and only counts if that page came back empty. A valid page costs nothing extra; the fallback is paid for only when the requested page really is out of range.
For #51 specifically: @jungleman12's exact usage (
paginator.nextPageandpaginator|lengthover 2M rows) now runs noCOUNTat all, and @seb-jean's diagnosis in that thread was exactly right - thenextPage()→lastPage()→totalCount()chain is what's gone. What this does not fix is theOFFSETcost you already called out there: deep pages still make the database walk and discard rows. That's still #52's territory, so I left this as a reference rather than a fix.Also adds
Pager/_simple.html.twigandPager/_full.html.twigfor rendering either mode against a plainPage, and Twig becomes a dev dependency so they're actually rendered in tests.Behavior change:
nextPage()returnsnullfor a page past the end where it previously returned the next page number (paginate(page: 999)on an 8-page collection used to report1000). Two existing assertions covered that and were updated.