mailpoet
/
vendor-prefixed
/
doctrine
/
orm
/
src
/
Persisters
/
Entity
/
JoinedSubclassPersister.php
AbstractEntityInheritancePersister.php
9 months ago
BasicEntityPersister.php
9 months ago
CachedPersisterContext.php
9 months ago
EntityPersister.php
9 months ago
JoinedSubclassPersister.php
9 months ago
SingleTablePersister.php
9 months ago
index.php
9 months ago
JoinedSubclassPersister.php
408 lines
| 1 | <?php |
| 2 | declare (strict_types=1); |
| 3 | namespace MailPoetVendor\Doctrine\ORM\Persisters\Entity; |
| 4 | if (!defined('ABSPATH')) exit; |
| 5 | use MailPoetVendor\Doctrine\Common\Collections\Criteria; |
| 6 | use MailPoetVendor\Doctrine\DBAL\LockMode; |
| 7 | use MailPoetVendor\Doctrine\DBAL\Types\Type; |
| 8 | use MailPoetVendor\Doctrine\DBAL\Types\Types; |
| 9 | use MailPoetVendor\Doctrine\ORM\Internal\SQLResultCasing; |
| 10 | use MailPoetVendor\Doctrine\ORM\Mapping\ClassMetadata; |
| 11 | use MailPoetVendor\Doctrine\ORM\Utility\LockSqlHelper; |
| 12 | use MailPoetVendor\Doctrine\ORM\Utility\PersisterHelper; |
| 13 | use LengthException; |
| 14 | use function array_combine; |
| 15 | use function array_keys; |
| 16 | use function array_values; |
| 17 | use function implode; |
| 18 | class JoinedSubclassPersister extends AbstractEntityInheritancePersister |
| 19 | { |
| 20 | use LockSqlHelper; |
| 21 | use SQLResultCasing; |
| 22 | private $owningTableMap = []; |
| 23 | private $quotedTableMap = []; |
| 24 | protected function getDiscriminatorColumnTableName() |
| 25 | { |
| 26 | $class = $this->class->name !== $this->class->rootEntityName ? $this->em->getClassMetadata($this->class->rootEntityName) : $this->class; |
| 27 | return $class->getTableName(); |
| 28 | } |
| 29 | private function getVersionedClassMetadata() : ClassMetadata |
| 30 | { |
| 31 | if ($this->class->versionField !== null && isset($this->class->fieldMappings[$this->class->versionField]['inherited'])) { |
| 32 | $definingClassName = $this->class->fieldMappings[$this->class->versionField]['inherited']; |
| 33 | return $this->em->getClassMetadata($definingClassName); |
| 34 | } |
| 35 | return $this->class; |
| 36 | } |
| 37 | public function getOwningTable($fieldName) |
| 38 | { |
| 39 | if (isset($this->owningTableMap[$fieldName])) { |
| 40 | return $this->owningTableMap[$fieldName]; |
| 41 | } |
| 42 | switch (\true) { |
| 43 | case isset($this->class->associationMappings[$fieldName]['inherited']): |
| 44 | $cm = $this->em->getClassMetadata($this->class->associationMappings[$fieldName]['inherited']); |
| 45 | break; |
| 46 | case isset($this->class->fieldMappings[$fieldName]['inherited']): |
| 47 | $cm = $this->em->getClassMetadata($this->class->fieldMappings[$fieldName]['inherited']); |
| 48 | break; |
| 49 | default: |
| 50 | $cm = $this->class; |
| 51 | break; |
| 52 | } |
| 53 | $tableName = $cm->getTableName(); |
| 54 | $quotedTableName = $this->quoteStrategy->getTableName($cm, $this->platform); |
| 55 | $this->owningTableMap[$fieldName] = $tableName; |
| 56 | $this->quotedTableMap[$tableName] = $quotedTableName; |
| 57 | return $tableName; |
| 58 | } |
| 59 | public function executeInserts() |
| 60 | { |
| 61 | if (!$this->queuedInserts) { |
| 62 | return; |
| 63 | } |
| 64 | $uow = $this->em->getUnitOfWork(); |
| 65 | $idGenerator = $this->class->idGenerator; |
| 66 | $isPostInsertId = $idGenerator->isPostInsertGenerator(); |
| 67 | $rootClass = $this->class->name !== $this->class->rootEntityName ? $this->em->getClassMetadata($this->class->rootEntityName) : $this->class; |
| 68 | // Prepare statement for the root table |
| 69 | $rootPersister = $this->em->getUnitOfWork()->getEntityPersister($rootClass->name); |
| 70 | $rootTableName = $rootClass->getTableName(); |
| 71 | $rootTableStmt = $this->conn->prepare($rootPersister->getInsertSQL()); |
| 72 | // Prepare statements for sub tables. |
| 73 | $subTableStmts = []; |
| 74 | if ($rootClass !== $this->class) { |
| 75 | $subTableStmts[$this->class->getTableName()] = $this->conn->prepare($this->getInsertSQL()); |
| 76 | } |
| 77 | foreach ($this->class->parentClasses as $parentClassName) { |
| 78 | $parentClass = $this->em->getClassMetadata($parentClassName); |
| 79 | $parentTableName = $parentClass->getTableName(); |
| 80 | if ($parentClass !== $rootClass) { |
| 81 | $parentPersister = $this->em->getUnitOfWork()->getEntityPersister($parentClassName); |
| 82 | $subTableStmts[$parentTableName] = $this->conn->prepare($parentPersister->getInsertSQL()); |
| 83 | } |
| 84 | } |
| 85 | // Execute all inserts. For each entity: |
| 86 | // 1) Insert on root table |
| 87 | // 2) Insert on sub tables |
| 88 | foreach ($this->queuedInserts as $key => $entity) { |
| 89 | $insertData = $this->prepareInsertData($entity); |
| 90 | // Execute insert on root table |
| 91 | $paramIndex = 1; |
| 92 | foreach ($insertData[$rootTableName] as $columnName => $value) { |
| 93 | $rootTableStmt->bindValue($paramIndex++, $value, $this->columnTypes[$columnName]); |
| 94 | } |
| 95 | $rootTableStmt->executeStatement(); |
| 96 | if ($isPostInsertId) { |
| 97 | $generatedId = $idGenerator->generateId($this->em, $entity); |
| 98 | $id = [$this->class->identifier[0] => $generatedId]; |
| 99 | $uow->assignPostInsertId($entity, $generatedId); |
| 100 | } else { |
| 101 | $id = $this->em->getUnitOfWork()->getEntityIdentifier($entity); |
| 102 | } |
| 103 | // Execute inserts on subtables. |
| 104 | // The order doesn't matter because all child tables link to the root table via FK. |
| 105 | foreach ($subTableStmts as $tableName => $stmt) { |
| 106 | $paramIndex = 1; |
| 107 | $data = $insertData[$tableName] ?? []; |
| 108 | foreach ($id as $idName => $idVal) { |
| 109 | $type = $this->columnTypes[$idName] ?? Types::STRING; |
| 110 | $stmt->bindValue($paramIndex++, $idVal, $type); |
| 111 | } |
| 112 | foreach ($data as $columnName => $value) { |
| 113 | if (!isset($id[$columnName])) { |
| 114 | $stmt->bindValue($paramIndex++, $value, $this->columnTypes[$columnName]); |
| 115 | } |
| 116 | } |
| 117 | $stmt->executeStatement(); |
| 118 | } |
| 119 | if ($this->class->requiresFetchAfterChange) { |
| 120 | $this->assignDefaultVersionAndUpsertableValues($entity, $id); |
| 121 | } |
| 122 | // Unset this queued insert, so that the prepareUpdateData() method (called via prepareInsertData() method) |
| 123 | // knows right away (for the next entity already) that the current entity has been written to the database |
| 124 | // and no extra updates need to be scheduled to refer to it. |
| 125 | // |
| 126 | // In \MailPoetVendor\Doctrine\ORM\UnitOfWork::executeInserts(), the UoW already removed entities |
| 127 | // from its own list (\Doctrine\ORM\UnitOfWork::$entityInsertions) right after they |
| 128 | // were given to our addInsert() method. |
| 129 | unset($this->queuedInserts[$key]); |
| 130 | } |
| 131 | } |
| 132 | public function update($entity) |
| 133 | { |
| 134 | $updateData = $this->prepareUpdateData($entity); |
| 135 | if (!$updateData) { |
| 136 | return; |
| 137 | } |
| 138 | $isVersioned = $this->class->isVersioned; |
| 139 | $versionedClass = $this->getVersionedClassMetadata(); |
| 140 | $versionedTable = $versionedClass->getTableName(); |
| 141 | foreach ($updateData as $tableName => $data) { |
| 142 | $tableName = $this->quotedTableMap[$tableName]; |
| 143 | $versioned = $isVersioned && $versionedTable === $tableName; |
| 144 | $this->updateTable($entity, $tableName, $data, $versioned); |
| 145 | } |
| 146 | if ($this->class->requiresFetchAfterChange) { |
| 147 | // Make sure the table with the version column is updated even if no columns on that |
| 148 | // table were affected. |
| 149 | if ($isVersioned && !isset($updateData[$versionedTable])) { |
| 150 | $tableName = $this->quoteStrategy->getTableName($versionedClass, $this->platform); |
| 151 | $this->updateTable($entity, $tableName, [], \true); |
| 152 | } |
| 153 | $identifiers = $this->em->getUnitOfWork()->getEntityIdentifier($entity); |
| 154 | $this->assignDefaultVersionAndUpsertableValues($entity, $identifiers); |
| 155 | } |
| 156 | } |
| 157 | public function delete($entity) |
| 158 | { |
| 159 | $identifier = $this->em->getUnitOfWork()->getEntityIdentifier($entity); |
| 160 | $id = array_combine($this->class->getIdentifierColumnNames(), $identifier); |
| 161 | $types = $this->getClassIdentifiersTypes($this->class); |
| 162 | $this->deleteJoinTableRecords($identifier, $types); |
| 163 | // If the database platform supports FKs, just |
| 164 | // delete the row from the root table. Cascades do the rest. |
| 165 | // @phpstan-ignore method.deprecated |
| 166 | if ($this->platform->supportsForeignKeyConstraints()) { |
| 167 | $rootClass = $this->em->getClassMetadata($this->class->rootEntityName); |
| 168 | $rootTable = $this->quoteStrategy->getTableName($rootClass, $this->platform); |
| 169 | $rootTypes = $this->getClassIdentifiersTypes($rootClass); |
| 170 | return (bool) $this->conn->delete($rootTable, $id, $rootTypes); |
| 171 | } |
| 172 | // Delete from all tables individually, starting from this class' table up to the root table. |
| 173 | $rootTable = $this->quoteStrategy->getTableName($this->class, $this->platform); |
| 174 | $rootTypes = $this->getClassIdentifiersTypes($this->class); |
| 175 | $affectedRows = $this->conn->delete($rootTable, $id, $rootTypes); |
| 176 | foreach ($this->class->parentClasses as $parentClass) { |
| 177 | $parentMetadata = $this->em->getClassMetadata($parentClass); |
| 178 | $parentTable = $this->quoteStrategy->getTableName($parentMetadata, $this->platform); |
| 179 | $parentTypes = $this->getClassIdentifiersTypes($parentMetadata); |
| 180 | $this->conn->delete($parentTable, $id, $parentTypes); |
| 181 | } |
| 182 | return (bool) $affectedRows; |
| 183 | } |
| 184 | public function getSelectSQL($criteria, $assoc = null, $lockMode = null, $limit = null, $offset = null, ?array $orderBy = null) |
| 185 | { |
| 186 | $this->switchPersisterContext($offset, $limit); |
| 187 | $baseTableAlias = $this->getSQLTableAlias($this->class->name); |
| 188 | $joinSql = $this->getJoinSql($baseTableAlias); |
| 189 | if ($assoc !== null && $assoc['type'] === ClassMetadata::MANY_TO_MANY) { |
| 190 | $joinSql .= $this->getSelectManyToManyJoinSQL($assoc); |
| 191 | } |
| 192 | $conditionSql = $criteria instanceof Criteria ? $this->getSelectConditionCriteriaSQL($criteria) : $this->getSelectConditionSQL($criteria, $assoc); |
| 193 | $filterSql = $this->generateFilterConditionSQL($this->em->getClassMetadata($this->class->rootEntityName), $this->getSQLTableAlias($this->class->rootEntityName)); |
| 194 | // If the current class in the root entity, add the filters |
| 195 | if ($filterSql) { |
| 196 | $conditionSql .= $conditionSql ? ' AND ' . $filterSql : $filterSql; |
| 197 | } |
| 198 | $orderBySql = ''; |
| 199 | if ($assoc !== null && isset($assoc['orderBy'])) { |
| 200 | $orderBy = $assoc['orderBy']; |
| 201 | } |
| 202 | if ($orderBy) { |
| 203 | $orderBySql = $this->getOrderBySQL($orderBy, $baseTableAlias); |
| 204 | } |
| 205 | $lockSql = ''; |
| 206 | switch ($lockMode) { |
| 207 | case LockMode::PESSIMISTIC_READ: |
| 208 | $lockSql = ' ' . $this->getReadLockSQL($this->platform); |
| 209 | break; |
| 210 | case LockMode::PESSIMISTIC_WRITE: |
| 211 | $lockSql = ' ' . $this->getWriteLockSQL($this->platform); |
| 212 | break; |
| 213 | } |
| 214 | $tableName = $this->quoteStrategy->getTableName($this->class, $this->platform); |
| 215 | $from = ' FROM ' . $tableName . ' ' . $baseTableAlias; |
| 216 | $where = $conditionSql !== '' ? ' WHERE ' . $conditionSql : ''; |
| 217 | $lock = $this->platform->appendLockHint($from, $lockMode ?? LockMode::NONE); |
| 218 | $columnList = $this->getSelectColumnsSQL(); |
| 219 | $query = 'SELECT ' . $columnList . $lock . $joinSql . $where . $orderBySql; |
| 220 | return $this->platform->modifyLimitQuery($query, $limit, $offset ?? 0) . $lockSql; |
| 221 | } |
| 222 | public function getCountSQL($criteria = []) |
| 223 | { |
| 224 | $tableName = $this->quoteStrategy->getTableName($this->class, $this->platform); |
| 225 | $baseTableAlias = $this->getSQLTableAlias($this->class->name); |
| 226 | $joinSql = $this->getJoinSql($baseTableAlias); |
| 227 | $conditionSql = $criteria instanceof Criteria ? $this->getSelectConditionCriteriaSQL($criteria) : $this->getSelectConditionSQL($criteria); |
| 228 | $filterSql = $this->generateFilterConditionSQL($this->em->getClassMetadata($this->class->rootEntityName), $this->getSQLTableAlias($this->class->rootEntityName)); |
| 229 | if ($filterSql !== '') { |
| 230 | $conditionSql = $conditionSql ? $conditionSql . ' AND ' . $filterSql : $filterSql; |
| 231 | } |
| 232 | return 'SELECT COUNT(*) ' . 'FROM ' . $tableName . ' ' . $baseTableAlias . $joinSql . (empty($conditionSql) ? '' : ' WHERE ' . $conditionSql); |
| 233 | } |
| 234 | protected function getLockTablesSql($lockMode) |
| 235 | { |
| 236 | $joinSql = ''; |
| 237 | $identifierColumns = $this->class->getIdentifierColumnNames(); |
| 238 | $baseTableAlias = $this->getSQLTableAlias($this->class->name); |
| 239 | // INNER JOIN parent tables |
| 240 | foreach ($this->class->parentClasses as $parentClassName) { |
| 241 | $conditions = []; |
| 242 | $tableAlias = $this->getSQLTableAlias($parentClassName); |
| 243 | $parentClass = $this->em->getClassMetadata($parentClassName); |
| 244 | $joinSql .= ' INNER JOIN ' . $this->quoteStrategy->getTableName($parentClass, $this->platform) . ' ' . $tableAlias . ' ON '; |
| 245 | foreach ($identifierColumns as $idColumn) { |
| 246 | $conditions[] = $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn; |
| 247 | } |
| 248 | $joinSql .= implode(' AND ', $conditions); |
| 249 | } |
| 250 | return parent::getLockTablesSql($lockMode) . $joinSql; |
| 251 | } |
| 252 | protected function getSelectColumnsSQL() |
| 253 | { |
| 254 | // Create the column list fragment only once |
| 255 | if ($this->currentPersisterContext->selectColumnListSql !== null && $this->isFilterHashUpToDate()) { |
| 256 | return $this->currentPersisterContext->selectColumnListSql; |
| 257 | } |
| 258 | $columnList = []; |
| 259 | $discrColumn = $this->class->getDiscriminatorColumn(); |
| 260 | $discrColumnName = $discrColumn['name']; |
| 261 | $discrColumnType = $discrColumn['type']; |
| 262 | $baseTableAlias = $this->getSQLTableAlias($this->class->name); |
| 263 | $resultColumnName = $this->getSQLResultCasing($this->platform, $discrColumnName); |
| 264 | $this->currentPersisterContext->rsm->addEntityResult($this->class->name, 'r'); |
| 265 | $this->currentPersisterContext->rsm->setDiscriminatorColumn('r', $resultColumnName); |
| 266 | $this->currentPersisterContext->rsm->addMetaResult('r', $resultColumnName, $discrColumnName, \false, $discrColumnType); |
| 267 | // Add regular columns |
| 268 | foreach ($this->class->fieldMappings as $fieldName => $mapping) { |
| 269 | $class = isset($mapping['inherited']) ? $this->em->getClassMetadata($mapping['inherited']) : $this->class; |
| 270 | $columnList[] = $this->getSelectColumnSQL($fieldName, $class); |
| 271 | } |
| 272 | // Add foreign key columns |
| 273 | foreach ($this->class->associationMappings as $mapping) { |
| 274 | if (!$mapping['isOwningSide'] || !($mapping['type'] & ClassMetadata::TO_ONE)) { |
| 275 | continue; |
| 276 | } |
| 277 | $tableAlias = isset($mapping['inherited']) ? $this->getSQLTableAlias($mapping['inherited']) : $baseTableAlias; |
| 278 | $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); |
| 279 | foreach ($mapping['joinColumns'] as $joinColumn) { |
| 280 | $columnList[] = $this->getSelectJoinColumnSQL($tableAlias, $joinColumn['name'], $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform), PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass, $this->em)); |
| 281 | } |
| 282 | } |
| 283 | // Add discriminator column (DO NOT ALIAS, see AbstractEntityInheritancePersister#processSQLResult). |
| 284 | $tableAlias = $this->class->rootEntityName === $this->class->name ? $baseTableAlias : $this->getSQLTableAlias($this->class->rootEntityName); |
| 285 | $columnList[] = $tableAlias . '.' . $discrColumnName; |
| 286 | // sub tables |
| 287 | foreach ($this->class->subClasses as $subClassName) { |
| 288 | $subClass = $this->em->getClassMetadata($subClassName); |
| 289 | $tableAlias = $this->getSQLTableAlias($subClassName); |
| 290 | // Add subclass columns |
| 291 | foreach ($subClass->fieldMappings as $fieldName => $mapping) { |
| 292 | if (isset($mapping['inherited'])) { |
| 293 | continue; |
| 294 | } |
| 295 | $columnList[] = $this->getSelectColumnSQL($fieldName, $subClass); |
| 296 | } |
| 297 | // Add join columns (foreign keys) |
| 298 | foreach ($subClass->associationMappings as $mapping) { |
| 299 | if (!$mapping['isOwningSide'] || !($mapping['type'] & ClassMetadata::TO_ONE) || isset($mapping['inherited'])) { |
| 300 | continue; |
| 301 | } |
| 302 | $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); |
| 303 | foreach ($mapping['joinColumns'] as $joinColumn) { |
| 304 | $columnList[] = $this->getSelectJoinColumnSQL($tableAlias, $joinColumn['name'], $this->quoteStrategy->getJoinColumnName($joinColumn, $subClass, $this->platform), PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass, $this->em)); |
| 305 | } |
| 306 | } |
| 307 | } |
| 308 | $this->currentPersisterContext->selectColumnListSql = implode(', ', $columnList); |
| 309 | $this->updateFilterHash(); |
| 310 | return $this->currentPersisterContext->selectColumnListSql; |
| 311 | } |
| 312 | protected function getInsertColumnList() |
| 313 | { |
| 314 | // Identifier columns must always come first in the column list of subclasses. |
| 315 | $columns = $this->class->parentClasses ? $this->class->getIdentifierColumnNames() : []; |
| 316 | foreach ($this->class->reflFields as $name => $field) { |
| 317 | if (isset($this->class->fieldMappings[$name]['inherited']) && !isset($this->class->fieldMappings[$name]['id']) || isset($this->class->associationMappings[$name]['inherited']) || $this->class->isVersioned && $this->class->versionField === $name || isset($this->class->embeddedClasses[$name]) || isset($this->class->fieldMappings[$name]['notInsertable'])) { |
| 318 | continue; |
| 319 | } |
| 320 | if (isset($this->class->associationMappings[$name])) { |
| 321 | $assoc = $this->class->associationMappings[$name]; |
| 322 | if ($assoc['type'] & ClassMetadata::TO_ONE && $assoc['isOwningSide']) { |
| 323 | foreach ($assoc['targetToSourceKeyColumns'] as $sourceCol) { |
| 324 | $columns[] = $sourceCol; |
| 325 | } |
| 326 | } |
| 327 | } elseif ($this->class->name !== $this->class->rootEntityName || !$this->class->isIdGeneratorIdentity() || $this->class->identifier[0] !== $name) { |
| 328 | $columns[] = $this->quoteStrategy->getColumnName($name, $this->class, $this->platform); |
| 329 | $this->columnTypes[$name] = $this->class->fieldMappings[$name]['type']; |
| 330 | } |
| 331 | } |
| 332 | // Add discriminator column if it is the topmost class. |
| 333 | if ($this->class->name === $this->class->rootEntityName) { |
| 334 | $columns[] = $this->class->getDiscriminatorColumn()['name']; |
| 335 | } |
| 336 | return $columns; |
| 337 | } |
| 338 | protected function assignDefaultVersionAndUpsertableValues($entity, array $id) |
| 339 | { |
| 340 | $values = $this->fetchVersionAndNotUpsertableValues($this->getVersionedClassMetadata(), $id); |
| 341 | foreach ($values as $field => $value) { |
| 342 | $value = Type::getType($this->class->fieldMappings[$field]['type'])->convertToPHPValue($value, $this->platform); |
| 343 | $this->class->setFieldValue($entity, $field, $value); |
| 344 | } |
| 345 | } |
| 346 | protected function fetchVersionAndNotUpsertableValues($versionedClass, array $id) |
| 347 | { |
| 348 | $columnNames = []; |
| 349 | foreach ($this->class->fieldMappings as $key => $column) { |
| 350 | $class = null; |
| 351 | if ($this->class->isVersioned && $key === $versionedClass->versionField) { |
| 352 | $class = $versionedClass; |
| 353 | } elseif (isset($column['generated'])) { |
| 354 | $class = isset($column['inherited']) ? $this->em->getClassMetadata($column['inherited']) : $this->class; |
| 355 | } else { |
| 356 | continue; |
| 357 | } |
| 358 | $columnNames[$key] = $this->getSelectColumnSQL($key, $class); |
| 359 | } |
| 360 | $tableName = $this->quoteStrategy->getTableName($versionedClass, $this->platform); |
| 361 | $baseTableAlias = $this->getSQLTableAlias($this->class->name); |
| 362 | $joinSql = $this->getJoinSql($baseTableAlias); |
| 363 | $identifier = $this->quoteStrategy->getIdentifierColumnNames($versionedClass, $this->platform); |
| 364 | foreach ($identifier as $i => $idValue) { |
| 365 | $identifier[$i] = $baseTableAlias . '.' . $idValue; |
| 366 | } |
| 367 | $sql = 'SELECT ' . implode(', ', $columnNames) . ' FROM ' . $tableName . ' ' . $baseTableAlias . $joinSql . ' WHERE ' . implode(' = ? AND ', $identifier) . ' = ?'; |
| 368 | $flatId = $this->identifierFlattener->flattenIdentifier($versionedClass, $id); |
| 369 | $values = $this->conn->fetchNumeric($sql, array_values($flatId), $this->extractIdentifierTypes($id, $versionedClass)); |
| 370 | if ($values === \false) { |
| 371 | throw new LengthException('Unexpected empty result for database query.'); |
| 372 | } |
| 373 | $values = array_combine(array_keys($columnNames), $values); |
| 374 | if (!$values) { |
| 375 | throw new LengthException('Unexpected number of database columns.'); |
| 376 | } |
| 377 | return $values; |
| 378 | } |
| 379 | private function getJoinSql(string $baseTableAlias) : string |
| 380 | { |
| 381 | $joinSql = ''; |
| 382 | $identifierColumn = $this->class->getIdentifierColumnNames(); |
| 383 | // INNER JOIN parent tables |
| 384 | foreach ($this->class->parentClasses as $parentClassName) { |
| 385 | $conditions = []; |
| 386 | $parentClass = $this->em->getClassMetadata($parentClassName); |
| 387 | $tableAlias = $this->getSQLTableAlias($parentClassName); |
| 388 | $joinSql .= ' INNER JOIN ' . $this->quoteStrategy->getTableName($parentClass, $this->platform) . ' ' . $tableAlias . ' ON '; |
| 389 | foreach ($identifierColumn as $idColumn) { |
| 390 | $conditions[] = $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn; |
| 391 | } |
| 392 | $joinSql .= implode(' AND ', $conditions); |
| 393 | } |
| 394 | // OUTER JOIN sub tables |
| 395 | foreach ($this->class->subClasses as $subClassName) { |
| 396 | $conditions = []; |
| 397 | $subClass = $this->em->getClassMetadata($subClassName); |
| 398 | $tableAlias = $this->getSQLTableAlias($subClassName); |
| 399 | $joinSql .= ' LEFT JOIN ' . $this->quoteStrategy->getTableName($subClass, $this->platform) . ' ' . $tableAlias . ' ON '; |
| 400 | foreach ($identifierColumn as $idColumn) { |
| 401 | $conditions[] = $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn; |
| 402 | } |
| 403 | $joinSql .= implode(' AND ', $conditions); |
| 404 | } |
| 405 | return $joinSql; |
| 406 | } |
| 407 | } |
| 408 |