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

JoinedSubclassPersister   F

Complexity

Total Complexity 88

Size/Duplication

Total Lines 520
Duplicated Lines 0 %

Test Coverage

Coverage 95.75%

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 252
c 3
b 0
f 0
dl 0
loc 520
ccs 248
cts 259
cp 0.9575
rs 2
wmc 88

9 Methods

Rating   Name   Duplication   Size   Complexity  
F insert() 0 92 16
B getSelectSQL() 0 61 10
A delete() 0 28 3
A getLockTablesSql() 0 25 3
A getCountSQL() 0 24 5
A update() 0 31 6
D getSelectColumnsSQL() 0 93 19
D getInsertColumnList() 0 72 20
B getJoinSql() 0 43 6

How to fix   Complexity   

Complex Class

Complex classes like JoinedSubclassPersister often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use JoinedSubclassPersister, and based on these observations, apply Extract Interface, too.

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