From 1668337164f05fe383cf75a817e98d6feec9c059 Mon Sep 17 00:00:00 2001 From: Priyadi Iman Nurcahyo <1102197+priyadi@users.noreply.github.com> Date: Tue, 23 Jul 2024 02:01:42 +0700 Subject: [PATCH] feat: offset pagination support (#132) * feat: offset pagination support * add tests * orm * update --- CHANGELOG.md | 3 +- Makefile | 2 +- composer.json | 8 ++--- packages/collections-common/composer.json | 2 +- .../collections-common/src/Configuration.php | 2 ++ .../collections-common/src/Pagination.php | 20 ++++++++++++ packages/collections-contracts/composer.json | 2 +- packages/collections-domain/composer.json | 6 ++-- .../src/CriteriaPageable.php | 5 +++ .../src/CriteriaRecollection.php | 5 +++ .../src/MinimalCriteriaRecollection.php | 5 +++ .../src/MinimalRecollectionDecorator.php | 5 +++ .../src/RecollectionDecorator.php | 5 +++ .../src/Trait/RecollectionPageableTrait.php | 20 +++++++++--- packages/collections-orm/composer.json | 6 ++-- .../src/AbstractMinimalRepository.php | 2 ++ .../src/AbstractRepository.php | 2 ++ .../collections-orm/src/QueryPageable.php | 6 ++++ .../collections-orm/src/QueryRecollection.php | 6 ++++ .../src/Trait/QueryBuilderPageableTrait.php | 20 +++++++++--- tests/src/App/Entity/Country.php | 32 +++++++++++++------ .../Base/BaseRecollectionTestCase.php | 7 ++++ .../CriteriaRecollectionLargeSetTest.php | 4 ++- .../CriteriaRecollectionMediumSetTest.php | 4 ++- .../CriteriaRecollectionSmallSetTest.php | 4 ++- .../MinimalCriteriaRecollectionTest.php | 4 ++- .../MinimalRecollectionDecoratorTest.php | 4 ++- .../RecollectionDecoratorMediumSetTest.php | 4 ++- .../RecollectionDecoratorSmallSetTest.php | 2 +- .../Domain/RecollectionDecoratorTest.php | 6 ++-- ...CriteriaRecollectionLargeSetOffsetTest.php | 26 +++++++++++++++ ...riteriaRecollectionMediumSetOffsetTest.php | 26 +++++++++++++++ ...CriteriaRecollectionSmallSetOffsetTest.php | 26 +++++++++++++++ .../MinimalCriteriaRecollectionOffsetTest.php | 26 +++++++++++++++ ...MinimalRecollectionDecoratorOffsetTest.php | 26 +++++++++++++++ ...collectionDecoratorMediumSetOffsetTest.php | 26 +++++++++++++++ .../RecollectionDecoratorOffsetTest.php | 26 +++++++++++++++ ...ecollectionDecoratorSmallSetOffsetTest.php | 26 +++++++++++++++ .../Trait/PageableTestsTrait.php | 25 +++++++++++++++ 39 files changed, 393 insertions(+), 43 deletions(-) create mode 100644 packages/collections-common/src/Pagination.php create mode 100644 tests/src/IntegrationTests/DomainOffset/CriteriaRecollectionLargeSetOffsetTest.php create mode 100644 tests/src/IntegrationTests/DomainOffset/CriteriaRecollectionMediumSetOffsetTest.php create mode 100644 tests/src/IntegrationTests/DomainOffset/CriteriaRecollectionSmallSetOffsetTest.php create mode 100644 tests/src/IntegrationTests/DomainOffset/MinimalCriteriaRecollectionOffsetTest.php create mode 100644 tests/src/IntegrationTests/DomainOffset/MinimalRecollectionDecoratorOffsetTest.php create mode 100644 tests/src/IntegrationTests/DomainOffset/RecollectionDecoratorMediumSetOffsetTest.php create mode 100644 tests/src/IntegrationTests/DomainOffset/RecollectionDecoratorOffsetTest.php create mode 100644 tests/src/IntegrationTests/DomainOffset/RecollectionDecoratorSmallSetOffsetTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e5aacd..2e33a5c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,9 @@ # Changelog -## 0.10.1 +## 0.11.0 * docs: update readme +* feat: offset pagination support ## 0.10.0 diff --git a/Makefile b/Makefile index c3e6737..032da4f 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ test: composer-dump phpstan psalm phpunit .PHONY: composer-dump composer-dump: - $(COMPOSER) dump-autoload + $(COMPOSER) dump-autoload --optimize .PHONY: phpstan phpstan: diff --git a/composer.json b/composer.json index 9e1fadb..00ba7b9 100644 --- a/composer.json +++ b/composer.json @@ -27,10 +27,10 @@ "doctrine/collections": "^2.2.2 || ^2.3", "doctrine/orm": "^2.14 || ^3.0", "php": "^8.2", - "rekalogika/rekapager-contracts": "^0.14", - "rekalogika/rekapager-doctrine-collections-adapter": "^0.14", - "rekalogika/rekapager-doctrine-orm-adapter": "^0.14", - "rekalogika/rekapager-keyset-pagination": "^0.14" + "rekalogika/rekapager-contracts": "^0.15", + "rekalogika/rekapager-doctrine-collections-adapter": "^0.15", + "rekalogika/rekapager-doctrine-orm-adapter": "^0.15", + "rekalogika/rekapager-keyset-pagination": "^0.15" }, "require-dev": { "ekino/phpstan-banned-code": "^1.0", diff --git a/packages/collections-common/composer.json b/packages/collections-common/composer.json index d184595..adc44a5 100644 --- a/packages/collections-common/composer.json +++ b/packages/collections-common/composer.json @@ -35,6 +35,6 @@ "require": { "php": "^8.2", "doctrine/collections": "^2.2.2 || ^2.3", - "rekalogika/rekapager-contracts": "^0.14" + "rekalogika/rekapager-contracts": "^0.15" } } diff --git a/packages/collections-common/src/Configuration.php b/packages/collections-common/src/Configuration.php index 32c996d..16bb4ce 100644 --- a/packages/collections-common/src/Configuration.php +++ b/packages/collections-common/src/Configuration.php @@ -103,4 +103,6 @@ private function __construct() * @var int<1,max> */ public static int $defaultItemsPerPage = 50; + + public static Pagination $defaultPagination = Pagination::Keyset; } diff --git a/packages/collections-common/src/Pagination.php b/packages/collections-common/src/Pagination.php new file mode 100644 index 0000000..fa86903 --- /dev/null +++ b/packages/collections-common/src/Pagination.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE file + * that was distributed with this source code. + */ + +namespace Rekalogika\Domain\Collections\Common; + +enum Pagination +{ + case Keyset; + case Offset; +} diff --git a/packages/collections-contracts/composer.json b/packages/collections-contracts/composer.json index 8af3e04..43a185d 100644 --- a/packages/collections-contracts/composer.json +++ b/packages/collections-contracts/composer.json @@ -35,6 +35,6 @@ "require": { "php": "^8.2", "doctrine/collections": "^2.2.2 || ^2.3", - "rekalogika/rekapager-contracts": "^0.14" + "rekalogika/rekapager-contracts": "^0.15" } } diff --git a/packages/collections-domain/composer.json b/packages/collections-domain/composer.json index ee6b83e..c964f9d 100644 --- a/packages/collections-domain/composer.json +++ b/packages/collections-domain/composer.json @@ -37,8 +37,8 @@ "doctrine/collections": "^2.2.2 || ^2.3", "rekalogika/collections-common": "^0.10", "rekalogika/collections-contracts": "^0.10", - "rekalogika/rekapager-contracts": "^0.14", - "rekalogika/rekapager-doctrine-collections-adapter": "^0.14", - "rekalogika/rekapager-keyset-pagination": "^0.14" + "rekalogika/rekapager-contracts": "^0.15", + "rekalogika/rekapager-doctrine-collections-adapter": "^0.15", + "rekalogika/rekapager-keyset-pagination": "^0.15" } } diff --git a/packages/collections-domain/src/CriteriaPageable.php b/packages/collections-domain/src/CriteriaPageable.php index a4429f5..4245689 100644 --- a/packages/collections-domain/src/CriteriaPageable.php +++ b/packages/collections-domain/src/CriteriaPageable.php @@ -21,6 +21,7 @@ use Rekalogika\Domain\Collections\Common\Configuration; use Rekalogika\Domain\Collections\Common\Count\CountStrategy; use Rekalogika\Domain\Collections\Common\Internal\ParameterUtil; +use Rekalogika\Domain\Collections\Common\Pagination; use Rekalogika\Domain\Collections\Common\Trait\PageableTrait; use Rekalogika\Domain\Collections\Common\Trait\RefreshCountTrait; use Rekalogika\Domain\Collections\Trait\RecollectionPageableTrait; @@ -69,6 +70,7 @@ final private function __construct( ?string $indexBy = null, ?int $itemsPerPage = null, private readonly ?CountStrategy $count = null, + private readonly ?Pagination $pagination = null, ) { $this->indexBy = $indexBy ?? Configuration::$defaultIndexBy; $this->itemsPerPage = $itemsPerPage ?? Configuration::$defaultItemsPerPage; @@ -105,6 +107,7 @@ final public static function create( ?string $indexBy = null, int $itemsPerPage = 50, ?CountStrategy $count = null, + ?Pagination $pagination = null, ): static { if (self::$instances === null) { /** @var \WeakMap>> */ @@ -117,6 +120,7 @@ final public static function create( $instanceId ?? $criteria, $indexBy, $itemsPerPage, + $pagination, ])); if (isset(self::$instances[$collection][$cacheKey])) { @@ -131,6 +135,7 @@ final public static function create( indexBy: $indexBy, itemsPerPage: $itemsPerPage, count: $count, + pagination: $pagination, ); if (!isset(self::$instances[$collection])) { diff --git a/packages/collections-domain/src/CriteriaRecollection.php b/packages/collections-domain/src/CriteriaRecollection.php index af8fffa..857235e 100644 --- a/packages/collections-domain/src/CriteriaRecollection.php +++ b/packages/collections-domain/src/CriteriaRecollection.php @@ -23,6 +23,7 @@ use Rekalogika\Domain\Collections\Common\Count\CountStrategy; use Rekalogika\Domain\Collections\Common\Internal\ParameterUtil; use Rekalogika\Domain\Collections\Common\KeyTransformer\KeyTransformer; +use Rekalogika\Domain\Collections\Common\Pagination; use Rekalogika\Domain\Collections\Common\Trait\ReadableRecollectionTrait; use Rekalogika\Domain\Collections\Common\Trait\SafeCollectionTrait; use Rekalogika\Domain\Collections\Trait\CriteriaReadableTrait; @@ -90,6 +91,7 @@ final private function __construct( private readonly ?int $softLimit = null, private readonly ?int $hardLimit = null, private readonly ?KeyTransformer $keyTransformer = null, + private readonly ?Pagination $pagination = null, ) { $this->indexBy = $indexBy ?? Configuration::$defaultIndexBy; $this->itemsPerPage = $itemsPerPage ?? Configuration::$defaultItemsPerPage; @@ -132,6 +134,7 @@ final public static function create( ?int $softLimit = null, ?int $hardLimit = null, ?KeyTransformer $keyTransformer = null, + ?Pagination $pagination = null, ): ReadableRecollection { if (self::$instances === null) { /** @var \WeakMap>> */ @@ -144,6 +147,7 @@ final public static function create( $instanceId ?? $criteria, $indexBy, $itemsPerPage, + $pagination, ])); if (isset(self::$instances[$collection][$cacheKey])) { @@ -161,6 +165,7 @@ final public static function create( softLimit: $softLimit, hardLimit: $hardLimit, keyTransformer: $keyTransformer, + pagination: $pagination, ); if (!isset(self::$instances[$collection])) { diff --git a/packages/collections-domain/src/MinimalCriteriaRecollection.php b/packages/collections-domain/src/MinimalCriteriaRecollection.php index 42f7c9c..094f07a 100644 --- a/packages/collections-domain/src/MinimalCriteriaRecollection.php +++ b/packages/collections-domain/src/MinimalCriteriaRecollection.php @@ -22,6 +22,7 @@ use Rekalogika\Domain\Collections\Common\Count\CountStrategy; use Rekalogika\Domain\Collections\Common\Internal\ParameterUtil; use Rekalogika\Domain\Collections\Common\KeyTransformer\KeyTransformer; +use Rekalogika\Domain\Collections\Common\Pagination; use Rekalogika\Domain\Collections\Common\Trait\MinimalReadableRecollectionTrait; use Rekalogika\Domain\Collections\Common\Trait\SafeCollectionTrait; use Rekalogika\Domain\Collections\Trait\RecollectionPageableTrait; @@ -72,6 +73,7 @@ final private function __construct( ?int $itemsPerPage = null, private readonly ?CountStrategy $count = null, private readonly ?KeyTransformer $keyTransformer = null, + private readonly ?Pagination $pagination = null, ) { $this->indexBy = $indexBy ?? Configuration::$defaultIndexBy; $this->itemsPerPage = $itemsPerPage ?? Configuration::$defaultItemsPerPage; @@ -128,6 +130,7 @@ final public static function create( int $itemsPerPage = 50, ?CountStrategy $count = null, ?KeyTransformer $keyTransformer = null, + ?Pagination $pagination = null, ): MinimalReadableRecollection { if (self::$instances === null) { /** @var \WeakMap>> */ @@ -140,6 +143,7 @@ final public static function create( $instanceId ?? $criteria, $indexBy, $itemsPerPage, + $pagination, ])); if (isset(self::$instances[$collection][$cacheKey])) { @@ -155,6 +159,7 @@ final public static function create( itemsPerPage: $itemsPerPage, count: $count, keyTransformer: $keyTransformer, + pagination: $pagination, ); if (!isset(self::$instances[$collection])) { diff --git a/packages/collections-domain/src/MinimalRecollectionDecorator.php b/packages/collections-domain/src/MinimalRecollectionDecorator.php index 2d8a47c..5af90e5 100644 --- a/packages/collections-domain/src/MinimalRecollectionDecorator.php +++ b/packages/collections-domain/src/MinimalRecollectionDecorator.php @@ -24,6 +24,7 @@ use Rekalogika\Domain\Collections\Common\Count\CountStrategy; use Rekalogika\Domain\Collections\Common\Internal\ParameterUtil; use Rekalogika\Domain\Collections\Common\KeyTransformer\KeyTransformer; +use Rekalogika\Domain\Collections\Common\Pagination; use Rekalogika\Domain\Collections\Common\Trait\MinimalRecollectionTrait; use Rekalogika\Domain\Collections\Trait\RecollectionPageableTrait; @@ -76,6 +77,7 @@ final private function __construct( ?int $itemsPerPage = null, private readonly ?CountStrategy $count = null, private readonly ?KeyTransformer $keyTransformer = null, + private readonly ?Pagination $pagination = null, ) { $this->indexBy = $indexBy ?? Configuration::$defaultIndexBy; $this->itemsPerPage = $itemsPerPage ?? Configuration::$defaultItemsPerPage; @@ -113,6 +115,7 @@ final public static function create( int $itemsPerPage = 50, ?CountStrategy $count = null, ?KeyTransformer $keyTransformer = null, + ?Pagination $pagination = null, ): MinimalRecollection { if (self::$instances === null) { /** @var \WeakMap>> */ @@ -125,6 +128,7 @@ final public static function create( $orderBy, $indexBy, $itemsPerPage, + $pagination, ])); if (isset(self::$instances[$collection][$cacheKey])) { @@ -140,6 +144,7 @@ final public static function create( itemsPerPage: $itemsPerPage, count: $count, keyTransformer: $keyTransformer, + pagination: $pagination, ); if (!isset(self::$instances[$collection])) { diff --git a/packages/collections-domain/src/RecollectionDecorator.php b/packages/collections-domain/src/RecollectionDecorator.php index e725ba4..171d8a8 100644 --- a/packages/collections-domain/src/RecollectionDecorator.php +++ b/packages/collections-domain/src/RecollectionDecorator.php @@ -23,6 +23,7 @@ use Rekalogika\Domain\Collections\Common\Count\CountStrategy; use Rekalogika\Domain\Collections\Common\Internal\ParameterUtil; use Rekalogika\Domain\Collections\Common\KeyTransformer\KeyTransformer; +use Rekalogika\Domain\Collections\Common\Pagination; use Rekalogika\Domain\Collections\Common\Trait\RecollectionTrait; use Rekalogika\Domain\Collections\Common\Trait\SafeCollectionTrait; use Rekalogika\Domain\Collections\Trait\ExtraLazyTrait; @@ -100,6 +101,7 @@ final private function __construct( private readonly ?int $softLimit = null, private readonly ?int $hardLimit = null, private readonly ?KeyTransformer $keyTransformer = null, + private readonly ?Pagination $pagination = null, ) { $this->indexBy = $indexBy ?? Configuration::$defaultIndexBy; $this->itemsPerPage = $itemsPerPage ?? Configuration::$defaultItemsPerPage; @@ -141,6 +143,7 @@ final public static function create( ?int $softLimit = null, ?int $hardLimit = null, ?KeyTransformer $keyTransformer = null, + ?Pagination $pagination = null, ): Recollection { if (self::$instances === null) { /** @var \WeakMap>> */ @@ -153,6 +156,7 @@ final public static function create( $orderBy, $indexBy, $itemsPerPage, + $pagination, ])); if (isset(self::$instances[$collection][$cacheKey])) { @@ -170,6 +174,7 @@ final public static function create( softLimit: $softLimit, hardLimit: $hardLimit, keyTransformer: $keyTransformer, + pagination: $pagination, ); if (!isset(self::$instances[$collection])) { diff --git a/packages/collections-domain/src/Trait/RecollectionPageableTrait.php b/packages/collections-domain/src/Trait/RecollectionPageableTrait.php index 4b9b9d9..f402e6d 100644 --- a/packages/collections-domain/src/Trait/RecollectionPageableTrait.php +++ b/packages/collections-domain/src/Trait/RecollectionPageableTrait.php @@ -14,9 +14,12 @@ namespace Rekalogika\Domain\Collections\Trait; use Rekalogika\Contracts\Rekapager\PageableInterface; +use Rekalogika\Domain\Collections\Common\Configuration; use Rekalogika\Domain\Collections\Common\Exception\GettingCountUnsupportedException; +use Rekalogika\Domain\Collections\Common\Pagination; use Rekalogika\Rekapager\Doctrine\Collections\SelectableAdapter; use Rekalogika\Rekapager\Keyset\KeysetPageable; +use Rekalogika\Rekapager\Offset\OffsetPageable; /** * @template TKey of array-key @@ -59,11 +62,18 @@ private function getPageable(): PageableInterface } }; - $this->pageable = new KeysetPageable( - adapter: $adapter, - itemsPerPage: $this->itemsPerPage, - count: $count, - ); + $this->pageable = match ($this->pagination ?? Configuration::$defaultPagination) { + Pagination::Keyset => new KeysetPageable( + adapter: $adapter, + itemsPerPage: $this->itemsPerPage, + count: $count, + ), + Pagination::Offset => new OffsetPageable( + adapter: $adapter, + itemsPerPage: $this->itemsPerPage, + count: $count, + ), + }; return $this->pageable; } diff --git a/packages/collections-orm/composer.json b/packages/collections-orm/composer.json index b60a5f7..8d3b38b 100644 --- a/packages/collections-orm/composer.json +++ b/packages/collections-orm/composer.json @@ -39,8 +39,8 @@ "rekalogika/collections-common": "^0.10", "rekalogika/collections-contracts": "^0.10", "rekalogika/collections-domain": "^0.10", - "rekalogika/rekapager-contracts": "^0.14", - "rekalogika/rekapager-doctrine-orm-adapter": "^0.14", - "rekalogika/rekapager-keyset-pagination": "^0.14" + "rekalogika/rekapager-contracts": "^0.15", + "rekalogika/rekapager-doctrine-orm-adapter": "^0.15", + "rekalogika/rekapager-keyset-pagination": "^0.15" } } diff --git a/packages/collections-orm/src/AbstractMinimalRepository.php b/packages/collections-orm/src/AbstractMinimalRepository.php index 22485eb..0dbfa51 100644 --- a/packages/collections-orm/src/AbstractMinimalRepository.php +++ b/packages/collections-orm/src/AbstractMinimalRepository.php @@ -26,6 +26,7 @@ use Rekalogika\Domain\Collections\Common\Configuration; use Rekalogika\Domain\Collections\Common\Count\CountStrategy; use Rekalogika\Domain\Collections\Common\Internal\ParameterUtil; +use Rekalogika\Domain\Collections\Common\Pagination; /** * @template TKey of array-key @@ -76,6 +77,7 @@ public function __construct( array|string|null $orderBy = null, ?int $itemsPerPage = null, private readonly ?CountStrategy $count = null, + private readonly ?Pagination $pagination = null, ) { $this->itemsPerPage = $itemsPerPage ?? Configuration::$defaultItemsPerPage; diff --git a/packages/collections-orm/src/AbstractRepository.php b/packages/collections-orm/src/AbstractRepository.php index e952916..214d05a 100644 --- a/packages/collections-orm/src/AbstractRepository.php +++ b/packages/collections-orm/src/AbstractRepository.php @@ -27,6 +27,7 @@ use Rekalogika\Domain\Collections\Common\Count\CountStrategy; use Rekalogika\Domain\Collections\Common\Internal\ParameterUtil; use Rekalogika\Domain\Collections\Common\KeyTransformer\KeyTransformer; +use Rekalogika\Domain\Collections\Common\Pagination; use Rekalogika\Domain\Collections\Common\Trait\SafeCollectionTrait; /** @@ -88,6 +89,7 @@ public function __construct( private readonly ?int $softLimit = null, private readonly ?int $hardLimit = null, private readonly ?KeyTransformer $keyTransformer = null, + private readonly ?Pagination $pagination = null, ) { $this->itemsPerPage = $itemsPerPage ?? Configuration::$defaultItemsPerPage; diff --git a/packages/collections-orm/src/QueryPageable.php b/packages/collections-orm/src/QueryPageable.php index 6eeb860..d496ab9 100644 --- a/packages/collections-orm/src/QueryPageable.php +++ b/packages/collections-orm/src/QueryPageable.php @@ -19,6 +19,7 @@ use Rekalogika\Domain\Collections\Common\Configuration; use Rekalogika\Domain\Collections\Common\Count\CountStrategy; use Rekalogika\Domain\Collections\Common\Internal\ParameterUtil; +use Rekalogika\Domain\Collections\Common\Pagination; use Rekalogika\Domain\Collections\Common\Trait\PageableTrait; use Rekalogika\Domain\Collections\Common\Trait\RefreshCountTrait; @@ -52,6 +53,7 @@ public function __construct( ?string $indexBy = null, ?int $itemsPerPage = null, private readonly ?CountStrategy $count = null, + private readonly ?Pagination $pagination = null, ) { $this->indexBy = $indexBy ?? Configuration::$defaultIndexBy; $this->itemsPerPage = $itemsPerPage ?? Configuration::$defaultItemsPerPage; @@ -106,6 +108,7 @@ final protected function createQueryRecollection( QueryBuilder $queryBuilder, ?string $indexBy = null, ?CountStrategy $count = null, + ?Pagination $pagination = null, ): self { /** * @var self @@ -116,6 +119,7 @@ final protected function createQueryRecollection( indexBy: $indexBy ?? $this->indexBy, count: $count, itemsPerPage: $this->itemsPerPage, + pagination: $pagination, ); } @@ -126,6 +130,7 @@ final protected function createQueryPageable( QueryBuilder $queryBuilder, ?string $indexBy = null, ?CountStrategy $count = null, + ?Pagination $pagination = null, ): PageableRecollection { /** * @var PageableRecollection @@ -136,6 +141,7 @@ final protected function createQueryPageable( itemsPerPage: $this->itemsPerPage, indexBy: $indexBy ?? $this->indexBy, count: $count, + pagination: $pagination, ); } } diff --git a/packages/collections-orm/src/QueryRecollection.php b/packages/collections-orm/src/QueryRecollection.php index 5c76bb5..b4dc43a 100644 --- a/packages/collections-orm/src/QueryRecollection.php +++ b/packages/collections-orm/src/QueryRecollection.php @@ -21,6 +21,7 @@ use Rekalogika\Domain\Collections\Common\Count\CountStrategy; use Rekalogika\Domain\Collections\Common\Internal\ParameterUtil; use Rekalogika\Domain\Collections\Common\KeyTransformer\KeyTransformer; +use Rekalogika\Domain\Collections\Common\Pagination; use Rekalogika\Domain\Collections\Common\Trait\PageableTrait; use Rekalogika\Domain\Collections\Common\Trait\ReadableCollectionTrait; use Rekalogika\Domain\Collections\Common\Trait\ReadableRecollectionTrait; @@ -68,6 +69,7 @@ public function __construct( private readonly ?int $softLimit = null, private readonly ?int $hardLimit = null, private readonly ?KeyTransformer $keyTransformer = null, + private readonly ?Pagination $pagination = null, ) { $this->indexBy = $indexBy ?? Configuration::$defaultIndexBy; $this->itemsPerPage = $itemsPerPage ?? Configuration::$defaultItemsPerPage; @@ -139,6 +141,7 @@ final protected function createQueryRecollection( QueryBuilder $queryBuilder, ?string $indexBy = null, ?CountStrategy $count = null, + ?Pagination $pagination = null, ): self { /** @var QueryRecollection */ return new QueryRecollection( @@ -148,6 +151,7 @@ final protected function createQueryRecollection( itemsPerPage: $this->itemsPerPage, softLimit: $this->softLimit, hardLimit: $this->hardLimit, + pagination: $pagination, ); } @@ -158,6 +162,7 @@ final protected function createQueryPageable( QueryBuilder $queryBuilder, ?string $indexBy = null, ?CountStrategy $count = null, + ?Pagination $pagination = null, ): PageableRecollection { /** * @var PageableRecollection @@ -168,6 +173,7 @@ final protected function createQueryPageable( itemsPerPage: $this->itemsPerPage, indexBy: $indexBy ?? $this->indexBy, count: $count, + pagination: $pagination, ); } } diff --git a/packages/collections-orm/src/Trait/QueryBuilderPageableTrait.php b/packages/collections-orm/src/Trait/QueryBuilderPageableTrait.php index c34dbe6..dfb341a 100644 --- a/packages/collections-orm/src/Trait/QueryBuilderPageableTrait.php +++ b/packages/collections-orm/src/Trait/QueryBuilderPageableTrait.php @@ -15,9 +15,12 @@ use Doctrine\ORM\Tools\Pagination\Paginator; use Rekalogika\Contracts\Rekapager\PageableInterface; +use Rekalogika\Domain\Collections\Common\Configuration; use Rekalogika\Domain\Collections\Common\Exception\GettingCountUnsupportedException; +use Rekalogika\Domain\Collections\Common\Pagination; use Rekalogika\Rekapager\Doctrine\ORM\QueryBuilderAdapter; use Rekalogika\Rekapager\Keyset\KeysetPageable; +use Rekalogika\Rekapager\Offset\OffsetPageable; /** * @template TKey of array-key @@ -55,11 +58,18 @@ private function getPageable(): PageableInterface }; // @phpstan-ignore-next-line - $this->pageable = new KeysetPageable( - adapter: $adapter, - itemsPerPage: $this->itemsPerPage, - count: $count, - ); + $this->pageable = match ($this->pagination ?? Configuration::$defaultPagination) { + Pagination::Keyset => new KeysetPageable( + adapter: $adapter, + itemsPerPage: $this->itemsPerPage, + count: $count, + ), + Pagination::Offset => new OffsetPageable( + adapter: $adapter, + itemsPerPage: $this->itemsPerPage, + count: $count, + ), + }; // @phpstan-ignore-next-line return $this->pageable; diff --git a/tests/src/App/Entity/Country.php b/tests/src/App/Entity/Country.php index 17fdc08..b2dea0d 100644 --- a/tests/src/App/Entity/Country.php +++ b/tests/src/App/Entity/Country.php @@ -22,6 +22,8 @@ use Rekalogika\Contracts\Collections\ReadableRecollection; use Rekalogika\Contracts\Collections\Recollection; use Rekalogika\Domain\Collections\ArrayCollection; +use Rekalogika\Domain\Collections\Common\Count\DisabledCountStrategy; +use Rekalogika\Domain\Collections\Common\Pagination; use Rekalogika\Domain\Collections\CriteriaRecollection; use Rekalogika\Domain\Collections\MinimalCriteriaRecollection; use Rekalogika\Domain\Collections\MinimalRecollectionDecorator; @@ -87,48 +89,58 @@ private function getWorkingAgeCriteria(): Criteria /** * @return Recollection */ - public function getCitizensInRecollection(): Recollection - { + public function getCitizensInRecollection( + Pagination $pagination + ): Recollection { return RecollectionDecorator::create( collection: $this->citizens, - indexBy: 'id' + indexBy: 'id', + pagination: $pagination, + count: new DisabledCountStrategy() ); } /** * @return MinimalRecollection */ - public function getCitizensInMinimalRecollection(): MinimalRecollection - { + public function getCitizensInMinimalRecollection( + Pagination $pagination + ): MinimalRecollection { return MinimalRecollectionDecorator::create( collection: $this->citizens, - indexBy: 'id' + indexBy: 'id', + pagination: $pagination, + count: new DisabledCountStrategy() ); } /** * @return ReadableRecollection */ - public function getWorkingAgeCitizensInRecollection(): ReadableRecollection - { + public function getWorkingAgeCitizensInRecollection( + Pagination $pagination + ): ReadableRecollection { return CriteriaRecollection::create( collection: $this->citizens, criteria: $this->getWorkingAgeCriteria(), indexBy: 'id', instanceId: __METHOD__, + pagination: $pagination, ); } /** * @return MinimalReadableRecollection */ - public function getWorkingAgeCitizensInMinimalRecollection(): MinimalReadableRecollection - { + public function getWorkingAgeCitizensInMinimalRecollection( + Pagination $pagination + ): MinimalReadableRecollection { return MinimalCriteriaRecollection::create( collection: $this->citizens, criteria: $this->getWorkingAgeCriteria(), indexBy: 'id', instanceId: __METHOD__, + pagination: $pagination, ); } diff --git a/tests/src/IntegrationTests/Base/BaseRecollectionTestCase.php b/tests/src/IntegrationTests/Base/BaseRecollectionTestCase.php index befadfe..3427f1f 100644 --- a/tests/src/IntegrationTests/Base/BaseRecollectionTestCase.php +++ b/tests/src/IntegrationTests/Base/BaseRecollectionTestCase.php @@ -17,10 +17,12 @@ use Rekalogika\Collections\Tests\App\Entity\Citizen; use Rekalogika\Contracts\Collections\Exception\OverflowException; use Rekalogika\Contracts\Rekapager\PageableInterface; +use Rekalogika\Domain\Collections\Common\Pagination; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; /** * @template-covariant R of object + * @runTestsInSeparateProcesses */ abstract class BaseRecollectionTestCase extends KernelTestCase { @@ -35,6 +37,11 @@ abstract protected function isSingleton(): bool; abstract protected function getExpectedTotal(): int; + protected function getPaginationType(): Pagination + { + return Pagination::Keyset; + } + protected function getEntityManager(): EntityManagerInterface { return static::getContainer()->get(EntityManagerInterface::class); diff --git a/tests/src/IntegrationTests/Domain/CriteriaRecollectionLargeSetTest.php b/tests/src/IntegrationTests/Domain/CriteriaRecollectionLargeSetTest.php index 4f66dfb..65cfe8f 100644 --- a/tests/src/IntegrationTests/Domain/CriteriaRecollectionLargeSetTest.php +++ b/tests/src/IntegrationTests/Domain/CriteriaRecollectionLargeSetTest.php @@ -58,7 +58,9 @@ protected function getObject(): ReadableRecollection /** @var MinimalRepository $repository */ $country = $repository->fetch(1); - $citizens = $country->getWorkingAgeCitizensInRecollection(); + $citizens = $country->getWorkingAgeCitizensInRecollection( + $this->getPaginationType() + ); static::assertInstanceOf(ReadableRecollection::class, $citizens); diff --git a/tests/src/IntegrationTests/Domain/CriteriaRecollectionMediumSetTest.php b/tests/src/IntegrationTests/Domain/CriteriaRecollectionMediumSetTest.php index aa80375..1aa2ec2 100644 --- a/tests/src/IntegrationTests/Domain/CriteriaRecollectionMediumSetTest.php +++ b/tests/src/IntegrationTests/Domain/CriteriaRecollectionMediumSetTest.php @@ -58,7 +58,9 @@ protected function getObject(): ReadableRecollection /** @var MinimalRepository $repository */ $country = $repository->fetch(2); - $citizens = $country->getWorkingAgeCitizensInRecollection(); + $citizens = $country->getWorkingAgeCitizensInRecollection( + $this->getPaginationType() + ); static::assertInstanceOf(ReadableRecollection::class, $citizens); diff --git a/tests/src/IntegrationTests/Domain/CriteriaRecollectionSmallSetTest.php b/tests/src/IntegrationTests/Domain/CriteriaRecollectionSmallSetTest.php index dfea7ae..51761e4 100644 --- a/tests/src/IntegrationTests/Domain/CriteriaRecollectionSmallSetTest.php +++ b/tests/src/IntegrationTests/Domain/CriteriaRecollectionSmallSetTest.php @@ -58,7 +58,9 @@ protected function getObject(): ReadableRecollection /** @var MinimalRepository $repository */ $country = $repository->fetch(3); - $citizens = $country->getWorkingAgeCitizensInRecollection(); + $citizens = $country->getWorkingAgeCitizensInRecollection( + $this->getPaginationType() + ); static::assertInstanceOf(ReadableRecollection::class, $citizens); diff --git a/tests/src/IntegrationTests/Domain/MinimalCriteriaRecollectionTest.php b/tests/src/IntegrationTests/Domain/MinimalCriteriaRecollectionTest.php index 128df09..e452569 100644 --- a/tests/src/IntegrationTests/Domain/MinimalCriteriaRecollectionTest.php +++ b/tests/src/IntegrationTests/Domain/MinimalCriteriaRecollectionTest.php @@ -56,7 +56,9 @@ protected function getObject(): MinimalReadableRecollection /** @var MinimalRepository $repository */ $country = $repository->fetch(2); - $citizens = $country->getWorkingAgeCitizensInMinimalRecollection(); + $citizens = $country->getWorkingAgeCitizensInMinimalRecollection( + $this->getPaginationType() + ); static::assertInstanceOf(MinimalReadableRecollection::class, $citizens); diff --git a/tests/src/IntegrationTests/Domain/MinimalRecollectionDecoratorTest.php b/tests/src/IntegrationTests/Domain/MinimalRecollectionDecoratorTest.php index f226737..2e7814c 100644 --- a/tests/src/IntegrationTests/Domain/MinimalRecollectionDecoratorTest.php +++ b/tests/src/IntegrationTests/Domain/MinimalRecollectionDecoratorTest.php @@ -57,7 +57,9 @@ protected function getObject(): MinimalRecollection /** @var MinimalRepository $repository */ $country = $repository->fetch(1); - $citizens = $country->getCitizensInMinimalRecollection(); + $citizens = $country->getCitizensInMinimalRecollection( + $this->getPaginationType() + ); static::assertInstanceOf(MinimalRecollection::class, $citizens); static::assertInstanceOf(PersistentCollection::class, $country->getRawCitizens()); diff --git a/tests/src/IntegrationTests/Domain/RecollectionDecoratorMediumSetTest.php b/tests/src/IntegrationTests/Domain/RecollectionDecoratorMediumSetTest.php index 2b2db36..1852707 100644 --- a/tests/src/IntegrationTests/Domain/RecollectionDecoratorMediumSetTest.php +++ b/tests/src/IntegrationTests/Domain/RecollectionDecoratorMediumSetTest.php @@ -57,7 +57,9 @@ protected function getObject(): Recollection /** @var MinimalRepository $repository */ $country = $repository->fetch(2); - $citizens = $country->getCitizensInRecollection(); + $citizens = $country->getCitizensInRecollection( + $this->getPaginationType() + ); static::assertInstanceOf(Recollection::class, $citizens); static::assertInstanceOf(PersistentCollection::class, $country->getRawCitizens()); diff --git a/tests/src/IntegrationTests/Domain/RecollectionDecoratorSmallSetTest.php b/tests/src/IntegrationTests/Domain/RecollectionDecoratorSmallSetTest.php index bb1d859..a96f997 100644 --- a/tests/src/IntegrationTests/Domain/RecollectionDecoratorSmallSetTest.php +++ b/tests/src/IntegrationTests/Domain/RecollectionDecoratorSmallSetTest.php @@ -57,7 +57,7 @@ protected function getObject(): Recollection /** @var MinimalRepository $repository */ $country = $repository->fetch(3); - $citizens = $country->getCitizensInRecollection(); + $citizens = $country->getCitizensInRecollection($this->getPaginationType()); static::assertInstanceOf(Recollection::class, $citizens); static::assertInstanceOf(PersistentCollection::class, $country->getRawCitizens()); diff --git a/tests/src/IntegrationTests/Domain/RecollectionDecoratorTest.php b/tests/src/IntegrationTests/Domain/RecollectionDecoratorTest.php index 0e8f870..7052b67 100644 --- a/tests/src/IntegrationTests/Domain/RecollectionDecoratorTest.php +++ b/tests/src/IntegrationTests/Domain/RecollectionDecoratorTest.php @@ -11,7 +11,7 @@ * that was distributed with this source code. */ -namespace Rekalogika\Collections\Tests\IntegrationTests; +namespace Rekalogika\Collections\Tests\IntegrationTests\Domain; use Doctrine\ORM\PersistentCollection; use Rekalogika\Collections\Tests\App\Entity\Citizen; @@ -57,7 +57,9 @@ protected function getObject(): Recollection /** @var MinimalRepository $repository */ $country = $repository->fetch(1); - $citizens = $country->getCitizensInRecollection(); + $citizens = $country->getCitizensInRecollection( + $this->getPaginationType() + ); static::assertInstanceOf(Recollection::class, $citizens); static::assertInstanceOf(PersistentCollection::class, $country->getRawCitizens()); diff --git a/tests/src/IntegrationTests/DomainOffset/CriteriaRecollectionLargeSetOffsetTest.php b/tests/src/IntegrationTests/DomainOffset/CriteriaRecollectionLargeSetOffsetTest.php new file mode 100644 index 0000000..f05040e --- /dev/null +++ b/tests/src/IntegrationTests/DomainOffset/CriteriaRecollectionLargeSetOffsetTest.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE file + * that was distributed with this source code. + */ + +namespace Rekalogika\Collections\Tests\IntegrationTests\DomainOffset; + +use Rekalogika\Collections\Tests\IntegrationTests\Domain\CriteriaRecollectionLargeSetTest; +use Rekalogika\Domain\Collections\Common\Pagination; + +class CriteriaRecollectionLargeSetOffsetTest extends CriteriaRecollectionLargeSetTest +{ + #[\Override] + protected function getPaginationType(): Pagination + { + return Pagination::Offset; + } +} diff --git a/tests/src/IntegrationTests/DomainOffset/CriteriaRecollectionMediumSetOffsetTest.php b/tests/src/IntegrationTests/DomainOffset/CriteriaRecollectionMediumSetOffsetTest.php new file mode 100644 index 0000000..b82f83f --- /dev/null +++ b/tests/src/IntegrationTests/DomainOffset/CriteriaRecollectionMediumSetOffsetTest.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE file + * that was distributed with this source code. + */ + +namespace Rekalogika\Collections\Tests\IntegrationTests\DomainOffset; + +use Rekalogika\Collections\Tests\IntegrationTests\Domain\CriteriaRecollectionMediumSetTest; +use Rekalogika\Domain\Collections\Common\Pagination; + +class CriteriaRecollectionMediumSetOffsetTest extends CriteriaRecollectionMediumSetTest +{ + #[\Override] + protected function getPaginationType(): Pagination + { + return Pagination::Offset; + } +} diff --git a/tests/src/IntegrationTests/DomainOffset/CriteriaRecollectionSmallSetOffsetTest.php b/tests/src/IntegrationTests/DomainOffset/CriteriaRecollectionSmallSetOffsetTest.php new file mode 100644 index 0000000..d9eaf81 --- /dev/null +++ b/tests/src/IntegrationTests/DomainOffset/CriteriaRecollectionSmallSetOffsetTest.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE file + * that was distributed with this source code. + */ + +namespace Rekalogika\Collections\Tests\IntegrationTests\DomainOffset; + +use Rekalogika\Collections\Tests\IntegrationTests\Domain\CriteriaRecollectionSmallSetTest; +use Rekalogika\Domain\Collections\Common\Pagination; + +class CriteriaRecollectionSmallSetOffsetTest extends CriteriaRecollectionSmallSetTest +{ + #[\Override] + protected function getPaginationType(): Pagination + { + return Pagination::Offset; + } +} diff --git a/tests/src/IntegrationTests/DomainOffset/MinimalCriteriaRecollectionOffsetTest.php b/tests/src/IntegrationTests/DomainOffset/MinimalCriteriaRecollectionOffsetTest.php new file mode 100644 index 0000000..760f8c8 --- /dev/null +++ b/tests/src/IntegrationTests/DomainOffset/MinimalCriteriaRecollectionOffsetTest.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE file + * that was distributed with this source code. + */ + +namespace Rekalogika\Collections\Tests\IntegrationTests\DomainOffset; + +use Rekalogika\Collections\Tests\IntegrationTests\Domain\MinimalCriteriaRecollectionTest; +use Rekalogika\Domain\Collections\Common\Pagination; + +class MinimalCriteriaRecollectionOffsetTest extends MinimalCriteriaRecollectionTest +{ + #[\Override] + protected function getPaginationType(): Pagination + { + return Pagination::Offset; + } +} diff --git a/tests/src/IntegrationTests/DomainOffset/MinimalRecollectionDecoratorOffsetTest.php b/tests/src/IntegrationTests/DomainOffset/MinimalRecollectionDecoratorOffsetTest.php new file mode 100644 index 0000000..e427827 --- /dev/null +++ b/tests/src/IntegrationTests/DomainOffset/MinimalRecollectionDecoratorOffsetTest.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE file + * that was distributed with this source code. + */ + +namespace Rekalogika\Collections\Tests\IntegrationTests\DomainOffset; + +use Rekalogika\Collections\Tests\IntegrationTests\Domain\MinimalRecollectionDecoratorTest; +use Rekalogika\Domain\Collections\Common\Pagination; + +class MinimalRecollectionDecoratorOffsetTest extends MinimalRecollectionDecoratorTest +{ + #[\Override] + protected function getPaginationType(): Pagination + { + return Pagination::Offset; + } +} diff --git a/tests/src/IntegrationTests/DomainOffset/RecollectionDecoratorMediumSetOffsetTest.php b/tests/src/IntegrationTests/DomainOffset/RecollectionDecoratorMediumSetOffsetTest.php new file mode 100644 index 0000000..fd4d5e7 --- /dev/null +++ b/tests/src/IntegrationTests/DomainOffset/RecollectionDecoratorMediumSetOffsetTest.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE file + * that was distributed with this source code. + */ + +namespace Rekalogika\Collections\Tests\IntegrationTests\DomainOffset; + +use Rekalogika\Collections\Tests\IntegrationTests\Domain\RecollectionDecoratorMediumSetTest; +use Rekalogika\Domain\Collections\Common\Pagination; + +class RecollectionDecoratorMediumSetOffsetTest extends RecollectionDecoratorMediumSetTest +{ + #[\Override] + protected function getPaginationType(): Pagination + { + return Pagination::Offset; + } +} diff --git a/tests/src/IntegrationTests/DomainOffset/RecollectionDecoratorOffsetTest.php b/tests/src/IntegrationTests/DomainOffset/RecollectionDecoratorOffsetTest.php new file mode 100644 index 0000000..262d44e --- /dev/null +++ b/tests/src/IntegrationTests/DomainOffset/RecollectionDecoratorOffsetTest.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE file + * that was distributed with this source code. + */ + +namespace Rekalogika\Collections\Tests\IntegrationTests\DomainOffset; + +use Rekalogika\Collections\Tests\IntegrationTests\Domain\RecollectionDecoratorTest; +use Rekalogika\Domain\Collections\Common\Pagination; + +class RecollectionDecoratorOffsetTest extends RecollectionDecoratorTest +{ + #[\Override] + protected function getPaginationType(): Pagination + { + return Pagination::Offset; + } +} diff --git a/tests/src/IntegrationTests/DomainOffset/RecollectionDecoratorSmallSetOffsetTest.php b/tests/src/IntegrationTests/DomainOffset/RecollectionDecoratorSmallSetOffsetTest.php new file mode 100644 index 0000000..160bae7 --- /dev/null +++ b/tests/src/IntegrationTests/DomainOffset/RecollectionDecoratorSmallSetOffsetTest.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE file + * that was distributed with this source code. + */ + +namespace Rekalogika\Collections\Tests\IntegrationTests\DomainOffset; + +use Rekalogika\Collections\Tests\IntegrationTests\Domain\RecollectionDecoratorSmallSetTest; +use Rekalogika\Domain\Collections\Common\Pagination; + +class RecollectionDecoratorSmallSetOffsetTest extends RecollectionDecoratorSmallSetTest +{ + #[\Override] + protected function getPaginationType(): Pagination + { + return Pagination::Offset; + } +} diff --git a/tests/src/IntegrationTests/Trait/PageableTestsTrait.php b/tests/src/IntegrationTests/Trait/PageableTestsTrait.php index 3a491dc..4ae3e21 100644 --- a/tests/src/IntegrationTests/Trait/PageableTestsTrait.php +++ b/tests/src/IntegrationTests/Trait/PageableTestsTrait.php @@ -14,7 +14,11 @@ namespace Rekalogika\Collections\Tests\IntegrationTests\Trait; use Rekalogika\Collections\Tests\App\Entity\Citizen; +use Rekalogika\Contracts\Rekapager\Exception\LimitException; use Rekalogika\Contracts\Rekapager\PageableInterface; +use Rekalogika\Domain\Collections\Common\Pagination; +use Rekalogika\Rekapager\Keyset\Contracts\KeysetPageIdentifier; +use Rekalogika\Rekapager\Offset\Contracts\PageNumber; /** * @template-covariant R of PageableInterface @@ -32,6 +36,13 @@ public function testFirstPage(): void public function testPageableIteration(): void { + if ( + $this->getPaginationType() === Pagination::Offset + && $this->getExpectedTotal() > 5000 + ) { + $this->expectException(LimitException::class); + } + $i = 0; foreach ($this->getObject()->getPages() as $page) { foreach ($page as $key => $citizen) { @@ -43,4 +54,18 @@ public function testPageableIteration(): void static::assertEquals($this->getExpectedTotal(), $i); } + + public function testPaginationType(): void + { + $object = $this->getObject(); + + $firstPage = $object->getFirstPage(); + $identifier = $firstPage->getPageIdentifier(); + + if ($this->getPaginationType() === Pagination::Keyset) { + $this->assertInstanceOf(KeysetPageIdentifier::class, $identifier); + } else { + $this->assertInstanceOf(PageNumber::class, $identifier); + } + } }