1: <?php declare(strict_types=1);
2:
3: namespace Salient\Curler\Pager;
4:
5: use Psr\Http\Message\RequestInterface as PsrRequestInterface;
6: use Salient\Contract\Curler\CurlerInterface;
7: use Salient\Contract\Curler\CurlerPageInterface;
8: use Salient\Contract\Curler\CurlerPagerInterface;
9: use Salient\Contract\Http\Message\ResponseInterface;
10: use Salient\Curler\CurlerPage;
11: use Salient\Utility\Test;
12: use Closure;
13: use LogicException;
14:
15: /**
16: * Increments a value in the query string of each request until no results are
17: * returned
18: *
19: * @api
20: */
21: final class QueryPager implements CurlerPagerInterface
22: {
23: use HasEntitySelector;
24:
25: /** @var array-key|null */
26: private $PageKey;
27: /** @var int<1,max>|null */
28: private ?int $PageSize;
29:
30: /**
31: * @api
32: *
33: * @param array-key|null $pageKey The value to increment in the query string
34: * of each request, or `null` to use the first value in the query. Added to
35: * the second and subsequent requests if missing from the first.
36: * @param (Closure(mixed): list<mixed>)|array-key|null $entitySelector Entities
37: * are returned from:
38: * - `$entitySelector($data)` if `$entitySelector` is a closure
39: * - `Arr::get($data, $entitySelector)` if `$entitySelector` is a string or
40: * integer, or
41: * - `$data` if `$entitySelector` is `null`
42: * @param int<1,max>|null $pageSize Another page is requested if:
43: * - `$pageSize` is `null` and at least one result is returned, or
44: * - exactly `$pageSize` results are returned
45: */
46: public function __construct(
47: $pageKey = null,
48: $entitySelector = null,
49: ?int $pageSize = null
50: ) {
51: $this->PageKey = $pageKey;
52: $this->PageSize = $pageSize;
53: $this->applyEntitySelector($entitySelector);
54: }
55:
56: /**
57: * @inheritDoc
58: */
59: public function getFirstRequest(
60: PsrRequestInterface $request,
61: CurlerInterface $curler,
62: ?array $query = null
63: ): PsrRequestInterface {
64: return $request;
65: }
66:
67: /**
68: * @inheritDoc
69: */
70: public function getPage(
71: $data,
72: PsrRequestInterface $request,
73: ResponseInterface $response,
74: CurlerInterface $curler,
75: ?CurlerPageInterface $previousPage = null,
76: ?int $previousEntities = null,
77: ?array $query = null
78: ): CurlerPageInterface {
79: $data = ($this->EntitySelector)($data);
80:
81: if ($data && (
82: $this->PageSize === null
83: || count($data) === $this->PageSize
84: )) {
85: $key = $this->PageKey;
86: if ($key === null && $query && Test::isInteger(reset($query))) {
87: $key = key($query);
88: }
89: if ($key === null) {
90: throw new LogicException('No page key and no integer value at query offset 0');
91: }
92: $query[$key] ??= 1;
93: if (!Test::isInteger($query[$key])) {
94: throw new LogicException('Value at page key is not an integer');
95: }
96: $query[$key]++;
97: $nextRequest = $curler->replaceQuery($request, $query);
98: }
99:
100: return new CurlerPage($data, $nextRequest ?? null, $query);
101: }
102: }
103: