1: <?php declare(strict_types=1);
2:
3: namespace Salient\Curler\Pager;
4:
5: use Psr\Http\Message\RequestInterface;
6: use Salient\Contract\Curler\CurlerInterface;
7: use Salient\Contract\Curler\CurlerPageInterface;
8: use Salient\Contract\Curler\CurlerPagerInterface;
9: use Salient\Contract\Http\HttpResponseInterface;
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: * Creates a new QueryPager object
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: RequestInterface $request,
61: CurlerInterface $curler,
62: ?array $query = null
63: ): RequestInterface {
64: return $request;
65: }
66:
67: /**
68: * @inheritDoc
69: */
70: public function getPage(
71: $data,
72: RequestInterface $request,
73: HttpResponseInterface $response,
74: CurlerInterface $curler,
75: ?CurlerPageInterface $previousPage = null,
76: ?array $query = null
77: ): CurlerPageInterface {
78: $data = ($this->EntitySelector)($data);
79:
80: if ($data && (
81: $this->PageSize === null
82: || count($data) === $this->PageSize
83: )) {
84: $key = $this->PageKey;
85: if ($key === null && $query && Test::isInteger(reset($query))) {
86: $key = key($query);
87: }
88: if ($key === null) {
89: throw new LogicException('No page key and no integer value at query offset 0');
90: }
91: $query[$key] ??= 1;
92: if (!Test::isInteger($query[$key])) {
93: throw new LogicException('Value at page key is not an integer');
94: }
95: $query[$key]++;
96: $nextRequest = $curler->replaceQuery($request, $query);
97: }
98:
99: return new CurlerPage($data, $nextRequest ?? null, $query);
100: }
101: }
102: