vendor/api-platform/core/src/Hydra/Serializer/PartialCollectionViewNormalizer.php line 50

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the API Platform project.
  4.  *
  5.  * (c) Kévin Dunglas <dunglas@gmail.com>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. declare(strict_types=1);
  11. namespace ApiPlatform\Hydra\Serializer;
  12. use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
  13. use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
  14. use ApiPlatform\State\Pagination\PaginatorInterface;
  15. use ApiPlatform\State\Pagination\PartialPaginatorInterface;
  16. use ApiPlatform\Util\IriHelper;
  17. use Symfony\Component\PropertyAccess\PropertyAccess;
  18. use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
  19. use Symfony\Component\Serializer\Exception\UnexpectedValueException;
  20. use Symfony\Component\Serializer\Normalizer\CacheableSupportsMethodInterface;
  21. use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
  22. use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
  23. /**
  24.  * Adds a view key to the result of a paginated Hydra collection.
  25.  *
  26.  * @author Kévin Dunglas <dunglas@gmail.com>
  27.  * @author Samuel ROZE <samuel.roze@gmail.com>
  28.  */
  29. final class PartialCollectionViewNormalizer implements NormalizerInterfaceNormalizerAwareInterfaceCacheableSupportsMethodInterface
  30. {
  31.     private $collectionNormalizer;
  32.     private $pageParameterName;
  33.     private $enabledParameterName;
  34.     private $resourceMetadataFactory;
  35.     private $propertyAccessor;
  36.     public function __construct(NormalizerInterface $collectionNormalizerstring $pageParameterName 'page'string $enabledParameterName 'pagination'$resourceMetadataFactory nullPropertyAccessorInterface $propertyAccessor null)
  37.     {
  38.         $this->collectionNormalizer $collectionNormalizer;
  39.         $this->pageParameterName $pageParameterName;
  40.         $this->enabledParameterName $enabledParameterName;
  41.         $this->resourceMetadataFactory $resourceMetadataFactory;
  42.         if (!$resourceMetadataFactory instanceof ResourceMetadataCollectionFactoryInterface) {
  43.             trigger_deprecation('api-platform/core''2.7'sprintf('Use "%s" instead of "%s".'ResourceMetadataCollectionFactoryInterface::class, ResourceMetadataFactoryInterface::class));
  44.         }
  45.         $this->propertyAccessor $propertyAccessor ?? PropertyAccess::createPropertyAccessor();
  46.     }
  47.     /**
  48.      * {@inheritdoc}
  49.      *
  50.      * @return array|string|int|float|bool|\ArrayObject|null
  51.      */
  52.     public function normalize($object$format null, array $context = [])
  53.     {
  54.         $data $this->collectionNormalizer->normalize($object$format$context);
  55.         if (!\is_array($data)) {
  56.             throw new UnexpectedValueException('Expected data to be an array');
  57.         }
  58.         if (isset($context['api_sub_level'])) {
  59.             return $data;
  60.         }
  61.         $currentPage $lastPage $itemsPerPage $pageTotalItems null;
  62.         if ($paginated = ($object instanceof PartialPaginatorInterface)) {
  63.             if ($object instanceof PaginatorInterface) {
  64.                 $paginated 1. !== $lastPage $object->getLastPage();
  65.             } else {
  66.                 $itemsPerPage $object->getItemsPerPage();
  67.                 $pageTotalItems = (float) \count($object);
  68.             }
  69.             $currentPage $object->getCurrentPage();
  70.         }
  71.         $parsed IriHelper::parseIri($context['request_uri'] ?? '/'$this->pageParameterName);
  72.         $appliedFilters $parsed['parameters'];
  73.         unset($appliedFilters[$this->enabledParameterName]);
  74.         if (!$appliedFilters && !$paginated) {
  75.             return $data;
  76.         }
  77.         $isPaginatedWithCursor false;
  78.         $cursorPaginationAttribute null;
  79.         if ($this->resourceMetadataFactory instanceof ResourceMetadataFactoryInterface && isset($context['resource_class']) && $paginated) {
  80.             $metadata $this->resourceMetadataFactory->create($context['resource_class']);
  81.             $isPaginatedWithCursor null !== $cursorPaginationAttribute $metadata->getCollectionOperationAttribute($context['collection_operation_name'] ?? $context['subresource_operation_name'], 'pagination_via_cursor'nulltrue);
  82.         } elseif ($this->resourceMetadataFactory instanceof ResourceMetadataCollectionFactoryInterface && isset($context['resource_class']) && $paginated) {
  83.             $operation $this->resourceMetadataFactory->create($context['resource_class'])->getOperation($context['operation_name'] ?? null);
  84.             $isPaginatedWithCursor = [] !== $cursorPaginationAttribute = ($operation->getPaginationViaCursor() ?? []);
  85.         }
  86.         $data['hydra:view'] = ['@id' => null'@type' => 'hydra:PartialCollectionView'];
  87.         if ($isPaginatedWithCursor) {
  88.             return $this->populateDataWithCursorBasedPagination($data$parsed$object$cursorPaginationAttribute);
  89.         }
  90.         $data['hydra:view']['@id'] = IriHelper::createIri($parsed['parts'], $parsed['parameters'], $this->pageParameterName$paginated $currentPage null);
  91.         if ($paginated) {
  92.             return $this->populateDataWithPagination($data$parsed$currentPage$lastPage$itemsPerPage$pageTotalItems);
  93.         }
  94.         return $data;
  95.     }
  96.     /**
  97.      * {@inheritdoc}
  98.      */
  99.     public function supportsNormalization($data$format null, array $context = []): bool
  100.     {
  101.         return $this->collectionNormalizer->supportsNormalization($data$format$context);
  102.     }
  103.     /**
  104.      * {@inheritdoc}
  105.      */
  106.     public function hasCacheableSupportsMethod(): bool
  107.     {
  108.         return $this->collectionNormalizer instanceof CacheableSupportsMethodInterface && $this->collectionNormalizer->hasCacheableSupportsMethod();
  109.     }
  110.     /**
  111.      * {@inheritdoc}
  112.      */
  113.     public function setNormalizer(NormalizerInterface $normalizer)
  114.     {
  115.         if ($this->collectionNormalizer instanceof NormalizerAwareInterface) {
  116.             $this->collectionNormalizer->setNormalizer($normalizer);
  117.         }
  118.     }
  119.     private function cursorPaginationFields(array $fieldsint $direction$object)
  120.     {
  121.         $paginationFilters = [];
  122.         foreach ($fields as $field) {
  123.             $forwardRangeOperator 'desc' === strtolower($field['direction']) ? 'lt' 'gt';
  124.             $backwardRangeOperator 'gt' === $forwardRangeOperator 'lt' 'gt';
  125.             $operator $direction $forwardRangeOperator $backwardRangeOperator;
  126.             $paginationFilters[$field['field']] = [
  127.                 $operator => (string) $this->propertyAccessor->getValue($object$field['field']),
  128.             ];
  129.         }
  130.         return $paginationFilters;
  131.     }
  132.     private function populateDataWithCursorBasedPagination(array $data, array $parsed\Traversable $object$cursorPaginationAttribute): array
  133.     {
  134.         $objects iterator_to_array($object);
  135.         $firstObject current($objects);
  136.         $lastObject end($objects);
  137.         $data['hydra:view']['@id'] = IriHelper::createIri($parsed['parts'], $parsed['parameters']);
  138.         if (false !== $lastObject && \is_array($cursorPaginationAttribute)) {
  139.             $data['hydra:view']['hydra:next'] = IriHelper::createIri($parsed['parts'], array_merge($parsed['parameters'], $this->cursorPaginationFields($cursorPaginationAttribute1$lastObject)));
  140.         }
  141.         if (false !== $firstObject && \is_array($cursorPaginationAttribute)) {
  142.             $data['hydra:view']['hydra:previous'] = IriHelper::createIri($parsed['parts'], array_merge($parsed['parameters'], $this->cursorPaginationFields($cursorPaginationAttribute, -1$firstObject)));
  143.         }
  144.         return $data;
  145.     }
  146.     private function populateDataWithPagination(array $data, array $parsed, ?float $currentPage, ?float $lastPage, ?float $itemsPerPage, ?float $pageTotalItems): array
  147.     {
  148.         if (null !== $lastPage) {
  149.             $data['hydra:view']['hydra:first'] = IriHelper::createIri($parsed['parts'], $parsed['parameters'], $this->pageParameterName1.);
  150.             $data['hydra:view']['hydra:last'] = IriHelper::createIri($parsed['parts'], $parsed['parameters'], $this->pageParameterName$lastPage);
  151.         }
  152.         if (1. !== $currentPage) {
  153.             $data['hydra:view']['hydra:previous'] = IriHelper::createIri($parsed['parts'], $parsed['parameters'], $this->pageParameterName$currentPage 1.);
  154.         }
  155.         if ((null !== $lastPage && $currentPage $lastPage) || (null === $lastPage && $pageTotalItems >= $itemsPerPage)) {
  156.             $data['hydra:view']['hydra:next'] = IriHelper::createIri($parsed['parts'], $parsed['parameters'], $this->pageParameterName$currentPage 1.);
  157.         }
  158.         return $data;
  159.     }
  160. }
  161. class_alias(PartialCollectionViewNormalizer::class, \ApiPlatform\Core\Hydra\Serializer\PartialCollectionViewNormalizer::class);