Failed Conditions
Pull Request — 2.7 (#8027)
by
unknown
07:00
created

MultiTableUpdateExecutor::__construct()   C

Complexity

Conditions 12
Paths 52

Size

Total Lines 88
Code Lines 50

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 156

Importance

Changes 0
Metric Value
eloc 50
c 0
b 0
f 0
dl 0
loc 88
ccs 0
cts 50
cp 0
rs 6.9666
cc 12
nc 52
nop 2
crap 156

How to fix   Long Method    Complexity   

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\Query\Exec;
21
22
use Doctrine\DBAL\Connection;
23
use Doctrine\DBAL\Types\Type;
24
use Doctrine\ORM\Query\ParameterTypeInferer;
25
use Doctrine\ORM\Query\AST;
26
use Doctrine\ORM\Utility\PersisterHelper;
27
use Throwable;
28
29
/**
30
 * Executes the SQL statements for bulk DQL UPDATE statements on classes in
31
 * Class Table Inheritance (JOINED).
32
 *
33
 * @author Roman Borschel <[email protected]>
34
 * @since 2.0
35
 */
36
class MultiTableUpdateExecutor extends AbstractSqlExecutor
37
{
38
    /**
39
     * @var string
40
     */
41
    private $_createTempTableSql;
42
43
    /**
44
     * @var string
45
     */
46
    private $_dropTempTableSql;
47
48
    /**
49
     * @var string
50
     */
51
    private $_insertSql;
52
53
    /**
54
     * @var array
55
     */
56
    private $_sqlParameters = [];
57
58
    /**
59
     * @var int
60
     */
61
    private $_numParametersInUpdateClause = 0;
62
63
    /**
64
     * Initializes a new <tt>MultiTableUpdateExecutor</tt>.
65
     *
66
     * Internal note: Any SQL construction and preparation takes place in the constructor for
67
     *                best performance. With a query cache the executor will be cached.
68
     *
69
     * @param \Doctrine\ORM\Query\AST\Node  $AST The root AST node of the DQL query.
70
     * @param \Doctrine\ORM\Query\SqlWalker $sqlWalker The walker used for SQL generation from the AST.
71
     */
72
    public function __construct(AST\Node $AST, $sqlWalker)
73
    {
74
        $em             = $sqlWalker->getEntityManager();
75
        $conn           = $em->getConnection();
76
        $platform       = $conn->getDatabasePlatform();
77
        $quoteStrategy  = $em->getConfiguration()->getQuoteStrategy();
78
79
        $updateClause   = $AST->updateClause;
80
        $primaryClass   = $sqlWalker->getEntityManager()->getClassMetadata($updateClause->abstractSchemaName);
81
        $rootClass      = $em->getClassMetadata($primaryClass->rootEntityName);
82
83
        $updateItems    = $updateClause->updateItems;
84
85
        $tempTable      = $platform->getTemporaryTableName($rootClass->getTemporaryIdTableName());
86
        $idColumnNames  = $rootClass->getIdentifierColumnNames();
87
        $idColumnList   = implode(', ', $idColumnNames);
88
89
        // 1. Create an INSERT INTO temptable ... SELECT identifiers WHERE $AST->getWhereClause()
90
        $sqlWalker->setSQLTableAlias($primaryClass->getTableName(), 't0', $updateClause->aliasIdentificationVariable);
91
92
        $this->_insertSql = 'INSERT INTO ' . $tempTable . ' (' . $idColumnList . ')'
93
                . ' SELECT t0.' . implode(', t0.', $idColumnNames);
94
95
        $rangeDecl = new AST\RangeVariableDeclaration($primaryClass->name, $updateClause->aliasIdentificationVariable);
96
        $fromClause = new AST\FromClause([new AST\IdentificationVariableDeclaration($rangeDecl, null, [])]);
97
98
        $this->_insertSql .= $sqlWalker->walkFromClause($fromClause);
99
100
        // 2. Create ID subselect statement used in UPDATE ... WHERE ... IN (subselect)
101
        $idSubselect = 'SELECT ' . $idColumnList . ' FROM ' . $tempTable;
102
103
        // 3. Create and store UPDATE statements
104
        $classNames = array_merge($primaryClass->parentClasses, [$primaryClass->name], $primaryClass->subClasses);
105
        $i = -1;
106
107
        foreach (array_reverse($classNames) as $className) {
108
            $affected = false;
109
            $class = $em->getClassMetadata($className);
110
            $updateSql = 'UPDATE ' . $quoteStrategy->getTableName($class, $platform) . ' SET ';
111
112
            foreach ($updateItems as $updateItem) {
113
                $field = $updateItem->pathExpression->field;
114
115
                if ((isset($class->fieldMappings[$field]) && ! isset($class->fieldMappings[$field]['inherited'])) ||
116
                    (isset($class->associationMappings[$field]) && ! isset($class->associationMappings[$field]['inherited']))) {
117
                    $newValue = $updateItem->newValue;
118
119
                    if ( ! $affected) {
120
                        $affected = true;
121
                        ++$i;
122
                    } else {
123
                        $updateSql .= ', ';
124
                    }
125
126
                    $updateSql .= $sqlWalker->walkUpdateItem($updateItem);
127
128
                    if ($newValue instanceof AST\InputParameter) {
129
                        $this->_sqlParameters[$i][] = $newValue->name;
130
131
                        ++$this->_numParametersInUpdateClause;
132
                    }
133
                }
134
            }
135
136
            if ($affected) {
137
                $this->_sqlStatements[$i] = $updateSql . ' WHERE (' . $idColumnList . ') IN (' . $idSubselect . ')';
138
            }
139
        }
140
141
        // Append WHERE clause to insertSql, if there is one.
142
        if ($AST->whereClause) {
143
            $this->_insertSql .= $sqlWalker->walkWhereClause($AST->whereClause);
144
        }
145
146
        // 4. Store DDL for temporary identifier table.
147
        $columnDefinitions = [];
148
149
        foreach ($idColumnNames as $idColumnName) {
150
            $columnDefinitions[$idColumnName] = [
151
                'notnull' => true,
152
                'type'    => Type::getType(PersisterHelper::getTypeOfColumn($idColumnName, $rootClass, $em)),
153
            ];
154
        }
155
156
        $this->_createTempTableSql = $platform->getCreateTemporaryTableSnippetSQL() . ' ' . $tempTable . ' ('
157
                . $platform->getColumnDeclarationListSQL($columnDefinitions) . ')';
158
159
        $this->_dropTempTableSql = $platform->getDropTemporaryTableSQL($tempTable);
160
    }
161
162
    /**
163
     * {@inheritDoc}
164
     */
165
    public function execute(Connection $conn, array $params, array $types)
166
    {
167
        // Create temporary id table
168
        $conn->executeUpdate($this->_createTempTableSql);
169
170
        try {
171
            // Insert identifiers. Parameters from the update clause are cut off.
172
            $numUpdated = $conn->executeUpdate(
173
                $this->_insertSql,
174
                array_slice($params, $this->_numParametersInUpdateClause),
175
                array_slice($types, $this->_numParametersInUpdateClause)
176
            );
177
178
            // Execute UPDATE statements
179
            foreach ($this->_sqlStatements as $key => $statement) {
180
                $paramValues = [];
181
                $paramTypes  = [];
182
183
                if (isset($this->_sqlParameters[$key])) {
184
                    foreach ($this->_sqlParameters[$key] as $parameterKey => $parameterName) {
185
                        $paramValues[] = $params[$parameterKey];
186
                        $paramTypes[]  = $types[$parameterKey] ?? ParameterTypeInferer::inferType($params[$parameterKey]);
187
                    }
188
                }
189
190
                $conn->executeUpdate($statement, $paramValues, $paramTypes);
191
            }
192
        } catch (Throwable $exception) {
193
            // FAILURE! Drop temporary table to avoid possible collisions
194
            $conn->executeUpdate($this->_dropTempTableSql);
195
196
            // Re-throw exception
197
            throw $exception;
198
        }
199
200
        // Drop temporary table
201
        $conn->executeUpdate($this->_dropTempTableSql);
202
203
        return $numUpdated;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $numUpdated returns the type integer which is incompatible with the return type mandated by Doctrine\ORM\Query\Exec\...tSqlExecutor::execute() of Doctrine\DBAL\Driver\Statement.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
204
    }
205
}
206