diff --git a/.coveralls.yml b/.coveralls.yml deleted file mode 100644 index 9160059..0000000 --- a/.coveralls.yml +++ /dev/null @@ -1 +0,0 @@ -service_name: travis-ci diff --git a/.gitattributes b/.gitattributes index 3cf489b..95ec386 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,8 +1,9 @@ /spec export-ignore -.coveralls.yml export-ignore .editorconfig export-ignore .gitattributes export-ignore .gitignore export-ignore -.travis.yml export-ignore -.php_cs export-ignore -phpspec.yml.dist export-ignore +.php-cs-fixer.php export-ignore +phpspec.yml export-ignore +rector.php export-ignore +phpstan.neon export-ignore +phpstan-baseline.neon export-ignore diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml new file mode 100644 index 0000000..cef2c14 --- /dev/null +++ b/.github/workflows/php.yml @@ -0,0 +1,43 @@ +name: PHP Composer + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +permissions: + contents: read + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Validate composer.json and composer.lock + run: docker compose run composer validate --strict + + - name: Cache Composer packages + id: composer-cache + uses: actions/cache@v3 + with: + path: vendor + key: ${{ runner.os }}-php-${{ hashFiles('**/composer.json') }}-${{ github.ref_name }} + restore-keys: | + ${{ runner.os }}-php-${{ hashFiles('**/composer.json') }} + ${{ runner.os }}-php- + + - name: Install packages + run: docker compose run composer install + + - name: Run test suite + run: docker compose run phpspec + + - name: Run static analyzer + run: docker compose run phpstan + + - name: Run code style checker + run: docker compose run php-cs-fixer --dry-run diff --git a/.gitignore b/.gitignore index a6ac1df..bcf29c6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -build/ +coverage/ vendor/ -.php_cs.cache +.php-cs-fixer.cache composer.lock diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php new file mode 100644 index 0000000..2070fa1 --- /dev/null +++ b/.php-cs-fixer.php @@ -0,0 +1,17 @@ +exclude(['vendor', 'build']) + ->in(__DIR__); + +return (new PhpCsFixer\Config()) + ->setRiskyAllowed(true) + ->setRules([ + '@Symfony' => true, + 'no_useless_else' => true, + 'no_useless_return' => true, + 'ordered_class_elements' => true, + 'strict_comparison' => true, + 'strict_param' => true, + ]) + ->setFinder($finder); diff --git a/.php_cs b/.php_cs deleted file mode 100644 index 475f713..0000000 --- a/.php_cs +++ /dev/null @@ -1,26 +0,0 @@ -exclude(['vendor', 'build']) - ->in(__DIR__); - -return PhpCsFixer\Config::create() - ->setRiskyAllowed(true) - ->setRules([ - '@Symfony' => true, - 'binary_operator_spaces' => [ - 'align_double_arrow' => true, - 'align_equals' => true, - ], - 'concat_space' => ['spacing' => 'one'], - 'not_operator_with_successor_space' => true, - 'no_multiline_whitespace_before_semicolons' => true, - 'no_useless_else' => true, - 'no_useless_return' => true, - 'ordered_class_elements' => true, - 'ordered_imports' => true, - 'phpdoc_order' => true, - 'strict_comparison' => true, - 'strict_param' => true, - ]) - ->setFinder($finder); diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 471417a..0000000 --- a/.travis.yml +++ /dev/null @@ -1,28 +0,0 @@ -language: php - -matrix: - include: - - php: 5.5 - - php: 5.6 - - php: 7.0 - -sudo: false - -cache: - directories: - - $HOME/.composer/cache - -before_install: - - travis_retry composer self-update - -install: - - travis_retry composer install --no-interaction - -before_script: - - mkdir -p build/logs - -script: - - composer test - -after_success: - - php vendor/bin/coveralls -v diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..744a37c --- /dev/null +++ b/Dockerfile @@ -0,0 +1,6 @@ +FROM php:8.3-cli-alpine +RUN apk add --no-cache --update --virtual .build-deps $PHPIZE_DEPS linux-headers \ + && pecl install xdebug-3.3.2 \ + && docker-php-ext-enable xdebug \ + && apk del -f .build-deps \ + && echo "xdebug.mode=coverage" >> $PHP_INI_DIR/conf.d/docker-php-ext-xdebug.ini diff --git a/LICENSE b/LICENSE index 3c98b61..75abca4 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,7 @@ The MIT License (MIT) Copyright (c) 2015 Rik Bruil +Copyright (c) 2024 Nicholas Ruunu Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -19,4 +20,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - diff --git a/README.md b/README.md index c151fa6..413e247 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,23 @@ -# Doctrine Specification -[![Build Status](https://travis-ci.org/rikbruil/Doctrine-Specification.svg)](https://travis-ci.org/rikbruil/Doctrine-Specification) -[![Coverage Status](https://coveralls.io/repos/rikbruil/Doctrine-Specification/badge.svg?branch=master)](https://coveralls.io/r/rikbruil/Doctrine-Specification?branch=master) -[![Latest Stable Version](https://poser.pugx.org/rikbruil/doctrine-specification/v/stable.svg)](https://packagist.org/packages/rikbruil/doctrine-specification) -[![License](https://poser.pugx.org/rikbruil/doctrine-specification/license.svg)](https://packagist.org/packages/rikbruil/doctrine-specification) -[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/rikbruil/Doctrine-Specification/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/rikbruil/Doctrine-Specification/?branch=master) -[![SensioLabsInsight](https://insight.sensiolabs.com/projects/bb221821-1ba6-4613-b39f-f43718aaa92d/mini.png)](https://insight.sensiolabs.com/projects/bb221821-1ba6-4613-b39f-f43718aaa92d) +# Doctrine Specification -Doctrine [Specification pattern][specification_pattern] for building queries dynamically and with re-usable classes for composition. +Doctrine [Specification pattern][specification_pattern] for building queries dynamically and with re-usable classes for +composition. -This library started out as an adaptation of Benjamin Eberlei's [blog post][blog_post]. I was also inspired by the [Happyr Doctrine-Specification][happyr_spec] code, however this library has some small differences. -The main one is that SpecificationRepository->match() does not return the results directly, but returns the query object. +This library started out as an adaptation of Benjamin Eberlei's [blog post][blog_post]. I was also inspired by +the [Happyr Doctrine-Specification][happyr_spec] code, however this library has some small differences. +The main one is that SpecificationRepository->match() does not return the results directly, but returns the query +object. Since I like Doctrine's Paginator object, I wanted to be able to use that in combination with the Specification pattern. -__Note:__ In versions prior to 1.2 it was required to extend the SpecificationRepository class. This is no longer needed since we provide a SpecificationRepositoryTrait that you can use instead. -The class is still provided for backwards compatibility reasons. There is also the SpecificationAwareInterface that you can use if you need it. +__Note:__ In versions prior to 1.2 it was required to extend the SpecificationRepository class. This is no longer needed +since we provide a SpecificationRepositoryTrait that you can use instead. +The class is still provided for backwards compatibility reasons. There is also the SpecificationAwareInterface that you +can use if you need it. ## Usage -Install the latest version with `composer require rikbruil/doctrine-specification` +Install the latest version with `composer require purist/doctrine-specification` ```php // Not using the lib @@ -43,12 +42,12 @@ return $qb->where('r.ended = 0') ``` ```php -use Rb\Specification\Doctrine\Condition\Equals; -use Rb\Specification\Doctrine\Condition\IsNull; -use Rb\Specification\Doctrine\Condition\LessThan; -use Rb\Specification\Doctrine\Logic\AndX; -use Rb\Specification\Doctrine\Logic\OrX; -use Rb\Specification\Doctrine\Specification; +use Purist\Specification\Doctrine\Condition\Equals; +use Purist\Specification\Doctrine\Condition\IsNull; +use Purist\Specification\Doctrine\Condition\LessThan; +use Purist\Specification\Doctrine\Logic\AndX; +use Purist\Specification\Doctrine\Logic\OrX; +use Purist\Specification\Doctrine\Specification; // Using the lib $spec = new Specification([ @@ -67,6 +66,7 @@ return $this->em->getRepository('Advertisement')->match($spec)->execute(); ``` ## Composition + A bonus of this pattern is composition, which makes specifications very reusable: ```php @@ -89,7 +89,7 @@ class ExpiredAds extends Specification ]; parent::__construct($specs); } - + public function isSatisfiedBy($value) { return $value === Advertisement::class; @@ -107,7 +107,7 @@ class AdsByUser extends Specification new Join('user', 'u'), new Equals('id', $user->getId(), 'u'), ]; - + parent::__construct($specs); } @@ -131,7 +131,7 @@ class SomeService return $this->em->getRepository('Advertisement')->match($spec)->execute(); } - + /** * Fetch adverts paginated by Doctrine Paginator with joins intact. * A paginator can be iterated over like a normal array or Doctrine Collection @@ -142,7 +142,7 @@ class SomeService new ExpiredAds(), new AdsByUser($user), ]); - + $query = $this->em->getRepository('Advertisement')->match($spec); $query->setFirstResult(($page - 1) * $size)) ->setMaxResults($size); @@ -155,7 +155,7 @@ class SomeService Doctrine-Specification requires: -- PHP 5.5+ +- PHP 8.3+ - Doctrine 2.2 ## License diff --git a/composer.json b/composer.json index 719b208..6656720 100644 --- a/composer.json +++ b/composer.json @@ -1,33 +1,34 @@ { - "name": "rikbruil/doctrine-specification", + "name": "purist/doctrine-specification", "description": "Doctrine Specification pattern for building queries dynamically and with re-usable classes for composition.", "type": "library", "keywords": ["doctrine", "repository", "specification"], "license": "MIT", "authors": [ { - "name": "Rik Bruil", - "email": "rik.bruil@gmail.com" + "name": "Nicholas Ruunu", + "email": "nicholas@ruu.nu" } ], "minimum-stability": "stable", "autoload": { - "psr-4": {"Rb\\Specification\\Doctrine\\": "src/"} + "psr-4": {"Purist\\Specification\\Doctrine\\": "src/"} }, "autoload-dev": { - "psr-4": {"spec\\Rb\\Specification\\Doctrine\\": "spec/"} + "psr-4": {"spec\\Purist\\Specification\\Doctrine\\": "spec/"} }, "require": { - "php": ">=5.5", - "doctrine/orm": "^2.2", - "doctrine/collections": "^1.2", - "rikbruil/specification": "0.9.*" + "php": "^8.3", + "doctrine/orm": "^2.2|^3.0", + "doctrine/collections": "^2.0", + "purist/specification": "dev-master" }, "require-dev": { - "phpspec/phpspec": "^2.0", - "henrikbjorn/phpspec-code-coverage": "^2.0", - "friendsofphp/php-cs-fixer": "^2.0", - "satooshi/php-coveralls": "^1.0" + "phpspec/phpspec": "^7.5", + "rector/rector": "^1.1", + "friendsofphp/php-cs-fixer": "^3.59", + "phpstan/phpstan": "^1.11", + "friends-of-phpspec/phpspec-code-coverage": "^6.3" }, "extra": { "branch-alias": { @@ -36,6 +37,11 @@ }, "scripts": { "test": "vendor/bin/phpspec run", + "static-analyzer": "vendor/bin/phpstan", + "check-style": "vendor/bin/php-cs-fixer fix --dry-run", "fix-style": "vendor/bin/php-cs-fixer fix" - } + }, + "repositories": [ + { "type": "git", "url": "https://github.com/PuristPHP/specification.git" } + ] } diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..d992c97 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,25 @@ +services: + php: &php + build: . + volumes: + - .:/app + working_dir: /app + + phpspec: + <<: *php + entrypoint: vendor/bin/phpspec + command: run + + phpstan: + <<: *php + entrypoint: vendor/bin/phpstan + + php-cs-fixer: + <<: *php + entrypoint: vendor/bin/php-cs-fixer fix + + composer: + image: composer:latest + volumes: + - .:/app + working_dir: /app diff --git a/phpspec.yml b/phpspec.yml new file mode 100644 index 0000000..dc0a833 --- /dev/null +++ b/phpspec.yml @@ -0,0 +1,13 @@ +suites: + main: + namespace: Purist\Specification\Doctrine + psr4_prefix: Purist\Specification\Doctrine +extensions: + FriendsOfPhpSpec\PhpSpec\CodeCoverage\CodeCoverageExtension: + format: + - text + - clover + - html + output: + html: coverage/html + clover: coverage/clover.xml diff --git a/phpspec.yml.dist b/phpspec.yml.dist deleted file mode 100644 index 639b21a..0000000 --- a/phpspec.yml.dist +++ /dev/null @@ -1,15 +0,0 @@ -extensions: - - PhpSpec\Extension\CodeCoverageExtension - -code_coverage: - format: - - clover - - html - output: - html: build/logs/html - clover: build/logs/clover.xml - -suites: - main: - namespace: Rb\Specification\Doctrine - psr4_prefix: Rb\Specification\Doctrine diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon new file mode 100644 index 0000000..e657b02 --- /dev/null +++ b/phpstan-baseline.neon @@ -0,0 +1,11 @@ +parameters: + ignoreErrors: + - + message: "#^Else branch is unreachable because ternary operator condition is always true\\.$#" + count: 1 + path: src/Result/ModifierCollection.php + + - + message: "#^Else branch is unreachable because ternary operator condition is always true\\.$#" + count: 1 + path: src/Specification.php diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..3d3952c --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,7 @@ +parameters: + level: max + paths: + - src + +includes: + - phpstan-baseline.neon diff --git a/rector.php b/rector.php new file mode 100644 index 0000000..12037ce --- /dev/null +++ b/rector.php @@ -0,0 +1,24 @@ +withPaths([ + __DIR__.'/src', + __DIR__.'/spec', + ]) + ->withPhpSets(php83: true) + ->withAttributesSets( + symfony: true, + doctrine: true, + ) + // here we can define, what prepared sets of rules will be applied + ->withPreparedSets( + deadCode: true, + codeQuality: true, + typeDeclarations: true, + privatization: true, + instanceOf: true, + earlyReturn: true, + strictBooleans: true, + ); diff --git a/spec/Condition/BetweenSpec.php b/spec/Condition/BetweenSpec.php index 6fa882a..ff4b3f4 100644 --- a/spec/Condition/BetweenSpec.php +++ b/spec/Condition/BetweenSpec.php @@ -1,34 +1,34 @@ beConstructedWith($this->field, $this->from, $this->to); } - public function it_is_an_expression() + public function it_is_an_expression(): void { $this->shouldBeAnInstanceOf(SpecificationInterface::class); } - public function it_returns_an_expression_func_object(QueryBuilder $queryBuilder, ArrayCollection $parameters, Expr $expr) + public function it_returns_an_expression_func_object(QueryBuilder $queryBuilder, ArrayCollection $parameters, Expr $expr): void { - $dqlAlias = 'a'; + $dqlAlias = 'a'; $expression = 'a.foo between(:from_10, :to_10)'; $queryBuilder->expr()->willReturn($expr); @@ -37,16 +37,16 @@ public function it_returns_an_expression_func_object(QueryBuilder $queryBuilder, $queryBuilder->getParameters()->willReturn($parameters); $parameters->count()->willReturn(10); - $queryBuilder->setParameter('from_10', $this->from)->shouldBeCalled(); - $queryBuilder->setParameter('to_10', $this->to)->shouldBeCalled(); + $queryBuilder->setParameter('from_10', $this->from)->shouldBeCalled()->willReturn($queryBuilder); + $queryBuilder->setParameter('to_10', $this->to)->shouldBeCalled()->willReturn($queryBuilder); $this->isSatisfiedBy('foo')->shouldReturn(true); $this->modify($queryBuilder, $dqlAlias)->shouldReturn($expression); } - public function it_should_use_dql_alias_if_set(QueryBuilder $queryBuilder, ArrayCollection $parameters, Expr $expr) + public function it_should_use_dql_alias_if_set(QueryBuilder $queryBuilder, ArrayCollection $parameters, Expr $expr): void { - $dqlAlias = 'x'; + $dqlAlias = 'x'; $expression = 'x.foo between(:from_10, :to_10)'; $this->beConstructedWith($this->field, $this->from, $this->to, $dqlAlias); @@ -57,8 +57,8 @@ public function it_should_use_dql_alias_if_set(QueryBuilder $queryBuilder, Array $queryBuilder->getParameters()->willReturn($parameters); $parameters->count()->willReturn(10); - $queryBuilder->setParameter('from_10', $this->from)->shouldBeCalled(); - $queryBuilder->setParameter('to_10', $this->to)->shouldBeCalled(); + $queryBuilder->setParameter('from_10', $this->from)->shouldBeCalled()->willReturn($queryBuilder); + $queryBuilder->setParameter('to_10', $this->to)->shouldBeCalled()->willReturn($queryBuilder); $this->isSatisfiedBy('foo')->shouldReturn(true); $this->modify($queryBuilder, $dqlAlias)->shouldReturn($expression); diff --git a/spec/Condition/ComparisonSpec.php b/spec/Condition/ComparisonSpec.php index ed27442..d9b6263 100644 --- a/spec/Condition/ComparisonSpec.php +++ b/spec/Condition/ComparisonSpec.php @@ -1,54 +1,54 @@ beConstructedWith(Comparison::GT, 'age', 18, 'a'); } - public function it_is_an_expression() + public function it_is_an_expression(): void { $this->shouldBeAnInstanceOf(SpecificationInterface::class); } - public function it_returns_comparison_object(QueryBuilder $queryBuilder, ArrayCollection $parameters) + public function it_returns_comparison_object(QueryBuilder $queryBuilder, ArrayCollection $parameters): void { $queryBuilder->getParameters()->willReturn($parameters); $parameters->count()->willReturn(10); - $queryBuilder->setParameter('comparison_10', 18)->shouldBeCalled(); + $queryBuilder->setParameter('comparison_10', 18)->shouldBeCalled()->willReturn($queryBuilder); $this->isSatisfiedBy('foo')->shouldReturn(true); - $this->modify($queryBuilder, null) + $this->modify($queryBuilder, 'x') ->shouldReturn('a.age > :comparison_10'); } public function it_uses_comparison_specific_dql_alias_if_passed( QueryBuilder $queryBuilder, - ArrayCollection $parameters - ) { + ArrayCollection $parameters, + ): void { $this->beConstructedWith(Comparison::GT, 'age', 18, null); $queryBuilder->getParameters()->willReturn($parameters); $parameters->count()->willReturn(10); - $queryBuilder->setParameter('comparison_10', 18)->shouldBeCalled(); + $queryBuilder->setParameter('comparison_10', 18)->shouldBeCalled()->willReturn($queryBuilder); $this->isSatisfiedBy('foo')->shouldReturn(true); $this->modify($queryBuilder, 'x')->shouldReturn('x.age > :comparison_10'); } - public function it_validates_operator() + public function it_validates_operator(): void { $this->shouldThrow(InvalidArgumentException::class) ->during('__construct', ['==', 'age', 18, null]); diff --git a/spec/Condition/EqualsPropertySpec.php b/spec/Condition/EqualsPropertySpec.php index 44198c4..0ff435e 100644 --- a/spec/Condition/EqualsPropertySpec.php +++ b/spec/Condition/EqualsPropertySpec.php @@ -1,37 +1,37 @@ beConstructedWith('age', 'address', 'a'); } - public function it_is_an_expression() + public function it_is_an_expression(): void { $this->shouldBeAnInstanceOf(SpecificationInterface::class); } - public function it_returns_comparison_object(QueryBuilder $queryBuilder, ArrayCollection $parameters) + public function it_returns_comparison_object(QueryBuilder $queryBuilder, ArrayCollection $parameters): void { $queryBuilder->getParameters()->willReturn($parameters); $parameters->count()->willReturn(10); - $condition = $this->modify($queryBuilder, null) + $this->modify($queryBuilder, null) ->shouldReturn('a.age = a.address'); } public function it_uses_comparison_specific_dql_alias_if_passed( QueryBuilder $queryBuilder, - ArrayCollection $parameters - ) { + ArrayCollection $parameters, + ): void { $this->beConstructedWith('age', 'address', null); $queryBuilder->getParameters()->willReturn($parameters); diff --git a/spec/Condition/EqualsSpec.php b/spec/Condition/EqualsSpec.php index e229486..bdf0bc6 100644 --- a/spec/Condition/EqualsSpec.php +++ b/spec/Condition/EqualsSpec.php @@ -1,45 +1,45 @@ beConstructedWith('age', 18, 'a'); } - public function it_is_an_expression() + public function it_is_an_expression(): void { $this->shouldBeAnInstanceOf(SpecificationInterface::class); } - public function it_returns_comparison_object(QueryBuilder $queryBuilder, ArrayCollection $parameters) + public function it_returns_comparison_object(QueryBuilder $queryBuilder, ArrayCollection $parameters): void { $queryBuilder->getParameters()->willReturn($parameters); $parameters->count()->willReturn(10); - $queryBuilder->setParameter('comparison_10', 18)->shouldBeCalled(); + $queryBuilder->setParameter('comparison_10', 18)->shouldBeCalled()->willReturn($queryBuilder); - $condition = $this->modify($queryBuilder, null) + $this->modify($queryBuilder, null) ->shouldReturn('a.age = :comparison_10'); } public function it_uses_comparison_specific_dql_alias_if_passed( QueryBuilder $queryBuilder, - ArrayCollection $parameters - ) { + ArrayCollection $parameters, + ): void { $this->beConstructedWith('age', 18, null); $queryBuilder->getParameters()->willReturn($parameters); $parameters->count()->willReturn(10); - $queryBuilder->setParameter('comparison_10', 18)->shouldBeCalled(); + $queryBuilder->setParameter('comparison_10', 18)->shouldBeCalled()->willReturn($queryBuilder); $this->modify($queryBuilder, 'x')->shouldReturn('x.age = :comparison_10'); } diff --git a/spec/Condition/GreaterThanOrEqualsSpec.php b/spec/Condition/GreaterThanOrEqualsSpec.php index caac0bd..c7ffedd 100644 --- a/spec/Condition/GreaterThanOrEqualsSpec.php +++ b/spec/Condition/GreaterThanOrEqualsSpec.php @@ -1,45 +1,45 @@ beConstructedWith('age', 18, 'a'); } - public function it_is_an_expression() + public function it_is_an_expression(): void { $this->shouldBeAnInstanceOf(SpecificationInterface::class); } - public function it_returns_comparison_object(QueryBuilder $queryBuilder, ArrayCollection $parameters) + public function it_returns_comparison_object(QueryBuilder $queryBuilder, ArrayCollection $parameters): void { $queryBuilder->getParameters()->willReturn($parameters); $parameters->count()->willReturn(10); - $queryBuilder->setParameter('comparison_10', 18)->shouldBeCalled(); + $queryBuilder->setParameter('comparison_10', 18)->shouldBeCalled()->willReturn($queryBuilder); - $condition = $this->modify($queryBuilder, null) + $this->modify($queryBuilder, null) ->shouldReturn('a.age >= :comparison_10'); } public function it_uses_comparison_specific_dql_alias_if_passed( QueryBuilder $queryBuilder, - ArrayCollection $parameters - ) { + ArrayCollection $parameters, + ): void { $this->beConstructedWith('age', 18, null); $queryBuilder->getParameters()->willReturn($parameters); $parameters->count()->willReturn(10); - $queryBuilder->setParameter('comparison_10', 18)->shouldBeCalled(); + $queryBuilder->setParameter('comparison_10', 18)->shouldBeCalled()->willReturn($queryBuilder); $this->modify($queryBuilder, 'x')->shouldReturn('x.age >= :comparison_10'); } diff --git a/spec/Condition/GreaterThanSpec.php b/spec/Condition/GreaterThanSpec.php index 023d5b4..4971cc5 100644 --- a/spec/Condition/GreaterThanSpec.php +++ b/spec/Condition/GreaterThanSpec.php @@ -1,45 +1,45 @@ beConstructedWith('age', 18, 'a'); } - public function it_is_an_expression() + public function it_is_an_expression(): void { $this->shouldBeAnInstanceOf(SpecificationInterface::class); } - public function it_returns_comparison_object(QueryBuilder $queryBuilder, ArrayCollection $parameters) + public function it_returns_comparison_object(QueryBuilder $queryBuilder, ArrayCollection $parameters): void { $queryBuilder->getParameters()->willReturn($parameters); $parameters->count()->willReturn(10); - $queryBuilder->setParameter('comparison_10', 18)->shouldBeCalled(); + $queryBuilder->setParameter('comparison_10', 18)->shouldBeCalled()->willReturn($queryBuilder); - $condition = $this->modify($queryBuilder, null) + $this->modify($queryBuilder, null) ->shouldReturn('a.age > :comparison_10'); } public function it_uses_comparison_specific_dql_alias_if_passed( QueryBuilder $queryBuilder, - ArrayCollection $parameters - ) { + ArrayCollection $parameters, + ): void { $this->beConstructedWith('age', 18, null); $queryBuilder->getParameters()->willReturn($parameters); $parameters->count()->willReturn(10); - $queryBuilder->setParameter('comparison_10', 18)->shouldBeCalled(); + $queryBuilder->setParameter('comparison_10', 18)->shouldBeCalled()->willReturn($queryBuilder); $this->modify($queryBuilder, 'x')->shouldReturn('x.age > :comparison_10'); } diff --git a/spec/Condition/InSpec.php b/spec/Condition/InSpec.php index 4deaf9a..6e8bb05 100644 --- a/spec/Condition/InSpec.php +++ b/spec/Condition/InSpec.php @@ -1,33 +1,33 @@ beConstructedWith($this->field, $this->value); } - public function it_is_an_expression() + public function it_is_an_expression(): void { $this->shouldBeAnInstanceOf(SpecificationInterface::class); } - public function it_returns_an_expression_func_object(QueryBuilder $queryBuilder, ArrayCollection $parameters, Expr $expr) + public function it_returns_an_expression_func_object(QueryBuilder $queryBuilder, ArrayCollection $parameters, Expr $expr): void { - $dqlAlias = 'a'; - $expression = 'a.foo in(:in_10)'; + $dqlAlias = 'a'; + $expression = new Expr\Func('a.foo in', ':in_10'); $queryBuilder->expr()->willReturn($expr); $expr->in(sprintf('%s.%s', $dqlAlias, $this->field), ':in_10')->willReturn($expression); @@ -35,15 +35,15 @@ public function it_returns_an_expression_func_object(QueryBuilder $queryBuilder, $queryBuilder->getParameters()->willReturn($parameters); $parameters->count()->willReturn(10); - $queryBuilder->setParameter('in_10', $this->value)->shouldBeCalled(); + $queryBuilder->setParameter('in_10', $this->value)->shouldBeCalled()->willReturn($queryBuilder); $this->isSatisfiedBy('foo')->shouldReturn(true); - $this->modify($queryBuilder, $dqlAlias)->shouldReturn($expression); + $this->modify($queryBuilder, $dqlAlias)->shouldReturn((string) $expression); } - public function it_should_use_dql_alias_if_set(QueryBuilder $queryBuilder, ArrayCollection $parameters, Expr $expr) + public function it_should_use_dql_alias_if_set(QueryBuilder $queryBuilder, ArrayCollection $parameters, Expr $expr): void { - $dqlAlias = 'x'; - $expression = 'x.foo in(:in_10)'; + $dqlAlias = 'x'; + $expression = new Expr\Func('x.foo in', ':in_10'); $this->beConstructedWith($this->field, $this->value, $dqlAlias); @@ -53,8 +53,8 @@ public function it_should_use_dql_alias_if_set(QueryBuilder $queryBuilder, Array $queryBuilder->getParameters()->willReturn($parameters); $parameters->count()->willReturn(10); - $queryBuilder->setParameter('in_10', $this->value)->shouldBeCalled(); + $queryBuilder->setParameter('in_10', $this->value)->shouldBeCalled()->willReturn($queryBuilder); $this->isSatisfiedBy('foo')->shouldReturn(true); - $this->modify($queryBuilder, $dqlAlias)->shouldReturn($expression); + $this->modify($queryBuilder, $dqlAlias)->shouldReturn((string) $expression); } } diff --git a/spec/Condition/IsInstanceOfSpec.php b/spec/Condition/IsInstanceOfSpec.php index 74593ff..8b3fbc8 100644 --- a/spec/Condition/IsInstanceOfSpec.php +++ b/spec/Condition/IsInstanceOfSpec.php @@ -1,49 +1,52 @@ beConstructedWith($this->field, $this->className, $this->dqlAlias); } - public function it_is_an_expression() + public function it_is_an_expression(): void { $this->shouldBeAnInstanceOf(SpecificationInterface::class); } - public function it_calls_is_instance_of(QueryBuilder $queryBuilder, Expr $expr) + public function it_calls_is_instance_of(QueryBuilder $queryBuilder, Expr $expr): void { - $expression = 'a.foo instance of ' . $this->className; + $expression = new Expr\Comparison('a.foo', 'instance of', $this->className); $queryBuilder->expr()->willReturn($expr); - $expr->isInstanceOf(sprintf('%s.%s', $this->dqlAlias, $this->field), $this->className)->willReturn($expression); + $expr->isInstanceOf(sprintf('%s.%s', $this->dqlAlias, $this->field), $this->className)->shouldBeCalled()->willReturn($expression); $this->isSatisfiedBy('foo')->shouldReturn(true); - $this->modify($queryBuilder, 'b')->shouldReturn($expression); + $this->modify($queryBuilder, 'b')->shouldReturn((string) $expression); } - public function it_uses_dql_alias_if_passed(QueryBuilder $queryBuilder, Expr $expr) + public function it_uses_dql_alias_if_passed(QueryBuilder $queryBuilder, Expr $expr): void { $dqlAlias = 'x'; + $expression = new Expr\Comparison('x.foo', 'instance of', $this->className); + $this->beConstructedWith($this->field, $this->className, null); $queryBuilder->expr()->willReturn($expr); - $expr->isInstanceOf(sprintf('%s.%s', $dqlAlias, $this->field), $this->className)->shouldBeCalled(); + $expr->isInstanceOf(sprintf('%s.%s', $dqlAlias, $this->field), $this->className)->shouldBeCalled()->willReturn($expression); $this->isSatisfiedBy('foo')->shouldReturn(true); - $this->modify($queryBuilder, $dqlAlias); + $this->modify($queryBuilder, $dqlAlias)->shouldReturn((string) $expression); } } diff --git a/spec/Condition/IsMemberOfSpec.php b/spec/Condition/IsMemberOfSpec.php index bc2b4a3..a3902e3 100644 --- a/spec/Condition/IsMemberOfSpec.php +++ b/spec/Condition/IsMemberOfSpec.php @@ -1,49 +1,51 @@ beConstructedWith($this->field, $this->className, $this->dqlAlias); } - public function it_is_an_expression() + public function it_is_an_expression(): void { $this->shouldBeAnInstanceOf(SpecificationInterface::class); } - public function it_calls_is_instance_of(QueryBuilder $queryBuilder, Expr $expr) + public function it_calls_is_instance_of(QueryBuilder $queryBuilder, Expr $expr): void { - $expression = 'a.foo member of ' . $this->className; + $expression = new Expr\Comparison($this->field, 'member of', $this->className); $queryBuilder->expr()->willReturn($expr); $expr->isMemberOf(sprintf('%s.%s', $this->dqlAlias, $this->field), $this->className)->willReturn($expression); $this->isSatisfiedBy('foo')->shouldReturn(true); - $this->modify($queryBuilder, 'b')->shouldReturn($expression); + $this->modify($queryBuilder, 'b')->shouldReturn((string) $expression); } - public function it_uses_dql_alias_if_passed(QueryBuilder $queryBuilder, Expr $expr) + public function it_uses_dql_alias_if_passed(QueryBuilder $queryBuilder, Expr $expr): void { $dqlAlias = 'x'; + $aliasedField = sprintf('%s.%s', $dqlAlias, $this->field); + $expression = new Expr\Comparison($aliasedField, 'member of', $this->className); + $this->beConstructedWith($this->field, $this->className, null); $queryBuilder->expr()->willReturn($expr); - $expr->isMemberOf(sprintf('%s.%s', $dqlAlias, $this->field), $this->className)->shouldBeCalled(); + $expr->isMemberOf($aliasedField, $this->className)->shouldBeCalled()->willReturn($expression); $this->isSatisfiedBy('foo')->shouldReturn(true); - $this->modify($queryBuilder, $dqlAlias); + $this->modify($queryBuilder, $dqlAlias)->shouldReturn((string) $expression); } } diff --git a/spec/Condition/IsNotNullSpec.php b/spec/Condition/IsNotNullSpec.php index 0bcade7..21f9e9d 100644 --- a/spec/Condition/IsNotNullSpec.php +++ b/spec/Condition/IsNotNullSpec.php @@ -1,28 +1,29 @@ beConstructedWith($this->field, $this->dqlAlias); } - public function it_is_an_expression() + public function it_is_an_expression(): void { $this->shouldBeAnInstanceOf(SpecificationInterface::class); } - public function it_calls_not_null(QueryBuilder $queryBuilder, Expr $expr) + public function it_calls_not_null(QueryBuilder $queryBuilder, Expr $expr): void { $expression = 'a.foo is not null'; @@ -32,13 +33,16 @@ public function it_calls_not_null(QueryBuilder $queryBuilder, Expr $expr) $this->modify($queryBuilder, null)->shouldReturn($expression); } - public function it_uses_dql_alias_if_passed(QueryBuilder $queryBuilder, Expr $expr) + public function it_uses_dql_alias_if_passed(QueryBuilder $queryBuilder, Expr $expr): void { $dqlAlias = 'x'; + $expression = 'x.foo is not null'; + $this->beConstructedWith($this->field, null); + $queryBuilder->expr()->willReturn($expr); + $expr->isNotNull(sprintf('%s.%s', $dqlAlias, $this->field))->willReturn($expression); - $expr->isNotNull(sprintf('%s.%s', $dqlAlias, $this->field))->shouldBeCalled(); - $this->modify($queryBuilder, $dqlAlias); + $this->modify($queryBuilder, $dqlAlias)->shouldReturn($expression); } } diff --git a/spec/Condition/IsNullSpec.php b/spec/Condition/IsNullSpec.php index 275155e..70eca69 100644 --- a/spec/Condition/IsNullSpec.php +++ b/spec/Condition/IsNullSpec.php @@ -1,29 +1,29 @@ beConstructedWith($this->field, $this->dqlAlias); } - public function it_is_an_expression() + public function it_is_an_expression(): void { $this->shouldBeAnInstanceOf(SpecificationInterface::class); } - public function it_calls_null(QueryBuilder $queryBuilder, Expr $expr) + public function it_calls_null(QueryBuilder $queryBuilder, Expr $expr): void { $expression = 'a.foo is null'; @@ -34,15 +34,16 @@ public function it_calls_null(QueryBuilder $queryBuilder, Expr $expr) $this->modify($queryBuilder, 'b')->shouldReturn($expression); } - public function it_uses_dql_alias_if_passed(QueryBuilder $queryBuilder, Expr $expr) + public function it_uses_dql_alias_if_passed(QueryBuilder $queryBuilder, Expr $expr): void { $dqlAlias = 'x'; + $expression = 'x.foo is null'; + $this->beConstructedWith($this->field, null); $queryBuilder->expr()->willReturn($expr); - - $expr->isNull(sprintf('%s.%s', $dqlAlias, $this->field))->shouldBeCalled(); + $expr->isNull(sprintf('%s.%s', $dqlAlias, $this->field))->willReturn($expression); $this->isSatisfiedBy('foo')->shouldReturn(true); - $this->modify($queryBuilder, $dqlAlias); + $this->modify($queryBuilder, $dqlAlias)->shouldReturn($expression); } } diff --git a/spec/Condition/LessThanOrEqualsSpec.php b/spec/Condition/LessThanOrEqualsSpec.php index 06eba13..e237e19 100644 --- a/spec/Condition/LessThanOrEqualsSpec.php +++ b/spec/Condition/LessThanOrEqualsSpec.php @@ -1,45 +1,45 @@ beConstructedWith('age', 18, 'a'); } - public function it_is_an_expression() + public function it_is_an_expression(): void { $this->shouldBeAnInstanceOf(SpecificationInterface::class); } - public function it_returns_comparison_object(QueryBuilder $queryBuilder, ArrayCollection $parameters) + public function it_returns_comparison_object(QueryBuilder $queryBuilder, ArrayCollection $parameters): void { $queryBuilder->getParameters()->willReturn($parameters); $parameters->count()->willReturn(10); - $queryBuilder->setParameter('comparison_10', 18)->shouldBeCalled(); + $queryBuilder->setParameter('comparison_10', 18)->shouldBeCalled()->willReturn($queryBuilder); - $condition = $this->modify($queryBuilder, null) + $this->modify($queryBuilder, null) ->shouldReturn('a.age <= :comparison_10'); } public function it_uses_comparison_specific_dql_alias_if_passed( QueryBuilder $queryBuilder, - ArrayCollection $parameters - ) { + ArrayCollection $parameters, + ): void { $this->beConstructedWith('age', 18, null); $queryBuilder->getParameters()->willReturn($parameters); $parameters->count()->willReturn(10); - $queryBuilder->setParameter('comparison_10', 18)->shouldBeCalled(); + $queryBuilder->setParameter('comparison_10', 18)->shouldBeCalled()->willReturn($queryBuilder); $this->modify($queryBuilder, 'x')->shouldReturn('x.age <= :comparison_10'); } diff --git a/spec/Condition/LessThanSpec.php b/spec/Condition/LessThanSpec.php index 6d6bf7c..6f05541 100644 --- a/spec/Condition/LessThanSpec.php +++ b/spec/Condition/LessThanSpec.php @@ -1,45 +1,45 @@ beConstructedWith('age', 18, 'a'); } - public function it_is_an_expression() + public function it_is_an_expression(): void { $this->shouldBeAnInstanceOf(SpecificationInterface::class); } - public function it_returns_comparison_object(QueryBuilder $queryBuilder, ArrayCollection $parameters) + public function it_returns_comparison_object(QueryBuilder $queryBuilder, ArrayCollection $parameters): void { $queryBuilder->getParameters()->willReturn($parameters); $parameters->count()->willReturn(10); - $queryBuilder->setParameter('comparison_10', 18)->shouldBeCalled(); + $queryBuilder->setParameter('comparison_10', 18)->shouldBeCalled()->willReturn($queryBuilder); - $condition = $this->modify($queryBuilder, null) + $this->modify($queryBuilder, null) ->shouldReturn('a.age < :comparison_10'); } public function it_uses_comparison_specific_dql_alias_if_passed( QueryBuilder $queryBuilder, - ArrayCollection $parameters - ) { + ArrayCollection $parameters, + ): void { $this->beConstructedWith('age', 18, null); $queryBuilder->getParameters()->willReturn($parameters); $parameters->count()->willReturn(10); - $queryBuilder->setParameter('comparison_10', 18)->shouldBeCalled(); + $queryBuilder->setParameter('comparison_10', 18)->shouldBeCalled()->willReturn($queryBuilder); $this->modify($queryBuilder, 'x')->shouldReturn('x.age < :comparison_10'); } diff --git a/spec/Condition/LikeSpec.php b/spec/Condition/LikeSpec.php index 677086c..38f696f 100644 --- a/spec/Condition/LikeSpec.php +++ b/spec/Condition/LikeSpec.php @@ -1,64 +1,64 @@ beConstructedWith($this->field, $this->value, Like::CONTAINS, 'dqlAlias'); } - public function it_is_a_specification() + public function it_is_a_specification(): void { $this->shouldHaveType(SpecificationInterface::class); } public function it_surrounds_with_wildcards_when_using_contains( QueryBuilder $queryBuilder, - ArrayCollection $parameters - ) { + ArrayCollection $parameters, + ): void { $this->beConstructedWith($this->field, $this->value, Like::CONTAINS, 'dqlAlias'); $queryBuilder->getParameters()->willReturn($parameters); $parameters->count()->willReturn(1); - $queryBuilder->setParameter('comparison_1', '%bar%')->shouldBeCalled(); + $queryBuilder->setParameter('comparison_1', '%bar%')->shouldBeCalled()->willReturn($queryBuilder); $this->modify($queryBuilder, null); } public function it_starts_with_wildcard_when_using_ends_with( QueryBuilder $queryBuilder, - ArrayCollection $parameters - ) { + ArrayCollection $parameters, + ): void { $this->beConstructedWith($this->field, $this->value, Like::ENDS_WITH, 'dqlAlias'); $queryBuilder->getParameters()->willReturn($parameters); $parameters->count()->willReturn(1); - $queryBuilder->setParameter('comparison_1', '%bar')->shouldBeCalled(); + $queryBuilder->setParameter('comparison_1', '%bar')->shouldBeCalled()->willReturn($queryBuilder); $this->modify($queryBuilder, null); } public function it_ends_with_wildcard_when_using_starts_with( QueryBuilder $queryBuilder, - ArrayCollection $parameters - ) { + ArrayCollection $parameters, + ): void { $this->beConstructedWith($this->field, $this->value, Like::STARTS_WITH, 'dqlAlias'); $queryBuilder->getParameters()->willReturn($parameters); $parameters->count()->willReturn(1); - $queryBuilder->setParameter('comparison_1', 'bar%')->shouldBeCalled(); + $queryBuilder->setParameter('comparison_1', 'bar%')->shouldBeCalled()->willReturn($queryBuilder); $this->modify($queryBuilder, null); } diff --git a/spec/Condition/NotEqualsSpec.php b/spec/Condition/NotEqualsSpec.php index 38b857c..5a76ef7 100644 --- a/spec/Condition/NotEqualsSpec.php +++ b/spec/Condition/NotEqualsSpec.php @@ -1,45 +1,45 @@ beConstructedWith('age', 18, 'a'); } - public function it_is_an_expression() + public function it_is_an_expression(): void { $this->shouldBeAnInstanceOf(SpecificationInterface::class); } - public function it_returns_comparison_object(QueryBuilder $queryBuilder, ArrayCollection $parameters) + public function it_returns_comparison_object(QueryBuilder $queryBuilder, ArrayCollection $parameters): void { $queryBuilder->getParameters()->willReturn($parameters); $parameters->count()->willReturn(10); - $queryBuilder->setParameter('comparison_10', 18)->shouldBeCalled(); + $queryBuilder->setParameter('comparison_10', 18)->shouldBeCalled()->willReturn($queryBuilder); - $condition = $this->modify($queryBuilder, null) + $this->modify($queryBuilder, null) ->shouldReturn('a.age <> :comparison_10'); } public function it_uses_comparison_specific_dql_alias_if_passed( QueryBuilder $queryBuilder, - ArrayCollection $parameters - ) { + ArrayCollection $parameters, + ): void { $this->beConstructedWith('age', 18, null); $queryBuilder->getParameters()->willReturn($parameters); $parameters->count()->willReturn(10); - $queryBuilder->setParameter('comparison_10', 18)->shouldBeCalled(); + $queryBuilder->setParameter('comparison_10', 18)->shouldBeCalled()->willReturn($queryBuilder); $this->modify($queryBuilder, 'x')->shouldReturn('x.age <> :comparison_10'); } diff --git a/spec/Condition/NotInSpec.php b/spec/Condition/NotInSpec.php index 0bca1e1..1840142 100644 --- a/spec/Condition/NotInSpec.php +++ b/spec/Condition/NotInSpec.php @@ -1,62 +1,64 @@ beConstructedWith($this->field, $this->value); } - public function it_is_an_expression() + public function it_is_an_expression(): void { $this->shouldBeAnInstanceOf(SpecificationInterface::class); - $this->shouldBeAnInstanceOf(In::class); + $this->shouldBeAnInstanceOf(NotIn::class); } - public function it_returns_an_expression_func_object(QueryBuilder $queryBuilder, ArrayCollection $parameters, Expr $expr) + public function it_returns_an_expression_func_object(QueryBuilder $queryBuilder, ArrayCollection $parameters, Expr $expr): void { - $dqlAlias = 'a'; - $expression = 'a.foo not in(:not_in_10)'; + $dqlAlias = 'a'; + $aliasedField = sprintf('%s.%s', $dqlAlias, $this->field); + $expression = new Expr\Func(sprintf('%s not in', $aliasedField), ':not_in_10'); $queryBuilder->expr()->willReturn($expr); - $expr->notIn(sprintf('%s.%s', $dqlAlias, $this->field), ':not_in_10')->willReturn($expression); + $expr->notIn($aliasedField, ':not_in_10')->willReturn($expression); $queryBuilder->getParameters()->willReturn($parameters); $parameters->count()->willReturn(10); - $queryBuilder->setParameter('not_in_10', $this->value)->shouldBeCalled(); + $queryBuilder->setParameter('not_in_10', $this->value)->shouldBeCalled()->willReturn($queryBuilder); $this->isSatisfiedBy('foo')->shouldReturn(true); - $this->modify($queryBuilder, $dqlAlias)->shouldReturn($expression); + $this->modify($queryBuilder, $dqlAlias)->shouldReturn((string) $expression); } - public function it_should_use_dql_alias_if_set(QueryBuilder $queryBuilder, ArrayCollection $parameters, Expr $expr) + public function it_should_use_dql_alias_if_set(QueryBuilder $queryBuilder, ArrayCollection $parameters, Expr $expr): void { - $dqlAlias = 'x'; - $expression = 'x.foo not in(:not_in_10)'; + $dqlAlias = 'x'; + $aliasedField = sprintf('%s.%s', $dqlAlias, $this->field); + $expression = new Expr\Func(sprintf('%s not in', $aliasedField), ':not_in_10'); $this->beConstructedWith($this->field, $this->value, $dqlAlias); $queryBuilder->expr()->willReturn($expr); - $expr->notIn(sprintf('%s.%s', $dqlAlias, $this->field), ':not_in_10')->willReturn($expression); + $expr->notIn($aliasedField, ':not_in_10')->willReturn($expression); $queryBuilder->getParameters()->willReturn($parameters); $parameters->count()->willReturn(10); - $queryBuilder->setParameter('not_in_10', $this->value)->shouldBeCalled(); + $queryBuilder->setParameter('not_in_10', $this->value)->shouldBeCalled()->willReturn($queryBuilder); $this->isSatisfiedBy('foo')->shouldReturn(true); - $this->modify($queryBuilder, $dqlAlias)->shouldReturn($expression); + $this->modify($queryBuilder, $dqlAlias)->shouldReturn((string) $expression); } } diff --git a/spec/Logic/AndXSpec.php b/spec/Logic/AndXSpec.php index d27c905..24579a2 100644 --- a/spec/Logic/AndXSpec.php +++ b/spec/Logic/AndXSpec.php @@ -1,15 +1,15 @@ shouldHaveType(SpecificationInterface::class); $this->shouldHaveType(Composite::class); diff --git a/spec/Logic/CompositeSpec.php b/spec/Logic/CompositeSpec.php index 6bd55d8..8774f7f 100644 --- a/spec/Logic/CompositeSpec.php +++ b/spec/Logic/CompositeSpec.php @@ -1,57 +1,58 @@ beConstructedWith(self::EXPRESSION, [$specificationA, $specificationB]); } - public function it_is_a_specification() + public function it_is_a_specification(): void { $this->shouldHaveType(Composite::class); } public function it_supports_conditions( QueryBuilder $queryBuilder, - Expr $expression, + Expr $expr, SpecificationInterface $conditionA, SpecificationInterface $conditionB, - $x, - $y - ) { + ): void { $this->beConstructedWith(self::EXPRESSION, [$conditionA, $conditionB]); $dqlAlias = 'a'; + $x = 'x'; + $y = 'y'; + $expression = new Expr\Andx([$x, $y]); $conditionA->isSatisfiedBy('foo')->willReturn(true); $conditionB->isSatisfiedBy('foo')->willReturn(true); - $conditionA->modify($queryBuilder, $dqlAlias)->willReturn($x); - $conditionB->modify($queryBuilder, $dqlAlias)->willReturn($y); - $queryBuilder->expr()->willReturn($expression); + $conditionA->modify($queryBuilder, $dqlAlias)->shouldBeCalled()->willReturn($x); + $conditionB->modify($queryBuilder, $dqlAlias)->shouldBeCalled()->willReturn($y); + $queryBuilder->expr()->willReturn($expr); - $expression->{self::EXPRESSION}($x, $y)->shouldBeCalled(); + $expr->{self::EXPRESSION}($x, $y)->shouldBeCalled()->willReturn($expression); $this->isSatisfiedBy('foo')->shouldReturn(true); - $this->modify($queryBuilder, $dqlAlias); + $this->modify($queryBuilder, $dqlAlias)->shouldReturn((string) $expression); } public function it_should_fail_satisfaction_if_child_fails( SpecificationInterface $specificationA, - SpecificationInterface $specificationB - ) { + SpecificationInterface $specificationB, + ): void { $this->beConstructedWith(self::EXPRESSION, [$specificationA, $specificationB]); $specificationA->isSatisfiedBy('foo')->willReturn(true); @@ -64,8 +65,8 @@ public function it_should_return_null_for_specifications_without_conditions( QueryBuilder $queryBuilder, Expr $expression, SpecificationInterface $specificationA, - SpecificationInterface $specificationB - ) { + SpecificationInterface $specificationB, + ): void { $this->beConstructedWith(self::EXPRESSION, [$specificationA, $specificationB]); $dqlAlias = 'a'; @@ -79,8 +80,8 @@ public function it_should_return_null_for_specifications_without_conditions( public function it_should_throw_exception_on_invalid_type( SpecificationInterface $specificationA, - SpecificationInterface $specificationB - ) { + SpecificationInterface $specificationB, + ): void { $type = 'foo'; $this->beConstructedWith($type, [$specificationA, $specificationB]); @@ -89,7 +90,7 @@ public function it_should_throw_exception_on_invalid_type( ->during('__construct', [$type, [$specificationA, $specificationB]]); } - public function it_should_throw_exception_on_invalid_child() + public function it_should_throw_exception_on_invalid_child(): void { $child = 'bar'; diff --git a/spec/Logic/NotSpec.php b/spec/Logic/NotSpec.php index 1db2ab8..7c456a6 100644 --- a/spec/Logic/NotSpec.php +++ b/spec/Logic/NotSpec.php @@ -1,53 +1,54 @@ beConstructedWith($condition, null); + $this->beConstructedWith($condition); } - public function it_calls_parent_match(QueryBuilder $queryBuilder, Expr $expr, SpecificationInterface $condition) + public function it_calls_parent_match(QueryBuilder $queryBuilder, Expr $expr, SpecificationInterface $condition): void { - $dqlAlias = 'a'; - $expression = 'expression'; - $parentExpression = 'foo'; + $dqlAlias = 'a'; + $aliasedField = sprintf('%s.%s', $dqlAlias, 'foo'); + $parentExpression = new Expr\Func($aliasedField, ':bar'); + $expression = new Expr\Func('NOT', [$parentExpression]); $queryBuilder->expr()->willReturn($expr); - $condition->modify($queryBuilder, $dqlAlias)->willReturn($parentExpression); + $condition->modify($queryBuilder, $dqlAlias)->willReturn((string) $parentExpression); $expr->not($parentExpression)->willReturn($expression); - $this->modify($queryBuilder, $dqlAlias)->shouldReturn($expression); + $this->modify($queryBuilder, $dqlAlias)->shouldReturn((string) $expression); } - public function it_modifies_parent_query(QueryBuilder $queryBuilder, SpecificationInterface $specification) + public function it_modifies_parent_query(QueryBuilder $queryBuilder, SpecificationInterface $specification): void { $dqlAlias = 'a'; - $this->beConstructedWith($specification, null); + $this->beConstructedWith($specification); - $specification->modify($queryBuilder, $dqlAlias)->shouldBeCalled(); + $specification->modify($queryBuilder, $dqlAlias)->shouldBeCalled()->willReturn(null); $this->modify($queryBuilder, $dqlAlias); } - public function it_should_call_supports_on_parent(SpecificationInterface $specification) + public function it_should_call_supports_on_parent(SpecificationInterface $specification): void { $className = 'foo'; - $this->beConstructedWith($specification, null); + $this->beConstructedWith($specification); $specification->isSatisfiedBy($className)->shouldBeCalled(); $this->isSatisfiedBy($className); } - public function it_does_not_modify_parent_query(QueryBuilder $queryBuilder) + public function it_does_not_modify_parent_query(QueryBuilder $queryBuilder): void { $this->modify($queryBuilder, 'a'); } diff --git a/spec/Logic/OrXSpec.php b/spec/Logic/OrXSpec.php index e282397..e55a451 100644 --- a/spec/Logic/OrXSpec.php +++ b/spec/Logic/OrXSpec.php @@ -1,15 +1,15 @@ shouldHaveType(SpecificationInterface::class); $this->shouldHaveType(Composite::class); diff --git a/spec/Query/GroupBySpec.php b/spec/Query/GroupBySpec.php index fb809bd..c2c80cf 100644 --- a/spec/Query/GroupBySpec.php +++ b/spec/Query/GroupBySpec.php @@ -1,47 +1,44 @@ beConstructedWith($this->field, GroupBy::ADD_GROUP_BY, $this->alias); + $this->beConstructedWith($this->field, GroupBy::ADD_GROUP_BY); $this->isSatisfiedBy('foo')->shouldReturn(true); } - public function it_calls_groupBy_on_query_builder(QueryBuilder $queryBuilder) + public function it_calls_groupBy_on_query_builder(QueryBuilder $queryBuilder): void { - $this->beConstructedWith($this->field, GroupBy::GROUP_BY, $this->alias); + $this->beConstructedWith($this->field, GroupBy::GROUP_BY); - $arg = sprintf('%s.%s', $this->alias, $this->field); - $queryBuilder->groupBy($arg)->shouldBeCalled(); + $queryBuilder->groupBy($this->field)->shouldBeCalled()->willReturn($queryBuilder); - $this->modify($queryBuilder, $this->alias); + $this->modify($queryBuilder); } - public function it_calls_addGroupBy_on_query_builder(QueryBuilder $queryBuilder) + public function it_calls_addGroupBy_on_query_builder(QueryBuilder $queryBuilder): void { - $this->beConstructedWith($this->field, GroupBy::ADD_GROUP_BY, $this->alias); + $this->beConstructedWith($this->field, GroupBy::ADD_GROUP_BY); - $arg = sprintf('%s.%s', $this->alias, $this->field); - $queryBuilder->addGroupBy($arg)->shouldBeCalled(); + $queryBuilder->addGroupBy($this->field)->shouldBeCalled()->willReturn($queryBuilder); - $this->modify($queryBuilder, $this->alias); + $this->modify($queryBuilder); } - public function it_throws_exception_when_setting_illegal_type() + public function it_throws_exception_when_setting_illegal_type(): void { - $this->beConstructedWith($this->field, 'foo', $this->alias); + $this->beConstructedWith($this->field, 'foo'); $this->shouldThrow(InvalidArgumentException::class)->duringInstantiation(); } } diff --git a/spec/Query/HavingSpec.php b/spec/Query/HavingSpec.php index 7b1e76d..6c991b4 100644 --- a/spec/Query/HavingSpec.php +++ b/spec/Query/HavingSpec.php @@ -1,71 +1,70 @@ */ class HavingSpec extends ObjectBehavior { - private $dqlAlias = 'a'; + private string $dqlAlias = 'a'; - public function let(SpecificationInterface $specification) + public function let(SpecificationInterface $specification): void { $this->beConstructedWith($specification); } - public function it_should_support_anything() + public function it_should_support_anything(): void { $this->isSatisfiedBy('foo')->shouldReturn(true); } - public function it_calls_modify_on_child_specification(QueryBuilder $queryBuilder, SpecificationInterface $specification) + public function it_calls_modify_on_child_specification(QueryBuilder $queryBuilder, SpecificationInterface $specification): void { - $specification->modify($queryBuilder, $this->dqlAlias)->shouldBeCalled(); + $condition = 'foo'; + $specification->modify($queryBuilder, $this->dqlAlias)->shouldBeCalled()->willReturn($condition); + $queryBuilder->andHaving($condition)->shouldBeCalled()->willReturn($queryBuilder); $this->modify($queryBuilder, $this->dqlAlias); } - public function it_calls_having_on_query_builder(QueryBuilder $queryBuilder, SpecificationInterface $specification) + public function it_calls_having_on_query_builder(QueryBuilder $queryBuilder, SpecificationInterface $specification): void { $condition = 'foo'; $specification->modify($queryBuilder, $this->dqlAlias)->willReturn($condition); + $queryBuilder->having($condition)->shouldBeCalled()->willReturn($queryBuilder); - $this->setType(Having::HAVING); - $queryBuilder->having($condition)->shouldBeCalled(); - - $this->modify($queryBuilder, $this->dqlAlias); + $having = $this->setType(Having::HAVING); + $having->modify($queryBuilder, $this->dqlAlias); } - public function it_calls_andHaving_on_query_builder(QueryBuilder $queryBuilder, SpecificationInterface $specification) + public function it_calls_andHaving_on_query_builder(QueryBuilder $queryBuilder, SpecificationInterface $specification): void { $condition = 'foo'; $specification->modify($queryBuilder, $this->dqlAlias)->willReturn($condition); + $queryBuilder->andHaving($condition)->shouldBeCalled()->willReturn($queryBuilder); - $this->setType(Having::AND_HAVING); - $queryBuilder->andHaving($condition)->shouldBeCalled(); - - $this->modify($queryBuilder, $this->dqlAlias); + $having = $this->setType(Having::AND_HAVING); + $having->modify($queryBuilder, $this->dqlAlias); } - public function it_calls_orHaving_on_query_builder(QueryBuilder $queryBuilder, SpecificationInterface $specification) + public function it_calls_orHaving_on_query_builder(QueryBuilder $queryBuilder, SpecificationInterface $specification): void { $condition = 'foo'; $specification->modify($queryBuilder, $this->dqlAlias)->willReturn($condition); + $queryBuilder->orHaving($condition)->shouldBeCalled()->willReturn($queryBuilder); - $this->setType(Having::OR_HAVING); - $queryBuilder->orHaving($condition)->shouldBeCalled(); - - $this->modify($queryBuilder, $this->dqlAlias); + $having = $this->setType(Having::OR_HAVING); + $having->modify($queryBuilder, $this->dqlAlias); } - public function it_throws_exception_when_setting_illegal_type() + public function it_throws_exception_when_setting_illegal_type(): void { $this->shouldThrow(InvalidArgumentException::class) ->during('setType', ['foo']); diff --git a/spec/Query/IndexBySpec.php b/spec/Query/IndexBySpec.php index bce1ff7..849bf5e 100644 --- a/spec/Query/IndexBySpec.php +++ b/spec/Query/IndexBySpec.php @@ -1,25 +1,26 @@ beConstructedWith($this->field, $this->alias); } - public function it_should_modify_query_builder(QueryBuilder $queryBuilder) + public function it_should_modify_query_builder(QueryBuilder $queryBuilder): void { $property = sprintf('%s.%s', $this->alias, $this->field); - $queryBuilder->indexBy($this->alias, $property)->shouldBeCalled(); + $queryBuilder->indexBy($this->alias, $property)->shouldBeCalled()->willReturn($queryBuilder); $this->isSatisfiedBy('foo')->shouldReturn(true); $this->modify($queryBuilder, $this->alias); diff --git a/spec/Query/InnerJoinSpec.php b/spec/Query/InnerJoinSpec.php index d6da001..e162162 100644 --- a/spec/Query/InnerJoinSpec.php +++ b/spec/Query/InnerJoinSpec.php @@ -1,33 +1,33 @@ beConstructedWith('user', 'authUser', 'a'); } - public function it_is_a_specification() + public function it_is_a_specification(): void { $this->shouldHaveType(SpecificationInterface::class); } - public function it_joins_with_default_dql_alias(QueryBuilder $queryBuilder) + public function it_joins_with_default_dql_alias(QueryBuilder $queryBuilder): void { - $queryBuilder->innerJoin('a.user', 'authUser', null, null, null)->shouldBeCalled(); + $queryBuilder->innerJoin('a.user', 'authUser', null, null, null)->shouldBeCalled()->willReturn($queryBuilder); $this->modify($queryBuilder, 'a'); } - public function it_uses_local_alias_if_global_was_not_set(QueryBuilder $queryBuilder) + public function it_uses_local_alias_if_global_was_not_set(QueryBuilder $queryBuilder): void { $this->beConstructedWith('user', 'authUser'); - $queryBuilder->innerJoin('b.user', 'authUser', null, null, null)->shouldBeCalled(); + $queryBuilder->innerJoin('b.user', 'authUser', null, null, null)->shouldBeCalled()->willReturn($queryBuilder); $this->modify($queryBuilder, 'b'); } } diff --git a/spec/Query/JoinSpec.php b/spec/Query/JoinSpec.php index cda6b76..4b1fa63 100644 --- a/spec/Query/JoinSpec.php +++ b/spec/Query/JoinSpec.php @@ -1,89 +1,87 @@ beConstructedWith('user', 'authUser', 'a'); } - public function it_is_a_specification() + public function it_is_a_specification(): void { $this->shouldHaveType(SpecificationInterface::class); } - public function it_should_support_anything() + public function it_should_support_anything(): void { $this->isSatisfiedBy('foo')->shouldReturn(true); } - public function it_joins_with_default_dql_alias(QueryBuilder $queryBuilder) + public function it_joins_with_default_dql_alias(QueryBuilder $queryBuilder): void { - $queryBuilder->join('a.user', 'authUser', null, null, null)->shouldBeCalled(); + $queryBuilder->join('a.user', 'authUser', null, null, null)->shouldBeCalled()->willReturn($queryBuilder); $this->modify($queryBuilder, 'a'); } - public function it_uses_local_alias_if_global_was_not_set(QueryBuilder $queryBuilder) + public function it_uses_local_alias_if_global_was_not_set(QueryBuilder $queryBuilder): void { $this->beConstructedWith('user', 'authUser'); - $queryBuilder->join('b.user', 'authUser', null, null, null)->shouldBeCalled(); + $queryBuilder->join('b.user', 'authUser', null, null, null)->shouldBeCalled()->willReturn($queryBuilder); $this->modify($queryBuilder, 'b'); } - public function it_should_use_be_able_to_use_join_conditions(QueryBuilder $queryBuilder) + public function it_should_use_be_able_to_use_join_conditions(QueryBuilder $queryBuilder): void { - $joinType = DoctrineJoin::ON; + $joinType = DoctrineJoin::ON; $joinCondition = 'join condition'; $this->beConstructedWith('user', 'authUser'); - $this->setConditionType($joinType)->shouldReturn($this); - $this->setCondition($joinCondition)->shouldReturn($this); + $join = $this->setConditionType($joinType)->setCondition($joinCondition); - $queryBuilder->join('a.user', 'authUser', $joinType, $joinCondition, null)->shouldBeCalled(); + $queryBuilder->join('a.user', 'authUser', $joinType, $joinCondition, null)->shouldBeCalled()->willReturn($queryBuilder); - $this->modify($queryBuilder, 'a'); + $join->modify($queryBuilder, 'a'); } - public function it_should_be_able_to_set_index_by_for_join(QueryBuilder $queryBuilder) + public function it_should_be_able_to_set_index_by_for_join(QueryBuilder $queryBuilder): void { $indexedBy = 'index_by'; $this->beConstructedWith('user', 'authUser'); - $queryBuilder->join('a.user', 'authUser', null, null, $indexedBy)->shouldBeCalled(); + $queryBuilder->join('a.user', 'authUser', null, null, $indexedBy)->shouldBeCalled()->willReturn($queryBuilder); - $this->setIndexedBy($indexedBy)->shouldReturn($this); + $join = $this->setIndexedBy($indexedBy); - $this->modify($queryBuilder, 'a'); + $join->modify($queryBuilder, 'a'); } - public function it_should_accept_specifications_as_condition(QueryBuilder $queryBuilder, SpecificationInterface $specification) + public function it_should_accept_specifications_as_condition(QueryBuilder $queryBuilder, SpecificationInterface $specification): void { - $type = DoctrineJoin::ON; + $type = DoctrineJoin::ON; $condition = 'condition'; $this->beConstructedWith('user', 'authUser'); $specification->modify($queryBuilder, 'a')->willReturn($condition); - $queryBuilder->join('a.user', 'authUser', $type, $condition, null)->shouldBeCalled(); + $queryBuilder->join('a.user', 'authUser', $type, $condition, null)->shouldBeCalled()->willReturn($queryBuilder); - $this->setConditionType($type)->shouldReturn($this); - $this->setCondition($specification)->shouldReturn($this); - $this->modify($queryBuilder, 'a'); + $join = $this->setConditionType($type)->setCondition($specification); + $join->modify($queryBuilder, 'a'); } - public function it_throws_an_exception_when_setting_illegal_type() + public function it_throws_an_exception_when_setting_illegal_type(): void { $this->setType(Join::LEFT_JOIN); diff --git a/spec/Query/LeftJoinSpec.php b/spec/Query/LeftJoinSpec.php index 1f56ab7..b8efaf0 100644 --- a/spec/Query/LeftJoinSpec.php +++ b/spec/Query/LeftJoinSpec.php @@ -1,33 +1,33 @@ beConstructedWith('user', 'authUser', 'a'); } - public function it_is_a_specification() + public function it_is_a_specification(): void { $this->shouldHaveType(SpecificationInterface::class); } - public function it_joins_with_default_dql_alias(QueryBuilder $queryBuilder) + public function it_joins_with_default_dql_alias(QueryBuilder $queryBuilder): void { - $queryBuilder->leftJoin('a.user', 'authUser', null, null, null)->shouldBeCalled(); + $queryBuilder->leftJoin('a.user', 'authUser', null, null, null)->shouldBeCalled()->willReturn($queryBuilder); $this->modify($queryBuilder, 'a'); } - public function it_uses_local_alias_if_global_was_not_set(QueryBuilder $queryBuilder) + public function it_uses_local_alias_if_global_was_not_set(QueryBuilder $queryBuilder): void { $this->beConstructedWith('user', 'authUser'); - $queryBuilder->leftJoin('b.user', 'authUser', null, null, null)->shouldBeCalled(); + $queryBuilder->leftJoin('b.user', 'authUser', null, null, null)->shouldBeCalled()->willReturn($queryBuilder); $this->modify($queryBuilder, 'b'); } } diff --git a/spec/Query/OrderBySpec.php b/spec/Query/OrderBySpec.php index 95afadc..3f6b59b 100644 --- a/spec/Query/OrderBySpec.php +++ b/spec/Query/OrderBySpec.php @@ -1,34 +1,36 @@ beConstructedWith($this->field, $this->order, $this->alias); } - public function it_should_throw_exception_when_given_invalid_order() + public function it_should_throw_exception_when_given_invalid_order(): void { $this->shouldThrow(InvalidArgumentException::class) ->during('__construct', ['foo', 'bar']); } - public function it_should_modify_query_builder(QueryBuilder $queryBuilder) + public function it_should_modify_query_builder(QueryBuilder $queryBuilder): void { $sort = sprintf('%s.%s', $this->alias, $this->field); - $queryBuilder->addOrderBy($sort, $this->order)->shouldBeCalled(); + $queryBuilder->addOrderBy($sort, $this->order)->shouldBeCalled()->willReturn($queryBuilder); $this->isSatisfiedBy('foo')->shouldReturn(true); $this->modify($queryBuilder, $this->alias); diff --git a/spec/Query/SelectSpec.php b/spec/Query/SelectSpec.php index a52cb94..90d8f31 100644 --- a/spec/Query/SelectSpec.php +++ b/spec/Query/SelectSpec.php @@ -1,41 +1,41 @@ beConstructedWith($entity, $type); - $queryBuilder->addSelect($entity)->shouldBeCalled(); + $queryBuilder->addSelect($entity)->shouldBeCalled()->willReturn($queryBuilder); $this->isSatisfiedBy('foo')->shouldReturn(true); $this->modify($queryBuilder, $alias); } - public function it_should_replace_selects_in_query_builder(QueryBuilder $queryBuilder) + public function it_should_replace_selects_in_query_builder(QueryBuilder $queryBuilder): void { - $alias = 'a'; + $alias = 'a'; $entity = 'foo'; - $type = Select::SELECT; + $type = Select::SELECT; $this->beConstructedWith($entity, $type); - $queryBuilder->select($entity)->shouldBeCalled(); + $queryBuilder->select($entity)->shouldBeCalled()->willReturn($queryBuilder); $this->isSatisfiedBy('foo')->shouldReturn(true); $this->modify($queryBuilder, $alias); } - public function it_throws_an_exception_when_setting_illegal_type() + public function it_throws_an_exception_when_setting_illegal_type(): void { $entity = 'foo'; $this->beConstructedWith($entity); diff --git a/spec/Result/AsArraySpec.php b/spec/Result/AsArraySpec.php index 54a8bbc..e26df51 100644 --- a/spec/Result/AsArraySpec.php +++ b/spec/Result/AsArraySpec.php @@ -1,22 +1,22 @@ shouldHaveType(ModifierInterface::class); } - public function it_sets_hydration_mode_to_array(AbstractQuery $query) + public function it_sets_hydration_mode_to_array(AbstractQuery $query): void { - $query->setHydrationMode(Query::HYDRATE_ARRAY)->shouldBeCalled(); + $query->setHydrationMode(Query::HYDRATE_ARRAY)->shouldBeCalled()->willReturn($query); $this->modify($query); } diff --git a/spec/Result/ModifierCollectionSpec.php b/spec/Result/ModifierCollectionSpec.php index 54117f2..b70db11 100644 --- a/spec/Result/ModifierCollectionSpec.php +++ b/spec/Result/ModifierCollectionSpec.php @@ -1,19 +1,19 @@ beConstructedWith($modifierA, $modifierB); $modifierA->modify($query)->shouldBeCalled(); @@ -22,7 +22,7 @@ public function it_should_call_modify_on_child_modifiers( $this->modify($query); } - public function it_should_throw_exception_when_adding_incorrect_children() + public function it_should_throw_exception_when_adding_incorrect_children(): void { $this->shouldThrow(InvalidArgumentException::class) ->during('add', ['test']); diff --git a/spec/SpecificationRepositoryDouble.php b/spec/SpecificationRepositoryDouble.php new file mode 100644 index 0000000..9bbbd65 --- /dev/null +++ b/spec/SpecificationRepositoryDouble.php @@ -0,0 +1,20 @@ +beAnInstanceOf(SpecificationRepository::class); + $classMetadata->name = 'foo'; + $this->beAnInstanceOf(SpecificationRepositoryDouble::class); $this->beConstructedWith($entityManager, $classMetadata); } @@ -31,17 +29,14 @@ public function it_matches_a_specification_without_result_modifier( SpecificationInterface $specification, EntityManager $entityManager, QueryBuilder $queryBuilder, - AbstractQuery $query - ) { + Query $query, + ): void { $this->prepare($specification, $entityManager, $queryBuilder, $query); - $specification->isSatisfiedBy(Argument::any()) - ->willReturn(true); - $specification->modify($queryBuilder, $this->dqlAlias) - ->shouldBeCalled(); + $specification->isSatisfiedBy(Argument::any())->willReturn(true); + $specification->modify($queryBuilder, $this->dqlAlias)->shouldBeCalled(); - $this->match($specification) - ->shouldReturn($query); + $this->match($specification)->shouldReturn($query); $query->execute(); } @@ -50,9 +45,9 @@ public function it_matches_a_specification_with_result_modifier( SpecificationInterface $specification, EntityManager $entityManager, QueryBuilder $queryBuilder, - AbstractQuery $query, - ModifierInterface $modifier - ) { + Query $query, + ModifierInterface $modifier, + ): void { $this->prepare($specification, $entityManager, $queryBuilder, $query); $specification->isSatisfiedBy(Argument::any()) ->willReturn(true); @@ -72,8 +67,8 @@ public function it_should_throw_logic_exception_if_spec_not_supported( SpecificationInterface $specification, EntityManager $entityManager, QueryBuilder $queryBuilder, - AbstractQuery $query - ) { + Query $query, + ): void { $this->prepare($specification, $entityManager, $queryBuilder, $query); $specification->isSatisfiedBy(Argument::any()) ->willReturn(false); @@ -86,8 +81,8 @@ public function it_should_accept_specification_with_only_query_modifiers( SpecificationInterface $specification, EntityManager $entityManager, QueryBuilder $queryBuilder, - AbstractQuery $query - ) { + Query $query, + ): void { $entityManager->createQueryBuilder()->willReturn($queryBuilder); $queryBuilder->select($this->dqlAlias)->willReturn($queryBuilder); @@ -97,25 +92,20 @@ public function it_should_accept_specification_with_only_query_modifiers( $specification->modify($queryBuilder, $this->dqlAlias, Argument::any())->shouldBeCalled(); $specification->isSatisfiedBy(Argument::any())->willReturn(true); - $specification->modify($queryBuilder, $this->dqlAlias, Argument::any())->willReturn(''); + $specification->modify($queryBuilder, $this->dqlAlias, Argument::any())->willReturn(null); $this->match($specification); } /** * Prepare mocks. - * - * @param SpecificationInterface $specification - * @param EntityManager $entityManager - * @param QueryBuilder $queryBuilder - * @param AbstractQuery $query */ private function prepare( SpecificationInterface $specification, EntityManager $entityManager, QueryBuilder $queryBuilder, - AbstractQuery $query - ) { + Query $query, + ): void { $entityManager->createQueryBuilder()->willReturn($queryBuilder); $specification->modify($queryBuilder, $this->dqlAlias)->willReturn($this->expression); diff --git a/spec/SpecificationSpec.php b/spec/SpecificationSpec.php index ed63f64..4ad09c1 100644 --- a/spec/SpecificationSpec.php +++ b/spec/SpecificationSpec.php @@ -1,16 +1,16 @@ shouldHaveType(SpecificationInterface::class); } @@ -18,8 +18,8 @@ public function it_is_a_specification() public function it_modifies_all_child_queries( QueryBuilder $queryBuilder, SpecificationInterface $specificationA, - SpecificationInterface $specificationB - ) { + SpecificationInterface $specificationB, + ): void { $this->beConstructedWith([$specificationA, $specificationB]); $dqlAlias = 'a'; @@ -31,13 +31,14 @@ public function it_modifies_all_child_queries( public function it_supports_conditions( QueryBuilder $queryBuilder, - Expr $expression, + Expr $expr, SpecificationInterface $conditionA, SpecificationInterface $conditionB, - $x, - $y - ) { + ): void { $dqlAlias = 'a'; + $x = 'x'; + $y = 'y'; + $expression = new Expr\Andx([$x, $y]); $this[] = $conditionA; $this[] = $conditionB; @@ -47,9 +48,9 @@ public function it_supports_conditions( $conditionA->modify($queryBuilder, $dqlAlias)->willReturn($x); $conditionB->modify($queryBuilder, $dqlAlias)->willReturn($y); - $queryBuilder->expr()->willReturn($expression); + $queryBuilder->expr()->willReturn($expr); - $expression->andX($x, $y)->shouldBeCalled(); + $expr->andX($x, $y)->shouldBeCalled()->willReturn($expression); $this->isSatisfiedBy('foo')->shouldReturn(true); $this->modify($queryBuilder, $dqlAlias); @@ -58,8 +59,8 @@ public function it_supports_conditions( public function it_supports_query_modifiers( QueryBuilder $queryBuilder, SpecificationInterface $modifierA, - SpecificationInterface $modifierB - ) { + SpecificationInterface $modifierB, + ): void { $this->beConstructedWith([$modifierA, $modifierB]); $dqlAlias = 'a'; @@ -76,8 +77,8 @@ public function it_supports_query_modifiers( public function it_should_throw_exception_when_child_does_not_support_class( SpecificationInterface $specificationA, - SpecificationInterface $specificationB - ) { + SpecificationInterface $specificationB, + ): void { $className = 'foo'; $this->beConstructedWith([$specificationA, $specificationB]); @@ -87,7 +88,7 @@ public function it_should_throw_exception_when_child_does_not_support_class( $this->isSatisfiedBy($className)->shouldReturn(false); } - public function it_should_throw_exception_on_invalid_child() + public function it_should_throw_exception_on_invalid_child(): void { $this->shouldThrow(InvalidArgumentException::class) ->during('add', ['bar']); diff --git a/src/AbstractSpecification.php b/src/AbstractSpecification.php index 59c04cb..c68db28 100644 --- a/src/AbstractSpecification.php +++ b/src/AbstractSpecification.php @@ -1,64 +1,39 @@ field = $field; - $this->dqlAlias = $dqlAlias; } - /** - * {@inheritdoc} - */ - public function isSatisfiedBy($value) + #[\Override] + public function isSatisfiedBy(mixed $value): bool { return true; } /** * Create a formatted string for the given property prefixed with the DQL alias. - * - * @param string $dqlAlias - * - * @return string */ - protected function createPropertyWithAlias($dqlAlias) + protected function createPropertyWithAlias(?string $dqlAlias): string { return $this->createAliasedName($this->field, $dqlAlias); } /** * Create a formatted string where the value will be prefixed with DQL alias (if not already present). - * - * @param string $value - * @param string $dqlAlias - * - * @return string */ - protected function createAliasedName($value, $dqlAlias) + protected function createAliasedName(string $value, ?string $dqlAlias): string { - if (strpos($value, '.') !== false) { + if (str_contains($value, '.')) { return $value; } - if (! empty($this->dqlAlias)) { + if (null !== $this->dqlAlias) { $dqlAlias = $this->dqlAlias; } diff --git a/src/Condition/Between.php b/src/Condition/Between.php index eca0c45..b6b7ea6 100644 --- a/src/Condition/Between.php +++ b/src/Condition/Between.php @@ -1,61 +1,36 @@ from = $from; - $this->to = $to; - parent::__construct($field, $dqlAlias); } - /** - * {@inheritdoc} - */ - public function modify(QueryBuilder $queryBuilder, $dqlAlias) + #[\Override] + public function modify(QueryBuilder $queryBuilder, ?string $dqlAlias = null): string { $fromParam = $this->generateParameterName('from', $queryBuilder); - $toParam = $this->generateParameterName('to', $queryBuilder); + $toParam = $this->generateParameterName('to', $queryBuilder); $queryBuilder->setParameter($fromParam, $this->from); $queryBuilder->setParameter($toParam, $this->to); - return (string) $queryBuilder->expr()->between( + return $queryBuilder->expr()->between( $this->createPropertyWithAlias($dqlAlias), sprintf(':%s', $fromParam), - sprintf(':%s', $toParam) + sprintf(':%s', $toParam), ); } - /** - * @param string $type - * @param QueryBuilder $queryBuilder - * - * @return string - */ - private function generateParameterName($type, QueryBuilder $queryBuilder) + private function generateParameterName(string $type, QueryBuilder $queryBuilder): string { return sprintf('%s_%d', $type, count($queryBuilder->getParameters())); } diff --git a/src/Condition/Comparison.php b/src/Condition/Comparison.php index 4b0b525..4dfccf1 100644 --- a/src/Condition/Comparison.php +++ b/src/Condition/Comparison.php @@ -1,72 +1,45 @@ '; - const LT = '<'; - const LTE = '<='; - const GT = '>'; - const GTE = '>='; - const LIKE = 'LIKE'; - + public const string EQ = '='; + public const string NEQ = '<>'; + public const string LT = '<'; + public const string LTE = '<='; + public const string GT = '>'; + public const string GTE = '>='; + public const string LIKE = 'LIKE'; /** * @var string[] */ - protected static $operators = [self::EQ, self::NEQ, self::LT, self::LTE, self::GT, self::GTE, self::LIKE]; - - /** - * @var string - */ - protected $value; + protected const array OPERATORS = [self::EQ, self::NEQ, self::LT, self::LTE, self::GT, self::GTE, self::LIKE]; /** - * @var string - */ - protected $operator; - - /** - * @param string $operator - * @param string $field - * @param string $value - * @param string|null $dqlAlias - * * @throws InvalidArgumentException */ - public function __construct($operator, $field, $value, $dqlAlias = null) + public function __construct(protected string $operator, string $field, protected string $value, ?string $dqlAlias = null) { - if (! in_array($operator, self::$operators, true)) { - throw new InvalidArgumentException( - sprintf( - '"%s" is not a valid operator. Valid operators: %s', - $operator, - implode(', ', self::$operators) - ) - ); + if (!in_array($operator, self::OPERATORS, true)) { + throw new InvalidArgumentException(sprintf('"%s" is not a valid operator. Valid operators: %s', $operator, implode(', ', self::OPERATORS))); } - $this->operator = $operator; - $this->value = $value; - parent::__construct($field, $dqlAlias); } /** * Return a string expression which can be used as condition (in WHERE-clause). - * - * @param QueryBuilder $queryBuilder - * @param string $dqlAlias - * - * @return string */ - public function modify(QueryBuilder $queryBuilder, $dqlAlias) + #[\Override] + public function modify(QueryBuilder $queryBuilder, ?string $dqlAlias = null): string { $paramName = $this->generateParameterName($queryBuilder); $queryBuilder->setParameter($paramName, $this->value); @@ -74,18 +47,14 @@ public function modify(QueryBuilder $queryBuilder, $dqlAlias) return (string) new DoctrineComparison( $this->createPropertyWithAlias($dqlAlias), $this->operator, - sprintf(':%s', $paramName) + sprintf(':%s', $paramName), ); } /** * Return automatically generated parameter name. - * - * @param QueryBuilder $queryBuilder - * - * @return string */ - protected function generateParameterName(QueryBuilder $queryBuilder) + protected function generateParameterName(QueryBuilder $queryBuilder): string { return sprintf('comparison_%d', count($queryBuilder->getParameters())); } diff --git a/src/Condition/Equals.php b/src/Condition/Equals.php index 0e5b212..1e6fb2e 100644 --- a/src/Condition/Equals.php +++ b/src/Condition/Equals.php @@ -1,19 +1,17 @@ createPropertyWithAlias($dqlAlias), $this->operator, - $this->createAliasedName($this->value, $dqlAlias) + $this->createAliasedName($this->value, $dqlAlias), ); } } diff --git a/src/Condition/GreaterThan.php b/src/Condition/GreaterThan.php index 7989bad..4c26c4b 100644 --- a/src/Condition/GreaterThan.php +++ b/src/Condition/GreaterThan.php @@ -1,19 +1,17 @@ value = $value; - parent::__construct($field, $dqlAlias); } - /** - * {@inheritdoc} - */ - public function modify(QueryBuilder $queryBuilder, $dqlAlias) + #[\Override] + public function modify(QueryBuilder $queryBuilder, ?string $dqlAlias = null): string { $paramName = $this->generateParameterName($queryBuilder); $queryBuilder->setParameter($paramName, $this->value); return (string) $queryBuilder->expr()->in( $this->createPropertyWithAlias($dqlAlias), - sprintf(':%s', $paramName) + sprintf(':%s', $paramName), ); } - /** - * @param QueryBuilder $queryBuilder - * - * @return string - */ - protected function generateParameterName(QueryBuilder $queryBuilder) + protected function generateParameterName(QueryBuilder $queryBuilder): string { return sprintf('in_%d', count($queryBuilder->getParameters())); } diff --git a/src/Condition/IsInstanceOf.php b/src/Condition/IsInstanceOf.php index 04f377d..f6ccd7d 100644 --- a/src/Condition/IsInstanceOf.php +++ b/src/Condition/IsInstanceOf.php @@ -1,37 +1,25 @@ className = $className; - parent::__construct($field, $dqlAlias); } - /** - * {@inheritdoc} - */ - public function modify(QueryBuilder $queryBuilder, $dqlAlias) + #[\Override] + public function modify(QueryBuilder $queryBuilder, ?string $dqlAlias = null): string { return (string) $queryBuilder->expr()->isInstanceOf( $this->createPropertyWithAlias($dqlAlias), - $this->className + $this->className, ); } } diff --git a/src/Condition/IsMemberOf.php b/src/Condition/IsMemberOf.php index de7b684..e1150e2 100644 --- a/src/Condition/IsMemberOf.php +++ b/src/Condition/IsMemberOf.php @@ -1,37 +1,25 @@ className = $className; - parent::__construct($field, $dqlAlias); } - /** - * {@inheritdoc} - */ - public function modify(QueryBuilder $queryBuilder, $dqlAlias) + #[\Override] + public function modify(QueryBuilder $queryBuilder, ?string $dqlAlias = null): string { return (string) $queryBuilder->expr()->isMemberOf( $this->createPropertyWithAlias($dqlAlias), - $this->className + $this->className, ); } } diff --git a/src/Condition/IsNotNull.php b/src/Condition/IsNotNull.php index 0d446d8..8dd720e 100644 --- a/src/Condition/IsNotNull.php +++ b/src/Condition/IsNotNull.php @@ -1,18 +1,19 @@ expr()->isNotNull( - $this->createPropertyWithAlias($dqlAlias) + return $queryBuilder->expr()->isNotNull( + $this->createPropertyWithAlias($dqlAlias), ); } } diff --git a/src/Condition/IsNull.php b/src/Condition/IsNull.php index 84e106d..88c9a82 100644 --- a/src/Condition/IsNull.php +++ b/src/Condition/IsNull.php @@ -1,19 +1,19 @@ expr()->isNull( - $this->createPropertyWithAlias($dqlAlias) + return $queryBuilder->expr()->isNull( + $this->createPropertyWithAlias($dqlAlias), ); } } diff --git a/src/Condition/LessThan.php b/src/Condition/LessThan.php index 04ad304..4856e13 100644 --- a/src/Condition/LessThan.php +++ b/src/Condition/LessThan.php @@ -1,19 +1,17 @@ formatValue($format, $value); parent::__construct(self::LIKE, $field, $formattedValue, $dqlAlias); } - /** - * @param string $format - * @param string $value - * - * @return string - */ - private function formatValue($format, $value) + private function formatValue(string $format, string $value): string { return sprintf($format, $value); } diff --git a/src/Condition/NotEquals.php b/src/Condition/NotEquals.php index 024979d..e5ad9e8 100644 --- a/src/Condition/NotEquals.php +++ b/src/Condition/NotEquals.php @@ -1,19 +1,17 @@ generateParameterName($queryBuilder); $queryBuilder->setParameter($paramName, $this->value); return (string) $queryBuilder->expr()->notIn( $this->createPropertyWithAlias($dqlAlias), - sprintf(':%s', $paramName) + sprintf(':%s', $paramName), ); } - /** - * @param QueryBuilder $queryBuilder - * - * @return string - */ - protected function generateParameterName(QueryBuilder $queryBuilder) + protected function generateParameterName(QueryBuilder $queryBuilder): string { return sprintf('not_in_%d', count($queryBuilder->getParameters())); } diff --git a/src/Exception/InvalidArgumentException.php b/src/Exception/InvalidArgumentException.php index fc4b461..0ef45ac 100644 --- a/src/Exception/InvalidArgumentException.php +++ b/src/Exception/InvalidArgumentException.php @@ -1,6 +1,8 @@ setType($type) - ->setChildren($children); + parent::__construct($children); + + $this->setType($type); } } diff --git a/src/Logic/Not.php b/src/Logic/Not.php index f3f38ab..1c467c7 100644 --- a/src/Logic/Not.php +++ b/src/Logic/Not.php @@ -1,48 +1,35 @@ parent = $expr; } - /** - * @param QueryBuilder $queryBuilder - * @param string $dqlAlias - * - * @return string - */ - public function modify(QueryBuilder $queryBuilder, $dqlAlias) + #[\Override] + public function modify(QueryBuilder $queryBuilder, ?string $dqlAlias = null): ?string { $filter = $this->parent->modify($queryBuilder, $dqlAlias); - if (empty($filter)) { + + if (null === $filter) { return ''; } return (string) $queryBuilder->expr()->not($filter); } - /** - * {@inheritdoc} - */ - public function isSatisfiedBy($value) + #[\Override] + public function isSatisfiedBy(mixed $value): bool { return $this->parent->isSatisfiedBy($value); } diff --git a/src/Logic/OrX.php b/src/Logic/OrX.php index aef60c0..62fb1a4 100644 --- a/src/Logic/OrX.php +++ b/src/Logic/OrX.php @@ -1,19 +1,15 @@ */ -class GroupBy extends AbstractSpecification +readonly class GroupBy extends AbstractSpecification { - const GROUP_BY = 'groupBy'; - const ADD_GROUP_BY = 'addGroupBy'; - - /** @var string[] */ - protected static $types = [self::GROUP_BY, self::ADD_GROUP_BY]; - - /** @var string */ - protected $type; - + public const string GROUP_BY = 'groupBy'; + public const string ADD_GROUP_BY = 'addGroupBy'; /** - * Constructor. - * - * @param string $field - * @param string $type - * @param string|null $dqlAlias + * @var array */ - public function __construct($field, $type = self::ADD_GROUP_BY, $dqlAlias = null) - { - $this->setType($type); - parent::__construct($field, $dqlAlias); - } + protected const array TYPES = [self::GROUP_BY, self::ADD_GROUP_BY]; /** - * {@inheritdoc} + * @throws InvalidArgumentException */ - public function modify(QueryBuilder $queryBuilder, $dqlAlias) + public function __construct(string $field, protected string $type = self::ADD_GROUP_BY, ?string $dqlAlias = null) { - call_user_func_array( - [$queryBuilder, $this->type], - [ - $this->createPropertyWithAlias($dqlAlias), - ] - ); + parent::__construct($field, $dqlAlias); + + if (!in_array($type, self::TYPES, true)) { + throw new InvalidArgumentException(sprintf('"%s" is not a valid type! Valid types: %s', $type, implode(', ', self::TYPES))); + } } - /** - * @param string $type - * - * @throws InvalidArgumentException - */ - public function setType($type) + #[\Override] + public function modify(QueryBuilder $queryBuilder, ?string $dqlAlias = null): ?string { - if (! in_array($type, self::$types, true)) { - throw new InvalidArgumentException(sprintf( - '"%s" is not a valid type! Valid types: %s', - $type, - implode(', ', self::$types) - )); - } + $queryBuilder->{$this->type}($this->createPropertyWithAlias($dqlAlias)); - $this->type = $type; + return null; } } diff --git a/src/Query/Having.php b/src/Query/Having.php index a4ef385..b90abfa 100644 --- a/src/Query/Having.php +++ b/src/Query/Having.php @@ -1,59 +1,55 @@ + * @author Kyle Tucker */ -class Having implements SpecificationInterface +readonly class Having implements SpecificationInterface { - const HAVING = 'having'; - const AND_HAVING = 'andHaving'; - const OR_HAVING = 'orHaving'; - - protected static $types = [self::HAVING, self::AND_HAVING, self::OR_HAVING]; - - /** @var string */ - protected $type; - - /** @var SpecificationInterface */ - protected $specification; - - public function __construct(SpecificationInterface $specification, $type = self::AND_HAVING) + public const string HAVING = 'having'; + public const string AND_HAVING = 'andHaving'; + public const string OR_HAVING = 'orHaving'; + /** + * @var array + */ + protected const array TYPES = [self::HAVING, self::AND_HAVING, self::OR_HAVING]; + + /** + * @throws InvalidArgumentException + */ + public function __construct(protected SpecificationInterface $specification, protected string $type = self::AND_HAVING) { - $this->specification = $specification; - $this->setType($type); + if (!in_array($type, self::TYPES, true)) { + throw new InvalidArgumentException(sprintf('"%s" is not a valid type! Valid types: %s', $type, implode(', ', self::TYPES))); + } } - public function modify(QueryBuilder $queryBuilder, $dqlAlias) + #[\Override] + public function modify(QueryBuilder $queryBuilder, ?string $dqlAlias = null): ?string { - call_user_func_array( - [$queryBuilder, $this->type], - [ - $this->specification->modify($queryBuilder, $dqlAlias), - ] - ); + $queryBuilder->{$this->type}($this->specification->modify($queryBuilder, $dqlAlias)); + + return null; } - public function isSatisfiedBy($value) + #[\Override] + public function isSatisfiedBy(mixed $value): bool { return true; } - public function setType($type) + /** + * @throws InvalidArgumentException + */ + public function setType(string $type): self { - if (! in_array($type, self::$types, true)) { - throw new InvalidArgumentException(sprintf( - '"%s" is not a valid type! Valid types: %s', - $type, - implode(', ', self::$types) - )); - } - - $this->type = $type; + return new self($this->specification, $type); } } diff --git a/src/Query/IndexBy.php b/src/Query/IndexBy.php index 367a2da..b39aa33 100644 --- a/src/Query/IndexBy.php +++ b/src/Query/IndexBy.php @@ -1,23 +1,29 @@ indexBy( - $this->dqlAlias ?: $dqlAlias, - $this->createPropertyWithAlias($dqlAlias) + $this->dqlAlias ?? $dqlAlias ?? '', + $this->createPropertyWithAlias($dqlAlias), ); + + return null; } } diff --git a/src/Query/InnerJoin.php b/src/Query/InnerJoin.php index 03c56eb..bf4e2a9 100644 --- a/src/Query/InnerJoin.php +++ b/src/Query/InnerJoin.php @@ -1,21 +1,18 @@ setType(self::INNER_JOIN); - parent::__construct($field, $newAlias, $dqlAlias); + parent::__construct($field, $newAlias, $dqlAlias, self::INNER_JOIN); } } diff --git a/src/Query/Join.php b/src/Query/Join.php index a0c2ed7..f4775c3 100644 --- a/src/Query/Join.php +++ b/src/Query/Join.php @@ -1,90 +1,47 @@ */ - private $conditionType = null; + protected const array TYPES = [self::JOIN, self::LEFT_JOIN, self::INNER_JOIN]; /** - * @var string|SpecificationInterface|null - */ - private $condition = null; - - /** - * @var string|null - */ - private $indexedBy = null; - - /** - * @var string - */ - private $type = self::JOIN; - - /** - * @param string $field - * @param string $newAlias - * @param string|null $dqlAlias - * * @throws InvalidArgumentException */ - public function __construct($field, $newAlias, $dqlAlias = null) - { - $this->newAlias = $newAlias; - - parent::__construct($field, $dqlAlias); - } - - /** - * @param string $type - * - * @throws InvalidArgumentException - * - * @return $this - */ - public function setType($type) - { - if (! in_array($type, self::$types, true)) { - throw new InvalidArgumentException(sprintf( - '"%s" is not a valid type! Valid types: %s', - $type, - implode(', ', self::$types) - )); + public function __construct( + string $field, + private string $newAlias, + ?string $dqlAlias = null, + private string $type = self::JOIN, + private string|SpecificationInterface|null $condition = null, + private ?string $conditionType = null, + private ?string $indexedBy = null, + ) { + if (!in_array($type, self::TYPES, true)) { + throw new InvalidArgumentException(sprintf('"%s" is not a valid type! Valid types: %s', $type, implode(', ', self::TYPES))); } - $this->type = $type; - - return $this; + parent::__construct($field, $dqlAlias); } - /** - * {@inheritdoc} - */ - public function modify(QueryBuilder $queryBuilder, $dqlAlias) + #[\Override] + public function modify(QueryBuilder $queryBuilder, ?string $dqlAlias = null): ?string { - if (! is_null($this->dqlAlias)) { + if (!is_null($this->dqlAlias)) { $dqlAlias = $this->dqlAlias; } @@ -95,51 +52,72 @@ public function modify(QueryBuilder $queryBuilder, $dqlAlias) $condition = $condition->modify($queryBuilder, $dqlAlias); } - call_user_func_array( - [$queryBuilder, $this->type], - [$property, $this->newAlias, $this->conditionType, $condition, $this->indexedBy] + $queryBuilder->{$this->type}($property, $this->newAlias, $this->conditionType, $condition, $this->indexedBy); + + return null; + } + + /** + * @throws InvalidArgumentException + */ + public function setType(string $type): self + { + return new self( + $this->field, + $this->newAlias, + $this->dqlAlias, + $type, + $this->condition, + $this->conditionType, + $this->indexedBy, ); } /** * Set the condition type to be used on the join (WITH/ON). - * - * @param string $conditionType - * - * @return $this */ - public function setConditionType($conditionType) + public function setConditionType(?string $conditionType): self { - $this->conditionType = $conditionType; - - return $this; + return new self( + $this->field, + $this->newAlias, + $this->dqlAlias, + $this->type, + $this->condition, + $conditionType, + $this->indexedBy, + ); } /** * Set the condition to be used for the join statement. - * - * @param string|SpecificationInterface $condition - * - * @return $this */ - public function setCondition($condition) + public function setCondition(SpecificationInterface|string|null $condition): self { - $this->condition = $condition; - - return $this; + return new self( + $this->field, + $this->newAlias, + $this->dqlAlias, + $this->type, + $condition, + $this->conditionType, + $this->indexedBy, + ); } /** * Set the property which will be used as index for the returned collection. - * - * @param mixed $indexedBy - * - * @return $this */ - public function setIndexedBy($indexedBy) + public function setIndexedBy(?string $indexedBy): self { - $this->indexedBy = $indexedBy; - - return $this; + return new self( + $this->field, + $this->newAlias, + $this->dqlAlias, + $this->type, + $this->condition, + $this->conditionType, + $indexedBy, + ); } } diff --git a/src/Query/LeftJoin.php b/src/Query/LeftJoin.php index 6380363..0c3ff69 100644 --- a/src/Query/LeftJoin.php +++ b/src/Query/LeftJoin.php @@ -1,21 +1,18 @@ setType(self::LEFT_JOIN); - parent::__construct($field, $newAlias, $dqlAlias); + parent::__construct($field, $newAlias, $dqlAlias, self::LEFT_JOIN); } } diff --git a/src/Query/OrderBy.php b/src/Query/OrderBy.php index eaf488e..57f889d 100644 --- a/src/Query/OrderBy.php +++ b/src/Query/OrderBy.php @@ -1,54 +1,44 @@ */ - protected $order; - - private static $validOrder = [self::ASC, self::DESC]; + private const array VALID_ORDERS = [self::ASC, self::DESC]; /** - * @param string $field - * @param string $order - * @param string|null $dqlAlias - * * @throws InvalidArgumentException */ - public function __construct($field, $order = null, $dqlAlias = null) + public function __construct(string $field, private ?string $order = null, ?string $dqlAlias = null) { - $order = ! $order ? self::ASC : strtoupper($order); + $order = null !== $order && '' !== $order && '0' !== $order ? strtoupper($order) : self::ASC; - if (! in_array($order, self::$validOrder, true)) { + if (!in_array($order, self::VALID_ORDERS, true)) { throw new InvalidArgumentException(); } - $this->order = $order; - parent::__construct($field, $dqlAlias); } - /** - * {@inheritdoc} - */ - public function modify(QueryBuilder $queryBuilder, $dqlAlias) + #[\Override] + public function modify(QueryBuilder $queryBuilder, ?string $dqlAlias = null): ?string { $queryBuilder->addOrderBy( $this->createPropertyWithAlias($dqlAlias), - $this->order + $this->order, ); + + return null; } } diff --git a/src/Query/Select.php b/src/Query/Select.php index 449c74c..7f15f84 100644 --- a/src/Query/Select.php +++ b/src/Query/Select.php @@ -1,71 +1,55 @@ */ - protected $select; + protected const array TYPES = [self::SELECT, self::ADD_SELECT]; /** - * @var string - */ - protected $type; - - /** - * @param string|array $select - * @param string $type + * @param string|array $select + * + * @throws InvalidArgumentException */ - public function __construct($select, $type = self::ADD_SELECT) + public function __construct(protected string|array $select, private string $type = self::ADD_SELECT) { - $this->setType($type); - $this->select = $select; + if (!in_array($type, self::TYPES, true)) { + throw new InvalidArgumentException(sprintf('"%s" is not a valid type! Valid types: %s', $type, implode(', ', self::TYPES))); + } } - /** - * {@inheritdoc} - */ - public function modify(QueryBuilder $queryBuilder, $dqlAlias) + #[\Override] + public function modify(QueryBuilder $queryBuilder, ?string $dqlAlias = null): ?string { - call_user_func_array([$queryBuilder, $this->type], [$this->select]); + $queryBuilder->{$this->type}($this->select); + + return null; } /** - * @param string $type - * * @throws InvalidArgumentException */ - public function setType($type) + public function setType(string $type): self { - if (! in_array($type, self::$types, true)) { - throw new InvalidArgumentException(sprintf( - '"%s" is not a valid type! Valid types: %s', - $type, - implode(', ', self::$types) - )); - } - - $this->type = $type; + return new self($this->select, $type); } - /** - * {@inheritdoc} - */ - public function isSatisfiedBy($value) + #[\Override] + public function isSatisfiedBy(mixed $value): bool { return true; } diff --git a/src/Result/AsArray.php b/src/Result/AsArray.php index 849e53a..61e28f9 100644 --- a/src/Result/AsArray.php +++ b/src/Result/AsArray.php @@ -1,22 +1,22 @@ setHydrationMode(Query::HYDRATE_ARRAY); + $query->setHydrationMode(AbstractQuery::HYDRATE_ARRAY); } } diff --git a/src/Result/ModifierCollection.php b/src/Result/ModifierCollection.php index c442d67..82ba66c 100644 --- a/src/Result/ModifierCollection.php +++ b/src/Result/ModifierCollection.php @@ -1,13 +1,17 @@ */ class ModifierCollection extends ArrayCollection implements ModifierInterface { @@ -15,40 +19,38 @@ class ModifierCollection extends ArrayCollection implements ModifierInterface * Compose one or more ResultModifier and evaluate as a single modifier. * * @param ModifierInterface ...$modifiers + * + * @throws InvalidArgumentException */ - public function __construct() + public function __construct(mixed ...$modifiers) { - array_map([$this, 'add'], func_get_args()); + parent::__construct(); + + foreach ($modifiers as $modifier) { + $this->add($modifier); + } } /** - * @param ModifierInterface $value + * @param ModifierInterface $element * * @throws InvalidArgumentException - * - * @return bool */ - public function add($value) + #[\Override] + public function add(mixed $element): void { - if (! $value instanceof ModifierInterface) { - throw new InvalidArgumentException(sprintf( - '"%s" does not implement "%s"!', - (is_object($value)) ? get_class($value) : $value, - ModifierInterface::class - )); + if (!$element instanceof ModifierInterface) { + throw new InvalidArgumentException(sprintf('"%s" does not implement "%s"!', (is_object($element)) ? $element::class : $element, ModifierInterface::class)); } - return parent::add($value); + parent::add($element); } /** * Modify the query (e.g. select more fields/relations). - * - * @param AbstractQuery $query - * - * @throws InvalidArgumentException */ - public function modify(AbstractQuery $query) + #[\Override] + public function modify(AbstractQuery $query): void { foreach ($this as $child) { $child->modify($query); diff --git a/src/Result/ModifierInterface.php b/src/Result/ModifierInterface.php index dbcba07..9f69182 100644 --- a/src/Result/ModifierInterface.php +++ b/src/Result/ModifierInterface.php @@ -1,6 +1,8 @@ */ class Specification extends ArrayCollection implements SpecificationInterface { - const AND_X = 'andX'; - const OR_X = 'orX'; - - protected static $types = [self::OR_X, self::AND_X]; - + public const string AND_X = 'andX'; + public const string OR_X = 'orX'; /** - * @var string + * @var array */ - private $type = self::AND_X; + protected const array TYPES = [self::OR_X, self::AND_X]; + private string $type = self::AND_X; /** * @param SpecificationInterface[] $elements + * + * @throws InvalidArgumentException */ public function __construct(array $elements = []) { @@ -33,52 +37,41 @@ public function __construct(array $elements = []) } /** - * @param SpecificationInterface $value + * @param SpecificationInterface $element * * @throws InvalidArgumentException - * - * @return bool */ - public function add($value) + #[\Override] + public function add(mixed $element): void { - if (! $value instanceof SpecificationInterface) { - throw new InvalidArgumentException(sprintf( - '"%s" does not implement "%s"!', - (is_object($value)) ? get_class($value) : $value, - SpecificationInterface::class - )); + if (!$element instanceof SpecificationInterface) { + throw new InvalidArgumentException(sprintf('"%s" does not implement "%s"!', (is_object($element)) ? $element::class : $element, SpecificationInterface::class)); } - return parent::add($value); + parent::add($element); } - /** - * {@inheritdoc} - */ - public function modify(QueryBuilder $queryBuilder, $dqlAlias) + #[\Override] + public function modify(QueryBuilder $queryBuilder, ?string $dqlAlias = null): ?string { - $match = function (SpecificationInterface $specification) use ($queryBuilder, $dqlAlias) { - return $specification->modify($queryBuilder, $dqlAlias); - }; + $match = static fn (SpecificationInterface $specification): ?string => $specification->modify($queryBuilder, $dqlAlias); $result = array_filter(array_map($match, $this->toArray())); - if (empty($result)) { - return; + if ([] === $result) { + return null; } - return call_user_func_array( - [$queryBuilder->expr(), $this->type], - $result - ); + return (string) $queryBuilder->expr()->{$this->type}(...$result); } /** - * {@inheritdoc} + * @throws \Exception */ - public function isSatisfiedBy($value) + #[\Override] + public function isSatisfiedBy(mixed $value): bool { /** @var SpecificationInterface $child */ - foreach ($this as $child) { + foreach ($this->getIterator() as $child) { if ($child->isSatisfiedBy($value)) { continue; } @@ -90,14 +83,17 @@ public function isSatisfiedBy($value) } /** - * @param SpecificationInterface[] $children + * @param array $children * - * @return $this + * @throws InvalidArgumentException */ - protected function setChildren(array $children) + protected function setChildren(array $children): static { $this->clear(); - array_map([$this, 'add'], $children); + + foreach ($children as $child) { + $this->add($child); + } return $this; } @@ -105,16 +101,12 @@ protected function setChildren(array $children) /** * Set the type of comparison. * - * @param string $type - * * @throws InvalidArgumentException - * - * @return $this */ - protected function setType($type) + protected function setType(string $type): static { - if (! in_array($type, self::$types, true)) { - $message = sprintf('"%s" is not a valid type! Valid types: %s', $type, implode(', ', self::$types)); + if (!in_array($type, self::TYPES, true)) { + $message = sprintf('"%s" is not a valid type! Valid types: %s', $type, implode(', ', self::TYPES)); throw new InvalidArgumentException($message); } diff --git a/src/SpecificationAwareInterface.php b/src/SpecificationAwareInterface.php index 72803b8..4cebdc8 100644 --- a/src/SpecificationAwareInterface.php +++ b/src/SpecificationAwareInterface.php @@ -1,9 +1,11 @@ isSatisfiedBy($this->getEntityName())) { - throw new LogicException(sprintf( - 'Specification "%s" not supported by this repository!', - get_class($specification) - )); + if (!$specification->isSatisfiedBy($this->getEntityName())) { + throw new LogicException(sprintf('Specification "%s" not supported by this repository!', $specification::class)); } $queryBuilder = $this->createQueryBuilder($this->dqlAlias); @@ -46,16 +34,13 @@ public function match(SpecificationInterface $specification, ModifierInterface $ * Modifies the QueryBuilder according to the passed Specification. * Will also set the condition for this query if needed. * - * @param QueryBuilder $queryBuilder - * @param SpecificationInterface $specification - * * @internal param string $dqlAlias */ - private function modifyQueryBuilder(QueryBuilder $queryBuilder, SpecificationInterface $specification) + private function modifyQueryBuilder(QueryBuilder $queryBuilder, SpecificationInterface $specification): void { $condition = $specification->modify($queryBuilder, $this->dqlAlias); - if (empty($condition)) { + if (null === $condition) { return; } @@ -64,17 +49,12 @@ private function modifyQueryBuilder(QueryBuilder $queryBuilder, SpecificationInt /** * Modifies and returns a Query object according to the (optional) result modifier. - * - * @param QueryBuilder $queryBuilder - * @param ModifierInterface|null $modifier - * - * @return Query */ - private function modifyQuery(QueryBuilder $queryBuilder, ModifierInterface $modifier = null) + private function modifyQuery(QueryBuilder $queryBuilder, ?ModifierInterface $modifier = null): AbstractQuery { $query = $queryBuilder->getQuery(); - if ($modifier) { + if ($modifier instanceof ModifierInterface) { $modifier->modify($query); }