Completed
Pull Request — master (#7825)
by
unknown
09:43
created

JoinedSubclassPersister::getSelectColumnsSQL()   D

Complexity

Conditions 19
Paths 65

Size

Total Lines 93
Code Lines 48

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 46
CRAP Score 19.0261

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 19
eloc 48
c 1
b 0
f 0
nc 65
nop 0
dl 0
loc 93
ccs 46
cts 48
cp 0.9583
crap 19.0261
rs 4.5166

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
declare(strict_types=1);
4
5
namespace Doctrine\ORM\Persisters\Entity;
6
7
use Doctrine\Common\Collections\Criteria;
8
use Doctrine\DBAL\LockMode;
9
use Doctrine\DBAL\Statement;
10
use Doctrine\DBAL\Types\Type;
11
use Doctrine\ORM\Mapping\AssociationMetadata;
12
use Doctrine\ORM\Mapping\FieldMetadata;
13
use Doctrine\ORM\Mapping\GeneratorType;
14
use Doctrine\ORM\Mapping\JoinColumnMetadata;
15
use Doctrine\ORM\Mapping\ManyToManyAssociationMetadata;
16
use Doctrine\ORM\Mapping\ToManyAssociationMetadata;
17
use Doctrine\ORM\Mapping\ToOneAssociationMetadata;
18
use Doctrine\ORM\Utility\PersisterHelper;
19
use function array_combine;
20
use function array_keys;
21
use function implode;
22
use function is_array;
23
24
/**
25
 * The joined subclass persister maps a single entity instance to several tables in the
26
 * database as it is defined by the <tt>Class Table Inheritance</tt> strategy.
27
 *
28
 * @see https://martinfowler.com/eaaCatalog/classTableInheritance.html
29
 */
30
class JoinedSubclassPersister extends AbstractEntityInheritancePersister
31
{
32
    /**
33
     * {@inheritdoc}
34
     */
35 295
    public function insert($entity)
36
    {
37 295
        $rootClass      = ! $this->class->isRootEntity()
38 289
            ? $this->em->getClassMetadata($this->class->getRootClassName())
39 295
            : $this->class;
40 295
        $generationPlan = $this->class->getValueGenerationPlan();
41
42
        // Prepare statement for the root table
43 295
        $rootPersister = $this->em->getUnitOfWork()->getEntityPersister($rootClass->getClassName());
0 ignored issues
show
Bug introduced by
The method getClassName() does not exist on Doctrine\Common\Persistence\Mapping\ClassMetadata. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

43
        $rootPersister = $this->em->getUnitOfWork()->getEntityPersister($rootClass->/** @scrutinizer ignore-call */ getClassName());

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
44 295
        $rootTableName = $rootClass->getTableName();
0 ignored issues
show
Bug introduced by
The method getTableName() does not exist on Doctrine\Common\Persistence\Mapping\ClassMetadata. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

44
        /** @scrutinizer ignore-call */ 
45
        $rootTableName = $rootClass->getTableName();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
45 295
        $rootTableStmt = $this->conn->prepare($rootPersister->getInsertSQL());
0 ignored issues
show
Bug introduced by
The method getInsertSQL() does not exist on Doctrine\ORM\Persisters\Entity\EntityPersister. It seems like you code against a sub-type of said class. However, the method does not exist in Doctrine\ORM\Cache\Persi...y\CachedEntityPersister. Are you sure you never get one of those? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

45
        $rootTableStmt = $this->conn->prepare($rootPersister->/** @scrutinizer ignore-call */ getInsertSQL());
Loading history...
46
47
        // Prepare statements for sub tables.
48 295
        $subTableStmts = [];
49
50 295
        if ($rootClass !== $this->class) {
51 289
            $subTableStmts[$this->class->getTableName()] = $this->conn->prepare($this->getInsertSQL());
52
        }
53
54 295
        $parentClass = $this->class;
55
56 295
        while (($parentClass = $parentClass->getParent()) !== null) {
57 289
            if (! $parentClass->isMappedSuperclass) {
58 289
                $parentTableName = $parentClass->getTableName();
0 ignored issues
show
Bug introduced by
The method getTableName() does not exist on Doctrine\ORM\Mapping\ComponentMetadata. It seems like you code against a sub-type of Doctrine\ORM\Mapping\ComponentMetadata such as Doctrine\ORM\Mapping\ClassMetadata. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

58
                /** @scrutinizer ignore-call */ 
59
                $parentTableName = $parentClass->getTableName();
Loading history...
59
60 289
                if ($parentClass !== $rootClass) {
61 170
                    $parentPersister = $this->em->getUnitOfWork()->getEntityPersister($parentClass->getClassName());
62
63 170
                    $subTableStmts[$parentTableName] = $this->conn->prepare($parentPersister->getInsertSQL());
64
                }
65
            }
66
        }
67
68
        // Execute all inserts. For each entity:
69
        // 1) Insert on root table
70
        // 2) Insert on sub tables
71 295
        $insertData = $this->prepareInsertData($entity);
72
73
        // Execute insert on root table
74 295
        $paramIndex = 1;
75
76 295
        foreach ($insertData[$rootTableName] as $columnName => $value) {
77 295
            $type = $this->columns[$columnName]->getType();
78
79 295
            $rootTableStmt->bindValue($paramIndex++, $value, $type);
80
        }
81
82 295
        $rootTableStmt->execute();
83
84 295
        if ($generationPlan->containsDeferred()) {
85 291
            $generationPlan->executeDeferred($this->em, $entity);
86 291
            $id = $this->getIdentifier($entity);
87
        } else {
88 4
            $id = $this->em->getUnitOfWork()->getEntityIdentifier($entity);
89
        }
90
91 295
        if ($this->class->isVersioned()) {
92 9
            $this->assignDefaultVersionValue($entity, $id);
93
        }
94
95
        // Execute inserts on subtables.
96
        // The order doesn't matter because all child tables link to the root table via FK.
97 295
        foreach ($subTableStmts as $tableName => $stmt) {
98
            /** @var Statement $stmt */
99 289
            $paramIndex = 1;
100 289
            $data       = $insertData[$tableName] ?? [];
101
102 289
            foreach ((array) $id as $idName => $idVal) {
103 289
                $type = Type::getType('string');
104
105 289
                if (isset($this->columns[$idName])) {
106 289
                    $type = $this->columns[$idName]->getType();
107
                }
108
109 289
                $stmt->bindValue($paramIndex++, $idVal, $type);
110
            }
111
112 289
            foreach ($data as $columnName => $value) {
113 224
                if (! is_array($id) || ! isset($id[$columnName])) {
114 224
                    $type = $this->columns[$columnName]->getType();
115
116 224
                    $stmt->bindValue($paramIndex++, $value, $type);
117
                }
118
            }
119
120 289
            $stmt->execute();
121
        }
122
123 295
        $rootTableStmt->closeCursor();
124
125 295
        foreach ($subTableStmts as $stmt) {
126 289
            $stmt->closeCursor();
127
        }
128 295
    }
129
130
    /**
131
     * {@inheritdoc}
132
     */
133 30
    public function update($entity)
134
    {
135 30
        $updateData = $this->prepareUpdateData($entity);
136
137 30
        if (! $updateData) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $updateData of type array<mixed,mixed> is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
138
            return;
139
        }
140
141 30
        $isVersioned = $this->class->isVersioned();
142
143 30
        foreach ($updateData as $tableName => $data) {
144 30
            $versioned = $isVersioned && $this->class->versionProperty->getTableName() === $tableName;
145
146 30
            $this->updateTable($entity, $this->platform->quoteIdentifier($tableName), $data, $versioned);
147
        }
148
149
        // Make sure the table with the version column is updated even if no columns on that
150
        // table were affected.
151 29
        if ($isVersioned) {
152 5
            $versionedClass = $this->class->versionProperty->getDeclaringClass();
0 ignored issues
show
Bug introduced by
The method getDeclaringClass() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

152
            /** @scrutinizer ignore-call */ 
153
            $versionedClass = $this->class->versionProperty->getDeclaringClass();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
153 5
            $versionedTable = $versionedClass->getTableName();
154
155 5
            if (! isset($updateData[$versionedTable])) {
156 2
                $tableName = $versionedClass->table->getQuotedQualifiedName($this->platform);
157
158 2
                $this->updateTable($entity, $tableName, [], true);
159
            }
160
161 4
            $identifiers = $this->em->getUnitOfWork()->getEntityIdentifier($entity);
162
163 4
            $this->assignDefaultVersionValue($entity, $identifiers);
164
        }
165 28
    }
166
167
    /**
168
     * {@inheritdoc}
169
     */
170 4
    public function delete($entity)
171
    {
172 4
        $identifier = $this->em->getUnitOfWork()->getEntityIdentifier($entity);
173 4
        $id         = array_combine(array_keys($this->class->getIdentifierColumns($this->em)), $identifier);
174
175 4
        $this->deleteJoinTableRecords($identifier);
176
177
        // If the database platform supports FKs, just
178
        // delete the row from the root table. Cascades do the rest.
179 4
        if ($this->platform->supportsForeignKeyConstraints()) {
180
            $rootClass = $this->em->getClassMetadata($this->class->getRootClassName());
181
            $rootTable = $rootClass->table->getQuotedQualifiedName($this->platform);
0 ignored issues
show
Bug introduced by
Accessing table on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
182
183
            return (bool) $this->conn->delete($rootTable, $id);
0 ignored issues
show
Bug introduced by
It seems like $id can also be of type false; however, parameter $identifier of Doctrine\DBAL\Connection::delete() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

183
            return (bool) $this->conn->delete($rootTable, /** @scrutinizer ignore-type */ $id);
Loading history...
184
        }
185
186
        // Delete from all tables individually, starting from this class' table up to the root table.
187 4
        $rootTable    = $this->class->table->getQuotedQualifiedName($this->platform);
188 4
        $affectedRows = $this->conn->delete($rootTable, $id);
189 4
        $parentClass  = $this->class;
190
191 4
        while (($parentClass = $parentClass->getParent()) !== null) {
192 3
            $parentTable = $parentClass->table->getQuotedQualifiedName($this->platform);
193
194 3
            $this->conn->delete($parentTable, $id);
195
        }
196
197 4
        return (bool) $affectedRows;
198
    }
199
200
    /**
201
     * {@inheritdoc}
202
     */
203 68
    public function getSelectSQL(
204
        $criteria,
205
        ?AssociationMetadata $association = null,
206
        $lockMode = null,
207
        $limit = null,
208
        $offset = null,
209
        array $orderBy = []
210
    ) {
211 68
        $this->switchPersisterContext($offset, $limit);
212
213 68
        $baseTableAlias = $this->getSQLTableAlias($this->class->getTableName());
214 68
        $joinSql        = $this->getJoinSql($baseTableAlias);
215
216 68
        if ($association instanceof ManyToManyAssociationMetadata) {
217 2
            $joinSql .= $this->getSelectManyToManyJoinSQL($association);
218
        }
219
220 68
        if ($association instanceof ToManyAssociationMetadata && $association->getOrderBy()) {
221 1
            $orderBy = $association->getOrderBy();
222
        }
223
224 68
        $orderBySql   = $this->getOrderBySQL($orderBy, $baseTableAlias);
225 68
        $conditionSql = $criteria instanceof Criteria
226
            ? $this->getSelectConditionCriteriaSQL($criteria)
227 68
            : $this->getSelectConditionSQL($criteria, $association);
228
229
        // If the current class in the root entity, add the filters
230 68
        $rootClass  = $this->em->getClassMetadata($this->class->getRootClassName());
231 68
        $tableAlias = $this->getSQLTableAlias($rootClass->getTableName());
232 68
        $filterSql  = $this->generateFilterConditionSQL($rootClass, $tableAlias);
233
234 68
        if ($filterSql) {
235 4
            $conditionSql .= $conditionSql
236 2
                ? ' AND ' . $filterSql
237 4
                : $filterSql;
238
        }
239
240 68
        $lockSql = '';
241
242
        switch ($lockMode) {
243 68
            case LockMode::PESSIMISTIC_READ:
244
                $lockSql = ' ' . $this->platform->getReadLockSQL();
245
                break;
246
247 68
            case LockMode::PESSIMISTIC_WRITE:
248
                $lockSql = ' ' . $this->platform->getWriteLockSQL();
249
                break;
250
        }
251
252 68
        $tableName  = $this->class->table->getQuotedQualifiedName($this->platform);
253 68
        $from       = ' FROM ' . $tableName . ' ' . $baseTableAlias;
254 68
        $where      = $conditionSql !== '' ? ' WHERE ' . $conditionSql : '';
255 68
        $lock       = $this->platform->appendLockHint($from, $lockMode);
256 68
        $columnList = $this->getSelectColumnsSQL();
257 68
        $query      = 'SELECT ' . $columnList
258 68
                    . $lock
259 68
                    . $joinSql
260 68
                    . $where
261 68
                    . $orderBySql;
262
263 68
        return $this->platform->modifyLimitQuery($query, $limit, $offset ?? 0) . $lockSql;
264
    }
265
266
    /**
267
     * {@inheritDoc}
268
     */
269 6
    public function getCountSQL($criteria = [])
270
    {
271 6
        $tableName      = $this->class->table->getQuotedQualifiedName($this->platform);
272 6
        $baseTableAlias = $this->getSQLTableAlias($this->class->getTableName());
273 6
        $joinSql        = $this->getJoinSql($baseTableAlias);
274
275 6
        $conditionSql = $criteria instanceof Criteria
276 1
            ? $this->getSelectConditionCriteriaSQL($criteria)
277 6
            : $this->getSelectConditionSQL($criteria);
278
279 6
        $rootClass  = $this->em->getClassMetadata($this->class->getRootClassName());
280 6
        $tableAlias = $this->getSQLTableAlias($rootClass->getTableName());
281 6
        $filterSql  = $this->generateFilterConditionSQL($rootClass, $tableAlias);
282
283 6
        if ($filterSql !== '') {
284 1
            $conditionSql = $conditionSql
285 1
                ? $conditionSql . ' AND ' . $filterSql
286 1
                : $filterSql;
287
        }
288
289
        return 'SELECT COUNT(*) '
290 6
            . 'FROM ' . $tableName . ' ' . $baseTableAlias
291 6
            . $joinSql
292 6
            . (empty($conditionSql) ? '' : ' WHERE ' . $conditionSql);
293
    }
294
295
    /**
296
     * {@inheritdoc}
297
     */
298 6
    protected function getLockTablesSql($lockMode)
299
    {
300 6
        $joinSql           = '';
301 6
        $identifierColumns = $this->class->getIdentifierColumns($this->em);
302 6
        $baseTableAlias    = $this->getSQLTableAlias($this->class->getTableName());
303
304
        // INNER JOIN parent tables
305 6
        $parentClass = $this->class;
306
307 6
        while (($parentClass = $parentClass->getParent()) !== null) {
308 5
            $conditions = [];
309 5
            $tableName  = $parentClass->table->getQuotedQualifiedName($this->platform);
310 5
            $tableAlias = $this->getSQLTableAlias($parentClass->getTableName());
311 5
            $joinSql   .= ' INNER JOIN ' . $tableName . ' ' . $tableAlias . ' ON ';
312
313 5
            foreach ($identifierColumns as $idColumn) {
314 5
                $quotedColumnName = $this->platform->quoteIdentifier($idColumn->getColumnName());
315
316 5
                $conditions[] = $baseTableAlias . '.' . $quotedColumnName . ' = ' . $tableAlias . '.' . $quotedColumnName;
317
            }
318
319 5
            $joinSql .= implode(' AND ', $conditions);
320
        }
321
322 6
        return parent::getLockTablesSql($lockMode) . $joinSql;
323
    }
324
325
    /**
326
     * Ensure this method is never called. This persister overrides getSelectEntitiesSQL directly.
327
     *
328
     * @return string
329
     */
330 68
    protected function getSelectColumnsSQL()
331
    {
332
        // Create the column list fragment only once
333 68
        if ($this->currentPersisterContext->selectColumnListSql !== null) {
334 12
            return $this->currentPersisterContext->selectColumnListSql;
335
        }
336
337 68
        $this->currentPersisterContext->rsm->addEntityResult($this->class->getClassName(), 'r');
338
339 68
        $columnList = [];
340
341
        // Add columns
342 68
        foreach ($this->class->getPropertiesIterator() as $fieldName => $property) {
343 68
            if ($property instanceof FieldMetadata) {
344 68
                $tableClass = $parentClass = $this->class;
345 68
                while ($parentClass !== $property->getDeclaringClass() && ($parentClass = $parentClass->getParent()) !== null) {
346 45
                    if (! $parentClass->isMappedSuperclass) {
347 45
                        $tableClass = $parentClass;
348
                    }
349
                }
350 68
                $columnList[] = $this->getSelectColumnSQL($fieldName, $tableClass);
351
352 68
                continue;
353
            }
354
355 56
            if (! ($property instanceof ToOneAssociationMetadata) || ! $property->isOwningSide()) {
356 44
                continue;
357
            }
358
359 55
            $targetClass = $this->em->getClassMetadata($property->getTargetEntity());
360
361 55
            foreach ($property->getJoinColumns() as $joinColumn) {
362
                /** @var JoinColumnMetadata $joinColumn */
363 55
                $referencedColumnName = $joinColumn->getReferencedColumnName();
364
365 55
                if (! $joinColumn->getType()) {
366
                    $joinColumn->setType(PersisterHelper::getTypeOfColumn($referencedColumnName, $targetClass, $this->em));
367
                }
368
369 55
                $columnList[] = $this->getSelectJoinColumnSQL($joinColumn);
370
            }
371
        }
372
373
        // Add discriminator column (DO NOT ALIAS, see AbstractEntityInheritancePersister#processSQLResult).
374 68
        $discrColumn      = $this->class->discriminatorColumn;
375 68
        $discrTableAlias  = $this->getSQLTableAlias($discrColumn->getTableName());
376 68
        $discrColumnName  = $discrColumn->getColumnName();
377 68
        $discrColumnType  = $discrColumn->getType();
378 68
        $resultColumnName = $this->platform->getSQLResultCasing($discrColumnName);
379 68
        $quotedColumnName = $this->platform->quoteIdentifier($discrColumn->getColumnName());
380
381 68
        $this->currentPersisterContext->rsm->setDiscriminatorColumn('r', $resultColumnName);
382 68
        $this->currentPersisterContext->rsm->addMetaResult('r', $resultColumnName, $discrColumnName, false, $discrColumnType);
0 ignored issues
show
Bug introduced by
It seems like $discrColumnType can also be of type null; however, parameter $type of Doctrine\ORM\Query\Resul...apping::addMetaResult() does only seem to accept Doctrine\DBAL\Types\Type, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

382
        $this->currentPersisterContext->rsm->addMetaResult('r', $resultColumnName, $discrColumnName, false, /** @scrutinizer ignore-type */ $discrColumnType);
Loading history...
383
384 68
        $columnList[] = $discrColumnType->convertToDatabaseValueSQL($discrTableAlias . '.' . $quotedColumnName, $this->platform);
385
386
        // sub tables
387 68
        foreach ($this->class->getSubClasses() as $subClassName) {
388 50
            $subClass = $this->em->getClassMetadata($subClassName);
389
390
            // Add columns
391 50
            foreach ($subClass->getPropertiesIterator() as $fieldName => $property) {
0 ignored issues
show
Bug introduced by
The method getPropertiesIterator() does not exist on Doctrine\Common\Persistence\Mapping\ClassMetadata. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

391
            foreach ($subClass->/** @scrutinizer ignore-call */ getPropertiesIterator() as $fieldName => $property) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
392 50
                if ($subClass->isInheritedProperty($fieldName)) {
0 ignored issues
show
Bug introduced by
The method isInheritedProperty() does not exist on Doctrine\Common\Persistence\Mapping\ClassMetadata. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

392
                if ($subClass->/** @scrutinizer ignore-call */ isInheritedProperty($fieldName)) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
393 50
                    continue;
394
                }
395
396
                switch (true) {
397 46
                    case $property instanceof FieldMetadata:
398 46
                        $columnList[] = $this->getSelectColumnSQL($fieldName, $subClass);
399 46
                        break;
400
401 32
                    case $property instanceof ToOneAssociationMetadata && $property->isOwningSide():
402 32
                        $targetClass = $this->em->getClassMetadata($property->getTargetEntity());
403
404 32
                        foreach ($property->getJoinColumns() as $joinColumn) {
405
                            /** @var JoinColumnMetadata $joinColumn */
406 32
                            $referencedColumnName = $joinColumn->getReferencedColumnName();
407
408 32
                            if (! $joinColumn->getType()) {
409
                                $joinColumn->setType(PersisterHelper::getTypeOfColumn($referencedColumnName, $targetClass, $this->em));
410
                            }
411
412 32
                            $columnList[] = $this->getSelectJoinColumnSQL($joinColumn);
413
                        }
414
415 32
                        break;
416
                }
417
            }
418
        }
419
420 68
        $this->currentPersisterContext->selectColumnListSql = implode(', ', $columnList);
421
422 68
        return $this->currentPersisterContext->selectColumnListSql;
423
    }
424
425
    /**
426
     * {@inheritdoc}
427
     */
428 295
    protected function getInsertColumnList()
429
    {
430
        // Identifier columns must always come first in the column list of subclasses.
431 295
        $columns     = [];
432 295
        $parentClass = $this->class;
433 295
        while (($parentClass = $parentClass->getParent()) !== null) {
434 289
            if (! $parentClass->isMappedSuperclass) {
435 289
                $parentColumns = $parentClass->getIdentifierColumns($this->em);
0 ignored issues
show
Bug introduced by
The method getIdentifierColumns() does not exist on Doctrine\ORM\Mapping\ComponentMetadata. It seems like you code against a sub-type of Doctrine\ORM\Mapping\ComponentMetadata such as Doctrine\ORM\Mapping\ClassMetadata. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

435
                /** @scrutinizer ignore-call */ 
436
                $parentColumns = $parentClass->getIdentifierColumns($this->em);
Loading history...
436
437 289
                foreach ($parentColumns as $columnName => $column) {
438 289
                    $columns[] = $columnName;
439
440 289
                    $this->columns[$columnName] = $column;
441
                }
442
443 289
                break;
444
            }
445
        }
446
447 295
        foreach ($this->class->getPropertiesIterator() as $name => $property) {
448 295
            if (($property instanceof FieldMetadata && ($property->isVersioned() || $this->class->isInheritedProperty($name)))
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: ($property instanceof Do...nheritedProperty($name), Probably Intended Meaning: $property instanceof Doc...heritedProperty($name))
Loading history...
449 295
                || ($property instanceof AssociationMetadata && $this->class->isInheritedProperty($name))
450
                /*|| isset($this->class->embeddedClasses[$name])*/) {
451 293
                continue;
452
            }
453
454 295
            if ($property instanceof AssociationMetadata) {
455 265
                if ($property->isOwningSide() && $property instanceof ToOneAssociationMetadata) {
456 264
                    $targetClass = $this->em->getClassMetadata($property->getTargetEntity());
457
458 264
                    foreach ($property->getJoinColumns() as $joinColumn) {
459
                        /** @var JoinColumnMetadata $joinColumn */
460 264
                        $columnName           = $joinColumn->getColumnName();
461 264
                        $referencedColumnName = $joinColumn->getReferencedColumnName();
462
463 264
                        if (! $joinColumn->getType()) {
464 12
                            $joinColumn->setType(PersisterHelper::getTypeOfColumn($referencedColumnName, $targetClass, $this->em));
465
                        }
466
467 264
                        $columns[] = $columnName;
468
469 264
                        $this->columns[$columnName] = $joinColumn;
470
                    }
471
                }
472
473 265
                continue;
474
            }
475
476 295
            if ($this->class->getClassName() !== $this->class->getRootClassName()
477 295
                || ! $this->class->getProperty($name)->hasValueGenerator()
0 ignored issues
show
Bug introduced by
The method hasValueGenerator() does not exist on Doctrine\ORM\Mapping\Property. It seems like you code against a sub-type of Doctrine\ORM\Mapping\Property such as Doctrine\ORM\Mapping\FieldMetadata. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

477
                || ! $this->class->getProperty($name)->/** @scrutinizer ignore-call */ hasValueGenerator()
Loading history...
478 291
                || $this->class->getProperty($name)->getValueGenerator()->getType() !== GeneratorType::IDENTITY
0 ignored issues
show
Bug introduced by
The method getValueGenerator() does not exist on Doctrine\ORM\Mapping\Property. Did you maybe mean getValue()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

478
                || $this->class->getProperty($name)->/** @scrutinizer ignore-call */ getValueGenerator()->getType() !== GeneratorType::IDENTITY

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
479 295
                || $this->class->identifier[0] !== $name
480
            ) {
481 233
                $columnName = $property->getColumnName();
0 ignored issues
show
Bug introduced by
The method getColumnName() does not exist on Doctrine\ORM\Mapping\Property. It seems like you code against a sub-type of Doctrine\ORM\Mapping\Property such as Doctrine\ORM\Mapping\FieldMetadata. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

481
                /** @scrutinizer ignore-call */ 
482
                $columnName = $property->getColumnName();
Loading history...
482
483 233
                $columns[] = $columnName;
484
485 233
                $this->columns[$columnName] = $property;
486
            }
487
        }
488
489
        // Add discriminator column if it is the topmost class.
490 295
        if ($this->class->isRootEntity()) {
491 295
            $discrColumn     = $this->class->discriminatorColumn;
492 295
            $discrColumnName = $discrColumn->getColumnName();
493
494 295
            $columns[] = $discrColumnName;
495
496 295
            $this->columns[$discrColumnName] = $discrColumn;
497
        }
498
499 295
        return $columns;
500
    }
501
502
    /**
503
     * @param string $baseTableAlias
504
     *
505
     * @return string
506
     */
507 73
    private function getJoinSql($baseTableAlias)
508
    {
509 73
        $joinSql           = '';
510 73
        $identifierColumns = $this->class->getIdentifierColumns($this->em);
511
512
        // INNER JOIN parent tables
513 73
        $parentClass = $this->class;
514
515 73
        while (($parentClass = $parentClass->getParent()) !== null) {
516 49
            if (! $parentClass->isMappedSuperclass) {
517 49
                $conditions = [];
518 49
                $tableName  = $parentClass->table->getQuotedQualifiedName($this->platform);
519 49
                $tableAlias = $this->getSQLTableAlias($parentClass->getTableName());
520 49
                $joinSql   .= ' INNER JOIN ' . $tableName . ' ' . $tableAlias . ' ON ';
521
522 49
                foreach ($identifierColumns as $idColumn) {
523 49
                    $quotedColumnName = $this->platform->quoteIdentifier($idColumn->getColumnName());
524
525 49
                    $conditions[] = $baseTableAlias . '.' . $quotedColumnName . ' = ' . $tableAlias . '.' . $quotedColumnName;
526
                }
527
528 49
                $joinSql .= implode(' AND ', $conditions);
529
            }
530
        }
531
532
        // OUTER JOIN sub tables
533 73
        foreach ($this->class->getSubClasses() as $subClassName) {
534 52
            $conditions = [];
535 52
            $subClass   = $this->em->getClassMetadata($subClassName);
536 52
            $tableName  = $subClass->table->getQuotedQualifiedName($this->platform);
0 ignored issues
show
Bug introduced by
Accessing table on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
537 52
            $tableAlias = $this->getSQLTableAlias($subClass->getTableName());
538 52
            $joinSql   .= ' LEFT JOIN ' . $tableName . ' ' . $tableAlias . ' ON ';
539
540 52
            foreach ($identifierColumns as $idColumn) {
541 52
                $quotedColumnName = $this->platform->quoteIdentifier($idColumn->getColumnName());
542
543 52
                $conditions[] = $baseTableAlias . '.' . $quotedColumnName . ' = ' . $tableAlias . '.' . $quotedColumnName;
544
            }
545
546 52
            $joinSql .= implode(' AND ', $conditions);
547
        }
548
549 73
        return $joinSql;
550
    }
551
}
552