From e3d133af045815167bdbb0e47a496cc49c86fc23 Mon Sep 17 00:00:00 2001 From: Guilherme Blanco Date: Wed, 7 Sep 2011 01:48:19 -0300 Subject: [PATCH 1/2] Added getQuotedTableName() in missing places of Doctrine ORM. Fixes DDC-1365. --- .../ORM/Mapping/ClassMetadataFactory.php | 2 +- .../ORM/Mapping/ClassMetadataInfo.php | 19 +- .../ORM/Persisters/BasicEntityPersister.php | 229 +++++++++++------- .../Persisters/JoinedSubclassPersister.php | 121 +++++---- .../ORM/Persisters/ManyToManyPersister.php | 95 ++++---- .../ORM/Persisters/SingleTablePersister.php | 25 +- .../ORM/Query/AST/Functions/SizeFunction.php | 6 +- .../Query/Exec/MultiTableDeleteExecutor.php | 2 +- .../Query/Exec/MultiTableUpdateExecutor.php | 2 +- lib/Doctrine/ORM/Query/SqlWalker.php | 145 +++++------ .../ORM/Tools/ConvertDoctrine1Schema.php | 1 + lib/Doctrine/ORM/Tools/EntityGenerator.php | 7 +- .../ORM/Tools/Export/Driver/YamlExporter.php | 2 + lib/Doctrine/ORM/UnitOfWork.php | 2 + 14 files changed, 378 insertions(+), 280 deletions(-) diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php index 2044932d6..796b941b4 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php @@ -448,7 +448,7 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface // __seq in PostgreSQL for SERIAL columns. // Not pretty but necessary and the simplest solution that currently works. $seqName = $this->targetPlatform instanceof Platforms\PostgreSQLPlatform ? - $class->table['name'] . '_' . $class->columnNames[$class->identifier[0]] . '_seq' : + $class->getTableName() . '_' . $class->columnNames[$class->identifier[0]] . '_seq' : null; $class->setIdGenerator(new \Doctrine\ORM\Id\IdentityGenerator($seqName)); break; diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php index 8bdf42305..17255cb23 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php @@ -1270,7 +1270,7 @@ class ClassMetadataInfo implements ClassMetadata public function getTemporaryIdTableName() { // replace dots with underscores because PostgreSQL creates temporary tables in a special schema - return str_replace('.', '_', $this->table['name'] . '_id_tmp'); + return str_replace('.', '_', $this->getTableName() . '_id_tmp'); } /** @@ -1369,9 +1369,11 @@ class ClassMetadataInfo implements ClassMetadata $this->table['name'] = $table['name']; } } + if (isset($table['indexes'])) { $this->table['indexes'] = $table['indexes']; } + if (isset($table['uniqueConstraints'])) { $this->table['uniqueConstraints'] = $table['uniqueConstraints']; } @@ -1885,9 +1887,10 @@ class ClassMetadataInfo implements ClassMetadata */ public function getAssociationTargetClass($assocName) { - if (!isset($this->associationMappings[$assocName])) { + if ( ! isset($this->associationMappings[$assocName])) { throw new \InvalidArgumentException("Association name expected, '" . $assocName ."' is not an association."); } + return $this->associationMappings[$assocName]['targetEntity']; } @@ -1911,9 +1914,7 @@ class ClassMetadataInfo implements ClassMetadata */ public function getQuotedColumnName($field, $platform) { - return isset($this->fieldMappings[$field]['quoted']) ? - $platform->quoteIdentifier($this->fieldMappings[$field]['columnName']) : - $this->fieldMappings[$field]['columnName']; + return isset($this->fieldMappings[$field]['quoted']) ? $platform->quoteIdentifier($this->fieldMappings[$field]['columnName']) : $this->fieldMappings[$field]['columnName']; } /** @@ -1925,9 +1926,7 @@ class ClassMetadataInfo implements ClassMetadata */ public function getQuotedTableName($platform) { - return isset($this->table['quoted']) ? - $platform->quoteIdentifier($this->table['name']) : - $this->table['name']; + return isset($this->table['quoted']) ? $platform->quoteIdentifier($this->table['name']) : $this->table['name']; } /** @@ -1938,8 +1937,6 @@ class ClassMetadataInfo implements ClassMetadata */ public function getQuotedJoinTableName(array $assoc, $platform) { - return isset($assoc['joinTable']['quoted']) - ? $platform->quoteIdentifier($assoc['joinTable']['name']) - : $assoc['joinTable']['name']; + return isset($assoc['joinTable']['quoted']) ? $platform->quoteIdentifier($assoc['joinTable']['name']) : $assoc['joinTable']['name']; } } diff --git a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php index 590df6894..a093f9a09 100644 --- a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php @@ -222,7 +222,7 @@ class BasicEntityPersister $isPostInsertId = $idGen->isPostInsertGenerator(); $stmt = $this->_conn->prepare($this->_getInsertSQL()); - $tableName = $this->_class->table['name']; + $tableName = $this->_class->getTableName(); foreach ($this->_queuedInserts as $entity) { $insertData = $this->_prepareInsertData($entity); @@ -279,11 +279,14 @@ class BasicEntityPersister protected function fetchVersionValue($versionedClass, $id) { $versionField = $versionedClass->versionField; - $identifier = $versionedClass->getIdentifierColumnNames(); - $versionFieldColumnName = $versionedClass->getColumnName($versionField); + $identifier = $versionedClass->getIdentifierColumnNames(); + + $versionFieldColumnName = $versionedClass->getQuotedColumnName($versionField, $this->_platform); + //FIXME: Order with composite keys might not be correct - $sql = "SELECT " . $versionFieldColumnName . " FROM " . $versionedClass->getQuotedTableName($this->_platform) - . " WHERE " . implode(' = ? AND ', $identifier) . " = ?"; + $sql = 'SELECT ' . $versionFieldColumnName + . ' FROM ' . $versionedClass->getQuotedTableName($this->_platform) + . ' WHERE ' . implode(' = ? AND ', $identifier) . ' = ?'; $value = $this->_conn->fetchColumn($sql, array_values((array)$id)); return Type::getType($versionedClass->fieldMappings[$versionField]['type'])->convertToPHPValue($value, $this->_platform); @@ -306,7 +309,8 @@ class BasicEntityPersister public function update($entity) { $updateData = $this->_prepareUpdateData($entity); - $tableName = $this->_class->table['name']; + $tableName = $this->_class->getTableName(); + if (isset($updateData[$tableName]) && $updateData[$tableName]) { $this->_updateTable( $entity, $this->_class->getQuotedTableName($this->_platform), @@ -334,17 +338,17 @@ class BasicEntityPersister $set = $params = $types = array(); foreach ($updateData as $columnName => $value) { - if (isset($this->_class->fieldNames[$columnName])) { - $set[] = $this->_class->getQuotedColumnName($this->_class->fieldNames[$columnName], $this->_platform) . ' = ?'; - } else { - $set[] = $columnName . ' = ?'; - } + $set[] = (isset($this->_class->fieldNames[$columnName])) + ? $this->_class->getQuotedColumnName($this->_class->fieldNames[$columnName], $this->_platform) . ' = ?' + : $columnName . ' = ?'; + $params[] = $value; $types[] = $this->_columnTypes[$columnName]; } $where = array(); $id = $this->_em->getUnitOfWork()->getEntityIdentifier($entity); + foreach ($this->_class->identifier as $idField) { if (isset($this->_class->associationMappings[$idField])) { $targetMapping = $this->_em->getClassMetadata($this->_class->associationMappings[$idField]['targetEntity']); @@ -362,18 +366,21 @@ class BasicEntityPersister $versionField = $this->_class->versionField; $versionFieldType = $this->_class->fieldMappings[$versionField]['type']; $versionColumn = $this->_class->getQuotedColumnName($versionField, $this->_platform); + if ($versionFieldType == Type::INTEGER) { $set[] = $versionColumn . ' = ' . $versionColumn . ' + 1'; } else if ($versionFieldType == Type::DATETIME) { $set[] = $versionColumn . ' = CURRENT_TIMESTAMP'; } + $where[] = $versionColumn; $params[] = $this->_class->reflFields[$versionField]->getValue($entity); $types[] = $this->_class->fieldMappings[$versionField]['type']; } - $sql = "UPDATE $quotedTableName SET " . implode(', ', $set) - . ' WHERE ' . implode(' = ? AND ', $where) . ' = ?'; + $sql = 'UPDATE ' . $quotedTableName + . ' SET ' . implode(', ', $set) + . ' WHERE ' . implode(' = ? AND ', $where) . ' = ?'; $result = $this->_conn->executeUpdate($sql, $params, $types); @@ -399,21 +406,29 @@ class BasicEntityPersister $relatedClass = $this->_em->getClassMetadata($mapping['targetEntity']); $mapping = $relatedClass->associationMappings[$mapping['mappedBy']]; $keys = array_keys($mapping['relationToTargetKeyColumns']); + if ($selfReferential) { $otherKeys = array_keys($mapping['relationToSourceKeyColumns']); } } else { $keys = array_keys($mapping['relationToSourceKeyColumns']); + if ($selfReferential) { $otherKeys = array_keys($mapping['relationToTargetKeyColumns']); } } if ( ! isset($mapping['isOnDeleteCascade'])) { - $this->_conn->delete($mapping['joinTable']['name'], array_combine($keys, $identifier)); + $this->_conn->delete( + $this->_class->getQuotedJoinTableName($mapping, $this->_platform), + array_combine($keys, $identifier) + ); if ($selfReferential) { - $this->_conn->delete($mapping['joinTable']['name'], array_combine($otherKeys, $identifier)); + $this->_conn->delete( + $this->_class->getQuotedJoinTableName($mapping, $this->_platform), + array_combine($otherKeys, $identifier) + ); } } } @@ -464,7 +479,7 @@ class BasicEntityPersister $result = array(); $uow = $this->_em->getUnitOfWork(); - if ($versioned = $this->_class->isVersioned) { + if (($versioned = $this->_class->isVersioned) != false) { $versionField = $this->_class->versionField; } @@ -478,6 +493,7 @@ class BasicEntityPersister if (isset($this->_class->associationMappings[$field])) { $assoc = $this->_class->associationMappings[$field]; + // Only owning side of x-1 associations can have a FK column. if ( ! $assoc['isOwningSide'] || ! ($assoc['type'] & ClassMetadata::TO_ONE)) { continue; @@ -485,6 +501,7 @@ class BasicEntityPersister if ($newVal !== null) { $oid = spl_object_hash($newVal); + if (isset($this->_queuedInserts[$oid]) || $uow->isScheduledForInsert($newVal)) { // The associated entity $newVal is not yet persisted, so we must // set $newVal = null, in order to insert a null value and schedule an @@ -511,6 +528,7 @@ class BasicEntityPersister } else { $result[$owningTable][$sourceColumn] = $newValId[$targetClass->fieldNames[$targetColumn]]; } + $this->_columnTypes[$sourceColumn] = $targetClass->getTypeOfColumn($targetColumn); } } else { @@ -519,6 +537,7 @@ class BasicEntityPersister $result[$this->getOwningTable($field)][$columnName] = $newVal; } } + return $result; } @@ -549,7 +568,7 @@ class BasicEntityPersister */ public function getOwningTable($fieldName) { - return $this->_class->table['name']; + return $this->_class->getTableName(); } /** @@ -575,12 +594,9 @@ class BasicEntityPersister $hints[Query::HINT_REFRESH_ENTITY] = $entity; } - if ($this->_selectJoinSql) { - $hydrator = $this->_em->newHydrator(Query::HYDRATE_OBJECT); - } else { - $hydrator = $this->_em->newHydrator(Query::HYDRATE_SIMPLEOBJECT); - } + $hydrator = $this->_em->newHydrator($this->_selectJoinSql ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT); $entities = $hydrator->hydrateAll($stmt, $this->_rsm, $hints); + return $entities ? $entities[0] : null; } @@ -597,7 +613,7 @@ class BasicEntityPersister */ public function loadOneToOneEntity(array $assoc, $sourceEntity, array $identifier = array()) { - if ($foundEntity = $this->_em->getUnitOfWork()->tryGetById($identifier, $assoc['targetEntity'])) { + if (($foundEntity = $this->_em->getUnitOfWork()->tryGetById($identifier, $assoc['targetEntity'])) != false) { return $foundEntity; } @@ -609,14 +625,17 @@ class BasicEntityPersister // Mark inverse side as fetched in the hints, otherwise the UoW would // try to load it in a separate query (remember: to-one inverse sides can not be lazy). $hints = array(); + if ($isInverseSingleValued) { $hints['fetched'][$targetClass->name][$assoc['inversedBy']] = true; + if ($targetClass->subClasses) { foreach ($targetClass->subClasses as $targetSubclassName) { $hints['fetched'][$targetSubclassName][$assoc['inversedBy']] = true; } } } + /* cascade read-only status if ($this->_em->getUnitOfWork()->isReadOnly($sourceEntity)) { $hints[Query::HINT_READ_ONLY] = true; @@ -632,19 +651,21 @@ class BasicEntityPersister } else { $sourceClass = $this->_em->getClassMetadata($assoc['sourceEntity']); $owningAssoc = $targetClass->getAssociationMapping($assoc['mappedBy']); + // TRICKY: since the association is specular source and target are flipped foreach ($owningAssoc['targetToSourceKeyColumns'] as $sourceKeyColumn => $targetKeyColumn) { - if (isset($sourceClass->fieldNames[$sourceKeyColumn])) { - // unset the old value and set the new sql aliased value here. By definition - // unset($identifier[$targetKeyColumn] works here with how UnitOfWork::createEntity() calls this method. - $identifier[$this->_getSQLTableAlias($targetClass->name) . "." . $targetKeyColumn] = - $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity); - unset($identifier[$targetKeyColumn]); - } else { + if ( ! isset($sourceClass->fieldNames[$sourceKeyColumn])) { throw MappingException::joinColumnMustPointToMappedField( $sourceClass->name, $sourceKeyColumn ); } + + // unset the old value and set the new sql aliased value here. By definition + // unset($identifier[$targetKeyColumn] works here with how UnitOfWork::createEntity() calls this method. + $identifier[$this->_getSQLTableAlias($targetClass->name) . "." . $targetKeyColumn] = + $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity); + + unset($identifier[$targetKeyColumn]); } $targetEntity = $this->load($identifier, null, $assoc); @@ -676,7 +697,9 @@ class BasicEntityPersister if (isset($this->_class->lifecycleCallbacks[Events::postLoad])) { $this->_class->invokeLifecycleCallbacks(Events::postLoad, $entity); } + $evm = $this->_em->getEventManager(); + if ($evm->hasListeners(Events::postLoad)) { $evm->dispatchEvent(Events::postLoad, new LifecycleEventArgs($entity, $this->_em)); } @@ -698,11 +721,8 @@ class BasicEntityPersister list($params, $types) = $this->expandParameters($criteria); $stmt = $this->_conn->executeQuery($sql, $params, $types); - if ($this->_selectJoinSql) { - $hydrator = $this->_em->newHydrator(Query::HYDRATE_OBJECT); - } else { - $hydrator = $this->_em->newHydrator(Query::HYDRATE_SIMPLEOBJECT); - } + $hydrator = $this->_em->newHydrator(($this->_selectJoinSql) ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT); + return $hydrator->hydrateAll($stmt, $this->_rsm, array('deferEagerLoads' => true)); } @@ -718,6 +738,7 @@ class BasicEntityPersister public function getManyToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null) { $stmt = $this->getManyToManyStatement($assoc, $sourceEntity, $offset, $limit); + return $this->loadArrayFromStatement($assoc, $stmt); } @@ -740,6 +761,7 @@ class BasicEntityPersister } $hydrator = $this->_em->newHydrator(Query::HYDRATE_OBJECT); + return $hydrator->hydrateAll($stmt, $rsm, $hints); } @@ -778,6 +800,7 @@ class BasicEntityPersister public function loadManyToManyCollection(array $assoc, $sourceEntity, PersistentCollection $coll) { $stmt = $this->getManyToManyStatement($assoc, $sourceEntity); + return $this->loadCollectionFromStatement($assoc, $stmt, $coll); } @@ -785,12 +808,15 @@ class BasicEntityPersister { $criteria = array(); $sourceClass = $this->_em->getClassMetadata($assoc['sourceEntity']); + if ($assoc['isOwningSide']) { $quotedJoinTable = $sourceClass->getQuotedJoinTableName($assoc, $this->_platform); + foreach ($assoc['relationToSourceKeyColumns'] as $relationKeyColumn => $sourceKeyColumn) { if ($sourceClass->containsForeignIdentifier) { $field = $sourceClass->getFieldForColumn($sourceKeyColumn); $value = $sourceClass->reflFields[$field]->getValue($sourceEntity); + if (isset($sourceClass->associationMappings[$field])) { $value = $this->_em->getUnitOfWork()->getEntityIdentifier($value); $value = $value[$this->_em->getClassMetadata($assoc['targetEntity'])->identifier[0]]; @@ -808,15 +834,18 @@ class BasicEntityPersister } else { $owningAssoc = $this->_em->getClassMetadata($assoc['targetEntity'])->associationMappings[$assoc['mappedBy']]; $quotedJoinTable = $sourceClass->getQuotedJoinTableName($owningAssoc, $this->_platform); + // TRICKY: since the association is inverted source and target are flipped foreach ($owningAssoc['relationToTargetKeyColumns'] as $relationKeyColumn => $sourceKeyColumn) { if ($sourceClass->containsForeignIdentifier) { $field = $sourceClass->getFieldForColumn($sourceKeyColumn); $value = $sourceClass->reflFields[$field]->getValue($sourceEntity); + if (isset($sourceClass->associationMappings[$field])) { $value = $this->_em->getUnitOfWork()->getEntityIdentifier($value); $value = $value[$this->_em->getClassMetadata($assoc['targetEntity'])->identifier[0]]; } + $criteria[$quotedJoinTable . "." . $relationKeyColumn] = $value; } else if (isset($sourceClass->fieldNames[$sourceKeyColumn])) { $criteria[$quotedJoinTable . "." . $relationKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity); @@ -830,6 +859,7 @@ class BasicEntityPersister $sql = $this->_getSelectEntitiesSQL($criteria, $assoc, 0, $limit, $offset); list($params, $types) = $this->expandParameters($criteria); + return $this->_conn->executeQuery($sql, $params, $types); } @@ -848,15 +878,14 @@ class BasicEntityPersister */ protected function _getSelectEntitiesSQL(array $criteria, $assoc = null, $lockMode = 0, $limit = null, $offset = null, array $orderBy = null) { - $joinSql = $assoc != null && $assoc['type'] == ClassMetadata::MANY_TO_MANY ? - $this->_getSelectManyToManyJoinSQL($assoc) : ''; - + $joinSql = ($assoc != null && $assoc['type'] == ClassMetadata::MANY_TO_MANY) ? $this->_getSelectManyToManyJoinSQL($assoc) : ''; $conditionSql = $this->_getSelectConditionSQL($criteria, $assoc); - $orderBy = ($assoc !== null && isset($assoc['orderBy'])) ? $assoc['orderBy'] : $orderBy; + $orderBy = ($assoc !== null && isset($assoc['orderBy'])) ? $assoc['orderBy'] : $orderBy; $orderBySql = $orderBy ? $this->_getOrderBySQL($orderBy, $this->_getSQLTableAlias($this->_class->name)) : ''; $lockSql = ''; + if ($lockMode == LockMode::PESSIMISTIC_READ) { $lockSql = ' ' . $this->_platform->getReadLockSql(); } else if ($lockMode == LockMode::PESSIMISTIC_WRITE) { @@ -883,6 +912,7 @@ class BasicEntityPersister protected final function _getOrderBySQL(array $orderBy, $baseTableAlias) { $orderBySql = ''; + foreach ($orderBy as $fieldName => $orientation) { if ( ! isset($this->_class->fieldMappings[$fieldName])) { throw ORMException::unrecognizedField($fieldName); @@ -893,6 +923,7 @@ class BasicEntityPersister : $baseTableAlias; $columnName = $this->_class->getQuotedColumnName($fieldName, $this->_platform); + $orderBySql .= $orderBySql ? ', ' : ' ORDER BY '; $orderBySql .= $tableAlias . '.' . $columnName . ' ' . $orientation; } @@ -925,20 +956,25 @@ class BasicEntityPersister // Add regular columns to select list foreach ($this->_class->fieldNames as $field) { if ($columnList) $columnList .= ', '; + $columnList .= $this->_getSelectColumnSQL($field, $this->_class); } $this->_selectJoinSql = ''; $eagerAliasCounter = 0; + foreach ($this->_class->associationMappings as $assocField => $assoc) { $assocColumnSQL = $this->_getSelectColumnAssociationSQL($assocField, $assoc, $this->_class); + if ($assocColumnSQL) { if ($columnList) $columnList .= ', '; + $columnList .= $assocColumnSQL; } if ($assoc['type'] & ClassMetadata::TO_ONE && ($assoc['fetch'] == ClassMetadata::FETCH_EAGER || !$assoc['isOwningSide'])) { $eagerEntity = $this->_em->getClassMetadata($assoc['targetEntity']); + if ($eagerEntity->inheritanceType != ClassMetadata::INHERITANCE_TYPE_NONE) { continue; // now this is why you shouldn't use inheritance } @@ -948,41 +984,48 @@ class BasicEntityPersister foreach ($eagerEntity->fieldNames AS $field) { if ($columnList) $columnList .= ', '; + $columnList .= $this->_getSelectColumnSQL($field, $eagerEntity, $assocAlias); } foreach ($eagerEntity->associationMappings as $assoc2Field => $assoc2) { $assoc2ColumnSQL = $this->_getSelectColumnAssociationSQL($assoc2Field, $assoc2, $eagerEntity, $assocAlias); + if ($assoc2ColumnSQL) { if ($columnList) $columnList .= ', '; $columnList .= $assoc2ColumnSQL; } } + $this->_selectJoinSql .= ' LEFT JOIN'; // TODO: Inner join when all join columns are NOT nullable. $first = true; + if ($assoc['isOwningSide']) { - $this->_selectJoinSql .= ' ' . $eagerEntity->table['name'] . ' ' . $this->_getSQLTableAlias($eagerEntity->name, $assocAlias) .' ON '; + $this->_selectJoinSql .= ' ' . $eagerEntity->getQuotedTableName($this->_platform) . ' ' . $this->_getSQLTableAlias($eagerEntity->name, $assocAlias) .' ON '; foreach ($assoc['sourceToTargetKeyColumns'] AS $sourceCol => $targetCol) { - if (!$first) { + if ( ! $first) { $this->_selectJoinSql .= ' AND '; } - $this->_selectJoinSql .= $this->_getSQLTableAlias($assoc['sourceEntity']) . '.'.$sourceCol.' = ' . - $this->_getSQLTableAlias($assoc['targetEntity'], $assocAlias) . '.'.$targetCol.' '; + + $this->_selectJoinSql .= $this->_getSQLTableAlias($assoc['sourceEntity']) . '.' . $sourceCol . ' = ' + . $this->_getSQLTableAlias($assoc['targetEntity'], $assocAlias) . '.' . $targetCol . ' '; $first = false; } } else { $eagerEntity = $this->_em->getClassMetadata($assoc['targetEntity']); $owningAssoc = $eagerEntity->getAssociationMapping($assoc['mappedBy']); - $this->_selectJoinSql .= ' ' . $eagerEntity->table['name'] . ' ' . $this->_getSQLTableAlias($eagerEntity->name, $assocAlias) .' ON '; + $this->_selectJoinSql .= ' ' . $eagerEntity->getQuotedTableName($this->_platform) . ' ' + . $this->_getSQLTableAlias($eagerEntity->name, $assocAlias) . ' ON '; foreach ($owningAssoc['sourceToTargetKeyColumns'] AS $sourceCol => $targetCol) { - if (!$first) { + if ( ! $first) { $this->_selectJoinSql .= ' AND '; } - $this->_selectJoinSql .= $this->_getSQLTableAlias($owningAssoc['sourceEntity'], $assocAlias) . '.'.$sourceCol.' = ' . - $this->_getSQLTableAlias($owningAssoc['targetEntity']) . '.' . $targetCol . ' '; + + $this->_selectJoinSql .= $this->_getSQLTableAlias($owningAssoc['sourceEntity'], $assocAlias) . '.' . $sourceCol . ' = ' + . $this->_getSQLTableAlias($owningAssoc['targetEntity']) . '.' . $targetCol . ' '; $first = false; } } @@ -994,19 +1037,32 @@ class BasicEntityPersister return $this->_selectColumnListSql; } + /** + * Gets the SQL join fragment used when selecting entities from an association. + * + * @param string $field + * @param array $assoc + * @param ClassMetadata $class + * @param string $alias + * + * @return string + */ protected function _getSelectColumnAssociationSQL($field, $assoc, ClassMetadata $class, $alias = 'r') { $columnList = ''; + if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) { foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) { if ($columnList) $columnList .= ', '; $columnAlias = $srcColumn . $this->_sqlAliasCounter++; - $columnList .= $this->_getSQLTableAlias($class->name, ($alias == 'r' ? '' : $alias) ) . ".$srcColumn AS $columnAlias"; + $columnList .= $this->_getSQLTableAlias($class->name, ($alias == 'r' ? '' : $alias) ) + . '.' . $srcColumn . ' AS ' . $columnAlias; $resultColumnName = $this->_platform->getSQLResultCasing($columnAlias); $this->_rsm->addMetaResult($alias, $this->_platform->getSQLResultCasing($columnAlias), $srcColumn, isset($assoc['id']) && $assoc['id'] === true); } } + return $columnList; } @@ -1028,23 +1084,22 @@ class BasicEntityPersister } $joinTableName = $this->_class->getQuotedJoinTableName($owningAssoc, $this->_platform); - $joinSql = ''; + foreach ($joinClauses as $joinTableColumn => $sourceColumn) { if ($joinSql != '') $joinSql .= ' AND '; - if ($this->_class->containsForeignIdentifier && !isset($this->_class->fieldNames[$sourceColumn])) { + if ($this->_class->containsForeignIdentifier && ! isset($this->_class->fieldNames[$sourceColumn])) { $quotedColumn = $sourceColumn; // join columns cannot be quoted } else { $quotedColumn = $this->_class->getQuotedColumnName($this->_class->fieldNames[$sourceColumn], $this->_platform); } - $joinSql .= $this->_getSQLTableAlias($this->_class->name) . - '.' . $quotedColumn . ' = ' - . $joinTableName . '.' . $joinTableColumn; + $joinSql .= $this->_getSQLTableAlias($this->_class->name) . '.' . $quotedColumn . ' = ' + . $joinTableName . '.' . $joinTableColumn; } - return " INNER JOIN $joinTableName ON $joinSql"; + return ' INNER JOIN ' . $joinTableName . ' ON ' . $joinSql; } /** @@ -1057,21 +1112,23 @@ class BasicEntityPersister if ($this->_insertSql === null) { $insertSql = ''; $columns = $this->_getInsertColumnList(); + if (empty($columns)) { $insertSql = $this->_platform->getEmptyIdentityInsertSQL( - $this->_class->getQuotedTableName($this->_platform), - $this->_class->getQuotedColumnName($this->_class->identifier[0], $this->_platform) + $this->_class->getQuotedTableName($this->_platform), + $this->_class->getQuotedColumnName($this->_class->identifier[0], $this->_platform) ); } else { $columns = array_unique($columns); $values = array_fill(0, count($columns), '?'); $insertSql = 'INSERT INTO ' . $this->_class->getQuotedTableName($this->_platform) - . ' (' . implode(', ', $columns) . ') ' - . 'VALUES (' . implode(', ', $values) . ')'; + . ' (' . implode(', ', $columns) . ') VALUES (' . implode(', ', $values) . ')'; } + $this->_insertSql = $insertSql; } + return $this->_insertSql; } @@ -1086,19 +1143,21 @@ class BasicEntityPersister protected function _getInsertColumnList() { $columns = array(); + foreach ($this->_class->reflFields as $name => $field) { if ($this->_class->isVersioned && $this->_class->versionField == $name) { continue; } + if (isset($this->_class->associationMappings[$name])) { $assoc = $this->_class->associationMappings[$name]; + if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) { foreach ($assoc['targetToSourceKeyColumns'] as $sourceCol) { $columns[] = $sourceCol; } } - } else if ($this->_class->generatorType != ClassMetadata::GENERATOR_TYPE_IDENTITY || - $this->_class->identifier[0] != $name) { + } else if ($this->_class->generatorType != ClassMetadata::GENERATOR_TYPE_IDENTITY || $this->_class->identifier[0] != $name) { $columns[] = $this->_class->getQuotedColumnName($name, $this->_platform); } } @@ -1117,11 +1176,12 @@ class BasicEntityPersister protected function _getSelectColumnSQL($field, ClassMetadata $class, $alias = 'r') { $columnName = $class->columnNames[$field]; - $sql = $this->_getSQLTableAlias($class->name, $alias == 'r' ? '' : $alias) . '.' . $class->getQuotedColumnName($field, $this->_platform); + $sql = $this->_getSQLTableAlias($class->name, $alias == 'r' ? '' : $alias) + . '.' . $class->getQuotedColumnName($field, $this->_platform); $columnAlias = $this->_platform->getSQLResultCasing($columnName . $this->_sqlAliasCounter++); $this->_rsm->addFieldResult($alias, $columnAlias, $field); - return "$sql AS $columnAlias"; + return $sql . ' AS ' . $columnAlias; } /** @@ -1134,15 +1194,17 @@ class BasicEntityPersister protected function _getSQLTableAlias($className, $assocName = '') { if ($assocName) { - $className .= '#'.$assocName; + $className .= '#' . $assocName; } if (isset($this->_sqlTableAliases[$className])) { return $this->_sqlTableAliases[$className]; } + $tableAlias = 't' . $this->_sqlAliasCounter++; $this->_sqlTableAliases[$className] = $tableAlias; + return $tableAlias; } @@ -1178,7 +1240,7 @@ class BasicEntityPersister protected function getLockTablesSql() { return 'FROM ' . $this->_class->getQuotedTableName($this->_platform) . ' ' - . $this->_getSQLTableAlias($this->_class->name); + . $this->_getSQLTableAlias($this->_class->name); } /** @@ -1195,28 +1257,26 @@ class BasicEntityPersister protected function _getSelectConditionSQL(array $criteria, $assoc = null) { $conditionSql = ''; + foreach ($criteria as $field => $value) { $conditionSql .= $conditionSql ? ' AND ' : ''; if (isset($this->_class->columnNames[$field])) { - if (isset($this->_class->fieldMappings[$field]['inherited'])) { - $conditionSql .= $this->_getSQLTableAlias($this->_class->fieldMappings[$field]['inherited']) . '.'; - } else { - $conditionSql .= $this->_getSQLTableAlias($this->_class->name) . '.'; - } - $conditionSql .= $this->_class->getQuotedColumnName($field, $this->_platform); + $className = (isset($this->_class->fieldMappings[$field]['inherited'])) + ? $this->_class->fieldMappings[$field]['inherited'] + : $this->_class->name; + + $conditionSql .= $this->_getSQLTableAlias($className) . '.' . $this->_class->getQuotedColumnName($field, $this->_platform); } else if (isset($this->_class->associationMappings[$field])) { - if (!$this->_class->associationMappings[$field]['isOwningSide']) { + if ( ! $this->_class->associationMappings[$field]['isOwningSide']) { throw ORMException::invalidFindByInverseAssociation($this->_class->name, $field); } - - if (isset($this->_class->associationMappings[$field]['inherited'])) { - $conditionSql .= $this->_getSQLTableAlias($this->_class->associationMappings[$field]['inherited']) . '.'; - } else { - $conditionSql .= $this->_getSQLTableAlias($this->_class->name) . '.'; - } - $conditionSql .= $this->_class->associationMappings[$field]['joinColumns'][0]['name']; + $className = (isset($this->_class->associationMappings[$field]['inherited'])) + ? $this->_class->associationMappings[$field]['inherited'] + : $this->_class->name; + + $conditionSql .= $this->_getSQLTableAlias($className) . '.' . $this->_class->associationMappings[$field]['joinColumns'][0]['name']; } else if ($assoc !== null && strpos($field, " ") === false && strpos($field, "(") === false) { // very careless developers could potentially open up this normally hidden api for userland attacks, // therefore checking for spaces and function calls which are not allowed. @@ -1244,6 +1304,7 @@ class BasicEntityPersister public function getOneToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null) { $stmt = $this->getOneToManyStatement($assoc, $sourceEntity, $offset, $limit); + return $this->loadArrayFromStatement($assoc, $stmt); } @@ -1277,18 +1338,18 @@ class BasicEntityPersister $owningAssoc = $this->_class->associationMappings[$assoc['mappedBy']]; $sourceClass = $this->_em->getClassMetadata($assoc['sourceEntity']); - $tableAlias = isset($owningAssoc['inherited']) ? - $this->_getSQLTableAlias($owningAssoc['inherited']) - : $this->_getSQLTableAlias($this->_class->name); + $tableAlias = $this->_getSQLTableAlias(isset($owningAssoc['inherited']) ? $owningAssoc['inherited'] : $this->_class->name); foreach ($owningAssoc['targetToSourceKeyColumns'] as $sourceKeyColumn => $targetKeyColumn) { if ($sourceClass->containsForeignIdentifier) { $field = $sourceClass->getFieldForColumn($sourceKeyColumn); $value = $sourceClass->reflFields[$field]->getValue($sourceEntity); + if (isset($sourceClass->associationMappings[$field])) { $value = $this->_em->getUnitOfWork()->getEntityIdentifier($value); $value = $value[$this->_em->getClassMetadata($assoc['targetEntity'])->identifier[0]]; } + $criteria[$tableAlias . "." . $targetKeyColumn] = $value; } else { $criteria[$tableAlias . "." . $targetKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity); @@ -1421,9 +1482,9 @@ class BasicEntityPersister $criteria = array_merge($criteria, $extraConditions); } - $sql = 'SELECT 1 FROM ' . $this->_class->getQuotedTableName($this->_platform) - . ' ' . $this->_getSQLTableAlias($this->_class->name) - . ' WHERE ' . $this->_getSelectConditionSQL($criteria); + $sql = 'SELECT 1' + . ' FROM ' . $this->_class->getQuotedTableName($this->_platform) . ' ' . $this->_getSQLTableAlias($this->_class->name) + . ' WHERE ' . $this->_getSelectConditionSQL($criteria); return (bool) $this->_conn->fetchColumn($sql, array_values($criteria)); } diff --git a/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php b/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php index abc3fbc79..c95fee755 100644 --- a/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php +++ b/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php @@ -56,11 +56,11 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister */ protected function _getDiscriminatorColumnTableName() { - if ($this->_class->name == $this->_class->rootEntityName) { - return $this->_class->table['name']; - } else { - return $this->_em->getClassMetadata($this->_class->rootEntityName)->table['name']; - } + $class = ($this->_class->name !== $this->_class->rootEntityName) + ? $this->_em->getClassMetadata($this->_class->rootEntityName) + : $this->_class; + + return $class->getTableName(); } /** @@ -73,8 +73,10 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister { if (isset($this->_class->fieldMappings[$this->_class->versionField]['inherited'])) { $definingClassName = $this->_class->fieldMappings[$this->_class->versionField]['inherited']; + return $this->_em->getClassMetadata($definingClassName); } + return $this->_class; } @@ -87,19 +89,24 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister */ public function getOwningTable($fieldName) { - if (!isset($this->_owningTableMap[$fieldName])) { - if (isset($this->_class->associationMappings[$fieldName]['inherited'])) { - $cm = $this->_em->getClassMetadata($this->_class->associationMappings[$fieldName]['inherited']); - } else if (isset($this->_class->fieldMappings[$fieldName]['inherited'])) { - $cm = $this->_em->getClassMetadata($this->_class->fieldMappings[$fieldName]['inherited']); - } else { - $cm = $this->_class; - } - $this->_owningTableMap[$fieldName] = $cm->table['name']; - $this->_quotedTableMap[$cm->table['name']] = $cm->getQuotedTableName($this->_platform); + if (isset($this->_owningTableMap[$fieldName])) { + return $this->_owningTableMap[$fieldName]; + } + + if (isset($this->_class->associationMappings[$fieldName]['inherited'])) { + $cm = $this->_em->getClassMetadata($this->_class->associationMappings[$fieldName]['inherited']); + } else if (isset($this->_class->fieldMappings[$fieldName]['inherited'])) { + $cm = $this->_em->getClassMetadata($this->_class->fieldMappings[$fieldName]['inherited']); + } else { + $cm = $this->_class; } - return $this->_owningTableMap[$fieldName]; + $tableName = $cm->getTableName(); + + $this->_owningTableMap[$fieldName] = $tableName; + $this->_quotedTableMap[$tableName] = $cm->getQuotedTableName($this->_platform); + + return $tableName; } /** @@ -116,20 +123,22 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister $isPostInsertId = $idGen->isPostInsertGenerator(); // Prepare statement for the root table - $rootClass = $this->_class->name == $this->_class->rootEntityName ? - $this->_class : $this->_em->getClassMetadata($this->_class->rootEntityName); + $rootClass = ($this->_class->name !== $this->_class->rootEntityName) ? $this->_em->getClassMetadata($this->_class->rootEntityName) : $this->_class; $rootPersister = $this->_em->getUnitOfWork()->getEntityPersister($rootClass->name); - $rootTableName = $rootClass->table['name']; + $rootTableName = $rootClass->getTableName(); $rootTableStmt = $this->_conn->prepare($rootPersister->_getInsertSQL()); // Prepare statements for sub tables. $subTableStmts = array(); + if ($rootClass !== $this->_class) { - $subTableStmts[$this->_class->table['name']] = $this->_conn->prepare($this->_getInsertSQL()); + $subTableStmts[$this->_class->getTableName()] = $this->_conn->prepare($this->_getInsertSQL()); } + foreach ($this->_class->parentClasses as $parentClassName) { $parentClass = $this->_em->getClassMetadata($parentClassName); - $parentTableName = $parentClass->table['name']; + $parentTableName = $parentClass->getTableName(); + if ($parentClass !== $rootClass) { $parentPersister = $this->_em->getUnitOfWork()->getEntityPersister($parentClassName); $subTableStmts[$parentTableName] = $this->_conn->prepare($parentPersister->_getInsertSQL()); @@ -179,6 +188,7 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister } $rootTableStmt->closeCursor(); + foreach ($subTableStmts as $stmt) { $stmt->closeCursor(); } @@ -201,12 +211,14 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister if (($isVersioned = $this->_class->isVersioned) != false) { $versionedClass = $this->_getVersionedClassMetadata(); - $versionedTable = $versionedClass->table['name']; + $versionedTable = $versionedClass->getTableName(); } if ($updateData) { foreach ($updateData as $tableName => $data) { - $this->_updateTable($entity, $this->_quotedTableMap[$tableName], $data, $isVersioned && $versionedTable == $tableName); + $this->_updateTable( + $entity, $this->_quotedTableMap[$tableName], $data, $isVersioned && $versionedTable == $tableName + ); } // Make sure the table with the version column is updated even if no columns on that @@ -233,14 +245,17 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister // If the database platform supports FKs, just // delete the row from the root table. Cascades do the rest. if ($this->_platform->supportsForeignKeyConstraints()) { - $this->_conn->delete($this->_em->getClassMetadata($this->_class->rootEntityName) - ->getQuotedTableName($this->_platform), $id); + $this->_conn->delete( + $this->_em->getClassMetadata($this->_class->rootEntityName)->getQuotedTableName($this->_platform), $id + ); } else { // Delete from all tables individually, starting from this class' table up to the root table. $this->_conn->delete($this->_class->getQuotedTableName($this->_platform), $id); foreach ($this->_class->parentClasses as $parentClass) { - $this->_conn->delete($this->_em->getClassMetadata($parentClass)->getQuotedTableName($this->_platform), $id); + $this->_conn->delete( + $this->_em->getClassMetadata($parentClass)->getQuotedTableName($this->_platform), $id + ); } } } @@ -261,23 +276,27 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister // Add regular columns $columnList = ''; + foreach ($this->_class->fieldMappings as $fieldName => $mapping) { if ($columnList != '') $columnList .= ', '; - $columnList .= $this->_getSelectColumnSQL($fieldName, - isset($mapping['inherited']) ? - $this->_em->getClassMetadata($mapping['inherited']) : - $this->_class); + + $columnList .= $this->_getSelectColumnSQL( + $fieldName, + isset($mapping['inherited']) ? $this->_em->getClassMetadata($mapping['inherited']) : $this->_class + ); } // Add foreign key columns foreach ($this->_class->associationMappings as $assoc2) { if ($assoc2['isOwningSide'] && $assoc2['type'] & ClassMetadata::TO_ONE) { - $tableAlias = isset($assoc2['inherited']) ? - $this->_getSQLTableAlias($assoc2['inherited']) - : $baseTableAlias; + $tableAlias = isset($assoc2['inherited']) ? $this->_getSQLTableAlias($assoc2['inherited']) : $baseTableAlias; + foreach ($assoc2['targetToSourceKeyColumns'] as $srcColumn) { if ($columnList != '') $columnList .= ', '; - $columnList .= $this->getSelectJoinColumnSQL($tableAlias, $srcColumn, + + $columnList .= $this->getSelectJoinColumnSQL( + $tableAlias, + $srcColumn, isset($assoc2['inherited']) ? $assoc2['inherited'] : $this->_class->name ); } @@ -286,27 +305,27 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister // Add discriminator column (DO NOT ALIAS, see AbstractEntityInheritancePersister#_processSQLResult). $discrColumn = $this->_class->discriminatorColumn['name']; - if ($this->_class->rootEntityName == $this->_class->name) { - $columnList .= ", $baseTableAlias.$discrColumn"; - } else { - $columnList .= ', ' . $this->_getSQLTableAlias($this->_class->rootEntityName) - . ".$discrColumn"; - } + $tableAlias = ($this->_class->rootEntityName == $this->_class->name) ? $baseTableAlias : $this->_getSQLTableAlias($this->_class->rootEntityName); + $columnList .= ', ' . $tableAlias . '.' . $discrColumn; $resultColumnName = $this->_platform->getSQLResultCasing($discrColumn); + $this->_rsm->setDiscriminatorColumn('r', $resultColumnName); $this->_rsm->addMetaResult('r', $resultColumnName, $discrColumn); } // INNER JOIN parent tables $joinSql = ''; + foreach ($this->_class->parentClasses as $parentClassName) { $parentClass = $this->_em->getClassMetadata($parentClassName); $tableAlias = $this->_getSQLTableAlias($parentClassName); $joinSql .= ' INNER JOIN ' . $parentClass->getQuotedTableName($this->_platform) . ' ' . $tableAlias . ' ON '; $first = true; + foreach ($idColumns as $idColumn) { if ($first) $first = false; else $joinSql .= ' AND '; + $joinSql .= $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn; } } @@ -319,19 +338,20 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister if ($this->_selectColumnListSql === null) { // Add subclass columns foreach ($subClass->fieldMappings as $fieldName => $mapping) { - if (isset($mapping['inherited'])) { - continue; - } + if (isset($mapping['inherited'])) continue; + $columnList .= ', ' . $this->_getSelectColumnSQL($fieldName, $subClass); } // Add join columns (foreign keys) foreach ($subClass->associationMappings as $assoc2) { - if ($assoc2['isOwningSide'] && $assoc2['type'] & ClassMetadata::TO_ONE - && ! isset($assoc2['inherited'])) { + if ($assoc2['isOwningSide'] && $assoc2['type'] & ClassMetadata::TO_ONE && ! isset($assoc2['inherited'])) { foreach ($assoc2['targetToSourceKeyColumns'] as $srcColumn) { if ($columnList != '') $columnList .= ', '; - $columnList .= $this->getSelectJoinColumnSQL($tableAlias, $srcColumn, + + $columnList .= $this->getSelectJoinColumnSQL( + $tableAlias, + $srcColumn, isset($assoc2['inherited']) ? $assoc2['inherited'] : $subClass->name ); } @@ -342,14 +362,15 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister // Add LEFT JOIN $joinSql .= ' LEFT JOIN ' . $subClass->getQuotedTableName($this->_platform) . ' ' . $tableAlias . ' ON '; $first = true; + foreach ($idColumns as $idColumn) { if ($first) $first = false; else $joinSql .= ' AND '; + $joinSql .= $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn; } } - $joinSql .= $assoc != null && $assoc['type'] == ClassMetadata::MANY_TO_MANY ? - $this->_getSelectManyToManyJoinSQL($assoc) : ''; + $joinSql .= ($assoc != null && $assoc['type'] == ClassMetadata::MANY_TO_MANY) ? $this->_getSelectManyToManyJoinSQL($assoc) : ''; $conditionSql = $this->_getSelectConditionSQL($criteria, $assoc); @@ -361,6 +382,7 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister } $lockSql = ''; + if ($lockMode == LockMode::PESSIMISTIC_READ) { $lockSql = ' ' . $this->_platform->getReadLockSql(); } else if ($lockMode == LockMode::PESSIMISTIC_WRITE) { @@ -386,13 +408,16 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister // INNER JOIN parent tables $joinSql = ''; + foreach ($this->_class->parentClasses as $parentClassName) { $parentClass = $this->_em->getClassMetadata($parentClassName); $tableAlias = $this->_getSQLTableAlias($parentClassName); $joinSql .= ' INNER JOIN ' . $parentClass->getQuotedTableName($this->_platform) . ' ' . $tableAlias . ' ON '; $first = true; + foreach ($idColumns as $idColumn) { if ($first) $first = false; else $joinSql .= ' AND '; + $joinSql .= $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn; } } diff --git a/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php b/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php index ce794781c..863d350dc 100644 --- a/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php +++ b/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php @@ -39,11 +39,11 @@ class ManyToManyPersister extends AbstractCollectionPersister */ protected function _getDeleteRowSQL(PersistentCollection $coll) { - $mapping = $coll->getMapping(); - $joinTable = $mapping['joinTable']; - $columns = $mapping['joinTableColumns']; + $mapping = $coll->getMapping(); + $class = $this->_em->getClassMetadata(get_class($coll->getOwner())); - return 'DELETE FROM ' . $joinTable['name'] . ' WHERE ' . implode(' = ? AND ', $columns) . ' = ?'; + return 'DELETE FROM ' . $class->getQuotedJoinTableName($mapping, $this->_conn->getDatabasePlatform()) + . ' WHERE ' . implode(' = ? AND ', $mapping['joinTableColumns']) . ' = ?'; } /** @@ -76,10 +76,11 @@ class ManyToManyPersister extends AbstractCollectionPersister protected function _getInsertRowSQL(PersistentCollection $coll) { $mapping = $coll->getMapping(); - $joinTable = $mapping['joinTable']; $columns = $mapping['joinTableColumns']; - return 'INSERT INTO ' . $joinTable['name'] . ' (' . implode(', ', $columns) . ')' - . ' VALUES (' . implode(', ', array_fill(0, count($columns), '?')) . ')'; + $class = $this->_em->getClassMetadata(get_class($coll->getOwner())); + + return 'INSERT INTO ' . $class->getQuotedJoinTableName($mapping, $this->_conn->getDatabasePlatform()) + . ' (' . implode(', ', $columns) . ') VALUES (' . implode(', ', array_fill(0, count($columns), '?')) . ')'; } /** @@ -104,8 +105,8 @@ class ManyToManyPersister extends AbstractCollectionPersister */ private function _collectJoinTableColumnParameters(PersistentCollection $coll, $element) { - $params = array(); - $mapping = $coll->getMapping(); + $params = array(); + $mapping = $coll->getMapping(); $isComposite = count($mapping['joinTableColumns']) > 2; $identifier1 = $this->_uow->getEntityIdentifier($coll->getOwner()); @@ -150,14 +151,19 @@ class ManyToManyPersister extends AbstractCollectionPersister */ protected function _getDeleteSQL(PersistentCollection $coll) { - $mapping = $coll->getMapping(); + $mapping = $coll->getMapping(); + $class = $this->_em->getClassMetadata(get_class($coll->getOwner())); $joinTable = $mapping['joinTable']; $whereClause = ''; + foreach ($mapping['relationToSourceKeyColumns'] as $relationColumn => $srcColumn) { if ($whereClause !== '') $whereClause .= ' AND '; - $whereClause .= "$relationColumn = ?"; + + $whereClause .= $relationColumn . ' = ?'; } - return 'DELETE FROM ' . $joinTable['name'] . ' WHERE ' . $whereClause; + + return 'DELETE FROM ' . $class->getQuotedJoinTableName($mapping, $this->_conn->getDatabasePlatform()) + . ' WHERE ' . $whereClause; } /** @@ -172,8 +178,10 @@ class ManyToManyPersister extends AbstractCollectionPersister $params = array(); $mapping = $coll->getMapping(); $identifier = $this->_uow->getEntityIdentifier($coll->getOwner()); + if (count($mapping['relationToSourceKeyColumns']) > 1) { $sourceClass = $this->_em->getClassMetadata(get_class($mapping->getOwner())); + foreach ($mapping['relationToSourceKeyColumns'] as $relColumn => $srcColumn) { $params[] = $identifier[$sourceClass->fieldNames[$srcColumn]]; } @@ -189,36 +197,37 @@ class ManyToManyPersister extends AbstractCollectionPersister */ public function count(PersistentCollection $coll) { - $params = array(); + $params = array(); $mapping = $coll->getMapping(); - $class = $this->_em->getClassMetadata($mapping['sourceEntity']); - $id = $this->_em->getUnitOfWork()->getEntityIdentifier($coll->getOwner()); + $class = $this->_em->getClassMetadata($mapping['sourceEntity']); + $id = $this->_em->getUnitOfWork()->getEntityIdentifier($coll->getOwner()); if ($mapping['isOwningSide']) { - $joinTable = $mapping['joinTable']; $joinColumns = $mapping['relationToSourceKeyColumns']; } else { $mapping = $this->_em->getClassMetadata($mapping['targetEntity'])->associationMappings[$mapping['mappedBy']]; - $joinTable = $mapping['joinTable']; $joinColumns = $mapping['relationToTargetKeyColumns']; } $whereClause = ''; + foreach ($mapping['joinTableColumns'] as $joinTableColumn) { if (isset($joinColumns[$joinTableColumn])) { if ($whereClause !== '') { $whereClause .= ' AND '; } + $whereClause .= "$joinTableColumn = ?"; - if ($class->containsForeignIdentifier) { - $params[] = $id[$class->getFieldForColumn($joinColumns[$joinTableColumn])]; - } else { - $params[] = $id[$class->fieldNames[$joinColumns[$joinTableColumn]]]; - } + $params[] = ($class->containsForeignIdentifier) + ? $id[$class->getFieldForColumn($joinColumns[$joinTableColumn])] + : $id[$class->fieldNames[$joinColumns[$joinTableColumn]]]; } } - $sql = 'SELECT count(*) FROM ' . $joinTable['name'] . ' WHERE ' . $whereClause; + + $sql = 'SELECT COUNT(*)' + . ' FROM ' . $class->getQuotedJoinTableName($mapping, $this->_conn->getDatabasePlatform()) + . ' WHERE ' . $whereClause; return $this->_conn->fetchColumn($sql, $params); } @@ -232,9 +241,8 @@ class ManyToManyPersister extends AbstractCollectionPersister public function slice(PersistentCollection $coll, $offset, $length = null) { $mapping = $coll->getMapping(); - return $this->_em->getUnitOfWork() - ->getEntityPersister($mapping['targetEntity']) - ->getManyToManyCollection($mapping, $coll->getOwner(), $offset, $length); + + return $this->_em->getUnitOfWork()->getEntityPersister($mapping['targetEntity'])->getManyToManyCollection($mapping, $coll->getOwner(), $offset, $length); } /** @@ -253,7 +261,7 @@ class ManyToManyPersister extends AbstractCollectionPersister $params = array(); $mapping = $coll->getMapping(); - if (!$mapping['isOwningSide']) { + if ( ! $mapping['isOwningSide']) { $sourceClass = $this->_em->getClassMetadata($mapping['targetEntity']); $targetClass = $this->_em->getClassMetadata($mapping['sourceEntity']); $sourceId = $uow->getEntityIdentifier($element); @@ -266,36 +274,37 @@ class ManyToManyPersister extends AbstractCollectionPersister $sourceId = $uow->getEntityIdentifier($coll->getOwner()); $targetId = $uow->getEntityIdentifier($element); } - $joinTable = $mapping['joinTable']; - + $whereClause = ''; + foreach ($mapping['joinTableColumns'] as $joinTableColumn) { if (isset($mapping['relationToTargetKeyColumns'][$joinTableColumn])) { if ($whereClause !== '') { $whereClause .= ' AND '; } - $whereClause .= "$joinTableColumn = ?"; + + $whereClause .= $joinTableColumn . ' = ?'; - if ($targetClass->containsForeignIdentifier) { - $params[] = $targetId[$targetClass->getFieldForColumn($mapping['relationToTargetKeyColumns'][$joinTableColumn])]; - } else { - $params[] = $targetId[$targetClass->fieldNames[$mapping['relationToTargetKeyColumns'][$joinTableColumn]]]; - } + $params[] = ($targetClass->containsForeignIdentifier) + ? $targetId[$targetClass->getFieldForColumn($mapping['relationToTargetKeyColumns'][$joinTableColumn])] + : $targetId[$targetClass->fieldNames[$mapping['relationToTargetKeyColumns'][$joinTableColumn]]]; } else if (isset($mapping['relationToSourceKeyColumns'][$joinTableColumn])) { if ($whereClause !== '') { $whereClause .= ' AND '; } - $whereClause .= "$joinTableColumn = ?"; + + $whereClause .= $joinTableColumn . ' = ?'; - if ($sourceClass->containsForeignIdentifier) { - $params[] = $sourceId[$sourceClass->getFieldForColumn($mapping['relationToSourceKeyColumns'][$joinTableColumn])]; - } else { - $params[] = $sourceId[$sourceClass->fieldNames[$mapping['relationToSourceKeyColumns'][$joinTableColumn]]]; - } + $params[] = ($sourceClass->containsForeignIdentifier) + ? $sourceId[$sourceClass->getFieldForColumn($mapping['relationToSourceKeyColumns'][$joinTableColumn])] + : $sourceId[$sourceClass->fieldNames[$mapping['relationToSourceKeyColumns'][$joinTableColumn]]]; } } - $sql = 'SELECT 1 FROM ' . $joinTable['name'] . ' WHERE ' . $whereClause; + + $sql = 'SELECT 1' + . ' FROM ' . $sourceClass->getQuotedJoinTableName($mapping, $this->_conn->getDatabasePlatform()) + . ' WHERE ' . $whereClause; - return (bool)$this->_conn->fetchColumn($sql, $params); + return (bool) $this->_conn->fetchColumn($sql, $params); } } \ No newline at end of file diff --git a/lib/Doctrine/ORM/Persisters/SingleTablePersister.php b/lib/Doctrine/ORM/Persisters/SingleTablePersister.php index f910a8e06..c9ab27c69 100644 --- a/lib/Doctrine/ORM/Persisters/SingleTablePersister.php +++ b/lib/Doctrine/ORM/Persisters/SingleTablePersister.php @@ -35,7 +35,7 @@ class SingleTablePersister extends AbstractEntityInheritancePersister /** {@inheritdoc} */ protected function _getDiscriminatorColumnTableName() { - return $this->_class->table['name']; + return $this->_class->getTableName(); } /** {@inheritdoc} */ @@ -45,28 +45,35 @@ class SingleTablePersister extends AbstractEntityInheritancePersister // Append discriminator column $discrColumn = $this->_class->discriminatorColumn['name']; - $columnList .= ", $discrColumn"; - $rootClass = $this->_em->getClassMetadata($this->_class->rootEntityName); + $columnList .= ', ' . $discrColumn; + + $rootClass = $this->_em->getClassMetadata($this->_class->rootEntityName); $tableAlias = $this->_getSQLTableAlias($rootClass->name); $resultColumnName = $this->_platform->getSQLResultCasing($discrColumn); + $this->_rsm->setDiscriminatorColumn('r', $resultColumnName); $this->_rsm->addMetaResult('r', $resultColumnName, $discrColumn); // Append subclass columns foreach ($this->_class->subClasses as $subClassName) { $subClass = $this->_em->getClassMetadata($subClassName); + // Regular columns foreach ($subClass->fieldMappings as $fieldName => $mapping) { if ( ! isset($mapping['inherited'])) { $columnList .= ', ' . $this->_getSelectColumnSQL($fieldName, $subClass); } } + // Foreign key columns foreach ($subClass->associationMappings as $assoc) { if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE && ! isset($assoc['inherited'])) { foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) { if ($columnList != '') $columnList .= ', '; - $columnList .= $this->getSelectJoinColumnSQL($tableAlias, $srcColumn, + + $columnList .= $this->getSelectJoinColumnSQL( + $tableAlias, + $srcColumn, isset($assoc['inherited']) ? $assoc['inherited'] : $this->_class->name ); } @@ -81,6 +88,7 @@ class SingleTablePersister extends AbstractEntityInheritancePersister protected function _getInsertColumnList() { $columns = parent::_getInsertColumnList(); + // Add discriminator column to the INSERT SQL $columns[] = $this->_class->discriminatorColumn['name']; @@ -100,18 +108,21 @@ class SingleTablePersister extends AbstractEntityInheritancePersister // Append discriminator condition if ($conditionSql) $conditionSql .= ' AND '; + $values = array(); + if ($this->_class->discriminatorValue !== null) { // discriminators can be 0 $values[] = $this->_conn->quote($this->_class->discriminatorValue); } $discrValues = array_flip($this->_class->discriminatorMap); + foreach ($this->_class->subClasses as $subclassName) { $values[] = $this->_conn->quote($discrValues[$subclassName]); } - $conditionSql .= $this->_getSQLTableAlias($this->_class->name) . '.' - . $this->_class->discriminatorColumn['name'] - . ' IN (' . implode(', ', $values) . ')'; + + $conditionSql .= $this->_getSQLTableAlias($this->_class->name) . '.' . $this->_class->discriminatorColumn['name'] + . ' IN (' . implode(', ', $values) . ')'; return $conditionSql; } diff --git a/lib/Doctrine/ORM/Query/AST/Functions/SizeFunction.php b/lib/Doctrine/ORM/Query/AST/Functions/SizeFunction.php index c66df6a8b..91e40395a 100644 --- a/lib/Doctrine/ORM/Query/AST/Functions/SizeFunction.php +++ b/lib/Doctrine/ORM/Query/AST/Functions/SizeFunction.php @@ -53,8 +53,8 @@ class SizeFunction extends FunctionNode if ($assoc['type'] == \Doctrine\ORM\Mapping\ClassMetadata::ONE_TO_MANY) { $targetClass = $sqlWalker->getEntityManager()->getClassMetadata($assoc['targetEntity']); - $targetTableAlias = $sqlWalker->getSQLTableAlias($targetClass->table['name']); - $sourceTableAlias = $sqlWalker->getSQLTableAlias($class->table['name'], $dqlAlias); + $targetTableAlias = $sqlWalker->getSQLTableAlias($targetClass->getTableName()); + $sourceTableAlias = $sqlWalker->getSQLTableAlias($class->getTableName(), $dqlAlias); $sql .= $targetClass->getQuotedTableName($platform) . ' ' . $targetTableAlias . ' WHERE '; @@ -77,7 +77,7 @@ class SizeFunction extends FunctionNode // SQL table aliases $joinTableAlias = $sqlWalker->getSQLTableAlias($joinTable['name']); - $sourceTableAlias = $sqlWalker->getSQLTableAlias($class->table['name'], $dqlAlias); + $sourceTableAlias = $sqlWalker->getSQLTableAlias($class->getTableName(), $dqlAlias); // join to target table $sql .= $targetClass->getQuotedJoinTableName($owningAssoc, $platform) . ' ' . $joinTableAlias . ' WHERE '; diff --git a/lib/Doctrine/ORM/Query/Exec/MultiTableDeleteExecutor.php b/lib/Doctrine/ORM/Query/Exec/MultiTableDeleteExecutor.php index e8bb20212..311c818ce 100644 --- a/lib/Doctrine/ORM/Query/Exec/MultiTableDeleteExecutor.php +++ b/lib/Doctrine/ORM/Query/Exec/MultiTableDeleteExecutor.php @@ -63,7 +63,7 @@ class MultiTableDeleteExecutor extends AbstractSqlExecutor $idColumnList = implode(', ', $idColumnNames); // 1. Create an INSERT INTO temptable ... SELECT identifiers WHERE $AST->getWhereClause() - $sqlWalker->setSQLTableAlias($primaryClass->table['name'], 't0', $primaryDqlAlias); + $sqlWalker->setSQLTableAlias($primaryClass->getTableName(), 't0', $primaryDqlAlias); $this->_insertSql = 'INSERT INTO ' . $tempTable . ' (' . $idColumnList . ')' . ' SELECT t0.' . implode(', t0.', $idColumnNames); diff --git a/lib/Doctrine/ORM/Query/Exec/MultiTableUpdateExecutor.php b/lib/Doctrine/ORM/Query/Exec/MultiTableUpdateExecutor.php index 7becaec98..b45abe83b 100644 --- a/lib/Doctrine/ORM/Query/Exec/MultiTableUpdateExecutor.php +++ b/lib/Doctrine/ORM/Query/Exec/MultiTableUpdateExecutor.php @@ -64,7 +64,7 @@ class MultiTableUpdateExecutor extends AbstractSqlExecutor $idColumnList = implode(', ', $idColumnNames); // 1. Create an INSERT INTO temptable ... SELECT identifiers WHERE $AST->getWhereClause() - $sqlWalker->setSQLTableAlias($primaryClass->table['name'], 't0', $updateClause->aliasIdentificationVariable); + $sqlWalker->setSQLTableAlias($primaryClass->getTableName(), 't0', $updateClause->aliasIdentificationVariable); $this->_insertSql = 'INSERT INTO ' . $tempTable . ' (' . $idColumnList . ')' . ' SELECT t0.' . implode(', t0.', $idColumnNames); diff --git a/lib/Doctrine/ORM/Query/SqlWalker.php b/lib/Doctrine/ORM/Query/SqlWalker.php index 46563615b..d78d1d134 100644 --- a/lib/Doctrine/ORM/Query/SqlWalker.php +++ b/lib/Doctrine/ORM/Query/SqlWalker.php @@ -244,24 +244,23 @@ class SqlWalker implements TreeWalker { $sql = ''; - $baseTableAlias = $this->getSQLTableAlias($class->table['name'], $dqlAlias); + $baseTableAlias = $this->getSQLTableAlias($class->getTableName(), $dqlAlias); // INNER JOIN parent class tables foreach ($class->parentClasses as $parentClassName) { $parentClass = $this->_em->getClassMetadata($parentClassName); - $tableAlias = $this->getSQLTableAlias($parentClass->table['name'], $dqlAlias); + $tableAlias = $this->getSQLTableAlias($parentClass->getTableName(), $dqlAlias); + // If this is a joined association we must use left joins to preserve the correct result. $sql .= isset($this->_queryComponents[$dqlAlias]['relation']) ? ' LEFT ' : ' INNER '; - $sql .= 'JOIN ' . $parentClass->getQuotedTableName($this->_platform) - . ' ' . $tableAlias . ' ON '; + $sql .= 'JOIN ' . $parentClass->getQuotedTableName($this->_platform) . ' ' . $tableAlias . ' ON '; $first = true; + foreach ($class->identifier as $idField) { if ($first) $first = false; else $sql .= ' AND '; $columnName = $class->getQuotedColumnName($idField, $this->_platform); - $sql .= $baseTableAlias . '.' . $columnName - . ' = ' - . $tableAlias . '.' . $columnName; + $sql .= $baseTableAlias . '.' . $columnName . ' = ' . $tableAlias . '.' . $columnName; } } @@ -269,17 +268,15 @@ class SqlWalker implements TreeWalker if ( ! $this->_query->getHint(Query::HINT_FORCE_PARTIAL_LOAD)) { foreach ($class->subClasses as $subClassName) { $subClass = $this->_em->getClassMetadata($subClassName); - $tableAlias = $this->getSQLTableAlias($subClass->table['name'], $dqlAlias); - $sql .= ' LEFT JOIN ' . $subClass->getQuotedTableName($this->_platform) - . ' ' . $tableAlias . ' ON '; + $tableAlias = $this->getSQLTableAlias($subClass->getTableName(), $dqlAlias); + $sql .= ' LEFT JOIN ' . $subClass->getQuotedTableName($this->_platform) . ' ' . $tableAlias . ' ON '; $first = true; + foreach ($class->identifier as $idField) { if ($first) $first = false; else $sql .= ' AND '; $columnName = $class->getQuotedColumnName($idField, $this->_platform); - $sql .= $baseTableAlias . '.' . $columnName - . ' = ' - . $tableAlias . '.' . $columnName; + $sql .= $baseTableAlias . '.' . $columnName . ' = ' . $tableAlias . '.' . $columnName; } } } @@ -290,24 +287,26 @@ class SqlWalker implements TreeWalker private function _generateOrderedCollectionOrderByItems() { $sql = ''; + foreach ($this->_selectedClasses AS $dqlAlias => $class) { $qComp = $this->_queryComponents[$dqlAlias]; + if (isset($qComp['relation']['orderBy'])) { foreach ($qComp['relation']['orderBy'] AS $fieldName => $orientation) { - if ($qComp['metadata']->isInheritanceTypeJoined()) { - $tableName = $this->_em->getUnitOfWork()->getEntityPersister($class->name)->getOwningTable($fieldName); - } else { - $tableName = $qComp['metadata']->table['name']; - } + $tableName = ($qComp['metadata']->isInheritanceTypeJoined()) + ? $this->_em->getUnitOfWork()->getEntityPersister($class->name)->getOwningTable($fieldName) + : $qComp['metadata']->getTableName(); if ($sql != '') { $sql .= ', '; } - $sql .= $this->getSQLTableAlias($tableName, $dqlAlias) . '.' . - $qComp['metadata']->getQuotedColumnName($fieldName, $this->_platform) . " $orientation"; + + $sql .= $this->getSQLTableAlias($tableName, $dqlAlias) . '.' + . $qComp['metadata']->getQuotedColumnName($fieldName, $this->_platform) . ' ' . $orientation; } } } + return $sql; } @@ -328,6 +327,7 @@ class SqlWalker implements TreeWalker if ($class->isInheritanceTypeSingleTable()) { $conn = $this->_em->getConnection(); $values = array(); + if ($class->discriminatorValue !== null) { // discrimnators can be 0 $values[] = $conn->quote($class->discriminatorValue); } @@ -342,7 +342,7 @@ class SqlWalker implements TreeWalker } $sql .= ($sql != '' ? ' AND ' : '') - . (($this->_useSqlTableAliases) ? $this->getSQLTableAlias($class->table['name'], $dqlAlias) . '.' : '') + . (($this->_useSqlTableAliases) ? $this->getSQLTableAlias($class->getTableName(), $dqlAlias) . '.' : '') . $class->discriminatorColumn['name'] . ' IN (' . implode(', ', $values) . ')'; } } @@ -357,7 +357,7 @@ class SqlWalker implements TreeWalker */ public function walkSelectStatement(AST\SelectStatement $AST) { - $sql = $this->walkSelectClause($AST->selectClause); + $sql = $this->walkSelectClause($AST->selectClause); $sql .= $this->walkFromClause($AST->fromClause); $sql .= $this->walkWhereClause($AST->whereClause); $sql .= $AST->groupByClause ? $this->walkGroupByClause($AST->groupByClause) : ''; @@ -366,19 +366,18 @@ class SqlWalker implements TreeWalker if (($orderByClause = $AST->orderByClause) !== null) { $sql .= $AST->orderByClause ? $this->walkOrderByClause($AST->orderByClause) : ''; } else if (($orderBySql = $this->_generateOrderedCollectionOrderByItems()) !== '') { - $sql .= ' ORDER BY '.$orderBySql; + $sql .= ' ORDER BY ' . $orderBySql; } - $sql = $this->_platform->modifyLimitQuery( $sql, $this->_query->getMaxResults(), $this->_query->getFirstResult() ); if (($lockMode = $this->_query->getHint(Query::HINT_LOCK_MODE)) !== false) { if ($lockMode == LockMode::PESSIMISTIC_READ) { - $sql .= " " . $this->_platform->getReadLockSQL(); + $sql .= ' ' . $this->_platform->getReadLockSQL(); } else if ($lockMode == LockMode::PESSIMISTIC_WRITE) { - $sql .= " " . $this->_platform->getWriteLockSQL(); + $sql .= ' ' . $this->_platform->getWriteLockSQL(); } else if ($lockMode == LockMode::OPTIMISTIC) { foreach ($this->_selectedClasses AS $class) { if ( ! $class->isVersioned) { @@ -400,10 +399,8 @@ class SqlWalker implements TreeWalker public function walkUpdateStatement(AST\UpdateStatement $AST) { $this->_useSqlTableAliases = false; - $sql = $this->walkUpdateClause($AST->updateClause); - $sql .= $this->walkWhereClause($AST->whereClause); - - return $sql; + + return $this->walkUpdateClause($AST->updateClause) . $this->walkWhereClause($AST->whereClause); } /** @@ -415,10 +412,8 @@ class SqlWalker implements TreeWalker public function walkDeleteStatement(AST\DeleteStatement $AST) { $this->_useSqlTableAliases = false; - $sql = $this->walkDeleteClause($AST->deleteClause); - $sql .= $this->walkWhereClause($AST->whereClause); - - return $sql; + + return $this->walkDeleteClause($AST->deleteClause) . $this->walkWhereClause($AST->whereClause); } @@ -440,7 +435,7 @@ class SqlWalker implements TreeWalker $class = $this->_em->getClassMetadata($class->fieldMappings[$fieldName]['inherited']); } - return $this->getSQLTableAlias($class->table['name'], $identificationVariable); + return $this->getSQLTableAlias($class->getTableName(), $identificationVariable); } /** @@ -489,7 +484,7 @@ class SqlWalker implements TreeWalker } if ($this->_useSqlTableAliases) { - $sql .= $this->getSQLTableAlias($class->table['name'], $dqlAlias) . '.'; + $sql .= $this->getSQLTableAlias($class->getTableName(), $dqlAlias) . '.'; } $sql .= reset($assoc['targetToSourceKeyColumns']); @@ -534,7 +529,7 @@ class SqlWalker implements TreeWalker if ($class->isInheritanceTypeSingleTable() || $class->isInheritanceTypeJoined()) { // Add discriminator columns to SQL $rootClass = $this->_em->getClassMetadata($class->rootEntityName); - $tblAlias = $this->getSQLTableAlias($rootClass->table['name'], $dqlAlias); + $tblAlias = $this->getSQLTableAlias($rootClass->getTableName(), $dqlAlias); $discrColumn = $rootClass->discriminatorColumn; $columnAlias = $this->getSQLColumnAlias($discrColumn['name']); @@ -551,9 +546,9 @@ class SqlWalker implements TreeWalker if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) { if (isset($assoc['inherited'])) { $owningClass = $this->_em->getClassMetadata($assoc['inherited']); - $sqlTableAlias = $this->getSQLTableAlias($owningClass->table['name'], $dqlAlias); + $sqlTableAlias = $this->getSQLTableAlias($owningClass->getTableName(), $dqlAlias); } else { - $sqlTableAlias = $this->getSQLTableAlias($class->table['name'], $dqlAlias); + $sqlTableAlias = $this->getSQLTableAlias($class->getTableName(), $dqlAlias); } foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) { @@ -570,7 +565,8 @@ class SqlWalker implements TreeWalker } else { // Add foreign key columns to SQL, if necessary if ($addMetaColumns) { - $sqlTableAlias = $this->getSQLTableAlias($class->table['name'], $dqlAlias); + $sqlTableAlias = $this->getSQLTableAlias($class->getTableName(), $dqlAlias); + foreach ($class->associationMappings as $assoc) { if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) { foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) { @@ -612,7 +608,7 @@ class SqlWalker implements TreeWalker $class = $this->_em->getClassMetadata($rangeDecl->abstractSchemaName); $sql .= $class->getQuotedTableName($this->_platform) . ' ' - . $this->getSQLTableAlias($class->table['name'], $dqlAlias); + . $this->getSQLTableAlias($class->getTableName(), $dqlAlias); if ($class->isInheritanceTypeJoined()) { $sql .= $this->_generateClassTableInheritanceJoins($class, $dqlAlias); @@ -723,13 +719,15 @@ class SqlWalker implements TreeWalker } $joinAssocPathExpr = $join->joinAssociationPathExpression; - $joinedDqlAlias = $join->aliasIdentificationVariable; - $relation = $this->_queryComponents[$joinedDqlAlias]['relation']; - $targetClass = $this->_em->getClassMetadata($relation['targetEntity']); - $sourceClass = $this->_em->getClassMetadata($relation['sourceEntity']); + $joinedDqlAlias = $join->aliasIdentificationVariable; + + $relation = $this->_queryComponents[$joinedDqlAlias]['relation']; + $targetClass = $this->_em->getClassMetadata($relation['targetEntity']); + $sourceClass = $this->_em->getClassMetadata($relation['sourceEntity']); $targetTableName = $targetClass->getQuotedTableName($this->_platform); - $targetTableAlias = $this->getSQLTableAlias($targetClass->table['name'], $joinedDqlAlias); - $sourceTableAlias = $this->getSQLTableAlias($sourceClass->table['name'], $joinAssocPathExpr->identificationVariable); + + $targetTableAlias = $this->getSQLTableAlias($targetClass->getTableName(), $joinedDqlAlias); + $sourceTableAlias = $this->getSQLTableAlias($sourceClass->getTableName(), $joinAssocPathExpr->identificationVariable); // Ensure we got the owning side, since it has all mapping info $assoc = ( ! $relation['isOwningSide']) ? $targetClass->associationMappings[$relation['mappedBy']] : $relation; @@ -811,8 +809,7 @@ class SqlWalker implements TreeWalker } // Join target table - $sql .= ($joinType == AST\Join::JOIN_TYPE_LEFT || $joinType == AST\Join::JOIN_TYPE_LEFTOUTER) - ? ' LEFT JOIN ' : ' INNER JOIN '; + $sql .= ($joinType == AST\Join::JOIN_TYPE_LEFT || $joinType == AST\Join::JOIN_TYPE_LEFTOUTER) ? ' LEFT JOIN ' : ' INNER JOIN '; $sql .= $targetTableName . ' ' . $targetTableAlias . ' ON '; $first = true; @@ -1118,11 +1115,9 @@ class SqlWalker implements TreeWalker continue; } - if (isset($mapping['inherited'])) { - $tableName = $this->_em->getClassMetadata($mapping['inherited'])->table['name']; - } else { - $tableName = $class->table['name']; - } + $tableName = (isset($mapping['inherited'])) + ? $this->_em->getClassMetadata($mapping['inherited'])->getTableName() + : $class->getTableName(); if ($beginning) $beginning = false; else $sql .= ', '; @@ -1142,7 +1137,7 @@ class SqlWalker implements TreeWalker if ($class->isInheritanceTypeSingleTable() || ! $this->_query->getHint(Query::HINT_FORCE_PARTIAL_LOAD)) { foreach ($class->subClasses as $subClassName) { $subClass = $this->_em->getClassMetadata($subClassName); - $sqlTableAlias = $this->getSQLTableAlias($subClass->table['name'], $dqlAlias); + $sqlTableAlias = $this->getSQLTableAlias($subClass->getTableName(), $dqlAlias); foreach ($subClass->fieldMappings as $fieldName => $mapping) { if (isset($mapping['inherited']) || $partialFieldSet && !in_array($fieldName, $partialFieldSet)) { continue; @@ -1235,7 +1230,7 @@ class SqlWalker implements TreeWalker $class = $this->_em->getClassMetadata($rangeDecl->abstractSchemaName); $sql .= $class->getQuotedTableName($this->_platform) . ' ' - . $this->getSQLTableAlias($class->table['name'], $dqlAlias); + . $this->getSQLTableAlias($class->getTableName(), $dqlAlias); $this->_rootAliases[] = $dqlAlias; @@ -1589,15 +1584,13 @@ class SqlWalker implements TreeWalker $assoc = $class->associationMappings[$fieldName]; if ($assoc['type'] == ClassMetadata::ONE_TO_MANY) { - $targetClass = $this->_em->getClassMetadata($assoc['targetEntity']); - $targetTableAlias = $this->getSQLTableAlias($targetClass->table['name']); - $sourceTableAlias = $this->getSQLTableAlias($class->table['name'], $dqlAlias); + $targetClass = $this->_em->getClassMetadata($assoc['targetEntity']); + $targetTableAlias = $this->getSQLTableAlias($targetClass->getTableName()); + $sourceTableAlias = $this->getSQLTableAlias($class->getTableName(), $dqlAlias); - $sql .= $targetClass->getQuotedTableName($this->_platform) - . ' ' . $targetTableAlias . ' WHERE '; + $sql .= $targetClass->getQuotedTableName($this->_platform) . ' ' . $targetTableAlias . ' WHERE '; $owningAssoc = $targetClass->associationMappings[$assoc['mappedBy']]; - $first = true; foreach ($owningAssoc['targetToSourceKeyColumns'] as $targetColumn => $sourceColumn) { @@ -1625,15 +1618,13 @@ class SqlWalker implements TreeWalker $joinTable = $owningAssoc['joinTable']; // SQL table aliases - $joinTableAlias = $this->getSQLTableAlias($joinTable['name']); - $targetTableAlias = $this->getSQLTableAlias($targetClass->table['name']); - $sourceTableAlias = $this->getSQLTableAlias($class->table['name'], $dqlAlias); + $joinTableAlias = $this->getSQLTableAlias($joinTable['name']); + $targetTableAlias = $this->getSQLTableAlias($targetClass->getTableName()); + $sourceTableAlias = $this->getSQLTableAlias($class->getTableName(), $dqlAlias); // join to target table - $sql .= $targetClass->getQuotedJoinTableName($owningAssoc, $this->_platform) - . ' ' . $joinTableAlias . ' INNER JOIN ' - . $targetClass->getQuotedTableName($this->_platform) - . ' ' . $targetTableAlias . ' ON '; + $sql .= $targetClass->getQuotedJoinTableName($owningAssoc, $this->_platform) . ' ' . $joinTableAlias + . ' INNER JOIN ' . $targetClass->getQuotedTableName($this->_platform) . ' ' . $targetTableAlias . ' ON '; // join conditions $joinColumns = $assoc['isOwningSide'] @@ -1645,25 +1636,19 @@ class SqlWalker implements TreeWalker if ($first) $first = false; else $sql .= ' AND '; $sql .= $joinTableAlias . '.' . $joinColumn['name'] . ' = ' - . $targetTableAlias . '.' . $targetClass->getQuotedColumnName( - $targetClass->fieldNames[$joinColumn['referencedColumnName']], - $this->_platform); + . $targetTableAlias . '.' . $targetClass->getQuotedColumnName($targetClass->fieldNames[$joinColumn['referencedColumnName']], $this->_platform); } $sql .= ' WHERE '; - $joinColumns = $assoc['isOwningSide'] - ? $joinTable['joinColumns'] - : $joinTable['inverseJoinColumns']; - + $joinColumns = $assoc['isOwningSide'] ? $joinTable['joinColumns'] : $joinTable['inverseJoinColumns']; $first = true; + foreach ($joinColumns as $joinColumn) { if ($first) $first = false; else $sql .= ' AND '; $sql .= $joinTableAlias . '.' . $joinColumn['name'] . ' = ' - . $sourceTableAlias . '.' . $class->getQuotedColumnName( - $class->fieldNames[$joinColumn['referencedColumnName']], - $this->_platform); + . $sourceTableAlias . '.' . $class->getQuotedColumnName($class->fieldNames[$joinColumn['referencedColumnName']], $this->_platform); } $sql .= ' AND '; @@ -1760,7 +1745,7 @@ class SqlWalker implements TreeWalker } if ($this->_useSqlTableAliases) { - $sql .= $this->getSQLTableAlias($discrClass->table['name'], $dqlAlias) . '.'; + $sql .= $this->getSQLTableAlias($discrClass->getTableName(), $dqlAlias) . '.'; } $sql .= $class->discriminatorColumn['name'] . ($instanceOfExpr->not ? ' NOT IN ' : ' IN '); diff --git a/lib/Doctrine/ORM/Tools/ConvertDoctrine1Schema.php b/lib/Doctrine/ORM/Tools/ConvertDoctrine1Schema.php index fd4ed3a41..8b69ce067 100644 --- a/lib/Doctrine/ORM/Tools/ConvertDoctrine1Schema.php +++ b/lib/Doctrine/ORM/Tools/ConvertDoctrine1Schema.php @@ -101,6 +101,7 @@ class ConvertDoctrine1Schema { if (isset($model['tableName']) && $model['tableName']) { $e = explode('.', $model['tableName']); + if (count($e) > 1) { $metadata->table['schema'] = $e[0]; $metadata->table['name'] = $e[1]; diff --git a/lib/Doctrine/ORM/Tools/EntityGenerator.php b/lib/Doctrine/ORM/Tools/EntityGenerator.php index e569ae965..e53505f98 100644 --- a/lib/Doctrine/ORM/Tools/EntityGenerator.php +++ b/lib/Doctrine/ORM/Tools/EntityGenerator.php @@ -570,7 +570,12 @@ public function () private function _generateTableAnnotation($metadata) { $table = array(); - if ($metadata->table['name']) { + + if (isset($metadata->table['schema'])) { + $table[] = 'schema="' . $metadata->table['schema'] . '"'; + } + + if (isset($metadata->table['name'])) { $table[] = 'name="' . $metadata->table['name'] . '"'; } diff --git a/lib/Doctrine/ORM/Tools/Export/Driver/YamlExporter.php b/lib/Doctrine/ORM/Tools/Export/Driver/YamlExporter.php index 03074bb0c..e8410db2d 100644 --- a/lib/Doctrine/ORM/Tools/Export/Driver/YamlExporter.php +++ b/lib/Doctrine/ORM/Tools/Export/Driver/YamlExporter.php @@ -49,11 +49,13 @@ class YamlExporter extends AbstractExporter public function exportClassMetadata(ClassMetadataInfo $metadata) { $array = array(); + if ($metadata->isMappedSuperclass) { $array['type'] = 'mappedSuperclass'; } else { $array['type'] = 'entity'; } + $array['table'] = $metadata->table['name']; if (isset($metadata->table['schema'])) { diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index 830829d14..d5965d2d4 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -2183,9 +2183,11 @@ class UnitOfWork implements PropertyChangedListener public function tryGetById($id, $rootClassName) { $idHash = implode(' ', (array) $id); + if (isset($this->identityMap[$rootClassName][$idHash])) { return $this->identityMap[$rootClassName][$idHash]; } + return false; } From bd5393a3187ab25136bcd8e5efea894928697e95 Mon Sep 17 00:00:00 2001 From: Guilherme Blanco Date: Wed, 7 Sep 2011 13:12:02 -0300 Subject: [PATCH 2/2] Added coverage for orphanRemoval in OneToOne when unlinking an entity. --- tests/Doctrine/Tests/Models/CMS/CmsEmail.php | 48 +++++++++++++++++++ tests/Doctrine/Tests/Models/CMS/CmsUser.php | 17 +++++++ .../ORM/Functional/CustomTreeWalkersTest.php | 6 +-- .../Functional/OneToOneOrphanRemovalTest.php | 34 +++++++++++++ .../ORM/Query/SelectSqlGenerationTest.php | 4 +- .../Doctrine/Tests/OrmFunctionalTestCase.php | 1 + 6 files changed, 105 insertions(+), 5 deletions(-) create mode 100644 tests/Doctrine/Tests/Models/CMS/CmsEmail.php diff --git a/tests/Doctrine/Tests/Models/CMS/CmsEmail.php b/tests/Doctrine/Tests/Models/CMS/CmsEmail.php new file mode 100644 index 000000000..2d8818cbd --- /dev/null +++ b/tests/Doctrine/Tests/Models/CMS/CmsEmail.php @@ -0,0 +1,48 @@ +id; + } + + public function getEmail() { + return $this->email; + } + + public function setEmail($email) { + $this->email = $email; + } + + public function getUser() { + return $this->user; + } + + public function setUser(CmsUser $user) { + $this->user = $user; + } +} \ No newline at end of file diff --git a/tests/Doctrine/Tests/Models/CMS/CmsUser.php b/tests/Doctrine/Tests/Models/CMS/CmsUser.php index 4047a3635..f02d0441b 100644 --- a/tests/Doctrine/Tests/Models/CMS/CmsUser.php +++ b/tests/Doctrine/Tests/Models/CMS/CmsUser.php @@ -42,6 +42,11 @@ class CmsUser * @OneToOne(targetEntity="CmsAddress", mappedBy="user", cascade={"persist"}, orphanRemoval=true) */ public $address; + /** + * @OneToOne(targetEntity="CmsEmail", inversedBy="user", cascade={"persist"}, orphanRemoval=true) + * @JoinColumn(referencedColumnName="id", nullable=true) + */ + public $email; /** * @ManyToMany(targetEntity="CmsGroup", inversedBy="users", cascade={"persist", "merge"}) * @JoinTable(name="cms_users_groups", @@ -119,4 +124,16 @@ class CmsUser $address->setUser($this); } } + + public function getEmail() { return $this->email; } + + public function setEmail(CmsEmail $email = null) { + if ($this->email !== $email) { + $this->email = $email; + + if ($email) { + $email->setUser($this); + } + } + } } diff --git a/tests/Doctrine/Tests/ORM/Functional/CustomTreeWalkersTest.php b/tests/Doctrine/Tests/ORM/Functional/CustomTreeWalkersTest.php index c295af478..ada41d00b 100644 --- a/tests/Doctrine/Tests/ORM/Functional/CustomTreeWalkersTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/CustomTreeWalkersTest.php @@ -59,7 +59,7 @@ class CustomTreeWalkersTest extends \Doctrine\Tests\OrmFunctionalTestCase { $this->assertSqlGeneration( 'select u from Doctrine\Tests\Models\CMS\CmsUser u', - "SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3 FROM cms_users c0_ WHERE c0_.id = 1" + "SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3, c0_.email_id AS email_id4 FROM cms_users c0_ WHERE c0_.id = 1" ); } @@ -67,7 +67,7 @@ class CustomTreeWalkersTest extends \Doctrine\Tests\OrmFunctionalTestCase { $this->assertSqlGeneration( 'select u from Doctrine\Tests\Models\CMS\CmsUser u where u.name = :name or u.name = :otherName', - "SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3 FROM cms_users c0_ WHERE (c0_.name = ? OR c0_.name = ?) AND c0_.id = 1" + "SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3, c0_.email_id AS email_id4 FROM cms_users c0_ WHERE (c0_.name = ? OR c0_.name = ?) AND c0_.id = 1" ); } @@ -75,7 +75,7 @@ class CustomTreeWalkersTest extends \Doctrine\Tests\OrmFunctionalTestCase { $this->assertSqlGeneration( 'select u from Doctrine\Tests\Models\CMS\CmsUser u where u.name = :name', - "SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3 FROM cms_users c0_ WHERE c0_.name = ? AND c0_.id = 1" + "SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3, c0_.email_id AS email_id4 FROM cms_users c0_ WHERE c0_.name = ? AND c0_.id = 1" ); } } diff --git a/tests/Doctrine/Tests/ORM/Functional/OneToOneOrphanRemovalTest.php b/tests/Doctrine/Tests/ORM/Functional/OneToOneOrphanRemovalTest.php index e82961aff..c9bd72fcf 100644 --- a/tests/Doctrine/Tests/ORM/Functional/OneToOneOrphanRemovalTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/OneToOneOrphanRemovalTest.php @@ -3,6 +3,7 @@ namespace Doctrine\Tests\ORM\Functional; use Doctrine\Tests\Models\CMS\CmsUser, + Doctrine\Tests\Models\CMS\CmsEmail, Doctrine\Tests\Models\CMS\CmsAddress, Doctrine\Tests\Models\CMS\CmsPhonenumber; @@ -57,4 +58,37 @@ class OneToOneOrphanRemovalTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertEquals(0, count($result), 'CmsAddress should be removed by orphanRemoval'); } + + public function testOrphanRemovalWhenUnlink() + { + $user = new CmsUser; + $user->status = 'dev'; + $user->username = 'beberlei'; + $user->name = 'Bejamin Eberlei'; + + $email = new CmsEmail; + $email->email = 'beberlei@domain.com'; + + $user->setEmail($email); + + $this->_em->persist($user); + $this->_em->flush(); + + $userId = $user->getId(); + + $this->_em->clear(); + + $user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $userId); + + $user->setEmail(null); + + $this->_em->persist($user); + $this->_em->flush(); + $this->_em->clear(); + + $query = $this->_em->createQuery('SELECT e FROM Doctrine\Tests\Models\CMS\CmsEmail e'); + $result = $query->getResult(); + + $this->assertEquals(0, count($result), 'CmsEmail should be removed by orphanRemoval'); + } } \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php index 4da9cf3e3..63a885497 100644 --- a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php +++ b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php @@ -614,7 +614,7 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase ->createQuery('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u') ->setMaxResults(10); - $this->assertEquals('SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3 FROM cms_users c0_ LIMIT 10', $q->getSql()); + $this->assertEquals('SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3, c0_.email_id AS email_id4 FROM cms_users c0_ LIMIT 10', $q->getSql()); } public function testLimitAndOffsetFromQueryClass() @@ -624,7 +624,7 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase ->setMaxResults(10) ->setFirstResult(0); - $this->assertEquals('SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3 FROM cms_users c0_ LIMIT 10 OFFSET 0', $q->getSql()); + $this->assertEquals('SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3, c0_.email_id AS email_id4 FROM cms_users c0_ LIMIT 10 OFFSET 0', $q->getSql()); } public function testSizeFunction() diff --git a/tests/Doctrine/Tests/OrmFunctionalTestCase.php b/tests/Doctrine/Tests/OrmFunctionalTestCase.php index 56345bc6f..0900e4e99 100644 --- a/tests/Doctrine/Tests/OrmFunctionalTestCase.php +++ b/tests/Doctrine/Tests/OrmFunctionalTestCase.php @@ -44,6 +44,7 @@ abstract class OrmFunctionalTestCase extends OrmTestCase 'Doctrine\Tests\Models\CMS\CmsUser', 'Doctrine\Tests\Models\CMS\CmsPhonenumber', 'Doctrine\Tests\Models\CMS\CmsAddress', + 'Doctrine\Tests\Models\CMS\CmsEmail', 'Doctrine\Tests\Models\CMS\CmsGroup', 'Doctrine\Tests\Models\CMS\CmsArticle', 'Doctrine\Tests\Models\CMS\CmsComment',