diff --git a/src/JsonApi/State/JsonApiProvider.php b/src/JsonApi/State/JsonApiProvider.php index bf86cb406de..6e96c7ad0ce 100644 --- a/src/JsonApi/State/JsonApiProvider.php +++ b/src/JsonApi/State/JsonApiProvider.php @@ -67,7 +67,8 @@ public function provide(Operation $operation, array $uriVariables = [], array $c if ( \is_array($pageParameter) ) { - $filters = array_merge($pageParameter, $filters); + // To not break existing integration, put page array in _page + $filters = array_merge(['_page' => $pageParameter], $pageParameter, $filters); } [$included, $properties] = $this->transformFieldsetsParameters($queryParameters, $operation->getShortName() ?? ''); diff --git a/src/State/Pagination/Pagination.php b/src/State/Pagination/Pagination.php index 9d070631122..7652d3f2cb8 100644 --- a/src/State/Pagination/Pagination.php +++ b/src/State/Pagination/Pagination.php @@ -219,13 +219,38 @@ private function getGraphQlEnabled(?Operation $operation): bool return $operation?->getPaginationEnabled() ?? $enabled; } + /** + * Extract pagination parameter + * page[page] => $contextFilters['page'] with default configuration page_parameter_name: page + * page[number] => $contextFilters['_page'][number] with configuration page_parameter_name: page[number]. + */ + private function extractParameter(array $contextFilters, string $parameterName): mixed + { + preg_match_all("/[\w-]+/", $parameterName, $matches); + foreach ($matches[0] as $i => $key) { + if (0 === $i && 'page' === $key && \count($matches[0]) > 1) { + $key = '_page'; + } + if (false === \is_array($contextFilters)) { + return $contextFilters; + } + if (!\array_key_exists($key, $contextFilters)) { + return null; + } + $contextFilters = $contextFilters[$key]; + } + + return $contextFilters; + } + /** * Gets the given pagination parameter name from the given context. */ - private function getParameterFromContext(array $context, string $parameterName, mixed $default = null) + private function getParameterFromContext(array $context, string $parameterName, mixed $default = null): mixed { $filters = $context['filters'] ?? []; + $filterValue = $this->extractParameter($filters, $parameterName); - return \array_key_exists($parameterName, $filters) ? $filters[$parameterName] : $default; + return $filterValue ?? $default; } } diff --git a/src/State/Tests/PaginationTest.php b/src/State/Tests/PaginationTest.php new file mode 100644 index 00000000000..3c399410294 --- /dev/null +++ b/src/State/Tests/PaginationTest.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace ApiPlatform\State\Tests; + +use ApiPlatform\Metadata\GetCollection; +use ApiPlatform\State\Pagination\Pagination; +use PHPUnit\Framework\TestCase; + +class PaginationTest extends TestCase +{ + public function testPaginationGetPaginationWithDefaultOptionsAndDefaultContext(): void + { + $operation = new GetCollection(name: 'hello', provider: 'provider'); + $pagination = new Pagination(); + $paginationInfo = $pagination->getPagination($operation); + $this->assertSame(1, $paginationInfo[0]); + $this->assertSame(0, $paginationInfo[1]); + $this->assertSame(30, $paginationInfo[2]); + } + + public function testPaginationGetPaginationWithPageParameterNameAsArrayAndDefaultContext(): void + { + $operation = new GetCollection(name: 'hello', provider: 'provider'); + $pagination = new Pagination(['page_parameter_name' => 'page[number]']); + $paginationInfo = $pagination->getPagination($operation); + $this->assertSame(1, $paginationInfo[0]); + $this->assertSame(0, $paginationInfo[1]); + $this->assertSame(30, $paginationInfo[2]); + } + + public function testPaginationGetPaginationWithPageParametersAsArrayAndCustomContext(): void + { + $operation = new GetCollection(paginationClientItemsPerPage: true, name: 'hello', provider: 'provider'); + $pagination = new Pagination([ + 'page_parameter_name' => 'page[number]', + 'items_per_page_parameter_name' => 'page[size]', + ]); + $paginationInfo = $pagination->getPagination( + $operation, + [ + 'filters' => [ + 'number' => 2, + 'size' => 10, + '_page' => [ + 'number' => 2, + 'size' => 10, + ], + ], + ], + ); + $this->assertSame(2, $paginationInfo[0]); + $this->assertSame(10, $paginationInfo[1]); + $this->assertSame(10, $paginationInfo[2]); + } +}