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

JoinedSubclassPersister::getJoinSql()   B

Complexity

Conditions 6
Paths 12

Size

Total Lines 43
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 25
CRAP Score 6

Importance

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

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

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

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

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

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

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

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

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

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

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

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

Loading history...
397 49
                    continue;
398
                }
399
400
                switch (true) {
401 45
                    case $property instanceof FieldMetadata:
402 45
                        $columnList[] = $this->getSelectColumnSQL($fieldName, $subClass);
403 45
                        break;
404
405 31
                    case $property instanceof ToOneAssociationMetadata && $property->isOwningSide():
406 31
                        $targetClass = $this->em->getClassMetadata($property->getTargetEntity());
407
408 31
                        foreach ($property->getJoinColumns() as $joinColumn) {
409
                            /** @var JoinColumnMetadata $joinColumn */
410 31
                            $referencedColumnName = $joinColumn->getReferencedColumnName();
411
412 31
                            if (! $joinColumn->getType()) {
413
                                $joinColumn->setType(PersisterHelper::getTypeOfColumn($referencedColumnName, $targetClass, $this->em));
414
                            }
415
416 31
                            $columnList[] = $this->getSelectJoinColumnSQL($joinColumn);
417
                        }
418
419 31
                        break;
420
                }
421
            }
422
        }
423
424 68
        $this->currentPersisterContext->selectColumnListSql = implode(', ', $columnList);
425
426 68
        return $this->currentPersisterContext->selectColumnListSql;
427
    }
428
429
    /**
430
     * {@inheritdoc}
431
     */
432 295
    protected function getInsertColumnList()
433
    {
434
        // Identifier columns must always come first in the column list of subclasses.
435 295
        $columns       = [];
436 295
        $parentColumns = $this->class->isRootEntity()
437 295
            ? []
438 295
            : $this->class->getIdentifierColumns($this->em);
439
440 295
        foreach ($parentColumns as $columnName => $column) {
441 289
            $columns[] = $columnName;
442
443 289
            $this->columns[$columnName] = $column;
444
        }
445
446 295
        foreach ($this->class->getPropertiesIterator() as $name => $property) {
447 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...
448 295
                || ($property instanceof AssociationMetadata && $this->class->isInheritedProperty($name))
449
                /*|| isset($this->class->embeddedClasses[$name])*/) {
450 293
                continue;
451
            }
452
453 295
            if ($property instanceof AssociationMetadata) {
454 264
                if ($property->isOwningSide() && $property instanceof ToOneAssociationMetadata) {
455 263
                    $targetClass = $this->em->getClassMetadata($property->getTargetEntity());
456
457 263
                    foreach ($property->getJoinColumns() as $joinColumn) {
458
                        /** @var JoinColumnMetadata $joinColumn */
459 263
                        $columnName           = $joinColumn->getColumnName();
460 263
                        $referencedColumnName = $joinColumn->getReferencedColumnName();
461
462 263
                        if (! $joinColumn->getType()) {
463 12
                            $joinColumn->setType(PersisterHelper::getTypeOfColumn($referencedColumnName, $targetClass, $this->em));
464
                        }
465
466 263
                        $columns[] = $columnName;
467
468 263
                        $this->columns[$columnName] = $joinColumn;
469
                    }
470
                }
471
472 264
                continue;
473
            }
474
475 295
            if ($this->class->getClassName() !== $this->class->getRootClassName()
476 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

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

477
                || $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...
478 295
                || $this->class->identifier[0] !== $name
479
            ) {
480 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

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