Failed Conditions
Push — master ( 2ade86...13f838 )
by Jonathan
18s
created

Doctrine/Tests/ORM/Query/CustomTreeWalkersTest.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace Doctrine\Tests\ORM\Functional;
4
5
use Doctrine\ORM\Query;
6
use Doctrine\ORM\Query\QueryException;
7
use Doctrine\Tests\Models\CMS\CmsAddress;
8
use Doctrine\Tests\Models\CMS\CmsUser;
9
use Doctrine\Tests\OrmTestCase;
10
11
/**
12
 * Test case for custom AST walking and modification.
13
 *
14
 * @author      Roman Borschel <[email protected]>
15
 * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
16
 * @link        http://www.doctrine-project.org
17
 * @since       2.0
18
 */
19
class CustomTreeWalkersTest extends OrmTestCase
20
{
21
    private $_em;
22
23
    protected function setUp()
24
    {
25
        $this->_em = $this->_getTestEntityManager();
26
    }
27
28
    public function generateSql($dqlToBeTested, $treeWalkers, $outputWalker)
29
    {
30
        $query = $this->_em->createQuery($dqlToBeTested);
31
        $query->setHint(Query::HINT_CUSTOM_TREE_WALKERS, $treeWalkers)
32
            ->useQueryCache(false);
33
34
        if ($outputWalker) {
35
            $query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, $outputWalker);
36
        }
37
38
        return $query->getSql();
39
    }
40
41
    public function assertSqlGeneration($dqlToBeTested, $sqlToBeConfirmed, $treeWalkers = [], $outputWalker = null)
42
    {
43
        try {
44
            $this->assertEquals($sqlToBeConfirmed, $this->generateSql($dqlToBeTested, $treeWalkers, $outputWalker));
45
        } catch (\Exception $e) {
46
            $this->fail($e->getMessage() . ' at "' . $e->getFile() . '" on line ' . $e->getLine());
47
        }
48
    }
49
50
    public function testSupportsQueriesWithoutWhere()
51
    {
52
        $this->assertSqlGeneration(
53
            'select u from Doctrine\Tests\Models\CMS\CmsUser u',
54
            "SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.id = 1",
55
            [CustomTreeWalker::class]
56
        );
57
    }
58
59
    public function testSupportsQueriesWithMultipleConditionalExpressions()
60
    {
61
        $this->assertSqlGeneration(
62
            'select u from Doctrine\Tests\Models\CMS\CmsUser u where u.name = :name or u.name = :otherName',
63
            "SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE (c0_.name = ? OR c0_.name = ?) AND c0_.id = 1",
64
            [CustomTreeWalker::class]
65
        );
66
    }
67
68
    public function testSupportsQueriesWithSimpleConditionalExpression()
69
    {
70
        $this->assertSqlGeneration(
71
            'select u from Doctrine\Tests\Models\CMS\CmsUser u where u.name = :name',
72
            "SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.name = ? AND c0_.id = 1",
73
            [CustomTreeWalker::class]
74
        );
75
    }
76
77
    public function testSetUnknownQueryComponentThrowsException()
78
    {
79
        $this->expectException(QueryException::class);
80
        $this->expectExceptionMessage("Invalid query component given for DQL alias 'x', requires 'metadata', 'parent', 'relation', 'map', 'nestingLevel' and 'token' keys.");
81
82
        $this->generateSql(
83
            'select u from Doctrine\Tests\Models\CMS\CmsUser u',
84
            [],
85
            AddUnknownQueryComponentWalker::class
86
        );
87
    }
88
89
    public function testSupportsSeveralHintsQueries()
90
    {
91
        $this->assertSqlGeneration(
92
            'select u from Doctrine\Tests\Models\CMS\CmsUser u',
93
            "SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c1_.id AS id_4, c1_.country AS country_5, c1_.zip AS zip_6, c1_.city AS city_7, c0_.email_id AS email_id_8, c1_.user_id AS user_id_9 FROM cms_users c0_ LEFT JOIN cms_addresses c1_ ON c0_.id = c1_.user_id WHERE c0_.id = 1",
94
            [CustomTreeWalkerJoin::class, CustomTreeWalker::class]
95
        );
96
    }
97
}
98
99
class AddUnknownQueryComponentWalker extends Query\SqlWalker
100
{
101
    public function walkSelectStatement(Query\AST\SelectStatement $selectStatement)
102
    {
103
        parent::walkSelectStatement($selectStatement);
104
105
        $this->setQueryComponent('x', []);
106
    }
107
}
108
109
class CustomTreeWalker extends Query\TreeWalkerAdapter
110
{
111
    public function walkSelectStatement(Query\AST\SelectStatement $selectStatement)
112
    {
113
        // Get the DQL aliases of all the classes we want to modify
114
        $dqlAliases = [];
115
116
        foreach ($this->_getQueryComponents() as $dqlAlias => $comp) {
117
            // Hard-coded check just for demonstration: We want to modify the query if
118
            // it involves the CmsUser class.
119
            if ($comp['metadata']->name == CmsUser::class) {
120
                $dqlAliases[] = $dqlAlias;
121
            }
122
        }
123
124
        // Create our conditions for all involved classes
125
        $factors = [];
126
        foreach ($dqlAliases as $alias) {
127
            $pathExpr = new Query\AST\PathExpression(Query\AST\PathExpression::TYPE_STATE_FIELD, $alias, 'id');
128
            $pathExpr->type = Query\AST\PathExpression::TYPE_STATE_FIELD;
129
            $comparisonExpr = new Query\AST\ComparisonExpression($pathExpr, '=', 1);
130
131
            $condPrimary = new Query\AST\ConditionalPrimary;
132
            $condPrimary->simpleConditionalExpression = $comparisonExpr;
133
134
            $factor = new Query\AST\ConditionalFactor($condPrimary);
135
            $factors[] = $factor;
136
        }
137
138
        if (($whereClause = $selectStatement->whereClause) !== null) {
139
            // There is already a WHERE clause, so append the conditions
140
            $condExpr = $whereClause->conditionalExpression;
141
142
            // Since Phase 1 AST optimizations were included, we need to re-add the ConditionalExpression
143
            if ( ! ($condExpr instanceof Query\AST\ConditionalExpression)) {
144
                $condExpr = new Query\AST\ConditionalExpression([$condExpr]);
145
146
                $whereClause->conditionalExpression = $condExpr;
147
            }
148
149
            $existingTerms = $whereClause->conditionalExpression->conditionalTerms;
150
151
            if (count($existingTerms) > 1) {
152
                // More than one term, so we need to wrap all these terms in a single root term
153
                // i.e: "WHERE u.name = :foo or u.other = :bar" => "WHERE (u.name = :foo or u.other = :bar) AND <our condition>"
154
155
                $primary = new Query\AST\ConditionalPrimary;
156
                $primary->conditionalExpression = new Query\AST\ConditionalExpression($existingTerms);
157
                $existingFactor = new Query\AST\ConditionalFactor($primary);
158
                $term = new Query\AST\ConditionalTerm(array_merge([$existingFactor], $factors));
159
160
                $selectStatement->whereClause->conditionalExpression->conditionalTerms = [$term];
161
            } else {
162
                // Just one term so we can simply append our factors to that term
163
                $singleTerm = $selectStatement->whereClause->conditionalExpression->conditionalTerms[0];
164
165
                // Since Phase 1 AST optimizations were included, we need to re-add the ConditionalExpression
166
                if ( ! ($singleTerm instanceof Query\AST\ConditionalTerm)) {
167
                    $singleTerm = new Query\AST\ConditionalTerm([$singleTerm]);
168
169
                    $selectStatement->whereClause->conditionalExpression->conditionalTerms[0] = $singleTerm;
170
                }
171
172
                $singleTerm->conditionalFactors = array_merge($singleTerm->conditionalFactors, $factors);
173
                $selectStatement->whereClause->conditionalExpression->conditionalTerms = [$singleTerm];
174
            }
175
        } else {
176
            // Create a new WHERE clause with our factors
177
            $term = new Query\AST\ConditionalTerm($factors);
178
            $condExpr = new Query\AST\ConditionalExpression([$term]);
179
            $whereClause = new Query\AST\WhereClause($condExpr);
180
            $selectStatement->whereClause = $whereClause;
181
        }
182
    }
183
}
184
185 View Code Duplication
class CustomTreeWalkerJoin extends Query\TreeWalkerAdapter
0 ignored issues
show
This class seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
186
{
187
    public function walkSelectStatement(Query\AST\SelectStatement $selectStatement)
188
    {
189
        foreach ($selectStatement->fromClause->identificationVariableDeclarations as $identificationVariableDeclaration) {
190
            $rangeVariableDecl = $identificationVariableDeclaration->rangeVariableDeclaration;
191
192
            if ($rangeVariableDecl->abstractSchemaName !== CmsUser::class) {
193
                continue;
194
            }
195
196
            $this->modifySelectStatement($selectStatement, $identificationVariableDeclaration);
197
        }
198
    }
199
200
    private function modifySelectStatement(Query\AST\SelectStatement $selectStatement, $identificationVariableDecl)
201
    {
202
        $rangeVariableDecl       = $identificationVariableDecl->rangeVariableDeclaration;
203
        $joinAssocPathExpression = new Query\AST\JoinAssociationPathExpression($rangeVariableDecl->aliasIdentificationVariable, 'address');
204
        $joinAssocDeclaration    = new Query\AST\JoinAssociationDeclaration($joinAssocPathExpression, $rangeVariableDecl->aliasIdentificationVariable . 'a', null);
205
        $join                    = new Query\AST\Join(Query\AST\Join::JOIN_TYPE_LEFT, $joinAssocDeclaration);
206
        $selectExpression        = new Query\AST\SelectExpression($rangeVariableDecl->aliasIdentificationVariable . 'a', null, false);
207
208
        $identificationVariableDecl->joins[]                = $join;
209
        $selectStatement->selectClause->selectExpressions[] = $selectExpression;
210
211
        $entityManager   = $this->_getQuery()->getEntityManager();
212
        $userMetadata    = $entityManager->getClassMetadata(CmsUser::class);
213
        $addressMetadata = $entityManager->getClassMetadata(CmsAddress::class);
214
215
        $this->setQueryComponent($rangeVariableDecl->aliasIdentificationVariable . 'a',
216
            [
217
                'metadata'     => $addressMetadata,
218
                'parent'       => $rangeVariableDecl->aliasIdentificationVariable,
219
                'relation'     => $userMetadata->getAssociationMapping('address'),
220
                'map'          => null,
221
                'nestingLevel' => 0,
222
                'token'        => null,
223
            ]
224
        );
225
    }
226
}
227