vendor/api-platform/core/src/Doctrine/Odm/Extension/PaginationExtension.php line 112

  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\Doctrine\Odm\Extension;
  12. use ApiPlatform\Doctrine\Odm\Paginator;
  13. use ApiPlatform\Exception\RuntimeException;
  14. use ApiPlatform\Metadata\Operation;
  15. use ApiPlatform\State\Pagination\Pagination;
  16. use Doctrine\ODM\MongoDB\Aggregation\Builder;
  17. use Doctrine\ODM\MongoDB\DocumentManager;
  18. use Doctrine\ODM\MongoDB\Repository\DocumentRepository;
  19. use Doctrine\Persistence\ManagerRegistry;
  20. /**
  21.  * Applies pagination on the Doctrine aggregation for resource collection when enabled.
  22.  *
  23.  * @author Kévin Dunglas <dunglas@gmail.com>
  24.  * @author Samuel ROZE <samuel.roze@gmail.com>
  25.  * @author Alan Poulain <contact@alanpoulain.eu>
  26.  */
  27. final class PaginationExtension implements AggregationResultCollectionExtensionInterface
  28. {
  29.     public function __construct(private readonly ManagerRegistry $managerRegistry, private readonly Pagination $pagination)
  30.     {
  31.     }
  32.     /**
  33.      * {@inheritdoc}
  34.      *
  35.      * @throws RuntimeException
  36.      */
  37.     public function applyToCollection(Builder $aggregationBuilderstring $resourceClassOperation $operation null, array &$context = []): void
  38.     {
  39.         if (!$this->pagination->isEnabled($operation$context)) {
  40.             return;
  41.         }
  42.         if (($context['graphql_operation_name'] ?? false) && !$this->pagination->isGraphQlEnabled($operation$context)) {
  43.             return;
  44.         }
  45.         $context $this->addCountToContext(clone $aggregationBuilder$context);
  46.         [, $offset$limit] = $this->pagination->getPagination($operation$context);
  47.         $manager $this->managerRegistry->getManagerForClass($resourceClass);
  48.         if (!$manager instanceof DocumentManager) {
  49.             throw new RuntimeException(sprintf('The manager for "%s" must be an instance of "%s".'$resourceClassDocumentManager::class));
  50.         }
  51.         /**
  52.          * @var DocumentRepository
  53.          */
  54.         $repository $manager->getRepository($resourceClass);
  55.         $resultsAggregationBuilder $repository->createAggregationBuilder()->skip($offset);
  56.         if ($limit 0) {
  57.             $resultsAggregationBuilder->limit($limit);
  58.         } else {
  59.             // Results have to be 0 but MongoDB does not support a limit equal to 0.
  60.             $resultsAggregationBuilder->match()->field(Paginator::LIMIT_ZERO_MARKER_FIELD)->equals(Paginator::LIMIT_ZERO_MARKER);
  61.         }
  62.         $aggregationBuilder
  63.             ->facet()
  64.             ->field('results')->pipeline(
  65.                 $resultsAggregationBuilder
  66.             )
  67.             ->field('count')->pipeline(
  68.                 $repository->createAggregationBuilder()
  69.                     ->count('count')
  70.             );
  71.     }
  72.     /**
  73.      * {@inheritdoc}
  74.      */
  75.     public function supportsResult(string $resourceClassOperation $operation null, array $context = []): bool
  76.     {
  77.         if ($context['graphql_operation_name'] ?? false) {
  78.             return $this->pagination->isGraphQlEnabled($operation$context);
  79.         }
  80.         return $this->pagination->isEnabled($operation$context);
  81.     }
  82.     /**
  83.      * {@inheritdoc}
  84.      *
  85.      * @throws RuntimeException
  86.      */
  87.     public function getResult(Builder $aggregationBuilderstring $resourceClassOperation $operation null, array $context = []): iterable
  88.     {
  89.         $manager $this->managerRegistry->getManagerForClass($resourceClass);
  90.         if (!$manager instanceof DocumentManager) {
  91.             throw new RuntimeException(sprintf('The manager for "%s" must be an instance of "%s".'$resourceClassDocumentManager::class));
  92.         }
  93.         $attribute $operation?->getExtraProperties()['doctrine_mongodb'] ?? [];
  94.         $executeOptions $attribute['execute_options'] ?? [];
  95.         return new Paginator($aggregationBuilder->execute($executeOptions), $manager->getUnitOfWork(), $resourceClass$aggregationBuilder->getPipeline());
  96.     }
  97.     private function addCountToContext(Builder $aggregationBuilder, array $context): array
  98.     {
  99.         if (!($context['graphql_operation_name'] ?? false)) {
  100.             return $context;
  101.         }
  102.         if (isset($context['filters']['last']) && !isset($context['filters']['before'])) {
  103.             $context['count'] = $aggregationBuilder->count('count')->execute()->toArray()[0]['count'];
  104.         }
  105.         return $context;
  106.     }
  107. }