|
| 1 | +<?php |
| 2 | + |
| 3 | + |
| 4 | +namespace Twilio; |
| 5 | + |
| 6 | + |
| 7 | +use Twilio\Exceptions\KeyErrorException; |
| 8 | +use Twilio\Http\Response; |
| 9 | + |
| 10 | +/** |
| 11 | + * TokenPaginationPage is an abstract base class for handling paginated API responses |
| 12 | + * that use token-based pagination rather than traditional page numbers. These are part of |
| 13 | + * the new Twilio API Standards V1. |
| 14 | + * |
| 15 | + * Unlike the base {@see Page} class, which typically uses page numbers and URLs for navigation, |
| 16 | + * TokenPaginationPage manages pagination using tokens (e.g., nextToken, previousToken) provided |
| 17 | + * in the API response metadata. This allows for more flexible and scalable pagination, especially |
| 18 | + * for APIs that do not support offset-based pagination. |
| 19 | + * |
| 20 | + * Example expected response format with token metadata: |
| 21 | + * { |
| 22 | + * "meta": { |
| 23 | + * "key": "items", |
| 24 | + * "pageSize": 50, |
| 25 | + * "nextToken": "abc123", |
| 26 | + * "previousToken": "xyz789" |
| 27 | + * }, |
| 28 | + * "items": [ |
| 29 | + * { "id": 1, "name": "Item 1" }, |
| 30 | + * { "id": 2, "name": "Item 2" } |
| 31 | + * // ... |
| 32 | + * ] |
| 33 | + * } |
| 34 | + * |
| 35 | + */ |
| 36 | +abstract class TokenPaginationPage extends Page { |
| 37 | + |
| 38 | + protected $key; |
| 39 | + protected $pageSize; |
| 40 | + protected $nextToken; |
| 41 | + protected $previousToken; |
| 42 | + protected $url; |
| 43 | + protected $previousPageUrl; |
| 44 | + protected $nextPageUrl; |
| 45 | + |
| 46 | + /** |
| 47 | + * TokenPaginationPage constructor. |
| 48 | + * |
| 49 | + * @param Version $version The API version object. |
| 50 | + * @param Response $response The HTTP response object. |
| 51 | + * @throws KeyErrorException If the 'key' metadata is missing. |
| 52 | + */ |
| 53 | + public function __construct(Version $version, Response $response) { |
| 54 | + parent::__construct($version, $response); |
| 55 | + |
| 56 | + $httpClient = $version->getDomain()->getClient()->getHttpClient(); |
| 57 | + |
| 58 | + $this->url = ''; |
| 59 | + if ($httpClient->lastRequest) { |
| 60 | + $fullUrl = $httpClient->lastRequest[CURLOPT_URL]; |
| 61 | + // remove query parameters from url |
| 62 | + $parts = explode('?', $fullUrl); |
| 63 | + $this->url = $parts[0]; |
| 64 | + } |
| 65 | + |
| 66 | + $this->key = $this->getMeta('key'); |
| 67 | + $this->pageSize = (int) $this->getMeta('pageSize'); |
| 68 | + $this->nextToken = $this->getMeta('nextToken'); |
| 69 | + $this->previousToken = $this->getMeta('previousToken'); |
| 70 | + } |
| 71 | + |
| 72 | + /** |
| 73 | + * Load the current page of records based on the 'key' metadata. |
| 74 | + * |
| 75 | + * @return array Array of records from the current page. |
| 76 | + * @throws KeyErrorException If the 'key' metadata is missing. |
| 77 | + */ |
| 78 | + protected function loadPage(): array { |
| 79 | + $this->key = $this->getMeta('key'); |
| 80 | + if ($this->key) { |
| 81 | + return $this->payload[$this->key]; |
| 82 | + } |
| 83 | + |
| 84 | + throw new KeyErrorException('key not found in the response'); |
| 85 | + } |
| 86 | + |
| 87 | + /** |
| 88 | + * Construct the query string for pagination URLs. |
| 89 | + * |
| 90 | + * @param string|null $pageToken The token for the desired page. |
| 91 | + * @return string The constructed query string. |
| 92 | + */ |
| 93 | + protected function getQueryString(?string $pageToken): string { |
| 94 | + $params = []; |
| 95 | + if ($this->pageSize) { |
| 96 | + $params['pageSize'] = $this->pageSize; |
| 97 | + } |
| 98 | + if ($pageToken !== null && $pageToken !== '') { |
| 99 | + $params['pageToken'] = $pageToken; |
| 100 | + } |
| 101 | + $queryString = http_build_query($params); |
| 102 | + return $queryString ? '?' . $queryString : ''; |
| 103 | + } |
| 104 | + |
| 105 | + /** |
| 106 | + * Get the URL for the previous page of results. |
| 107 | + * |
| 108 | + * @return string|null The URL for the previous page, or null if there is no previous page. |
| 109 | + */ |
| 110 | + public function getPreviousPageUrl(): ?string { |
| 111 | + if (!$this->previousToken) { |
| 112 | + return null; |
| 113 | + } |
| 114 | + if (!$this->previousPageUrl) { |
| 115 | + $this->previousPageUrl = $this->url . $this->getQueryString($this->previousToken); |
| 116 | + } |
| 117 | + return $this->previousPageUrl; |
| 118 | + } |
| 119 | + |
| 120 | + /** |
| 121 | + * Get the URL for the next page of results. |
| 122 | + * |
| 123 | + * @return string|null The URL for the next page, or null if there is no next page. |
| 124 | + */ |
| 125 | + public function getNextPageUrl(): ?string { |
| 126 | + if (!$this->nextToken) { |
| 127 | + return null; |
| 128 | + } |
| 129 | + if (!$this->nextPageUrl) { |
| 130 | + $this->nextPageUrl = $this->url . $this->getQueryString($this->nextToken); |
| 131 | + } |
| 132 | + return $this->nextPageUrl; |
| 133 | + } |
| 134 | + |
| 135 | + public function __toString(): string { |
| 136 | + return '[TokenPaginationPage]'; |
| 137 | + } |
| 138 | + |
| 139 | +} |
0 commit comments