Commit 555b0976 authored by romanb's avatar romanb

[2.0][DDC-164][DDC-165] Fixed. Cleaned up ManyToManyMapping. Cleaned up...

[2.0][DDC-164][DDC-165] Fixed. Cleaned up ManyToManyMapping. Cleaned up identifier handling and handling of composite identifiers in some places.
parent a9d739a7
...@@ -30,8 +30,10 @@ foreach ($revisions as $rev) { ...@@ -30,8 +30,10 @@ foreach ($revisions as $rev) {
$xml = simplexml_load_file($logsPath . $rev . '/log.xml'); $xml = simplexml_load_file($logsPath . $rev . '/log.xml');
foreach ($xml->testsuite as $suite) { foreach ($xml->testsuite as $suite) {
foreach ($suite->testcase as $test) { foreach ($suite->testcase as $test) {
$name = (string)$suite['name'] . '#' . (string)$test['name']; if (stripos((string)$suite['name'], 'performance') !== false || stripos((string)$test['name'], 'performance') !== false) {
$graphs[$name][] = (double)$test['time']; $name = (string)$suite['name'] . '#' . (string)$test['name'];
$graphs[$name][] = (double)$test['time'];
}
} }
} }
} }
......
...@@ -4,7 +4,9 @@ namespace Doctrine\DBAL\Types; ...@@ -4,7 +4,9 @@ namespace Doctrine\DBAL\Types;
/** /**
* Type that maps an SQL INT to a PHP integer. * Type that maps an SQL INT to a PHP integer.
* *
* @author Roman Borschel <roman@code-factory.org>
* @since 2.0
*/ */
class IntegerType extends Type class IntegerType extends Type
{ {
......
...@@ -124,7 +124,7 @@ class Configuration extends \Doctrine\DBAL\Configuration ...@@ -124,7 +124,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
*/ */
public function getMetadataDriverImpl() public function getMetadataDriverImpl()
{ {
if($this->_attributes['metadataDriverImpl'] == null) { if ($this->_attributes['metadataDriverImpl'] == null) {
$reader = new \Doctrine\Common\Annotations\AnnotationReader(new \Doctrine\Common\Cache\ArrayCache); $reader = new \Doctrine\Common\Annotations\AnnotationReader(new \Doctrine\Common\Cache\ArrayCache);
$reader->setDefaultAnnotationNamespace('Doctrine\ORM\Mapping\\'); $reader->setDefaultAnnotationNamespace('Doctrine\ORM\Mapping\\');
$this->_attributes['metadataDriverImpl'] = new \Doctrine\ORM\Mapping\Driver\AnnotationDriver($reader); $this->_attributes['metadataDriverImpl'] = new \Doctrine\ORM\Mapping\Driver\AnnotationDriver($reader);
......
...@@ -121,12 +121,12 @@ final class ClassMetadata extends ClassMetadataInfo ...@@ -121,12 +121,12 @@ final class ClassMetadata extends ClassMetadataInfo
* Gets the ReflectionProperty for the single identifier field. * Gets the ReflectionProperty for the single identifier field.
* *
* @return ReflectionProperty * @return ReflectionProperty
* @throws DoctrineException If the class has a composite identifier. * @throws BadMethodCallException If the class has a composite identifier.
*/ */
public function getSingleIdReflectionProperty() public function getSingleIdReflectionProperty()
{ {
if ($this->isIdentifierComposite) { if ($this->isIdentifierComposite) {
throw DoctrineException::singleIdNotAllowedOnCompositePrimaryKey(); throw new \BadMethodCallException("Class " . $this->name . " has a composite identifier.");
} }
return $this->reflFields[$this->identifier[0]]; return $this->reflFields[$this->identifier[0]];
} }
...@@ -163,12 +163,12 @@ final class ClassMetadata extends ClassMetadataInfo ...@@ -163,12 +163,12 @@ final class ClassMetadata extends ClassMetadataInfo
foreach ($this->identifier as $idField) { foreach ($this->identifier as $idField) {
$value = $this->reflFields[$idField]->getValue($entity); $value = $this->reflFields[$idField]->getValue($entity);
if ($value !== null) { if ($value !== null) {
$id[] = $value; $id[$idField] = $value;
} }
} }
return $id; return $id;
} else { } else {
return $this->reflFields[$this->identifier[0]]->getValue($entity); return array($this->identifier[0] => $this->reflFields[$this->identifier[0]]->getValue($entity));
} }
} }
......
...@@ -108,8 +108,9 @@ class ClassMetadataFactory ...@@ -108,8 +108,9 @@ class ClassMetadataFactory
if (($cached = $this->_cacheDriver->fetch($cacheKey)) !== false) { if (($cached = $this->_cacheDriver->fetch($cacheKey)) !== false) {
$this->_loadedMetadata[$className] = $cached; $this->_loadedMetadata[$className] = $cached;
} else { } else {
$this->_loadMetadata($className); foreach ($this->_loadMetadata($className) as $loadedClassName) {
$this->_cacheDriver->save($cacheKey, $this->_loadedMetadata[$className], null); $this->_cacheDriver->save($cacheKey, $this->_loadedMetadata[$className], null);
}
} }
} else { } else {
$this->_loadMetadata($className); $this->_loadMetadata($className);
...@@ -151,6 +152,8 @@ class ClassMetadataFactory ...@@ -151,6 +152,8 @@ class ClassMetadataFactory
*/ */
protected function _loadMetadata($name) protected function _loadMetadata($name)
{ {
$loaded = array();
// Collect parent classes, ignoring transient (not-mapped) classes. // Collect parent classes, ignoring transient (not-mapped) classes.
$parentClass = $name; $parentClass = $name;
$parentClasses = array(); $parentClasses = array();
...@@ -241,7 +244,11 @@ class ClassMetadataFactory ...@@ -241,7 +244,11 @@ class ClassMetadataFactory
if ( ! $class->isMappedSuperclass) { if ( ! $class->isMappedSuperclass) {
array_unshift($visited, $className); array_unshift($visited, $className);
} }
$loaded[] = $className;
} }
return $loaded;
} }
/** /**
......
...@@ -264,6 +264,9 @@ class ClassMetadataInfo ...@@ -264,6 +264,9 @@ class ClassMetadataInfo
* as any join columns and discriminator columns. * as any join columns and discriminator columns.
* *
* @var array * @var array
* @todo Remove. Or at least remove from serialization/unserialization and instead
* populate them during runtime.
* See http://www.doctrine-project.org/jira/browse/DDC-132.
*/ */
public $resultColumnNames = array(); public $resultColumnNames = array();
...@@ -486,7 +489,7 @@ class ClassMetadataInfo ...@@ -486,7 +489,7 @@ class ClassMetadataInfo
* @param string $fieldName * @param string $fieldName
* @return string * @return string
*/ */
public function getOwningClass($fieldName) /*public function getOwningClass($fieldName)
{ {
if ($this->inheritanceType == self::INHERITANCE_TYPE_NONE) { if ($this->inheritanceType == self::INHERITANCE_TYPE_NONE) {
return $this->name; return $this->name;
...@@ -494,7 +497,7 @@ class ClassMetadataInfo ...@@ -494,7 +497,7 @@ class ClassMetadataInfo
$mapping = $this->getFieldMapping($fieldName); $mapping = $this->getFieldMapping($fieldName);
return $mapping['inherited']; return $mapping['inherited'];
} }
} }*/
/** /**
* Gets the name of the root class of the mapped entity hierarchy. If the entity described * Gets the name of the root class of the mapped entity hierarchy. If the entity described
......
...@@ -40,24 +40,14 @@ namespace Doctrine\ORM\Mapping; ...@@ -40,24 +40,14 @@ namespace Doctrine\ORM\Mapping;
class ManyToManyMapping extends AssociationMapping class ManyToManyMapping extends AssociationMapping
{ {
/** /**
* The key columns of the source table. * Maps the columns in the relational table to the columns in the source table.
*/ */
public $sourceKeyColumns = array(); public $relationToSourceKeyColumns = array();
/** /**
* The key columns of the target table. * Maps the columns in the relation table to the columns in the target table.
*/ */
public $targetKeyColumns = array(); public $relationToTargetKeyColumns = array();
/**
* Maps the columns in the source table to the columns in the relation table.
*/
public $sourceToRelationKeyColumns = array();
/**
* Maps the columns in the target table to the columns in the relation table.
*/
public $targetToRelationKeyColumns = array();
/** /**
* List of aggregated column names on the join table. * List of aggregated column names on the join table.
...@@ -105,46 +95,35 @@ class ManyToManyMapping extends AssociationMapping ...@@ -105,46 +95,35 @@ class ManyToManyMapping extends AssociationMapping
$joinColumn['name'] = trim($joinColumn['name'], '`'); $joinColumn['name'] = trim($joinColumn['name'], '`');
$joinColumn['quoted'] = true; $joinColumn['quoted'] = true;
} }
$this->sourceToRelationKeyColumns[$joinColumn['referencedColumnName']] = $joinColumn['name']; $this->relationToSourceKeyColumns[$joinColumn['name']] = $joinColumn['referencedColumnName'];
$this->joinTableColumns[] = $joinColumn['name']; $this->joinTableColumns[] = $joinColumn['name'];
} }
$this->sourceKeyColumns = array_keys($this->sourceToRelationKeyColumns);
foreach ($mapping['joinTable']['inverseJoinColumns'] as &$inverseJoinColumn) { foreach ($mapping['joinTable']['inverseJoinColumns'] as &$inverseJoinColumn) {
if ($inverseJoinColumn['name'][0] == '`') { if ($inverseJoinColumn['name'][0] == '`') {
$inverseJoinColumn['name'] = trim($inverseJoinColumn['name'], '`'); $inverseJoinColumn['name'] = trim($inverseJoinColumn['name'], '`');
$inverseJoinColumn['quoted'] = true; $inverseJoinColumn['quoted'] = true;
} }
$this->targetToRelationKeyColumns[$inverseJoinColumn['referencedColumnName']] = $inverseJoinColumn['name']; $this->relationToTargetKeyColumns[$inverseJoinColumn['name']] = $inverseJoinColumn['referencedColumnName'];
$this->joinTableColumns[] = $inverseJoinColumn['name']; $this->joinTableColumns[] = $inverseJoinColumn['name'];
} }
$this->targetKeyColumns = array_keys($this->targetToRelationKeyColumns);
} }
} }
public function getJoinTableColumnNames() public function getJoinTableColumnNames()
{ {
return $this->joinTableColumns; return $this->joinTableColumns;
//return array_merge(array_keys($this->relationToSourceKeyColumns), array_keys($this->relationToTargetKeyColumns));
} }
public function getSourceToRelationKeyColumns() public function getRelationToSourceKeyColumns()
{
return $this->sourceToRelationKeyColumns;
}
public function getTargetToRelationKeyColumns()
{
return $this->targetToRelationKeyColumns;
}
public function getSourceKeyColumns()
{ {
return $this->sourceKeyColumns; return $this->relationToSourceKeyColumns;
} }
public function getTargetKeyColumns() public function getRelationToTargetKeyColumns()
{ {
return $this->targetKeyColumns; return $this->relationToTargetKeyColumns;
} }
/** /**
...@@ -161,7 +140,7 @@ class ManyToManyMapping extends AssociationMapping ...@@ -161,7 +140,7 @@ class ManyToManyMapping extends AssociationMapping
$sourceClass = $em->getClassMetadata($this->sourceEntityName); $sourceClass = $em->getClassMetadata($this->sourceEntityName);
$joinTableConditions = array(); $joinTableConditions = array();
if ($this->isOwningSide) { if ($this->isOwningSide) {
foreach ($this->sourceToRelationKeyColumns as $sourceKeyColumn => $relationKeyColumn) { foreach ($this->relationToSourceKeyColumns as $relationKeyColumn => $sourceKeyColumn) {
// getting id // getting id
if (isset($sourceClass->fieldNames[$sourceKeyColumn])) { if (isset($sourceClass->fieldNames[$sourceKeyColumn])) {
$joinTableConditions[$relationKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity); $joinTableConditions[$relationKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);
...@@ -172,7 +151,7 @@ class ManyToManyMapping extends AssociationMapping ...@@ -172,7 +151,7 @@ class ManyToManyMapping extends AssociationMapping
} else { } else {
$owningAssoc = $em->getClassMetadata($this->targetEntityName)->associationMappings[$this->mappedByFieldName]; $owningAssoc = $em->getClassMetadata($this->targetEntityName)->associationMappings[$this->mappedByFieldName];
// TRICKY: since the association is inverted source and target are flipped // TRICKY: since the association is inverted source and target are flipped
foreach ($owningAssoc->targetToRelationKeyColumns as $sourceKeyColumn => $relationKeyColumn) { foreach ($owningAssoc->relationToTargetKeyColumns as $relationKeyColumn => $sourceKeyColumn) {
// getting id // getting id
if (isset($sourceClass->fieldNames[$sourceKeyColumn])) { if (isset($sourceClass->fieldNames[$sourceKeyColumn])) {
$joinTableConditions[$relationKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity); $joinTableConditions[$relationKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);
......
...@@ -162,11 +162,18 @@ final class PersistentCollection implements \Doctrine\Common\Collections\Collect ...@@ -162,11 +162,18 @@ final class PersistentCollection implements \Doctrine\Common\Collections\Collect
* Gets the class descriptor for the owning entity class. * Gets the class descriptor for the owning entity class.
* *
* @return Doctrine\ORM\Mapping\ClassMetadata * @return Doctrine\ORM\Mapping\ClassMetadata
* @deprecated
* @todo Remove
*/ */
public function getOwnerClass() public function getOwnerClass()
{ {
return $this->_typeClass; return $this->_typeClass;
} }
public function getTypeClass()
{
return $this->_typeClass;
}
/** /**
* INTERNAL: * INTERNAL:
...@@ -430,13 +437,22 @@ final class PersistentCollection implements \Doctrine\Common\Collections\Collect ...@@ -430,13 +437,22 @@ final class PersistentCollection implements \Doctrine\Common\Collections\Collect
*/ */
public function contains($element) public function contains($element)
{ {
// TODO: Assuming the identity of entities in a collection is always based /* DRAFT
// on their primary key (there is no equals/hashCode in PHP), if ($this->_initialized) {
// if the collection is not initialized, we could issue a straight return $this->_coll->contains($element);
// SQL "SELECT 1" on the association (table) without initializing } else {
// the collection. if ($element is MANAGED) {
if ($this->_coll->contains($element)) {
// TODO: Change to use PK identity, not php object identity!? return true;
}
$exists = check db for existence;
if ($exists) {
$this->_coll->add($element);
}
return $exists;
}
return false;
}*/
$this->_initialize(); $this->_initialize();
return $this->_coll->contains($element); return $this->_coll->contains($element);
......
...@@ -48,13 +48,13 @@ abstract class AbstractCollectionPersister ...@@ -48,13 +48,13 @@ abstract class AbstractCollectionPersister
$this->_conn = $em->getConnection(); $this->_conn = $em->getConnection();
} }
public function recreate(PersistentCollection $coll) /*public function recreate(PersistentCollection $coll)
{ {
if ($coll->getRelation()->isInverseSide()) { if ($coll->getRelation()->isInverseSide()) {
return; return;
} }
//... //...
} }*/
/** /**
* Deletes the persistent state represented by the given collection. * Deletes the persistent state represented by the given collection.
...@@ -110,8 +110,8 @@ abstract class AbstractCollectionPersister ...@@ -110,8 +110,8 @@ abstract class AbstractCollectionPersister
} }
} }
public function updateRows(PersistentCollection $coll) //public function updateRows(PersistentCollection $coll)
{} //{}
public function insertRows(PersistentCollection $coll) public function insertRows(PersistentCollection $coll)
{ {
......
...@@ -78,8 +78,11 @@ class JoinedSubclassPersister extends StandardEntityPersister ...@@ -78,8 +78,11 @@ class JoinedSubclassPersister extends StandardEntityPersister
} }
/** /**
* {@inheritdoc} * Gets the name of the table that owns the column the given field is mapped to.
* Does only look upwards in the hierarchy, not downwards.
* *
* @param string $fieldName
* @return string
* @override * @override
*/ */
public function getOwningTable($fieldName) public function getOwningTable($fieldName)
...@@ -372,4 +375,10 @@ class JoinedSubclassPersister extends StandardEntityPersister ...@@ -372,4 +375,10 @@ class JoinedSubclassPersister extends StandardEntityPersister
. $joinSql . $joinSql
. ($conditionSql != '' ? ' WHERE ' . $conditionSql : ''); . ($conditionSql != '' ? ' WHERE ' . $conditionSql : '');
} }
/** @override */
protected function _processSqlResult(array $sqlResult)
{
return $this->_processSqlResultInheritanceAware($sqlResult);
}
} }
...@@ -48,15 +48,12 @@ class ManyToManyPersister extends AbstractCollectionPersister ...@@ -48,15 +48,12 @@ class ManyToManyPersister extends AbstractCollectionPersister
* {@inheritdoc} * {@inheritdoc}
* *
* @override * @override
* @internal Order of the parameters must be the same as the order of the columns in
* _getDeleteRowSql.
*/ */
protected function _getDeleteRowSqlParameters(PersistentCollection $coll, $element) protected function _getDeleteRowSqlParameters(PersistentCollection $coll, $element)
{ {
$params = array_merge( return $this->_collectJoinTableColumnParameters($coll, $element);
$this->_uow->getEntityIdentifier($coll->getOwner()),
$this->_uow->getEntityIdentifier($element)
);
//var_dump($params);
return $params;
} }
/** /**
...@@ -71,12 +68,14 @@ class ManyToManyPersister extends AbstractCollectionPersister ...@@ -71,12 +68,14 @@ class ManyToManyPersister extends AbstractCollectionPersister
* {@inheritdoc} * {@inheritdoc}
* *
* @override * @override
* @internal Order of the parameters must be the same as the order of the columns in
* _getInsertRowSql.
*/ */
protected function _getInsertRowSql(PersistentCollection $coll) protected function _getInsertRowSql(PersistentCollection $coll)
{ {
$mapping = $coll->getMapping(); $mapping = $coll->getMapping();
$joinTable = $mapping->getJoinTable(); $joinTable = $mapping->getJoinTable();
$columns = $mapping->getJoinTableColumnNames(); $columns = $mapping->joinTableColumns;
return 'INSERT INTO ' . $joinTable['name'] . ' (' . implode(', ', $columns) . ')' return 'INSERT INTO ' . $joinTable['name'] . ' (' . implode(', ', $columns) . ')'
. ' VALUES (' . implode(', ', array_fill(0, count($columns), '?')) . ')'; . ' VALUES (' . implode(', ', array_fill(0, count($columns), '?')) . ')';
} }
...@@ -85,16 +84,52 @@ class ManyToManyPersister extends AbstractCollectionPersister ...@@ -85,16 +84,52 @@ class ManyToManyPersister extends AbstractCollectionPersister
* {@inheritdoc} * {@inheritdoc}
* *
* @override * @override
* @internal Order of the parameters must be the same as the order of the columns in
* _getInsertRowSql.
*/ */
protected function _getInsertRowSqlParameters(PersistentCollection $coll, $element) protected function _getInsertRowSqlParameters(PersistentCollection $coll, $element)
{ {
// FIXME: This is still problematic for composite keys because we silently return $this->_collectJoinTableColumnParameters($coll, $element);
// rely on a specific ordering of the columns. }
$params = array_merge(
$this->_uow->getEntityIdentifier($coll->getOwner()), /**
$this->_uow->getEntityIdentifier($element) * Collects the parameters for inserting/deleting on the join table in the order
); * of the join table columns as specified in ManyToManyMapping#joinTableColumns.
//var_dump($params); *
* @param $coll
* @param $element
* @return array
*/
private function _collectJoinTableColumnParameters(PersistentCollection $coll, $element)
{
$params = array();
$mapping = $coll->getMapping();
$isComposite = count($mapping->joinTableColumns) > 2;
$identifier1 = $this->_uow->getEntityIdentifier($coll->getOwner());
$identifier2 = $this->_uow->getEntityIdentifier($element);
if ($isComposite) {
$class1 = $this->_em->getClassMetadata(get_class($coll->getOwner()));
$class2 = $coll->getTypeClass();
}
foreach ($mapping->joinTableColumns as $joinTableColumn) {
if (isset($mapping->relationToSourceKeyColumns[$joinTableColumn])) {
if ($isComposite) {
$params[] = $identifier1[$class1->fieldNames[$mapping->relationToSourceKeyColumns[$joinTableColumn]]];
} else {
$params[] = array_pop($identifier1);
}
} else {
if ($isComposite) {
$params[] = $identifier2[$class2->fieldNames[$mapping->relationToTargetKeyColumns[$joinTableColumn]]];
} else {
$params[] = array_pop($identifier2);
}
}
}
return $params; return $params;
} }
...@@ -108,7 +143,7 @@ class ManyToManyPersister extends AbstractCollectionPersister ...@@ -108,7 +143,7 @@ class ManyToManyPersister extends AbstractCollectionPersister
$mapping = $coll->getMapping(); $mapping = $coll->getMapping();
$joinTable = $mapping->getJoinTable(); $joinTable = $mapping->getJoinTable();
$whereClause = ''; $whereClause = '';
foreach ($mapping->sourceToRelationKeyColumns as $relationColumn) { foreach ($mapping->relationToSourceKeyColumns as $relationColumn => $srcColumn) {
if ($whereClause !== '') $whereClause .= ' AND '; if ($whereClause !== '') $whereClause .= ' AND ';
$whereClause .= "$relationColumn = ?"; $whereClause .= "$relationColumn = ?";
} }
...@@ -119,11 +154,23 @@ class ManyToManyPersister extends AbstractCollectionPersister ...@@ -119,11 +154,23 @@ class ManyToManyPersister extends AbstractCollectionPersister
* {@inheritdoc} * {@inheritdoc}
* *
* @override * @override
* @internal Order of the parameters must be the same as the order of the columns in
* _getDeleteSql.
*/ */
protected function _getDeleteSqlParameters(PersistentCollection $coll) protected function _getDeleteSqlParameters(PersistentCollection $coll)
{ {
//FIXME: This is still problematic for composite keys because we silently $params = array();
// rely on a specific ordering of the columns. $mapping = $coll->getMapping();
return $this->_uow->getEntityIdentifier($coll->getOwner()); $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]];
}
} else {
$params[] = array_pop($identifier);
}
return $params;
} }
} }
\ No newline at end of file
...@@ -21,6 +21,8 @@ ...@@ -21,6 +21,8 @@
namespace Doctrine\ORM\Persisters; namespace Doctrine\ORM\Persisters;
use Doctrine\DBAL\Types\Type;
/** /**
* Persister for entities that participate in a hierarchy mapped with the * Persister for entities that participate in a hierarchy mapped with the
* SINGLE_TABLE strategy. * SINGLE_TABLE strategy.
...@@ -44,4 +46,39 @@ class SingleTablePersister extends StandardEntityPersister ...@@ -44,4 +46,39 @@ class SingleTablePersister extends StandardEntityPersister
$this->_class->discriminatorValue; $this->_class->discriminatorValue;
} }
} }
/** @override */
protected function _getSelectColumnList()
{
$columnList = parent::_getSelectColumnList();
// Append discriminator column
$columnList .= ', ' . $this->_class->getQuotedDiscriminatorColumnName($this->_platform);
///$tableAlias = $this->_class->getQuotedTableName($this->_platform);
foreach ($this->_class->subClasses as $subClassName) {
$subClass = $this->_em->getClassMetadata($subClassName);
// Append subclass columns
foreach ($subClass->fieldMappings as $fieldName => $mapping) {
if ( ! isset($mapping['inherited'])) {
$columnList .= ', ' . $subClass->getQuotedColumnName($fieldName, $this->_platform);
}
}
// Append subclass foreign keys
foreach ($subClass->associationMappings as $assoc) {
if ($assoc->isOwningSide && $assoc->isOneToOne() && ! isset($subClass->inheritedAssociationFields[$assoc->sourceFieldName])) {
foreach ($assoc->targetToSourceKeyColumns as $srcColumn) {
$columnList .= ', ' /*. $tableAlias . '.'*/ . $assoc->getQuotedJoinColumnName($srcColumn, $this->_platform);
}
}
}
}
return $columnList;
}
/** @override */
protected function _processSqlResult(array $sqlResult)
{
return $this->_processSqlResultInheritanceAware($sqlResult);
}
} }
\ No newline at end of file
...@@ -21,13 +21,11 @@ ...@@ -21,13 +21,11 @@
namespace Doctrine\ORM\Persisters; namespace Doctrine\ORM\Persisters;
use Doctrine\Common\DoctrineException, use Doctrine\ORM\ORMException,
Doctrine\ORM\ORMException,
Doctrine\Common\Collections\ArrayCollection, Doctrine\Common\Collections\ArrayCollection,
Doctrine\DBAL\Connection, Doctrine\DBAL\Connection,
Doctrine\DBAL\Types\Type, Doctrine\DBAL\Types\Type,
Doctrine\ORM\EntityManager, Doctrine\ORM\EntityManager,
Doctrine\ORM\UnitOfWork,
Doctrine\ORM\Query, Doctrine\ORM\Query,
Doctrine\ORM\PersistentCollection, Doctrine\ORM\PersistentCollection,
Doctrine\ORM\Mapping\ClassMetadata, Doctrine\ORM\Mapping\ClassMetadata,
...@@ -43,6 +41,7 @@ use Doctrine\Common\DoctrineException, ...@@ -43,6 +41,7 @@ use Doctrine\Common\DoctrineException,
* @version $Revision: 3406 $ * @version $Revision: 3406 $
* @link www.doctrine-project.org * @link www.doctrine-project.org
* @since 2.0 * @since 2.0
* @todo Rename: BasicEntityPersister
*/ */
class StandardEntityPersister class StandardEntityPersister
{ {
...@@ -53,13 +52,6 @@ class StandardEntityPersister ...@@ -53,13 +52,6 @@ class StandardEntityPersister
*/ */
protected $_class; protected $_class;
/**
* The name of the entity the persister is used for.
*
* @var string
*/
protected $_entityName;
/** /**
* The Connection instance. * The Connection instance.
* *
...@@ -101,7 +93,6 @@ class StandardEntityPersister ...@@ -101,7 +93,6 @@ class StandardEntityPersister
$this->_em = $em; $this->_em = $em;
$this->_conn = $em->getConnection(); $this->_conn = $em->getConnection();
$this->_platform = $this->_conn->getDatabasePlatform(); $this->_platform = $this->_conn->getDatabasePlatform();
$this->_entityName = $class->name;
$this->_class = $class; $this->_class = $class;
} }
...@@ -189,10 +180,10 @@ class StandardEntityPersister ...@@ -189,10 +180,10 @@ class StandardEntityPersister
$versionField = $this->_class->versionField; $versionField = $this->_class->versionField;
$identifier = $this->_class->getIdentifierColumnNames(); $identifier = $this->_class->getIdentifierColumnNames();
$versionFieldColumnName = $this->_class->getColumnName($versionField); $versionFieldColumnName = $this->_class->getColumnName($versionField);
//FIXME: Order with composite keys might not be correct
$sql = "SELECT " . $versionFieldColumnName . " FROM " . $class->getQuotedTableName($this->_platform) . $sql = "SELECT " . $versionFieldColumnName . " FROM " . $class->getQuotedTableName($this->_platform) .
" WHERE " . implode(' = ? AND ', $identifier) . " = ?"; " WHERE " . implode(' = ? AND ', $identifier) . " = ?";
$value = $this->_conn->fetchColumn($sql, (array) $id); $value = $this->_conn->fetchColumn($sql, array_values($id));
$this->_class->setFieldValue($entity, $versionField, $value); $this->_class->setFieldValue($entity, $versionField, $value);
} }
...@@ -347,15 +338,13 @@ class StandardEntityPersister ...@@ -347,15 +338,13 @@ class StandardEntityPersister
if (isset($this->_class->associationMappings[$field])) { if (isset($this->_class->associationMappings[$field])) {
$assocMapping = $this->_class->associationMappings[$field]; $assocMapping = $this->_class->associationMappings[$field];
// Only owning side of x-1 associations can have a FK column. // Only owning side of x-1 associations can have a FK column.
if ( ! $assocMapping->isOneToOne() || $assocMapping->isInverseSide()) { if ( ! $assocMapping->isOneToOne() || ! $assocMapping->isOwningSide) {
continue; continue;
} }
// Special case: One-one self-referencing of the same class. if ($newVal !== null) {
if ($newVal !== null /*&& $assocMapping->sourceEntityName == $assocMapping->targetEntityName*/) {
$oid = spl_object_hash($newVal); $oid = spl_object_hash($newVal);
$isScheduledForInsert = $uow->isScheduledForInsert($newVal); if (isset($this->_queuedInserts[$oid]) || $uow->isScheduledForInsert($newVal)) {
if (isset($this->_queuedInserts[$oid]) || $isScheduledForInsert) {
// The associated entity $newVal is not yet persisted, so we must // The associated entity $newVal is not yet persisted, so we must
// set $newVal = null, in order to insert a null value and schedule an // set $newVal = null, in order to insert a null value and schedule an
// extra update on the UnitOfWork. // extra update on the UnitOfWork.
...@@ -366,15 +355,19 @@ class StandardEntityPersister ...@@ -366,15 +355,19 @@ class StandardEntityPersister
} }
} }
if ($newVal !== null) {
$newValId = $uow->getEntityIdentifier($newVal);
}
$targetClass = $this->_em->getClassMetadata($assocMapping->targetEntityName);
$owningTable = $this->getOwningTable($field);
foreach ($assocMapping->sourceToTargetKeyColumns as $sourceColumn => $targetColumn) { foreach ($assocMapping->sourceToTargetKeyColumns as $sourceColumn => $targetColumn) {
$quotedSourceColumn = $assocMapping->getQuotedJoinColumnName($sourceColumn, $this->_platform); $quotedSourceColumn = $assocMapping->getQuotedJoinColumnName($sourceColumn, $this->_platform);
if ($newVal === null) { if ($newVal === null) {
$result[$this->getOwningTable($field)][$quotedSourceColumn] = null; $result[$owningTable][$quotedSourceColumn] = null;
} else { } else {
$otherClass = $this->_em->getClassMetadata($assocMapping->targetEntityName); $result[$owningTable][$quotedSourceColumn] = $newValId[$targetClass->fieldNames[$targetColumn]];
$result[$this->getOwningTable($field)][$quotedSourceColumn] =
$otherClass->reflFields[$otherClass->fieldNames[$targetColumn]]
->getValue($newVal);
} }
} }
} else if ($newVal === null) { } else if ($newVal === null) {
...@@ -426,7 +419,7 @@ class StandardEntityPersister ...@@ -426,7 +419,7 @@ class StandardEntityPersister
* @param array $id The identifier of the entity as an associative array from column names to values. * @param array $id The identifier of the entity as an associative array from column names to values.
* @param object $entity The entity to refresh. * @param object $entity The entity to refresh.
*/ */
public function refresh(array $id, $entity) final public function refresh(array $id, $entity)
{ {
$stmt = $this->_conn->prepare($this->_getSelectEntitiesSql($id)); $stmt = $this->_conn->prepare($this->_getSelectEntitiesSql($id));
$stmt->execute(array_values($id)); $stmt->execute(array_values($id));
...@@ -550,7 +543,6 @@ class StandardEntityPersister ...@@ -550,7 +543,6 @@ class StandardEntityPersister
$stmt = $this->_conn->prepare($this->_getSelectManyToManyEntityCollectionSql($assoc, $criteria)); $stmt = $this->_conn->prepare($this->_getSelectManyToManyEntityCollectionSql($assoc, $criteria));
$stmt->execute(array_values($criteria)); $stmt->execute(array_values($criteria));
while ($result = $stmt->fetch(Connection::FETCH_ASSOC)) { while ($result = $stmt->fetch(Connection::FETCH_ASSOC)) {
//$coll->add($this->_createEntity($result));
$coll->hydrateAdd($this->_createEntity($result)); $coll->hydrateAdd($this->_createEntity($result));
} }
$stmt->closeCursor(); $stmt->closeCursor();
...@@ -570,38 +562,48 @@ class StandardEntityPersister ...@@ -570,38 +562,48 @@ class StandardEntityPersister
return null; return null;
} }
$data = $joinColumnValues = array(); list($entityName, $data) = $this->_processSqlResult($result);
$entityName = $this->_entityName;
foreach ($result as $column => $value) {
$column = $this->_class->resultColumnNames[$column];
if (isset($this->_class->fieldNames[$column])) {
$fieldName = $this->_class->fieldNames[$column];
$data[$fieldName] = Type::getType($this->_class->fieldMappings[$fieldName]['type'])
->convertToPHPValue($value, $this->_platform);
} else if ($this->_class->discriminatorColumn !== null && $column == $this->_class->discriminatorColumn['name']) {
$entityName = $this->_class->discriminatorMap[$value];
} else {
$data[$column] = $value;
$joinColumnValues[$column] = $value;
}
}
if ($entity !== null) { if ($entity !== null) {
$hints[Query::HINT_REFRESH] = true; $hints[Query::HINT_REFRESH] = true;
$id = array(); $id = array();
if ($this->_class->isIdentifierComposite) { if ($this->_class->isIdentifierComposite) {
foreach ($this->_class->identifier as $fieldName) { foreach ($this->_class->identifier as $fieldName) {
$id[] = $data[$fieldName]; $id[$fieldName] = $data[$fieldName];
} }
} else { } else {
$id = array($data[$this->_class->identifier[0]]); $id = array($this->_class->identifier[0] => $data[$this->_class->identifier[0]]);
} }
$this->_em->getUnitOfWork()->registerManaged($entity, $id, $data); $this->_em->getUnitOfWork()->registerManaged($entity, $id, $data);
} }
return $this->_em->getUnitOfWork()->createEntity($entityName, $data, $hints); return $this->_em->getUnitOfWork()->createEntity($entityName, $data, $hints);
} }
/**
* Processes an SQL result set row that contains data for an entity of the type
* this persister is responsible for.
*
* @param array $sqlResult The SQL result set row to process.
* @return array A tuple where the first value is the actual type of the entity and
* the second value the data of the entity.
*/
protected function _processSqlResult(array $sqlResult)
{
$data = array();
foreach ($sqlResult as $column => $value) {
$column = $this->_class->resultColumnNames[$column];
if (isset($this->_class->fieldNames[$column])) {
$field = $this->_class->fieldNames[$column];
$data[$field] = Type::getType($this->_class->fieldMappings[$field]['type'])
->convertToPHPValue($value, $this->_platform);
} else {
$data[$column] = $value;
}
}
return array($this->_class->name, $data);
}
/** /**
* Gets the SELECT SQL to select one or more entities by a set of field criteria. * Gets the SELECT SQL to select one or more entities by a set of field criteria.
...@@ -611,23 +613,6 @@ class StandardEntityPersister ...@@ -611,23 +613,6 @@ class StandardEntityPersister
*/ */
protected function _getSelectEntitiesSql(array &$criteria, $assoc = null) protected function _getSelectEntitiesSql(array &$criteria, $assoc = null)
{ {
$columnList = '';
// Add regular columns to select list
foreach ($this->_class->fieldNames as $field) {
if ($columnList != '') $columnList .= ', ';
$columnList .= $this->_class->getQuotedColumnName($field, $this->_platform);
}
// Add join columns (foreign keys) to select list
foreach ($this->_class->associationMappings as $assoc2) {
if ($assoc2->isOwningSide && $assoc2->isOneToOne()) {
foreach ($assoc2->targetToSourceKeyColumns as $srcColumn) {
$columnList .= ', ' . $assoc2->getQuotedJoinColumnName($srcColumn, $this->_platform);
}
}
}
// Construct WHERE conditions // Construct WHERE conditions
$conditionSql = ''; $conditionSql = '';
foreach ($criteria as $field => $value) { foreach ($criteria as $field => $value) {
...@@ -647,11 +632,39 @@ class StandardEntityPersister ...@@ -647,11 +632,39 @@ class StandardEntityPersister
$conditionSql .= ' = ?'; $conditionSql .= ' = ?';
} }
return 'SELECT ' . $columnList return 'SELECT ' . $this->_getSelectColumnList()
. ' FROM ' . $this->_class->getQuotedTableName($this->_platform) . ' FROM ' . $this->_class->getQuotedTableName($this->_platform)
. ($conditionSql ? ' WHERE ' . $conditionSql : ''); . ($conditionSql ? ' WHERE ' . $conditionSql : '');
} }
/**
* Gets the SQL fragment with the list of columns to select when querying for
* a entity of the type of this persister.
*
* @return string The SQL fragment.
*/
protected function _getSelectColumnList()
{
$columnList = '';
// Add regular columns to select list
foreach ($this->_class->fieldNames as $field) {
if ($columnList != '') $columnList .= ', ';
$columnList .= $this->_class->getQuotedColumnName($field, $this->_platform);
}
// Add join columns (foreign keys) to select list
foreach ($this->_class->associationMappings as $assoc) {
if ($assoc->isOwningSide && $assoc->isOneToOne()) {
foreach ($assoc->targetToSourceKeyColumns as $srcColumn) {
$columnList .= ', ' . $assoc->getQuotedJoinColumnName($srcColumn, $this->_platform);
}
}
}
return $columnList;
}
/** /**
* Gets the SQL to select a collection of entities in a many-many association. * Gets the SQL to select a collection of entities in a many-many association.
* *
...@@ -678,16 +691,16 @@ class StandardEntityPersister ...@@ -678,16 +691,16 @@ class StandardEntityPersister
if ($manyToMany->isOwningSide) { if ($manyToMany->isOwningSide) {
$owningAssoc = $manyToMany; $owningAssoc = $manyToMany;
$joinClauses = $manyToMany->targetToRelationKeyColumns; $joinClauses = $manyToMany->relationToTargetKeyColumns;
} else { } else {
$owningAssoc = $this->_em->getClassMetadata($manyToMany->targetEntityName)->associationMappings[$manyToMany->mappedByFieldName]; $owningAssoc = $this->_em->getClassMetadata($manyToMany->targetEntityName)->associationMappings[$manyToMany->mappedByFieldName];
$joinClauses = $owningAssoc->sourceToRelationKeyColumns; $joinClauses = $owningAssoc->relationToSourceKeyColumns;
} }
$joinTableName = $owningAssoc->getQuotedJoinTableName($this->_platform); $joinTableName = $owningAssoc->getQuotedJoinTableName($this->_platform);
$joinSql = ''; $joinSql = '';
foreach ($joinClauses as $sourceColumn => $joinTableColumn) { foreach ($joinClauses as $joinTableColumn => $sourceColumn) {
if ($joinSql != '') $joinSql .= ' AND '; if ($joinSql != '') $joinSql .= ' AND ';
$joinSql .= $this->_class->getQuotedTableName($this->_platform) . $joinSql .= $this->_class->getQuotedTableName($this->_platform) .
'.' . $this->_class->getQuotedColumnName($this->_class->fieldNames[$sourceColumn], $this->_platform) . ' = ' '.' . $this->_class->getQuotedColumnName($this->_class->fieldNames[$sourceColumn], $this->_platform) . ' = '
...@@ -710,4 +723,50 @@ class StandardEntityPersister ...@@ -710,4 +723,50 @@ class StandardEntityPersister
. $joinSql . $joinSql
. ' WHERE ' . $conditionSql; . ' WHERE ' . $conditionSql;
} }
/** @override */
final protected function _processSqlResultInheritanceAware(array $sqlResult)
{
$data = array();
$entityName = $this->_class->name;
foreach ($sqlResult as $column => $value) {
$column = $this->_class->resultColumnNames[$column];
if (($class = $this->_findDeclaringClass($column)) !== false) {
$field = $class->fieldNames[$column];
$data[$field] = Type::getType($class->fieldMappings[$field]['type'])
->convertToPHPValue($value, $this->_platform);
} else if ($column == $this->_class->discriminatorColumn['name']) {
$entityName = $this->_class->discriminatorMap[$value];
} else {
$data[$column] = $value;
}
}
return array($entityName, $data);
}
private function _findDeclaringClass($column)
{
static $cache = array();
if (isset($cache[$column])) {
return $cache[$column];
}
if (isset($this->_class->fieldNames[$column])) {
$cache[$column] = $this->_class;
return $this->_class;
}
foreach ($this->_class->subClasses as $subClassName) {
$subClass = $this->_em->getClassMetadata($subClassName);
if (isset($subClass->fieldNames[$column])) {
$cache[$column] = $subClass;
return $subClass;
}
}
$cache[$column] = false;
return false;
}
} }
<?php <?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Proxy; namespace Doctrine\ORM\Proxy;
/** /**
* Marker interface for proxy classes. * Interface for proxy classes.
* *
* @author Roman Borschel <roman@code-factory.org> * @author Roman Borschel <roman@code-factory.org>
* @since 2.0 * @since 2.0
......
...@@ -208,12 +208,12 @@ final class Query extends AbstractQuery ...@@ -208,12 +208,12 @@ final class Query extends AbstractQuery
$paramMappings = $this->_parserResult->getParameterMappings(); $paramMappings = $this->_parserResult->getParameterMappings();
if(count($paramMappings) != count($params)) { if (count($paramMappings) != count($params)) {
throw QueryException::invalidParameterNumber(); throw QueryException::invalidParameterNumber();
} }
foreach ($params as $key => $value) { foreach ($params as $key => $value) {
if(!isset($paramMappings[$key])) { if ( ! isset($paramMappings[$key])) {
throw QueryException::unknownParameter($key); throw QueryException::unknownParameter($key);
} }
......
...@@ -251,7 +251,7 @@ class SqlWalker implements TreeWalker ...@@ -251,7 +251,7 @@ class SqlWalker implements TreeWalker
} }
} }
// LEFT JOIN subclass tables, only if partial objects disallowed // LEFT JOIN subclass tables, if partial objects disallowed
if ( ! $this->_query->getHint(Query::HINT_FORCE_PARTIAL_LOAD)) { if ( ! $this->_query->getHint(Query::HINT_FORCE_PARTIAL_LOAD)) {
foreach ($class->subClasses as $subClassName) { foreach ($class->subClasses as $subClassName) {
$subClass = $this->_em->getClassMetadata($subClassName); $subClass = $this->_em->getClassMetadata($subClassName);
...@@ -327,7 +327,7 @@ class SqlWalker implements TreeWalker ...@@ -327,7 +327,7 @@ class SqlWalker implements TreeWalker
$sql .= $AST->orderByClause ? $this->walkOrderByClause($AST->orderByClause) : ''; $sql .= $AST->orderByClause ? $this->walkOrderByClause($AST->orderByClause) : '';
$q = $this->getQuery(); $q = $this->getQuery();
$sql = $this->getConnection()->getDatabasePlatform()->modifyLimitQuery( $sql = $this->_platform->modifyLimitQuery(
$sql, $q->getMaxResults(), $q->getFirstResult() $sql, $q->getMaxResults(), $q->getFirstResult()
); );
...@@ -678,13 +678,13 @@ class SqlWalker implements TreeWalker ...@@ -678,13 +678,13 @@ class SqlWalker implements TreeWalker
$sql .= $assoc->getQuotedJoinTableName($this->_platform) . ' ' . $joinTableAlias . ' ON '; $sql .= $assoc->getQuotedJoinTableName($this->_platform) . ' ' . $joinTableAlias . ' ON ';
if ($relation->isOwningSide) { if ($relation->isOwningSide) {
foreach ($assoc->sourceToRelationKeyColumns as $sourceColumn => $relationColumn) { foreach ($assoc->relationToSourceKeyColumns as $relationColumn => $sourceColumn) {
$sql .= $sourceTableAlias . '.' . $sourceClass->getQuotedColumnName($sourceClass->fieldNames[$sourceColumn], $this->_platform) $sql .= $sourceTableAlias . '.' . $sourceClass->getQuotedColumnName($sourceClass->fieldNames[$sourceColumn], $this->_platform)
. ' = ' . ' = '
. $joinTableAlias . '.' . $assoc->getQuotedJoinColumnName($relationColumn, $this->_platform); . $joinTableAlias . '.' . $assoc->getQuotedJoinColumnName($relationColumn, $this->_platform);
} }
} else { } else {
foreach ($assoc->targetToRelationKeyColumns as $targetColumn => $relationColumn) { foreach ($assoc->relationToTargetKeyColumns as $relationColumn => $targetColumn) {
$sql .= $sourceTableAlias . '.' . $targetClass->getQuotedColumnName($targetClass->fieldNames[$targetColumn], $this->_platform) $sql .= $sourceTableAlias . '.' . $targetClass->getQuotedColumnName($targetClass->fieldNames[$targetColumn], $this->_platform)
. ' = ' . ' = '
. $joinTableAlias . '.' . $assoc->getQuotedJoinColumnName($relationColumn, $this->_platform); . $joinTableAlias . '.' . $assoc->getQuotedJoinColumnName($relationColumn, $this->_platform);
...@@ -697,13 +697,13 @@ class SqlWalker implements TreeWalker ...@@ -697,13 +697,13 @@ class SqlWalker implements TreeWalker
$sql .= $targetTableName . ' ' . $targetTableAlias . ' ON '; $sql .= $targetTableName . ' ' . $targetTableAlias . ' ON ';
if ($relation->isOwningSide) { if ($relation->isOwningSide) {
foreach ($assoc->targetToRelationKeyColumns as $targetColumn => $relationColumn) { foreach ($assoc->relationToTargetKeyColumns as $relationColumn => $targetColumn) {
$sql .= $targetTableAlias . '.' . $targetClass->getQuotedColumnName($targetClass->fieldNames[$targetColumn], $this->_platform) $sql .= $targetTableAlias . '.' . $targetClass->getQuotedColumnName($targetClass->fieldNames[$targetColumn], $this->_platform)
. ' = ' . ' = '
. $joinTableAlias . '.' . $assoc->getQuotedJoinColumnName($relationColumn, $this->_platform); . $joinTableAlias . '.' . $assoc->getQuotedJoinColumnName($relationColumn, $this->_platform);
} }
} else { } else {
foreach ($assoc->sourceToRelationKeyColumns as $sourceColumn => $relationColumn) { foreach ($assoc->relationToSourceKeyColumns as $relationColumn => $sourceColumn) {
$sql .= $targetTableAlias . '.' . $sourceClass->getQuotedColumnName($sourceClass->fieldNames[$sourceColumn], $this->_platform) $sql .= $targetTableAlias . '.' . $sourceClass->getQuotedColumnName($sourceClass->fieldNames[$sourceColumn], $this->_platform)
. ' = ' . ' = '
. $joinTableAlias . '.' . $assoc->getQuotedJoinColumnName($relationColumn, $this->_platform); . $joinTableAlias . '.' . $assoc->getQuotedJoinColumnName($relationColumn, $this->_platform);
...@@ -787,7 +787,7 @@ class SqlWalker implements TreeWalker ...@@ -787,7 +787,7 @@ class SqlWalker implements TreeWalker
$columnAlias = $this->_platform->getSqlResultCasing($columnAlias); $columnAlias = $this->_platform->getSqlResultCasing($columnAlias);
$this->_rsm->addScalarResult($columnAlias, $resultAlias); $this->_rsm->addScalarResult($columnAlias, $resultAlias);
} else { } else {
// IdentificationVariable // $expr == IdentificationVariable
$dqlAlias = $expr; $dqlAlias = $expr;
$queryComp = $this->_queryComponents[$dqlAlias]; $queryComp = $this->_queryComponents[$dqlAlias];
$class = $queryComp['metadata']; $class = $queryComp['metadata'];
......
...@@ -23,7 +23,6 @@ namespace Doctrine\ORM; ...@@ -23,7 +23,6 @@ namespace Doctrine\ORM;
use Doctrine\Common\Collections\ArrayCollection, use Doctrine\Common\Collections\ArrayCollection,
Doctrine\Common\Collections\Collection, Doctrine\Common\Collections\Collection,
Doctrine\Common\DoctrineException,
Doctrine\Common\NotifyPropertyChanged, Doctrine\Common\NotifyPropertyChanged,
Doctrine\Common\PropertyChangedListener, Doctrine\Common\PropertyChangedListener,
Doctrine\ORM\Event\LifecycleEventArgs, Doctrine\ORM\Event\LifecycleEventArgs,
...@@ -405,6 +404,10 @@ class UnitOfWork implements PropertyChangedListener ...@@ -405,6 +404,10 @@ class UnitOfWork implements PropertyChangedListener
$this->_scheduledForDirtyCheck[$className] : $entities; $this->_scheduledForDirtyCheck[$className] : $entities;
foreach ($entitiesToProcess as $entity) { foreach ($entitiesToProcess as $entity) {
// Ignore uninitialized proxy objects
if ($entity instanceof Proxy && ! $entity->__isInitialized__()) {
continue;
}
// Only MANAGED entities that are NOT SCHEDULED FOR INSERTION are processed here. // Only MANAGED entities that are NOT SCHEDULED FOR INSERTION are processed here.
$oid = spl_object_hash($entity); $oid = spl_object_hash($entity);
if ( ! isset($this->_entityInsertions[$oid]) && isset($this->_entityStates[$oid])) { if ( ! isset($this->_entityInsertions[$oid]) && isset($this->_entityStates[$oid])) {
...@@ -580,7 +583,7 @@ class UnitOfWork implements PropertyChangedListener ...@@ -580,7 +583,7 @@ class UnitOfWork implements PropertyChangedListener
$idValue = $idGen->generate($this->_em, $entry); $idValue = $idGen->generate($this->_em, $entry);
$this->_entityStates[$oid] = self::STATE_MANAGED; $this->_entityStates[$oid] = self::STATE_MANAGED;
if ( ! $idGen instanceof \Doctrine\ORM\Id\Assigned) { if ( ! $idGen instanceof \Doctrine\ORM\Id\Assigned) {
$this->_entityIdentifiers[$oid] = array($idValue); $this->_entityIdentifiers[$oid] = array($targetClass->identifier[0] => $idValue);
$targetClass->getSingleIdReflectionProperty()->setValue($entry, $idValue); $targetClass->getSingleIdReflectionProperty()->setValue($entry, $idValue);
} else { } else {
$this->_entityIdentifiers[$oid] = $idValue; $this->_entityIdentifiers[$oid] = $idValue;
...@@ -601,7 +604,7 @@ class UnitOfWork implements PropertyChangedListener ...@@ -601,7 +604,7 @@ class UnitOfWork implements PropertyChangedListener
} }
} else if ($state == self::STATE_REMOVED) { } else if ($state == self::STATE_REMOVED) {
throw DoctrineException::removedEntityInCollectionDetected(); throw ORMException::removedEntityInCollectionDetected($entity, $assoc);
} }
// MANAGED associated entities are already taken into account // MANAGED associated entities are already taken into account
// during changeset calculation anyway, since they are in the identity map. // during changeset calculation anyway, since they are in the identity map.
...@@ -700,7 +703,7 @@ class UnitOfWork implements PropertyChangedListener ...@@ -700,7 +703,7 @@ class UnitOfWork implements PropertyChangedListener
$oid = spl_object_hash($entity); $oid = spl_object_hash($entity);
$idField = $class->identifier[0]; $idField = $class->identifier[0];
$class->reflFields[$idField]->setValue($entity, $id); $class->reflFields[$idField]->setValue($entity, $id);
$this->_entityIdentifiers[$oid] = array($id); $this->_entityIdentifiers[$oid] = array($idField => $id);
$this->_entityStates[$oid] = self::STATE_MANAGED; $this->_entityStates[$oid] = self::STATE_MANAGED;
$this->_originalEntityData[$oid][$idField] = $id; $this->_originalEntityData[$oid][$idField] = $id;
$this->addToIdentityMap($entity); $this->addToIdentityMap($entity);
...@@ -1198,7 +1201,7 @@ class UnitOfWork implements PropertyChangedListener ...@@ -1198,7 +1201,7 @@ class UnitOfWork implements PropertyChangedListener
if ( ! $idGen->isPostInsertGenerator()) { if ( ! $idGen->isPostInsertGenerator()) {
$idValue = $idGen->generate($this->_em, $entity); $idValue = $idGen->generate($this->_em, $entity);
if ( ! $idGen instanceof \Doctrine\ORM\Id\Assigned) { if ( ! $idGen instanceof \Doctrine\ORM\Id\Assigned) {
$this->_entityIdentifiers[$oid] = array($idValue); $this->_entityIdentifiers[$oid] = array($class->identifier[0] => $idValue);
$class->setIdentifierValues($entity, $idValue); $class->setIdentifierValues($entity, $idValue);
} else { } else {
$this->_entityIdentifiers[$oid] = $idValue; $this->_entityIdentifiers[$oid] = $idValue;
...@@ -1359,7 +1362,7 @@ class UnitOfWork implements PropertyChangedListener ...@@ -1359,7 +1362,7 @@ class UnitOfWork implements PropertyChangedListener
$id = $targetClass->getIdentifierValues($other); $id = $targetClass->getIdentifierValues($other);
$proxy = $this->_em->getProxyFactory()->getProxy($assoc2->targetEntityName, $id); $proxy = $this->_em->getProxyFactory()->getProxy($assoc2->targetEntityName, $id);
$prop->setValue($managedCopy, $proxy); $prop->setValue($managedCopy, $proxy);
$this->registerManaged($proxy, (array)$id, array()); $this->registerManaged($proxy, $id, array());
} }
} }
} else { } else {
...@@ -1701,12 +1704,12 @@ class UnitOfWork implements PropertyChangedListener ...@@ -1701,12 +1704,12 @@ class UnitOfWork implements PropertyChangedListener
if ($class->isIdentifierComposite) { if ($class->isIdentifierComposite) {
$id = array(); $id = array();
foreach ($class->identifier as $fieldName) { foreach ($class->identifier as $fieldName) {
$id[] = $data[$fieldName]; $id[$fieldName] = $data[$fieldName];
} }
$idHash = implode(' ', $id); $idHash = implode(' ', $id);
} else { } else {
$id = array($data[$class->identifier[0]]); $idHash = $data[$class->identifier[0]];
$idHash = $id[0]; $id = array($class->identifier[0] => $idHash);
} }
if (isset($this->_identityMap[$class->rootEntityName][$idHash])) { if (isset($this->_identityMap[$class->rootEntityName][$idHash])) {
...@@ -1754,7 +1757,7 @@ class UnitOfWork implements PropertyChangedListener ...@@ -1754,7 +1757,7 @@ class UnitOfWork implements PropertyChangedListener
foreach ($assoc->targetToSourceKeyColumns as $targetColumn => $srcColumn) { foreach ($assoc->targetToSourceKeyColumns as $targetColumn => $srcColumn) {
$joinColumnValue = $data[$srcColumn]; $joinColumnValue = $data[$srcColumn];
if ($joinColumnValue !== null) { if ($joinColumnValue !== null) {
$associatedId[$targetColumn] = $joinColumnValue; $associatedId[$targetClass->fieldNames[$targetColumn]] = $joinColumnValue;
} }
} }
if ( ! $associatedId) { if ( ! $associatedId) {
......
...@@ -20,6 +20,7 @@ class AllTests ...@@ -20,6 +20,7 @@ class AllTests
$suite = new \Doctrine\Tests\OrmFunctionalTestSuite('Doctrine Orm Functional'); $suite = new \Doctrine\Tests\OrmFunctionalTestSuite('Doctrine Orm Functional');
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\BasicFunctionalTest'); $suite->addTestSuite('Doctrine\Tests\ORM\Functional\BasicFunctionalTest');
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\DefaultValuesTest');
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\AdvancedAssociationTest'); $suite->addTestSuite('Doctrine\Tests\ORM\Functional\AdvancedAssociationTest');
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\NativeQueryTest'); $suite->addTestSuite('Doctrine\Tests\ORM\Functional\NativeQueryTest');
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\SingleTableInheritanceTest'); $suite->addTestSuite('Doctrine\Tests\ORM\Functional\SingleTableInheritanceTest');
......
...@@ -509,4 +509,37 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase ...@@ -509,4 +509,37 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase
"select count(u.id) from Doctrine\Tests\Models\CMS\CmsUser u") "select count(u.id) from Doctrine\Tests\Models\CMS\CmsUser u")
->getSingleScalarResult()); ->getSingleScalarResult());
} }
//DRAFT OF EXPECTED/DESIRED BEHAVIOR
/*public function testPersistentCollectionContainsDoesNeverInitialize()
{
$user = new CmsUser;
$user->name = 'Guilherme';
$user->username = 'gblanco';
$user->status = 'developer';
$group = new CmsGroup;
$group->name = 'Developers';
$user->addGroup($group);
$this->_em->persist($user);
$this->_em->flush();
$this->_em->clear();
$group = $this->_em->find(get_class($group), $group->getId());
$user2 = new CmsUser;
$user2->id = $user->getId();
$this->assertFalse($group->getUsers()->contains($user2));
$this->assertFalse($group->getUsers()->isInitialized());
$user2 = $this->_em->getReference(get_class($user), $user->getId());
$this->assertTrue($group->getUsers()->contains($user2));
$this->assertFalse($group->getUsers()->isInitialized());
}
*/
} }
...@@ -49,7 +49,6 @@ class ClassTableInheritanceTest2 extends \Doctrine\Tests\OrmFunctionalTestCase ...@@ -49,7 +49,6 @@ class ClassTableInheritanceTest2 extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertSame($related2, $related2->getCTIParent()->getRelated()); $this->assertSame($related2, $related2->getCTIParent()->getRelated());
} }
} }
/** /**
......
<?php
namespace Doctrine\Tests\ORM\Functional;
require_once __DIR__ . '/../../TestInit.php';
/**
* Tests basic operations on entities with default values.
*
* @author robo
*/
class DefaultValuesTest extends \Doctrine\Tests\OrmFunctionalTestCase
{
protected function setUp() {
parent::setUp();
try {
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata('Doctrine\Tests\ORM\Functional\DefaultValueUser'),
$this->_em->getClassMetadata('Doctrine\Tests\ORM\Functional\DefaultValueAddress')
));
} catch (\Exception $e) {
// Swallow all exceptions. We do not test the schema tool here.
}
}
public function testSimpleDetachMerge() {
$user = new DefaultValueUser;
$user->name = 'romanb';
$this->_em->persist($user);
$this->_em->flush();
$this->_em->clear();
$userId = $user->id; // e.g. from $_REQUEST
$user2 = $this->_em->getReference(get_class($user), $userId);
$this->_em->flush();
$this->assertFalse($user2->__isInitialized__());
$a = new DefaultValueAddress;
$a->country = 'de';
$a->zip = '12345';
$a->city = 'Berlin';
$a->street = 'Sesamestreet';
$a->user = $user2;
$this->_em->persist($a);
$this->_em->flush();
$this->assertFalse($user2->__isInitialized__());
$this->_em->clear();
$a2 = $this->_em->find(get_class($a), $a->id);
$this->assertTrue($a2->getUser() instanceof DefaultValueUser);
$this->assertEquals($userId, $a2->getUser()->getId());
$this->assertEquals('Poweruser', $a2->getUser()->type);
}
}
/**
* @Entity @Table(name="defaultvalueuser")
*/
class DefaultValueUser
{
/**
* @Id @Column(type="integer")
* @GeneratedValue(strategy="AUTO")
*/
public $id;
/**
* @Column(type="string")
*/
public $name = '';
/**
* @Column(type="string")
*/
public $type = 'Poweruser';
/**
* @OneToOne(targetEntity="DefaultValueAddress", mappedBy="user", cascade={"persist"})
*/
public $address;
public function getId() {return $this->id;}
}
/**
* CmsAddress
*
* @Entity @Table(name="defaultvalueaddresses")
*/
class DefaultValueAddress
{
/**
* @Column(type="integer")
* @Id @GeneratedValue(strategy="AUTO")
*/
public $id;
/**
* @Column(type="string", length=50)
*/
public $country;
/**
* @Column(type="string", length=50)
*/
public $zip;
/**
* @Column(type="string", length=50)
*/
public $city;
/**
* Testfield for Schema Updating Tests.
*/
public $street;
/**
* @OneToOne(targetEntity="DefaultValueUser")
* @JoinColumn(name="user_id", referencedColumnName="id")
*/
public $user;
public function getUser() {return $this->user;}
}
...@@ -137,6 +137,24 @@ class SingleTableInheritanceTest extends \Doctrine\Tests\OrmFunctionalTestCase ...@@ -137,6 +137,24 @@ class SingleTableInheritanceTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertNull($result[0]['e_related_entity_id']); $this->assertNull($result[0]['e_related_entity_id']);
$this->assertEquals('child', $result[0]['e_discr']); $this->assertEquals('child', $result[0]['e_discr']);
} }
public function testPolymorphicFind()
{
$child = new ChildEntity;
$child->setData('thedata');
$child->setNumber(1234);
$this->_em->persist($child);
$this->_em->flush();
$this->_em->clear();
$child2 = $this->_em->find('Doctrine\Tests\ORM\Functional\ParentEntity', $child->getId());
$this->assertTrue($child2 instanceof ChildEntity);
$this->assertEquals('thedata', $child2->getData());
$this->assertSame(1234, $child2->getNumber());
}
} }
/** /**
......
...@@ -56,7 +56,7 @@ class TestUtil ...@@ -56,7 +56,7 @@ class TestUtil
// Connect to tmpdb in order to drop and create the real test db. // Connect to tmpdb in order to drop and create the real test db.
$tmpConn = \Doctrine\DBAL\DriverManager::getConnection($tmpDbParams); $tmpConn = \Doctrine\DBAL\DriverManager::getConnection($tmpDbParams);
$realConn = \Doctrine\DBAL\DriverManager::getConnection($realDbParams); $realConn = \Doctrine\DBAL\DriverManager::getConnection($realDbParams);
$dbname = $realConn->getDatabase(); $dbname = $realConn->getDatabase();
$realConn->close(); $realConn->close();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment