diff --git a/CHANGELOG.md b/CHANGELOG.md index b77af38..54a056f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,33 @@ you can use the following url: https://github.com/ericsizemore/pagination/compar Simply replace the version numbers depending on which set of changes you wish to see. +### 2.0.1 (2024-03-02) + +Mostly small coding standard (CS) related changes, with some improvements to method docblocks throughout. + +#### Changed + + * Bumped version to `2.0.1`. + * Small change to how `$pagesInRange` within `paginate()` is determined. + * (CS) Rearranged the order of methods within `Paginator` and `PaginatorInterface`. + * Made the following helper functions static: + * `determinePageRange()` + * `determinePreviousPageNumber()` + * `determineNextPageNumber()` + * Updated `composer.lock` + +#### Added + + * Added validation to `Paginator`'s construct for the passed `$config` parameter. + * Uses a new helper function `validateConfig()`, which is a static protected method. + * Added some documentation/docblocks throughout, mostly to the `PaginatorInterface`. + * Added `ext-pdo` and `ext-pdo_sqlite` to the composer require-dev. + * Added the `Override` attribute to `Paginator` methods that are from `PaginatorInterface`. + +#### Removed + + * None + ### 2.0.0 (2024-02-28) diff --git a/composer.json b/composer.json index 1867939..3cd8d9d 100644 --- a/composer.json +++ b/composer.json @@ -38,6 +38,8 @@ "php": "^8.2 <8.5" }, "require-dev": { + "ext-pdo": "*", + "ext-pdo_sqlite": "*", "friendsofphp/php-cs-fixer": "dev-master", "phpstan/phpstan": "^1.11", "phpstan/phpstan-phpunit": "^1.4", @@ -66,4 +68,4 @@ "phpstan": "vendor/bin/phpstan analyse -c phpstan.neon", "test": "phpunit" } -} \ No newline at end of file +} diff --git a/composer.lock b/composer.lock index 7b8bc29..e5ddd18 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "098dfe02a56336ea5659c2d6a0b4c9dd", + "content-hash": "4096712e5c0afbfde8eac82d6b4116a1", "packages": [], "packages-dev": [ { @@ -233,12 +233,12 @@ "source": { "type": "git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", - "reference": "1543a7a0ce3f08e4e074db0c7c1697bbe9ea2615" + "reference": "438d5eac73bdd50164dded5f0048526ab078c4e4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/1543a7a0ce3f08e4e074db0c7c1697bbe9ea2615", - "reference": "1543a7a0ce3f08e4e074db0c7c1697bbe9ea2615", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/438d5eac73bdd50164dded5f0048526ab078c4e4", + "reference": "438d5eac73bdd50164dded5f0048526ab078c4e4", "shasum": "" }, "require": { @@ -318,7 +318,7 @@ "type": "github" } ], - "time": "2024-02-27T22:15:52+00:00" + "time": "2024-02-28T19:50:58+00:00" }, { "name": "myclabs/deep-copy", @@ -565,12 +565,12 @@ "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "7d6943daa4e3b247f18f7d8bb860d8f2ca41e3bf" + "reference": "d9430ff06660959d9fd659b65acc2236e02cd850" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/7d6943daa4e3b247f18f7d8bb860d8f2ca41e3bf", - "reference": "7d6943daa4e3b247f18f7d8bb860d8f2ca41e3bf", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/d9430ff06660959d9fd659b65acc2236e02cd850", + "reference": "d9430ff06660959d9fd659b65acc2236e02cd850", "shasum": "" }, "require": { @@ -620,7 +620,7 @@ "type": "tidelift" } ], - "time": "2024-02-27T20:14:47+00:00" + "time": "2024-03-01T14:22:19+00:00" }, { "name": "phpstan/phpstan-phpunit", @@ -1059,12 +1059,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "3efde166f8370f1620a72f72c0c996a035cfe1f7" + "reference": "c7095e9ada364041de8da7208c31ab1c37ec9c45" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3efde166f8370f1620a72f72c0c996a035cfe1f7", - "reference": "3efde166f8370f1620a72f72c0c996a035cfe1f7", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c7095e9ada364041de8da7208c31ab1c37ec9c45", + "reference": "c7095e9ada364041de8da7208c31ab1c37ec9c45", "shasum": "" }, "require": { @@ -1152,7 +1152,7 @@ "type": "tidelift" } ], - "time": "2024-02-27T08:26:57+00:00" + "time": "2024-03-01T17:07:45+00:00" }, { "name": "psr/container", @@ -3596,6 +3596,9 @@ "platform": { "php": "^8.2 <8.5" }, - "platform-dev": [], + "platform-dev": { + "ext-pdo": "*", + "ext-pdo_sqlite": "*" + }, "plugin-api-version": "2.6.0" } diff --git a/src/Exception/CallbackInvalidException.php b/src/Exception/CallbackInvalidException.php index 5c23feb..a3ab0c0 100644 --- a/src/Exception/CallbackInvalidException.php +++ b/src/Exception/CallbackInvalidException.php @@ -6,7 +6,7 @@ * Pagination - Simple, lightweight and universal service that implements pagination on collections of things. * * @author Eric Sizemore - * @version 2.0.0 + * @version 2.0.1 * @copyright (C) 2024 Eric Sizemore * @license The MIT License (MIT) * diff --git a/src/Exception/CallbackNotFoundException.php b/src/Exception/CallbackNotFoundException.php index 7906a1f..bdfb86a 100644 --- a/src/Exception/CallbackNotFoundException.php +++ b/src/Exception/CallbackNotFoundException.php @@ -6,7 +6,7 @@ * Pagination - Simple, lightweight and universal service that implements pagination on collections of things. * * @author Eric Sizemore - * @version 2.0.0 + * @version 2.0.1 * @copyright (C) 2024 Eric Sizemore * @license The MIT License (MIT) * diff --git a/src/Exception/InvalidPageNumberException.php b/src/Exception/InvalidPageNumberException.php index c459833..7fa1753 100644 --- a/src/Exception/InvalidPageNumberException.php +++ b/src/Exception/InvalidPageNumberException.php @@ -6,7 +6,7 @@ * Pagination - Simple, lightweight and universal service that implements pagination on collections of things. * * @author Eric Sizemore - * @version 2.0.0 + * @version 2.0.1 * @copyright (C) 2024 Eric Sizemore * @license The MIT License (MIT) * diff --git a/src/Pagination.php b/src/Pagination.php index 50bd183..88d3499 100644 --- a/src/Pagination.php +++ b/src/Pagination.php @@ -6,7 +6,7 @@ * Pagination - Simple, lightweight and universal service that implements pagination on collections of things. * * @author Eric Sizemore - * @version 2.0.0 + * @version 2.0.1 * @copyright (C) 2024 Eric Sizemore * @license The MIT License (MIT) * @@ -48,48 +48,86 @@ use function count; /** - * Class Pagination + * Pagination class. * * @implements IteratorAggregate<(int|string), int> */ class Pagination implements IteratorAggregate, Countable { /** + * Item collection. + * * @var array */ private array $items = []; /** + * Page collection. + * * @var array */ private array $pages = []; + /** + * Total number of pages for the item collection. + */ private int $totalNumberOfPages; + /** + * Current page number of item collection. + */ private int $currentPageNumber; + /** + * First page number of item collection. + */ private int $firstPageNumber; + /** + * Last page number of item collection. + */ private int $lastPageNumber; + /** + * Previous page number. Will be null if on the first page. + */ private ?int $previousPageNumber = null; + /** + * Next page number. Will be null if on the last page. + */ private ?int $nextPageNumber = null; + /** + * Currently set amount of items we want per page. + */ private int $itemsPerPage; + /** + * Total number of items in the collection. + */ private int $totalNumberOfItems; + /** + * Given a page range, first page number. + */ private int $firstPageNumberInRange; + /** + * Given a page range, last page number. + */ private int $lastPageNumberInRange; /** + * Optional meta data to include with pagination. + * * @var array|array */ private array $meta; /** + * Returns the current collection. + * * @return array */ public function getItems(): array @@ -98,11 +136,14 @@ public function getItems(): array } /** + * Sets the item collection to be paginated. + * * @param array $items */ public function setItems(array $items): static { $this->items = $items; + return $this; } @@ -114,6 +155,7 @@ public function getCurrentPageNumber(): int public function setCurrentPageNumber(int $currentPageNumber): static { $this->currentPageNumber = $currentPageNumber; + return $this; } @@ -125,6 +167,7 @@ public function getFirstPageNumber(): int public function setFirstPageNumber(int $firstPageNumber): static { $this->firstPageNumber = $firstPageNumber; + return $this; } @@ -136,6 +179,7 @@ public function getFirstPageNumberInRange(): int public function setFirstPageNumberInRange(int $firstPageNumberInRange): static { $this->firstPageNumberInRange = $firstPageNumberInRange; + return $this; } @@ -147,6 +191,7 @@ public function getItemsPerPage(): int public function setItemsPerPage(int $itemsPerPage): static { $this->itemsPerPage = $itemsPerPage; + return $this; } @@ -158,6 +203,7 @@ public function getLastPageNumber(): int public function setLastPageNumber(int $lastPageNumber): static { $this->lastPageNumber = $lastPageNumber; + return $this; } @@ -169,6 +215,7 @@ public function getLastPageNumberInRange(): int public function setLastPageNumberInRange(int $lastPageNumberInRange): static { $this->lastPageNumberInRange = $lastPageNumberInRange; + return $this; } @@ -180,6 +227,7 @@ public function getNextPageNumber(): ?int public function setNextPageNumber(?int $nextPageNumber): static { $this->nextPageNumber = $nextPageNumber; + return $this; } @@ -197,6 +245,7 @@ public function getPages(): array public function setPages(array $pages): static { $this->pages = $pages; + return $this; } @@ -208,6 +257,7 @@ public function getPreviousPageNumber(): ?int public function setPreviousPageNumber(?int $previousPageNumber): static { $this->previousPageNumber = $previousPageNumber; + return $this; } @@ -219,6 +269,7 @@ public function getTotalNumberOfItems(): int public function setTotalNumberOfItems(int $totalNumberOfItems): static { $this->totalNumberOfItems = $totalNumberOfItems; + return $this; } @@ -230,6 +281,7 @@ public function getTotalNumberOfPages(): int public function setTotalNumberOfPages(int $totalNumberOfPages): static { $this->totalNumberOfPages = $totalNumberOfPages; + return $this; } @@ -265,6 +317,7 @@ public function getMeta(): array public function setMeta(array $meta): static { $this->meta = $meta; + return $this; } } diff --git a/src/Paginator.php b/src/Paginator.php index ce76733..7814423 100644 --- a/src/Paginator.php +++ b/src/Paginator.php @@ -6,7 +6,7 @@ * Pagination - Simple, lightweight and universal service that implements pagination on collections of things. * * @author Eric Sizemore - * @version 2.0.0 + * @version 2.0.1 * @copyright (C) 2024 Eric Sizemore * @license The MIT License (MIT) * @@ -46,15 +46,21 @@ use Esi\Pagination\Exception\InvalidPageNumberException; use Iterator; +use function array_filter; use function ceil; +use function in_array; +use function is_int; use function iterator_to_array; use function max; use function min; use function range; use function sprintf; +use const ARRAY_FILTER_USE_BOTH; + /** - * Class Paginator + * Main Paginator Class. + * * @see \Esi\Pagination\Tests\PaginatorTest */ class Paginator implements PaginatorInterface @@ -62,12 +68,12 @@ class Paginator implements PaginatorInterface /** * A callback that is used to determine the total number of items in your collection (returned as an integer). */ - private ?Closure $itemTotalCallback = null; + private ?Closure $itemTotalCallback = null; /** * A callback to slice your collection given an offset and length argument. */ - private ?Closure $sliceCallback = null; + private ?Closure $sliceCallback = null; /** * A callback to run before the count and slice queries. @@ -77,7 +83,7 @@ class Paginator implements PaginatorInterface /** * A callback to run after the count and slice queries. */ - private ?Closure $afterQueryCallback = null; + private ?Closure $afterQueryCallback = null; /** * Number of items to include per page. @@ -85,6 +91,7 @@ class Paginator implements PaginatorInterface private int $itemsPerPage = 10; /** + * Number of pages in range. */ private int $pagesInRange = 5; @@ -92,7 +99,7 @@ class Paginator implements PaginatorInterface * Constructor - passing optional configuration * * - * $paginator = new Paginator(array( + * $paginator = new Paginator([ * 'itemTotalCallback' => function () { * // ... * }, @@ -100,8 +107,8 @@ class Paginator implements PaginatorInterface * // ... * }, * 'itemsPerPage' => 10, - * 'pagesInRange' => 5 - * )); + * 'pagesInRange' => 5, + * ]); * * * @param null|array{}|array{ @@ -113,17 +120,22 @@ class Paginator implements PaginatorInterface */ public function __construct(?array $config = null) { - if ($config !== null && $config !== []) { - $this->setItemTotalCallback($config['itemTotalCallback']); - $this->setSliceCallback($config['sliceCallback']); - $this->setItemsPerPage($config['itemsPerPage']); - $this->setPagesInRange($config['pagesInRange']); + $config = self::validateConfig($config); + + if ($config === []) { + return; } + + $this->setItemTotalCallback($config['itemTotalCallback']); + $this->setSliceCallback($config['sliceCallback']); + $this->setItemsPerPage($config['itemsPerPage']); + $this->setPagesInRange($config['pagesInRange']); } /** * {@inheritdoc} */ + #[\Override] public function paginate(int $currentPageNumber = 1): Pagination { if ($this->itemTotalCallback === null) { @@ -146,9 +158,7 @@ public function paginate(int $currentPageNumber = 1): Pagination $sliceCallback = $this->sliceCallback; $itemTotalCallback = $this->itemTotalCallback; - /** @var Closure $beforeQueryCallback */ $beforeQueryCallback = $this->prepareBeforeQueryCallback(); - /** @var Closure $afterQueryCallback */ $afterQueryCallback = $this->prepareAfterQueryCallback(); $pagination = new Pagination(); @@ -158,8 +168,8 @@ public function paginate(int $currentPageNumber = 1): Pagination $afterQueryCallback($this, $pagination); $numberOfPages = (int) ceil($totalNumberOfItems / $this->itemsPerPage); - $pagesInRange = $this->pagesInRange > $numberOfPages ? $numberOfPages : $this->pagesInRange; - $pages = $this->determinePageRange($currentPageNumber, $pagesInRange, $numberOfPages); + $pagesInRange = min($this->pagesInRange, $numberOfPages); + $pages = self::determinePageRange($currentPageNumber, $pagesInRange, $numberOfPages); $offset = ($currentPageNumber - 1) * $this->itemsPerPage; $beforeQueryCallback($this, $pagination); @@ -176,8 +186,8 @@ public function paginate(int $currentPageNumber = 1): Pagination $afterQueryCallback($this, $pagination); - $previousPageNumber = $this->determinePreviousPageNumber($currentPageNumber); - $nextPageNumber = $this->determineNextPageNumber($currentPageNumber, $numberOfPages); + $previousPageNumber = self::determinePreviousPageNumber($currentPageNumber); + $nextPageNumber = self::determineNextPageNumber($currentPageNumber, $numberOfPages); /** @var non-empty-array $pages **/ $pagination @@ -201,17 +211,19 @@ public function paginate(int $currentPageNumber = 1): Pagination /** * {@inheritdoc} */ - public function getSliceCallback(): ?Closure + #[\Override] + public function getItemTotalCallback(): ?Closure { - return $this->sliceCallback; + return $this->itemTotalCallback; } /** * {@inheritdoc} */ - public function setSliceCallback(?Closure $sliceCallback): static + #[\Override] + public function setItemTotalCallback(?Closure $itemTotalCallback): static { - $this->sliceCallback = $sliceCallback; + $this->itemTotalCallback = $itemTotalCallback; return $this; } @@ -219,14 +231,27 @@ public function setSliceCallback(?Closure $sliceCallback): static /** * {@inheritdoc} */ - public function getItemTotalCallback(): ?Closure + #[\Override] + public function getSliceCallback(): ?Closure { - return $this->itemTotalCallback; + return $this->sliceCallback; } /** * {@inheritdoc} */ + #[\Override] + public function setSliceCallback(?Closure $sliceCallback): static + { + $this->sliceCallback = $sliceCallback; + + return $this; + } + + /** + * {@inheritdoc} + */ + #[\Override] public function getBeforeQueryCallback(): ?Closure { return $this->beforeQueryCallback; @@ -235,6 +260,7 @@ public function getBeforeQueryCallback(): ?Closure /** * {@inheritdoc} */ + #[\Override] public function setBeforeQueryCallback(?Closure $beforeQueryCallback): static { $this->beforeQueryCallback = $beforeQueryCallback; @@ -245,6 +271,7 @@ public function setBeforeQueryCallback(?Closure $beforeQueryCallback): static /** * {@inheritdoc} */ + #[\Override] public function getAfterQueryCallback(): ?Closure { return $this->afterQueryCallback; @@ -253,6 +280,7 @@ public function getAfterQueryCallback(): ?Closure /** * {@inheritdoc} */ + #[\Override] public function setAfterQueryCallback(?Closure $afterQueryCallback): static { $this->afterQueryCallback = $afterQueryCallback; @@ -263,16 +291,7 @@ public function setAfterQueryCallback(?Closure $afterQueryCallback): static /** * {@inheritdoc} */ - public function setItemTotalCallback(?Closure $itemTotalCallback): static - { - $this->itemTotalCallback = $itemTotalCallback; - - return $this; - } - - /** - * {@inheritdoc} - */ + #[\Override] public function getItemsPerPage(): int { return $this->itemsPerPage; @@ -281,6 +300,7 @@ public function getItemsPerPage(): int /** * {@inheritdoc} */ + #[\Override] public function setItemsPerPage(int $itemsPerPage): static { $this->itemsPerPage = $itemsPerPage; @@ -291,6 +311,7 @@ public function setItemsPerPage(int $itemsPerPage): static /** * {@inheritdoc} */ + #[\Override] public function getPagesInRange(): int { return $this->pagesInRange; @@ -299,6 +320,7 @@ public function getPagesInRange(): int /** * {@inheritdoc} */ + #[\Override] public function setPagesInRange(int $pagesInRange): static { $this->pagesInRange = $pagesInRange; @@ -306,9 +328,47 @@ public function setPagesInRange(int $pagesInRange): static return $this; } - // Helper functions to the main paginate() function. + /** + * Helper function for __construct() to validate the passed $config. + * + * @param null|array{}|array{ + * itemTotalCallback: Closure, + * sliceCallback: Closure, + * itemsPerPage: int, + * pagesInRange: int + * } $config Expected array signature. + * + * @return array{}|array{ + * itemTotalCallback: Closure, + * sliceCallback: Closure, + * itemsPerPage: int, + * pagesInRange: int + * } + */ + protected static function validateConfig(?array $config = null): array + { + static $validKeys = ['itemTotalCallback', 'sliceCallback', 'itemsPerPage', 'pagesInRange']; + + $config ??= []; + + return array_filter($config, static function (mixed $value, string $key) use ($validKeys): bool { + if (!in_array($key, $validKeys, true)) { + return false; + } + + return match($key) { + 'itemTotalCallback', 'sliceCallback' => $value instanceof Closure, + default => is_int($value) + }; + }, ARRAY_FILTER_USE_BOTH); + + } /** + * A helper function to {@see self::paginate()}. + * + * Ensures the beforeQueryCallback is a valid Closure. If the currently set + * beforeQueryCallback is null, it will return an empty Closure object. */ protected function prepareBeforeQueryCallback(): Closure { @@ -320,6 +380,10 @@ protected function prepareBeforeQueryCallback(): Closure } /** + * A helper function to {@see self::paginate()}. + * + * Ensures the afterQueryCallback is a valid Closure. If the currently set + * afterQueryCallback is null, it will return an empty Closure object. */ protected function prepareAfterQueryCallback(): Closure { @@ -331,9 +395,14 @@ protected function prepareAfterQueryCallback(): Closure } /** + * A helper function to {@see self::paginate()}. + * + * Determines the number of pages in range given the current page number, currently + * set pages in range, and total number of pages. + * * @return array */ - protected function determinePageRange(int $currentPageNumber, int $pagesInRange, int $numberOfPages): array + protected static function determinePageRange(int $currentPageNumber, int $pagesInRange, int $numberOfPages): array { $change = (int) ceil($pagesInRange / 2); @@ -352,28 +421,30 @@ protected function determinePageRange(int $currentPageNumber, int $pagesInRange, } /** + * A helper function to {@see self::paginate()}. + * + * Determines the previous page number based on the current page number. */ - protected function determinePreviousPageNumber(int $currentPageNumber): ?int + protected static function determinePreviousPageNumber(int $currentPageNumber): ?int { - $previousPageNumber = null; - if (($currentPageNumber - 1) > 0) { - $previousPageNumber = $currentPageNumber - 1; + return $currentPageNumber - 1; } - return $previousPageNumber; + return null; } /** + * A helper function to {@see self::paginate()}. + * + * Determines the next page number based on the current page number. */ - protected function determineNextPageNumber(int $currentPageNumber, int $numberOfPages): ?int + protected static function determineNextPageNumber(int $currentPageNumber, int $numberOfPages): ?int { - $nextPageNumber = null; - if (($currentPageNumber + 1) <= $numberOfPages) { - $nextPageNumber = $currentPageNumber + 1; + return $currentPageNumber + 1; } - return $nextPageNumber; + return null; } } diff --git a/src/PaginatorInterface.php b/src/PaginatorInterface.php index d3655b8..1ab992c 100644 --- a/src/PaginatorInterface.php +++ b/src/PaginatorInterface.php @@ -6,7 +6,7 @@ * Pagination - Simple, lightweight and universal service that implements pagination on collections of things. * * @author Eric Sizemore - * @version 2.0.0 + * @version 2.0.1 * @copyright (C) 2024 Eric Sizemore * @license The MIT License (MIT) * @@ -42,6 +42,7 @@ namespace Esi\Pagination; use Closure; +use Esi\Pagination\Exception\CallbackNotFoundException; use Esi\Pagination\Exception\InvalidPageNumberException; use InvalidArgumentException; @@ -51,42 +52,89 @@ interface PaginatorInterface { /** - * Run paginate algorithm using the current page number + * Run paginate algorithm using the current page number. * - * @param int $currentPageNumber Page number, usually passed from the current request - * @return Pagination Collection of items returned by the slice callback with pagination meta information - * @throws InvalidArgumentException + * @param int $currentPageNumber Page number, usually passed from the current request. + * @return Pagination Collection of items returned by the slice callback with pagination meta information. + * + * @throws CallbackNotFoundException * @throws InvalidPageNumberException */ public function paginate(int $currentPageNumber = 1): Pagination; - public function getSliceCallback(): ?Closure; + /** + * Returns the currently assigned item total callback, or null if not set. + */ + public function getItemTotalCallback(): ?Closure; - public function setSliceCallback(Closure $sliceCallback): PaginatorInterface; + /** + * Sets the item total callback. Used to determine the total number of items in your collection. + * + * This should be a Closure, and it would be expected to return an integer. For example: + * + * function() use($items): int { + * return count($items); + * } + */ + public function setItemTotalCallback(?Closure $itemTotalCallback): PaginatorInterface; - public function getItemTotalCallback(): ?Closure; + /** + * Returns the currently assigned slice callback, or null if not set. + */ + public function getSliceCallback(): ?Closure; - public function setItemTotalCallback(Closure $itemTotalCallback): PaginatorInterface; + /** + * Sets the slice callback. Actually slices your collection given an **offset** and **length** argument. + * + * This should be a Closure, and it would be expected to return an array. For example: + * + * function (int $offset, int $length) use ($items): array { + * return array_slice($items, $offset, $length); + * } + */ + public function setSliceCallback(?Closure $sliceCallback): PaginatorInterface; + /** + * Returns the currently assigned before query callback, null if not set. + */ public function getBeforeQueryCallback(): ?Closure; - public function setBeforeQueryCallback(Closure $beforeQueryCallback): PaginatorInterface; + /** + * Sets the before query callback. Called before the count and slice queries. + * + * This should be a Closure, and there is no real return or signature expectation. + */ + public function setBeforeQueryCallback(?Closure $beforeQueryCallback): PaginatorInterface; + /** + * Returns the currently assigned after query callback, null if not set. + */ public function getAfterQueryCallback(): ?Closure; - public function setAfterQueryCallback(Closure $afterQueryCallback): PaginatorInterface; + /** + * Sets the after query callback. Called after the count and slice queries. + * + * This should be a Closure, and there is no real return or signature expectation. + */ + public function setAfterQueryCallback(?Closure $afterQueryCallback): PaginatorInterface; + /** + * Returns the number of items per page. + */ public function getItemsPerPage(): int; /** - * @throws InvalidArgumentException + * Sets the number of items per page. */ public function setItemsPerPage(int $itemsPerPage): PaginatorInterface; + /** + * Returns the number of pages in range. + */ public function getPagesInRange(): int; /** - * @throws InvalidArgumentException + * Sets the number of pages in range. */ public function setPagesInRange(int $pagesInRange): PaginatorInterface; } diff --git a/tests/PaginatorTest.php b/tests/PaginatorTest.php index d45b934..f5b0fdb 100644 --- a/tests/PaginatorTest.php +++ b/tests/PaginatorTest.php @@ -6,7 +6,7 @@ * Pagination - Simple, lightweight and universal service that implements pagination on collections of things. * * @author Eric Sizemore - * @version 2.0.0 + * @version 2.0.1 * @copyright (C) 2024 Eric Sizemore * @license The MIT License (MIT) * @@ -41,12 +41,13 @@ namespace Esi\Pagination\Tests; +use ArrayIterator; use Esi\Pagination\Exception\CallbackNotFoundException; use Esi\Pagination\Exception\InvalidPageNumberException; use Esi\Pagination\Pagination; use Esi\Pagination\Paginator; use PDO; -use PDOException; +use PDOStatement; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\UsesClass; use PHPUnit\Framework\TestCase; @@ -66,16 +67,55 @@ #[UsesClass(InvalidPageNumberException::class)] class PaginatorTest extends TestCase { + /** + * Paginator object used throughout testing. + */ protected Paginator $paginator; + /** + * PDO_SQLITE object for the database testing. + */ protected static PDO $dbObj; + /** + * Creates our Paginator and PDO objects. + */ protected function setUp(): void { + parent::setUp(); + $this->paginator = new Paginator(); self::$dbObj = new PDO(sprintf('sqlite:%s/fixtures/factbook.db', __DIR__)); } + /** + */ + public function testConstructionWithInvalidConfig(): void + { + // @phpstan-ignore-next-line + $paginator = new Paginator([ + 'itemTotalCallback' => '', + 'sliceCallback' => '', + 'itemsPerPage' => '', + 'pagesInRange' => '', + 'nonexistent' => '', + ]); + + self::assertNull($paginator->getItemTotalCallback()); + self::assertNull($paginator->getSliceCallback()); + self::assertSame(10, $paginator->getItemsPerPage()); + self::assertSame(5, $paginator->getPagesInRange()); + } + + /** + * Test pagination of database results. This test uses PDO and a SQLite db, though this is perfectly + * valid for MySQL, etc. as well. + * + * Uses factbook.db, which is licensed under the CC0-1.0 (public domain). + * + * @see https://github.com/factbook/factbook.sql/releases + * @see ../fixtures/factbook.db + */ public function testPaginateDbResults(): void { $paginator = new Paginator(); @@ -84,7 +124,7 @@ public function testPaginateDbResults(): void ->setPagesInRange(5); $paginator->setItemTotalCallback(static function (): int { - /** @var \PDOStatement $result */ + /** @var PDOStatement $result */ $result = self::$dbObj->query("SELECT COUNT(*) as totalCount FROM facts"); $row = $result->fetchColumn(); return (int) $row; @@ -92,7 +132,7 @@ public function testPaginateDbResults(): void // Pass our slice callback. $paginator->setSliceCallback(static function (int $offset, int $length): array { - /** @var \PDOStatement $result */ + /** @var PDOStatement $result */ $result = self::$dbObj->query(sprintf('SELECT name, area FROM facts ORDER BY area DESC LIMIT %d, %d', $offset, $length), PDO::FETCH_ASSOC); $collection = []; @@ -103,7 +143,7 @@ public function testPaginateDbResults(): void return $collection; }); - $pagination = $paginator->paginate(1); + $pagination = $paginator->paginate(); self::assertNotEmpty($pagination->getItems()); self::assertSame(261, $pagination->getTotalNumberOfItems()); @@ -366,7 +406,7 @@ public function testBeforeAndAfterQueryCallbacks(): void self::assertFalse($beforeQueryFired); self::assertFalse($afterQueryFired); - $paginator->paginate(1); + $paginator->paginate(); self::assertTrue($beforeQueryFired); // @phpstan-ignore-line self::assertTrue($afterQueryFired); // @phpstan-ignore-line @@ -392,7 +432,7 @@ public function testPaginateLowVolumeConstructorConfig(): void 'pagesInRange' => 5, ]); - $pagination = $paginator->paginate(1); + $pagination = $paginator->paginate(); self::assertCount(10, $pagination->getItems()); self::assertCount(3, $pagination->getPages()); @@ -458,7 +498,7 @@ public function testPaginationIteratorAggregate(): void 'pagesInRange' => 5, ]); - $pagination = $paginator->paginate(1); + $pagination = $paginator->paginate(); self::assertCount(15, $pagination); @@ -491,7 +531,7 @@ public function testPaginateLowVolume(): void return array_slice($items, $offset, $length); }); - $pagination = $this->paginator->paginate(1); + $pagination = $this->paginator->paginate(); self::assertContains('meta_1', $pagination->getMeta()); self::assertContains('meta_2', $pagination->getMeta()); @@ -570,7 +610,7 @@ public function testPaginateHighVolume(): void return array_slice($items, $offset, $length); }); - $pagination = $this->paginator->paginate(1); + $pagination = $this->paginator->paginate(); self::assertContains('meta_3', $pagination->getMeta()); self::assertContains('meta_4', $pagination->getMeta()); @@ -631,7 +671,7 @@ public function testPaginateHighVolume(): void self::assertContains('meta_4', $pagination->getMeta()); } - public function testPaginateNegativeOne(): void + public function testPaginateItemsPerPageNegativeOne(): void { $items = range(0, 1000); @@ -649,7 +689,7 @@ public function testPaginateNegativeOne(): void return array_slice($items, $offset, $length); }); - $pagination = $this->paginator->paginate(1); + $pagination = $this->paginator->paginate(); self::assertContains('meta_3', $pagination->getMeta()); self::assertContains('meta_4', $pagination->getMeta()); @@ -716,12 +756,12 @@ public function testPaginationIterator(): void $paginator = new Paginator([ 'itemTotalCallback' => static fn (): int => count($items), - 'sliceCallback' => static fn (int $offset, int $length): \ArrayIterator => new \ArrayIterator(array_slice($items, $offset, $length)), + 'sliceCallback' => static fn (int $offset, int $length): ArrayIterator => new ArrayIterator(array_slice($items, $offset, $length)), 'itemsPerPage' => 15, 'pagesInRange' => 5, ]); - $pagination = $paginator->paginate(1); + $pagination = $paginator->paginate(); self::assertCount(15, $pagination); @@ -732,8 +772,6 @@ public function testPaginationIterator(): void public function testItemTotalCallbackNotFound(): void { - $items = range(0, 27); - $this->paginator->setItemsPerPage(10)->setPagesInRange(5); $this->expectException(CallbackNotFoundException::class); @@ -744,8 +782,6 @@ public function testItemTotalCallbackNotFound(): void public function testSliceCallbackNotFound(): void { - $items = range(0, 27); - $this->paginator->setItemsPerPage(10)->setPagesInRange(5); $this->expectException(CallbackNotFoundException::class);