From 886c9611083e511c36d12d490b98f8b13aa83d60 Mon Sep 17 00:00:00 2001 From: romanb Date: Wed, 4 Feb 2009 16:35:36 +0000 Subject: [PATCH] [2.0] Continued work on association mappings and class exporting (DDL generation). Fixed #1863. --- lib/Doctrine/DBAL/Connection.php | 3 +- lib/Doctrine/DBAL/Driver/PDOMySql/Driver.php | 24 +++ .../DBAL/Platforms/AbstractPlatform.php | 4 +- .../DBAL/Platforms/SqlitePlatform.php | 17 ++- lib/Doctrine/ORM/EntityManager.php | 45 +++--- lib/Doctrine/ORM/Export/ClassExporter.php | 141 +++++++++--------- lib/Doctrine/ORM/Id/Assigned.php | 19 +++ .../ORM/Mapping/AssociationMapping.php | 48 ++---- lib/Doctrine/ORM/Mapping/ClassMetadata.php | 90 ++++++----- .../ORM/Mapping/Driver/AnnotationDriver.php | 84 +++++++++-- .../Mapping/Driver/DoctrineAnnotations.php | 50 +++++-- .../ORM/Mapping/ManyToManyMapping.php | 134 +++++++++++------ lib/Doctrine/ORM/Mapping/OneToManyMapping.php | 7 +- lib/Doctrine/ORM/Mapping/OneToOneMapping.php | 30 +++- lib/Doctrine/ORM/PersistentCollection.php | 25 +++- .../AbstractCollectionPersister.php | 36 ++++- .../Persisters/AbstractEntityPersister.php | 2 +- .../Persisters/ElementCollectionPersister.php | 33 ++++ .../ORM/Persisters/ManyToManyPersister.php | 19 +++ .../ORM/Persisters/OneToManyPersister.php | 20 ++- lib/Doctrine/ORM/UnitOfWork.php | 13 +- .../Doctrine/Tests/Models/CMS/CmsAddress.php | 8 +- .../Doctrine/Tests/Models/CMS/CmsArticle.php | 9 +- .../Doctrine/Tests/Models/CMS/CmsComment.php | 7 +- tests/Doctrine/Tests/Models/CMS/CmsGroup.php | 33 ++++ .../Tests/Models/CMS/CmsPhonenumber.php | 7 +- tests/Doctrine/Tests/Models/CMS/CmsUser.php | 18 ++- .../Tests/Models/Forum/ForumAvatar.php | 3 +- .../Tests/Models/Forum/ForumBoard.php | 5 +- .../Tests/Models/Forum/ForumCategory.php | 3 +- .../Tests/Models/Forum/ForumEntry.php | 1 + .../Doctrine/Tests/Models/Forum/ForumUser.php | 9 +- .../ORM/Associations/OneToOneMappingTest.php | 2 +- .../Tests/ORM/Mapping/ClassMetadataTest.php | 3 +- 34 files changed, 651 insertions(+), 301 deletions(-) create mode 100644 lib/Doctrine/ORM/Persisters/ElementCollectionPersister.php create mode 100644 tests/Doctrine/Tests/Models/CMS/CmsGroup.php diff --git a/lib/Doctrine/DBAL/Connection.php b/lib/Doctrine/DBAL/Connection.php index 322567b2e..43a4aaa7d 100644 --- a/lib/Doctrine/DBAL/Connection.php +++ b/lib/Doctrine/DBAL/Connection.php @@ -16,7 +16,7 @@ * * This software consists of voluntary contributions made by many individuals * and is licensed under the LGPL. For more information, see - * . + * . */ namespace Doctrine\DBAL; @@ -317,7 +317,6 @@ class Connection // column names are specified as array keys $cols = array(); - // the query VALUES will contain either expressions (eg 'NOW()') or ? $a = array(); foreach ($data as $columnName => $value) { $cols[] = $this->quoteIdentifier($columnName); diff --git a/lib/Doctrine/DBAL/Driver/PDOMySql/Driver.php b/lib/Doctrine/DBAL/Driver/PDOMySql/Driver.php index 4875cf0c8..e49024a0a 100644 --- a/lib/Doctrine/DBAL/Driver/PDOMySql/Driver.php +++ b/lib/Doctrine/DBAL/Driver/PDOMySql/Driver.php @@ -1,7 +1,31 @@ . + */ namespace Doctrine\DBAL\Driver\PDOMySql; +/** + * PDO MySql driver. + * + * @since 2.0 + */ class Driver implements \Doctrine\DBAL\Driver { /** diff --git a/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php b/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php index 25c7ad882..04d5f335c 100644 --- a/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php +++ b/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php @@ -16,7 +16,7 @@ * * This software consists of voluntary contributions made by many individuals * and is licensed under the LGPL. For more information, see - * . + * . */ namespace Doctrine\DBAL\Platforms; @@ -1591,7 +1591,7 @@ abstract class AbstractPlatform $sql .= implode(', ', array_map(array($this, 'quoteIdentifier'), $definition['local'])) . ') REFERENCES ' - . $this->_conn->quoteIdentifier($definition['foreignTable']) . '(' + . $this->quoteIdentifier($definition['foreignTable']) . '(' . implode(', ', array_map(array($this, 'quoteIdentifier'), $definition['foreign'])) . ')'; return $sql; diff --git a/lib/Doctrine/DBAL/Platforms/SqlitePlatform.php b/lib/Doctrine/DBAL/Platforms/SqlitePlatform.php index 4dbf12dff..30e3df91f 100644 --- a/lib/Doctrine/DBAL/Platforms/SqlitePlatform.php +++ b/lib/Doctrine/DBAL/Platforms/SqlitePlatform.php @@ -431,9 +431,7 @@ class SqlitePlatform extends AbstractPlatform protected function _getCommonIntegerTypeDeclarationSql(array $columnDef) { $autoinc = ! empty($columnDef['autoincrement']) ? 'AUTOINCREMENT' : ''; - $pk = ! empty($columnDef['primary']) ? 'PRIMARY KEY' : ''; - - //$unsigned = (isset($columnDef['unsigned']) && $columnDef['unsigned']) ? ' UNSIGNED' : ''; + $pk = ! empty($columnDef['primary']) && ! empty($autoinc) ? 'PRIMARY KEY' : ''; return "INTEGER $pk $autoinc"; } @@ -533,5 +531,18 @@ class SqlitePlatform extends AbstractPlatform return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)') : ($length ? 'VARCHAR(' . $length . ')' : 'TEXT'); } + + /** + * SQLite does support foreign key constraints, but only in CREATE TABLE statements... + * This really limits their usefulness and requires SQLite specific handling, so + * we simply say that SQLite does NOT support foreign keys for now... + * + * @return boolean + * @override + */ + public function supportsForeignKeyConstraints() + { + return false; + } } diff --git a/lib/Doctrine/ORM/EntityManager.php b/lib/Doctrine/ORM/EntityManager.php index 5a0d2aa86..7be4db1fb 100644 --- a/lib/Doctrine/ORM/EntityManager.php +++ b/lib/Doctrine/ORM/EntityManager.php @@ -16,7 +16,7 @@ * * This software consists of voluntary contributions made by many individuals * and is licensed under the LGPL. For more information, see - * . + * . */ namespace Doctrine\ORM; @@ -138,7 +138,9 @@ class EntityManager */ private $_hydrators = array(); - /** Whether the EntityManager is closed or not. */ + /** + * Whether the EntityManager is closed or not. + */ private $_closed = false; /** @@ -150,11 +152,7 @@ class EntityManager * @param Doctrine\ORM\Configuration $config * @param Doctrine\Common\EventManager $eventManager */ - protected function __construct( - Connection $conn, - $name, - Configuration $config, - EventManager $eventManager) + protected function __construct(Connection $conn, $name, Configuration $config, EventManager $eventManager) { $this->_conn = $conn; $this->_name = $name; @@ -207,6 +205,7 @@ class EntityManager /** * Commits a running transaction. + * * This causes a flush() of the EntityManager if the flush mode is set to * AUTO or COMMIT. * @@ -245,7 +244,7 @@ class EntityManager } /** - * Used to lazily create the id generator. + * Used to lazily create an ID generator. * * @param string $generatorType * @return object @@ -281,7 +280,7 @@ class EntityManager /** * Detaches an entity from the manager. It's lifecycle is no longer managed. * - * @param Doctrine\ORM\Entity $entity + * @param object $entity * @return boolean */ public function detach($entity) @@ -337,6 +336,7 @@ class EntityManager /** * Finds an Entity by its identifier. + * * This is just a convenient shortcut for getRepository($entityName)->find($id). * * @param string $entityName @@ -349,7 +349,7 @@ class EntityManager } /** - * Sets the flush mode. + * Sets the flush mode to use. * * @param string $flushMode */ @@ -437,21 +437,21 @@ class EntityManager * Refreshes the persistent state of the entity from the database, * overriding any local changes that have not yet been persisted. * - * @param Doctrine\ORM\Entity $entity + * @param object $entity * @todo FIX Impl */ - public function refresh(Doctrine_ORM_Entity $entity) + public function refresh($entity) { - $this->_mergeData($entity, $entity->getRepository()->find( + /*$this->_mergeData($entity, $this->getRepository(get_class($entity))->find( $entity->identifier(), Query::HYDRATE_ARRAY), - true); + true);*/ } /** * Creates a copy of the given entity. Can create a shallow or a deep copy. * - * @param Doctrine\ORM\Entity $entity The entity to copy. - * @return Doctrine\ORM\Entity The new entity. + * @param object $entity The entity to copy. + * @return object The new entity. */ public function copy($entity, $deep = false) { @@ -561,16 +561,16 @@ class EntityManager case Query::HYDRATE_OBJECT: $this->_hydrators[$hydrationMode] = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this); break; - case Doctrine_ORM_Query::HYDRATE_ARRAY: + case Query::HYDRATE_ARRAY: $this->_hydrators[$hydrationMode] = new \Doctrine\ORM\Internal\Hydration\ArrayHydrator($this); break; - case Doctrine_ORM_Query::HYDRATE_SCALAR: + case Query::HYDRATE_SCALAR: $this->_hydrators[$hydrationMode] = new \Doctrine\ORM\Internal\Hydration\ScalarHydrator($this); break; - case Doctrine_ORM_Query::HYDRATE_SINGLE_SCALAR: + case Query::HYDRATE_SINGLE_SCALAR: $this->_hydrators[$hydrationMode] = new \Doctrine\ORM\Internal\Hydration\SingleScalarHydrator($this); break; - case Doctrine_ORM_Query::HYDRATE_NONE: + case Query::HYDRATE_NONE: $this->_hydrators[$hydrationMode] = new \Doctrine\ORM\Internal\Hydration\NoneHydrator($this); break; default: @@ -617,10 +617,7 @@ class EntityManager * @param EventManager $eventManager The EventManager instance to use. * @return EntityManager The created EntityManager. */ - public static function create( - $conn, - $name, - Configuration $config = null, + public static function create($conn, $name, Configuration $config = null, EventManager $eventManager = null) { if (is_array($conn)) { diff --git a/lib/Doctrine/ORM/Export/ClassExporter.php b/lib/Doctrine/ORM/Export/ClassExporter.php index 0508ecd52..523c1d2d2 100644 --- a/lib/Doctrine/ORM/Export/ClassExporter.php +++ b/lib/Doctrine/ORM/Export/ClassExporter.php @@ -21,6 +21,8 @@ namespace Doctrine\ORM\Export; +use Doctrine\ORM\EntityManager; + /** * The ClassExporter can generate database schemas/structures from ClassMetadata * class descriptors. @@ -41,25 +43,32 @@ class ClassExporter private $_sm; /** The EntityManager */ private $_em; + /** The DatabasePlatform */ + private $_platform; - public function __construct(\Doctrine\ORM\EntityManager $em) + /** + * Initializes a new ClassExporter instance that uses the connection of the + * provided EntityManager. + * + * @param Doctrine\ORM\EntityManager $em + */ + public function __construct(EntityManager $em) { $this->_em = $em; $this->_sm = $em->getConnection()->getSchemaManager(); + $this->_platform = $em->getConnection()->getDatabasePlatform(); } /** - * Exports entity classes to a schema. - * - * FIXME: This method is a big huge hack. The sql needs to be executed in the correct order. I have some stupid logic to - * make sure they are in the right order. + * Exports entity classes to a database, according to the specified mappings. * * @param array $classes - * @return void */ public function exportClasses(array $classes) { - //TODO: order them + $foreignKeyConstraints = array(); + + // First we create the tables foreach ($classes as $class) { $columns = array(); $options = array(); @@ -70,92 +79,84 @@ class ClassExporter $column['type'] = $mapping['type']; $column['length'] = $mapping['length']; $column['notnull'] = ! $mapping['nullable']; - if ($class->isIdentifier($fieldName)) { $column['primary'] = true; if ($class->isIdGeneratorIdentity()) { $column['autoincrement'] = true; } } - $columns[$mapping['columnName']] = $column; } foreach ($class->getAssociationMappings() as $mapping) { + $foreignClass = $this->_em->getClassMetadata($mapping->getTargetEntityName()); if ($mapping->isOneToOne() && $mapping->isOwningSide()) { - foreach ($mapping->getSourceToTargetKeyColumns() as $sourceColumn => $targetColumn) { + $constraint = array(); + $constraint['tableName'] = $class->getTableName(); + $constraint['foreignTable'] = $foreignClass->getTableName(); + $constraint['local'] = array(); + $constraint['foreign'] = array(); + foreach ($mapping->getJoinColumns() as $joinColumn) { $column = array(); - $column['name'] = $sourceColumn; - $column['type'] = $this->_em->getClassMetadata($mapping->getTargetEntityName()) - ->getTypeOfColumn($targetColumn); - $columns[$sourceColumn] = $column; + $column['name'] = $joinColumn['name']; + $column['type'] = $foreignClass->getTypeOfColumn($joinColumn['referencedColumnName']); + $columns[$joinColumn['name']] = $column; + $constraint['local'][] = $joinColumn['name']; + $constraint['foreign'][] = $joinColumn['referencedColumnName']; } - } else if ($mapping->isOneToMany() && $mapping->usesJoinTable()) { + $foreignKeyConstraints[] = $constraint; + } else if ($mapping->isOneToMany() && $mapping->isOwningSide()) { //... create join table, one-many through join table supported later - throw new Doctrine_Exception("Not yet implemented."); + throw new DoctrineException("Not yet implemented."); } else if ($mapping->isManyToMany() && $mapping->isOwningSide()) { //... create join table + $joinTableColumns = array(); + $joinTable = $mapping->getJoinTable(); + $constraint1 = array(); + $constraint1['tableName'] = $joinTable['name']; + $constraint1['foreignTable'] = $class->getTableName(); + $constraint1['local'] = array(); + $constraint1['foreign'] = array(); + foreach ($joinTable['joinColumns'] as $joinColumn) { + $column = array(); + $column['primary'] = true; + $column['name'] = $joinColumn['name']; + $column['type'] = $class->getTypeOfColumn($joinColumn['referencedColumnName']); + $joinTableColumns[$joinColumn['name']] = $column; + $constraint1['local'][] = $joinColumn['name']; + $constraint1['foreign'][] = $joinColumn['referencedColumnName']; + } + $foreignKeyConstraints[] = $constraint1; + + $constraint2 = array(); + $constraint2['tableName'] = $joinTable['name']; + $constraint2['foreignTable'] = $foreignClass->getTableName(); + $constraint2['local'] = array(); + $constraint2['foreign'] = array(); + foreach ($joinTable['inverseJoinColumns'] as $inverseJoinColumn) { + $column = array(); + $column['primary'] = true; + $column['name'] = $inverseJoinColumn['name']; + $column['type'] = $this->_em->getClassMetadata($mapping->getTargetEntityName()) + ->getTypeOfColumn($inverseJoinColumn['referencedColumnName']); + $joinTableColumns[$inverseJoinColumn['name']] = $column; + $constraint2['local'][] = $inverseJoinColumn['name']; + $constraint2['foreign'][] = $inverseJoinColumn['referencedColumnName']; + } + $foreignKeyConstraints[] = $constraint2; + + $this->_sm->createTable($joinTable['name'], $joinTableColumns, array()); } } $this->_sm->createTable($class->getTableName(), $columns, $options); } - } - /** - * exportClassesSql - * method for exporting entity classes to a schema - * - * @throws Doctrine_Connection_Exception if some error other than Doctrine::ERR_ALREADY_EXISTS - * occurred during the create table operation - * @param array $classes - * @return void - */ - public function exportClassesSql(array $classes) - { - $models = Doctrine::filterInvalidModels($classes); - - $sql = array(); - $finishedClasses = array(); - - foreach ($models as $name) { - if (in_array($name, $finishedClasses)) { - continue; - } - - $classMetadata = $this->conn->getClassMetadata($name); - - // In Class Table Inheritance we have to make sure that ALL tables of parent classes - // are exported, too as soon as ONE table is exported, because the data of one class is stored - // across many tables. - if ($classMetadata->getInheritanceType() == Doctrine::INHERITANCE_TYPE_JOINED) { - $parents = $classMetadata->getParentClasses(); - foreach ($parents as $parent) { - $data = $classMetadata->getConnection()->getClassMetadata($parent)->getExportableFormat(); - $query = $this->conn->export->createTableSql($data['tableName'], $data['columns'], $data['options']); - $sql = array_merge($sql, (array) $query); - $finishedClasses[] = $parent; - } - } - - $data = $classMetadata->getExportableFormat(); - $query = $this->conn->export->createTableSql($data['tableName'], $data['columns'], $data['options']); - - if (is_array($query)) { - $sql = array_merge($sql, $query); - } else { - $sql[] = $query; - } - - if ($classMetadata->getAttribute(Doctrine::ATTR_EXPORT) & Doctrine::EXPORT_PLUGINS) { - $sql = array_merge($sql, $this->exportGeneratorsSql($classMetadata)); + // Now create the foreign key constraints + if ($this->_platform->supportsForeignKeyConstraints()) { + foreach ($foreignKeyConstraints as $fkConstraint) { + $this->_sm->createForeignKey($fkConstraint['tableName'], $fkConstraint); } } - - $sql = array_unique($sql); - - rsort($sql); - - return $sql; } } diff --git a/lib/Doctrine/ORM/Id/Assigned.php b/lib/Doctrine/ORM/Id/Assigned.php index feea25f6d..ed718cda3 100644 --- a/lib/Doctrine/ORM/Id/Assigned.php +++ b/lib/Doctrine/ORM/Id/Assigned.php @@ -1,4 +1,23 @@ . + */ namespace Doctrine\ORM\Id; diff --git a/lib/Doctrine/ORM/Mapping/AssociationMapping.php b/lib/Doctrine/ORM/Mapping/AssociationMapping.php index 565d208fe..b40d68af4 100644 --- a/lib/Doctrine/ORM/Mapping/AssociationMapping.php +++ b/lib/Doctrine/ORM/Mapping/AssociationMapping.php @@ -21,6 +21,8 @@ namespace Doctrine\ORM\Mapping; +use Doctrine\ORM\Exceptions\MappingException; + /** * Base class for association mappings. * @@ -107,11 +109,11 @@ abstract class AssociationMapping protected $_mappedByFieldName; /** - * The name of the join table, if any. + * The join table definition, if any. * - * @var string + * @var array */ - protected $_joinTable; + protected $_joinTable = array(); /** * Initializes a new instance of a class derived from AssociationMapping. @@ -132,17 +134,17 @@ abstract class AssociationMapping { // Mandatory attributes for both sides if ( ! isset($mapping['fieldName'])) { - throw Doctrine_MappingException::missingFieldName(); + throw MappingException::missingFieldName(); } $this->_sourceFieldName = $mapping['fieldName']; if ( ! isset($mapping['sourceEntity'])) { - throw Doctrine_MappingException::missingSourceEntity($mapping['fieldName']); + throw MappingException::missingSourceEntity($mapping['fieldName']); } $this->_sourceEntityName = $mapping['sourceEntity']; if ( ! isset($mapping['targetEntity'])) { - throw Doctrine_ORM_Exceptions_MappingException::missingTargetEntity($mapping['fieldName']); + throw MappingException::missingTargetEntity($mapping['fieldName']); } $this->_targetEntityName = $mapping['targetEntity']; @@ -287,9 +289,9 @@ abstract class AssociationMapping } /** - * Gets the name of the join table. + * Gets the join table definition, if any. * - * @return string + * @return array */ public function getJoinTable() { @@ -316,36 +318,6 @@ abstract class AssociationMapping return $this->_mappedByFieldName; } - /*public function getInverseSideFieldName() - { - return $this->_inverseSideFieldName; - }*/ - /** - * Marks the association as bidirectional, specifying the field name of - * the inverse side. - * This is called on the owning side, when an inverse side is discovered. - * This does only make sense to call on the owning side. - * - * @param string $inverseSideFieldName - */ - /*public function setBidirectional($inverseSideFieldName) - { - if ( ! $this->_isOwningSide) { - return; //TODO: exception? - } - $this->_inverseSideFieldName = $inverseSideFieldName; - }*/ - - /** - * Whether the association is bidirectional. - * - * @return boolean - */ - /*public function isBidirectional() - { - return $this->_mappedByFieldName || $this->_inverseSideFieldName; - }*/ - public function isOneToOne() { return false; diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadata.php b/lib/Doctrine/ORM/Mapping/ClassMetadata.php index c006d0e72..c6ce2fc00 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadata.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadata.php @@ -22,6 +22,7 @@ namespace Doctrine\ORM\Mapping; use \ReflectionClass; +use Doctrine\Common\DoctrineException; /** * A ClassMetadata instance holds all the information (metadata) of an entity and @@ -100,9 +101,18 @@ class ClassMetadata */ const ENTITY_TYPE_MAPPED_SUPERCLASS = 'mappedSuperclass'; - /** The name of the entity class. */ + /** + * The name of the entity class. + */ protected $_entityName; + /** + * The namespace the entity class is contained in. + * + * @var string + */ + protected $_namespace; + /** * The name of the entity class that is at the root of the entity inheritance * hierarchy. If the entity is not part of an inheritance hierarchy this is the same @@ -268,11 +278,16 @@ class ClassMetadata protected $_discriminatorColumn; /** - * The name of the primary table. + * The primary table definition. The definition is an array with the + * following entries: * - * @var string + * name => + * schema => + * catalog => + * + * @var array */ - protected $_tableName; + protected $_primaryTable; /** * The cached lifecycle listeners. There is only one instance of each @@ -332,15 +347,16 @@ class ClassMetadata protected $_reflectionProperties; /** - * Initializes a new ClassMetadata instance that will hold the ORM metadata - * of the class with the given name. + * Initializes a new ClassMetadata instance that will hold the object-relational mapping + * metadata of the class with the given name. * * @param string $entityName Name of the entity class the new instance is used for. */ public function __construct($entityName) { $this->_entityName = $entityName; - $this->_tableName = $this->_entityName; + $this->_namespace = substr($entityName, 0, strrpos($entityName, '\\')); + $this->_primaryTable['name'] = str_replace($this->_namespace . '\\', '', $this->_entityName); $this->_rootEntityName = $entityName; $this->_reflectionClass = new ReflectionClass($entityName); } @@ -383,7 +399,7 @@ class ClassMetadata public function getSingleIdReflectionProperty() { if ($this->_isIdentifierComposite) { - throw new Doctrine_Exception("getSingleIdReflectionProperty called on entity with composite key."); + throw new DoctrineException("getSingleIdReflectionProperty called on entity with composite key."); } return $this->_reflectionProperties[$this->_identifier[0]]; } @@ -448,7 +464,6 @@ class ClassMetadata if ($mapping !== false) { return isset($mapping['unique']) && $mapping['unique'] == true; } - return false; } @@ -464,7 +479,6 @@ class ClassMetadata if ($mapping !== false) { return isset($mapping['notnull']) && $mapping['notnull'] == true; } - return false; } @@ -520,8 +534,8 @@ class ClassMetadata */ public function getInverseAssociationMapping($mappedByFieldName) { - if ( ! isset($this->_associationMappings[$fieldName])) { - throw new Doctrine_Exception("Mapping not found: " . $fieldName); + if ( ! isset($this->_inverseMappings[$mappedByFieldName])) { + throw new DoctrineException("Mapping not found: " . $mappedByFieldName); } return $this->_inverseMappings[$mappedByFieldName]; } @@ -932,13 +946,13 @@ class ClassMetadata } /** - * getTableName + * Gets the name of the primary table. * - * @return void + * @return string */ public function getTableName() { - return $this->_tableName; + return $this->_primaryTable['name']; } public function getInheritedFields() @@ -1092,34 +1106,34 @@ class ClassMetadata */ public function setTableName($tableName) { - $this->_tableName = $tableName; + $this->_primaryTable['name'] = $tableName; } /** - * Serializes the metadata. + * Sets the primary table definition. The provided array must have th + * following structure: * - * Part of the implementation of the Serializable interface. + * name => + * schema => + * catalog => * - * @return string The serialized metadata. + * @param array $primaryTableDefinition */ - /* public function serialize() + public function setPrimaryTable(array $primaryTableDefinition) { - //$contents = get_object_vars($this); - //return serialize($contents); - return ""; - }*/ + $this->_primaryTable = $primaryTableDefinition; + } /** - * Reconstructs the metadata class from it's serialized representation. + * Gets the primary table definition. * - * Part of the implementation of the Serializable interface. - * - * @param string $serialized The serialized metadata class. + * @see setPrimaryTable() + * @return array */ - /*public function unserialize($serialized) + public function getPrimaryTable() { - return true; - }*/ + return $this->_primaryTable; + } /** * Checks whether the given type identifies an entity type. @@ -1168,12 +1182,14 @@ class ClassMetadata * easier for the user. * * @param array $mapping - * @return unknown * @todo Pass param by ref? */ private function _completeAssociationMapping(array $mapping) { $mapping['sourceEntity'] = $this->_entityName; + if (isset($mapping['targetEntity']) && strpos($mapping['targetEntity'], '\\') === false) { + $mapping['targetEntity'] = $this->_namespace . '\\' . $mapping['targetEntity']; + } return $mapping; } @@ -1260,7 +1276,7 @@ class ClassMetadata /** * Stores the association mapping. * - * @param Doctrine_Association $assocMapping + * @param AssociationMapping $assocMapping */ private function _storeAssociationMapping(AssociationMapping $assocMapping) { @@ -1278,7 +1294,7 @@ class ClassMetadata } /** - * Registers a custom mapper for the entity class. + * Registers a custom repository class for the entity class. * * @param string $mapperClassName The class name of the custom mapper. */ @@ -1333,11 +1349,11 @@ class ClassMetadata //Entity::TYPE_ENTITY //Entity::TYPE_MAPPED_SUPERCLASS //Entity::TYPE_TRANSIENT - throw new Doctrine_Exception("Not yet implemented."); + throw new DoctrineException("Not yet implemented."); } /** - * Dispatches the lifecycle event of the given Entity to the registered + * Dispatches the lifecycle event of the given entity to the registered * lifecycle callbacks and lifecycle listeners. * * @param string $event The lifecycle event. @@ -1381,7 +1397,7 @@ class ClassMetadata } /** - * Adds a lifecycle listener for Entities of this class. + * Adds a lifecycle listener for entities of this class. * * Note: If the same listener class is registered more than once, the old * one will be overridden. diff --git a/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php b/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php index f260dc21f..db1a151e3 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php @@ -1,14 +1,34 @@ . + */ namespace Doctrine\ORM\Mapping\Driver; +use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Exceptions\MappingException; /* Addendum annotation reflection extensions */ if ( ! class_exists('\Addendum', false)) { - require dirname(__FILE__) . '/addendum/annotations.php'; + require __DIR__ . '/addendum/annotations.php'; } -require dirname(__FILE__) . '/DoctrineAnnotations.php'; +require __DIR__ . '/DoctrineAnnotations.php'; /** * The AnnotationDriver reads the mapping metadata from docblock annotations. @@ -20,23 +40,31 @@ class AnnotationDriver /** * Loads the metadata for the specified class into the provided container. */ - public function loadMetadataForClass($className, \Doctrine\ORM\Mapping\ClassMetadata $metadata) + public function loadMetadataForClass($className, ClassMetadata $metadata) { $annotClass = new \Addendum\ReflectionAnnotatedClass($className); + // Evaluate DoctrineEntity annotation if (($entityAnnot = $annotClass->getAnnotation('DoctrineEntity')) === false) { throw new MappingException("$className is no entity."); } - - if ($entityAnnot->tableName) { - $metadata->setTableName($entityAnnot->tableName); - } $metadata->setCustomRepositoryClass($entityAnnot->repositoryClass); + // Evaluate DoctrineTable annotation + if ($tableAnnot = $annotClass->getAnnotation('DoctrineTable')) { + $metadata->setPrimaryTable(array( + 'name' => $tableAnnot->name, + 'schema' => $tableAnnot->schema, + 'catalog' => $tableAnnot->catalog + )); + } + + // Evaluate DoctrineInheritanceType annotation if ($inheritanceTypeAnnot = $annotClass->getAnnotation('DoctrineInheritanceType')) { $metadata->setInheritanceType($inheritanceTypeAnnot->value); } + // Evaluate DoctrineDiscriminatorColumn annotation if ($discrColumnAnnot = $annotClass->getAnnotation('DoctrineDiscriminatorColumn')) { $metadata->setDiscriminatorColumn(array( 'name' => $discrColumnAnnot->name, @@ -45,17 +73,38 @@ class AnnotationDriver )); } + // Evaluate DoctrineDiscriminatorMap annotation if ($discrValueAnnot = $annotClass->getAnnotation('DoctrineDiscriminatorMap')) { $metadata->setDiscriminatorMap((array)$discrValueAnnot->value); } - + + // Evaluate DoctrineSubClasses annotation if ($subClassesAnnot = $annotClass->getAnnotation('DoctrineSubClasses')) { $metadata->setSubclasses($subClassesAnnot->value); } + // Evaluate annotations on properties/fields foreach ($annotClass->getProperties() as $property) { $mapping = array(); $mapping['fieldName'] = $property->getName(); + + // Check for DoctrineJoinColummn/DoctrineJoinColumns annotations + $joinColumns = array(); + if ($joinColumnAnnot = $property->getAnnotation('DoctrineJoinColumn')) { + $joinColumns[] = array( + 'name' => $joinColumnAnnot->name, + 'referencedColumnName' => $joinColumnAnnot->referencedColumnName, + 'unique' => $joinColumnAnnot->unique, + 'nullable' => $joinColumnAnnot->nullable, + 'onDelete' => $joinColumnAnnot->onDelete, + 'onUpdate' => $joinColumnAnnot->onUpdate + ); + } else if ($joinColumnsAnnot = $property->getAnnotation('DoctrineJoinColumns')) { + $joinColumns = $joinColumnsAnnot->value; + } + + // Field can only be annotated with one of: DoctrineColumn, + // DoctrineOneToOne, DoctrineOneToMany, DoctrineManyToOne, DoctrineManyToMany if ($columnAnnot = $property->getAnnotation('DoctrineColumn')) { if ($columnAnnot->type == null) { throw new MappingException("Missing type on property " . $property->getName()); @@ -72,7 +121,7 @@ class AnnotationDriver $metadata->mapField($mapping); } else if ($oneToOneAnnot = $property->getAnnotation('DoctrineOneToOne')) { $mapping['targetEntity'] = $oneToOneAnnot->targetEntity; - $mapping['joinColumns'] = $oneToOneAnnot->joinColumns; + $mapping['joinColumns'] = $joinColumns; $mapping['mappedBy'] = $oneToOneAnnot->mappedBy; $mapping['cascade'] = $oneToOneAnnot->cascade; $metadata->mapOneToOne($mapping); @@ -82,17 +131,26 @@ class AnnotationDriver $mapping['cascade'] = $oneToManyAnnot->cascade; $metadata->mapOneToMany($mapping); } else if ($manyToOneAnnot = $property->getAnnotation('DoctrineManyToOne')) { - $mapping['joinColumns'] = $manyToOneAnnot->joinColumns; + $mapping['joinColumns'] = $joinColumns; $mapping['targetEntity'] = $manyToOneAnnot->targetEntity; $metadata->mapManyToOne($mapping); } else if ($manyToManyAnnot = $property->getAnnotation('DoctrineManyToMany')) { + $joinTable = array(); + if ($joinTableAnnot = $property->getAnnotation('DoctrineJoinTable')) { + $joinTable = array( + 'name' => $joinTableAnnot->name, + 'schema' => $joinTableAnnot->schema, + 'catalog' => $joinTableAnnot->catalog, + 'joinColumns' => $joinTableAnnot->joinColumns, + 'inverseJoinColumns' => $joinTableAnnot->inverseJoinColumns + ); + } + $mapping['joinTable'] = $joinTable; $mapping['targetEntity'] = $manyToManyAnnot->targetEntity; - $mapping['joinColumns'] = $manyToManyAnnot->joinColumns; - $mapping['inverseJoinColumns'] = $manyToManyAnnot->inverseJoinColumns; - $mapping['joinTable'] = $manyToManyAnnot->joinTable; $mapping['mappedBy'] = $manyToManyAnnot->mappedBy; $metadata->mapManyToMany($mapping); } + } } } diff --git a/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php b/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php index e518bd9bd..6f48c4ae1 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php +++ b/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php @@ -1,15 +1,28 @@ . */ /* Annotations */ final class DoctrineEntity extends \Addendum\Annotation { - public $tableName; public $repositoryClass; - public $inheritanceType; } final class DoctrineInheritanceType extends \Addendum\Annotation {} final class DoctrineDiscriminatorColumn extends \Addendum\Annotation { @@ -24,11 +37,13 @@ final class DoctrineIdGenerator extends \Addendum\Annotation {} final class DoctrineVersion extends \Addendum\Annotation {} final class DoctrineJoinColumn extends \Addendum\Annotation { public $name; - public $type; - public $length; + public $referencedColumnName; + public $unique = false; + public $nullable = true; public $onDelete; public $onUpdate; } +final class DoctrineJoinColumns extends \Addendum\Annotation {} final class DoctrineColumn extends \Addendum\Annotation { public $type; public $length; @@ -38,7 +53,6 @@ final class DoctrineColumn extends \Addendum\Annotation { final class DoctrineOneToOne extends \Addendum\Annotation { public $targetEntity; public $mappedBy; - public $joinColumns; public $cascade; } final class DoctrineOneToMany extends \Addendum\Annotation { @@ -48,15 +62,25 @@ final class DoctrineOneToMany extends \Addendum\Annotation { } final class DoctrineManyToOne extends \Addendum\Annotation { public $targetEntity; - public $joinColumns; public $cascade; } final class DoctrineManyToMany extends \Addendum\Annotation { public $targetEntity; - public $joinColumns; - public $inverseJoinColumns; - public $joinTable; public $mappedBy; public $cascade; } - +final class DoctrineElementCollection extends \Addendum\Annotation { + public $tableName; +} +final class DoctrineTable extends \Addendum\Annotation { + public $name; + public $catalog; + public $schema; +} +final class DoctrineJoinTable extends \Addendum\Annotation { + public $name; + public $catalog; + public $schema; + public $joinColumns; + public $inverseJoinColumns; +} diff --git a/lib/Doctrine/ORM/Mapping/ManyToManyMapping.php b/lib/Doctrine/ORM/Mapping/ManyToManyMapping.php index dc67ca453..ebdd80cec 100644 --- a/lib/Doctrine/ORM/Mapping/ManyToManyMapping.php +++ b/lib/Doctrine/ORM/Mapping/ManyToManyMapping.php @@ -1,40 +1,61 @@ . + */ -#namespace Doctrine::ORM::Mapping; +namespace Doctrine\ORM\Mapping; + +use Doctrine\ORM\Exceptions\MappingException; /** * A many-to-many mapping describes the mapping between two collections of * entities. * * @since 2.0 - * @todo Rename to ManyToManyMapping + * @author Roman Borschel */ -class Doctrine_ORM_Mapping_ManyToManyMapping extends Doctrine_ORM_Mapping_AssociationMapping -{ +class ManyToManyMapping extends AssociationMapping +{ /** - * The name of the association class (if an association class is used). - * - * @var string + * The key columns of the source table. */ - private $_associationClass; - - /** The field in the source table that corresponds to the key in the relation table */ - protected $_sourceKeyColumns; + private $_sourceKeyColumns = array(); - /** The field in the target table that corresponds to the key in the relation table */ - protected $_targetKeyColumns; + /** + * The key columns of the target table. + */ + private $_targetKeyColumns = array(); - /** The field in the intermediate table that corresponds to the key in the source table */ - protected $_sourceRelationKeyColumns; + /** + * Maps the columns in the source table to the columns in the relation table. + */ + private $_sourceToRelationKeyColumns = array(); - /** The field in the intermediate table that corresponds to the key in the target table */ - protected $_targetRelationKeyColumns; + /** + * Maps the columns in the target table to the columns in the relation table. + */ + private $_targetToRelationKeyColumns = array(); /** - * Constructor. - * Creates a new ManyToManyMapping. + * Initializes a new ManyToManyMapping. * - * @param array $mapping The mapping info. + * @param array $mapping The mapping information. */ public function __construct(array $mapping) { @@ -50,38 +71,63 @@ class Doctrine_ORM_Mapping_ManyToManyMapping extends Doctrine_ORM_Mapping_Associ protected function _validateAndCompleteMapping(array $mapping) { parent::_validateAndCompleteMapping($mapping); - if ($this->isOwningSide()) { - // many-many owning MUST have a join table + // owning side MUST have a join table if ( ! isset($mapping['joinTable'])) { - throw Doctrine_MappingException::joinTableRequired($mapping['fieldName']); + throw MappingException::joinTableRequired($mapping['fieldName']); } - - // optional attributes for many-many owning side - $this->_associationClass = isset($mapping['associationClass']) ? - $mapping['associationClass'] : null; + // owning side MUST specify joinColumns + if ( ! isset($mapping['joinTable']['joinColumns'])) { + throw MappingException::invalidMapping($this->_sourceFieldName); + } + foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) { + $this->_sourceToRelationKeyColumns[$joinColumn['referencedColumnName']] = $joinColumn['name']; + } + $this->_sourceKeyColumns = array_keys($this->_sourceToRelationKeyColumns); + // owning side MUST specify inverseJoinColumns + if ( ! isset($mapping['joinTable']['inverseJoinColumns'])) { + throw MappingException::invalidMapping($this->_sourceFieldName); + } + foreach ($mapping['joinTable']['inverseJoinColumns'] as $inverseJoinColumn) { + $this->_targetToRelationKeyColumns[$inverseJoinColumn['referencedColumnName']] = $inverseJoinColumn['name']; + } + $this->_targetKeyColumns = array_keys($this->_targetToRelationKeyColumns); } } - - /** - * Whether the mapping uses an association class for the intermediary - * table. - * - * @return boolean - */ - public function usesAssociationClass() + + public function getSourceToRelationKeyColumns() { - return $this->_associationClass !== null; + return $this->_sourceToRelationKeyColumns; } - - /** - * Gets the name of the association class. - * - * @return string - */ - public function getAssociationClassName() + + public function getTargetToRelationKeyColumns() { - return $this->_associationClass; + return $this->_targetToRelationKeyColumns; + } + + public function getSourceKeyColumns() + { + return $this->_sourceKeyColumns; + } + + public function getTargetKeyColumns() + { + return $this->_targetKeyColumns; + } + + public function lazyLoadFor($entity, $entityManager) + { + + } + + /** + * {@inheritdoc} + * + * @override + */ + public function isManyToMany() + { + return true; } } diff --git a/lib/Doctrine/ORM/Mapping/OneToManyMapping.php b/lib/Doctrine/ORM/Mapping/OneToManyMapping.php index dca4026c9..748ae6f77 100644 --- a/lib/Doctrine/ORM/Mapping/OneToManyMapping.php +++ b/lib/Doctrine/ORM/Mapping/OneToManyMapping.php @@ -16,7 +16,7 @@ * * This software consists of voluntary contributions made by many individuals * and is licensed under the LGPL. For more information, see - * . + * . */ namespace Doctrine\ORM\Mapping; @@ -54,10 +54,9 @@ class OneToManyMapping extends AssociationMapping protected $_deleteOrphans = false; /** - * Constructor. - * Creates a new OneToManyMapping. + * Initializes a new OneToManyMapping. * - * @param array $mapping The mapping info. + * @param array $mapping The mapping information. */ public function __construct(array $mapping) { diff --git a/lib/Doctrine/ORM/Mapping/OneToOneMapping.php b/lib/Doctrine/ORM/Mapping/OneToOneMapping.php index 33edb1208..eb9f20eb5 100644 --- a/lib/Doctrine/ORM/Mapping/OneToOneMapping.php +++ b/lib/Doctrine/ORM/Mapping/OneToOneMapping.php @@ -35,21 +35,28 @@ class OneToOneMapping extends AssociationMapping * i.e. source.id (pk) => target.user_id (fk). * Reverse mapping of _targetToSourceKeyColumns. */ - protected $_sourceToTargetKeyColumns = array(); + private $_sourceToTargetKeyColumns = array(); /** * Maps the target primary/foreign key columns to the source foreign/primary key columns. * i.e. target.user_id (fk) => source.id (pk). * Reverse mapping of _sourceToTargetKeyColumns. */ - protected $_targetToSourceKeyColumns = array(); + private $_targetToSourceKeyColumns = array(); /** * Whether to delete orphaned elements (when nulled out, i.e. $foo->other = null) * * @var boolean */ - protected $_deleteOrphans = false; + private $_deleteOrphans = false; + + /** + * The join column definitions. + * + * @var array + */ + private $_joinColumns = array(); /** * Creates a new OneToOneMapping. @@ -74,9 +81,12 @@ class OneToOneMapping extends AssociationMapping if ($this->isOwningSide()) { if ( ! isset($mapping['joinColumns'])) { - throw Doctrine_ORM_Exceptions_MappingException::invalidMapping($this->_sourceFieldName); + throw MappingException::invalidMapping($this->_sourceFieldName); + } + $this->_joinColumns = $mapping['joinColumns']; + foreach ($mapping['joinColumns'] as $joinColumn) { + $this->_sourceToTargetKeyColumns[$joinColumn['name']] = $joinColumn['referencedColumnName']; } - $this->_sourceToTargetKeyColumns = $mapping['joinColumns']; $this->_targetToSourceKeyColumns = array_flip($this->_sourceToTargetKeyColumns); } @@ -85,6 +95,16 @@ class OneToOneMapping extends AssociationMapping return $mapping; } + + /** + * Gets the join column definitions for this mapping. + * + * @return array + */ + public function getJoinColumns() + { + return $this->_joinColumns; + } /** * Gets the source-to-target key column mapping. diff --git a/lib/Doctrine/ORM/PersistentCollection.php b/lib/Doctrine/ORM/PersistentCollection.php index 4ea1e24c8..07d3a27fe 100644 --- a/lib/Doctrine/ORM/PersistentCollection.php +++ b/lib/Doctrine/ORM/PersistentCollection.php @@ -1,6 +1,6 @@ . + * . */ namespace Doctrine\ORM; @@ -110,6 +110,12 @@ final class PersistentCollection extends \Doctrine\Common\Collections\Collection */ private $_ownerClass; + /** + * Whether the collection is dirty and needs to be synchronized with the database + * when the UnitOfWork that manages its persistent state commits. + * + * @var boolean + */ private $_isDirty = false; /** @@ -181,11 +187,21 @@ final class PersistentCollection extends \Doctrine\Common\Collections\Collection * * @return object */ - public function _getOwner() + public function getOwner() { return $this->_owner; } + /** + * Gets the class descriptor for the owning entity class. + * + * @return Doctrine\ORM\Mapping\ClassMetadata + */ + public function getOwnerClass() + { + return $this->_ownerClass; + } + /** * Removes an element from the collection. * @@ -251,8 +267,9 @@ final class PersistentCollection extends \Doctrine\Common\Collections\Collection /** * Adds all entities of the other collection to this collection. * - * @param unknown_type $otherCollection + * @param object $otherCollection * @todo Impl + * @override */ public function addAll($otherCollection) { diff --git a/lib/Doctrine/ORM/Persisters/AbstractCollectionPersister.php b/lib/Doctrine/ORM/Persisters/AbstractCollectionPersister.php index 57c9d28fc..8d6655f68 100644 --- a/lib/Doctrine/ORM/Persisters/AbstractCollectionPersister.php +++ b/lib/Doctrine/ORM/Persisters/AbstractCollectionPersister.php @@ -1,18 +1,50 @@ . + */ namespace Doctrine\ORM\Persisters; use Doctrine\ORM\EntityManager; use Doctrine\ORM\PersistentCollection; +/** + * Base class for all collection persisters. + * + * @since 2.0 + * @author Roman Borschel + */ abstract class AbstractCollectionPersister { protected $_em; protected $_conn; + protected $_uow; + /** + * Initializes a new instance of a class derived from {@link AbstractCollectionPersister}. + * + * @param Doctrine\ORM\EntityManager $em + */ public function __construct(EntityManager $em) { $this->_em = $em; + $this->_uow = $em->getUnitOfWork(); $this->_conn = $em->getConnection(); } @@ -51,7 +83,7 @@ abstract class AbstractCollectionPersister $sql = $this->_getDeleteRowSql($coll); $uow = $this->_em->getUnitOfWork(); foreach ($deleteDiff as $element) { - $this->_conn->exec($sql, $uow->getEntityIdentifier($element)); + $this->_conn->exec($sql, $this->_getDeleteRowSqlParameters($element)); } } @@ -81,6 +113,8 @@ abstract class AbstractCollectionPersister */ abstract protected function _getDeleteRowSql(PersistentCollection $coll); + abstract protected function _getDeleteRowSqlParameters(PersistentCollection $coll, $element); + /** * Gets the SQL statement used for updating a row in the collection. * diff --git a/lib/Doctrine/ORM/Persisters/AbstractEntityPersister.php b/lib/Doctrine/ORM/Persisters/AbstractEntityPersister.php index c51da552c..d767fd33c 100644 --- a/lib/Doctrine/ORM/Persisters/AbstractEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/AbstractEntityPersister.php @@ -16,7 +16,7 @@ * * This software consists of voluntary contributions made by many individuals * and is licensed under the LGPL. For more information, see - * . + * . */ namespace Doctrine\ORM\Persisters; diff --git a/lib/Doctrine/ORM/Persisters/ElementCollectionPersister.php b/lib/Doctrine/ORM/Persisters/ElementCollectionPersister.php new file mode 100644 index 000000000..947f51ef6 --- /dev/null +++ b/lib/Doctrine/ORM/Persisters/ElementCollectionPersister.php @@ -0,0 +1,33 @@ +. + */ + +namespace Doctrine\ORM\Persisters; + +/** + * Persister for collections of basic elements / value objects. + * + * @author robo + */ +class ElementCollectionPersister extends AbstractCollectionPersister +{ + //put your code here +} + diff --git a/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php b/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php index d160102be..c9b494f08 100644 --- a/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php +++ b/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php @@ -17,9 +17,28 @@ class ManyToManyPersister extends AbstractCollectionPersister * * @param $coll * @override + * @todo Identifier quoting. + * @see _getDeleteRowSqlParameters() */ protected function _getDeleteRowSql(PersistentCollection $coll) { + $mapping = $coll->getMapping(); + $joinTable = $mapping->getJoinTable(); + $columns = array_merge($mapping->getSourceKeyColumns(), $mapping->getTargetKeyColumns()); + return "DELETE FROM $joinTable WHERE " . implode(' = ?, ', $columns) . ' = ?'; + } + + /** + * + * @param $element + * @override + * @see _getDeleteRowSql() + */ + protected function _getDeleteRowSqlParameters(PersistentCollection $coll, $element) + { + $owner = $coll->getOwner(); + + } } diff --git a/lib/Doctrine/ORM/Persisters/OneToManyPersister.php b/lib/Doctrine/ORM/Persisters/OneToManyPersister.php index a1aa02bd9..4ae0e6338 100644 --- a/lib/Doctrine/ORM/Persisters/OneToManyPersister.php +++ b/lib/Doctrine/ORM/Persisters/OneToManyPersister.php @@ -9,7 +9,13 @@ use Doctrine\ORM\PersistentCollection; */ class OneToManyPersister extends AbstractCollectionPersister { - + /** + * {@inheritdoc} + * + * @param $coll + * @return + * @override + */ protected function _getDeleteRowSql(PersistentCollection $coll) { $mapping = $coll->getMapping(); @@ -33,6 +39,18 @@ class OneToManyPersister extends AbstractCollectionPersister return "UPDATE $table SET $setClause WHERE $whereClause"; } + /** + * {@inheritdoc} + * + * @param $element + * @return + * @override + */ + protected function _getDeleteRowSqlParameters(PersistentCollection $coll, $element) + { + return $this->_uow->getEntityIdentifier($element); + } + protected function _getInsertRowSql() { return "UPDATE xxx SET foreign_key = yyy WHERE foreign_key = zzz"; diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index 4048bd84f..e2fe23743 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -16,7 +16,7 @@ * * This software consists of voluntary contributions made by many individuals * and is licensed under the LGPL. For more information, see - * . + * . */ namespace Doctrine\ORM; @@ -584,13 +584,13 @@ class UnitOfWork $oid = spl_object_hash($entity); if (isset($this->_dirtyEntities[$oid])) { - throw new Doctrine_Exception("Dirty object can't be registered as new."); + throw new DoctrineException("Dirty object can't be registered as new."); } if (isset($this->_deletedEntities[$oid])) { - throw new Doctrine_Exception("Removed object can't be registered as new."); + throw new DoctrineException("Removed object can't be registered as new."); } if (isset($this->_newEntities[$oid])) { - throw new Doctrine_Exception("Object already registered as new. Can't register twice."); + throw new DoctrineException("Object already registered as new. Can't register twice."); } $this->_newEntities[$oid] = $entity; @@ -968,8 +968,7 @@ class UnitOfWork $this->registerNew($entity); break; case self::STATE_DETACHED: - //exception? - throw new Doctrine_Exception("Behavior of save() for a detached entity " + throw new DoctrineException("Behavior of save() for a detached entity " . "is not yet defined."); case self::STATE_DELETED: // entity becomes managed again @@ -983,7 +982,7 @@ class UnitOfWork break; default: //TODO: throw UnitOfWorkException::invalidEntityState() - throw new Doctrine_Exception("Encountered invalid entity state."); + throw new DoctrineException("Encountered invalid entity state."); } $this->_cascadeSave($entity, $visited, $insertNow); } diff --git a/tests/Doctrine/Tests/Models/CMS/CmsAddress.php b/tests/Doctrine/Tests/Models/CMS/CmsAddress.php index acf6b6bef..5d7a7763e 100644 --- a/tests/Doctrine/Tests/Models/CMS/CmsAddress.php +++ b/tests/Doctrine/Tests/Models/CMS/CmsAddress.php @@ -10,7 +10,8 @@ namespace Doctrine\Tests\Models\CMS; * Description of CmsAddress * * @author robo - * @DoctrineEntity(tableName="cms_addresses") + * @DoctrineEntity + * @DoctrineTable(name="cms_addresses") */ class CmsAddress { @@ -33,9 +34,8 @@ class CmsAddress */ public $city; /** - * @DoctrineOneToOne( - targetEntity="Doctrine\Tests\Models\CMS\CmsUser", - joinColumns={"user_id" = "id"}) + * @DoctrineOneToOne(targetEntity="CmsUser") + * @DoctrineJoinColumn(name="user_id", referencedColumnName="id") */ public $user; } diff --git a/tests/Doctrine/Tests/Models/CMS/CmsArticle.php b/tests/Doctrine/Tests/Models/CMS/CmsArticle.php index 900400241..88de6b36e 100644 --- a/tests/Doctrine/Tests/Models/CMS/CmsArticle.php +++ b/tests/Doctrine/Tests/Models/CMS/CmsArticle.php @@ -3,7 +3,8 @@ namespace Doctrine\Tests\Models\CMS; /** - * @DoctrineEntity(tableName="cms_articles") + * @DoctrineEntity + * @DoctrineTable(name="cms_articles") */ class CmsArticle { @@ -22,12 +23,12 @@ class CmsArticle */ public $text; /** - * @DoctrineManyToOne(targetEntity="Doctrine\Tests\Models\CMS\CmsUser", - joinColumns={"user_id" = "id"}) + * @DoctrineManyToOne(targetEntity="CmsUser") + * @DoctrineJoinColumn(name="user_id", referencedColumnName="id") */ public $user; /** - * @DoctrineOneToMany(targetEntity="Doctrine\Tests\Models\CMS\CmsComment", mappedBy="article") + * @DoctrineOneToMany(targetEntity="CmsComment", mappedBy="article") */ public $comments; } diff --git a/tests/Doctrine/Tests/Models/CMS/CmsComment.php b/tests/Doctrine/Tests/Models/CMS/CmsComment.php index cfb952dbc..f45629cb5 100644 --- a/tests/Doctrine/Tests/Models/CMS/CmsComment.php +++ b/tests/Doctrine/Tests/Models/CMS/CmsComment.php @@ -3,7 +3,8 @@ namespace Doctrine\Tests\Models\CMS; /** - * @DoctrineEntity(tableName="cms_comments") + * @DoctrineEntity + * @DoctrineTable(name="cms_comments") */ class CmsComment { @@ -22,8 +23,8 @@ class CmsComment */ public $text; /** - * @DoctrineManyToOne(targetEntity="Doctrine\Tests\Models\CMS\CmsArticle", - joinColumns={"article_id" = "id"}) + * @DoctrineManyToOne(targetEntity="CmsArticle") + * @DoctrineJoinColumn(name="article_id", referencedColumnName="id") */ public $article; } diff --git a/tests/Doctrine/Tests/Models/CMS/CmsGroup.php b/tests/Doctrine/Tests/Models/CMS/CmsGroup.php new file mode 100644 index 000000000..43f6d12a9 --- /dev/null +++ b/tests/Doctrine/Tests/Models/CMS/CmsGroup.php @@ -0,0 +1,33 @@ + 'address', 'targetEntity' => 'Address', - 'joinColumns' => array('address_id' => 'id'), + 'joinColumns' => array(array('name' => 'address_id', 'referencedColumnName' => 'id')), 'sourceEntity' => 'Person', // This is normally filled by ClassMetadata ); diff --git a/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php b/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php index e92578eaf..e6329f719 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php @@ -44,7 +44,6 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase $this->assertEquals(1, count($cm->getAssociationMappings())); $oneOneMapping = $cm->getAssociationMapping('phonenumbers'); $this->assertEquals('phonenumbers', $oneOneMapping->getSourceFieldName()); - $this->assertEquals('Bar', $oneOneMapping->getTargetEntityName()); + $this->assertEquals('Doctrine\Tests\Models\CMS\Bar', $oneOneMapping->getTargetEntityName()); } - } \ No newline at end of file