diff --git a/README.md b/README.md index f9bee03..413e247 100644 --- a/README.md +++ b/README.md @@ -168,7 +168,5 @@ This library is heavily inspired by Benjamin Eberlei's [blog post][blog_post] and [Happyr's Doctrine-Specification library][happyr_spec]. [specification_pattern]: http://en.wikipedia.org/wiki/Specification_pattern - [happyr_spec]: https://github.com/Happyr/Doctrine-Specification - [blog_post]: http://www.whitewashing.de/2013/03/04/doctrine_repositories.html diff --git a/spec/Condition/NotInSpec.php b/spec/Condition/NotInSpec.php index ee65a3a..1840142 100644 --- a/spec/Condition/NotInSpec.php +++ b/spec/Condition/NotInSpec.php @@ -6,7 +6,7 @@ use Doctrine\ORM\Query\Expr; use Doctrine\ORM\QueryBuilder; use PhpSpec\ObjectBehavior; -use Purist\Specification\Doctrine\Condition\In; +use Purist\Specification\Doctrine\Condition\NotIn; use Purist\Specification\Doctrine\SpecificationInterface; class NotInSpec extends ObjectBehavior @@ -23,7 +23,7 @@ public function let(): void 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): void diff --git a/spec/Logic/NotSpec.php b/spec/Logic/NotSpec.php index 985845c..7c456a6 100644 --- a/spec/Logic/NotSpec.php +++ b/spec/Logic/NotSpec.php @@ -11,7 +11,7 @@ class NotSpec extends ObjectBehavior { public function let(SpecificationInterface $condition): void { - $this->beConstructedWith($condition, null); + $this->beConstructedWith($condition); } public function it_calls_parent_match(QueryBuilder $queryBuilder, Expr $expr, SpecificationInterface $condition): void @@ -32,16 +32,16 @@ public function it_calls_parent_match(QueryBuilder $queryBuilder, Expr $expr, Sp 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): void { $className = 'foo'; - $this->beConstructedWith($specification, null); + $this->beConstructedWith($specification); $specification->isSatisfiedBy($className)->shouldBeCalled(); diff --git a/spec/Query/HavingSpec.php b/spec/Query/HavingSpec.php index 064c6d1..6c991b4 100644 --- a/spec/Query/HavingSpec.php +++ b/spec/Query/HavingSpec.php @@ -38,33 +38,30 @@ public function it_calls_having_on_query_builder(QueryBuilder $queryBuilder, Spe { $condition = 'foo'; $specification->modify($queryBuilder, $this->dqlAlias)->willReturn($condition); - - $this->setType(Having::HAVING); $queryBuilder->having($condition)->shouldBeCalled()->willReturn($queryBuilder); - $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): void { $condition = 'foo'; $specification->modify($queryBuilder, $this->dqlAlias)->willReturn($condition); - - $this->setType(Having::AND_HAVING); $queryBuilder->andHaving($condition)->shouldBeCalled()->willReturn($queryBuilder); - $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): void { $condition = 'foo'; $specification->modify($queryBuilder, $this->dqlAlias)->willReturn($condition); - - $this->setType(Having::OR_HAVING); $queryBuilder->orHaving($condition)->shouldBeCalled()->willReturn($queryBuilder); - $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(): void diff --git a/spec/Query/JoinSpec.php b/spec/Query/JoinSpec.php index 89fe300..61d67d2 100644 --- a/spec/Query/JoinSpec.php +++ b/spec/Query/JoinSpec.php @@ -13,7 +13,7 @@ class JoinSpec extends ObjectBehavior { public function let(): void { - $this->beConstructedWith('user', 'authUser', 'a'); + $this->beConstructedWith('user', 'authUser', Join::JOIN, 'a'); } public function it_is_a_specification(): void @@ -46,12 +46,11 @@ public function it_should_use_be_able_to_use_join_conditions(QueryBuilder $query $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()->willReturn($queryBuilder); - $this->modify($queryBuilder, 'a'); + $join->modify($queryBuilder, 'a'); } public function it_should_be_able_to_set_index_by_for_join(QueryBuilder $queryBuilder): void @@ -62,9 +61,9 @@ public function it_should_be_able_to_set_index_by_for_join(QueryBuilder $queryBu $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): void @@ -78,9 +77,8 @@ public function it_should_accept_specifications_as_condition(QueryBuilder $query $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(): void diff --git a/src/AbstractSpecification.php b/src/AbstractSpecification.php index 6420a05..3fa85a7 100644 --- a/src/AbstractSpecification.php +++ b/src/AbstractSpecification.php @@ -2,7 +2,7 @@ namespace Purist\Specification\Doctrine; -abstract class AbstractSpecification implements SpecificationInterface +abstract readonly class AbstractSpecification implements SpecificationInterface { public function __construct(protected string $field, protected ?string $dqlAlias = null) { @@ -31,7 +31,7 @@ protected function createAliasedName(string $value, ?string $dqlAlias): string return $value; } - if (null !== $this->dqlAlias && '' !== $this->dqlAlias && '0' !== $this->dqlAlias) { + if (null !== $this->dqlAlias) { $dqlAlias = $this->dqlAlias; } diff --git a/src/Condition/Between.php b/src/Condition/Between.php index e62ac56..4b5e005 100644 --- a/src/Condition/Between.php +++ b/src/Condition/Between.php @@ -5,7 +5,7 @@ use Doctrine\ORM\QueryBuilder; use Purist\Specification\Doctrine\AbstractSpecification; -class Between extends AbstractSpecification +readonly class Between extends AbstractSpecification { public function __construct(string $field, protected mixed $from, protected mixed $to, ?string $dqlAlias = null) { diff --git a/src/Condition/Comparison.php b/src/Condition/Comparison.php index 48b84b2..7ff4578 100644 --- a/src/Condition/Comparison.php +++ b/src/Condition/Comparison.php @@ -7,39 +7,29 @@ use Purist\Specification\Doctrine\AbstractSpecification; use Purist\Specification\Doctrine\Exception\InvalidArgumentException; -class Comparison extends AbstractSpecification +readonly class Comparison extends AbstractSpecification { 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 array $operators = [self::EQ, self::NEQ, self::LT, self::LTE, self::GT, self::GTE, self::LIKE]; - protected string $operator; + protected const array OPERATORS = [self::EQ, self::NEQ, self::LT, self::LTE, self::GT, self::GTE, self::LIKE]; /** * @throws InvalidArgumentException */ - public function __construct(string $operator, string $field, protected string $value, ?string $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; - parent::__construct($field, $dqlAlias); } diff --git a/src/Condition/Equals.php b/src/Condition/Equals.php index 13b9008..56f7ed1 100644 --- a/src/Condition/Equals.php +++ b/src/Condition/Equals.php @@ -4,7 +4,7 @@ use Purist\Specification\Doctrine\Exception\InvalidArgumentException; -class Equals extends Comparison +readonly class Equals extends Comparison { /** * @throws InvalidArgumentException diff --git a/src/Condition/EqualsProperty.php b/src/Condition/EqualsProperty.php index 5eebf0e..3559dbd 100644 --- a/src/Condition/EqualsProperty.php +++ b/src/Condition/EqualsProperty.php @@ -6,7 +6,7 @@ use Doctrine\ORM\QueryBuilder; use Purist\Specification\Doctrine\Exception\InvalidArgumentException; -class EqualsProperty extends Comparison +readonly class EqualsProperty extends Comparison { /** * @throws InvalidArgumentException diff --git a/src/Condition/GreaterThan.php b/src/Condition/GreaterThan.php index 0bfc153..32ebba1 100644 --- a/src/Condition/GreaterThan.php +++ b/src/Condition/GreaterThan.php @@ -4,7 +4,7 @@ use Purist\Specification\Doctrine\Exception\InvalidArgumentException; -class GreaterThan extends Comparison +readonly class GreaterThan extends Comparison { /** * @throws InvalidArgumentException diff --git a/src/Condition/GreaterThanOrEquals.php b/src/Condition/GreaterThanOrEquals.php index a237aa0..1a6cbd0 100644 --- a/src/Condition/GreaterThanOrEquals.php +++ b/src/Condition/GreaterThanOrEquals.php @@ -4,7 +4,7 @@ use Purist\Specification\Doctrine\Exception\InvalidArgumentException; -class GreaterThanOrEquals extends Comparison +readonly class GreaterThanOrEquals extends Comparison { /** * @throws InvalidArgumentException diff --git a/src/Condition/In.php b/src/Condition/In.php index 17bcb4f..ec087d6 100644 --- a/src/Condition/In.php +++ b/src/Condition/In.php @@ -5,7 +5,7 @@ use Doctrine\ORM\QueryBuilder; use Purist\Specification\Doctrine\AbstractSpecification; -class In extends AbstractSpecification +readonly class In extends AbstractSpecification { public function __construct(string $field, protected mixed $value, ?string $dqlAlias = null) { diff --git a/src/Condition/IsInstanceOf.php b/src/Condition/IsInstanceOf.php index 066d939..5c1053b 100644 --- a/src/Condition/IsInstanceOf.php +++ b/src/Condition/IsInstanceOf.php @@ -5,7 +5,7 @@ use Doctrine\ORM\QueryBuilder; use Purist\Specification\Doctrine\AbstractSpecification; -class IsInstanceOf extends AbstractSpecification +readonly class IsInstanceOf extends AbstractSpecification { public function __construct(string $field, private string $className, ?string $dqlAlias = null) { diff --git a/src/Condition/IsMemberOf.php b/src/Condition/IsMemberOf.php index fa33a60..357462e 100644 --- a/src/Condition/IsMemberOf.php +++ b/src/Condition/IsMemberOf.php @@ -5,7 +5,7 @@ use Doctrine\ORM\QueryBuilder; use Purist\Specification\Doctrine\AbstractSpecification; -class IsMemberOf extends AbstractSpecification +readonly class IsMemberOf extends AbstractSpecification { public function __construct(string $field, private string $className, ?string $dqlAlias = null) { diff --git a/src/Condition/IsNotNull.php b/src/Condition/IsNotNull.php index 8351e1b..c3912b5 100644 --- a/src/Condition/IsNotNull.php +++ b/src/Condition/IsNotNull.php @@ -3,8 +3,9 @@ namespace Purist\Specification\Doctrine\Condition; use Doctrine\ORM\QueryBuilder; +use Purist\Specification\Doctrine\AbstractSpecification; -class IsNotNull extends IsNull +readonly class IsNotNull extends AbstractSpecification { #[\Override] public function modify(QueryBuilder $queryBuilder, ?string $dqlAlias = null): string diff --git a/src/Condition/IsNull.php b/src/Condition/IsNull.php index 99a7268..0fa1cec 100644 --- a/src/Condition/IsNull.php +++ b/src/Condition/IsNull.php @@ -5,7 +5,7 @@ use Doctrine\ORM\QueryBuilder; use Purist\Specification\Doctrine\AbstractSpecification; -class IsNull extends AbstractSpecification +readonly class IsNull extends AbstractSpecification { #[\Override] public function modify(QueryBuilder $queryBuilder, ?string $dqlAlias = null): string diff --git a/src/Condition/LessThan.php b/src/Condition/LessThan.php index 7e572ec..57f5f8e 100644 --- a/src/Condition/LessThan.php +++ b/src/Condition/LessThan.php @@ -4,7 +4,7 @@ use Purist\Specification\Doctrine\Exception\InvalidArgumentException; -class LessThan extends Comparison +readonly class LessThan extends Comparison { /** * @throws InvalidArgumentException diff --git a/src/Condition/LessThanOrEquals.php b/src/Condition/LessThanOrEquals.php index 7c7c1e8..0795d12 100644 --- a/src/Condition/LessThanOrEquals.php +++ b/src/Condition/LessThanOrEquals.php @@ -4,7 +4,7 @@ use Purist\Specification\Doctrine\Exception\InvalidArgumentException; -class LessThanOrEquals extends Comparison +readonly class LessThanOrEquals extends Comparison { /** * @throws InvalidArgumentException diff --git a/src/Condition/Like.php b/src/Condition/Like.php index 41bce67..30e7ce0 100644 --- a/src/Condition/Like.php +++ b/src/Condition/Like.php @@ -4,12 +4,10 @@ use Purist\Specification\Doctrine\Exception\InvalidArgumentException; -class Like extends Comparison +readonly class Like extends Comparison { public const string CONTAINS = '%%%s%%'; - public const string ENDS_WITH = '%%%s'; - public const string STARTS_WITH = '%s%%'; /** diff --git a/src/Condition/NotEquals.php b/src/Condition/NotEquals.php index fc2898a..f0d1be5 100644 --- a/src/Condition/NotEquals.php +++ b/src/Condition/NotEquals.php @@ -4,7 +4,7 @@ use Purist\Specification\Doctrine\Exception\InvalidArgumentException; -class NotEquals extends Comparison +readonly class NotEquals extends Comparison { /** * @throws InvalidArgumentException diff --git a/src/Condition/NotIn.php b/src/Condition/NotIn.php index cfb7631..554b551 100644 --- a/src/Condition/NotIn.php +++ b/src/Condition/NotIn.php @@ -3,9 +3,15 @@ namespace Purist\Specification\Doctrine\Condition; use Doctrine\ORM\QueryBuilder; +use Purist\Specification\Doctrine\AbstractSpecification; -class NotIn extends In +readonly class NotIn extends AbstractSpecification { + public function __construct(string $field, protected mixed $value, ?string $dqlAlias = null) + { + parent::__construct($field, $dqlAlias); + } + #[\Override] public function modify(QueryBuilder $queryBuilder, ?string $dqlAlias = null): string { @@ -18,7 +24,6 @@ public function modify(QueryBuilder $queryBuilder, ?string $dqlAlias = null): st ); } - #[\Override] protected function generateParameterName(QueryBuilder $queryBuilder): string { return sprintf('not_in_%d', count($queryBuilder->getParameters())); diff --git a/src/Logic/Not.php b/src/Logic/Not.php index d410bc2..a269cb8 100644 --- a/src/Logic/Not.php +++ b/src/Logic/Not.php @@ -18,7 +18,8 @@ public function __construct(private SpecificationInterface $parent) public function modify(QueryBuilder $queryBuilder, ?string $dqlAlias = null): ?string { $filter = $this->parent->modify($queryBuilder, $dqlAlias); - if (null === $filter || '' === $filter || '0' === $filter) { + + if (null === $filter) { return ''; } diff --git a/src/Query/GroupBy.php b/src/Query/GroupBy.php index b533b35..1f105f5 100644 --- a/src/Query/GroupBy.php +++ b/src/Query/GroupBy.php @@ -9,23 +9,25 @@ /** * @author Kyle Tucker */ -class GroupBy extends AbstractSpecification +readonly class GroupBy extends AbstractSpecification { public const string GROUP_BY = 'groupBy'; public const string ADD_GROUP_BY = 'addGroupBy'; - - /** @var string[] */ - protected static array $types = [self::GROUP_BY, self::ADD_GROUP_BY]; - protected string $type; + /** + * @var array + */ + protected const array TYPES = [self::GROUP_BY, self::ADD_GROUP_BY]; /** * @throws InvalidArgumentException */ - public function __construct(string $field, string $type = self::ADD_GROUP_BY, ?string $dqlAlias = null) + public function __construct(string $field, protected string $type = self::ADD_GROUP_BY, ?string $dqlAlias = null) { parent::__construct($field, $dqlAlias); - $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))); + } } #[\Override] @@ -35,16 +37,4 @@ public function modify(QueryBuilder $queryBuilder, ?string $dqlAlias = null): ?s return null; } - - /** - * @throws InvalidArgumentException - */ - public function setType(string $type): void - { - 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; - } } diff --git a/src/Query/Having.php b/src/Query/Having.php index d78e343..aeb4d47 100644 --- a/src/Query/Having.php +++ b/src/Query/Having.php @@ -7,26 +7,26 @@ use Purist\Specification\Doctrine\SpecificationInterface; /** - * @author Kyle Tucker + * @author Kyle Tucker */ -class Having implements SpecificationInterface +readonly class Having implements SpecificationInterface { public const string HAVING = 'having'; public const string AND_HAVING = 'andHaving'; public const string OR_HAVING = 'orHaving'; - /** * @var array */ - protected static array $types = [self::HAVING, self::AND_HAVING, self::OR_HAVING]; - protected string $type; + protected const array TYPES = [self::HAVING, self::AND_HAVING, self::OR_HAVING]; /** * @throws InvalidArgumentException */ - public function __construct(protected SpecificationInterface $specification, string $type = self::AND_HAVING) + public function __construct(protected SpecificationInterface $specification, protected string $type = self::AND_HAVING) { - $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))); + } } #[\Override] @@ -46,12 +46,8 @@ public function isSatisfiedBy(mixed $value): bool /** * @throws InvalidArgumentException */ - public function setType(string $type): void + 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 9380ca8..ed5039a 100644 --- a/src/Query/IndexBy.php +++ b/src/Query/IndexBy.php @@ -9,7 +9,7 @@ /** * IndexBy will modify the query-builder, so you can specify INDEX BY-statements. */ -class IndexBy extends AbstractSpecification +readonly class IndexBy extends AbstractSpecification { /** * @throws QueryException diff --git a/src/Query/InnerJoin.php b/src/Query/InnerJoin.php index 18711d0..e009a87 100644 --- a/src/Query/InnerJoin.php +++ b/src/Query/InnerJoin.php @@ -4,15 +4,13 @@ use Purist\Specification\Doctrine\Exception\InvalidArgumentException; -class InnerJoin extends Join +readonly class InnerJoin extends Join { /** * @throws InvalidArgumentException */ public function __construct(string $field, string $newAlias, ?string $dqlAlias = null) { - parent::__construct($field, $newAlias, $dqlAlias); - - $this->setType(self::INNER_JOIN); + parent::__construct($field, $newAlias, self::INNER_JOIN, $dqlAlias); } } diff --git a/src/Query/Join.php b/src/Query/Join.php index de28d25..3f72c61 100644 --- a/src/Query/Join.php +++ b/src/Query/Join.php @@ -7,39 +7,33 @@ use Purist\Specification\Doctrine\Exception\InvalidArgumentException; use Purist\Specification\Doctrine\SpecificationInterface; -class Join extends AbstractSpecification +readonly class Join extends AbstractSpecification { public const string JOIN = 'join'; public const string LEFT_JOIN = 'leftJoin'; public const string INNER_JOIN = 'innerJoin'; - /** * @var array */ - protected static array $types = [self::JOIN, self::LEFT_JOIN, self::INNER_JOIN]; - - private ?string $conditionType = null; - private string|SpecificationInterface|null $condition = null; - private ?string $indexedBy = null; - private string $type = self::JOIN; - - public function __construct(string $field, private readonly string $newAlias, ?string $dqlAlias = null) - { - parent::__construct($field, $dqlAlias); - } + protected const array TYPES = [self::JOIN, self::LEFT_JOIN, self::INNER_JOIN]; /** * @throws InvalidArgumentException */ - public function setType(string $type): static - { - 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, + private string $type = self::JOIN, + ?string $dqlAlias = null, + 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); } #[\Override] @@ -62,32 +56,66 @@ public function modify(QueryBuilder $queryBuilder, ?string $dqlAlias = null): ?s } /** - * Set the condition type to be used on the join (WITH/ON). + * @throws InvalidArgumentException */ - public function setConditionType(?string $conditionType): static + public function setType(string $type): self { - $this->conditionType = $conditionType; + return new self( + $this->field, + $this->newAlias, + $type, + $this->dqlAlias, + $this->condition, + $this->conditionType, + $this->indexedBy, + ); + } - return $this; + /** + * Set the condition type to be used on the join (WITH/ON). + */ + public function setConditionType(?string $conditionType): self + { + return new self( + $this->field, + $this->newAlias, + $this->type, + $this->dqlAlias, + $this->condition, + $conditionType, + $this->indexedBy, + ); } /** * Set the condition to be used for the join statement. */ - public function setCondition(SpecificationInterface|string|null $condition): static + public function setCondition(SpecificationInterface|string|null $condition): self { - $this->condition = $condition; - - return $this; + return new self( + $this->field, + $this->newAlias, + $this->type, + $this->dqlAlias, + $condition, + $this->conditionType, + $this->indexedBy, + ); } /** * Set the property which will be used as index for the returned collection. */ - public function setIndexedBy(?string $indexedBy): static + public function setIndexedBy(?string $indexedBy): self { - $this->indexedBy = $indexedBy; - - return $this; + return new self( + $this->field, + $this->newAlias, + $this->type, + $this->dqlAlias, + $this->condition, + $this->conditionType, + $indexedBy, + ); } } diff --git a/src/Query/LeftJoin.php b/src/Query/LeftJoin.php index c562b71..64261ab 100644 --- a/src/Query/LeftJoin.php +++ b/src/Query/LeftJoin.php @@ -4,15 +4,13 @@ use Purist\Specification\Doctrine\Exception\InvalidArgumentException; -class LeftJoin extends Join +readonly class LeftJoin extends Join { /** * @throws InvalidArgumentException */ public function __construct(string $field, string $newAlias, ?string $dqlAlias = null) { - parent::__construct($field, $newAlias, $dqlAlias); - - $this->setType(self::LEFT_JOIN); + parent::__construct($field, $newAlias, self::LEFT_JOIN, $dqlAlias); } } diff --git a/src/Query/OrderBy.php b/src/Query/OrderBy.php index 8ea5918..ae87a58 100644 --- a/src/Query/OrderBy.php +++ b/src/Query/OrderBy.php @@ -6,29 +6,26 @@ use Purist\Specification\Doctrine\AbstractSpecification; use Purist\Specification\Doctrine\Exception\InvalidArgumentException; -class OrderBy extends AbstractSpecification +readonly class OrderBy extends AbstractSpecification { public const string ASC = 'ASC'; public const string DESC = 'DESC'; - protected ?string $order; /** * @var array */ - private static array $validOrder = [self::ASC, self::DESC]; + private const array VALID_ORDERS = [self::ASC, self::DESC]; /** * @throws InvalidArgumentException */ - public function __construct(string $field, ?string $order = null, ?string $dqlAlias = null) + public function __construct(string $field, private ?string $order = null, ?string $dqlAlias = null) { $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); } diff --git a/src/Query/Select.php b/src/Query/Select.php index 5a998ee..c3f5546 100644 --- a/src/Query/Select.php +++ b/src/Query/Select.php @@ -9,24 +9,25 @@ /** * Select will modify the query-builder, so you can specify SELECT-statements. */ -class Select implements SpecificationInterface +readonly class Select implements SpecificationInterface { public const string SELECT = 'select'; public const string ADD_SELECT = 'addSelect'; /** * @var array */ - protected static array $types = [self::SELECT, self::ADD_SELECT]; - protected string $type; + protected const array TYPES = [self::SELECT, self::ADD_SELECT]; /** * @param string|array $select * * @throws InvalidArgumentException */ - public function __construct(protected string|array $select, string $type = self::ADD_SELECT) + public function __construct(protected string|array $select, private string $type = self::ADD_SELECT) { - $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))); + } } #[\Override] @@ -40,13 +41,9 @@ public function modify(QueryBuilder $queryBuilder, ?string $dqlAlias = null): ?s /** * @throws InvalidArgumentException */ - public function setType(string $type): void + 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); } #[\Override] diff --git a/src/Result/AsArray.php b/src/Result/AsArray.php index f045802..e1690b5 100644 --- a/src/Result/AsArray.php +++ b/src/Result/AsArray.php @@ -7,7 +7,7 @@ /** * Hydrate results as array instead of objects. */ -class AsArray implements ModifierInterface +readonly class AsArray implements ModifierInterface { /** * Modify the query (e.g. select more fields/relations). diff --git a/src/Result/ModifierCollection.php b/src/Result/ModifierCollection.php index 42bc877..b774166 100644 --- a/src/Result/ModifierCollection.php +++ b/src/Result/ModifierCollection.php @@ -30,18 +30,18 @@ public function __construct(mixed ...$modifiers) } /** - * @param ModifierInterface $value + * @param ModifierInterface $element * * @throws InvalidArgumentException */ #[\Override] - public function add(mixed $value): void + public function add(mixed $element): void { - if (!$value instanceof ModifierInterface) { - throw new InvalidArgumentException(sprintf('"%s" does not implement "%s"!', (is_object($value)) ? $value::class : $value, ModifierInterface::class)); + if (!$element instanceof ModifierInterface) { + throw new InvalidArgumentException(sprintf('"%s" does not implement "%s"!', (is_object($element)) ? $element::class : $element, ModifierInterface::class)); } - parent::add($value); + parent::add($element); } /** diff --git a/src/Specification.php b/src/Specification.php index f839995..61fbf0b 100644 --- a/src/Specification.php +++ b/src/Specification.php @@ -19,11 +19,13 @@ class Specification extends ArrayCollection implements SpecificationInterface /** * @var array */ - protected static array $types = [self::OR_X, 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,18 +35,18 @@ public function __construct(array $elements = []) } /** - * @param SpecificationInterface $value + * @param SpecificationInterface $element * * @throws InvalidArgumentException */ #[\Override] - public function add(mixed $value): void + public function add(mixed $element): void { - if (!$value instanceof SpecificationInterface) { - throw new InvalidArgumentException(sprintf('"%s" does not implement "%s"!', (is_object($value)) ? $value::class : $value, SpecificationInterface::class)); + if (!$element instanceof SpecificationInterface) { + throw new InvalidArgumentException(sprintf('"%s" does not implement "%s"!', (is_object($element)) ? $element::class : $element, SpecificationInterface::class)); } - parent::add($value); + parent::add($element); } #[\Override] @@ -60,11 +62,14 @@ public function modify(QueryBuilder $queryBuilder, ?string $dqlAlias = null): ?s return $queryBuilder->expr()->{$this->type}(...$result); } + /** + * @throws \Exception + */ #[\Override] public function isSatisfiedBy(mixed $value): bool { /** @var SpecificationInterface $child */ - foreach ($this as $child) { + foreach ($this->getIterator() as $child) { if ($child->isSatisfiedBy($value)) { continue; } @@ -76,12 +81,17 @@ public function isSatisfiedBy(mixed $value): bool } /** - * @param SpecificationInterface[] $children + * @param array $children + * + * @throws InvalidArgumentException */ protected function setChildren(array $children): static { $this->clear(); - array_map($this->add(...), $children); + + foreach ($children as $child) { + $this->add($child); + } return $this; } @@ -93,8 +103,8 @@ protected function setChildren(array $children): static */ 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); }