Failed Conditions
Push — master ( a3e53b...559253 )
by Guilherme
14:58
created

JoinedSubclassPersister   F

Complexity

Total Complexity 80

Size/Duplication

Total Lines 481
Duplicated Lines 0 %

Test Coverage

Coverage 96.25%

Importance

Changes 0
Metric Value
eloc 232
dl 0
loc 481
ccs 231
cts 240
cp 0.9625
rs 2
c 0
b 0
f 0
wmc 80

9 Methods

Rating   Name   Duplication   Size   Complexity  
F insert() 0 90 15
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
C getSelectColumnsSQL() 0 69 14
D getInsertColumnList() 0 61 19
A getJoinSql() 0 41 5

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\Mapping\TransientMetadata;
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 296
    public function insert($entity)
36
    {
37 296
        $rootClass      = ! $this->class->isRootEntity()
38 290
            ? $this->em->getClassMetadata($this->class->getRootClassName())
39 296
            : $this->class;
40 296
        $generationPlan = $this->class->getValueGenerationPlan();
41
42
        // Prepare statement for the root table
43 296
        $rootPersister = $this->em->getUnitOfWork()->getEntityPersister($rootClass->getClassName());
0 ignored issues
show
Bug introduced by
The method getClassName() does not exist on Doctrine\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 296
        $rootTableName = $rootClass->getTableName();
0 ignored issues
show
Bug introduced by
The method getTableName() does not exist on Doctrine\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 296
        $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 296
        $subTableStmts = [];
49
50 296
        if ($rootClass !== $this->class) {
51 290
            $subTableStmts[$this->class->getTableName()] = $this->conn->prepare($this->getInsertSQL());
52
        }
53
54 296
        $parentClass = $this->class;
55
56 296
        while (($parentClass = $parentClass->getParent()) !== null) {
57 290
            $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

57
            /** @scrutinizer ignore-call */ 
58
            $parentTableName = $parentClass->getTableName();
Loading history...
58
59 290
            if ($parentClass !== $rootClass) {
60 170
                $parentPersister = $this->em->getUnitOfWork()->getEntityPersister($parentClass->getClassName());
61
62 170
                $subTableStmts[$parentTableName] = $this->conn->prepare($parentPersister->getInsertSQL());
63
            }
64
        }
65
66
        // Execute all inserts. For each entity:
67
        // 1) Insert on root table
68
        // 2) Insert on sub tables
69 296
        $insertData = $this->prepareInsertData($entity);
70
71
        // Execute insert on root table
72 296
        $paramIndex = 1;
73
74 296
        foreach ($insertData[$rootTableName] as $columnName => $value) {
75 296
            $type = $this->columns[$columnName]->getType();
76
77 296
            $rootTableStmt->bindValue($paramIndex++, $value, $type);
78
        }
79
80 296
        $rootTableStmt->execute();
81
82 296
        if ($generationPlan->containsDeferred()) {
83 292
            $generationPlan->executeDeferred($this->em, $entity);
84 292
            $id = $this->getIdentifier($entity);
85
        } else {
86 4
            $id = $this->em->getUnitOfWork()->getEntityIdentifier($entity);
87
        }
88
89 296
        if ($this->class->isVersioned()) {
90 9
            $this->assignDefaultVersionValue($entity, $id);
91
        }
92
93
        // Execute inserts on subtables.
94
        // The order doesn't matter because all child tables link to the root table via FK.
95 296
        foreach ($subTableStmts as $tableName => $stmt) {
96
            /** @var Statement $stmt */
97 290
            $paramIndex = 1;
98 290
            $data       = $insertData[$tableName] ?? [];
99
100 290
            foreach ((array) $id as $idName => $idVal) {
101 290
                $type = Type::getType('string');
102
103 290
                if (isset($this->columns[$idName])) {
104 290
                    $type = $this->columns[$idName]->getType();
105
                }
106
107 290
                $stmt->bindValue($paramIndex++, $idVal, $type);
108
            }
109
110 290
            foreach ($data as $columnName => $value) {
111 225
                if (! is_array($id) || ! isset($id[$columnName])) {
112 225
                    $type = $this->columns[$columnName]->getType();
113
114 225
                    $stmt->bindValue($paramIndex++, $value, $type);
115
                }
116
            }
117
118 290
            $stmt->execute();
119
        }
120
121 296
        $rootTableStmt->closeCursor();
122
123 296
        foreach ($subTableStmts as $stmt) {
124 290
            $stmt->closeCursor();
125
        }
126 296
    }
127
128
    /**
129
     * {@inheritdoc}
130
     */
131 31
    public function update($entity)
132
    {
133 31
        $updateData = $this->prepareUpdateData($entity);
134
135 31
        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...
136
            return;
137
        }
138
139 31
        $isVersioned = $this->class->isVersioned();
140
141 31
        foreach ($updateData as $tableName => $data) {
142 31
            $versioned = $isVersioned && $this->class->versionProperty->getTableName() === $tableName;
143
144 31
            $this->updateTable($entity, $this->platform->quoteIdentifier($tableName), $data, $versioned);
145
        }
146
147
        // Make sure the table with the version column is updated even if no columns on that
148
        // table were affected.
149 30
        if ($isVersioned) {
150 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

150
            /** @scrutinizer ignore-call */ 
151
            $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...
151 5
            $versionedTable = $versionedClass->getTableName();
152
153 5
            if (! isset($updateData[$versionedTable])) {
154 2
                $tableName = $versionedClass->table->getQuotedQualifiedName($this->platform);
155
156 2
                $this->updateTable($entity, $tableName, [], true);
157
            }
158
159 4
            $identifiers = $this->em->getUnitOfWork()->getEntityIdentifier($entity);
160
161 4
            $this->assignDefaultVersionValue($entity, $identifiers);
162
        }
163 29
    }
164
165
    /**
166
     * {@inheritdoc}
167
     */
168 4
    public function delete($entity)
169
    {
170 4
        $identifier = $this->em->getUnitOfWork()->getEntityIdentifier($entity);
171 4
        $id         = array_combine(array_keys($this->class->getIdentifierColumns($this->em)), $identifier);
172
173 4
        $this->deleteJoinTableRecords($identifier);
174
175
        // If the database platform supports FKs, just
176
        // delete the row from the root table. Cascades do the rest.
177 4
        if ($this->platform->supportsForeignKeyConstraints()) {
178
            $rootClass = $this->em->getClassMetadata($this->class->getRootClassName());
179
            $rootTable = $rootClass->table->getQuotedQualifiedName($this->platform);
0 ignored issues
show
Bug introduced by
Accessing table on the interface Doctrine\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
180
181
            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

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

365
        $this->currentPersisterContext->rsm->addMetaResult('r', $resultColumnName, $discrColumnName, false, /** @scrutinizer ignore-type */ $discrColumnType);
Loading history...
366
367 69
        $columnList[] = $discrColumnType->convertToDatabaseValueSQL($discrTableAlias . '.' . $quotedColumnName, $this->platform);
368
369
        // sub tables
370 69
        foreach ($this->class->getSubClasses() as $subClassName) {
371 50
            $subClass = $this->em->getClassMetadata($subClassName);
372
373
            // Add columns
374 50
            foreach ($subClass->getPropertiesIterator() as $fieldName => $property) {
0 ignored issues
show
Bug introduced by
The method getPropertiesIterator() does not exist on Doctrine\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

374
            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...
375 50
                if ($subClass->isInheritedProperty($fieldName)) {
0 ignored issues
show
Bug introduced by
The method isInheritedProperty() does not exist on Doctrine\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

375
                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...
376 50
                    continue;
377
                }
378
379
                switch (true) {
380 46
                    case $property instanceof FieldMetadata:
381 46
                        $columnList[] = $this->getSelectColumnSQL($fieldName, $subClass);
382 46
                        break;
383
384 32
                    case $property instanceof ToOneAssociationMetadata && $property->isOwningSide():
385 32
                        foreach ($property->getJoinColumns() as $joinColumn) {
386 32
                            $columnList[] = $this->getSelectJoinColumnSQL($joinColumn);
387
                        }
388
389 32
                        break;
390
                }
391
            }
392
        }
393
394 69
        $this->currentPersisterContext->selectColumnListSql = implode(', ', $columnList);
395
396 69
        return $this->currentPersisterContext->selectColumnListSql;
397
    }
398
399
    /**
400
     * {@inheritdoc}
401
     */
402 296
    protected function getInsertColumnList()
403
    {
404
        // Identifier columns must always come first in the column list of subclasses.
405 296
        $columns       = [];
406 296
        $parentColumns = $this->class->getParent()
407 290
            ? $this->class->getIdentifierColumns($this->em)
408 296
            : [];
409
410 296
        foreach ($parentColumns as $columnName => $column) {
411 290
            $columns[] = $columnName;
412
413 290
            $this->columns[$columnName] = $column;
414
        }
415
416 296
        foreach ($this->class->getPropertiesIterator() as $name => $property) {
417 296
            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...pping\TransientMetadata, Probably Intended Meaning: $property instanceof Doc...ping\TransientMetadata)
Loading history...
418 296
                || ($property instanceof AssociationMetadata && $this->class->isInheritedProperty($name)
419 296
                || $property instanceof TransientMetadata)
420
                /*|| isset($this->class->embeddedClasses[$name])*/) {
421 294
                continue;
422
            }
423
424 296
            if ($property instanceof AssociationMetadata) {
425 265
                if ($property->isOwningSide() && $property instanceof ToOneAssociationMetadata) {
426 264
                    foreach ($property->getJoinColumns() as $joinColumn) {
427
                        /** @var JoinColumnMetadata $joinColumn */
428 264
                        $columnName = $joinColumn->getColumnName();
429
430 264
                        $columns[] = $columnName;
431
432 264
                        $this->columns[$columnName] = $joinColumn;
433
                    }
434
                }
435
436 265
                continue;
437
            }
438
439 296
            if ($this->class->getClassName() !== $this->class->getRootClassName()
440 296
                || ! $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

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

441
                || $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...
442 296
                || $this->class->identifier[0] !== $name
443
            ) {
444 234
                $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

444
                /** @scrutinizer ignore-call */ 
445
                $columnName = $property->getColumnName();
Loading history...
445
446 234
                $columns[] = $columnName;
447
448 234
                $this->columns[$columnName] = $property;
449
            }
450
        }
451
452
        // Add discriminator column if it is the topmost class.
453 296
        if ($this->class->isRootEntity()) {
454 296
            $discrColumn     = $this->class->discriminatorColumn;
455 296
            $discrColumnName = $discrColumn->getColumnName();
456
457 296
            $columns[] = $discrColumnName;
458
459 296
            $this->columns[$discrColumnName] = $discrColumn;
460
        }
461
462 296
        return $columns;
463
    }
464
465
    /**
466
     * @param string $baseTableAlias
467
     *
468
     * @return string
469
     */
470 74
    private function getJoinSql($baseTableAlias)
471
    {
472 74
        $joinSql           = '';
473 74
        $identifierColumns = $this->class->getIdentifierColumns($this->em);
474
475
        // INNER JOIN parent tables
476 74
        $parentClass = $this->class;
477
478 74
        while (($parentClass = $parentClass->getParent()) !== null) {
479 50
            $conditions = [];
480 50
            $tableName  = $parentClass->table->getQuotedQualifiedName($this->platform);
481 50
            $tableAlias = $this->getSQLTableAlias($parentClass->getTableName());
482 50
            $joinSql   .= ' INNER JOIN ' . $tableName . ' ' . $tableAlias . ' ON ';
483
484 50
            foreach ($identifierColumns as $idColumn) {
485 50
                $quotedColumnName = $this->platform->quoteIdentifier($idColumn->getColumnName());
486
487 50
                $conditions[] = $baseTableAlias . '.' . $quotedColumnName . ' = ' . $tableAlias . '.' . $quotedColumnName;
488
            }
489
490 50
            $joinSql .= implode(' AND ', $conditions);
491
        }
492
493
        // OUTER JOIN sub tables
494 74
        foreach ($this->class->getSubClasses() as $subClassName) {
495 52
            $conditions = [];
496 52
            $subClass   = $this->em->getClassMetadata($subClassName);
497 52
            $tableName  = $subClass->table->getQuotedQualifiedName($this->platform);
0 ignored issues
show
Bug introduced by
Accessing table on the interface Doctrine\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
498 52
            $tableAlias = $this->getSQLTableAlias($subClass->getTableName());
499 52
            $joinSql   .= ' LEFT JOIN ' . $tableName . ' ' . $tableAlias . ' ON ';
500
501 52
            foreach ($identifierColumns as $idColumn) {
502 52
                $quotedColumnName = $this->platform->quoteIdentifier($idColumn->getColumnName());
503
504 52
                $conditions[] = $baseTableAlias . '.' . $quotedColumnName . ' = ' . $tableAlias . '.' . $quotedColumnName;
505
            }
506
507 52
            $joinSql .= implode(' AND ', $conditions);
508
        }
509
510 74
        return $joinSql;
511
    }
512
}
513