vendor/overblog/graphql-bundle/src/Relay/Connection/Paginator.php line 89

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. namespace Overblog\GraphQLBundle\Relay\Connection;
  4. use GraphQL\Executor\Promise\Promise;
  5. use Overblog\GraphQLBundle\Definition\ArgumentInterface;
  6. use Overblog\GraphQLBundle\Relay\Connection\Output\Connection;
  7. use function call_user_func;
  8. use function call_user_func_array;
  9. use function count;
  10. use function is_callable;
  11. use function is_numeric;
  12. use function max;
  13. class Paginator
  14. {
  15.     public const MODE_REGULAR false;
  16.     public const MODE_PROMISE true;
  17.     private bool $promise;
  18.     private int $totalCount;
  19.     private ConnectionBuilder $connectionBuilder;
  20.     /** @var callable */
  21.     private $fetcher;
  22.     public function __construct(callable $fetcherbool $promise self::MODE_REGULARConnectionBuilder $connectionBuilder null)
  23.     {
  24.         $this->fetcher $fetcher;
  25.         $this->promise $promise;
  26.         $this->connectionBuilder $connectionBuilder ?? new ConnectionBuilder();
  27.     }
  28.     /**
  29.      * @param int|callable $total
  30.      *
  31.      * @return Connection|Promise A connection or a promise
  32.      */
  33.     public function backward(ArgumentInterface $args$total, array $callableArgs = [])
  34.     {
  35.         $total $this->computeTotalCount($total$callableArgs);
  36.         $limit $args['last'] ?? null;
  37.         $before $args['before'] ?? null;
  38.         $offset max(0$this->connectionBuilder->getOffsetWithDefault($before$total) - $limit);
  39.         $entities call_user_func($this->fetcher$offset$limit);
  40.         return $this->handleEntities($entities, function ($entities) use ($args$offset$total) {
  41.             return $this->connectionBuilder->connectionFromArraySlice($entities$args, [
  42.                 'sliceStart' => $offset,
  43.                 'arrayLength' => $total,
  44.             ]);
  45.         });
  46.     }
  47.     /**
  48.      * @return Connection|Promise A connection or a promise
  49.      */
  50.     public function forward(ArgumentInterface $args)
  51.     {
  52.         $limit $args['first'] ?? null;
  53.         $after $args['after'] ?? null;
  54.         $offset $this->connectionBuilder->getOffsetWithDefault($after0);
  55.         // If we don't have a cursor or if it's not valid, then we must not use the slice method
  56.         if (!is_numeric($this->connectionBuilder->cursorToOffset($after)) || !$after) {
  57.             $entities call_user_func($this->fetcher$offset$limit $limit $limit);
  58.             return $this->handleEntities($entities, fn ($entities) => $this->connectionBuilder->connectionFromArray($entities$args));
  59.         } else {
  60.             $entities call_user_func($this->fetcher$offset$limit $limit $limit);
  61.             return $this->handleEntities($entities, function ($entities) use ($args$offset) {
  62.                 return $this->connectionBuilder->connectionFromArraySlice($entities$args, [
  63.                     'sliceStart' => $offset,
  64.                     'arrayLength' => $offset count($entities),
  65.                 ]);
  66.             });
  67.         }
  68.     }
  69.     /**
  70.      * @param int|callable $total
  71.      *
  72.      * @return Connection|Promise A connection or a promise
  73.      */
  74.     public function auto(ArgumentInterface $args$total, array $callableArgs = [])
  75.     {
  76.         if (isset($args['last'])) {
  77.             $connection $this->backward($args$total$callableArgs);
  78.         } else {
  79.             $connection $this->forward($args);
  80.         }
  81.         if ($this->promise) {
  82.             /** @var Promise $connection */
  83.             return $connection->then(function (ConnectionInterface $connection) use ($total$callableArgs) {
  84.                 $connection->setTotalCount($this->computeTotalCount($total$callableArgs));
  85.                 return $connection;
  86.             });
  87.         } else {
  88.             /** @var Connection $connection */
  89.             $connection->setTotalCount($this->computeTotalCount($total$callableArgs));
  90.             return $connection;
  91.         }
  92.     }
  93.     /**
  94.      * @param array<int, string>|Promise $entities An array of entities to paginate or a promise
  95.      *
  96.      * @return Connection|Promise A connection or a promise
  97.      */
  98.     private function handleEntities($entities, callable $callback)
  99.     {
  100.         if ($this->promise) {
  101.             /** @var Promise $entities */
  102.             return $entities->then($callback);
  103.         }
  104.         return $callback($entities);
  105.     }
  106.     /**
  107.      * @param int|callable $total
  108.      *
  109.      * @return int|mixed
  110.      */
  111.     private function computeTotalCount($total, array $callableArgs = [])
  112.     {
  113.         if (isset($this->totalCount)) {
  114.             return $this->totalCount;
  115.         }
  116.         $this->totalCount is_callable($total) ? call_user_func_array($total$callableArgs) : $total;
  117.         return $this->totalCount;
  118.     }
  119. }