-
-
Notifications
You must be signed in to change notification settings - Fork 920
Supports keeping the cursors for pagination #1915
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
d3627b1
to
c4731df
Compare
(CircleCI failure in unrelated by the way) |
$lastObject = end($objects); | ||
|
||
if (false !== $lastObject) { | ||
$data['hydra:view']['@id'] = IriHelper::createIri($parsed['parts'], $parsed['parameters']); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This... doesn't require the $lastObject
to be not null
Issue with the merging stuff. Just rebase should be fine on master since #1917 . |
private $pageParameterName; | ||
private $enabledParameterName; | ||
|
||
public function __construct(NormalizerInterface $collectionNormalizer, string $pageParameterName = 'page', string $enabledParameterName = 'pagination') | ||
public function __construct(NormalizerInterface $collectionNormalizer, ResourceMetadataFactoryInterface $resourceMetadataFactory, string $pageParameterName = 'page', string $enabledParameterName = 'pagination') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
new arguments should have a default value according to SF BC promise IIRC
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, if we apply the BC policy here I shouldn't change the argument order either 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed, we can't do that :)
@@ -71,12 +75,40 @@ public function normalize($object, $format = null, array $context = []) | |||
return $data; | |||
} | |||
|
|||
$metadata = isset($context['resource_class']) ? $this->resourceMetadataFactory->create($context['resource_class']) : null; | |||
$isPaginatedWithCursor = $paginated && null !== $metadata && null !== $cursorPaginationAttribute = $metadata->getAttribute('pagination_via_cursor'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what about using getCollectionOperationAttribute()
instead of getAttribute()
?
isn't collection_operation_name
key set in the context ?
* @ORM\Entity | ||
* @ApiResource(attributes={ | ||
* "pagination_partial"=true, | ||
* "pagination_via_cursor"={ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what about supporting multiple fields? this is often how it is done in postgres...
SELECT * FROM table WHERE (date, id) < (prev_date, prev_id) ORDER BY date DESC, id DESC LIMIT 10;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking about adding more later but I think you're right, we can start with multiple.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Multiple fields added 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than just supporting fields, it might be needed. Unlike the sorting for a regular pagination, the cursor must be unique. Although not efficient performance-wise, the safest way to achieve this is to always append (when necessary as it requires what fields are already present) a sort by ID as a last order by.
I also think you need to account for the NULL
values as they might mess up the order
* } | ||
* }) | ||
* | ||
* @ApiFilter(RangeFilter::class, properties={"id"}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so these filters must match the pagination_via_cursor
. should there be a check for that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, shouldn't we put these filters automatically based on the pagination_via_cursor
configuration?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. Think so!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've looked at it, will do that in another PR while refactoring the pagination actually.
$forwardRangeOperator = 'desc' === strtolower($cursorPaginationAttribute['direction']) ? 'lt' : 'gt'; | ||
$backwardRangeOperator = 'gt' === $forwardRangeOperator ? 'lt' : 'gt'; | ||
|
||
$accessor = PropertyAccess::createPropertyAccessor(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should reuse the existing service if it exists (and create it only if null
is passed in the constructor). This way we'll benefit from the component's cache.
Status: Needs review |
@@ -71,12 +78,29 @@ public function normalize($object, $format = null, array $context = []) | |||
return $data; | |||
} | |||
|
|||
$metadata = isset($context['resource_class']) && null !== $this->resourceMetadataFactory ? $this->resourceMetadataFactory->create($context['resource_class']) : null; | |||
$isPaginatedWithCursor = $paginated && null !== $metadata && null !== $cursorPaginationAttribute = $metadata->getCollectionOperationAttribute($context['collection_operation_name'], 'pagination_via_cursor', null, true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find these lines a bit long but apart from that 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I split them, it will be longer (i.e. repeating these metadata !== null
) 🙊
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could be extracted into a private method?
What about HAL ( |
if ($paginated) { | ||
if ($isPaginatedWithCursor) { | ||
$objects = iterator_to_array($object); | ||
$firstObject = current($objects); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this will fail if there is no object
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will return false
if there is no object and this is tested below, isn't it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yup, what @meyerbaptiste said
Whoever needs it will add it? 🤷♂️ |
it would be nice if all output formats support a major feature like this. otherwise it sets a precedent that contributors only add features to the formats they use, which will result in not great framework caveats of "Awesome Feature XXXX (only supported for JSON API)" |
@bendavies I do agree with you. Do you want to add HAL support in this PR? 😉 |
i could but it would not be for a few weeks. currently on vacation |
About the format support, on the other hand, we would not want to discourage contribution because of the need to support all formats? And sometimes it's just better for people who are actually familiar with the format / who actually use it to add the feature. |
Only supported for JSON-LD / Hydra is acceptable to me, as it's the default / recommended(?) format. But not for the other formats. 😛 |
This PR doesn't support duplicate values for something you are sorting by. e.g if you are sorting by "createdAt" and there are multiple entities it needs to check for a backup field like id. Query would be something like:
direction of the > is going to change depending on which way you are going in the pagination. |
it supports multiple fields. it's up to the developer to use the fields they need to do the sorting. |
What do you expect as a change in here except fixing the merge conflicts? Can we go ahead without all the formats? |
Let’s go ahead |
f2d247a
to
a60b771
Compare
Rebased on master 👍 |
{ | ||
$this->collectionNormalizer = $collectionNormalizer; | ||
$this->pageParameterName = $pageParameterName; | ||
$this->enabledParameterName = $enabledParameterName; | ||
$this->resourceMetadataFactory = $resourceMetadataFactory; | ||
$this->propertyAccessor = $propertyAccessor ?: PropertyAccess::createPropertyAccessor(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
??
ping @sroze we'd like to merge this one |
Let's get this going again. It's an awesome feature. |
My please. Thanks for taking it over the line!
…On Wed, 20 Feb 2019 at 14:26, Antoine Bluchet ***@***.***> wrote:
Closed #1915 <#1915>.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#1915 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AAxHEea9E9UHoAXRI1Lei0LPA_HVd4Lnks5vPVsLgaJpZM4TuOwf>
.
|
The background of this pull-request is explained in the following (public but non-published) blog post:
https://medium.com/@sroze/cursor-based-pagination-with-apiplatform-74fd1d324723
This is the first step, allowing to manually use the filters to achieve a cursor-based pagination. A follow-up PR will come with an obfuscation mechanism if this one goes through :)