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

WhereInWalker::walkSelectStatement()   B

Complexity

Conditions 9
Paths 21

Size

Total Lines 73
Code Lines 45

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 41
CRAP Score 9.0256

Importance

Changes 0
Metric Value
cc 9
eloc 45
c 0
b 0
f 0
nc 21
nop 1
dl 0
loc 73
rs 7.6444
ccs 41
cts 44
cp 0.9318
crap 9.0256

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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