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\Types\Type; |
||
10 | use Doctrine\ORM\Mapping\AssociationMetadata; |
||
11 | use Doctrine\ORM\Mapping\FieldMetadata; |
||
12 | use Doctrine\ORM\Mapping\GeneratorType; |
||
13 | use Doctrine\ORM\Mapping\JoinColumnMetadata; |
||
14 | use Doctrine\ORM\Mapping\ManyToManyAssociationMetadata; |
||
15 | use Doctrine\ORM\Mapping\ToManyAssociationMetadata; |
||
16 | use Doctrine\ORM\Mapping\ToOneAssociationMetadata; |
||
17 | use Doctrine\ORM\Mapping\VersionFieldMetadata; |
||
18 | use Doctrine\ORM\Utility\PersisterHelper; |
||
19 | |||
20 | /** |
||
21 | * The joined subclass persister maps a single entity instance to several tables in the |
||
22 | * database as it is defined by the <tt>Class Table Inheritance</tt> strategy. |
||
23 | * |
||
24 | * @see http://martinfowler.com/eaaCatalog/classTableInheritance.html |
||
25 | */ |
||
26 | class JoinedSubclassPersister extends AbstractEntityInheritancePersister |
||
27 | { |
||
28 | /** |
||
29 | * {@inheritdoc} |
||
30 | */ |
||
31 | 293 | public function insert($entity) |
|
32 | { |
||
33 | 293 | $rootClass = ! $this->class->isRootEntity() |
|
34 | 287 | ? $this->em->getClassMetadata($this->class->getRootClassName()) |
|
35 | 293 | : $this->class; |
|
36 | 293 | $generationPlan = $this->class->getValueGenerationPlan(); |
|
37 | |||
38 | // Prepare statement for the root table |
||
39 | 293 | $rootPersister = $this->em->getUnitOfWork()->getEntityPersister($rootClass->getClassName()); |
|
40 | 293 | $rootTableName = $rootClass->getTableName(); |
|
41 | 293 | $rootTableStmt = $this->conn->prepare($rootPersister->getInsertSQL()); |
|
42 | |||
43 | // Prepare statements for sub tables. |
||
44 | 293 | $subTableStmts = []; |
|
45 | |||
46 | 293 | if ($rootClass !== $this->class) { |
|
47 | 287 | $subTableStmts[$this->class->getTableName()] = $this->conn->prepare($this->getInsertSQL()); |
|
48 | } |
||
49 | |||
50 | 293 | $parentClass = $this->class; |
|
51 | |||
52 | 293 | while (($parentClass = $parentClass->getParent()) !== null) { |
|
53 | 287 | $parentTableName = $parentClass->getTableName(); |
|
54 | |||
55 | 287 | if ($parentClass !== $rootClass) { |
|
56 | 166 | $parentPersister = $this->em->getUnitOfWork()->getEntityPersister($parentClass->getClassName()); |
|
57 | |||
58 | 166 | $subTableStmts[$parentTableName] = $this->conn->prepare($parentPersister->getInsertSQL()); |
|
59 | } |
||
60 | } |
||
61 | |||
62 | // Execute all inserts. For each entity: |
||
63 | // 1) Insert on root table |
||
64 | // 2) Insert on sub tables |
||
65 | 293 | $insertData = $this->prepareInsertData($entity); |
|
66 | |||
67 | // Execute insert on root table |
||
68 | 293 | $paramIndex = 1; |
|
69 | |||
70 | 293 | foreach ($insertData[$rootTableName] as $columnName => $value) { |
|
71 | 293 | $type = $this->columns[$columnName]->getType(); |
|
72 | |||
73 | 293 | $rootTableStmt->bindValue($paramIndex++, $value, $type); |
|
74 | } |
||
75 | |||
76 | 293 | $rootTableStmt->execute(); |
|
77 | |||
78 | 293 | if ($generationPlan->containsDeferred()) { |
|
79 | 289 | $generationPlan->executeDeferred($this->em, $entity); |
|
80 | 289 | $id = $this->getIdentifier($entity); |
|
81 | } else { |
||
82 | 4 | $id = $this->em->getUnitOfWork()->getEntityIdentifier($entity); |
|
83 | } |
||
84 | |||
85 | 293 | if ($this->class->isVersioned()) { |
|
86 | 9 | $this->assignDefaultVersionValue($entity, $id); |
|
87 | } |
||
88 | |||
89 | // Execute inserts on subtables. |
||
90 | // The order doesn't matter because all child tables link to the root table via FK. |
||
91 | 293 | foreach ($subTableStmts as $tableName => $stmt) { |
|
92 | /** @var \Doctrine\DBAL\Statement $stmt */ |
||
93 | 287 | $paramIndex = 1; |
|
94 | 287 | $data = $insertData[$tableName] ?? []; |
|
95 | |||
96 | 287 | foreach ((array) $id as $idName => $idVal) { |
|
97 | 287 | $type = Type::getType('string'); |
|
98 | |||
99 | 287 | if (isset($this->columns[$idName])) { |
|
100 | 287 | $type = $this->columns[$idName]->getType(); |
|
101 | } |
||
102 | |||
103 | 287 | $stmt->bindValue($paramIndex++, $idVal, $type); |
|
104 | } |
||
105 | |||
106 | 287 | foreach ($data as $columnName => $value) { |
|
107 | 222 | if (! is_array($id) || ! isset($id[$columnName])) { |
|
108 | 222 | $type = $this->columns[$columnName]->getType(); |
|
109 | |||
110 | 222 | $stmt->bindValue($paramIndex++, $value, $type); |
|
111 | } |
||
112 | } |
||
113 | |||
114 | 287 | $stmt->execute(); |
|
115 | } |
||
116 | |||
117 | 293 | $rootTableStmt->closeCursor(); |
|
118 | |||
119 | 293 | foreach ($subTableStmts as $stmt) { |
|
120 | 287 | $stmt->closeCursor(); |
|
121 | } |
||
122 | 293 | } |
|
123 | |||
124 | /** |
||
125 | * {@inheritdoc} |
||
126 | */ |
||
127 | 30 | public function update($entity) |
|
128 | { |
||
129 | 30 | $updateData = $this->prepareUpdateData($entity); |
|
130 | |||
131 | 30 | if (! $updateData) { |
|
0 ignored issues
–
show
|
|||
132 | return; |
||
133 | } |
||
134 | |||
135 | 30 | $isVersioned = $this->class->isVersioned(); |
|
136 | |||
137 | 30 | foreach ($updateData as $tableName => $data) { |
|
138 | 30 | $versioned = $isVersioned && $this->class->versionProperty->getTableName() === $tableName; |
|
139 | |||
140 | 30 | $this->updateTable($entity, $this->platform->quoteIdentifier($tableName), $data, $versioned); |
|
141 | } |
||
142 | |||
143 | // Make sure the table with the version column is updated even if no columns on that |
||
144 | // table were affected. |
||
145 | 29 | if ($isVersioned) { |
|
146 | 5 | $versionedClass = $this->class->versionProperty->getDeclaringClass(); |
|
147 | 5 | $versionedTable = $versionedClass->getTableName(); |
|
148 | |||
149 | 5 | if (! isset($updateData[$versionedTable])) { |
|
150 | 2 | $tableName = $versionedClass->table->getQuotedQualifiedName($this->platform); |
|
151 | |||
152 | 2 | $this->updateTable($entity, $tableName, [], true); |
|
153 | } |
||
154 | |||
155 | 4 | $identifiers = $this->em->getUnitOfWork()->getEntityIdentifier($entity); |
|
156 | |||
157 | 4 | $this->assignDefaultVersionValue($entity, $identifiers); |
|
158 | } |
||
159 | 28 | } |
|
160 | |||
161 | /** |
||
162 | * {@inheritdoc} |
||
163 | */ |
||
164 | 5 | public function delete($entity) |
|
165 | { |
||
166 | 5 | $identifier = $this->em->getUnitOfWork()->getEntityIdentifier($entity); |
|
167 | 5 | $id = array_combine(array_keys($this->class->getIdentifierColumns($this->em)), $identifier); |
|
168 | |||
169 | 5 | $this->deleteJoinTableRecords($identifier); |
|
170 | |||
171 | // If the database platform supports FKs, just |
||
172 | // delete the row from the root table. Cascades do the rest. |
||
173 | 5 | if ($this->platform->supportsForeignKeyConstraints()) { |
|
174 | $rootClass = $this->em->getClassMetadata($this->class->getRootClassName()); |
||
175 | $rootTable = $rootClass->table->getQuotedQualifiedName($this->platform); |
||
176 | |||
177 | return (bool) $this->conn->delete($rootTable, $id); |
||
178 | } |
||
179 | |||
180 | // Delete from all tables individually, starting from this class' table up to the root table. |
||
181 | 5 | $rootTable = $this->class->table->getQuotedQualifiedName($this->platform); |
|
182 | 5 | $affectedRows = $this->conn->delete($rootTable, $id); |
|
183 | 5 | $parentClass = $this->class; |
|
184 | |||
185 | 5 | while (($parentClass = $parentClass->getParent()) !== null) { |
|
186 | 4 | $parentTable = $parentClass->table->getQuotedQualifiedName($this->platform); |
|
187 | |||
188 | 4 | $this->conn->delete($parentTable, $id); |
|
189 | } |
||
190 | |||
191 | 5 | return (bool) $affectedRows; |
|
192 | } |
||
193 | |||
194 | /** |
||
195 | * {@inheritdoc} |
||
196 | */ |
||
197 | 68 | public function getSelectSQL( |
|
198 | $criteria, |
||
199 | ?AssociationMetadata $association = null, |
||
200 | $lockMode = null, |
||
201 | $limit = null, |
||
202 | $offset = null, |
||
203 | array $orderBy = [] |
||
204 | ) { |
||
205 | 68 | $this->switchPersisterContext($offset, $limit); |
|
206 | |||
207 | 68 | $baseTableAlias = $this->getSQLTableAlias($this->class->getTableName()); |
|
208 | 68 | $joinSql = $this->getJoinSql($baseTableAlias); |
|
209 | |||
210 | 68 | if ($association instanceof ManyToManyAssociationMetadata) { |
|
211 | 2 | $joinSql .= $this->getSelectManyToManyJoinSQL($association); |
|
212 | } |
||
213 | |||
214 | 68 | if ($association instanceof ToManyAssociationMetadata && $association->getOrderBy()) { |
|
215 | 1 | $orderBy = $association->getOrderBy(); |
|
216 | } |
||
217 | |||
218 | 68 | $orderBySql = $this->getOrderBySQL($orderBy, $baseTableAlias); |
|
219 | 68 | $conditionSql = ($criteria instanceof Criteria) |
|
220 | ? $this->getSelectConditionCriteriaSQL($criteria) |
||
221 | 68 | : $this->getSelectConditionSQL($criteria, $association); |
|
222 | |||
223 | // If the current class in the root entity, add the filters |
||
224 | 68 | $rootClass = $this->em->getClassMetadata($this->class->getRootClassName()); |
|
225 | 68 | $tableAlias = $this->getSQLTableAlias($rootClass->getTableName()); |
|
226 | 68 | $filterSql = $this->generateFilterConditionSQL($rootClass, $tableAlias); |
|
227 | |||
228 | 68 | if ($filterSql) { |
|
229 | 4 | $conditionSql .= $conditionSql |
|
230 | 2 | ? ' AND ' . $filterSql |
|
231 | 4 | : $filterSql; |
|
232 | } |
||
233 | |||
234 | 68 | $lockSql = ''; |
|
235 | |||
236 | switch ($lockMode) { |
||
237 | 68 | case LockMode::PESSIMISTIC_READ: |
|
238 | $lockSql = ' ' . $this->platform->getReadLockSQL(); |
||
239 | break; |
||
240 | |||
241 | 68 | case LockMode::PESSIMISTIC_WRITE: |
|
242 | $lockSql = ' ' . $this->platform->getWriteLockSQL(); |
||
243 | break; |
||
244 | } |
||
245 | |||
246 | 68 | $tableName = $this->class->table->getQuotedQualifiedName($this->platform); |
|
247 | 68 | $from = ' FROM ' . $tableName . ' ' . $baseTableAlias; |
|
248 | 68 | $where = $conditionSql !== '' ? ' WHERE ' . $conditionSql : ''; |
|
249 | 68 | $lock = $this->platform->appendLockHint($from, $lockMode); |
|
250 | 68 | $columnList = $this->getSelectColumnsSQL(); |
|
251 | 68 | $query = 'SELECT ' . $columnList |
|
252 | 68 | . $lock |
|
253 | 68 | . $joinSql |
|
254 | 68 | . $where |
|
255 | 68 | . $orderBySql; |
|
256 | |||
257 | 68 | return $this->platform->modifyLimitQuery($query, $limit, $offset) . $lockSql; |
|
258 | } |
||
259 | |||
260 | /** |
||
261 | * {@inheritDoc} |
||
262 | */ |
||
263 | 6 | public function getCountSQL($criteria = []) |
|
264 | { |
||
265 | 6 | $tableName = $this->class->table->getQuotedQualifiedName($this->platform); |
|
266 | 6 | $baseTableAlias = $this->getSQLTableAlias($this->class->getTableName()); |
|
267 | 6 | $joinSql = $this->getJoinSql($baseTableAlias); |
|
268 | |||
269 | 6 | $conditionSql = ($criteria instanceof Criteria) |
|
270 | 1 | ? $this->getSelectConditionCriteriaSQL($criteria) |
|
271 | 6 | : $this->getSelectConditionSQL($criteria); |
|
272 | |||
273 | 6 | $rootClass = $this->em->getClassMetadata($this->class->getRootClassName()); |
|
274 | 6 | $tableAlias = $this->getSQLTableAlias($rootClass->getTableName()); |
|
275 | 6 | $filterSql = $this->generateFilterConditionSQL($rootClass, $tableAlias); |
|
276 | |||
277 | 6 | if ($filterSql !== '') { |
|
278 | 1 | $conditionSql = $conditionSql |
|
279 | 1 | ? $conditionSql . ' AND ' . $filterSql |
|
280 | 1 | : $filterSql; |
|
281 | } |
||
282 | |||
283 | $sql = 'SELECT COUNT(*) ' |
||
284 | 6 | . 'FROM ' . $tableName . ' ' . $baseTableAlias |
|
285 | 6 | . $joinSql |
|
286 | 6 | . (empty($conditionSql) ? '' : ' WHERE ' . $conditionSql); |
|
287 | |||
288 | 6 | return $sql; |
|
289 | } |
||
290 | |||
291 | /** |
||
292 | * {@inheritdoc} |
||
293 | */ |
||
294 | 6 | protected function getLockTablesSql($lockMode) |
|
295 | { |
||
296 | 6 | $joinSql = ''; |
|
297 | 6 | $identifierColumns = $this->class->getIdentifierColumns($this->em); |
|
298 | 6 | $baseTableAlias = $this->getSQLTableAlias($this->class->getTableName()); |
|
299 | |||
300 | // INNER JOIN parent tables |
||
301 | 6 | $parentClass = $this->class; |
|
302 | |||
303 | 6 | while (($parentClass = $parentClass->getParent()) !== null) { |
|
304 | 5 | $conditions = []; |
|
305 | 5 | $tableName = $parentClass->table->getQuotedQualifiedName($this->platform); |
|
306 | 5 | $tableAlias = $this->getSQLTableAlias($parentClass->getTableName()); |
|
307 | 5 | $joinSql .= ' INNER JOIN ' . $tableName . ' ' . $tableAlias . ' ON '; |
|
308 | |||
309 | 5 | foreach ($identifierColumns as $idColumn) { |
|
310 | 5 | $quotedColumnName = $this->platform->quoteIdentifier($idColumn->getColumnName()); |
|
311 | |||
312 | 5 | $conditions[] = $baseTableAlias . '.' . $quotedColumnName . ' = ' . $tableAlias . '.' . $quotedColumnName; |
|
313 | } |
||
314 | |||
315 | 5 | $joinSql .= implode(' AND ', $conditions); |
|
316 | } |
||
317 | |||
318 | 6 | return parent::getLockTablesSql($lockMode) . $joinSql; |
|
319 | } |
||
320 | |||
321 | /** |
||
322 | * Ensure this method is never called. This persister overrides getSelectEntitiesSQL directly. |
||
323 | * |
||
324 | * @return string |
||
325 | */ |
||
326 | 68 | protected function getSelectColumnsSQL() |
|
327 | { |
||
328 | // Create the column list fragment only once |
||
329 | 68 | if ($this->currentPersisterContext->selectColumnListSql !== null) { |
|
330 | 12 | return $this->currentPersisterContext->selectColumnListSql; |
|
331 | } |
||
332 | |||
333 | 68 | $this->currentPersisterContext->rsm->addEntityResult($this->class->getClassName(), 'r'); |
|
334 | |||
335 | 68 | $columnList = []; |
|
336 | |||
337 | // Add columns |
||
338 | 68 | foreach ($this->class->getDeclaredPropertiesIterator() as $fieldName => $property) { |
|
339 | 68 | if ($property instanceof FieldMetadata) { |
|
340 | 68 | $columnList[] = $this->getSelectColumnSQL($fieldName, $property->getDeclaringClass()); |
|
341 | |||
342 | 68 | continue; |
|
343 | } |
||
344 | |||
345 | 56 | if (! ($property instanceof ToOneAssociationMetadata) || ! $property->isOwningSide()) { |
|
346 | 44 | continue; |
|
347 | } |
||
348 | |||
349 | 55 | $targetClass = $this->em->getClassMetadata($property->getTargetEntity()); |
|
350 | |||
351 | 55 | foreach ($property->getJoinColumns() as $joinColumn) { |
|
352 | /** @var JoinColumnMetadata $joinColumn */ |
||
353 | 55 | $referencedColumnName = $joinColumn->getReferencedColumnName(); |
|
354 | |||
355 | 55 | if (! $joinColumn->getType()) { |
|
356 | $joinColumn->setType(PersisterHelper::getTypeOfColumn($referencedColumnName, $targetClass, $this->em)); |
||
357 | } |
||
358 | |||
359 | 55 | $columnList[] = $this->getSelectJoinColumnSQL($joinColumn); |
|
360 | } |
||
361 | } |
||
362 | |||
363 | // Add discriminator column (DO NOT ALIAS, see AbstractEntityInheritancePersister#processSQLResult). |
||
364 | 68 | $discrColumn = $this->class->discriminatorColumn; |
|
365 | 68 | $discrTableAlias = $this->getSQLTableAlias($discrColumn->getTableName()); |
|
366 | 68 | $discrColumnName = $discrColumn->getColumnName(); |
|
367 | 68 | $discrColumnType = $discrColumn->getType(); |
|
368 | 68 | $resultColumnName = $this->platform->getSQLResultCasing($discrColumnName); |
|
369 | 68 | $quotedColumnName = $this->platform->quoteIdentifier($discrColumn->getColumnName()); |
|
370 | |||
371 | 68 | $this->currentPersisterContext->rsm->setDiscriminatorColumn('r', $resultColumnName); |
|
372 | 68 | $this->currentPersisterContext->rsm->addMetaResult('r', $resultColumnName, $discrColumnName, false, $discrColumnType); |
|
373 | |||
374 | 68 | $columnList[] = $discrColumnType->convertToDatabaseValueSQL($discrTableAlias . '.' . $quotedColumnName, $this->platform); |
|
375 | |||
376 | // sub tables |
||
377 | 68 | foreach ($this->class->getSubClasses() as $subClassName) { |
|
378 | 49 | $subClass = $this->em->getClassMetadata($subClassName); |
|
379 | |||
380 | // Add columns |
||
381 | 49 | foreach ($subClass->getDeclaredPropertiesIterator() as $fieldName => $property) { |
|
382 | 49 | if ($subClass->isInheritedProperty($fieldName)) { |
|
383 | 49 | continue; |
|
384 | } |
||
385 | |||
386 | switch (true) { |
||
387 | 45 | case ($property instanceof FieldMetadata): |
|
388 | 45 | $columnList[] = $this->getSelectColumnSQL($fieldName, $subClass); |
|
389 | 45 | break; |
|
390 | |||
391 | 32 | case ($property instanceof ToOneAssociationMetadata && $property->isOwningSide()): |
|
392 | 32 | $targetClass = $this->em->getClassMetadata($property->getTargetEntity()); |
|
393 | |||
394 | 32 | foreach ($property->getJoinColumns() as $joinColumn) { |
|
395 | /** @var JoinColumnMetadata $joinColumn */ |
||
396 | 32 | $referencedColumnName = $joinColumn->getReferencedColumnName(); |
|
397 | |||
398 | 32 | if (! $joinColumn->getType()) { |
|
399 | $joinColumn->setType(PersisterHelper::getTypeOfColumn($referencedColumnName, $targetClass, $this->em)); |
||
400 | } |
||
401 | |||
402 | 32 | $columnList[] = $this->getSelectJoinColumnSQL($joinColumn); |
|
403 | } |
||
404 | |||
405 | 49 | break; |
|
406 | } |
||
407 | } |
||
408 | } |
||
409 | |||
410 | 68 | $this->currentPersisterContext->selectColumnListSql = implode(', ', $columnList); |
|
411 | |||
412 | 68 | return $this->currentPersisterContext->selectColumnListSql; |
|
413 | } |
||
414 | |||
415 | /** |
||
416 | * {@inheritdoc} |
||
417 | */ |
||
418 | 293 | protected function getInsertColumnList() |
|
419 | { |
||
420 | // Identifier columns must always come first in the column list of subclasses. |
||
421 | 293 | $columns = []; |
|
422 | 293 | $parentColumns = $this->class->getParent() |
|
423 | 287 | ? $this->class->getIdentifierColumns($this->em) |
|
424 | 293 | : []; |
|
425 | |||
426 | 293 | foreach ($parentColumns as $columnName => $column) { |
|
427 | 287 | $columns[] = $columnName; |
|
428 | |||
429 | 287 | $this->columns[$columnName] = $column; |
|
430 | } |
||
431 | |||
432 | 293 | foreach ($this->class->getDeclaredPropertiesIterator() as $name => $property) { |
|
433 | 293 | if (($property instanceof FieldMetadata && ($property instanceof VersionFieldMetadata || $this->class->isInheritedProperty($name))) |
|
434 | 293 | || ($property instanceof AssociationMetadata && $this->class->isInheritedProperty($name)) |
|
435 | /*|| isset($this->class->embeddedClasses[$name])*/) { |
||
436 | 291 | continue; |
|
437 | } |
||
438 | |||
439 | 293 | if ($property instanceof AssociationMetadata) { |
|
440 | 264 | if ($property->isOwningSide() && $property instanceof ToOneAssociationMetadata) { |
|
441 | 263 | $targetClass = $this->em->getClassMetadata($property->getTargetEntity()); |
|
442 | |||
443 | 263 | foreach ($property->getJoinColumns() as $joinColumn) { |
|
444 | /** @var JoinColumnMetadata $joinColumn */ |
||
445 | 263 | $columnName = $joinColumn->getColumnName(); |
|
446 | 263 | $referencedColumnName = $joinColumn->getReferencedColumnName(); |
|
447 | |||
448 | 263 | if (! $joinColumn->getType()) { |
|
449 | 12 | $joinColumn->setType(PersisterHelper::getTypeOfColumn($referencedColumnName, $targetClass, $this->em)); |
|
450 | } |
||
451 | |||
452 | 263 | $columns[] = $columnName; |
|
453 | |||
454 | 263 | $this->columns[$columnName] = $joinColumn; |
|
455 | } |
||
456 | } |
||
457 | |||
458 | 264 | continue; |
|
459 | } |
||
460 | |||
461 | 293 | if ($this->class->getClassName() !== $this->class->getRootClassName() |
|
462 | 293 | || ! $this->class->getProperty($name)->hasValueGenerator() |
|
463 | 289 | || $this->class->getProperty($name)->getValueGenerator()->getType() !== GeneratorType::IDENTITY |
|
464 | 293 | || $this->class->identifier[0] !== $name |
|
465 | ) { |
||
466 | 231 | $columnName = $property->getColumnName(); |
|
467 | |||
468 | 231 | $columns[] = $columnName; |
|
469 | |||
470 | 293 | $this->columns[$columnName] = $property; |
|
471 | } |
||
472 | } |
||
473 | |||
474 | // Add discriminator column if it is the topmost class. |
||
475 | 293 | if ($this->class->isRootEntity()) { |
|
476 | 293 | $discrColumn = $this->class->discriminatorColumn; |
|
477 | 293 | $discrColumnName = $discrColumn->getColumnName(); |
|
478 | |||
479 | 293 | $columns[] = $discrColumnName; |
|
480 | |||
481 | 293 | $this->columns[$discrColumnName] = $discrColumn; |
|
482 | } |
||
483 | |||
484 | 293 | return $columns; |
|
485 | } |
||
486 | |||
487 | /** |
||
488 | * @param string $baseTableAlias |
||
489 | * |
||
490 | * @return string |
||
491 | */ |
||
492 | 73 | private function getJoinSql($baseTableAlias) |
|
493 | { |
||
494 | 73 | $joinSql = ''; |
|
495 | 73 | $identifierColumns = $this->class->getIdentifierColumns($this->em); |
|
496 | |||
497 | // INNER JOIN parent tables |
||
498 | 73 | $parentClass = $this->class; |
|
499 | |||
500 | 73 | while (($parentClass = $parentClass->getParent()) !== null) { |
|
501 | 50 | $conditions = []; |
|
502 | 50 | $tableName = $parentClass->table->getQuotedQualifiedName($this->platform); |
|
503 | 50 | $tableAlias = $this->getSQLTableAlias($parentClass->getTableName()); |
|
504 | 50 | $joinSql .= ' INNER JOIN ' . $tableName . ' ' . $tableAlias . ' ON '; |
|
505 | |||
506 | 50 | foreach ($identifierColumns as $idColumn) { |
|
507 | 50 | $quotedColumnName = $this->platform->quoteIdentifier($idColumn->getColumnName()); |
|
508 | |||
509 | 50 | $conditions[] = $baseTableAlias . '.' . $quotedColumnName . ' = ' . $tableAlias . '.' . $quotedColumnName; |
|
510 | } |
||
511 | |||
512 | 50 | $joinSql .= implode(' AND ', $conditions); |
|
513 | } |
||
514 | |||
515 | // OUTER JOIN sub tables |
||
516 | 73 | foreach ($this->class->getSubClasses() as $subClassName) { |
|
517 | 51 | $conditions = []; |
|
518 | 51 | $subClass = $this->em->getClassMetadata($subClassName); |
|
519 | 51 | $tableName = $subClass->table->getQuotedQualifiedName($this->platform); |
|
520 | 51 | $tableAlias = $this->getSQLTableAlias($subClass->getTableName()); |
|
521 | 51 | $joinSql .= ' LEFT JOIN ' . $tableName . ' ' . $tableAlias . ' ON '; |
|
522 | |||
523 | 51 | foreach ($identifierColumns as $idColumn) { |
|
524 | 51 | $quotedColumnName = $this->platform->quoteIdentifier($idColumn->getColumnName()); |
|
525 | |||
526 | 51 | $conditions[] = $baseTableAlias . '.' . $quotedColumnName . ' = ' . $tableAlias . '.' . $quotedColumnName; |
|
527 | } |
||
528 | |||
529 | 51 | $joinSql .= implode(' AND ', $conditions); |
|
530 | } |
||
531 | |||
532 | 73 | return $joinSql; |
|
533 | } |
||
534 | } |
||
535 |
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.