Skip to content

Add Pagination classes to ODM#2992

Draft
alcaeus wants to merge 1 commit intodoctrine:2.17.xfrom
alcaeus:pagination
Draft

Add Pagination classes to ODM#2992
alcaeus wants to merge 1 commit intodoctrine:2.17.xfrom
alcaeus:pagination

Conversation

@alcaeus
Copy link
Member

@alcaeus alcaeus commented Mar 4, 2026

Q A
Type feature
BC Break no
Fixed issues

Summary

This PR introduces four new paginator classes. We introduce two styles of paginator: offset-based and cursor-based. Both paginator styles come in flavours for aggregation pipeline and queries.

Offset-based pagination

Offset-based paginators take a page number and number of items per page (defaults to 24), along with a builder for aggregation or queries. It is Countable and returns the number of pages when counted. It provides an iterator for the current page. To accomplish this, it uses the count operation for queries, and $numDocuments for aggregation pipeline. When fetching items, it inserts the correct skip and limit options/stages.

Cursor-based pagination

Cursor-based pagination takes an ID where to continue paginating, the number of items per page, and an optional field name for the cursoring field (an ObjectId value in the _id field is usually a good choice). Together with the builder for aggregation or queries you can iterate over the items of the current "page". Iterating the paginator multiple times always returns the same items. You are responsible for extracting the ID of the last item, which is used when creating the paginator for the next page.

Todo
  • Write documentation
  • Add API to get next page from a cursor-based paginator?

Copy link
Member

@GromNaN GromNaN left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You decided not using the $facet stage, as done in Api Platform.

{
$builder = clone $this->aggregation;
$results = $builder
->hydrate(null)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the difference between hydrate(null) and hydrate(false) used in the query paginators?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The type, probably nothing else. We should eventually have a string|false type to control this.

Comment on lines +38 to +42
return (int) ceil($builder
->hydrate(false)
->count()
->getQuery()
->execute() / $this->perPage);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is immediately forgetting the exact number of pages. I think it would be better to make this value accessible with a method such as getNumberOfResults().

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can add something like this. Since we're discussing this, how do you feel about the paginator count being the number of pages? I felt that made more sense than returning the number of results (or worse, the number of items on the current page). If we don't want to change the Countable behaviour, I would use something like countResults (or similar) and use that to return the number of results, storing them in a private property as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants