Happyr / Doctrine-Specification

This library gives you a new way for writing queries. Using the Specification pattern you will get small Specification classes that are highly reusable.
MIT License
446 stars 41 forks source link

Not use BaseSpecification::getNestedContext() method #296

Closed peter-gribanov closed 3 years ago

peter-gribanov commented 3 years ago

Using of BaseSpecification::getNestedContext() can duplicate context.

Example:

final class UserActivated extends BaseSpecification
{
    protected function getSpec()
    {
        return new Equals('state', State::active(), State::class, new StateType());
    }
}

final class ContestPublished extends BaseSpecification
{

    protected function getSpec()
    {
        return Spec::eq('enabled', true);
    }
}

final class JoinedContestant extends BaseSpecification
{
    protected function getSpec()
    {
        return Spec::andX(
            new UserActivated($this->getNestedContext('user')),
            new ContestPublished($this->getNestedContext('contest')),
        );
    }
}

$this->rep->match(new JoinedContestant('contestant'));

In this example as result used root.contestant.contestant.user and root.contestant.contestant.contest context. The contestant context has been added twice by BaseSpecification::getNestedContext() in JoinedContestant spec and BaseSpecification::getContext() in BaseSpecification::getFilter().

Result DQL:

SELECT
    root
FROM
    Questionnaire root
INNER JOIN
    root.contestant contestant
INNER JOIN
    contestant.contestant contestant603e377bae24a
INNER JOIN
    contestant603e377bae24a.user user
INNER JOIN
    contestant603e377bae24a.contest contest
WHERE
    user.state = :comparison_0 AND
    contest.enabled = :comparison_1

To fix the problem, you should stop using the BaseSpecification::getNestedContext() method.

final class JoinedContestant extends BaseSpecification
{
    protected function getSpec()
    {
        return Spec::andX(
            new UserActivated('user'),
            new ContestPublished('contest'),
        );
    }
}

Result DQL:

SELECT
    root
FROM
    Questionnaire root
INNER JOIN
    root.contestant contestant
INNER JOIN
    contestant.user user
INNER JOIN
    contestant.contest contest
WHERE
    user.state = :comparison_0 AND
    contest.enabled = :comparison_1