Passed
Pull Request — 2.6 (#7821)
by Marco
09:17
created

convertWhereInIdentifiersToDatabaseValue()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 18
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 11
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 18
rs 9.9
ccs 11
cts 11
cp 1
crap 1
1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\ORM\Tools\Pagination;
21
22
use Doctrine\ORM\Mapping\ClassMetadataInfo;
23
use Doctrine\ORM\Query\AST\ArithmeticExpression;
24
use Doctrine\ORM\Query\AST\SimpleArithmeticExpression;
25
use Doctrine\ORM\Query\TreeWalkerAdapter;
26
use Doctrine\ORM\Query\AST\SelectStatement;
27
use Doctrine\ORM\Query\AST\PathExpression;
28
use Doctrine\ORM\Query\AST\InExpression;
29
use Doctrine\ORM\Query\AST\NullComparisonExpression;
30
use Doctrine\ORM\Query\AST\InputParameter;
31
use Doctrine\ORM\Query\AST\ConditionalPrimary;
32
use Doctrine\ORM\Query\AST\ConditionalTerm;
33
use Doctrine\ORM\Query\AST\ConditionalExpression;
34
use Doctrine\ORM\Query\AST\ConditionalFactor;
35
use Doctrine\ORM\Query\AST\WhereClause;
36
use function array_map;
37
use function assert;
38
use function is_array;
39
40
/**
41
 * Replaces the whereClause of the AST with a WHERE id IN (:foo_1, :foo_2) equivalent.
42
 *
43
 * @category    DoctrineExtensions
44
 * @package     DoctrineExtensions\Paginate
45
 * @author      David Abdemoulaie <[email protected]>
46
 * @copyright   Copyright (c) 2010 David Abdemoulaie (http://hobodave.com/)
47
 * @license     http://hobodave.com/license.txt New BSD License
48
 */
49
class WhereInWalker extends TreeWalkerAdapter
50
{
51
    /**
52
     * ID Count hint name.
53
     */
54
    const HINT_PAGINATOR_ID_COUNT = 'doctrine.id.count';
55
56
    /**
57
     * Primary key alias for query.
58
     */
59
    const PAGINATOR_ID_ALIAS = 'dpid';
60
61
    /**
62
     * Replaces the whereClause in the AST.
63
     *
64
     * Generates a clause equivalent to WHERE IN (:dpid_1, :dpid_2, ...)
65
     *
66
     * The parameter namespace (dpid) is defined by
67
     * the PAGINATOR_ID_ALIAS
68
     *
69
     * The total number of parameters is retrieved from
70
     * the HINT_PAGINATOR_ID_COUNT query hint.
71
     *
72
     * @param SelectStatement $AST
73
     *
74
     * @return void
75
     *
76
     * @throws \RuntimeException
77
     */
78 63
    public function walkSelectStatement(SelectStatement $AST)
79
    {
80 63
        $queryComponents = $this->_getQueryComponents();
81
        // Get the root entity and alias from the AST fromClause
82 63
        $from = $AST->fromClause->identificationVariableDeclarations;
83
84 63
        if (count($from) > 1) {
85
            throw new \RuntimeException("Cannot count query which selects two FROM components, cannot make distinction");
86
        }
87
88 63
        $fromRoot            = reset($from);
89 63
        $rootAlias           = $fromRoot->rangeVariableDeclaration->aliasIdentificationVariable;
90
        /** @var \Doctrine\ORM\Mapping\ClassMetadataInfo $rootClass */
91 63
        $rootClass           = $queryComponents[$rootAlias]['metadata'];
92 63
        $identifierFieldName = $rootClass->getSingleIdentifierFieldName();
93
94 63
        $pathType = PathExpression::TYPE_STATE_FIELD;
95 63
        if (isset($rootClass->associationMappings[$identifierFieldName])) {
96 2
            $pathType = PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION;
97
        }
98
99 63
        $pathExpression       = new PathExpression(PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, $rootAlias, $identifierFieldName);
100 63
        $pathExpression->type = $pathType;
101
102 63
        $count = $this->_getQuery()->getHint(self::HINT_PAGINATOR_ID_COUNT);
103
104 63
        if ($count > 0) {
105 63
            $arithmeticExpression = new ArithmeticExpression();
106 63
            $arithmeticExpression->simpleArithmeticExpression = new SimpleArithmeticExpression(
107 63
                [$pathExpression]
108
            );
109 63
            $expression = new InExpression($arithmeticExpression);
110 63
            $expression->literals[] = new InputParameter(":" . self::PAGINATOR_ID_ALIAS);
111
112 63
            $this->convertWhereInIdentifiersToDatabaseValue($this->getTypeOfSingleIdentifierColumn($rootClass));
113
        } else {
114
            $expression = new NullComparisonExpression($pathExpression);
115
            $expression->not = false;
116
        }
117
118 63
        $conditionalPrimary = new ConditionalPrimary;
119 63
        $conditionalPrimary->simpleConditionalExpression = $expression;
120 63
        if ($AST->whereClause) {
121 10
            if ($AST->whereClause->conditionalExpression instanceof ConditionalTerm) {
0 ignored issues
show
introduced by
$AST->whereClause->conditionalExpression is never a sub-type of Doctrine\ORM\Query\AST\ConditionalTerm.
Loading history...
122 2
                $AST->whereClause->conditionalExpression->conditionalFactors[] = $conditionalPrimary;
123 8
            } elseif ($AST->whereClause->conditionalExpression instanceof ConditionalPrimary) {
0 ignored issues
show
introduced by
$AST->whereClause->conditionalExpression is never a sub-type of Doctrine\ORM\Query\AST\ConditionalPrimary.
Loading history...
124 5
                $AST->whereClause->conditionalExpression = new ConditionalExpression(
125
                    [
126 5
                        new ConditionalTerm(
127
                            [
128 5
                                $AST->whereClause->conditionalExpression,
129 5
                                $conditionalPrimary
130
                            ]
131
                        )
132
                    ]
133
                );
134 3
            } elseif ($AST->whereClause->conditionalExpression instanceof ConditionalExpression
0 ignored issues
show
introduced by
$AST->whereClause->conditionalExpression is always a sub-type of Doctrine\ORM\Query\AST\ConditionalExpression.
Loading history...
135 3
                || $AST->whereClause->conditionalExpression instanceof ConditionalFactor
136
            ) {
137 3
                $tmpPrimary = new ConditionalPrimary;
138 3
                $tmpPrimary->conditionalExpression = $AST->whereClause->conditionalExpression;
139 3
                $AST->whereClause->conditionalExpression = new ConditionalTerm(
0 ignored issues
show
Documentation Bug introduced by
It seems like new Doctrine\ORM\Query\A..., $conditionalPrimary)) of type Doctrine\ORM\Query\AST\ConditionalTerm is incompatible with the declared type Doctrine\ORM\Query\AST\ConditionalExpression of property $conditionalExpression.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
140
                    [
141 3
                        $tmpPrimary,
142 10
                        $conditionalPrimary
143
                    ]
144
                );
145
            }
146
        } else {
147 53
            $AST->whereClause = new WhereClause(
148 53
                new ConditionalExpression(
149
                    [
150 53
                        new ConditionalTerm([$conditionalPrimary])
151
                    ]
152
                )
153
            );
154
        }
155 63
    }
156
157 63
    private function convertWhereInIdentifiersToDatabaseValue(string $type) : void
158
    {
159 63
        $query                = $this->_getQuery();
160 63
        $identifiersParameter = $query->getParameter(self::PAGINATOR_ID_ALIAS);
161
162 63
        assert($identifiersParameter !== null);
163
164 63
        $identifiers = $identifiersParameter->getValue();
165
166 63
        assert(is_array($identifiers));
167
168 63
        $connection = $this->_getQuery()
169 63
            ->getEntityManager()
170 63
            ->getConnection();
171
172
        $query->setParameter(self::PAGINATOR_ID_ALIAS, array_map(static function ($id) use ($connection, $type) {
173 63
            return $connection->convertToDatabaseValue($id, $type);
174 63
        }, $identifiers));
175 63
    }
176
177 63
    private function getTypeOfSingleIdentifierColumn(ClassMetadataInfo $class) : string
178
    {
179 63
        $identifierField = $class->getSingleIdentifierFieldName();
180
181 63
        if ($class->hasField($identifierField)) {
182 63
            return (string) $class->getTypeOfField($identifierField);
183
        }
184
185 2
        return $this->getTypeOfSingleIdentifierColumn(
186 2
            $this->_getQuery()
187 2
                ->getEntityManager()
188 2
                ->getClassMetadata($class->getAssociationTargetClass($identifierField))
189
        );
190
    }
191
}
192