Failed Conditions
Pull Request — master (#7885)
by Šimon
06:26
created

JoinedSubclassPersister::delete()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 28
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 3.072

Importance

Changes 0
Metric Value
cc 3
eloc 14
nc 3
nop 1
dl 0
loc 28
ccs 12
cts 15
cp 0.8
crap 3.072
rs 9.7998
c 0
b 0
f 0
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 Doctrine\ORM\Utility\PersisterHelper;
20
use function array_combine;
21
use function array_keys;
22
use function implode;
23
use function is_array;
24
25
/**
26
 * The joined subclass persister maps a single entity instance to several tables in the
27
 * database as it is defined by the <tt>Class Table Inheritance</tt> strategy.
28
 *
29
 * @see https://martinfowler.com/eaaCatalog/classTableInheritance.html
30
 */
31
class JoinedSubclassPersister extends AbstractEntityInheritancePersister
32
{
33
    /**
34
     * {@inheritdoc}
35
     */
36 296
    public function insert($entity)
37
    {
38 296
        $rootClass      = ! $this->class->isRootEntity()
39 290
            ? $this->em->getClassMetadata($this->class->getRootClassName())
40 296
            : $this->class;
41 296
        $generationPlan = $this->class->getValueGenerationPlan();
42
43
        // Prepare statement for the root table
44 296
        $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

44
        $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...
45 296
        $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

45
        /** @scrutinizer ignore-call */ 
46
        $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...
46 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

46
        $rootTableStmt = $this->conn->prepare($rootPersister->/** @scrutinizer ignore-call */ getInsertSQL());
Loading history...
47
48
        // Prepare statements for sub tables.
49 296
        $subTableStmts = [];
50
51 296
        if ($rootClass !== $this->class) {
52 290
            $subTableStmts[$this->class->getTableName()] = $this->conn->prepare($this->getInsertSQL());
53
        }
54
55 296
        $parentClass = $this->class;
56
57 296
        while (($parentClass = $parentClass->getParent()) !== null) {
58 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

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

151
            /** @scrutinizer ignore-call */ 
152
            $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...
152 5
            $versionedTable = $versionedClass->getTableName();
153
154 5
            if (! isset($updateData[$versionedTable])) {
155 2
                $tableName = $versionedClass->table->getQuotedQualifiedName($this->platform);
156
157 2
                $this->updateTable($entity, $tableName, [], true);
158
            }
159
160 4
            $identifiers = $this->em->getUnitOfWork()->getEntityIdentifier($entity);
161
162 4
            $this->assignDefaultVersionValue($entity, $identifiers);
163
        }
164 29
    }
165
166
    /**
167
     * {@inheritdoc}
168
     */
169 4
    public function delete($entity)
170
    {
171 4
        $identifier = $this->em->getUnitOfWork()->getEntityIdentifier($entity);
172 4
        $id         = array_combine(array_keys($this->class->getIdentifierColumns($this->em)), $identifier);
173
174 4
        $this->deleteJoinTableRecords($identifier);
175
176
        // If the database platform supports FKs, just
177
        // delete the row from the root table. Cascades do the rest.
178 4
        if ($this->platform->supportsForeignKeyConstraints()) {
179
            $rootClass = $this->em->getClassMetadata($this->class->getRootClassName());
180
            $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...
181
182
            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

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

375
        $this->currentPersisterContext->rsm->addMetaResult('r', $resultColumnName, $discrColumnName, false, /** @scrutinizer ignore-type */ $discrColumnType);
Loading history...
376
377 69
        $columnList[] = $discrColumnType->convertToDatabaseValueSQL($discrTableAlias . '.' . $quotedColumnName, $this->platform);
378
379
        // sub tables
380 69
        foreach ($this->class->getSubClasses() as $subClassName) {
381 50
            $subClass = $this->em->getClassMetadata($subClassName);
382
383
            // Add columns
384 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

384
            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...
385 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

385
                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...
386 50
                    continue;
387
                }
388
389
                switch (true) {
390 46
                    case $property instanceof FieldMetadata:
391 46
                        $columnList[] = $this->getSelectColumnSQL($fieldName, $subClass);
392 46
                        break;
393
394 32
                    case $property instanceof ToOneAssociationMetadata && $property->isOwningSide():
395 32
                        $targetClass = $this->em->getClassMetadata($property->getTargetEntity());
396
397 32
                        foreach ($property->getJoinColumns() as $joinColumn) {
398
                            /** @var JoinColumnMetadata $joinColumn */
399 32
                            $referencedColumnName = $joinColumn->getReferencedColumnName();
400
401 32
                            if (! $joinColumn->getType()) {
402
                                $joinColumn->setType(PersisterHelper::getTypeOfColumn($referencedColumnName, $targetClass, $this->em));
403
                            }
404
405 32
                            $columnList[] = $this->getSelectJoinColumnSQL($joinColumn);
406
                        }
407
408 32
                        break;
409
                }
410
            }
411
        }
412
413 69
        $this->currentPersisterContext->selectColumnListSql = implode(', ', $columnList);
414
415 69
        return $this->currentPersisterContext->selectColumnListSql;
416
    }
417
418
    /**
419
     * {@inheritdoc}
420
     */
421 296
    protected function getInsertColumnList()
422
    {
423
        // Identifier columns must always come first in the column list of subclasses.
424 296
        $columns       = [];
425 296
        $parentColumns = $this->class->getParent()
426 290
            ? $this->class->getIdentifierColumns($this->em)
427 296
            : [];
428
429 296
        foreach ($parentColumns as $columnName => $column) {
430 290
            $columns[] = $columnName;
431
432 290
            $this->columns[$columnName] = $column;
433
        }
434
435 296
        foreach ($this->class->getPropertiesIterator() as $name => $property) {
436 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...
437 296
                || ($property instanceof AssociationMetadata && $this->class->isInheritedProperty($name)
438 296
                || $property instanceof TransientMetadata)
439
                /*|| isset($this->class->embeddedClasses[$name])*/) {
440 294
                continue;
441
            }
442
443 296
            if ($property instanceof AssociationMetadata) {
444 265
                if ($property->isOwningSide() && $property instanceof ToOneAssociationMetadata) {
445 264
                    $targetClass = $this->em->getClassMetadata($property->getTargetEntity());
446
447 264
                    foreach ($property->getJoinColumns() as $joinColumn) {
448
                        /** @var JoinColumnMetadata $joinColumn */
449 264
                        $columnName           = $joinColumn->getColumnName();
450 264
                        $referencedColumnName = $joinColumn->getReferencedColumnName();
451
452 264
                        if (! $joinColumn->getType()) {
453 12
                            $joinColumn->setType(PersisterHelper::getTypeOfColumn($referencedColumnName, $targetClass, $this->em));
454
                        }
455
456 264
                        $columns[] = $columnName;
457
458 264
                        $this->columns[$columnName] = $joinColumn;
459
                    }
460
                }
461
462 265
                continue;
463
            }
464
465 296
            if ($this->class->getClassName() !== $this->class->getRootClassName()
466 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

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

467
                || $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...
468 296
                || $this->class->identifier[0] !== $name
469
            ) {
470 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

470
                /** @scrutinizer ignore-call */ 
471
                $columnName = $property->getColumnName();
Loading history...
471
472 234
                $columns[] = $columnName;
473
474 234
                $this->columns[$columnName] = $property;
475
            }
476
        }
477
478
        // Add discriminator column if it is the topmost class.
479 296
        if ($this->class->isRootEntity()) {
480 296
            $discrColumn     = $this->class->discriminatorColumn;
481 296
            $discrColumnName = $discrColumn->getColumnName();
482
483 296
            $columns[] = $discrColumnName;
484
485 296
            $this->columns[$discrColumnName] = $discrColumn;
486
        }
487
488 296
        return $columns;
489
    }
490
491
    /**
492
     * @param string $baseTableAlias
493
     *
494
     * @return string
495
     */
496 74
    private function getJoinSql($baseTableAlias)
497
    {
498 74
        $joinSql           = '';
499 74
        $identifierColumns = $this->class->getIdentifierColumns($this->em);
500
501
        // INNER JOIN parent tables
502 74
        $parentClass = $this->class;
503
504 74
        while (($parentClass = $parentClass->getParent()) !== null) {
505 50
            $conditions = [];
506 50
            $tableName  = $parentClass->table->getQuotedQualifiedName($this->platform);
507 50
            $tableAlias = $this->getSQLTableAlias($parentClass->getTableName());
508 50
            $joinSql   .= ' INNER JOIN ' . $tableName . ' ' . $tableAlias . ' ON ';
509
510 50
            foreach ($identifierColumns as $idColumn) {
511 50
                $quotedColumnName = $this->platform->quoteIdentifier($idColumn->getColumnName());
512
513 50
                $conditions[] = $baseTableAlias . '.' . $quotedColumnName . ' = ' . $tableAlias . '.' . $quotedColumnName;
514
            }
515
516 50
            $joinSql .= implode(' AND ', $conditions);
517
        }
518
519
        // OUTER JOIN sub tables
520 74
        foreach ($this->class->getSubClasses() as $subClassName) {
521 52
            $conditions = [];
522 52
            $subClass   = $this->em->getClassMetadata($subClassName);
523 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...
524 52
            $tableAlias = $this->getSQLTableAlias($subClass->getTableName());
525 52
            $joinSql   .= ' LEFT JOIN ' . $tableName . ' ' . $tableAlias . ' ON ';
526
527 52
            foreach ($identifierColumns as $idColumn) {
528 52
                $quotedColumnName = $this->platform->quoteIdentifier($idColumn->getColumnName());
529
530 52
                $conditions[] = $baseTableAlias . '.' . $quotedColumnName . ' = ' . $tableAlias . '.' . $quotedColumnName;
531
            }
532
533 52
            $joinSql .= implode(' AND ', $conditions);
534
        }
535
536 74
        return $joinSql;
537
    }
538
}
539