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

386
        $this->currentPersisterContext->rsm->addMetaResult('r', $resultColumnName, $discrColumnName, false, /** @scrutinizer ignore-type */ $discrColumnType);
Loading history...
387
388 68
        $columnList[] = $discrColumnType->convertToDatabaseValueSQL($discrTableAlias . '.' . $quotedColumnName, $this->platform);
389
390
        // sub tables
391 68
        foreach ($this->class->getSubClasses() as $subClassName) {
392 50
            $subClass = $this->em->getClassMetadata($subClassName);
393
394
            // Add columns
395 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

395
            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...
396 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

396
                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...
397 50
                    continue;
398
                }
399
400
                switch (true) {
401 46
                    case $property instanceof FieldMetadata:
402 46
                        $columnList[] = $this->getSelectColumnSQL($fieldName, $subClass);
403 46
                        break;
404
405 32
                    case $property instanceof ToOneAssociationMetadata && $property->isOwningSide():
406 32
                        $targetClass = $this->em->getClassMetadata($property->getTargetEntity());
407
408 32
                        foreach ($property->getJoinColumns() as $joinColumn) {
409
                            /** @var JoinColumnMetadata $joinColumn */
410 32
                            $referencedColumnName = $joinColumn->getReferencedColumnName();
411
412 32
                            if (! $joinColumn->getType()) {
413
                                $joinColumn->setType(PersisterHelper::getTypeOfColumn($referencedColumnName, $targetClass, $this->em));
414
                            }
415
416 32
                            $columnList[] = $this->getSelectJoinColumnSQL($joinColumn);
417
                        }
418
419 32
                        break;
420
                }
421
            }
422
        }
423
424 68
        $this->currentPersisterContext->selectColumnListSql = implode(', ', $columnList);
425
426 68
        return $this->currentPersisterContext->selectColumnListSql;
427
    }
428
429
    /**
430
     * {@inheritdoc}
431
     */
432 295
    protected function getInsertColumnList()
433
    {
434
        // Identifier columns must always come first in the column list of subclasses.
435 295
        $columns     = [];
436 295
        $parentClass = $this->class;
437 295
        while (($parentClass = $parentClass->getParent()) !== null) {
438 289
            if (! $parentClass->isMappedSuperclass) {
439 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

439
                /** @scrutinizer ignore-call */ 
440
                $parentColumns = $parentClass->getIdentifierColumns($this->em);
Loading history...
440
441 289
                foreach ($parentColumns as $columnName => $column) {
442 289
                    $columns[] = $columnName;
443
444 289
                    $this->columns[$columnName] = $column;
445
                }
446
447 289
                break;
448
            }
449
        }
450
451 295
        foreach ($this->class->getPropertiesIterator() as $name => $property) {
452 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...
453 295
                || ($property instanceof AssociationMetadata && $this->class->isInheritedProperty($name))
454
                /*|| isset($this->class->embeddedClasses[$name])*/) {
455 293
                continue;
456
            }
457
458 295
            if ($property instanceof AssociationMetadata) {
459 265
                if ($property->isOwningSide() && $property instanceof ToOneAssociationMetadata) {
460 264
                    $targetClass = $this->em->getClassMetadata($property->getTargetEntity());
461
462 264
                    foreach ($property->getJoinColumns() as $joinColumn) {
463
                        /** @var JoinColumnMetadata $joinColumn */
464 264
                        $columnName           = $joinColumn->getColumnName();
465 264
                        $referencedColumnName = $joinColumn->getReferencedColumnName();
466
467 264
                        if (! $joinColumn->getType()) {
468 12
                            $joinColumn->setType(PersisterHelper::getTypeOfColumn($referencedColumnName, $targetClass, $this->em));
469
                        }
470
471 264
                        $columns[] = $columnName;
472
473 264
                        $this->columns[$columnName] = $joinColumn;
474
                    }
475
                }
476
477 265
                continue;
478
            }
479
480 295
            if ($this->class->getClassName() !== $this->class->getRootClassName()
481 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

481
                || ! $this->class->getProperty($name)->/** @scrutinizer ignore-call */ hasValueGenerator()
Loading history...
482 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

482
                || $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...
483 295
                || $this->class->identifier[0] !== $name
484
            ) {
485 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

485
                /** @scrutinizer ignore-call */ 
486
                $columnName = $property->getColumnName();
Loading history...
486
487 233
                $columns[] = $columnName;
488
489 233
                $this->columns[$columnName] = $property;
490
            }
491
        }
492
493
        // Add discriminator column if it is the topmost class.
494 295
        if ($this->class->isRootEntity()) {
495 295
            $discrColumn     = $this->class->discriminatorColumn;
496 295
            $discrColumnName = $discrColumn->getColumnName();
497
498 295
            $columns[] = $discrColumnName;
499
500 295
            $this->columns[$discrColumnName] = $discrColumn;
501
        }
502
503 295
        return $columns;
504
    }
505
506
    /**
507
     * @param string $baseTableAlias
508
     *
509
     * @return string
510
     */
511 73
    private function getJoinSql($baseTableAlias)
512
    {
513 73
        $joinSql           = '';
514 73
        $identifierColumns = $this->class->getIdentifierColumns($this->em);
515
516
        // INNER JOIN parent tables
517 73
        $parentClass = $this->class;
518
519 73
        while (($parentClass = $parentClass->getParent()) !== null) {
520 49
            if (! $parentClass->isMappedSuperclass) {
521 49
                $conditions = [];
522 49
                $tableName  = $parentClass->table->getQuotedQualifiedName($this->platform);
523 49
                $tableAlias = $this->getSQLTableAlias($parentClass->getTableName());
524 49
                $joinSql   .= ' INNER JOIN ' . $tableName . ' ' . $tableAlias . ' ON ';
525
526 49
                foreach ($identifierColumns as $idColumn) {
527 49
                    $quotedColumnName = $this->platform->quoteIdentifier($idColumn->getColumnName());
528
529 49
                    $conditions[] = $baseTableAlias . '.' . $quotedColumnName . ' = ' . $tableAlias . '.' . $quotedColumnName;
530
                }
531
532 49
                $joinSql .= implode(' AND ', $conditions);
533
            }
534
        }
535
536
        // OUTER JOIN sub tables
537 73
        foreach ($this->class->getSubClasses() as $subClassName) {
538 52
            $conditions = [];
539 52
            $subClass   = $this->em->getClassMetadata($subClassName);
540 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...
541 52
            $tableAlias = $this->getSQLTableAlias($subClass->getTableName());
542 52
            $joinSql   .= ' LEFT JOIN ' . $tableName . ' ' . $tableAlias . ' ON ';
543
544 52
            foreach ($identifierColumns as $idColumn) {
545 52
                $quotedColumnName = $this->platform->quoteIdentifier($idColumn->getColumnName());
546
547 52
                $conditions[] = $baseTableAlias . '.' . $quotedColumnName . ' = ' . $tableAlias . '.' . $quotedColumnName;
548
            }
549
550 52
            $joinSql .= implode(' AND ', $conditions);
551
        }
552
553 73
        return $joinSql;
554
    }
555
}
556