diff --git a/CHANGELOG.md b/CHANGELOG.md index 24c9dd4..189dab7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +# 0.18.0 + +* test: add phpat architecture tests + # 0.17.5 * fix: `RekapagerLinkProcessor` was added to the wrong package diff --git a/composer.json b/composer.json index 7ca4f6d..cf34b14 100644 --- a/composer.json +++ b/composer.json @@ -1,15 +1,15 @@ { "name": "rekalogika/rekapager", "description": "Pagination library for PHP, supporting both offset-based and keyset-based pagination.", - "homepage": "https://rekalogika.dev/rekapager", "license": "MIT", + "type": "library", "authors": [ { "name": "Priyadi Iman Nurcahyo", "email": "priyadi@rekalogika.com" } ], - "type": "library", + "homepage": "https://rekalogika.dev/rekapager", "require": { "php": "^8.2", "api-platform/core": "^3.2", @@ -22,11 +22,13 @@ "spomky-labs/base64url": "^2.0", "symfony/config": "^6.4 || ^7.0", "symfony/dependency-injection": "^6.4 || ^7.0", + "symfony/flex": "^2.4", "symfony/http-foundation": "^6.4 || ^7.0", "symfony/http-kernel": "^6.4 || ^7.0", "symfony/routing": "^6.4 || ^7.0", "symfony/serializer": "^6.4 || ^7.0", "symfony/stimulus-bundle": "^2.16", + "symfony/weblink": "^6.4 || ^7.0", "twig/twig": "^3.8" }, "require-dev": { @@ -37,6 +39,7 @@ "ekino/phpstan-banned-code": "^1.0 || ^2.0", "fakerphp/faker": "^1.23", "graviton/link-header-rel-parser": "^1.0", + "phpat/phpat": "^0.10.18", "phpstan/phpstan": "^1.10.66 || ^1.11", "phpstan/phpstan-deprecation-rules": "^1.1", "phpstan/phpstan-phpunit": "^1.3", @@ -70,6 +73,20 @@ "vimeo/psalm": "^5.15", "zenstruck/foundry": "1.37.*" }, + "replace": { + "rekalogika/rekapager-adapter-common": "0.17.5", + "rekalogika/rekapager-api-platform": "0.17.5", + "rekalogika/rekapager-bundle": "0.17.5", + "rekalogika/rekapager-contracts": "0.17.5", + "rekalogika/rekapager-core": "0.17.5", + "rekalogika/rekapager-doctrine-collections-adapter": "0.17.5", + "rekalogika/rekapager-doctrine-dbal-adapter": "0.17.5", + "rekalogika/rekapager-doctrine-orm-adapter": "0.17.5", + "rekalogika/rekapager-keyset-pagination": "0.17.5", + "rekalogika/rekapager-offset-pagination": "0.17.5", + "rekalogika/rekapager-pagerfanta-adapter": "0.17.5", + "rekalogika/rekapager-symfony-bridge": "0.17.5" + }, "autoload": { "psr-4": { "Rekalogika\\Contracts\\Rekapager\\": "packages/rekapager-contracts/src/", @@ -97,19 +114,5 @@ "symfony/runtime": true, "symfony/flex": false } - }, - "replace": { - "rekalogika/rekapager-adapter-common": "0.17.5", - "rekalogika/rekapager-api-platform": "0.17.5", - "rekalogika/rekapager-bundle": "0.17.5", - "rekalogika/rekapager-contracts": "0.17.5", - "rekalogika/rekapager-core": "0.17.5", - "rekalogika/rekapager-doctrine-collections-adapter": "0.17.5", - "rekalogika/rekapager-doctrine-dbal-adapter": "0.17.5", - "rekalogika/rekapager-doctrine-orm-adapter": "0.17.5", - "rekalogika/rekapager-keyset-pagination": "0.17.5", - "rekalogika/rekapager-offset-pagination": "0.17.5", - "rekalogika/rekapager-pagerfanta-adapter": "0.17.5", - "rekalogika/rekapager-symfony-bridge": "0.17.5" } } diff --git a/packages/rekapager-adapter-common/composer.json b/packages/rekapager-adapter-common/composer.json index 1f3ba61..ef83ea7 100644 --- a/packages/rekapager-adapter-common/composer.json +++ b/packages/rekapager-adapter-common/composer.json @@ -17,6 +17,7 @@ }, "require": { "php": "^8.2", - "doctrine/collections": "^2.2" + "doctrine/collections": "^2.2", + "rekalogika/rekapager-contracts": "^0.17.5" } } diff --git a/packages/rekapager-api-platform/composer.json b/packages/rekapager-api-platform/composer.json index be09d92..bf8921a 100644 --- a/packages/rekapager-api-platform/composer.json +++ b/packages/rekapager-api-platform/composer.json @@ -33,11 +33,15 @@ "psr/container": "^2.0", "rekalogika/rekapager-contracts": "^0.17.5", "rekalogika/rekapager-core": "^0.17.5", + "rekalogika/rekapager-doctrine-orm-adapter": "^0.17.5", + "rekalogika/rekapager-keyset-pagination": "^0.17.5", "rekalogika/rekapager-symfony-bridge": "^0.17.5", "symfony/config": "^6.4 || ^7.0", "symfony/dependency-injection": "^6.4 || ^7.0", "symfony/flex": "^2.4", "symfony/http-foundation": "^6.4 || ^7.0", - "symfony/http-kernel": "^6.4 || ^7.0" + "symfony/http-kernel": "^6.4 || ^7.0", + "symfony/serializer": "^6.4 || ^7.0", + "symfony/weblink": "^6.4 || ^7.0" } } diff --git a/packages/rekapager-api-platform/config/services.php b/packages/rekapager-api-platform/config/services.php index 71728cc..9f16fae 100644 --- a/packages/rekapager-api-platform/config/services.php +++ b/packages/rekapager-api-platform/config/services.php @@ -11,6 +11,7 @@ * that was distributed with this source code. */ +use Doctrine\ORM\QueryBuilder; use Rekalogika\Rekapager\ApiPlatform\Implementation\PagerFactory; use Rekalogika\Rekapager\ApiPlatform\Implementation\PagerNormalizer; use Rekalogika\Rekapager\ApiPlatform\Implementation\RekapagerExtension; @@ -59,14 +60,16 @@ '$collectionNormalizer' => service('.inner'), ]); - $services - ->set('rekalogika.rekapager.api_platform.orm.extension') - ->class(RekapagerExtension::class) - ->args([ - '$pagerFactory' => service(PagerFactoryInterface::class), - '$pagination' => service('api_platform.pagination'), - ]) - ->tag('api_platform.doctrine.orm.query_extension.collection', [ - 'priority' => -48, - ]); + if (class_exists(QueryBuilder::class)) { + $services + ->set('rekalogika.rekapager.api_platform.orm.extension') + ->class(RekapagerExtension::class) + ->args([ + '$pagerFactory' => service(PagerFactoryInterface::class), + '$pagination' => service('api_platform.pagination'), + ]) + ->tag('api_platform.doctrine.orm.query_extension.collection', [ + 'priority' => -48, + ]); + } }; diff --git a/packages/rekapager-bundle/composer.json b/packages/rekapager-bundle/composer.json index fa6fc50..5a89e7e 100644 --- a/packages/rekapager-bundle/composer.json +++ b/packages/rekapager-bundle/composer.json @@ -38,7 +38,6 @@ "symfony/http-foundation": "^6.4 || ^7.0", "symfony/http-kernel": "^6.4 || ^7.0", "symfony/routing": "^6.4 || ^7.0", - "symfony/serializer": "^6.4 || ^7.0", "symfony/stimulus-bundle": "^2.16" } } diff --git a/packages/rekapager-bundle/src/DependencyInjection/RekalogikaRekapagerExtension.php b/packages/rekapager-bundle/src/DependencyInjection/RekalogikaRekapagerExtension.php index bab8e7b..8e77141 100644 --- a/packages/rekapager-bundle/src/DependencyInjection/RekalogikaRekapagerExtension.php +++ b/packages/rekapager-bundle/src/DependencyInjection/RekalogikaRekapagerExtension.php @@ -13,6 +13,7 @@ namespace Rekalogika\Rekapager\Bundle\DependencyInjection; +use Rekalogika\Contracts\Rekapager\Exception\InvalidArgumentException; use Rekalogika\Contracts\Rekapager\PageIdentifierEncoderInterface; use Rekalogika\Rekapager\Symfony\RekapagerSymfonyBridge; use Symfony\Component\AssetMapper\AssetMapperInterface; @@ -61,17 +62,17 @@ public function load(array $configs, ContainerBuilder $container): void $defaultTwigTemplate = $config['default_template'] ?? null; if (null === $defaultTwigTemplate || !\is_string($defaultTwigTemplate)) { - throw new \InvalidArgumentException('The "default_template" config is required.'); + throw new InvalidArgumentException('The "default_template" config is required.'); } $defaultPageParameterName = $config['default_page_parameter_name'] ?? null; if (null === $defaultPageParameterName || !\is_string($defaultPageParameterName)) { - throw new \InvalidArgumentException('The "default_page_parameter_name" config is required.'); + throw new InvalidArgumentException('The "default_page_parameter_name" config is required.'); } $defaultProximity = $config['default_proximity'] ?? null; if (null === $defaultProximity || !\is_int($defaultProximity)) { - throw new \InvalidArgumentException('The "default_proximity" config is required.'); + throw new InvalidArgumentException('The "default_proximity" config is required.'); } $container->setParameter( diff --git a/packages/rekapager-bundle/src/PagerFactory.php b/packages/rekapager-bundle/src/PagerFactory.php index b9359f8..9768d47 100644 --- a/packages/rekapager-bundle/src/PagerFactory.php +++ b/packages/rekapager-bundle/src/PagerFactory.php @@ -13,6 +13,7 @@ namespace Rekalogika\Rekapager\Bundle; +use Rekalogika\Contracts\Rekapager\Exception\InvalidArgumentException; use Rekalogika\Contracts\Rekapager\Exception\OutOfBoundsException as ContractsOutOfBoundsException; use Rekalogika\Contracts\Rekapager\PageableInterface; use Rekalogika\Rekapager\Bundle\Contracts\PagerFactoryInterface; @@ -61,13 +62,13 @@ public function createPager( // routeName $routeName = $options?->getRouteName() ?? $request->attributes->get('_route'); if (!\is_string($routeName)) { - throw new \InvalidArgumentException('Cannot determine route name from request.'); + throw new InvalidArgumentException('Cannot determine route name from request.'); } // routeParams $routeParams = $options?->getRouteParams() ?? $request->attributes->get('_route_params', []); if (!\is_array($routeParams)) { - throw new \InvalidArgumentException('Cannot determine route parameters from request.'); + throw new InvalidArgumentException('Cannot determine route parameters from request.'); } // urlReferenceType diff --git a/packages/rekapager-doctrine-collections-adapter/composer.json b/packages/rekapager-doctrine-collections-adapter/composer.json index 4d06321..95bd2dd 100644 --- a/packages/rekapager-doctrine-collections-adapter/composer.json +++ b/packages/rekapager-doctrine-collections-adapter/composer.json @@ -30,6 +30,7 @@ "php": "^8.2", "doctrine/collections": "^2.2", "rekalogika/rekapager-adapter-common": "^0.17.5", + "rekalogika/rekapager-contracts": "^0.17.5", "rekalogika/rekapager-keyset-pagination": "^0.17.5", "rekalogika/rekapager-offset-pagination": "^0.17.5" } diff --git a/packages/rekapager-doctrine-collections-adapter/src/Exception/UnsupportedCollectionItemException.php b/packages/rekapager-doctrine-collections-adapter/src/Exception/UnsupportedCollectionItemException.php index 3ee48a8..dfec174 100644 --- a/packages/rekapager-doctrine-collections-adapter/src/Exception/UnsupportedCollectionItemException.php +++ b/packages/rekapager-doctrine-collections-adapter/src/Exception/UnsupportedCollectionItemException.php @@ -19,6 +19,6 @@ class UnsupportedCollectionItemException extends UnexpectedValueException { public function __construct(string $type, \Throwable $previous) { - parent::__construct(\sprintf('Unsupported collection type. The items in the collection must be objects or arrays, an %s was given.', $type), 0, $previous); + parent::__construct(\sprintf('Unsupported collection type. The items in the collection must be objects or arrays, %s was given.', $type), 0, $previous); } } diff --git a/packages/rekapager-doctrine-collections-adapter/src/Internal/SelectableKeysetItem.php b/packages/rekapager-doctrine-collections-adapter/src/Internal/SelectableKeysetItem.php index 8670a1c..e8c214d 100644 --- a/packages/rekapager-doctrine-collections-adapter/src/Internal/SelectableKeysetItem.php +++ b/packages/rekapager-doctrine-collections-adapter/src/Internal/SelectableKeysetItem.php @@ -14,6 +14,7 @@ namespace Rekalogika\Rekapager\Doctrine\Collections\Internal; use Doctrine\Common\Collections\Expr\ClosureExpressionVisitor; +use Rekalogika\Contracts\Rekapager\Exception\LogicException; use Rekalogika\Contracts\Rekapager\Exception\NullBoundaryValueException; use Rekalogika\Rekapager\Keyset\Contracts\KeysetItemInterface; @@ -42,7 +43,7 @@ public function __construct( private array $boundaryProperties, ) { if (!\is_object($value) && !\is_array($value)) { - throw new \LogicException('The value must be an object or an array'); + throw new LogicException('The value must be an object or an array'); } $this->objectOrArrayValue = $value; diff --git a/packages/rekapager-doctrine-dbal-adapter/composer.json b/packages/rekapager-doctrine-dbal-adapter/composer.json index aebe1fe..fbb252f 100644 --- a/packages/rekapager-doctrine-dbal-adapter/composer.json +++ b/packages/rekapager-doctrine-dbal-adapter/composer.json @@ -31,6 +31,7 @@ "doctrine/dbal": "^3.8 || ^4.0", "doctrine/collections": "^2.2", "rekalogika/rekapager-adapter-common": "^0.17.5", + "rekalogika/rekapager-contracts": "^0.17.5", "rekalogika/rekapager-keyset-pagination": "^0.17.5", "rekalogika/rekapager-offset-pagination": "^0.17.5" } diff --git a/packages/rekapager-doctrine-orm-adapter/composer.json b/packages/rekapager-doctrine-orm-adapter/composer.json index 8beb287..336b8bc 100644 --- a/packages/rekapager-doctrine-orm-adapter/composer.json +++ b/packages/rekapager-doctrine-orm-adapter/composer.json @@ -32,6 +32,7 @@ "doctrine/orm": "^2.19 || ^3.0", "doctrine/collections": "^2.2", "rekalogika/rekapager-adapter-common": "^0.17.5", + "rekalogika/rekapager-contracts": "^0.17.5", "rekalogika/rekapager-keyset-pagination": "^0.17.5", "rekalogika/rekapager-offset-pagination": "^0.17.5" } diff --git a/packages/rekapager-doctrine-orm-adapter/src/QueryBuilderAdapter.php b/packages/rekapager-doctrine-orm-adapter/src/QueryBuilderAdapter.php index 74fd757..314da58 100644 --- a/packages/rekapager-doctrine-orm-adapter/src/QueryBuilderAdapter.php +++ b/packages/rekapager-doctrine-orm-adapter/src/QueryBuilderAdapter.php @@ -498,9 +498,9 @@ private function detectTypeByHeuristics(object $value): string|null return Types::DATETIME_MUTABLE; } elseif ($value instanceof \DateTimeImmutable) { return Types::DATETIME_IMMUTABLE; - } elseif ($value instanceof Uuid) { + } elseif ($value instanceof Uuid && class_exists(UuidType::class)) { return UuidType::NAME; - } elseif ($value instanceof Ulid) { + } elseif ($value instanceof Ulid && class_exists(UlidType::class)) { return UlidType::NAME; } diff --git a/packages/rekapager-symfony-bridge/src/Batch/Internal/CommandBatchProcessorDecorator.php b/packages/rekapager-symfony-bridge/src/Batch/Internal/CommandBatchProcessorDecorator.php index c3e0f6a..36198de 100644 --- a/packages/rekapager-symfony-bridge/src/Batch/Internal/CommandBatchProcessorDecorator.php +++ b/packages/rekapager-symfony-bridge/src/Batch/Internal/CommandBatchProcessorDecorator.php @@ -13,6 +13,7 @@ namespace Rekalogika\Rekapager\Symfony\Batch\Internal; +use Rekalogika\Contracts\Rekapager\Exception\LogicException; use Rekalogika\Contracts\Rekapager\PageInterface; use Rekalogika\Rekapager\Batch\BatchProcessorDecorator; use Rekalogika\Rekapager\Batch\BatchProcessorInterface; @@ -85,7 +86,7 @@ private function formatTime(\DateTimeInterface $time): string private function getStartTime(): \DateTimeInterface { if ($this->startTime === null) { - throw new \LogicException('Start time is not set'); + throw new LogicException('Start time is not set'); } return $this->startTime; diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 799391c..cdc5490 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -25,4 +25,11 @@ includes: - vendor/phpstan/phpstan-deprecation-rules/rules.neon - vendor/bnf/phpstan-psr-container/extension.neon - vendor/ekino/phpstan-banned-code/extension.neon + - vendor/phpat/phpat/extension.neon - phar://phpstan.phar/conf/bleedingEdge.neon + +services: + - + class: Rekalogika\Rekapager\Tests\ArchitectureTests\ArchitectureTest + tags: + - phpat.test \ No newline at end of file diff --git a/tests/src/ArchitectureTests/ArchitectureTest.php b/tests/src/ArchitectureTests/ArchitectureTest.php new file mode 100644 index 0000000..e00efc2 --- /dev/null +++ b/tests/src/ArchitectureTests/ArchitectureTest.php @@ -0,0 +1,284 @@ + + * + * For the full copyright and license information, please view the LICENSE file + * that was distributed with this source code. + */ + +namespace Rekalogika\Rekapager\Tests\ArchitectureTests; + +use Base64Url\Base64Url; +use PHPat\Selector\Selector; +use PHPat\Test\Builder\Rule; +use PHPat\Test\PHPat; +use Psr\Container\ContainerInterface; +use Ramsey\Uuid\UuidInterface; +use Symfony\Component\Uid\AbstractUid; + +final class ArchitectureTest +{ + public function testPackageAdapterCommon(): Rule + { + return PHPat::rule() + ->classes(Selector::inNamespace('Rekalogika\Rekapager\Adapter\Common')) + ->canOnlyDependOn() + ->classes( + Selector::inNamespace('Rekalogika\Rekapager\Adapter\Common'), + + Selector::inNamespace('Doctrine\Common\Collections'), + Selector::inNamespace('Rekalogika\Contracts\Rekapager\Exception'), + Selector::classname(\Throwable::class), + Selector::classname(\UnitEnum::class), + ); + } + + public function testPackageApiPlatform(): Rule + { + return PHPat::rule() + ->classes(Selector::inNamespace('Rekalogika\Rekapager\ApiPlatform')) + ->canOnlyDependOn() + ->classes( + Selector::inNamespace('Rekalogika\Rekapager\ApiPlatform'), + + Selector::inNamespace('Rekalogika\Contracts\Rekapager'), + Selector::inNamespace('Rekalogika\Rekapager\Contracts'), + Selector::inNamespace('Rekalogika\Rekapager\Doctrine\ORM'), + Selector::inNamespace('Rekalogika\Rekapager\Symfony'), + Selector::inNamespace('Rekalogika\Rekapager\Pager'), + Selector::inNamespace('Rekalogika\Rekapager\Keyset'), + Selector::inNamespace('Doctrine\ORM'), // optional + Selector::inNamespace('ApiPlatform\Metadata'), + Selector::inNamespace('ApiPlatform\State'), + Selector::inNamespace('ApiPlatform\OpenApi'), + Selector::inNamespace('ApiPlatform\Doctrine'), + Selector::inNamespace('Symfony\Component\Config'), + Selector::inNamespace('Symfony\Component\DependencyInjection'), + Selector::inNamespace('Symfony\Component\HttpFoundation'), + Selector::inNamespace('Symfony\Component\HttpKernel'), + Selector::inNamespace('Symfony\Component\Serializer'), + Selector::inNamespace('Symfony\Component\WebLink'), + Selector::classname(\ArrayObject::class), + ); + } + + public function testPackageBundle(): Rule + { + return PHPat::rule() + ->classes(Selector::inNamespace('Rekalogika\Rekapager\Bundle')) + ->canOnlyDependOn() + ->classes( + Selector::inNamespace('Rekalogika\Rekapager\Bundle'), + + Selector::inNamespace('Rekalogika\Contracts\Rekapager'), + Selector::inNamespace('Rekalogika\Rekapager\Contracts'), + Selector::inNamespace('Rekalogika\Rekapager\Pager'), + Selector::inNamespace('Rekalogika\Rekapager\Symfony'), + Selector::inNamespace('Symfony\Component\AssetMapper'), // optional + Selector::inNamespace('Symfony\Component\Config'), + Selector::inNamespace('Symfony\Component\DependencyInjection'), + Selector::inNamespace('Symfony\Component\HttpFoundation'), + Selector::inNamespace('Symfony\Component\HttpKernel'), + Selector::inNamespace('Symfony\Component\Routing'), + Selector::inNamespace('Twig'), // optional + ); + } + + public function testPackageContracts(): Rule + { + return PHPat::rule() + ->classes(Selector::inNamespace('Rekalogika\Contracts\Rekapager')) + ->canOnlyDependOn() + ->classes( + Selector::inNamespace('Rekalogika\Contracts\Rekapager'), + + Selector::classname(\Traversable::class), + Selector::classname(\Countable::class), + Selector::classname(\UnexpectedValueException::class), + Selector::classname(\RuntimeException::class), + Selector::classname(\LogicException::class), + Selector::classname(\InvalidArgumentException::class), + Selector::classname(\OutOfBoundsException::class), + Selector::classname(\Throwable::class), + Selector::inNamespace('Symfony\Component\HttpKernel'), // optional + ); + } + + public function testPackageCore(): Rule + { + return PHPat::rule() + ->classes( + Selector::inNamespace('Rekalogika\Rekapager\Batch'), + Selector::inNamespace('Rekalogika\Rekapager\Contracts'), + Selector::inNamespace('Rekalogika\Rekapager\Exception'), + Selector::inNamespace('Rekalogika\Rekapager\Implementation'), + Selector::inNamespace('Rekalogika\Rekapager\Pager'), + ) + ->canOnlyDependOn() + ->classes( + Selector::inNamespace('Rekalogika\Rekapager\Batch'), + Selector::inNamespace('Rekalogika\Rekapager\Contracts'), + Selector::inNamespace('Rekalogika\Rekapager\Exception'), + Selector::inNamespace('Rekalogika\Rekapager\Implementation'), + Selector::inNamespace('Rekalogika\Rekapager\Pager'), + + Selector::inNamespace('Rekalogika\Contracts\Rekapager'), + Selector::classname(\Traversable::class), + Selector::classname(\IteratorAggregate::class), + ); + } + + public function testPackageDoctrineCollectionsAdapter(): Rule + { + return PHPat::rule() + ->classes( + Selector::inNamespace('Rekalogika\Rekapager\Doctrine\Collections'), + ) + ->canOnlyDependOn() + ->classes( + Selector::inNamespace('Rekalogika\Rekapager\Doctrine\Collections'), + + Selector::inNamespace('Rekalogika\Rekapager\Keyset'), + Selector::inNamespace('Rekalogika\Rekapager\Offset'), + Selector::inNamespace('Doctrine\Common\Collections'), + Selector::inNamespace('Rekalogika\Contracts\Rekapager'), + Selector::inNamespace('Rekalogika\Rekapager\Adapter\Common'), + Selector::classname(\TypeError::class), + Selector::classname(\Throwable::class), + ); + } + + public function testPackageDoctrineDBALAdapter(): Rule + { + return PHPat::rule() + ->classes( + Selector::inNamespace('Rekalogika\Rekapager\Doctrine\DBAL'), + ) + ->canOnlyDependOn() + ->classes( + Selector::inNamespace('Rekalogika\Rekapager\Doctrine\DBAL'), + + Selector::inNamespace('Rekalogika\Rekapager\Keyset'), + Selector::inNamespace('Rekalogika\Rekapager\Offset'), + Selector::inNamespace('Doctrine\Common\Collections'), + Selector::inNamespace('Doctrine\DBAL'), + Selector::inNamespace('Rekalogika\Contracts\Rekapager'), + Selector::inNamespace('Rekalogika\Rekapager\Adapter\Common'), + ); + } + + public function testPackageDoctrineORMAdapter(): Rule + { + return PHPat::rule() + ->classes( + Selector::inNamespace('Rekalogika\Rekapager\Doctrine\ORM'), + ) + ->canOnlyDependOn() + ->classes( + Selector::inNamespace('Rekalogika\Rekapager\Doctrine\ORM'), + + Selector::inNamespace('Rekalogika\Rekapager\Keyset'), + Selector::inNamespace('Rekalogika\Rekapager\Offset'), + Selector::inNamespace('Doctrine\Common\Collections'), + Selector::inNamespace('Doctrine\ORM'), + Selector::inNamespace('Doctrine\DBAL'), + Selector::inNamespace('Rekalogika\Contracts\Rekapager'), + Selector::inNamespace('Rekalogika\Rekapager\Adapter\Common'), + Selector::inNamespace('Symfony\Bridge\Doctrine'), // optional + Selector::classname(\Throwable::class), + Selector::classname(\Traversable::class), + Selector::classname(\Countable::class), + ); + } + + public function testPackageKeysetPagination(): Rule + { + return PHPat::rule() + ->classes( + Selector::inNamespace('Rekalogika\Rekapager\Keyset'), + ) + ->canOnlyDependOn() + ->classes( + Selector::inNamespace('Rekalogika\Rekapager\Keyset'), + + Selector::inNamespace('Rekalogika\Contracts\Rekapager'), + Selector::classname(Base64Url::class), + Selector::classname(\Closure::class), + Selector::classname(\Traversable::class), + Selector::classname(\ArrayIterator::class), + Selector::classname(\BackedEnum::class), + Selector::classname(UuidInterface::class), + Selector::classname(AbstractUid::class), + Selector::classname(\InvalidArgumentException::class), + Selector::classname(\ErrorException::class), + Selector::classname(\DateTimeInterface::class), + Selector::classname(\IteratorAggregate::class), + Selector::inNamespace('Symfony\Component\Serializer'), // optional + ); + } + + public function testPackageOffsetPagination(): Rule + { + return PHPat::rule() + ->classes( + Selector::inNamespace('Rekalogika\Rekapager\Offset'), + ) + ->canOnlyDependOn() + ->classes( + Selector::inNamespace('Rekalogika\Rekapager\Offset'), + + Selector::inNamespace('Rekalogika\Contracts\Rekapager'), + Selector::classname(\Closure::class), + Selector::classname(\IteratorAggregate::class), + Selector::classname(\Traversable::class), + ); + } + + public function testPackagePagerfantaAdapter(): Rule + { + return PHPat::rule() + ->classes( + Selector::inNamespace('Rekalogika\Rekapager\Pagerfanta'), + ) + ->canOnlyDependOn() + ->classes( + Selector::inNamespace('Rekalogika\Rekapager\Pagerfanta'), + + Selector::inNamespace('Rekalogika\Contracts\Rekapager'), + Selector::inNamespace('Rekalogika\Rekapager\Offset'), + Selector::inNamespace('Pagerfanta'), // optional + Selector::inNamespace('Rekalogika\Rekapager\Adapter\Common'), + Selector::classname(\Closure::class), + Selector::classname(\Traversable::class), + ); + } + + public function testPackageSymfonyBridge(): Rule + { + return PHPat::rule() + ->classes( + Selector::inNamespace('Rekalogika\Rekapager\Symfony'), + ) + ->canOnlyDependOn() + ->classes( + Selector::inNamespace('Rekalogika\Rekapager\Symfony'), + + Selector::inNamespace('Rekalogika\Contracts\Rekapager'), + Selector::inNamespace('Rekalogika\Rekapager\Batch'), + Selector::inNamespace('Rekalogika\Rekapager\Contracts'), + Selector::inNamespace('Rekalogika\Rekapager\Exception'), + Selector::inNamespace('Symfony\Component\Config'), + Selector::inNamespace('Symfony\Component\DependencyInjection'), + Selector::classname(ContainerInterface::class), + Selector::classname(\DateTimeInterface::class), + Selector::classname(\DateTimeImmutable::class), + Selector::classname(\DateTimeZone::class), + Selector::inNamespace('Symfony\Component\Console'), // optional + ); + } +}