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) {
$xml = simplexml_load_file($logsPath . $rev . '/log.xml');
foreach ($xml->testsuite as $suite) {
foreach ($suite->testcase as $test) {
$name = (string)$suite['name'] . '#' . (string)$test['name'];
$graphs[$name][] = (double)$test['time'];
if (stripos((string)$suite['name'], 'performance') !== false || stripos((string)$test['name'], 'performance') !== false) {
$name = (string)$suite['name'] . '#' . (string)$test['name'];
$graphs[$name][] = (double)$test['time'];
}
}
}
}
......
......@@ -4,7 +4,9 @@ namespace Doctrine\DBAL\Types;
/**
* Type that maps an SQL INT to a PHP integer.
*
*
* @author Roman Borschel <roman@code-factory.org>
* @since 2.0
*/
class IntegerType extends Type
{
......
......@@ -124,7 +124,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
*/
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->setDefaultAnnotationNamespace('Doctrine\ORM\Mapping\\');
$this->_attributes['metadataDriverImpl'] = new \Doctrine\ORM\Mapping\Driver\AnnotationDriver($reader);
......
......@@ -121,12 +121,12 @@ final class ClassMetadata extends ClassMetadataInfo
* Gets the ReflectionProperty for the single identifier field.
*
* @return ReflectionProperty
* @throws DoctrineException If the class has a composite identifier.
* @throws BadMethodCallException If the class has a composite identifier.
*/
public function getSingleIdReflectionProperty()
{
if ($this->isIdentifierComposite) {
throw DoctrineException::singleIdNotAllowedOnCompositePrimaryKey();
throw new \BadMethodCallException("Class " . $this->name . " has a composite identifier.");
}
return $this->reflFields[$this->identifier[0]];
}
......@@ -163,12 +163,12 @@ final class ClassMetadata extends ClassMetadataInfo
foreach ($this->identifier as $idField) {
$value = $this->reflFields[$idField]->getValue($entity);
if ($value !== null) {
$id[] = $value;
$id[$idField] = $value;
}
}
return $id;
} 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
if (($cached = $this->_cacheDriver->fetch($cacheKey)) !== false) {
$this->_loadedMetadata[$className] = $cached;
} else {
$this->_loadMetadata($className);
$this->_cacheDriver->save($cacheKey, $this->_loadedMetadata[$className], null);
foreach ($this->_loadMetadata($className) as $loadedClassName) {
$this->_cacheDriver->save($cacheKey, $this->_loadedMetadata[$className], null);
}
}
} else {
$this->_loadMetadata($className);
......@@ -151,6 +152,8 @@ class ClassMetadataFactory
*/
protected function _loadMetadata($name)
{
$loaded = array();
// Collect parent classes, ignoring transient (not-mapped) classes.
$parentClass = $name;
$parentClasses = array();
......@@ -241,7 +244,11 @@ class ClassMetadataFactory
if ( ! $class->isMappedSuperclass) {
array_unshift($visited, $className);
}
$loaded[] = $className;
}
return $loaded;
}
/**
......
......@@ -264,6 +264,9 @@ class ClassMetadataInfo
* as any join columns and discriminator columns.
*
* @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();
......@@ -486,7 +489,7 @@ class ClassMetadataInfo
* @param string $fieldName
* @return string
*/
public function getOwningClass($fieldName)
/*public function getOwningClass($fieldName)
{
if ($this->inheritanceType == self::INHERITANCE_TYPE_NONE) {
return $this->name;
......@@ -494,7 +497,7 @@ class ClassMetadataInfo
$mapping = $this->getFieldMapping($fieldName);
return $mapping['inherited'];
}
}
}*/
/**
* Gets the name of the root class of the mapped entity hierarchy. If the entity described
......
......@@ -40,24 +40,14 @@ namespace Doctrine\ORM\Mapping;
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();
/**
* 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();
public $relationToTargetKeyColumns = array();
/**
* List of aggregated column names on the join table.
......@@ -105,46 +95,35 @@ class ManyToManyMapping extends AssociationMapping
$joinColumn['name'] = trim($joinColumn['name'], '`');
$joinColumn['quoted'] = true;
}
$this->sourceToRelationKeyColumns[$joinColumn['referencedColumnName']] = $joinColumn['name'];
$this->relationToSourceKeyColumns[$joinColumn['name']] = $joinColumn['referencedColumnName'];
$this->joinTableColumns[] = $joinColumn['name'];
}
$this->sourceKeyColumns = array_keys($this->sourceToRelationKeyColumns);
foreach ($mapping['joinTable']['inverseJoinColumns'] as &$inverseJoinColumn) {
if ($inverseJoinColumn['name'][0] == '`') {
$inverseJoinColumn['name'] = trim($inverseJoinColumn['name'], '`');
$inverseJoinColumn['quoted'] = true;
}
$this->targetToRelationKeyColumns[$inverseJoinColumn['referencedColumnName']] = $inverseJoinColumn['name'];
$this->relationToTargetKeyColumns[$inverseJoinColumn['name']] = $inverseJoinColumn['referencedColumnName'];
$this->joinTableColumns[] = $inverseJoinColumn['name'];
}
$this->targetKeyColumns = array_keys($this->targetToRelationKeyColumns);
}
}
public function getJoinTableColumnNames()
{
return $this->joinTableColumns;
//return array_merge(array_keys($this->relationToSourceKeyColumns), array_keys($this->relationToTargetKeyColumns));
}
public function getSourceToRelationKeyColumns()
{
return $this->sourceToRelationKeyColumns;
}
public function getTargetToRelationKeyColumns()
{
return $this->targetToRelationKeyColumns;
}
public function getSourceKeyColumns()
public function getRelationToSourceKeyColumns()
{
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
$sourceClass = $em->getClassMetadata($this->sourceEntityName);
$joinTableConditions = array();
if ($this->isOwningSide) {
foreach ($this->sourceToRelationKeyColumns as $sourceKeyColumn => $relationKeyColumn) {
foreach ($this->relationToSourceKeyColumns as $relationKeyColumn => $sourceKeyColumn) {
// getting id
if (isset($sourceClass->fieldNames[$sourceKeyColumn])) {
$joinTableConditions[$relationKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);
......@@ -172,7 +151,7 @@ class ManyToManyMapping extends AssociationMapping
} else {
$owningAssoc = $em->getClassMetadata($this->targetEntityName)->associationMappings[$this->mappedByFieldName];
// 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
if (isset($sourceClass->fieldNames[$sourceKeyColumn])) {
$joinTableConditions[$relationKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);
......
......@@ -162,11 +162,18 @@ final class PersistentCollection implements \Doctrine\Common\Collections\Collect
* Gets the class descriptor for the owning entity class.
*
* @return Doctrine\ORM\Mapping\ClassMetadata
* @deprecated
* @todo Remove
*/
public function getOwnerClass()
{
return $this->_typeClass;
}
public function getTypeClass()
{
return $this->_typeClass;
}
/**
* INTERNAL:
......@@ -430,13 +437,22 @@ final class PersistentCollection implements \Doctrine\Common\Collections\Collect
*/
public function contains($element)
{
// TODO: Assuming the identity of entities in a collection is always based
// on their primary key (there is no equals/hashCode in PHP),
// if the collection is not initialized, we could issue a straight
// SQL "SELECT 1" on the association (table) without initializing
// the collection.
// TODO: Change to use PK identity, not php object identity!?
/* DRAFT
if ($this->_initialized) {
return $this->_coll->contains($element);
} else {
if ($element is MANAGED) {
if ($this->_coll->contains($element)) {
return true;
}
$exists = check db for existence;
if ($exists) {
$this->_coll->add($element);
}
return $exists;
}
return false;
}*/
$this->_initialize();
return $this->_coll->contains($element);
......
......@@ -48,13 +48,13 @@ abstract class AbstractCollectionPersister
$this->_conn = $em->getConnection();
}
public function recreate(PersistentCollection $coll)
/*public function recreate(PersistentCollection $coll)
{
if ($coll->getRelation()->isInverseSide()) {
return;
}
//...
}
}*/
/**
* Deletes the persistent state represented by the given collection.
......@@ -110,8 +110,8 @@ abstract class AbstractCollectionPersister
}
}
public function updateRows(PersistentCollection $coll)
{}
//public function updateRows(PersistentCollection $coll)
//{}
public function insertRows(PersistentCollection $coll)
{
......
......@@ -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
*/
public function getOwningTable($fieldName)
......@@ -372,4 +375,10 @@ class JoinedSubclassPersister extends StandardEntityPersister
. $joinSql
. ($conditionSql != '' ? ' WHERE ' . $conditionSql : '');
}
/** @override */
protected function _processSqlResult(array $sqlResult)
{
return $this->_processSqlResultInheritanceAware($sqlResult);
}
}
......@@ -48,15 +48,12 @@ class ManyToManyPersister extends AbstractCollectionPersister
* {@inheritdoc}
*
* @override
* @internal Order of the parameters must be the same as the order of the columns in
* _getDeleteRowSql.
*/
protected function _getDeleteRowSqlParameters(PersistentCollection $coll, $element)
{
$params = array_merge(
$this->_uow->getEntityIdentifier($coll->getOwner()),
$this->_uow->getEntityIdentifier($element)
);
//var_dump($params);
return $params;
return $this->_collectJoinTableColumnParameters($coll, $element);
}
/**
......@@ -71,12 +68,14 @@ class ManyToManyPersister extends AbstractCollectionPersister
* {@inheritdoc}
*
* @override
* @internal Order of the parameters must be the same as the order of the columns in
* _getInsertRowSql.
*/
protected function _getInsertRowSql(PersistentCollection $coll)
{
$mapping = $coll->getMapping();
$joinTable = $mapping->getJoinTable();
$columns = $mapping->getJoinTableColumnNames();
$columns = $mapping->joinTableColumns;
return 'INSERT INTO ' . $joinTable['name'] . ' (' . implode(', ', $columns) . ')'
. ' VALUES (' . implode(', ', array_fill(0, count($columns), '?')) . ')';
}
......@@ -85,16 +84,52 @@ class ManyToManyPersister extends AbstractCollectionPersister
* {@inheritdoc}
*
* @override
* @internal Order of the parameters must be the same as the order of the columns in
* _getInsertRowSql.
*/
protected function _getInsertRowSqlParameters(PersistentCollection $coll, $element)
{
// FIXME: This is still problematic for composite keys because we silently
// rely on a specific ordering of the columns.
$params = array_merge(
$this->_uow->getEntityIdentifier($coll->getOwner()),
$this->_uow->getEntityIdentifier($element)
);
//var_dump($params);
return $this->_collectJoinTableColumnParameters($coll, $element);
}
/**
* Collects the parameters for inserting/deleting on the join table in the order
* of the join table columns as specified in ManyToManyMapping#joinTableColumns.
*
* @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;
}
......@@ -108,7 +143,7 @@ class ManyToManyPersister extends AbstractCollectionPersister
$mapping = $coll->getMapping();
$joinTable = $mapping->getJoinTable();
$whereClause = '';
foreach ($mapping->sourceToRelationKeyColumns as $relationColumn) {
foreach ($mapping->relationToSourceKeyColumns as $relationColumn => $srcColumn) {
if ($whereClause !== '') $whereClause .= ' AND ';
$whereClause .= "$relationColumn = ?";
}
......@@ -119,11 +154,23 @@ class ManyToManyPersister extends AbstractCollectionPersister
* {@inheritdoc}
*
* @override
* @internal Order of the parameters must be the same as the order of the columns in
* _getDeleteSql.
*/
protected function _getDeleteSqlParameters(PersistentCollection $coll)
{
//FIXME: This is still problematic for composite keys because we silently
// rely on a specific ordering of the columns.
return $this->_uow->getEntityIdentifier($coll->getOwner());
$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]];
}
} else {
$params[] = array_pop($identifier);
}
return $params;
}
}
\ No newline at end of file
......@@ -21,6 +21,8 @@
namespace Doctrine\ORM\Persisters;
use Doctrine\DBAL\Types\Type;
/**
* Persister for entities that participate in a hierarchy mapped with the
* SINGLE_TABLE strategy.
......@@ -44,4 +46,39 @@ class SingleTablePersister extends StandardEntityPersister
$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 @@
namespace Doctrine\ORM\Persisters;
use Doctrine\Common\DoctrineException,
Doctrine\ORM\ORMException,
use Doctrine\ORM\ORMException,
Doctrine\Common\Collections\ArrayCollection,
Doctrine\DBAL\Connection,
Doctrine\DBAL\Types\Type,
Doctrine\ORM\EntityManager,
Doctrine\ORM\UnitOfWork,
Doctrine\ORM\Query,
Doctrine\ORM\PersistentCollection,
Doctrine\ORM\Mapping\ClassMetadata,
......@@ -43,6 +41,7 @@ use Doctrine\Common\DoctrineException,
* @version $Revision: 3406 $
* @link www.doctrine-project.org
* @since 2.0
* @todo Rename: BasicEntityPersister
*/
class StandardEntityPersister
{
......@@ -53,13 +52,6 @@ class StandardEntityPersister
*/
protected $_class;
/**
* The name of the entity the persister is used for.
*
* @var string
*/
protected $_entityName;
/**
* The Connection instance.
*
......@@ -101,7 +93,6 @@ class StandardEntityPersister
$this->_em = $em;
$this->_conn = $em->getConnection();
$this->_platform = $this->_conn->getDatabasePlatform();
$this->_entityName = $class->name;
$this->_class = $class;
}
......@@ -189,10 +180,10 @@ class StandardEntityPersister
$versionField = $this->_class->versionField;
$identifier = $this->_class->getIdentifierColumnNames();
$versionFieldColumnName = $this->_class->getColumnName($versionField);
//FIXME: Order with composite keys might not be correct
$sql = "SELECT " . $versionFieldColumnName . " FROM " . $class->getQuotedTableName($this->_platform) .
" 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);
}
......@@ -347,15 +338,13 @@ class StandardEntityPersister
if (isset($this->_class->associationMappings[$field])) {
$assocMapping = $this->_class->associationMappings[$field];
// Only owning side of x-1 associations can have a FK column.
if ( ! $assocMapping->isOneToOne() || $assocMapping->isInverseSide()) {
if ( ! $assocMapping->isOneToOne() || ! $assocMapping->isOwningSide) {
continue;
}
// Special case: One-one self-referencing of the same class.
if ($newVal !== null /*&& $assocMapping->sourceEntityName == $assocMapping->targetEntityName*/) {
if ($newVal !== null) {
$oid = spl_object_hash($newVal);
$isScheduledForInsert = $uow->isScheduledForInsert($newVal);
if (isset($this->_queuedInserts[$oid]) || $isScheduledForInsert) {
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
// extra update on the UnitOfWork.
......@@ -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) {
$quotedSourceColumn = $assocMapping->getQuotedJoinColumnName($sourceColumn, $this->_platform);
if ($newVal === null) {
$result[$this->getOwningTable($field)][$quotedSourceColumn] = null;
$result[$owningTable][$quotedSourceColumn] = null;
} else {
$otherClass = $this->_em->getClassMetadata($assocMapping->targetEntityName);
$result[$this->getOwningTable($field)][$quotedSourceColumn] =
$otherClass->reflFields[$otherClass->fieldNames[$targetColumn]]
->getValue($newVal);
$result[$owningTable][$quotedSourceColumn] = $newValId[$targetClass->fieldNames[$targetColumn]];
}
}
} else if ($newVal === null) {
......@@ -426,7 +419,7 @@ class StandardEntityPersister
* @param array $id The identifier of the entity as an associative array from column names to values.
* @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->execute(array_values($id));
......@@ -550,7 +543,6 @@ class StandardEntityPersister
$stmt = $this->_conn->prepare($this->_getSelectManyToManyEntityCollectionSql($assoc, $criteria));
$stmt->execute(array_values($criteria));
while ($result = $stmt->fetch(Connection::FETCH_ASSOC)) {
//$coll->add($this->_createEntity($result));
$coll->hydrateAdd($this->_createEntity($result));
}
$stmt->closeCursor();
......@@ -570,38 +562,48 @@ class StandardEntityPersister
return null;
}
$data = $joinColumnValues = array();
$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;
}
}
list($entityName, $data) = $this->_processSqlResult($result);
if ($entity !== null) {
$hints[Query::HINT_REFRESH] = true;
$id = array();
if ($this->_class->isIdentifierComposite) {
foreach ($this->_class->identifier as $fieldName) {
$id[] = $data[$fieldName];
$id[$fieldName] = $data[$fieldName];
}
} 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);
}
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.
......@@ -611,23 +613,6 @@ class StandardEntityPersister
*/
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
$conditionSql = '';
foreach ($criteria as $field => $value) {
......@@ -647,11 +632,39 @@ class StandardEntityPersister
$conditionSql .= ' = ?';
}
return 'SELECT ' . $columnList
return 'SELECT ' . $this->_getSelectColumnList()
. ' FROM ' . $this->_class->getQuotedTableName($this->_platform)
. ($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.
*
......@@ -678,16 +691,16 @@ class StandardEntityPersister
if ($manyToMany->isOwningSide) {
$owningAssoc = $manyToMany;
$joinClauses = $manyToMany->targetToRelationKeyColumns;
$joinClauses = $manyToMany->relationToTargetKeyColumns;
} else {
$owningAssoc = $this->_em->getClassMetadata($manyToMany->targetEntityName)->associationMappings[$manyToMany->mappedByFieldName];
$joinClauses = $owningAssoc->sourceToRelationKeyColumns;
$joinClauses = $owningAssoc->relationToSourceKeyColumns;
}
$joinTableName = $owningAssoc->getQuotedJoinTableName($this->_platform);
$joinSql = '';
foreach ($joinClauses as $sourceColumn => $joinTableColumn) {
foreach ($joinClauses as $joinTableColumn => $sourceColumn) {
if ($joinSql != '') $joinSql .= ' AND ';
$joinSql .= $this->_class->getQuotedTableName($this->_platform) .
'.' . $this->_class->getQuotedColumnName($this->_class->fieldNames[$sourceColumn], $this->_platform) . ' = '
......@@ -710,4 +723,50 @@ class StandardEntityPersister
. $joinSql
. ' 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
/*
* $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;
/**
* Marker interface for proxy classes.
* Interface for proxy classes.
*
* @author Roman Borschel <roman@code-factory.org>
* @since 2.0
......
......@@ -208,12 +208,12 @@ final class Query extends AbstractQuery
$paramMappings = $this->_parserResult->getParameterMappings();
if(count($paramMappings) != count($params)) {
if (count($paramMappings) != count($params)) {
throw QueryException::invalidParameterNumber();
}
foreach ($params as $key => $value) {
if(!isset($paramMappings[$key])) {
if ( ! isset($paramMappings[$key])) {
throw QueryException::unknownParameter($key);
}
......
......@@ -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)) {
foreach ($class->subClasses as $subClassName) {
$subClass = $this->_em->getClassMetadata($subClassName);
......@@ -327,7 +327,7 @@ class SqlWalker implements TreeWalker
$sql .= $AST->orderByClause ? $this->walkOrderByClause($AST->orderByClause) : '';
$q = $this->getQuery();
$sql = $this->getConnection()->getDatabasePlatform()->modifyLimitQuery(
$sql = $this->_platform->modifyLimitQuery(
$sql, $q->getMaxResults(), $q->getFirstResult()
);
......@@ -678,13 +678,13 @@ class SqlWalker implements TreeWalker
$sql .= $assoc->getQuotedJoinTableName($this->_platform) . ' ' . $joinTableAlias . ' ON ';
if ($relation->isOwningSide) {
foreach ($assoc->sourceToRelationKeyColumns as $sourceColumn => $relationColumn) {
foreach ($assoc->relationToSourceKeyColumns as $relationColumn => $sourceColumn) {
$sql .= $sourceTableAlias . '.' . $sourceClass->getQuotedColumnName($sourceClass->fieldNames[$sourceColumn], $this->_platform)
. ' = '
. $joinTableAlias . '.' . $assoc->getQuotedJoinColumnName($relationColumn, $this->_platform);
}
} else {
foreach ($assoc->targetToRelationKeyColumns as $targetColumn => $relationColumn) {
foreach ($assoc->relationToTargetKeyColumns as $relationColumn => $targetColumn) {
$sql .= $sourceTableAlias . '.' . $targetClass->getQuotedColumnName($targetClass->fieldNames[$targetColumn], $this->_platform)
. ' = '
. $joinTableAlias . '.' . $assoc->getQuotedJoinColumnName($relationColumn, $this->_platform);
......@@ -697,13 +697,13 @@ class SqlWalker implements TreeWalker
$sql .= $targetTableName . ' ' . $targetTableAlias . ' ON ';
if ($relation->isOwningSide) {
foreach ($assoc->targetToRelationKeyColumns as $targetColumn => $relationColumn) {
foreach ($assoc->relationToTargetKeyColumns as $relationColumn => $targetColumn) {
$sql .= $targetTableAlias . '.' . $targetClass->getQuotedColumnName($targetClass->fieldNames[$targetColumn], $this->_platform)
. ' = '
. $joinTableAlias . '.' . $assoc->getQuotedJoinColumnName($relationColumn, $this->_platform);
}
} else {
foreach ($assoc->sourceToRelationKeyColumns as $sourceColumn => $relationColumn) {
foreach ($assoc->relationToSourceKeyColumns as $relationColumn => $sourceColumn) {
$sql .= $targetTableAlias . '.' . $sourceClass->getQuotedColumnName($sourceClass->fieldNames[$sourceColumn], $this->_platform)
. ' = '
. $joinTableAlias . '.' . $assoc->getQuotedJoinColumnName($relationColumn, $this->_platform);
......@@ -787,7 +787,7 @@ class SqlWalker implements TreeWalker
$columnAlias = $this->_platform->getSqlResultCasing($columnAlias);
$this->_rsm->addScalarResult($columnAlias, $resultAlias);
} else {
// IdentificationVariable
// $expr == IdentificationVariable
$dqlAlias = $expr;
$queryComp = $this->_queryComponents[$dqlAlias];
$class = $queryComp['metadata'];
......
......@@ -23,7 +23,6 @@ namespace Doctrine\ORM;
use Doctrine\Common\Collections\ArrayCollection,
Doctrine\Common\Collections\Collection,
Doctrine\Common\DoctrineException,
Doctrine\Common\NotifyPropertyChanged,
Doctrine\Common\PropertyChangedListener,
Doctrine\ORM\Event\LifecycleEventArgs,
......@@ -405,6 +404,10 @@ class UnitOfWork implements PropertyChangedListener
$this->_scheduledForDirtyCheck[$className] : $entities;
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.
$oid = spl_object_hash($entity);
if ( ! isset($this->_entityInsertions[$oid]) && isset($this->_entityStates[$oid])) {
......@@ -580,7 +583,7 @@ class UnitOfWork implements PropertyChangedListener
$idValue = $idGen->generate($this->_em, $entry);
$this->_entityStates[$oid] = self::STATE_MANAGED;
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);
} else {
$this->_entityIdentifiers[$oid] = $idValue;
......@@ -601,7 +604,7 @@ class UnitOfWork implements PropertyChangedListener
}
} else if ($state == self::STATE_REMOVED) {
throw DoctrineException::removedEntityInCollectionDetected();
throw ORMException::removedEntityInCollectionDetected($entity, $assoc);
}
// MANAGED associated entities are already taken into account
// during changeset calculation anyway, since they are in the identity map.
......@@ -700,7 +703,7 @@ class UnitOfWork implements PropertyChangedListener
$oid = spl_object_hash($entity);
$idField = $class->identifier[0];
$class->reflFields[$idField]->setValue($entity, $id);
$this->_entityIdentifiers[$oid] = array($id);
$this->_entityIdentifiers[$oid] = array($idField => $id);
$this->_entityStates[$oid] = self::STATE_MANAGED;
$this->_originalEntityData[$oid][$idField] = $id;
$this->addToIdentityMap($entity);
......@@ -1198,7 +1201,7 @@ class UnitOfWork implements PropertyChangedListener
if ( ! $idGen->isPostInsertGenerator()) {
$idValue = $idGen->generate($this->_em, $entity);
if ( ! $idGen instanceof \Doctrine\ORM\Id\Assigned) {
$this->_entityIdentifiers[$oid] = array($idValue);
$this->_entityIdentifiers[$oid] = array($class->identifier[0] => $idValue);
$class->setIdentifierValues($entity, $idValue);
} else {
$this->_entityIdentifiers[$oid] = $idValue;
......@@ -1359,7 +1362,7 @@ class UnitOfWork implements PropertyChangedListener
$id = $targetClass->getIdentifierValues($other);
$proxy = $this->_em->getProxyFactory()->getProxy($assoc2->targetEntityName, $id);
$prop->setValue($managedCopy, $proxy);
$this->registerManaged($proxy, (array)$id, array());
$this->registerManaged($proxy, $id, array());
}
}
} else {
......@@ -1701,12 +1704,12 @@ class UnitOfWork implements PropertyChangedListener
if ($class->isIdentifierComposite) {
$id = array();
foreach ($class->identifier as $fieldName) {
$id[] = $data[$fieldName];
$id[$fieldName] = $data[$fieldName];
}
$idHash = implode(' ', $id);
} else {
$id = array($data[$class->identifier[0]]);
$idHash = $id[0];
$idHash = $data[$class->identifier[0]];
$id = array($class->identifier[0] => $idHash);
}
if (isset($this->_identityMap[$class->rootEntityName][$idHash])) {
......@@ -1754,7 +1757,7 @@ class UnitOfWork implements PropertyChangedListener
foreach ($assoc->targetToSourceKeyColumns as $targetColumn => $srcColumn) {
$joinColumnValue = $data[$srcColumn];
if ($joinColumnValue !== null) {
$associatedId[$targetColumn] = $joinColumnValue;
$associatedId[$targetClass->fieldNames[$targetColumn]] = $joinColumnValue;
}
}
if ( ! $associatedId) {
......
......@@ -20,6 +20,7 @@ class AllTests
$suite = new \Doctrine\Tests\OrmFunctionalTestSuite('Doctrine Orm Functional');
$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\NativeQueryTest');
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\SingleTableInheritanceTest');
......
......@@ -509,4 +509,37 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase
"select count(u.id) from Doctrine\Tests\Models\CMS\CmsUser u")
->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
$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
$this->assertNull($result[0]['e_related_entity_id']);
$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
// Connect to tmpdb in order to drop and create the real test db.
$tmpConn = \Doctrine\DBAL\DriverManager::getConnection($tmpDbParams);
$realConn = \Doctrine\DBAL\DriverManager::getConnection($realDbParams);
$dbname = $realConn->getDatabase();
$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