happyr/doctrine-specification
Reusable Doctrine query Specifications for PHP. Replace messy repositories and huge QueryBuilder methods with small, composable, testable spec classes. Reduce duplication, avoid methods with many arguments, and extend queries cleanly as your app grows.
Full Changelog: https://github.com/Happyr/Doctrine-Specification/compare/v2.0.2...v2.1.0
Changelog (since 2.0.1...2.0.2)
AbstractQuery::toIterable() to compatible with Doctrine ORM 3.0 (@peter-gribanov)yield from for iterate from AbstractQuery::toIterable() (@peter-gribanov)Changelog (since 2.0.0...2.0.1)
DQLContextResolver (@peter-gribanov)default_repository_class instead of repository_factory (@peter-gribanov)BaseSpecification::getNestedContext() method (@peter-gribanov)Changelog (since 1.1.3...2.0.0)
JOIN before using the new alias from it (@peter-gribanov)BaseSpecification methods from Satisfiable interface (@peter-gribanov)IsEmpty filter (@peter-gribanov)SelectNew query modifier for use NEW Operator Syntax (@peter-gribanov)Satisfiable methods (@peter-gribanov)Operands is optional and may not be specified (@peter-gribanov)SelectEntity (@peter-gribanov)$context argument int BaseSpecification::__construct() (@peter-gribanov)DISTINCT in aggregate functions (@peter-gribanov)CountDistinct to Count platform function (@peter-gribanov)CONCAT function declaration (@peter-gribanov)BaseSpecification class (@peter-gribanov)Repository folder (@peter-gribanov)TRIM() platform functions (@peter-gribanov)Arithmetic::MOD constant (@peter-gribanov)Modulo arithmetic operand (@peter-gribanov)BaseSpecification::getSpec() method as abstract (@peter-gribanov)Happyr\DoctrineSpecification\Specification\Having class (@peter-gribanov)AbstractJoin query modifiers (@peter-gribanov)protected properties to private (@peter-gribanov)LogicX class as abstract (@peter-gribanov)Bitwise operands (@peter-gribanov)Comparison class as abstract (@peter-gribanov)The AbstractJoin::getJoinType() method was removed. Use AbstractJoin::modifyJoin() method inside.
The Comparison class marked as abstract.
The LogicX class marked as abstract.
The protected properties in Comparison class marked as private.
The protected properties in In class marked as private.
The protected properties in GroupBy class marked as private.
The protected properties in OrderBy class marked as private.
The protected properties in Limit class marked as private.
The protected properties in Offset class marked as private.
The Bitwise operands was removed.
The Happyr\DoctrineSpecification\Specification\Having class was removed, use
Happyr\DoctrineSpecification\Query\Having instead.
The Happyr\DoctrineSpecification\EntitySpecificationRepository class was removed, use
Happyr\DoctrineSpecification\Repository\EntitySpecificationRepository instead.
The Happyr\DoctrineSpecification\EntitySpecificationRepositoryInterface class was removed, use
Happyr\DoctrineSpecification\Repository\EntitySpecificationRepositoryInterface instead.
The Happyr\DoctrineSpecification\EntitySpecificationRepositoryTrait class was removed, use
Happyr\DoctrineSpecification\Repository\EntitySpecificationRepositoryTrait instead.
The Happyr\DoctrineSpecification\RepositoryFactory class was removed, use
Happyr\DoctrineSpecification\Repository\RepositoryFactory instead.
The Happyr\DoctrineSpecification\BaseSpecification class was removed, use
Happyr\DoctrineSpecification\Specification\BaseSpecification instead.
Removes the ability to use array as argument for the PlatformFunction operand.
Before:
$arguments = ['create_at', new \DateTimeImmutable()];
Spec::DATE_DIFF($arguments);
Spec::fun('DATE_DIFF', $arguments);
new PlatformFunction('DATE_DIFF', $arguments);
After:
$arguments = ['create_at', new \DateTimeImmutable()];
Spec::DATE_DIFF(...$arguments);
Spec::fun('DATE_DIFF', ...$arguments);
new PlatformFunction('DATE_DIFF', ...$arguments);
Removes the ability to use array as argument for the Select query modifier.
Before:
$fields = ['title', 'cover'];
Spec::select($fields);
new Select($fields);
After:
$fields = ['title', 'cover'];
Spec::select(...$fields);
new Select(...$fields);
Removes the ability to use array as argument for the AddSelect query modifier.
Before:
$fields = ['title', 'cover'];
Spec::addSelect($fields);
new AddSelect($fields);
After:
$fields = ['title', 'cover'];
Spec::addSelect(...$fields);
new AddSelect(...$fields);
The BaseSpecification::getSpec() method marked as abstract.
The InvalidArgumentException class marked as final.
The LogicException class marked as final.
The NonUniqueResultException class marked as final.
The NoResultException class marked as final.
The UnexpectedResultException class marked as abstract.
All the filter classes marked as final.
All the logic classes marked as final.
All the operand classes marked as final.
All the query modifier classes marked as final.
All the result modifier classes marked as final.
The CountOf class marked as final.
The DBALTypesResolver class marked as final.
The ValueConverter class marked as final.
The EntitySpecificationRepositoryTrait::getAlias() method returns nothing else.
The Operand::execute() method was added. This method performs the necessary actions on the operand and returns the
result. It is desirable to return a scalar value so that it is compatible with other operands.
The custom platform functions also need to be made executable and register the executor in the registry.
PlatformFunction::getExecutorRegistry()->register('POW', fn ($base, $exp) => pow($base, $exp));
The use of DQL aliases has been replaced with descriptions of contexts.
Before:
Spec::andX(
Spec::innerJoin('contestant', 'ct'),
Spec::innerJoin('contest', 'c', 'ct'),
Spec::innerJoin('user', 'u', 'ct'),
Spec::eq('state', State::active()->value(), 'u'),
Spec::eq('enabled', true, 'c')
);
After:
Spec::andX(
Spec::eq('contestant.user.state', State::active()->value()),
Spec::eq('contestant.contest.enabled', true)
);
Changed behavior of DQL aliases to use context.
Before:
final class PublishedQuestionnaires extends BaseSpecification
{
private string $contest_alias;
private string $contestant_alias;
private string $user_alias;
public function __construct(
string $contest_alias = 'c',
string $contestant_alias = 'ct',
string $user_alias = 'u',
?string $dql_alias = null
) {
$this->contest_alias = $contest_alias;
$this->contestant_alias = $contestant_alias;
$this->user_alias = $user_alias;
parent::__construct($dql_alias);
}
/**
* [@return](https://github.com/return) Filter|QueryModifier
*/
protected function getSpec()
{
return Spec::andX(
Spec::innerJoin('contestant', $this->contestant_alias),
new ContestantPublished($this->contest_alias, $this->user_alias, $this->contestant_alias)
);
}
}
final class ContestantPublished extends BaseSpecification
{
private string $contest_alias;
private string $user_alias;
public function __construct(string $contest_alias = 'c', string $user_alias = 'u', ?string $dql_alias = null)
{
$this->contest_alias = $contest_alias;
$this->user_alias = $user_alias;
parent::__construct($dql_alias);
}
/**
* [@return](https://github.com/return) Filter|QueryModifier
*/
protected function getSpec()
{
return Spec::andX(
new JoinedContestant($this->contest_alias, $this->user_alias),
new ContestantApproved($this->contest_alias)
);
}
}
final class ContestantApproved extends BaseSpecification implements Satisfiable
{
private string $contest_alias;
public function __construct(string $contest_alias = 'c', ?string $dql_alias = null)
{
$this->contest_alias = $contest_alias;
parent::__construct($dql_alias);
}
/**
* [@return](https://github.com/return) Filter|QueryModifier
*/
protected function getSpec()
{
return Spec::orX(
Spec::eq('permission', Permission::approved()->value()),
Spec::not(new ContestRequireModeration($this->contest_alias))
);
}
}
After:
final class PublishedQuestionnaires extends BaseSpecification
{
/**
* [@return](https://github.com/return) Filter|QueryModifier
*/
protected function getSpec()
{
return new ContestantPublished('contestant');
}
}
final class ContestantPublished extends BaseSpecification
{
/**
* [@return](https://github.com/return) Filter|QueryModifier
*/
protected function getSpec()
{
return Spec::andX(
new JoinedContestant(),
new ContestantApproved()
);
}
}
final class ContestantApproved extends BaseSpecification implements Satisfiable
{
/**
* [@return](https://github.com/return) Filter|QueryModifier
*/
protected function getSpec()
{
return Spec::orX(
Spec::eq('permission', Permission::approved()->value()),
Spec::not(new ContestRequireModeration('contest'))
);
}
}
The Satisfiable interface was added.
The Specification interface was extends Satisfiable interface.
The BaseSpecification class implement Satisfiable interface.
The Happyr\DoctrineSpecification\Operand\CountDistinct class was removed, use
Happyr\DoctrineSpecification\Operand\PlatformFunction\Count instead.
The Spec::countDistinct() method was removed, use Spec::COUNT() instead.
Before:
new CountDistinct('field_name');
Spec::countDistinct('field_name');
After:
new Count('field_name', true);
Spec::COUNT('field_name', true);
The COUNT function as argument to Spec::fun() is not longer supported, use Spec::COUNT() instead.
The COUNT function as argument to Happyr\DoctrineSpecification\Operand\PlatformFunction is not longer supported,
use Happyr\DoctrineSpecification\Operand\PlatformFunction\Count instead.
Before:
new PlatformFunction('COUNT', 'field_name');
Spec::fun('COUNT', 'field_name');
After:
new Count('field_name');
Spec::COUNT('field_name');
The AVG function as argument to Spec::fun() is not longer supported, use Spec::AVG() instead.
The AVG function as argument to Happyr\DoctrineSpecification\Operand\PlatformFunction is not longer supported,
use Happyr\DoctrineSpecification\Operand\PlatformFunction\Avg instead.
Before:
new PlatformFunction('AVG', 'field_name');
Spec::fun('AVG', 'field_name');
After:
new Avg('field_name');
Spec::AVG('field_name');
The MIN function as argument to Spec::fun() is not longer supported, use Spec::MIN() instead.
The MIN function as argument to Happyr\DoctrineSpecification\Operand\PlatformFunction is not longer supported,
use Happyr\DoctrineSpecification\Operand\PlatformFunction\Min instead.
Before:
new PlatformFunction('MIN', 'field_name');
Spec::fun('MIN', 'field_name');
After:
new Min('field_name');
Spec::MIN('field_name');
The MAX function as argument to Spec::fun() is not longer supported, use Spec::MAX() instead.
The MAX function as argument to Happyr\DoctrineSpecification\Operand\PlatformFunction is not longer supported,
use Happyr\DoctrineSpecification\Operand\PlatformFunction\Max instead.
Before:
new PlatformFunction('MAX', 'field_name');
Spec::fun('MAX', 'field_name');
After:
new Max('field_name');
Spec::MAX('field_name');
The SUM function as argument to Spec::fun() is not longer supported, use Spec::SUM() instead.
The SUM function as argument to Happyr\DoctrineSpecification\Operand\PlatformFunction is not longer supported,
use Happyr\DoctrineSpecification\Operand\PlatformFunction\Sum instead.
Before:
new PlatformFunction('SUM', 'field_name');
Spec::fun('SUM', 'field_name');
After:
new Sum('field_name');
Spec::SUM('field_name');
Define Spec::leftJoin(), Spec::innerJoin() and Spec::join() before using the new alias from it.
Before:
$spec = Spec::andX(
Spec::select(Spec::selectEntity('person')),
Spec::leftJoin('person', 'person')
);
After:
$spec = Spec::andX(
Spec::leftJoin('person', 'person'),
Spec::select(Spec::selectEntity('person'))
);
Changelog (since 1.1.2...1.1.3)
BaseSpecification class (@peter-gribanov)Repository folder (@peter-gribanov)Changelog (since 1.1.1...1.1.2)
MemberOfX::getFilter() and LogicX::getFilter() should return a string (@peter-gribanov)no_superfluous_phpdoc_tags_symfony in Style CI config (@peter-gribanov)short_array_syntax CS fixer (@peter-gribanov)Changelog (since 1.1.0...1.1.1)
Changelog (since 1.0.6...1.1.0)
single_line_throw and no_superfluous_phpdoc_tags rules (@peter-gribanov)master from 1.0 (@peter-gribanov)Operand (@peter-gribanov)RoundDateTimeSpec (@peter-gribanov)RoundDateTimeSpec must extends ObjectBehavior (@peter-gribanov)branch-alias for master to 1.1-dev (@peter-gribanov)php-cs-fixer to control the code style during development (@peter-gribanov)DateTimeInterface::setTimestamp(). (@peter-gribanov)Changelog (since 1.0.5...1.0.6)
Changelog (since 1.0.4...1.0.5)
Distinct query modifier and CountDistinct operand (@peter-gribanov)Changelog (since 1.0.3...1.0.4)
Having spec return NULL as filter (@peter-gribanov)Changelog (since 0.1.0...0.2.0)
README.md (@cordoval)LIKE specification (@AP-Hunt)OrderBy and Limit. Fixed #24 (@Nyholm)In spec. (@Nyholm)Logic Spec's (@cakper)Changelog (since 0.2.0...0.3.0)
getWrapped* functions (@Nyholm)Expression to Filter (@cakper)Modifiers to QueryModifier and ResultModifier #59 (@Nyholm)logicX takes an Expression (@Nyholm)IsNull and IsNotNull should not have a value (@Nyholm)ModifierCollection (@cordoval)Changelog (since 0.3.0...0.3.1)
Changelog (since 0.3.1...0.4.0)
Cache ResultModifier (@cakper, @Nyholm)AsSingle ResultModifier (@Nyholm)Offset Query Spec (@drewclauson)EntitySpecificationRepository (@Nyholm)Changelog (since 0.4.0...0.5.0)
Specification interface to Specification folder (@Nyholm)doctrine/orm 2.5 (@Nyholm)notIn filter (@drewclauson)AbstractJoin and added innerJoin (@Nyholm)QueryModifier instead of Specification (@Nyholm)Changelog (since 0.5.0...0.6.0)
Changelog (since 0.6.0...0.6.1)
INSTANCE OF (@Strontium-90)Filter and QueryModifier. (@Strontium-90)EntitySpecificationRepository accept specs not creating a filter (@kgilden)Spec::asSingle, matchSingleResult, matchOneOrNullResult (@adamquaile)AndX/OrX (@Nyholm)GroupBy and Having (@Nyholm)Changelog (since 0.6.1...0.6.2)
AsSingle (@Nyholm)Changelog (since 0.6.2..0.7.0)
Changelog (since 0.7.0..0.7.1)
Changelog (since 1.0.2...1.0.3)
BaseSpecification::getSpec() (@jaikdean)Changelog (since 1.0.1...1.0.2)
Changelog (since 1.0.0...1.0.1)
Spec::fun() (@peter-gribanov)Changelog (since 0.8.1...1.0.0)
[PR] #184
Changelog (since 0.8.0...0.8.1)
Changelog (since 0.7.2...0.8.0)
[PR] #168
Changelog (since 0.7.1...0.7.2)
How can I help you explore Laravel packages today?