1515
1616use ApiPlatform \Core \DataProvider \PaginatorInterface ;
1717use ApiPlatform \Core \DataProvider \PartialPaginatorInterface ;
18+ use ApiPlatform \Core \Metadata \Resource \Factory \ResourceMetadataFactoryInterface ;
1819use ApiPlatform \Core \Util \IriHelper ;
20+ use Symfony \Component \PropertyAccess \PropertyAccess ;
21+ use Symfony \Component \PropertyAccess \PropertyAccessorInterface ;
1922use Symfony \Component \Serializer \Normalizer \CacheableSupportsMethodInterface ;
2023use Symfony \Component \Serializer \Normalizer \NormalizerAwareInterface ;
2124use Symfony \Component \Serializer \Normalizer \NormalizerInterface ;
@@ -31,12 +34,16 @@ final class PartialCollectionViewNormalizer implements NormalizerInterface, Norm
3134 private $ collectionNormalizer ;
3235 private $ pageParameterName ;
3336 private $ enabledParameterName ;
37+ private $ resourceMetadataFactory ;
38+ private $ propertyAccessor ;
3439
35- public function __construct (NormalizerInterface $ collectionNormalizer , string $ pageParameterName = 'page ' , string $ enabledParameterName = 'pagination ' )
40+ public function __construct (NormalizerInterface $ collectionNormalizer , string $ pageParameterName = 'page ' , string $ enabledParameterName = 'pagination ' , ResourceMetadataFactoryInterface $ resourceMetadataFactory = null , PropertyAccessorInterface $ propertyAccessor = null )
3641 {
3742 $ this ->collectionNormalizer = $ collectionNormalizer ;
3843 $ this ->pageParameterName = $ pageParameterName ;
3944 $ this ->enabledParameterName = $ enabledParameterName ;
45+ $ this ->resourceMetadataFactory = $ resourceMetadataFactory ;
46+ $ this ->propertyAccessor = $ propertyAccessor ?? PropertyAccess::createPropertyAccessor ();
4047 }
4148
4249 /**
@@ -69,12 +76,29 @@ public function normalize($object, $format = null, array $context = [])
6976 return $ data ;
7077 }
7178
79+ $ metadata = isset ($ context ['resource_class ' ]) && null !== $ this ->resourceMetadataFactory ? $ this ->resourceMetadataFactory ->create ($ context ['resource_class ' ]) : null ;
80+ $ isPaginatedWithCursor = $ paginated && null !== $ metadata && null !== $ cursorPaginationAttribute = $ metadata ->getCollectionOperationAttribute ($ context ['collection_operation_name ' ], 'pagination_via_cursor ' , null , true );
81+
7282 $ data ['hydra:view ' ] = [
73- '@id ' => IriHelper::createIri ($ parsed ['parts ' ], $ parsed ['parameters ' ], $ this ->pageParameterName , $ paginated ? $ currentPage : null ),
83+ '@id ' => IriHelper::createIri ($ parsed ['parts ' ], $ parsed ['parameters ' ], $ this ->pageParameterName , $ paginated && ! $ isPaginatedWithCursor ? $ currentPage : null ),
7484 '@type ' => 'hydra:PartialCollectionView ' ,
7585 ];
7686
77- if ($ paginated ) {
87+ if ($ isPaginatedWithCursor ) {
88+ $ objects = iterator_to_array ($ object );
89+ $ firstObject = current ($ objects );
90+ $ lastObject = end ($ objects );
91+
92+ $ data ['hydra:view ' ]['@id ' ] = IriHelper::createIri ($ parsed ['parts ' ], $ parsed ['parameters ' ]);
93+
94+ if (false !== $ lastObject ) {
95+ $ data ['hydra:view ' ]['hydra:next ' ] = IriHelper::createIri ($ parsed ['parts ' ], array_merge ($ parsed ['parameters ' ], $ this ->cursorPaginationFields ($ cursorPaginationAttribute , 1 , $ lastObject )));
96+ }
97+
98+ if (false !== $ firstObject ) {
99+ $ data ['hydra:view ' ]['hydra:previous ' ] = IriHelper::createIri ($ parsed ['parts ' ], array_merge ($ parsed ['parameters ' ], $ this ->cursorPaginationFields ($ cursorPaginationAttribute , -1 , $ firstObject )));
100+ }
101+ } elseif ($ paginated ) {
78102 if (null !== $ lastPage ) {
79103 $ data ['hydra:view ' ]['hydra:first ' ] = IriHelper::createIri ($ parsed ['parts ' ], $ parsed ['parameters ' ], $ this ->pageParameterName , 1. );
80104 $ data ['hydra:view ' ]['hydra:last ' ] = IriHelper::createIri ($ parsed ['parts ' ], $ parsed ['parameters ' ], $ this ->pageParameterName , $ lastPage );
@@ -117,4 +141,22 @@ public function setNormalizer(NormalizerInterface $normalizer)
117141 $ this ->collectionNormalizer ->setNormalizer ($ normalizer );
118142 }
119143 }
144+
145+ private function cursorPaginationFields (array $ fields , int $ direction , $ object )
146+ {
147+ $ paginationFilters = [];
148+
149+ foreach ($ fields as $ field ) {
150+ $ forwardRangeOperator = 'desc ' === strtolower ($ field ['direction ' ]) ? 'lt ' : 'gt ' ;
151+ $ backwardRangeOperator = 'gt ' === $ forwardRangeOperator ? 'lt ' : 'gt ' ;
152+
153+ $ operator = $ direction > 0 ? $ forwardRangeOperator : $ backwardRangeOperator ;
154+
155+ $ paginationFilters [$ field ['field ' ]] = [
156+ $ operator => (string ) $ this ->propertyAccessor ->getValue ($ object , $ field ['field ' ]),
157+ ];
158+ }
159+
160+ return $ paginationFilters ;
161+ }
120162}
0 commit comments