From dcebc241b4e36f20f364699105aff258f5840962 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 8 Aug 2010 14:23:57 +0200 Subject: [PATCH 01/15] DDC-728 - Verified with test that checking for an inherited single valued association works --- .../Functional/ClassTableInheritanceTest.php | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/Doctrine/Tests/ORM/Functional/ClassTableInheritanceTest.php b/tests/Doctrine/Tests/ORM/Functional/ClassTableInheritanceTest.php index a492444f1..86075e854 100644 --- a/tests/Doctrine/Tests/ORM/Functional/ClassTableInheritanceTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/ClassTableInheritanceTest.php @@ -325,4 +325,32 @@ class ClassTableInheritanceTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertNull($this->_em->find(get_class($employee1), $employee1Id)); } + + /** + * @group DDC-728 + */ + public function testQueryForInheritedSingleValuedAssociation() + { + $manager = new CompanyManager(); + $manager->setName('gblanco'); + $manager->setSalary(1234); + $manager->setTitle('Awesome!'); + $manager->setDepartment('IT'); + + $person = new CompanyPerson(); + $person->setName('spouse'); + + $manager->setSpouse($person); + + $this->_em->persist($manager); + $this->_em->persist($person); + $this->_em->flush(); + $this->_em->clear(); + + $dql = "SELECT m FROM Doctrine\Tests\Models\Company\CompanyManager m WHERE m.spouse = ?1"; + $dqlManager = $this->_em->createQuery($dql)->setParameter(1, $person->getId())->getSingleResult(); + + $this->assertEquals($manager->getId(), $dqlManager->getId()); + $this->assertEquals($person->getId(), $dqlManager->getSpouse()->getId()); + } } From 0424d870999c9fb0a044a8b5773f250376d2368f Mon Sep 17 00:00:00 2001 From: "Roman S. Borschel" Date: Sun, 8 Aug 2010 15:01:03 +0200 Subject: [PATCH 02/15] [DDC-386][DDC-675] Fixed. --- lib/Doctrine/ORM/EntityManager.php | 21 +++++++++++++------ lib/Doctrine/ORM/Query/Lexer.php | 2 +- lib/Doctrine/ORM/Query/Parser.php | 2 +- .../ORM/Functional/BasicFunctionalTest.php | 6 ++++-- 4 files changed, 21 insertions(+), 10 deletions(-) diff --git a/lib/Doctrine/ORM/EntityManager.php b/lib/Doctrine/ORM/EntityManager.php index 397ff8938..0cd887fe1 100644 --- a/lib/Doctrine/ORM/EntityManager.php +++ b/lib/Doctrine/ORM/EntityManager.php @@ -25,6 +25,7 @@ use Closure, Exception, Doctrine\DBAL\LockMode, Doctrine\ORM\Mapping\ClassMetadata, Doctrine\ORM\Mapping\ClassMetadataFactory, + Doctrine\ORM\Query\ResultSetMapping, Doctrine\ORM\Proxy\ProxyFactory; /** @@ -225,7 +226,14 @@ class EntityManager } /** - * Returns the metadata for a class. + * Returns the ORM metadata descriptor for a class. + * + * The class name must be the fully-qualified class name without a leading backslash + * (as it is returned by get_class($obj)) or an aliased class name. + * + * Examples: + * MyProject\Domain\User + * sales:PriceRequest * * @return Doctrine\ORM\Mapping\ClassMetadata * @internal Performance-sensitive method. @@ -268,7 +276,7 @@ class EntityManager * @param ResultSetMapping $rsm The ResultSetMapping to use. * @return NativeQuery */ - public function createNativeQuery($sql, \Doctrine\ORM\Query\ResultSetMapping $rsm) + public function createNativeQuery($sql, ResultSetMapping $rsm) { $query = new NativeQuery($this); $query->setSql($sql); @@ -338,7 +346,7 @@ class EntityManager */ public function getReference($entityName, $identifier) { - $class = $this->metadataFactory->getMetadataFor($entityName); + $class = $this->metadataFactory->getMetadataFor(ltrim($entityName, '\\')); // Check identity map first, if its already in there just return it. if ($entity = $this->unitOfWork->tryGetById($identifier, $class->rootEntityName)) { @@ -374,7 +382,7 @@ class EntityManager */ public function getPartialReference($entityName, $identifier) { - $class = $this->metadataFactory->getMetadataFor($entityName); + $class = $this->metadataFactory->getMetadataFor(ltrim($entityName, '\\')); // Check identity map first, if its already in there just return it. if ($entity = $this->unitOfWork->tryGetById($identifier, $class->rootEntityName)) { @@ -534,11 +542,12 @@ class EntityManager /** * Gets the repository for an entity class. * - * @param string $entityName The name of the Entity. - * @return EntityRepository The repository. + * @param string $entityName The name of the entity. + * @return EntityRepository The repository class. */ public function getRepository($entityName) { + $entityName = ltrim($entityName, '\\'); if (isset($this->repositories[$entityName])) { return $this->repositories[$entityName]; } diff --git a/lib/Doctrine/ORM/Query/Lexer.php b/lib/Doctrine/ORM/Query/Lexer.php index 7026f6616..57ffc3ebe 100644 --- a/lib/Doctrine/ORM/Query/Lexer.php +++ b/lib/Doctrine/ORM/Query/Lexer.php @@ -119,7 +119,7 @@ class Lexer extends \Doctrine\Common\Lexer protected function getCatchablePatterns() { return array( - '[a-z_][a-z0-9_\:\\\]*[a-z0-9_]{1}', + '[a-z_\\\][a-z0-9_\:\\\]*[a-z0-9_]{1}', '(?:[0-9]+(?:[\.][0-9]+)*)(?:e[+-]?[0-9]+)?', "'(?:[^']|'')*'", '\?[1-9][0-9]*|:[a-z][a-z0-9_]+' diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index 21a86862d..aeedf47d1 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -765,7 +765,7 @@ class Parser { $this->match(Lexer::T_IDENTIFIER); - $schemaName = $this->_lexer->token['value']; + $schemaName = ltrim($this->_lexer->token['value'], '\\'); if (strrpos($schemaName, ':') !== false) { list($namespaceAlias, $simpleClassName) = explode(':', $schemaName); diff --git a/tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php b/tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php index d12a183d9..73a0c65cf 100644 --- a/tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php @@ -129,7 +129,7 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->_em->clear(); - $user2 = $this->_em->createQuery('select u from Doctrine\Tests\Models\CMS\CmsUser u where u.id=?1') + $user2 = $this->_em->createQuery('select u from \Doctrine\Tests\Models\CMS\CmsUser u where u.id=?1') ->setParameter(1, $userId) ->getSingleResult(); @@ -440,7 +440,9 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->_em->clear(); - $articleNew = $this->_em->find('Doctrine\Tests\Models\CMS\CmsArticle', $articleId); + // test find() with leading backslash at the same time + $articleNew = $this->_em->find('\Doctrine\Tests\Models\CMS\CmsArticle', $articleId); + $this->assertTrue($this->_em->contains($articleNew)); $this->assertEquals("Lorem ipsum dolor sunt.", $articleNew->text); $this->assertNotSame($article, $articleNew); From a705b81d9beeb0bc73ba916636fca61b6242ccff Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 8 Aug 2010 16:07:40 +0200 Subject: [PATCH 03/15] DDC-729 - Fix issue with merging of collections leading to dereference behaviour after next flush(). Added a test for dereferencing of collections as there was none --- lib/Doctrine/ORM/UnitOfWork.php | 18 +++-- .../ManyToManyBasicAssociationTest.php | 12 +++ .../ORM/Functional/Ticket/DDC729Test.php | 80 +++++++++++++++++++ 3 files changed, 103 insertions(+), 7 deletions(-) create mode 100644 tests/Doctrine/Tests/ORM/Functional/Ticket/DDC729Test.php diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index dbad60626..874df3ce2 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -1426,13 +1426,17 @@ class UnitOfWork implements PropertyChangedListener continue; } - $coll = new PersistentCollection($this->em, - $this->em->getClassMetadata($assoc2->targetEntityName), - new ArrayCollection - ); - $coll->setOwner($managedCopy, $assoc2); - $coll->setInitialized($assoc2->isCascadeMerge); - $prop->setValue($managedCopy, $coll); + $managedCol = $prop->getValue($managedCopy); + if (!$managedCol) { + $managedCol = new PersistentCollection($this->em, + $this->em->getClassMetadata($assoc2->targetEntityName), + new ArrayCollection + ); + $managedCol->setOwner($managedCopy, $assoc2); + $prop->setValue($managedCopy, $managedCol); + $this->originalEntityData[$oid][$name] = $managedCol; + } + $managedCol->setInitialized($assoc2->isCascadeMerge); } } if ($class->isChangeTrackingNotify()) { diff --git a/tests/Doctrine/Tests/ORM/Functional/ManyToManyBasicAssociationTest.php b/tests/Doctrine/Tests/ORM/Functional/ManyToManyBasicAssociationTest.php index efb15d2d9..1fee14767 100644 --- a/tests/Doctrine/Tests/ORM/Functional/ManyToManyBasicAssociationTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/ManyToManyBasicAssociationTest.php @@ -235,6 +235,18 @@ class ManyToManyBasicAssociationTest extends \Doctrine\Tests\OrmFunctionalTestCa $this->assertEquals(0, count($newUser->getGroups())); } + public function testDereferenceCollectionDelete() + { + $user = $this->addCmsUserGblancoWithGroups(2); + $user->groups = null; + + $this->_em->flush(); + $this->_em->clear(); + + $newUser = $this->_em->find(get_class($user), $user->getId()); + $this->assertEquals(0, count($newUser->getGroups())); + } + /** * @param int $groupCount * @return CmsUser diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC729Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC729Test.php new file mode 100644 index 000000000..b63dfdbce --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC729Test.php @@ -0,0 +1,80 @@ +_em); + $schemaTool->createSchema(array( + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC729A'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC729B'), + )); + } catch(\Exception $e) { + + } + } + + public function testMergeManyToMany() + { + $a = new DDC729A(); + $b = new DDC729B(); + $a->related[] = $b; + + $this->_em->persist($a); + $this->_em->persist($b); + $this->_em->flush(); + $this->_em->clear(); + $aId = $a->id; + + $a = new DDC729A(); + $a->id = $aId; + + $this->assertType('Doctrine\Common\Collections\ArrayCollection', $a->related); + + $a = $this->_em->merge($a); + + $this->assertType('Doctrine\ORM\PersistentCollection', $a->related); + + $this->assertFalse($a->related->isInitialized(), "Collection should not be marked initialized."); + $this->assertFalse($a->related->isDirty(), "Collection should not be marked as dirty."); + + $this->_em->flush(); + $this->_em->clear(); + + $a = $this->_em->find(__NAMESPACE__ . '\DDC729A', $aId); + $this->assertEquals(1, count($a->related)); + } +} + +/** + * @Entity + */ +class DDC729A +{ + /** @Id @GeneratedValue @Column(type="integer") */ + public $id; + + /** @ManyToMany(targetEntity="DDC729B") */ + public $related; + + public function __construct() + { + $this->related = new \Doctrine\Common\Collections\ArrayCollection(); + } +} + +/** + * @Entity + */ +class DDC729B +{ + /** @Id @GeneratedValue @Column(type="integer") */ + public $id; +} From ea954e81231e6e7fddaa78137e0316c49dd8ef52 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 8 Aug 2010 16:15:35 +0200 Subject: [PATCH 04/15] DDC-703 - Fixed change tracking not passed to child classes in inheritance hierachy --- lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php index 14bc7015d..c84778e4a 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php @@ -249,6 +249,7 @@ class ClassMetadataFactory $class->setVersionField($parent->versionField); $class->setDiscriminatorMap($parent->discriminatorMap); $class->setLifecycleCallbacks($parent->lifecycleCallbacks); + $class->setChangeTrackingPolicy($parent->changeTrackingPolicy); } // Invoke driver From 21753c71c9fe3ad66326b3d9db1b0e018ae9684d Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 8 Aug 2010 16:43:52 +0200 Subject: [PATCH 05/15] DDC-651 - Fix short name being used instead of long-name in extends section of EntityGenerator --- lib/Doctrine/ORM/Tools/EntityGenerator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Tools/EntityGenerator.php b/lib/Doctrine/ORM/Tools/EntityGenerator.php index 2af25891f..bc9faadd7 100644 --- a/lib/Doctrine/ORM/Tools/EntityGenerator.php +++ b/lib/Doctrine/ORM/Tools/EntityGenerator.php @@ -379,7 +379,7 @@ public function () { $refl = new \ReflectionClass($this->_getClassToExtend()); - return $refl->getShortName(); + return $refl->getName(); } private function _getClassToExtendNamespace() From 8ea1d3825fddaaa4e2c8dae34debf9b5c7824229 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 8 Aug 2010 17:13:03 +0200 Subject: [PATCH 06/15] DDC-735 - Fix PersistentCollection::remove() and PersistentCollection::removeElement() behaving differently with regards to orphan removal --- lib/Doctrine/ORM/PersistentCollection.php | 12 +- .../ORM/Functional/Ticket/DDC735Test.php | 121 ++++++++++++++++++ 2 files changed, 130 insertions(+), 3 deletions(-) create mode 100644 tests/Doctrine/Tests/ORM/Functional/Ticket/DDC735Test.php diff --git a/lib/Doctrine/ORM/PersistentCollection.php b/lib/Doctrine/ORM/PersistentCollection.php index c2e4207b7..971d1f72c 100644 --- a/lib/Doctrine/ORM/PersistentCollection.php +++ b/lib/Doctrine/ORM/PersistentCollection.php @@ -379,9 +379,15 @@ final class PersistentCollection implements Collection }*/ $this->initialize(); - $result = $this->coll->removeElement($element); - $this->changed(); - return $result; + $removed = $this->coll->removeElement($element); + if ($removed) { + $this->changed(); + if ($this->association !== null && $this->association->isOneToMany() && + $this->association->orphanRemoval) { + $this->em->getUnitOfWork()->scheduleOrphanRemoval($removed); + } + } + return $removed; } /** diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC735Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC735Test.php new file mode 100644 index 000000000..76d3720c9 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC735Test.php @@ -0,0 +1,121 @@ +_schemaTool->createSchema(array( + $this->_em->getClassMetadata(__NAMESPACE__ . '\\DDC735Product'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\\DDC735Review') + )); + } catch(\Exception $e) { + + } + } + + public function testRemoveElement_AppliesOrphanRemoval() + { + // Create a product and its first review + $product = new DDC735Product; + $review = new DDC735Review($product); + + // Persist and flush + $this->_em->persist($product); + $this->_em->flush(); + + // Now you see it + $this->assertEquals(1, count($product->getReviews())); + + // Remove the review + $product->removeReview($review); + $this->_em->flush(); + + // Now you don't + $this->assertEquals(0, count($product->getReviews()), 'count($reviews) should be 0 after removing its only Review'); + + // Refresh + $this->_em->refresh($product); + + // It should still be 0 + $this->assertEquals(0, count($product->getReviews()), 'count($reviews) should still be 0 after the refresh'); + + // Review should also not be available anymore + $this->assertNull($this->_em->find(__NAMESPACE__.'\DDC735Review', $review->getId())); + } +} + +/** + * @Entity + */ +class DDC735Product +{ + /** + * @Id @Column(type="integer") @GeneratedValue + */ + protected $id; + + /** + * @OneToMany( + * targetEntity="DDC735Review", + * mappedBy="product", + * cascade={"persist"}, + * orphanRemoval=true + * ) + */ + protected $reviews; + + public function __construct() + { + $this->reviews = new ArrayCollection; + } + + public function getReviews() + { + return $this->reviews; + } + + public function addReview(DDC735Review $review) + { + $this->reviews->add($review); + } + + public function removeReview(DDC735Review $review) + { + $this->reviews->removeElement($review); + } +} + +/** + * @Entity + */ +class DDC735Review +{ + /** + * @Id @Column(type="integer") @GeneratedValue + */ + protected $id; + + /** + * @ManyToOne(targetEntity="DDC735Product", inversedBy="reviews") + */ + protected $product; + + public function __construct(DDC735Product $product) + { + $this->product = $product; + $product->addReview($this); + } + + public function getId() + { + return $this->id; + } +} \ No newline at end of file From da809fdeda8d436392a8f71a6129a5c8a746d1b7 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Mon, 9 Aug 2010 21:26:10 +0200 Subject: [PATCH 07/15] DDC-735 - Forward compatible fix for ArrayCollection::removeElement() returning the element instead of true --- lib/Doctrine/ORM/PersistentCollection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/PersistentCollection.php b/lib/Doctrine/ORM/PersistentCollection.php index 971d1f72c..31e9b74dc 100644 --- a/lib/Doctrine/ORM/PersistentCollection.php +++ b/lib/Doctrine/ORM/PersistentCollection.php @@ -384,7 +384,7 @@ final class PersistentCollection implements Collection $this->changed(); if ($this->association !== null && $this->association->isOneToMany() && $this->association->orphanRemoval) { - $this->em->getUnitOfWork()->scheduleOrphanRemoval($removed); + $this->em->getUnitOfWork()->scheduleOrphanRemoval($element); } } return $removed; From 8d3e0e61eaeaaf8753838a8580b2ee36dbd8f34f Mon Sep 17 00:00:00 2001 From: "Roman S. Borschel" Date: Mon, 9 Aug 2010 13:13:21 +0200 Subject: [PATCH 08/15] Moved association mappings to plain arrays, just like field mappings. --- .../ORM/Internal/Hydration/ArrayHydrator.php | 4 +- .../ORM/Internal/Hydration/ObjectHydrator.php | 47 ++- .../ORM/Mapping/AssociationMapping.php | 399 ------------------ lib/Doctrine/ORM/Mapping/ClassMetadata.php | 21 +- .../ORM/Mapping/ClassMetadataFactory.php | 14 +- .../ORM/Mapping/ClassMetadataInfo.php | 274 +++++++++++- .../ORM/Mapping/Driver/AnnotationDriver.php | 8 +- lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php | 8 +- .../ORM/Mapping/Driver/YamlDriver.php | 8 +- .../ORM/Mapping/ManyToManyMapping.php | 195 --------- lib/Doctrine/ORM/Mapping/OneToManyMapping.php | 144 ------- lib/Doctrine/ORM/Mapping/OneToOneMapping.php | 165 -------- lib/Doctrine/ORM/PersistentCollection.php | 26 +- .../AbstractCollectionPersister.php | 6 +- .../ORM/Persisters/BasicEntityPersister.php | 136 +++--- .../Persisters/JoinedSubclassPersister.php | 31 +- .../ORM/Persisters/ManyToManyPersister.php | 26 +- .../ORM/Persisters/OneToManyPersister.php | 2 +- .../ORM/Persisters/SingleTablePersister.php | 4 +- .../ORM/Query/AST/Functions/SizeFunction.php | 18 +- .../Query/Exec/MultiTableUpdateExecutor.php | 2 +- lib/Doctrine/ORM/Query/Parser.php | 7 +- lib/Doctrine/ORM/Query/QueryException.php | 8 +- lib/Doctrine/ORM/Query/SqlWalker.php | 87 ++-- lib/Doctrine/ORM/Tools/EntityGenerator.php | 97 +++-- .../ORM/Tools/Export/Driver/PhpExporter.php | 32 +- .../ORM/Tools/Export/Driver/XmlExporter.php | 55 ++- .../ORM/Tools/Export/Driver/YamlExporter.php | 47 +-- lib/Doctrine/ORM/Tools/SchemaTool.php | 23 +- lib/Doctrine/ORM/Tools/SchemaValidator.php | 69 ++- lib/Doctrine/ORM/UnitOfWork.php | 111 +++-- tests/Doctrine/Tests/ORM/AllTests.php | 2 +- .../ORM/Associations/OneToOneMappingTest.php | 13 +- .../ORM/Functional/BasicFunctionalTest.php | 1 + .../ORM/Functional/DatabaseDriverTest.php | 2 +- ...nyToManySelfReferentialAssociationTest.php | 3 +- ...anyToManyUnidirectionalAssociationTest.php | 3 +- ...neToManySelfReferentialAssociationTest.php | 3 +- .../OneToOneBidirectionalAssociationTest.php | 5 +- ...OneToOneSelfReferentialAssociationTest.php | 3 +- .../OneToOneUnidirectionalAssociationTest.php | 3 +- .../ORM/Functional/Ticket/DDC599Test.php | 2 +- .../ORM/Hydration/ObjectHydratorTest.php | 3 +- .../ORM/Mapping/AbstractMappingDriverTest.php | 55 ++- .../Tests/ORM/Mapping/ClassMetadataTest.php | 23 +- .../Tests/ORM/PersistentCollectionTest.php | 2 + .../ORM/Tools/ConvertDoctrine1SchemaTest.php | 4 +- .../AbstractClassMetadataExporterTest.php | 76 ++-- 48 files changed, 828 insertions(+), 1449 deletions(-) delete mode 100644 lib/Doctrine/ORM/Mapping/AssociationMapping.php delete mode 100644 lib/Doctrine/ORM/Mapping/ManyToManyMapping.php delete mode 100644 lib/Doctrine/ORM/Mapping/OneToManyMapping.php delete mode 100644 lib/Doctrine/ORM/Mapping/OneToOneMapping.php diff --git a/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php index aaa2c6ece..92eb45c5c 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php @@ -19,7 +19,7 @@ namespace Doctrine\ORM\Internal\Hydration; -use PDO, Doctrine\DBAL\Connection; +use PDO, Doctrine\DBAL\Connection, Doctrine\ORM\Mapping\ClassMetadata; /** * The ArrayHydrator produces a nested array "graph" that is often (not always) @@ -109,7 +109,7 @@ class ArrayHydrator extends AbstractHydrator $relation = $this->_getClassMetadata($this->_rsm->aliasMap[$parent])->associationMappings[$relationAlias]; // Check the type of the relation (many or single-valued) - if ( ! $relation->isOneToOne()) { + if ( ! ($relation['type'] & ClassMetadata::TO_ONE)) { $oneToOne = false; if (isset($nonemptyComponents[$dqlAlias])) { if ( ! isset($baseElement[$relationAlias])) { diff --git a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php index 3764af7c5..b7aaed198 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php @@ -20,6 +20,7 @@ namespace Doctrine\ORM\Internal\Hydration; use PDO, + Doctrine\ORM\Mapping\ClassMetadata, Doctrine\ORM\PersistentCollection, Doctrine\ORM\Query, Doctrine\Common\Collections\ArrayCollection, @@ -74,24 +75,24 @@ class ObjectHydrator extends AbstractHydrator $sourceClassName = $this->_rsm->aliasMap[$this->_rsm->parentAliasMap[$dqlAlias]]; $sourceClass = $this->_getClassMetadata($sourceClassName); $assoc = $sourceClass->associationMappings[$this->_rsm->relationMap[$dqlAlias]]; - $this->_hints['fetched'][$sourceClassName][$assoc->sourceFieldName] = true; + $this->_hints['fetched'][$sourceClassName][$assoc['fieldName']] = true; if ($sourceClass->subClasses) { foreach ($sourceClass->subClasses as $sourceSubclassName) { - $this->_hints['fetched'][$sourceSubclassName][$assoc->sourceFieldName] = true; + $this->_hints['fetched'][$sourceSubclassName][$assoc['fieldName']] = true; } } - if ( ! $assoc->isManyToMany()) { + if ($assoc['type'] != ClassMetadata::MANY_TO_MANY) { // Mark any non-collection opposite sides as fetched, too. - if ($assoc->mappedBy) { - $this->_hints['fetched'][$className][$assoc->mappedBy] = true; + if ($assoc['mappedBy']) { + $this->_hints['fetched'][$className][$assoc['mappedBy']] = true; } else { - if ($assoc->inversedBy) { - $inverseAssoc = $class->associationMappings[$assoc->inversedBy]; - if ($inverseAssoc->isOneToOne()) { - $this->_hints['fetched'][$className][$inverseAssoc->sourceFieldName] = true; + if ($assoc['inversedBy']) { + $inverseAssoc = $class->associationMappings[$assoc['inversedBy']]; + if ($inverseAssoc['type'] & ClassMetadata::TO_ONE) { + $this->_hints['fetched'][$className][$inverseAssoc['fieldName']] = true; if ($class->subClasses) { foreach ($class->subClasses as $targetSubclassName) { - $this->_hints['fetched'][$targetSubclassName][$inverseAssoc->sourceFieldName] = true; + $this->_hints['fetched'][$targetSubclassName][$inverseAssoc['fieldName']] = true; } } } @@ -153,7 +154,7 @@ class ObjectHydrator extends AbstractHydrator if ( ! $value instanceof PersistentCollection) { $value = new PersistentCollection( $this->_em, - $this->_ce[$relation->targetEntityName], + $this->_ce[$relation['targetEntity']], $value ); $value->setOwner($entity, $relation); @@ -285,9 +286,9 @@ class ObjectHydrator extends AbstractHydrator $relationField = $this->_rsm->relationMap[$dqlAlias]; $relation = $parentClass->associationMappings[$relationField]; $reflField = $parentClass->reflFields[$relationField]; - + // Check the type of the relation (many or single-valued) - if ( ! $relation->isOneToOne()) { + if ( ! ($relation['type'] & ClassMetadata::TO_ONE)) { // PATH A: Collection-valued association if (isset($nonemptyComponents[$dqlAlias])) { $collKey = $oid . $relationField; @@ -342,24 +343,24 @@ class ObjectHydrator extends AbstractHydrator $element = $this->_getEntity($data, $dqlAlias); $reflField->setValue($parentObject, $element); $this->_uow->setOriginalEntityProperty($oid, $relationField, $element); - $targetClass = $this->_ce[$relation->targetEntityName]; - if ($relation->isOwningSide) { + $targetClass = $this->_ce[$relation['targetEntity']]; + if ($relation['isOwningSide']) { //TODO: Just check hints['fetched'] here? // If there is an inverse mapping on the target class its bidirectional - if ($relation->inversedBy) { - $inverseAssoc = $targetClass->associationMappings[$relation->inversedBy]; - if ($inverseAssoc->isOneToOne()) { - $targetClass->reflFields[$inverseAssoc->sourceFieldName]->setValue($element, $parentObject); - $this->_uow->setOriginalEntityProperty(spl_object_hash($element), $inverseAssoc->sourceFieldName, $parentObject); + if ($relation['inversedBy']) { + $inverseAssoc = $targetClass->associationMappings[$relation['inversedBy']]; + if ($inverseAssoc['type'] == ClassMetadata::ONE_TO_ONE) { + $targetClass->reflFields[$inverseAssoc['fieldName']]->setValue($element, $parentObject); + $this->_uow->setOriginalEntityProperty(spl_object_hash($element), $inverseAssoc['fieldName'], $parentObject); } - } else if ($parentClass === $targetClass && $relation->mappedBy) { + } else if ($parentClass === $targetClass && $relation['mappedBy']) { // Special case: bi-directional self-referencing one-one on the same class $targetClass->reflFields[$relationField]->setValue($element, $parentObject); } } else { // For sure bidirectional, as there is no inverse side in unidirectional mappings - $targetClass->reflFields[$relation->mappedBy]->setValue($element, $parentObject); - $this->_uow->setOriginalEntityProperty(spl_object_hash($element), $relation->mappedBy, $parentObject); + $targetClass->reflFields[$relation['mappedBy']]->setValue($element, $parentObject); + $this->_uow->setOriginalEntityProperty(spl_object_hash($element), $relation['mappedBy'], $parentObject); } // Update result pointer $this->_resultPointers[$dqlAlias] = $element; diff --git a/lib/Doctrine/ORM/Mapping/AssociationMapping.php b/lib/Doctrine/ORM/Mapping/AssociationMapping.php deleted file mode 100644 index 488bcd0ff..000000000 --- a/lib/Doctrine/ORM/Mapping/AssociationMapping.php +++ /dev/null @@ -1,399 +0,0 @@ -. - */ - -namespace Doctrine\ORM\Mapping; - -/** - * Base class for association mappings. - * - * IMPORTANT NOTE: - * - * The fields of this class are only public for 2 reasons: - * 1) To allow fast, internal READ access. - * 2) To drastically reduce the size of a serialized instance (private/protected members - * get the whole class name, namespace inclusive, prepended to every property in - * the serialized representation). - * - * @author Roman Borschel - * @since 2.0 - * @todo Potentially remove if assoc mapping objects get replaced by simple arrays. - */ -abstract class AssociationMapping -{ - /** - * Specifies that an association is to be fetched when it is first accessed. - * - * @var integer - */ - const FETCH_LAZY = 2; - /** - * Specifies that an association is to be fetched when the owner of the - * association is fetched. - * - * @var integer - */ - const FETCH_EAGER = 3; - - /** - * READ-ONLY: Whether the association cascades delete() operations from the source entity - * to the target entity/entities. - * - * @var boolean - */ - public $isCascadeRemove; - - /** - * READ-ONLY: Whether the association cascades persist() operations from the source entity - * to the target entity/entities. - * - * @var boolean - */ - public $isCascadePersist; - - /** - * READ-ONLY: Whether the association cascades refresh() operations from the source entity - * to the target entity/entities. - * - * @var boolean - */ - public $isCascadeRefresh; - - /** - * READ-ONLY: Whether the association cascades merge() operations from the source entity - * to the target entity/entities. - * - * @var boolean - */ - public $isCascadeMerge; - - /** - * READ-ONLY: Whether the association cascades detach() operations from the source entity - * to the target entity/entities. - * - * @var boolean - */ - public $isCascadeDetach; - - /** - * READ-ONLY: The fetch mode used for the association. - * - * @var integer - */ - public $fetchMode; - - /** - * READ-ONLY: Flag that indicates whether the class that defines this mapping is - * the owning side of the association. - * - * @var boolean - */ - public $isOwningSide = true; - - /** - * READ-ONLY: The name of the source Entity (the Entity that defines this mapping). - * - * @var string - */ - public $sourceEntityName; - - /** - * READ-ONLY: The name of the target Entity (the Enitity that is the target of the - * association). - * - * @var string - */ - public $targetEntityName; - - /** - * READ-ONLY: Identifies the field on the source class (the class this AssociationMapping - * belongs to) that represents the association and stores the reference to the - * other entity/entities. - * - * @var string - */ - public $sourceFieldName; - - /** - * READ-ONLY: Identifies the field on the owning side of a bidirectional association that - * controls the mapping for the association. This is only set on the inverse side - * of an association. - * - * @var string - */ - public $mappedBy; - - /** - * READ-ONLY: Identifies the field on the inverse side of a bidirectional association. - * This is only set on the owning side of an association. - * - * @var string - */ - public $inversedBy; - - /** - * READ-ONLY: The join table definition, if any. - * - * @var array - */ - public $joinTable; - - /** - * READ-ONLY: The name of the entity class from which the association was - * inherited in an inheritance hierarchy. - * - * @var string - */ - public $inherited; - - /** - * READ-ONLY: The name of the entity or mapped superclass that declares - * the association field in an inheritance hierarchy. - * - * @var string - */ - public $declared; - - /** - * Initializes a new instance of a class derived from AssociationMapping. - * - * @param array $mapping The mapping definition. - */ - public function __construct(array $mapping) - { - $this->_validateAndCompleteMapping($mapping); - } - - /** - * Validates & completes the mapping. Mapping defaults are applied here. - * - * @param array $mapping - * @throws MappingException If something is wrong with the mapping. - */ - protected function _validateAndCompleteMapping(array $mapping) - { - // Mandatory attributes for both sides - if ( ! isset($mapping['fieldName'])) { - throw MappingException::missingFieldName(); - } - $this->sourceFieldName = $mapping['fieldName']; - - if ( ! isset($mapping['sourceEntity'])) { - throw MappingException::missingSourceEntity($mapping['fieldName']); - } - $this->sourceEntityName = $mapping['sourceEntity']; - - if ( ! isset($mapping['targetEntity'])) { - throw MappingException::missingTargetEntity($mapping['fieldName']); - } - $this->targetEntityName = $mapping['targetEntity']; - - // Mandatory and optional attributes for either side - if ( ! isset($mapping['mappedBy'])) { - // Optional - if (isset($mapping['joinTable']) && $mapping['joinTable']) { - if ($mapping['joinTable']['name'][0] == '`') { - $mapping['joinTable']['name'] = trim($mapping['joinTable']['name'], '`'); - $mapping['joinTable']['quoted'] = true; - } - $this->joinTable = $mapping['joinTable']; - } - if (isset($mapping['inversedBy'])) { - $this->inversedBy = $mapping['inversedBy']; - } - } else { - $this->isOwningSide = false; - $this->mappedBy = $mapping['mappedBy']; - } - - // Optional attributes for both sides - $this->fetchMode = isset($mapping['fetch']) ? $mapping['fetch'] : self::FETCH_LAZY; - $cascades = isset($mapping['cascade']) ? $mapping['cascade'] : array(); - - if (in_array('all', $cascades)) { - $cascades = array( - 'remove', - 'persist', - 'refresh', - 'merge', - 'detach' - ); - } - - $this->isCascadeRemove = in_array('remove', $cascades); - $this->isCascadePersist = in_array('persist', $cascades); - $this->isCascadeRefresh = in_array('refresh', $cascades); - $this->isCascadeMerge = in_array('merge', $cascades); - $this->isCascadeDetach = in_array('detach', $cascades); - } - - /** - * Whether the target entity/entities of the association are eagerly fetched. - * - * @return boolean - */ - public function isEagerlyFetched() - { - return $this->fetchMode == self::FETCH_EAGER; - } - - /** - * Whether the target entity/entities of the association are lazily fetched. - * - * @return boolean - */ - public function isLazilyFetched() - { - return $this->fetchMode == self::FETCH_LAZY; - } - - /** - * Whether the association is a one-to-one association. - * - * @return boolean - */ - public function isOneToOne() - { - return false; - } - - /** - * Whether the association is a one-to-many association. - * - * @return boolean - */ - public function isOneToMany() - { - return false; - } - - /** - * Whether the association is a many-to-many association. - * - * @return boolean - */ - public function isManyToMany() - { - return false; - } - - /** - * Whether the association uses a join table for the mapping. - * - * @return boolean - */ - public function usesJoinTable() - { - return (bool) $this->joinTable; - } - - /** - * Checks whether the association has any cascades configured. - * - * @return boolean - */ - public function hasCascades() - { - return $this->isCascadePersist || - $this->isCascadeRemove || - $this->isCascadeRefresh || - $this->isCascadeMerge || - $this->isCascadeDetach; - } - - /** - * Loads data in $target domain object using this association. - * The data comes from the association navigated from $sourceEntity - * using $em. - * - * @param object $sourceEntity - * @param object $target an entity or a collection - * @param EntityManager $em - * @param array $joinColumnValues foreign keys (significative for this - * association) of $sourceEntity, if needed - */ - abstract public function load($sourceEntity, $target, $em, array $joinColumnValues = array()); - - /** - * Gets the (possibly quoted) name of the join table. - * - * @param AbstractPlatform $platform - * @return string - */ - public function getQuotedJoinTableName($platform) - { - return isset($this->joinTable['quoted']) - ? $platform->quoteIdentifier($this->joinTable['name']) - : $this->joinTable['name']; - } - - /** - * Determines which fields get serialized. - * - * It is only serialized what is necessary for best unserialization performance. - * That means any metadata properties that are not set or empty or simply have - * their default value are NOT serialized. - * - * @return array The names of all the fields that should be serialized. - */ - public function __sleep() - { - $serialized = array( - 'sourceEntityName', - 'targetEntityName', - 'sourceFieldName', - 'fetchMode' - ); - - if ($this->isCascadeDetach) { - $serialized[] = 'isCascadeDetach'; - } - if ($this->isCascadeMerge) { - $serialized[] = 'isCascadeMerge'; - } - if ($this->isCascadePersist) { - $serialized[] = 'isCascadePersist'; - } - if ($this->isCascadeRefresh) { - $serialized[] = 'isCascadeRefresh'; - } - if ($this->isCascadeRemove) { - $serialized[] = 'isCascadeRemove'; - } - if ( ! $this->isOwningSide) { - $serialized[] = 'isOwningSide'; - } - if ($this->mappedBy) { - $serialized[] = 'mappedBy'; - } - if ($this->inversedBy) { - $serialized[] = 'inversedBy'; - } - if ($this->joinTable) { - $serialized[] = 'joinTable'; - } - if ($this->inherited) { - $serialized[] = 'inherited'; - } - if ($this->declared) { - $serialized[] = 'declared'; - } - - return $serialized; - } -} diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadata.php b/lib/Doctrine/ORM/Mapping/ClassMetadata.php index de3cad029..15112adbe 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadata.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadata.php @@ -193,12 +193,12 @@ class ClassMetadata extends ClassMetadataInfo * * @param AssociationMapping $assocMapping */ - protected function _storeAssociationMapping(AssociationMapping $assocMapping) + protected function _storeAssociationMapping(array $assocMapping) { parent::_storeAssociationMapping($assocMapping); // Store ReflectionProperty of mapped field - $sourceFieldName = $assocMapping->sourceFieldName; + $sourceFieldName = $assocMapping['fieldName']; $refProp = $this->reflClass->getProperty($sourceFieldName); $refProp->setAccessible(true); @@ -234,6 +234,19 @@ class ClassMetadata extends ClassMetadataInfo $this->table['name']; } + /** + * Gets the (possibly quoted) name of the join table. + * + * @param AbstractPlatform $platform + * @return string + */ + public function getQuotedJoinTableName(array $assoc, $platform) + { + return isset($assoc['joinTable']['quoted']) + ? $platform->quoteIdentifier($assoc['joinTable']['name']) + : $assoc['joinTable']['name']; + } + /** * Creates a string representation of this instance. * @@ -337,8 +350,8 @@ class ClassMetadata extends ClassMetadataInfo } foreach ($this->associationMappings as $field => $mapping) { - if ($mapping->declared) { - $reflField = new ReflectionProperty($mapping->declared, $field); + if (isset($mapping['declared'])) { + $reflField = new ReflectionProperty($mapping['declared'], $field); } else { $reflField = $this->reflClass->getProperty($field); } diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php index c84778e4a..bc11daa3d 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php @@ -255,7 +255,7 @@ class ClassMetadataFactory // Invoke driver try { $this->_driver->loadMetadataForClass($className, $class); - } catch(ReflectionException $e) { + } catch (ReflectionException $e) { throw MappingException::reflectionFailure($className, $e); } @@ -346,14 +346,14 @@ class ClassMetadataFactory private function _addInheritedRelations(ClassMetadata $subClass, ClassMetadata $parentClass) { foreach ($parentClass->associationMappings as $field => $mapping) { - $subclassMapping = clone $mapping; - if ( ! isset($mapping->inherited) && ! $parentClass->isMappedSuperclass) { - $subclassMapping->inherited = $parentClass->name; + //$subclassMapping = $mapping; + if ( ! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass) { + $mapping['inherited'] = $parentClass->name; } - if ( ! isset($mapping->declared)) { - $subclassMapping->declared = $parentClass->name; + if ( ! isset($mapping['declared'])) { + $mapping['declared'] = $parentClass->name; } - $subClass->addInheritedAssociationMapping($subclassMapping); + $subClass->addInheritedAssociationMapping($mapping); } } diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php index 8a016fd47..c25835bb7 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php @@ -112,6 +112,25 @@ class ClassMetadataInfo * the NotifyPropertyChanged interface. */ const CHANGETRACKING_NOTIFY = 3; + /** + * Specifies that an association is to be fetched when it is first accessed. + * + * @var integer + */ + const FETCH_LAZY = 2; + /** + * Specifies that an association is to be fetched when the owner of the + * association is fetched. + * + * @var integer + */ + const FETCH_EAGER = 3; + const ONE_TO_ONE = 1; + const MANY_TO_ONE = 2; + const TO_ONE = 3; + const ONE_TO_MANY = 4; + const MANY_TO_MANY = 8; + const TO_MANY = 12; /** * READ-ONLY: The name of the entity class. @@ -600,6 +619,215 @@ class ClassMetadataInfo } } + /** + * Validates & completes the mapping. Mapping defaults are applied here. + * + * @param array $mapping + * @throws MappingException If something is wrong with the mapping. + */ + protected function _validateAndCompleteAssociationMapping(array $mapping) + { + if ( ! isset($mapping['mappedBy'])) { + $mapping['mappedBy'] = null; + } + if ( ! isset($mapping['inversedBy'])) { + $mapping['inversedBy'] = null; + } + $mapping['isOwningSide'] = true; + + $mapping['sourceEntity'] = $this->name; + if (isset($mapping['targetEntity']) && strpos($mapping['targetEntity'], '\\') === false && strlen($this->namespace) > 0) { + $mapping['targetEntity'] = $this->namespace . '\\' . $mapping['targetEntity']; + } + + // Mandatory attributes for both sides + if ( ! isset($mapping['fieldName'])) { + throw MappingException::missingFieldName(); + } + + if ( ! isset($mapping['sourceEntity'])) { + throw MappingException::missingSourceEntity($mapping['fieldName']); + } + + if ( ! isset($mapping['targetEntity'])) { + throw MappingException::missingTargetEntity($mapping['fieldName']); + } + + // Mandatory and optional attributes for either side + if ( ! isset($mapping['mappedBy'])) { + // Optional + if (isset($mapping['joinTable']) && $mapping['joinTable']) { + if ($mapping['joinTable']['name'][0] == '`') { + $mapping['joinTable']['name'] = trim($mapping['joinTable']['name'], '`'); + $mapping['joinTable']['quoted'] = true; + } + } + } else { + $mapping['isOwningSide'] = false; + } + + // Optional attributes for both sides + if ( ! isset($mapping['fetch'])) { + $mapping['fetch'] = self::FETCH_LAZY; + } + $cascades = isset($mapping['cascade']) ? $mapping['cascade'] : array(); + + if (in_array('all', $cascades)) { + $cascades = array( + 'remove', + 'persist', + 'refresh', + 'merge', + 'detach' + ); + } + $mapping['cascade'] = $cascades; + $mapping['isCascadeRemove'] = in_array('remove', $cascades); + $mapping['isCascadePersist'] = in_array('persist', $cascades); + $mapping['isCascadeRefresh'] = in_array('refresh', $cascades); + $mapping['isCascadeMerge'] = in_array('merge', $cascades); + $mapping['isCascadeDetach'] = in_array('detach', $cascades); + + return $mapping; + } + + /** + * {@inheritdoc} + * + * @param array $mapping The mapping to validate & complete. + * @return array The validated & completed mapping. + * @override + */ + protected function _validateAndCompleteOneToOneMapping(array $mapping) + { + $mapping = $this->_validateAndCompleteAssociationMapping($mapping); + + if (isset($mapping['joinColumns']) && $mapping['joinColumns']) { + $mapping['isOwningSide'] = true; + } + + if ($mapping['isOwningSide']) { + if ( ! isset($mapping['joinColumns']) || ! $mapping['joinColumns']) { + // Apply default join column + $mapping['joinColumns'] = array(array( + 'name' => $mapping['fieldName'] . '_id', + 'referencedColumnName' => 'id' + )); + } + foreach ($mapping['joinColumns'] as $joinColumn) { + $mapping['sourceToTargetKeyColumns'][$joinColumn['name']] = $joinColumn['referencedColumnName']; + $mapping['joinColumnFieldNames'][$joinColumn['name']] = isset($joinColumn['fieldName']) + ? $joinColumn['fieldName'] : $joinColumn['name']; + } + $mapping['targetToSourceKeyColumns'] = array_flip($mapping['sourceToTargetKeyColumns']); + } + + //TODO: if orphanRemoval, cascade=remove is implicit! + $mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ? + (bool) $mapping['orphanRemoval'] : false; + + return $mapping; + } + + /** + * Validates and completes the mapping. + * + * @param array $mapping The mapping to validate and complete. + * @return array The validated and completed mapping. + * @override + */ + protected function _validateAndCompleteOneToManyMapping(array $mapping) + { + $mapping = $this->_validateAndCompleteAssociationMapping($mapping); + + // OneToMany-side MUST be inverse (must have mappedBy) + if ( ! isset($mapping['mappedBy'])) { + throw MappingException::oneToManyRequiresMappedBy($mapping['fieldName']); + } + + //TODO: if orphanRemoval, cascade=remove is implicit! + $mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ? + (bool) $mapping['orphanRemoval'] : false; + + if (isset($mapping['orderBy'])) { + if ( ! is_array($mapping['orderBy'])) { + throw new \InvalidArgumentException("'orderBy' is expected to be an array, not ".gettype($mapping['orderBy'])); + } + } + + return $mapping; + } + + protected function _validateAndCompleteManyToManyMapping(array $mapping) + { + $mapping = $this->_validateAndCompleteAssociationMapping($mapping); + if ($mapping['isOwningSide']) { + // owning side MUST have a join table + if ( ! isset($mapping['joinTable']) || ! $mapping['joinTable']) { + // Apply default join table + $sourceShortName = substr($mapping['sourceEntity'], strrpos($mapping['sourceEntity'], '\\') + 1); + $targetShortName = substr($mapping['targetEntity'], strrpos($mapping['targetEntity'], '\\') + 1); + $mapping['joinTable'] = array( + 'name' => $sourceShortName .'_' . $targetShortName, + 'joinColumns' => array( + array( + 'name' => $sourceShortName . '_id', + 'referencedColumnName' => 'id', + 'onDelete' => 'CASCADE' + ) + ), + 'inverseJoinColumns' => array( + array( + 'name' => $targetShortName . '_id', + 'referencedColumnName' => 'id', + 'onDelete' => 'CASCADE' + ) + ) + ); + } + // owning side MUST specify joinColumns + else if ( ! isset($mapping['joinTable']['joinColumns'])) { + throw MappingException::missingRequiredOption( + $mapping['fieldName'], 'joinColumns', + 'Did you think of case sensitivity / plural s?' + ); + } + // owning side MUST specify inverseJoinColumns + else if ( ! isset($mapping['joinTable']['inverseJoinColumns'])) { + throw MappingException::missingRequiredOption( + $mapping['fieldName'], 'inverseJoinColumns', + 'Did you think of case sensitivity / plural s?' + ); + } + + foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) { + if (isset($joinColumn['onDelete']) && strtolower($joinColumn['onDelete']) == 'cascade') { + $mapping['isOnDeleteCascade'] = true; + } + + $mapping['relationToSourceKeyColumns'][$joinColumn['name']] = $joinColumn['referencedColumnName']; + $mapping['joinTableColumns'][] = $joinColumn['name']; + } + + foreach ($mapping['joinTable']['inverseJoinColumns'] as $inverseJoinColumn) { + if (isset($inverseJoinColumn['onDelete']) && strtolower($inverseJoinColumn['onDelete']) == 'cascade') { + $mapping['isOnDeleteCascade'] = true; + } + + $mapping['relationToTargetKeyColumns'][$inverseJoinColumn['name']] = $inverseJoinColumn['referencedColumnName']; + $mapping['joinTableColumns'][] = $inverseJoinColumn['name']; + } + } + + if (isset($mapping['orderBy'])) { + if ( ! is_array($mapping['orderBy'])) { + throw new \InvalidArgumentException("'orderBy' is expected to be an array, not ".gettype($mapping['orderBy'])); + } + } + + return $mapping; + } + /** * Gets the identifier (primary key) field names of the class. * @@ -897,7 +1125,7 @@ class ClassMetadataInfo */ public function isInheritedAssociation($fieldName) { - return isset($this->associationMappings[$fieldName]->inherited); + return isset($this->associationMappings[$fieldName]['inherited']); } /** @@ -951,14 +1179,14 @@ class ClassMetadataInfo * @param array $mapping * @todo Pass param by ref? */ - private function _completeAssociationMapping(array $mapping) + /*private function _completeAssociationMapping(array $mapping) { $mapping['sourceEntity'] = $this->name; if (isset($mapping['targetEntity']) && strpos($mapping['targetEntity'], '\\') === false && strlen($this->namespace) > 0) { $mapping['targetEntity'] = $this->namespace . '\\' . $mapping['targetEntity']; } return $mapping; - } + }*/ /** * Adds a mapped field to the class. @@ -982,12 +1210,12 @@ class ClassMetadataInfo * @param AssociationMapping $mapping * @param string $owningClassName The name of the class that defined this mapping. */ - public function addInheritedAssociationMapping(AssociationMapping $mapping/*, $owningClassName = null*/) + public function addInheritedAssociationMapping(array $mapping/*, $owningClassName = null*/) { - if (isset($this->associationMappings[$mapping->sourceFieldName])) { - throw MappingException::duplicateAssociationMapping($this->name, $mapping->sourceFieldName); + if (isset($this->associationMappings[$mapping['fieldName']])) { + throw MappingException::duplicateAssociationMapping($this->name, $mapping['fieldName']); } - $this->associationMappings[$mapping->sourceFieldName] = $mapping; + $this->associationMappings[$mapping['fieldName']] = $mapping; } /** @@ -1012,9 +1240,9 @@ class ClassMetadataInfo */ public function mapOneToOne(array $mapping) { - $mapping = $this->_completeAssociationMapping($mapping); - $oneToOneMapping = new OneToOneMapping($mapping); - $this->_storeAssociationMapping($oneToOneMapping); + $mapping['type'] = self::ONE_TO_ONE; + $mapping = $this->_validateAndCompleteOneToOneMapping($mapping); + $this->_storeAssociationMapping($mapping); } /** @@ -1024,9 +1252,9 @@ class ClassMetadataInfo */ public function mapOneToMany(array $mapping) { - $mapping = $this->_completeAssociationMapping($mapping); - $oneToManyMapping = new OneToManyMapping($mapping); - $this->_storeAssociationMapping($oneToManyMapping); + $mapping['type'] = self::ONE_TO_MANY; + $mapping = $this->_validateAndCompleteOneToManyMapping($mapping); + $this->_storeAssociationMapping($mapping); } /** @@ -1036,8 +1264,10 @@ class ClassMetadataInfo */ public function mapManyToOne(array $mapping) { - // A many-to-one mapping is simply a one-one backreference - $this->mapOneToOne($mapping); + $mapping['type'] = self::MANY_TO_ONE; + // A many-to-one mapping is essentially a one-one backreference + $mapping = $this->_validateAndCompleteOneToOneMapping($mapping); + $this->_storeAssociationMapping($mapping); } /** @@ -1047,9 +1277,9 @@ class ClassMetadataInfo */ public function mapManyToMany(array $mapping) { - $mapping = $this->_completeAssociationMapping($mapping); - $manyToManyMapping = new ManyToManyMapping($mapping); - $this->_storeAssociationMapping($manyToManyMapping); + $mapping['type'] = self::MANY_TO_MANY; + $mapping = $this->_validateAndCompleteManyToManyMapping($mapping); + $this->_storeAssociationMapping($mapping); } /** @@ -1057,9 +1287,9 @@ class ClassMetadataInfo * * @param AssociationMapping $assocMapping */ - protected function _storeAssociationMapping(AssociationMapping $assocMapping) + protected function _storeAssociationMapping(array $assocMapping) { - $sourceFieldName = $assocMapping->sourceFieldName; + $sourceFieldName = $assocMapping['fieldName']; if (isset($this->fieldMappings[$sourceFieldName]) || isset($this->associationMappings[$sourceFieldName])) { throw MappingException::duplicateFieldMapping($this->name, $sourceFieldName); } @@ -1207,7 +1437,7 @@ class ClassMetadataInfo public function isSingleValuedAssociation($fieldName) { return isset($this->associationMappings[$fieldName]) && - $this->associationMappings[$fieldName]->isOneToOne(); + ($this->associationMappings[$fieldName]['type'] & self::TO_ONE); } /** @@ -1220,7 +1450,7 @@ class ClassMetadataInfo public function isCollectionValuedAssociation($fieldName) { return isset($this->associationMappings[$fieldName]) && - ! $this->associationMappings[$fieldName]->isOneToOne(); + ! ($this->associationMappings[$fieldName]['type'] & self::TO_ONE); } /** diff --git a/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php b/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php index 7d0c1f778..f6faeb891 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php @@ -296,14 +296,14 @@ class AnnotationDriver implements Driver $mapping['inversedBy'] = $oneToOneAnnot->inversedBy; $mapping['cascade'] = $oneToOneAnnot->cascade; $mapping['orphanRemoval'] = $oneToOneAnnot->orphanRemoval; - $mapping['fetch'] = constant('Doctrine\ORM\Mapping\AssociationMapping::FETCH_' . $oneToOneAnnot->fetch); + $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $oneToOneAnnot->fetch); $metadata->mapOneToOne($mapping); } else if ($oneToManyAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\OneToMany')) { $mapping['mappedBy'] = $oneToManyAnnot->mappedBy; $mapping['targetEntity'] = $oneToManyAnnot->targetEntity; $mapping['cascade'] = $oneToManyAnnot->cascade; $mapping['orphanRemoval'] = $oneToManyAnnot->orphanRemoval; - $mapping['fetch'] = constant('Doctrine\ORM\Mapping\AssociationMapping::FETCH_' . $oneToManyAnnot->fetch); + $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $oneToManyAnnot->fetch); if ($orderByAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\OrderBy')) { $mapping['orderBy'] = $orderByAnnot->value; @@ -315,7 +315,7 @@ class AnnotationDriver implements Driver $mapping['cascade'] = $manyToOneAnnot->cascade; $mapping['inversedBy'] = $manyToOneAnnot->inversedBy; $mapping['targetEntity'] = $manyToOneAnnot->targetEntity; - $mapping['fetch'] = constant('Doctrine\ORM\Mapping\AssociationMapping::FETCH_' . $manyToOneAnnot->fetch); + $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $manyToOneAnnot->fetch); $metadata->mapManyToOne($mapping); } else if ($manyToManyAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\ManyToMany')) { $joinTable = array(); @@ -356,7 +356,7 @@ class AnnotationDriver implements Driver $mapping['mappedBy'] = $manyToManyAnnot->mappedBy; $mapping['inversedBy'] = $manyToManyAnnot->inversedBy; $mapping['cascade'] = $manyToManyAnnot->cascade; - $mapping['fetch'] = constant('Doctrine\ORM\Mapping\AssociationMapping::FETCH_' . $manyToManyAnnot->fetch); + $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $manyToManyAnnot->fetch); if ($orderByAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\OrderBy')) { $mapping['orderBy'] = $orderByAnnot->value; diff --git a/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php b/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php index e8f97d835..3a95ca52c 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php @@ -238,7 +238,7 @@ class XmlDriver extends AbstractFileDriver ); if (isset($oneToOneElement['fetch'])) { - $mapping['fetch'] = constant('Doctrine\ORM\Mapping\AssociationMapping::FETCH_' . (string)$oneToOneElement['fetch']); + $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . (string)$oneToOneElement['fetch']); } if (isset($oneToOneElement['mapped-by'])) { @@ -282,7 +282,7 @@ class XmlDriver extends AbstractFileDriver ); if (isset($oneToManyElement['fetch'])) { - $mapping['fetch'] = constant('Doctrine\ORM\Mapping\AssociationMapping::FETCH_' . (string)$oneToManyElement['fetch']); + $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . (string)$oneToManyElement['fetch']); } if (isset($oneToManyElement->cascade)) { @@ -314,7 +314,7 @@ class XmlDriver extends AbstractFileDriver ); if (isset($manyToOneElement['fetch'])) { - $mapping['fetch'] = constant('Doctrine\ORM\Mapping\AssociationMapping::FETCH_' . (string)$manyToOneElement['fetch']); + $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . (string)$manyToOneElement['fetch']); } if (isset($manyToOneElement['inversed-by'])) { @@ -357,7 +357,7 @@ class XmlDriver extends AbstractFileDriver ); if (isset($manyToManyElement['fetch'])) { - $mapping['fetch'] = constant('Doctrine\ORM\Mapping\AssociationMapping::FETCH_' . (string)$manyToManyElement['fetch']); + $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . (string)$manyToManyElement['fetch']); } if (isset($manyToManyElement['mapped-by'])) { diff --git a/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php b/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php index 967760785..5ab26e388 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php @@ -237,7 +237,7 @@ class YamlDriver extends AbstractFileDriver ); if (isset($oneToOneElement['fetch'])) { - $mapping['fetch'] = constant('Doctrine\ORM\Mapping\AssociationMapping::FETCH_' . $oneToOneElement['fetch']); + $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $oneToOneElement['fetch']); } if (isset($oneToOneElement['mappedBy'])) { @@ -282,7 +282,7 @@ class YamlDriver extends AbstractFileDriver ); if (isset($oneToManyElement['fetch'])) { - $mapping['fetch'] = constant('Doctrine\ORM\Mapping\AssociationMapping::FETCH_' . $oneToManyElement['fetch']); + $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $oneToManyElement['fetch']); } if (isset($oneToManyElement['cascade'])) { @@ -306,7 +306,7 @@ class YamlDriver extends AbstractFileDriver ); if (isset($manyToOneElement['fetch'])) { - $mapping['fetch'] = constant('Doctrine\ORM\Mapping\AssociationMapping::FETCH_' . $manyToOneElement['fetch']); + $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $manyToOneElement['fetch']); } if (isset($manyToOneElement['inversedBy'])) { @@ -346,7 +346,7 @@ class YamlDriver extends AbstractFileDriver ); if (isset($manyToManyElement['fetch'])) { - $mapping['fetch'] = constant('Doctrine\ORM\Mapping\AssociationMapping::FETCH_' . $manyToManyElement['fetch']); + $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $manyToManyElement['fetch']); } if (isset($manyToManyElement['mappedBy'])) { diff --git a/lib/Doctrine/ORM/Mapping/ManyToManyMapping.php b/lib/Doctrine/ORM/Mapping/ManyToManyMapping.php deleted file mode 100644 index a308b4eb8..000000000 --- a/lib/Doctrine/ORM/Mapping/ManyToManyMapping.php +++ /dev/null @@ -1,195 +0,0 @@ -. - */ - -namespace Doctrine\ORM\Mapping; - -/** - * A many-to-many mapping describes the mapping between two collections of - * entities. - * - * IMPORTANT NOTE: - * - * The fields of this class are only public for 2 reasons: - * 1) To allow fast READ access. - * 2) To drastically reduce the size of a serialized instance (private/protected members - * get the whole class name, namespace inclusive, prepended to every property in - * the serialized representation). - * - * Instances of this class are stored serialized in the metadata cache together with the - * owning ClassMetadata instance. - * - * @since 2.0 - * @author Roman Borschel - * @author Giorgio Sironi - * @todo Potentially remove if assoc mapping objects get replaced by simple arrays. - */ -class ManyToManyMapping extends AssociationMapping -{ - /** - * READ-ONLY: Maps the columns in the relational table to the columns in the source table. - */ - public $relationToSourceKeyColumns = array(); - - /** - * READ-ONLY: Maps the columns in the relation table to the columns in the target table. - */ - public $relationToTargetKeyColumns = array(); - - /** - * READ-ONLY: List of aggregated column names on the join table. - */ - public $joinTableColumns = array(); - - /** FUTURE: The key column mapping, if any. The key column holds the keys of the Collection. */ - //public $keyColumn; - - /** - * READ-ONLY: Order this collection by the given DQL snippet. - * - * Only simple unqualified field names and ASC|DESC are allowed - * - * @var array - */ - public $orderBy; - - /** - * READ-ONLY: Are entries on the owning AND inverse side of this join-table deleted through a database onDelete="CASCADE" operation? - * - * @var bool - */ - public $isOnDeleteCascade = false; - - /** - * {@inheritdoc} - */ - protected function _validateAndCompleteMapping(array $mapping) - { - parent::_validateAndCompleteMapping($mapping); - if ($this->isOwningSide) { - // owning side MUST have a join table - if ( ! isset($mapping['joinTable']) || ! $mapping['joinTable']) { - // Apply default join table - $sourceShortName = substr($this->sourceEntityName, strrpos($this->sourceEntityName, '\\') + 1); - $targetShortName = substr($this->targetEntityName, strrpos($this->targetEntityName, '\\') + 1); - $mapping['joinTable'] = array( - 'name' => $sourceShortName .'_' . $targetShortName, - 'joinColumns' => array( - array( - 'name' => $sourceShortName . '_id', - 'referencedColumnName' => 'id', - 'onDelete' => 'CASCADE' - ) - ), - 'inverseJoinColumns' => array( - array( - 'name' => $targetShortName . '_id', - 'referencedColumnName' => 'id', - 'onDelete' => 'CASCADE' - ) - ) - ); - $this->joinTable = $mapping['joinTable']; - } - // owning side MUST specify joinColumns - else if ( ! isset($mapping['joinTable']['joinColumns'])) { - throw MappingException::missingRequiredOption( - $this->sourceFieldName, 'joinColumns', - 'Did you think of case sensitivity / plural s?' - ); - } - // owning side MUST specify inverseJoinColumns - else if ( ! isset($mapping['joinTable']['inverseJoinColumns'])) { - throw MappingException::missingRequiredOption( - $this->sourceFieldName, 'inverseJoinColumns', - 'Did you think of case sensitivity / plural s?' - ); - } - - foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) { - if (isset($joinColumn['onDelete']) && strtolower($joinColumn['onDelete']) == 'cascade') { - $this->isOnDeleteCascade = true; - } - - $this->relationToSourceKeyColumns[$joinColumn['name']] = $joinColumn['referencedColumnName']; - $this->joinTableColumns[] = $joinColumn['name']; - } - - foreach ($mapping['joinTable']['inverseJoinColumns'] as $inverseJoinColumn) { - if (isset($inverseJoinColumn['onDelete']) && strtolower($inverseJoinColumn['onDelete']) == 'cascade') { - $this->isOnDeleteCascade = true; - } - - $this->relationToTargetKeyColumns[$inverseJoinColumn['name']] = $inverseJoinColumn['referencedColumnName']; - $this->joinTableColumns[] = $inverseJoinColumn['name']; - } - } - - if (isset($mapping['orderBy'])) { - if ( ! is_array($mapping['orderBy'])) { - throw new \InvalidArgumentException("'orderBy' is expected to be an array, not ".gettype($mapping['orderBy'])); - } - $this->orderBy = $mapping['orderBy']; - } - } - - /** - * Loads entities in $targetCollection using $em. - * The data of $sourceEntity are used to restrict the collection - * via the join table. - * - * @param object The owner of the collection. - * @param object The collection to populate. - * @param array - * @todo Remove - */ - public function load($sourceEntity, $targetCollection, $em, array $joinColumnValues = array()) - { - $em->getUnitOfWork()->getEntityPersister($this->targetEntityName)->loadManyToManyCollection($this, $sourceEntity, $targetCollection); - } - - /** {@inheritdoc} */ - public function isManyToMany() - { - return true; - } - - /** - * Determines which fields get serialized. - * - * It is only serialized what is necessary for best unserialization performance. - * That means any metadata properties that are not set or empty or simply have - * their default value are NOT serialized. - * - * @return array The names of all the fields that should be serialized. - */ - public function __sleep() - { - $serialized = parent::__sleep(); - $serialized[] = 'joinTableColumns'; - $serialized[] = 'relationToSourceKeyColumns'; - $serialized[] = 'relationToTargetKeyColumns'; - if ($this->isOnDeleteCascade) { - $serialized[] = 'isOnDeleteCascade'; - } - if ($this->orderBy) { - $serialized[] = 'orderBy'; - } - return $serialized; - } -} diff --git a/lib/Doctrine/ORM/Mapping/OneToManyMapping.php b/lib/Doctrine/ORM/Mapping/OneToManyMapping.php deleted file mode 100644 index 4ffc1d511..000000000 --- a/lib/Doctrine/ORM/Mapping/OneToManyMapping.php +++ /dev/null @@ -1,144 +0,0 @@ -. - */ - -namespace Doctrine\ORM\Mapping; - -/** - * Represents a one-to-many mapping. - * - * NOTE: One-to-many mappings can currently not be uni-directional (one -> many). - * They must either be bidirectional (one <-> many) or unidirectional (many -> one). - * In other words, the many-side MUST be the owning side and the one-side MUST be - * the inverse side. - * - * IMPORTANT NOTE: - * - * The fields of this class are only public for 2 reasons: - * 1) To allow fast READ access. - * 2) To drastically reduce the size of a serialized instance (private/protected members - * get the whole class name, namespace inclusive, prepended to every property in - * the serialized representation). - * - * Instances of this class are stored serialized in the metadata cache together with the - * owning ClassMetadata instance. - * - * @author Roman Borschel - * @author Giorgio Sironi - * @since 2.0 - * @todo Potentially remove if assoc mapping objects get replaced by simple arrays. - */ -class OneToManyMapping extends AssociationMapping -{ - /** - * READ-ONLY: Whether to delete orphaned elements (removed from the collection) - * - * @var boolean - */ - public $orphanRemoval = false; - - /** FUTURE: The key column mapping, if any. The key column holds the keys of the Collection. */ - //public $keyColumn; - - /** - * READ-ONLY: Order this collection by the given SQL snippet. - */ - public $orderBy; - - /** - * Validates and completes the mapping. - * - * @param array $mapping The mapping to validate and complete. - * @return array The validated and completed mapping. - * @override - */ - protected function _validateAndCompleteMapping(array $mapping) - { - parent::_validateAndCompleteMapping($mapping); - - // OneToMany-side MUST be inverse (must have mappedBy) - if ( ! isset($mapping['mappedBy'])) { - throw MappingException::oneToManyRequiresMappedBy($mapping['fieldName']); - } - - //TODO: if orphanRemoval, cascade=remove is implicit! - $this->orphanRemoval = isset($mapping['orphanRemoval']) ? - (bool) $mapping['orphanRemoval'] : false; - - if (isset($mapping['orderBy'])) { - if (!is_array($mapping['orderBy'])) { - throw new \InvalidArgumentException("'orderBy' is expected to be an array, not ".gettype($mapping['orderBy'])); - } - $this->orderBy = $mapping['orderBy']; - } - } - - /** - * Whether orphaned elements (removed from the collection) should be deleted. - * - * @return boolean TRUE if orphaned elements should be deleted, FALSE otherwise. - */ - public function shouldDeleteOrphans() - { - return $this->deleteOrphans; - } - - /** - * {@inheritdoc} - */ - public function isOneToMany() - { - return true; - } - - /** - * Loads a one-to-many collection. - * - * @param $sourceEntity The entity that owns the collection. - * @param $targetCollection The collection to load/fill. - * @param $em The EntityManager to use. - * @param $joinColumnValues - * @return void - * @todo Remove - */ - public function load($sourceEntity, $targetCollection, $em, array $joinColumnValues = array()) - { - $em->getUnitOfWork()->getEntityPersister($this->targetEntityName)->loadOneToManyCollection($this, $sourceEntity, $targetCollection); - } - - /** - * Determines which fields get serialized. - * - * It is only serialized what is necessary for best unserialization performance. - * That means any metadata properties that are not set or empty or simply have - * their default value are NOT serialized. - * - * @return array The names of all the fields that should be serialized. - */ - public function __sleep() - { - $serialized = parent::__sleep(); - if ($this->orderBy) { - $serialized[] = 'orderBy'; - } - if ($this->orphanRemoval) { - $serialized[] = 'orphanRemoval'; - } - return $serialized; - } -} diff --git a/lib/Doctrine/ORM/Mapping/OneToOneMapping.php b/lib/Doctrine/ORM/Mapping/OneToOneMapping.php deleted file mode 100644 index 918228060..000000000 --- a/lib/Doctrine/ORM/Mapping/OneToOneMapping.php +++ /dev/null @@ -1,165 +0,0 @@ -. - */ - -namespace Doctrine\ORM\Mapping; - -/** - * A one-to-one mapping describes a uni-directional mapping from one entity - * to another entity. - * - * IMPORTANT NOTE: - * - * The fields of this class are only public for 2 reasons: - * 1) To allow fast READ access. - * 2) To drastically reduce the size of a serialized instance (private/protected members - * get the whole class name, namespace inclusive, prepended to every property in - * the serialized representation). - * - * Instances of this class are stored serialized in the metadata cache together with the - * owning ClassMetadata instance. - * - * @since 2.0 - * @author Roman Borschel - * @author Giorgio Sironi - * @todo Potentially remove if assoc mapping objects get replaced by simple arrays. - */ -class OneToOneMapping extends AssociationMapping -{ - /** - * READ-ONLY: Maps the source foreign/primary key columns to the target primary/foreign key columns. - * i.e. source.id (pk) => target.user_id (fk). - * Reverse mapping of _targetToSourceKeyColumns. - */ - public $sourceToTargetKeyColumns = array(); - - /** - * READ-ONLY: 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. - */ - public $targetToSourceKeyColumns = array(); - - /** - * READ-ONLY: Whether to delete orphaned elements (when nulled out, i.e. $foo->other = null) - * - * @var boolean - */ - public $orphanRemoval = false; - - /** - * READ-ONLY: The join column definitions. Only present on the owning side. - * - * @var array - */ - public $joinColumns = array(); - - /** - * READ-ONLY: A map of join column names to field names that are used in cases - * when the join columns are fetched as part of the query result. - * - * @var array - */ - public $joinColumnFieldNames = array(); - - /** - * {@inheritdoc} - * - * @param array $mapping The mapping to validate & complete. - * @return array The validated & completed mapping. - * @override - */ - protected function _validateAndCompleteMapping(array $mapping) - { - parent::_validateAndCompleteMapping($mapping); - - if (isset($mapping['joinColumns']) && $mapping['joinColumns']) { - $this->isOwningSide = true; - } - - if ($this->isOwningSide) { - if ( ! isset($mapping['joinColumns']) || ! $mapping['joinColumns']) { - // Apply default join column - $mapping['joinColumns'] = array(array( - 'name' => $this->sourceFieldName . '_id', - 'referencedColumnName' => 'id' - )); - } - foreach ($mapping['joinColumns'] as $joinColumn) { - $this->sourceToTargetKeyColumns[$joinColumn['name']] = $joinColumn['referencedColumnName']; - $this->joinColumnFieldNames[$joinColumn['name']] = isset($joinColumn['fieldName']) - ? $joinColumn['fieldName'] : $joinColumn['name']; - } - $this->joinColumns = $mapping['joinColumns']; - $this->targetToSourceKeyColumns = array_flip($this->sourceToTargetKeyColumns); - } - - //TODO: if orphanRemoval, cascade=remove is implicit! - $this->orphanRemoval = isset($mapping['orphanRemoval']) ? - (bool) $mapping['orphanRemoval'] : false; - - return $mapping; - } - - /** - * {@inheritdoc} - * - * @return boolean - * @override - */ - public function isOneToOne() - { - return true; - } - - /** - * {@inheritdoc} - * - * @param object $sourceEntity the entity source of this association - * @param object $targetEntity the entity to load data in - * @param EntityManager $em - * @param array $joinColumnValues Values of the join columns of $sourceEntity. - * @todo Remove - */ - public function load($sourceEntity, $targetEntity, $em, array $joinColumnValues = array()) - { - return $em->getUnitOfWork()->getEntityPersister($this->targetEntityName)->loadOneToOneEntity($this, $sourceEntity, $targetEntity, $joinColumnValues); - } - - /** - * Determines which fields get serialized. - * - * It is only serialized what is necessary for best unserialization performance. - * That means any metadata properties that are not set or empty or simply have - * their default value are NOT serialized. - * - * @return array The names of all the fields that should be serialized. - */ - public function __sleep() - { - $serialized = parent::__sleep(); - $serialized[] = 'joinColumns'; - $serialized[] = 'joinColumnFieldNames'; - $serialized[] = 'sourceToTargetKeyColumns'; - $serialized[] = 'targetToSourceKeyColumns'; - if ($this->orphanRemoval) { - $serialized[] = 'orphanRemoval'; - } - return $serialized; - } -} diff --git a/lib/Doctrine/ORM/PersistentCollection.php b/lib/Doctrine/ORM/PersistentCollection.php index 31e9b74dc..0b7632605 100644 --- a/lib/Doctrine/ORM/PersistentCollection.php +++ b/lib/Doctrine/ORM/PersistentCollection.php @@ -19,7 +19,7 @@ namespace Doctrine\ORM; -use Doctrine\ORM\Mapping\AssociationMapping, +use Doctrine\ORM\Mapping\ClassMetadata, Doctrine\Common\Collections\Collection, Closure; @@ -127,11 +127,11 @@ final class PersistentCollection implements Collection * @param object $entity * @param AssociationMapping $assoc */ - public function setOwner($entity, AssociationMapping $assoc) + public function setOwner($entity, array $assoc) { $this->owner = $entity; $this->association = $assoc; - $this->backRefFieldName = $assoc->inversedBy ?: $assoc->mappedBy; + $this->backRefFieldName = $assoc['inversedBy'] ?: $assoc['mappedBy']; } /** @@ -162,7 +162,7 @@ final class PersistentCollection implements Collection $this->coll->add($element); // If _backRefFieldName is set and its a one-to-many association, // we need to set the back reference. - if ($this->backRefFieldName && $this->association->isOneToMany()) { + if ($this->backRefFieldName && $this->association['type'] == ClassMetadata::ONE_TO_MANY) { // Set back reference to owner $this->typeClass->reflFields[$this->backRefFieldName] ->setValue($element, $this->owner); @@ -185,7 +185,7 @@ final class PersistentCollection implements Collection $this->coll->set($key, $element); // If _backRefFieldName is set, then the association is bidirectional // and we need to set the back reference. - if ($this->backRefFieldName && $this->association->isOneToMany()) { + if ($this->backRefFieldName && $this->association['type'] == ClassMetadata::ONE_TO_MANY) { // Set back reference to owner $this->typeClass->reflFields[$this->backRefFieldName] ->setValue($element, $this->owner); @@ -204,7 +204,7 @@ final class PersistentCollection implements Collection $newObjects = $this->coll->toArray(); } $this->coll->clear(); - $this->association->load($this->owner, $this, $this->em); + $this->em->getUnitOfWork()->loadCollection($this->association, $this->owner, $this); $this->takeSnapshot(); // Reattach NEW objects added through add(), if any. if (isset($newObjects)) { @@ -279,7 +279,7 @@ final class PersistentCollection implements Collection { if ( ! $this->isDirty) { $this->isDirty = true; - if ($this->association !== null && $this->association->isOwningSide && $this->association->isManyToMany() && + if ($this->association !== null && $this->association['isOwningSide'] && $this->association['type'] == ClassMetadata::MANY_TO_MANY && $this->em->getClassMetadata(get_class($this->owner))->isChangeTrackingNotify()) { $this->em->getUnitOfWork()->scheduleForDirtyCheck($this->owner); } @@ -354,8 +354,8 @@ final class PersistentCollection implements Collection $removed = $this->coll->remove($key); if ($removed) { $this->changed(); - if ($this->association !== null && $this->association->isOneToMany() && - $this->association->orphanRemoval) { + if ($this->association !== null && $this->association['type'] == ClassMetadata::ONE_TO_MANY && + $this->association['orphanRemoval']) { $this->em->getUnitOfWork()->scheduleOrphanRemoval($removed); } } @@ -382,8 +382,8 @@ final class PersistentCollection implements Collection $removed = $this->coll->removeElement($element); if ($removed) { $this->changed(); - if ($this->association !== null && $this->association->isOneToMany() && - $this->association->orphanRemoval) { + if ($this->association !== null && $this->association['type'] == ClassMetadata::ONE_TO_MANY && + $this->association['orphanRemoval']) { $this->em->getUnitOfWork()->scheduleOrphanRemoval($element); } } @@ -570,13 +570,13 @@ final class PersistentCollection implements Collection if ($this->initialized && $this->isEmpty()) { return; } - if ($this->association->isOneToMany() && $this->association->orphanRemoval) { + if ($this->association['type'] == ClassMetadata::ONE_TO_MANY && $this->association['orphanRemoval']) { foreach ($this->coll as $element) { $this->em->getUnitOfWork()->scheduleOrphanRemoval($element); } } $this->coll->clear(); - if ($this->association->isOwningSide) { + if ($this->association['isOwningSide']) { $this->changed(); $this->em->getUnitOfWork()->scheduleCollectionDeletion($this); $this->takeSnapshot(); diff --git a/lib/Doctrine/ORM/Persisters/AbstractCollectionPersister.php b/lib/Doctrine/ORM/Persisters/AbstractCollectionPersister.php index b3195f13d..489bb82fc 100644 --- a/lib/Doctrine/ORM/Persisters/AbstractCollectionPersister.php +++ b/lib/Doctrine/ORM/Persisters/AbstractCollectionPersister.php @@ -64,7 +64,8 @@ abstract class AbstractCollectionPersister */ public function delete(PersistentCollection $coll) { - if ( ! $coll->getMapping()->isOwningSide) { + $mapping = $coll->getMapping(); + if ( ! $mapping['isOwningSide']) { return; // ignore inverse side } $sql = $this->_getDeleteSQL($coll); @@ -94,7 +95,8 @@ abstract class AbstractCollectionPersister */ public function update(PersistentCollection $coll) { - if ( ! $coll->getMapping()->isOwningSide) { + $mapping = $coll->getMapping(); + if ( ! $mapping['isOwningSide']) { return; // ignore inverse side } $this->deleteRows($coll); diff --git a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php index 15ce83392..93f6efa75 100644 --- a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php @@ -28,10 +28,7 @@ use PDO, Doctrine\ORM\Query, Doctrine\ORM\PersistentCollection, Doctrine\ORM\Mapping\MappingException, - Doctrine\ORM\Mapping\ClassMetadata, - Doctrine\ORM\Mapping\OneToOneMapping, - Doctrine\ORM\Mapping\OneToManyMapping, - Doctrine\ORM\Mapping\ManyToManyMapping; + Doctrine\ORM\Mapping\ClassMetadata; /** * A BasicEntityPersiter maps an entity to a single table in a relational database. @@ -342,32 +339,31 @@ class BasicEntityPersister */ protected function deleteJoinTableRecords($identifier) { - foreach ($this->_class->associationMappings AS $mapping) { - /* @var $mapping \Doctrine\ORM\Mapping\AssociationMapping */ - if ($mapping->isManyToMany()) { + foreach ($this->_class->associationMappings as $mapping) { + if ($mapping['type'] == ClassMetadata::MANY_TO_MANY) { // @Todo this only covers scenarios with no inheritance or of the same level. Is there something // like self-referential relationship between different levels of an inheritance hierachy? I hope not! - $selfReferential = ($mapping->targetEntityName == $mapping->sourceEntityName); + $selfReferential = ($mapping['targetEntity'] == $mapping['sourceEntity']); - if (!$mapping->isOwningSide) { - $relatedClass = $this->_em->getClassMetadata($mapping->targetEntityName); - $mapping = $relatedClass->associationMappings[$mapping->mappedBy]; - $keys = array_keys($mapping->relationToTargetKeyColumns); + if ( ! $mapping['isOwningSide']) { + $relatedClass = $this->_em->getClassMetadata($mapping['targetEntity']); + $mapping = $relatedClass->associationMappings[$mapping['mappedBy']]; + $keys = array_keys($mapping['relationToTargetKeyColumns']); if ($selfReferential) { - $otherKeys = array_keys($mapping->relationToSourceKeyColumns); + $otherKeys = array_keys($mapping['relationToSourceKeyColumns']); } } else { - $keys = array_keys($mapping->relationToSourceKeyColumns); + $keys = array_keys($mapping['relationToSourceKeyColumns']); if ($selfReferential) { - $otherKeys = array_keys($mapping->relationToTargetKeyColumns); + $otherKeys = array_keys($mapping['relationToTargetKeyColumns']); } } - if(!$mapping->isOnDeleteCascade) { - $this->_conn->delete($mapping->joinTable['name'], array_combine($keys, $identifier)); + if ( ! isset($mapping['isOnDeleteCascade'])) { + $this->_conn->delete($mapping['joinTable']['name'], array_combine($keys, $identifier)); if ($selfReferential) { - $this->_conn->delete($mapping->joinTable['name'], array_combine($otherKeys, $identifier)); + $this->_conn->delete($mapping['joinTable']['name'], array_combine($otherKeys, $identifier)); } } } @@ -443,7 +439,7 @@ class BasicEntityPersister if (isset($this->_class->associationMappings[$field])) { $assoc = $this->_class->associationMappings[$field]; // Only owning side of x-1 associations can have a FK column. - if ( ! $assoc->isOwningSide || ! $assoc->isOneToOne()) { + if ( ! $assoc['isOwningSide'] || ! ($assoc['type'] & ClassMetadata::TO_ONE)) { continue; } @@ -464,10 +460,10 @@ class BasicEntityPersister $newValId = $uow->getEntityIdentifier($newVal); } - $targetClass = $this->_em->getClassMetadata($assoc->targetEntityName); + $targetClass = $this->_em->getClassMetadata($assoc['targetEntity']); $owningTable = $this->getOwningTable($field); - foreach ($assoc->sourceToTargetKeyColumns as $sourceColumn => $targetColumn) { + foreach ($assoc['sourceToTargetKeyColumns'] as $sourceColumn => $targetColumn) { if ($newVal === null) { $result[$owningTable][$sourceColumn] = null; } else { @@ -540,7 +536,7 @@ class BasicEntityPersister * Loads an entity of this persister's mapped class as part of a single-valued * association from another entity. * - * @param OneToOneMapping $assoc The association to load. + * @param array $assoc The association to load. * @param object $sourceEntity The entity that owns the association (not necessarily the "owning side"). * @param object $targetEntity The existing ghost entity (proxy) to load, if any. * @param array $identifier The identifier of the entity to load. Must be provided if @@ -548,21 +544,21 @@ class BasicEntityPersister * the identifier is derived from the $sourceEntity. * @return object The loaded and managed entity instance or NULL if the entity can not be found. */ - public function loadOneToOneEntity(OneToOneMapping $assoc, $sourceEntity, $targetEntity, array $identifier = array()) + public function loadOneToOneEntity(array $assoc, $sourceEntity, $targetEntity, array $identifier = array()) { - $targetClass = $this->_em->getClassMetadata($assoc->targetEntityName); + $targetClass = $this->_em->getClassMetadata($assoc['targetEntity']); - if ($assoc->isOwningSide) { - $isInverseSingleValued = $assoc->inversedBy && ! $targetClass->isCollectionValuedAssociation($assoc->inversedBy); + if ($assoc['isOwningSide']) { + $isInverseSingleValued = $assoc['inversedBy'] && ! $targetClass->isCollectionValuedAssociation($assoc['inversedBy']); // Mark inverse side as fetched in the hints, otherwise the UoW would // try to load it in a separate query (remember: to-one inverse sides can not be lazy). $hints = array(); if ($isInverseSingleValued) { - $hints['fetched'][$targetClass->name][$assoc->inversedBy] = true; + $hints['fetched'][$targetClass->name][$assoc['inversedBy']] = true; if ($targetClass->subClasses) { foreach ($targetClass->subClasses as $targetSubclassName) { - $hints['fetched'][$targetSubclassName][$assoc->inversedBy] = true; + $hints['fetched'][$targetSubclassName][$assoc['inversedBy']] = true; } } } @@ -576,13 +572,13 @@ class BasicEntityPersister // Complete bidirectional association, if necessary if ($targetEntity !== null && $isInverseSingleValued) { - $targetClass->reflFields[$assoc->inversedBy]->setValue($targetEntity, $sourceEntity); + $targetClass->reflFields[$assoc['inversedBy']]->setValue($targetEntity, $sourceEntity); } } else { - $sourceClass = $this->_em->getClassMetadata($assoc->sourceEntityName); - $owningAssoc = $targetClass->getAssociationMapping($assoc->mappedBy); + $sourceClass = $this->_em->getClassMetadata($assoc['sourceEntity']); + $owningAssoc = $targetClass->getAssociationMapping($assoc['mappedBy']); // TRICKY: since the association is specular source and target are flipped - foreach ($owningAssoc->targetToSourceKeyColumns as $sourceKeyColumn => $targetKeyColumn) { + foreach ($owningAssoc['targetToSourceKeyColumns'] as $sourceKeyColumn => $targetKeyColumn) { if (isset($sourceClass->fieldNames[$sourceKeyColumn])) { $identifier[$targetKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity); } else { @@ -595,7 +591,7 @@ class BasicEntityPersister $targetEntity = $this->load($identifier, $targetEntity, $assoc); if ($targetEntity !== null) { - $targetClass->setFieldValue($targetEntity, $assoc->mappedBy, $sourceEntity); + $targetClass->setFieldValue($targetEntity, $assoc['mappedBy'], $sourceEntity); } } @@ -635,14 +631,14 @@ class BasicEntityPersister // Refresh associations foreach ($this->_class->associationMappings as $field => $assoc) { $value = $this->_class->reflFields[$field]->getValue($entity); - if ($assoc->isOneToOne()) { + if ($assoc['type'] & ClassMetadata::TO_ONE) { if ($value instanceof Proxy && ! $value->__isInitialized__) { continue; // skip uninitialized proxies } - if ($assoc->isOwningSide) { + if ($assoc['isOwningSide']) { $joinColumnValues = array(); - foreach ($assoc->targetToSourceKeyColumns as $targetColumn => $srcColumn) { + foreach ($assoc['targetToSourceKeyColumns'] as $targetColumn => $srcColumn) { if ($metaColumns[$srcColumn] !== null) { $joinColumnValues[$targetColumn] = $metaColumns[$srcColumn]; } @@ -653,18 +649,18 @@ class BasicEntityPersister } else if ($value !== null) { // Check identity map first, if the entity is not there, // place a proxy in there instead. - $targetClass = $this->_em->getClassMetadata($assoc->targetEntityName); + $targetClass = $this->_em->getClassMetadata($assoc['targetEntity']); if ($found = $this->_em->getUnitOfWork()->tryGetById($joinColumnValues, $targetClass->rootEntityName)) { $this->_class->reflFields[$field]->setValue($entity, $found); // Complete inverse side, if necessary. - if ($assoc->inversedBy) { - $inverseAssoc = $targetClass->associationMappings[$assoc->inversedBy]; - $targetClass->reflFields[$inverseAssoc->sourceFieldName]->setValue($found, $entity); + if ($assoc['inversedBy']) { + $inverseAssoc = $targetClass->associationMappings[$assoc['inversedBy']]; + $targetClass->reflFields[$inverseAssoc['fieldName']]->setValue($found, $entity); } $newData[$field] = $found; } else { // FIXME: What is happening with subClassees here? - $proxy = $this->_em->getProxyFactory()->getProxy($assoc->targetEntityName, $joinColumnValues); + $proxy = $this->_em->getProxyFactory()->getProxy($assoc['targetEntity'], $joinColumnValues); $this->_class->reflFields[$field]->setValue($entity, $proxy); $newData[$field] = $proxy; $this->_em->getUnitOfWork()->registerManaged($proxy, $joinColumnValues, array()); @@ -672,7 +668,9 @@ class BasicEntityPersister } } else { // Inverse side of 1-1/1-x can never be lazy. - $newData[$field] = $assoc->load($entity, null, $this->_em); + //$newData[$field] = $assoc->load($entity, null, $this->_em); + $newData[$field] = $this->_em->getUnitOfWork()->getEntityPersister($assoc['targetEntity']) + ->loadOneToOneEntity($assoc, $entity, null); } } else if ($value instanceof PersistentCollection && $value->isInitialized()) { $value->setInitialized(false); @@ -711,13 +709,13 @@ class BasicEntityPersister * @param object $sourceEntity The entity that owns the collection. * @param PersistentCollection $coll The collection to fill. */ - public function loadManyToManyCollection(ManyToManyMapping $assoc, $sourceEntity, PersistentCollection $coll) + public function loadManyToManyCollection(array $assoc, $sourceEntity, PersistentCollection $coll) { $criteria = array(); - $sourceClass = $this->_em->getClassMetadata($assoc->sourceEntityName); + $sourceClass = $this->_em->getClassMetadata($assoc['sourceEntity']); $joinTableConditions = array(); - if ($assoc->isOwningSide) { - foreach ($assoc->relationToSourceKeyColumns as $relationKeyColumn => $sourceKeyColumn) { + if ($assoc['isOwningSide']) { + foreach ($assoc['relationToSourceKeyColumns'] as $relationKeyColumn => $sourceKeyColumn) { if (isset($sourceClass->fieldNames[$sourceKeyColumn])) { $criteria[$relationKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity); } else { @@ -727,9 +725,9 @@ class BasicEntityPersister } } } else { - $owningAssoc = $this->_em->getClassMetadata($assoc->targetEntityName)->associationMappings[$assoc->mappedBy]; + $owningAssoc = $this->_em->getClassMetadata($assoc['targetEntity'])->associationMappings[$assoc['mappedBy']]; // TRICKY: since the association is inverted source and target are flipped - foreach ($owningAssoc->relationToTargetKeyColumns as $relationKeyColumn => $sourceKeyColumn) { + foreach ($owningAssoc['relationToTargetKeyColumns'] as $relationKeyColumn => $sourceKeyColumn) { if (isset($sourceClass->fieldNames[$sourceKeyColumn])) { $criteria[$relationKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity); } else { @@ -826,13 +824,13 @@ class BasicEntityPersister */ protected function _getSelectEntitiesSQL(array $criteria, $assoc = null, $lockMode = 0) { - $joinSql = $assoc != null && $assoc->isManyToMany() ? + $joinSql = $assoc != null && $assoc['type'] == ClassMetadata::MANY_TO_MANY ? $this->_getSelectManyToManyJoinSQL($assoc) : ''; $conditionSql = $this->_getSelectConditionSQL($criteria, $assoc); - $orderBySql = $assoc !== null && isset($assoc->orderBy) ? - $this->_getCollectionOrderBySQL($assoc->orderBy, $this->_getSQLTableAlias($this->_class->name)) + $orderBySql = $assoc !== null && isset($assoc['orderBy']) ? + $this->_getCollectionOrderBySQL($assoc['orderBy'], $this->_getSQLTableAlias($this->_class->name)) : ''; $lockSql = ''; @@ -917,17 +915,17 @@ class BasicEntityPersister * @param ManyToManyMapping $manyToMany * @return string */ - protected function _getSelectManyToManyJoinSQL(ManyToManyMapping $manyToMany) + protected function _getSelectManyToManyJoinSQL(array $manyToMany) { - if ($manyToMany->isOwningSide) { + if ($manyToMany['isOwningSide']) { $owningAssoc = $manyToMany; - $joinClauses = $manyToMany->relationToTargetKeyColumns; + $joinClauses = $manyToMany['relationToTargetKeyColumns']; } else { - $owningAssoc = $this->_em->getClassMetadata($manyToMany->targetEntityName)->associationMappings[$manyToMany->mappedBy]; - $joinClauses = $owningAssoc->relationToSourceKeyColumns; + $owningAssoc = $this->_em->getClassMetadata($manyToMany['targetEntity'])->associationMappings[$manyToMany['mappedBy']]; + $joinClauses = $owningAssoc['relationToSourceKeyColumns']; } - $joinTableName = $owningAssoc->getQuotedJoinTableName($this->_platform); + $joinTableName = $this->_class->getQuotedJoinTableName($owningAssoc, $this->_platform); $joinSql = ''; foreach ($joinClauses as $joinTableColumn => $sourceColumn) { @@ -985,8 +983,8 @@ class BasicEntityPersister } if (isset($this->_class->associationMappings[$name])) { $assoc = $this->_class->associationMappings[$name]; - if ($assoc->isOwningSide && $assoc->isOneToOne()) { - foreach ($assoc->targetToSourceKeyColumns as $sourceCol) { + if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) { + foreach ($assoc['targetToSourceKeyColumns'] as $sourceCol) { $columns[] = $sourceCol; } } @@ -1030,8 +1028,8 @@ class BasicEntityPersister { $sql = ''; foreach ($class->associationMappings as $assoc) { - if ($assoc->isOwningSide && $assoc->isOneToOne()) { - foreach ($assoc->targetToSourceKeyColumns as $srcColumn) { + if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) { + foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) { $columnAlias = $srcColumn . $this->_sqlAliasCounter++; $sql .= ', ' . $this->_getSQLTableAlias($this->_class->name) . ".$srcColumn AS $columnAlias"; $resultColumnName = $this->_platform->getSQLResultCasing($columnAlias); @@ -1123,10 +1121,10 @@ class BasicEntityPersister } $conditionSql .= $this->_class->getQuotedColumnName($field, $this->_platform); } else if ($assoc !== null) { - if ($assoc->isManyToMany()) { - $owningAssoc = $assoc->isOwningSide ? $assoc : $this->_em->getClassMetadata($assoc->targetEntityName) - ->associationMappings[$assoc->mappedBy]; - $conditionSql .= $owningAssoc->getQuotedJoinTableName($this->_platform) . '.' . $field; + if ($assoc['type'] == ClassMetadata::MANY_TO_MANY) { + $owningAssoc = $assoc['isOwningSide'] ? $assoc : $this->_em->getClassMetadata($assoc['targetEntity']) + ->associationMappings[$assoc['mappedBy']]; + $conditionSql .= $this->_class->getQuotedJoinTableName($owningAssoc, $this->_platform) . '.' . $field; } else { $conditionSql .= $field; } @@ -1145,12 +1143,12 @@ class BasicEntityPersister * @param array $criteria The criteria by which to select the entities. * @param PersistentCollection The collection to load/fill. */ - public function loadOneToManyCollection(OneToManyMapping $assoc, $sourceEntity, PersistentCollection $coll) + public function loadOneToManyCollection(array $assoc, $sourceEntity, PersistentCollection $coll) { $criteria = array(); - $owningAssoc = $this->_class->associationMappings[$assoc->mappedBy]; - $sourceClass = $this->_em->getClassMetadata($assoc->sourceEntityName); - foreach ($owningAssoc->targetToSourceKeyColumns as $sourceKeyColumn => $targetKeyColumn) { + $owningAssoc = $this->_class->associationMappings[$assoc['mappedBy']]; + $sourceClass = $this->_em->getClassMetadata($assoc['sourceEntity']); + foreach ($owningAssoc['targetToSourceKeyColumns'] as $sourceKeyColumn => $targetKeyColumn) { $criteria[$targetKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity); } diff --git a/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php b/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php index dc7d802a2..357497bb2 100644 --- a/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php +++ b/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php @@ -20,7 +20,7 @@ namespace Doctrine\ORM\Persisters; use Doctrine\ORM\ORMException, - Doctrine\ORM\Mapping\ManyToManyMapping; + Doctrine\ORM\Mapping\ClassMetadata; /** * The joined subclass persister maps a single entity instance to several tables in the @@ -75,9 +75,9 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister public function getOwningTable($fieldName) { if ( ! isset($this->_owningTableMap[$fieldName])) { - if (isset($this->_class->associationMappings[$fieldName]->inherited)) { + if (isset($this->_class->associationMappings[$fieldName]['inherited'])) { $this->_owningTableMap[$fieldName] = $this->_em->getClassMetadata( - $this->_class->associationMappings[$fieldName]->inherited + $this->_class->associationMappings[$fieldName]['inherited'] )->table['name']; } else if (isset($this->_class->fieldMappings[$fieldName]['inherited'])) { $this->_owningTableMap[$fieldName] = $this->_em->getClassMetadata( @@ -247,11 +247,11 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister // Add foreign key columns foreach ($this->_class->associationMappings as $assoc2) { - if ($assoc2->isOwningSide && $assoc2->isOneToOne()) { - $tableAlias = $assoc2->inherited ? - $this->_getSQLTableAlias($assoc2->inherited) + if ($assoc2['isOwningSide'] && $assoc2['type'] & ClassMetadata::TO_ONE) { + $tableAlias = isset($assoc2['inherited']) ? + $this->_getSQLTableAlias($assoc2['inherited']) : $baseTableAlias; - foreach ($assoc2->targetToSourceKeyColumns as $srcColumn) { + foreach ($assoc2['targetToSourceKeyColumns'] as $srcColumn) { $columnAlias = $srcColumn . $this->_sqlAliasCounter++; $columnList .= ", $tableAlias.$srcColumn AS $columnAlias"; $resultColumnName = $this->_platform->getSQLResultCasing($columnAlias); @@ -304,8 +304,9 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister // Add join columns (foreign keys) foreach ($subClass->associationMappings as $assoc2) { - if ($assoc2->isOwningSide && $assoc2->isOneToOne() && ! $assoc2->inherited) { - foreach ($assoc2->targetToSourceKeyColumns as $srcColumn) { + if ($assoc2['isOwningSide'] && $assoc2['type'] & ClassMetadata::TO_ONE + && ! isset($assoc2['inherited'])) { + foreach ($assoc2['targetToSourceKeyColumns'] as $srcColumn) { $columnAlias = $srcColumn . $this->_sqlAliasCounter++; $columnList .= ', ' . $tableAlias . ".$srcColumn AS $columnAlias"; $resultColumnName = $this->_platform->getSQLResultCasing($columnAlias); @@ -326,14 +327,14 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister } } - $joinSql .= $assoc != null && $assoc->isManyToMany() ? + $joinSql .= $assoc != null && $assoc['type'] == ClassMetadata::MANY_TO_MANY ? $this->_getSelectManyToManyJoinSQL($assoc) : ''; $conditionSql = $this->_getSelectConditionSQL($criteria, $assoc); $orderBySql = ''; - if ($assoc != null && isset($assoc->orderBy)) { - $orderBySql = $this->_getCollectionOrderBySQL($assoc->orderBy, $baseTableAlias); + if ($assoc != null && isset($assoc['orderBy'])) { + $orderBySql = $this->_getCollectionOrderBySQL($assoc['orderBy'], $baseTableAlias); } if ($this->_selectColumnListSql === null) { @@ -385,15 +386,15 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister foreach ($this->_class->reflFields as $name => $field) { if (isset($this->_class->fieldMappings[$name]['inherited']) && ! isset($this->_class->fieldMappings[$name]['id']) - || isset($this->_class->associationMappings[$name]->inherited) + || isset($this->_class->associationMappings[$name]['inherited']) || ($this->_class->isVersioned && $this->_class->versionField == $name)) { continue; } if (isset($this->_class->associationMappings[$name])) { $assoc = $this->_class->associationMappings[$name]; - if ($assoc->isOneToOne() && $assoc->isOwningSide) { - foreach ($assoc->targetToSourceKeyColumns as $sourceCol) { + if ($assoc['type'] & ClassMetadata::TO_ONE && $assoc['isOwningSide']) { + foreach ($assoc['targetToSourceKeyColumns'] as $sourceCol) { $columns[] = $sourceCol; } } diff --git a/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php b/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php index a182a9859..27e9a57fd 100644 --- a/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php +++ b/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php @@ -39,8 +39,8 @@ class ManyToManyPersister extends AbstractCollectionPersister protected function _getDeleteRowSQL(PersistentCollection $coll) { $mapping = $coll->getMapping(); - $joinTable = $mapping->joinTable; - $columns = $mapping->joinTableColumns; + $joinTable = $mapping['joinTable']; + $columns = $mapping['joinTableColumns']; return 'DELETE FROM ' . $joinTable['name'] . ' WHERE ' . implode(' = ? AND ', $columns) . ' = ?'; } @@ -74,8 +74,8 @@ class ManyToManyPersister extends AbstractCollectionPersister protected function _getInsertRowSQL(PersistentCollection $coll) { $mapping = $coll->getMapping(); - $joinTable = $mapping->joinTable; - $columns = $mapping->joinTableColumns; + $joinTable = $mapping['joinTable']; + $columns = $mapping['joinTableColumns']; return 'INSERT INTO ' . $joinTable['name'] . ' (' . implode(', ', $columns) . ')' . ' VALUES (' . implode(', ', array_fill(0, count($columns), '?')) . ')'; } @@ -104,7 +104,7 @@ class ManyToManyPersister extends AbstractCollectionPersister { $params = array(); $mapping = $coll->getMapping(); - $isComposite = count($mapping->joinTableColumns) > 2; + $isComposite = count($mapping['joinTableColumns']) > 2; $identifier1 = $this->_uow->getEntityIdentifier($coll->getOwner()); $identifier2 = $this->_uow->getEntityIdentifier($element); @@ -114,16 +114,16 @@ class ManyToManyPersister extends AbstractCollectionPersister $class2 = $coll->getTypeClass(); } - foreach ($mapping->joinTableColumns as $joinTableColumn) { - if (isset($mapping->relationToSourceKeyColumns[$joinTableColumn])) { + foreach ($mapping['joinTableColumns'] as $joinTableColumn) { + if (isset($mapping['relationToSourceKeyColumns'][$joinTableColumn])) { if ($isComposite) { - $params[] = $identifier1[$class1->fieldNames[$mapping->relationToSourceKeyColumns[$joinTableColumn]]]; + $params[] = $identifier1[$class1->fieldNames[$mapping['relationToSourceKeyColumns'][$joinTableColumn]]]; } else { $params[] = array_pop($identifier1); } } else { if ($isComposite) { - $params[] = $identifier2[$class2->fieldNames[$mapping->relationToTargetKeyColumns[$joinTableColumn]]]; + $params[] = $identifier2[$class2->fieldNames[$mapping['relationToTargetKeyColumns'][$joinTableColumn]]]; } else { $params[] = array_pop($identifier2); } @@ -141,9 +141,9 @@ class ManyToManyPersister extends AbstractCollectionPersister protected function _getDeleteSQL(PersistentCollection $coll) { $mapping = $coll->getMapping(); - $joinTable = $mapping->joinTable; + $joinTable = $mapping['joinTable']; $whereClause = ''; - foreach ($mapping->relationToSourceKeyColumns as $relationColumn => $srcColumn) { + foreach ($mapping['relationToSourceKeyColumns'] as $relationColumn => $srcColumn) { if ($whereClause !== '') $whereClause .= ' AND '; $whereClause .= "$relationColumn = ?"; } @@ -162,9 +162,9 @@ class ManyToManyPersister extends AbstractCollectionPersister $params = array(); $mapping = $coll->getMapping(); $identifier = $this->_uow->getEntityIdentifier($coll->getOwner()); - if (count($mapping->relationToSourceKeyColumns) > 1) { + if (count($mapping['relationToSourceKeyColumns']) > 1) { $sourceClass = $this->_em->getClassMetadata(get_class($mapping->getOwner())); - foreach ($mapping->relationToSourceKeyColumns as $relColumn => $srcColumn) { + foreach ($mapping['relationToSourceKeyColumns'] as $relColumn => $srcColumn) { $params[] = $identifier[$sourceClass->fieldNames[$srcColumn]]; } } else { diff --git a/lib/Doctrine/ORM/Persisters/OneToManyPersister.php b/lib/Doctrine/ORM/Persisters/OneToManyPersister.php index efecd2a50..f0d3aeafd 100644 --- a/lib/Doctrine/ORM/Persisters/OneToManyPersister.php +++ b/lib/Doctrine/ORM/Persisters/OneToManyPersister.php @@ -50,7 +50,7 @@ class OneToManyPersister extends AbstractCollectionPersister $targetClass = $this->_em->getClassMetadata($mapping->getTargetEntityName()); $table = $targetClass->getTableName(); - $ownerMapping = $targetClass->getAssociationMapping($mapping->mappedBy); + $ownerMapping = $targetClass->getAssociationMapping($mapping['mappedBy']); $setClause = ''; foreach ($ownerMapping->sourceToTargetKeyColumns as $sourceCol => $targetCol) { diff --git a/lib/Doctrine/ORM/Persisters/SingleTablePersister.php b/lib/Doctrine/ORM/Persisters/SingleTablePersister.php index ddeb1b108..856ff3485 100644 --- a/lib/Doctrine/ORM/Persisters/SingleTablePersister.php +++ b/lib/Doctrine/ORM/Persisters/SingleTablePersister.php @@ -61,8 +61,8 @@ class SingleTablePersister extends AbstractEntityInheritancePersister } // Foreign key columns foreach ($subClass->associationMappings as $assoc) { - if ($assoc->isOwningSide && $assoc->isOneToOne() && ! $assoc->inherited) { - foreach ($assoc->targetToSourceKeyColumns as $srcColumn) { + if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE && ! isset($assoc['inherited'])) { + foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) { $columnAlias = $srcColumn . $this->_sqlAliasCounter++; $columnList .= ', ' . $tableAlias . ".$srcColumn AS $columnAlias"; $resultColumnName = $this->_platform->getSQLResultCasing($columnAlias); diff --git a/lib/Doctrine/ORM/Query/AST/Functions/SizeFunction.php b/lib/Doctrine/ORM/Query/AST/Functions/SizeFunction.php index 9065276ba..601c4f804 100644 --- a/lib/Doctrine/ORM/Query/AST/Functions/SizeFunction.php +++ b/lib/Doctrine/ORM/Query/AST/Functions/SizeFunction.php @@ -51,18 +51,18 @@ class SizeFunction extends FunctionNode $assoc = $class->associationMappings[$assocField]; $sql = 'SELECT COUNT(*) FROM '; - if ($assoc->isOneToMany()) { - $targetClass = $sqlWalker->getEntityManager()->getClassMetadata($assoc->targetEntityName); + if ($assoc['type'] == \Doctrine\ORM\Mapping\ClassMetadata::ONE_TO_MANY) { + $targetClass = $sqlWalker->getEntityManager()->getClassMetadata($assoc['targetEntity']); $targetTableAlias = $sqlWalker->getSqlTableAlias($targetClass->table['name']); $sourceTableAlias = $sqlWalker->getSqlTableAlias($class->table['name'], $dqlAlias); $sql .= $targetClass->getQuotedTableName($platform) . ' ' . $targetTableAlias . ' WHERE '; - $owningAssoc = $targetClass->associationMappings[$assoc->mappedBy]; + $owningAssoc = $targetClass->associationMappings[$assoc['mappedBy']]; $first = true; - foreach ($owningAssoc->targetToSourceKeyColumns as $targetColumn => $sourceColumn) { + foreach ($owningAssoc['targetToSourceKeyColumns'] as $targetColumn => $sourceColumn) { if ($first) $first = false; else $sql .= ' AND '; $sql .= $targetTableAlias . '.' . $sourceColumn @@ -70,19 +70,19 @@ class SizeFunction extends FunctionNode . $sourceTableAlias . '.' . $class->getQuotedColumnName($class->fieldNames[$targetColumn], $platform); } } else { // many-to-many - $targetClass = $sqlWalker->getEntityManager()->getClassMetadata($assoc->targetEntityName); + $targetClass = $sqlWalker->getEntityManager()->getClassMetadata($assoc['targetEntity']); - $owningAssoc = $assoc->isOwningSide ? $assoc : $targetClass->associationMappings[$assoc->mappedBy]; - $joinTable = $owningAssoc->joinTable; + $owningAssoc = $assoc['isOwningSide'] ? $assoc : $targetClass->associationMappings[$assoc['mappedBy']]; + $joinTable = $owningAssoc['joinTable']; // SQL table aliases $joinTableAlias = $sqlWalker->getSqlTableAlias($joinTable['name']); $sourceTableAlias = $sqlWalker->getSqlTableAlias($class->table['name'], $dqlAlias); // join to target table - $sql .= $owningAssoc->getQuotedJoinTableName($platform) . ' ' . $joinTableAlias . ' WHERE '; + $sql .= $targetClass->getQuotedJoinTableName($owningAssoc, $platform) . ' ' . $joinTableAlias . ' WHERE '; - $joinColumns = $assoc->isOwningSide + $joinColumns = $assoc['isOwningSide'] ? $joinTable['joinColumns'] : $joinTable['inverseJoinColumns']; diff --git a/lib/Doctrine/ORM/Query/Exec/MultiTableUpdateExecutor.php b/lib/Doctrine/ORM/Query/Exec/MultiTableUpdateExecutor.php index 62d8b314a..7298db976 100644 --- a/lib/Doctrine/ORM/Query/Exec/MultiTableUpdateExecutor.php +++ b/lib/Doctrine/ORM/Query/Exec/MultiTableUpdateExecutor.php @@ -86,7 +86,7 @@ class MultiTableUpdateExecutor extends AbstractSqlExecutor foreach ($updateItems as $updateItem) { $field = $updateItem->pathExpression->field; if (isset($class->fieldMappings[$field]) && ! isset($class->fieldMappings[$field]['inherited']) || - isset($class->associationMappings[$field]) && ! $class->associationMappings[$field]->inherited) { + isset($class->associationMappings[$field]) && ! isset($class->associationMappings[$field]['inherited'])) { $newValue = $updateItem->newValue; if ( ! $affected) { diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index aeedf47d1..2a2fa70cd 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -20,6 +20,7 @@ namespace Doctrine\ORM\Query; use Doctrine\ORM\Query; +use Doctrine\ORM\Mapping\ClassMetadata; /** * An LL(*) recursive-descent parser for the context-free grammar of the Doctrine Query Language. @@ -581,9 +582,9 @@ class Parser $fieldType = AST\PathExpression::TYPE_STATE_FIELD; } else { $assoc = $class->associationMappings[$field]; - $class = $this->_em->getClassMetadata($assoc->targetEntityName); + $class = $this->_em->getClassMetadata($assoc['targetEntity']); - if ($assoc->isOneToOne()) { + if ($assoc['type'] & ClassMetadata::TO_ONE) { $fieldType = AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION; } else { $fieldType = AST\PathExpression::TYPE_COLLECTION_VALUED_ASSOCIATION; @@ -1478,7 +1479,7 @@ class Parser ); } - $targetClassName = $parentClass->getAssociationMapping($assocField)->targetEntityName; + $targetClassName = $parentClass->associationMappings[$assocField]['targetEntity']; // Building queryComponent $joinQueryComponent = array( diff --git a/lib/Doctrine/ORM/Query/QueryException.php b/lib/Doctrine/ORM/Query/QueryException.php index 9969e28b5..f9dfd0823 100644 --- a/lib/Doctrine/ORM/Query/QueryException.php +++ b/lib/Doctrine/ORM/Query/QueryException.php @@ -91,7 +91,7 @@ class QueryException extends \Doctrine\ORM\ORMException { return new self( "Invalid query operation: Not allowed to iterate over fetch join collections ". - "in class ".$assoc->sourceEntityName." assocation ".$assoc->sourceFieldName + "in class ".$assoc['sourceEntity']." assocation ".$assoc['fieldName'] ); } @@ -108,7 +108,7 @@ class QueryException extends \Doctrine\ORM\ORMException { return new self( "Unsupported query operation: It is not yet possible to overwrite the join ". - "conditions in class ".$assoc->sourceEntityName." assocation ".$assoc->sourceFieldName.". ". + "conditions in class ".$assoc['sourceEntityName']." assocation ".$assoc['fieldName'].". ". "Use WITH to append additional join conditions to the association." ); } @@ -123,8 +123,8 @@ class QueryException extends \Doctrine\ORM\ORMException public static function iterateWithFetchJoinNotAllowed($assoc) { return new self( - "Iterate with fetch join in class " . $assoc->sourceEntityName . - " using association " . $assoc->sourceFieldName . " not allowed." + "Iterate with fetch join in class " . $assoc['sourceEntity'] . + " using association " . $assoc['fieldName'] . " not allowed." ); } diff --git a/lib/Doctrine/ORM/Query/SqlWalker.php b/lib/Doctrine/ORM/Query/SqlWalker.php index d30941884..baaafcf77 100644 --- a/lib/Doctrine/ORM/Query/SqlWalker.php +++ b/lib/Doctrine/ORM/Query/SqlWalker.php @@ -20,6 +20,7 @@ namespace Doctrine\ORM\Query; use Doctrine\DBAL\LockMode, + Doctrine\ORM\Mapping\ClassMetadata, Doctrine\ORM\Query, Doctrine\ORM\Query\QueryException; @@ -287,8 +288,8 @@ class SqlWalker implements TreeWalker $sql = ''; foreach ($this->_selectedClasses AS $dqlAlias => $class) { $qComp = $this->_queryComponents[$dqlAlias]; - if (isset($qComp['relation']->orderBy)) { - foreach ($qComp['relation']->orderBy AS $fieldName => $orientation) { + if (isset($qComp['relation']['orderBy'])) { + foreach ($qComp['relation']['orderBy'] AS $fieldName => $orientation) { if ($qComp['metadata']->isInheritanceTypeJoined()) { $tableName = $this->_em->getUnitOfWork()->getEntityPersister($class->name)->getOwningTable($fieldName); } else { @@ -484,15 +485,15 @@ class SqlWalker implements TreeWalker $dqlAlias = $pathExpr->identificationVariable; $class = $this->_queryComponents[$dqlAlias]['metadata']; - if (isset($class->associationMappings[$fieldName]->inherited)) { - $class = $this->_em->getClassMetadata($class->associationMappings[$fieldName]->inherited); + if (isset($class->associationMappings[$fieldName]['inherited'])) { + $class = $this->_em->getClassMetadata($class->associationMappings[$fieldName]['inherited']); } $assoc = $class->associationMappings[$fieldName]; - if ($assoc->isOwningSide) { + if ($assoc['isOwningSide']) { // COMPOSITE KEYS NOT (YET?) SUPPORTED - if (count($assoc->sourceToTargetKeyColumns) > 1) { + if (count($assoc['sourceToTargetKeyColumns']) > 1) { throw QueryException::associationPathCompositeKeyNotSupported(); } @@ -500,7 +501,7 @@ class SqlWalker implements TreeWalker $sql .= $this->getSqlTableAlias($class->table['name'], $dqlAlias) . '.'; } - $sql .= reset($assoc->targetToSourceKeyColumns); + $sql .= reset($assoc['targetToSourceKeyColumns']); } else { throw QueryException::associationPathInverseSideNotSupported(); } @@ -539,7 +540,7 @@ class SqlWalker implements TreeWalker $this->_rsm->addJoinedEntityResult( $class->name, $dqlAlias, $this->_queryComponents[$dqlAlias]['parent'], - $this->_queryComponents[$dqlAlias]['relation']->sourceFieldName + $this->_queryComponents[$dqlAlias]['relation']['fieldName'] ); } @@ -559,15 +560,15 @@ class SqlWalker implements TreeWalker if ($addMetaColumns) { //FIXME: Include foreign key columns of child classes also!!?? foreach ($class->associationMappings as $assoc) { - if ($assoc->isOwningSide && $assoc->isOneToOne()) { - if ($assoc->inherited) { - $owningClass = $this->_em->getClassMetadata($assoc->inherited); + if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) { + if (isset($assoc['inherited'])) { + $owningClass = $this->_em->getClassMetadata($assoc['inherited']); $sqlTableAlias = $this->getSqlTableAlias($owningClass->table['name'], $dqlAlias); } else { $sqlTableAlias = $this->getSqlTableAlias($class->table['name'], $dqlAlias); } - foreach ($assoc->targetToSourceKeyColumns as $srcColumn) { + foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) { $columnAlias = $this->getSqlColumnAlias($srcColumn); $sql .= ", $sqlTableAlias." . $srcColumn . ' AS ' . $columnAlias; $columnAlias = $this->_platform->getSQLResultCasing($columnAlias); @@ -581,8 +582,8 @@ class SqlWalker implements TreeWalker if ($addMetaColumns) { $sqlTableAlias = $this->getSqlTableAlias($class->table['name'], $dqlAlias); foreach ($class->associationMappings as $assoc) { - if ($assoc->isOwningSide && $assoc->isOneToOne()) { - foreach ($assoc->targetToSourceKeyColumns as $srcColumn) { + if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) { + foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) { $columnAlias = $this->getSqlColumnAlias($srcColumn); $sql .= ', ' . $sqlTableAlias . '.' . $srcColumn . ' AS ' . $columnAlias; $columnAlias = $this->_platform->getSQLResultCasing($columnAlias); @@ -722,29 +723,29 @@ class SqlWalker implements TreeWalker $joinAssocPathExpr = $join->joinAssociationPathExpression; $joinedDqlAlias = $join->aliasIdentificationVariable; $relation = $this->_queryComponents[$joinedDqlAlias]['relation']; - $targetClass = $this->_em->getClassMetadata($relation->targetEntityName); - $sourceClass = $this->_em->getClassMetadata($relation->sourceEntityName); + $targetClass = $this->_em->getClassMetadata($relation['targetEntity']); + $sourceClass = $this->_em->getClassMetadata($relation['sourceEntity']); $targetTableName = $targetClass->getQuotedTableName($this->_platform); $targetTableAlias = $this->getSqlTableAlias($targetClass->table['name'], $joinedDqlAlias); $sourceTableAlias = $this->getSqlTableAlias($sourceClass->table['name'], $joinAssocPathExpr->identificationVariable); // Ensure we got the owning side, since it has all mapping info - $assoc = ( ! $relation->isOwningSide) ? $targetClass->associationMappings[$relation->mappedBy] : $relation; + $assoc = ( ! $relation['isOwningSide']) ? $targetClass->associationMappings[$relation['mappedBy']] : $relation; if ($this->_query->getHint(Query::HINT_INTERNAL_ITERATION) == true) { - if ($relation->isOneToMany() || $relation->isManyToMany()) { + if ($relation['type'] == ClassMetadata::ONE_TO_MANY || $relation['type'] == ClassMetadata::MANY_TO_MANY) { throw QueryException::iterateWithFetchJoinNotAllowed($assoc); } } - if ($assoc->isOneToOne()) { + if ($assoc['type'] & ClassMetadata::TO_ONE) { $sql .= $targetTableName . ' ' . $targetTableAlias . ' ON '; $first = true; - foreach ($assoc->sourceToTargetKeyColumns as $sourceColumn => $targetColumn) { + foreach ($assoc['sourceToTargetKeyColumns'] as $sourceColumn => $targetColumn) { if ( ! $first) $sql .= ' AND '; else $first = false; - if ($relation->isOwningSide) { + if ($relation['isOwningSide']) { $quotedTargetColumn = $targetClass->getQuotedColumnName($targetClass->fieldNames[$targetColumn], $this->_platform); $sql .= $sourceTableAlias . '.' . $sourceColumn . ' = ' @@ -756,15 +757,15 @@ class SqlWalker implements TreeWalker . $targetTableAlias . '.' . $sourceColumn; } } - } else if ($assoc->isManyToMany()) { + } else if ($assoc['type'] == ClassMetadata::MANY_TO_MANY) { // Join relation table - $joinTable = $assoc->joinTable; + $joinTable = $assoc['joinTable']; $joinTableAlias = $this->getSqlTableAlias($joinTable['name'], $joinedDqlAlias); - $sql .= $assoc->getQuotedJoinTableName($this->_platform) . ' ' . $joinTableAlias . ' ON '; + $sql .= $sourceClass->getQuotedJoinTableName($assoc, $this->_platform) . ' ' . $joinTableAlias . ' ON '; $first = true; - if ($relation->isOwningSide) { - foreach ($assoc->relationToSourceKeyColumns as $relationColumn => $sourceColumn) { + if ($relation['isOwningSide']) { + foreach ($assoc['relationToSourceKeyColumns'] as $relationColumn => $sourceColumn) { if ( ! $first) $sql .= ' AND '; else $first = false; $sql .= $sourceTableAlias . '.' . $sourceClass->getQuotedColumnName($sourceClass->fieldNames[$sourceColumn], $this->_platform) @@ -772,7 +773,7 @@ class SqlWalker implements TreeWalker . $joinTableAlias . '.' . $relationColumn; } } else { - foreach ($assoc->relationToTargetKeyColumns as $relationColumn => $targetColumn) { + foreach ($assoc['relationToTargetKeyColumns'] as $relationColumn => $targetColumn) { if ( ! $first) $sql .= ' AND '; else $first = false; $sql .= $sourceTableAlias . '.' . $sourceClass->getQuotedColumnName($sourceClass->fieldNames[$targetColumn], $this->_platform) @@ -787,8 +788,8 @@ class SqlWalker implements TreeWalker $sql .= $targetTableName . ' ' . $targetTableAlias . ' ON '; $first = true; - if ($relation->isOwningSide) { - foreach ($assoc->relationToTargetKeyColumns as $relationColumn => $targetColumn) { + if ($relation['isOwningSide']) { + foreach ($assoc['relationToTargetKeyColumns'] as $relationColumn => $targetColumn) { if ( ! $first) $sql .= ' AND '; else $first = false; $sql .= $targetTableAlias . '.' . $targetClass->getQuotedColumnName($targetClass->fieldNames[$targetColumn], $this->_platform) @@ -796,7 +797,7 @@ class SqlWalker implements TreeWalker . $joinTableAlias . '.' . $relationColumn; } } else { - foreach ($assoc->relationToSourceKeyColumns as $relationColumn => $sourceColumn) { + foreach ($assoc['relationToSourceKeyColumns'] as $relationColumn => $sourceColumn) { if ( ! $first) $sql .= ' AND '; else $first = false; $sql .= $targetTableAlias . '.' . $targetClass->getQuotedColumnName($targetClass->fieldNames[$sourceColumn], $this->_platform) @@ -995,8 +996,8 @@ class SqlWalker implements TreeWalker // Add join columns (foreign keys) of the subclass //TODO: Probably better do this in walkSelectClause to honor the INCLUDE_META_COLUMNS hint foreach ($subClass->associationMappings as $fieldName => $assoc) { - if ($assoc->isOwningSide && $assoc->isOneToOne() && ! $assoc->inherited) { - foreach ($assoc->targetToSourceKeyColumns as $srcColumn) { + if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE && ! isset($assoc['inherited'])) { + foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) { if ($beginning) $beginning = false; else $sql .= ', '; $columnAlias = $this->getSqlColumnAlias($srcColumn); $sql .= $sqlTableAlias . '.' . $srcColumn . ' AS ' . $columnAlias; @@ -1359,19 +1360,19 @@ class SqlWalker implements TreeWalker $assoc = $class->associationMappings[$fieldName]; - if ($assoc->isOneToMany()) { - $targetClass = $this->_em->getClassMetadata($assoc->targetEntityName); + if ($assoc['type'] == ClassMetadata::ONE_TO_MANY) { + $targetClass = $this->_em->getClassMetadata($assoc['targetEntity']); $targetTableAlias = $this->getSqlTableAlias($targetClass->table['name']); $sourceTableAlias = $this->getSqlTableAlias($class->table['name'], $dqlAlias); $sql .= $targetClass->getQuotedTableName($this->_platform) . ' ' . $targetTableAlias . ' WHERE '; - $owningAssoc = $targetClass->associationMappings[$assoc->mappedBy]; + $owningAssoc = $targetClass->associationMappings[$assoc['mappedBy']]; $first = true; - foreach ($owningAssoc->targetToSourceKeyColumns as $targetColumn => $sourceColumn) { + foreach ($owningAssoc['targetToSourceKeyColumns'] as $targetColumn => $sourceColumn) { if ($first) $first = false; else $sql .= ' AND '; $sql .= $sourceTableAlias . '.' . $class->getQuotedColumnName($class->fieldNames[$targetColumn], $this->_platform) @@ -1390,24 +1391,24 @@ class SqlWalker implements TreeWalker . $targetClass->getQuotedColumnName($idField, $this->_platform) . ' = ?'; } } else { // many-to-many - $targetClass = $this->_em->getClassMetadata($assoc->targetEntityName); - - $owningAssoc = $assoc->isOwningSide ? $assoc : $targetClass->associationMappings[$assoc->mappedBy]; - $joinTable = $owningAssoc->joinTable; + $targetClass = $this->_em->getClassMetadata($assoc['targetEntity']); + $owningAssoc = $assoc['isOwningSide'] ? $assoc : $targetClass->associationMappings[$assoc['mappedBy']]; + $joinTable = $owningAssoc['joinTable']; + // SQL table aliases $joinTableAlias = $this->getSqlTableAlias($joinTable['name']); $targetTableAlias = $this->getSqlTableAlias($targetClass->table['name']); $sourceTableAlias = $this->getSqlTableAlias($class->table['name'], $dqlAlias); // join to target table - $sql .= $owningAssoc->getQuotedJoinTableName($this->_platform) + $sql .= $targetClass->getQuotedJoinTableName($owningAssoc, $this->_platform) . ' ' . $joinTableAlias . ' INNER JOIN ' . $targetClass->getQuotedTableName($this->_platform) . ' ' . $targetTableAlias . ' ON '; // join conditions - $joinColumns = $assoc->isOwningSide + $joinColumns = $assoc['isOwningSide'] ? $joinTable['inverseJoinColumns'] : $joinTable['joinColumns']; @@ -1423,7 +1424,7 @@ class SqlWalker implements TreeWalker $sql .= ' WHERE '; - $joinColumns = $assoc->isOwningSide + $joinColumns = $assoc['isOwningSide'] ? $joinTable['joinColumns'] : $joinTable['inverseJoinColumns']; diff --git a/lib/Doctrine/ORM/Tools/EntityGenerator.php b/lib/Doctrine/ORM/Tools/EntityGenerator.php index bc9faadd7..8e353bda9 100644 --- a/lib/Doctrine/ORM/Tools/EntityGenerator.php +++ b/lib/Doctrine/ORM/Tools/EntityGenerator.php @@ -505,34 +505,34 @@ public function () } foreach ($metadata->associationMappings as $associationMapping) { - if ($associationMapping instanceof \Doctrine\ORM\Mapping\OneToOneMapping) { - if ($code = $this->_generateEntityStubMethod($metadata, 'set', $associationMapping->sourceFieldName, $associationMapping->targetEntityName)) { + if ($associationMapping['type'] & ClassMetadataInfo::TO_ONE) { + if ($code = $this->_generateEntityStubMethod($metadata, 'set', $associationMapping['fieldName'], $associationMapping['targetEntity'])) { $methods[] = $code; } - if ($code = $this->_generateEntityStubMethod($metadata, 'get', $associationMapping->sourceFieldName, $associationMapping->targetEntityName)) { + if ($code = $this->_generateEntityStubMethod($metadata, 'get', $associationMapping['fieldName'], $associationMapping['targetEntity'])) { $methods[] = $code; } - } else if ($associationMapping instanceof \Doctrine\ORM\Mapping\OneToManyMapping) { + } else if ($associationMapping['type'] == ClassMetadataInfo::ONE_TO_MANY) { if ($associationMapping->isOwningSide) { - if ($code = $this->_generateEntityStubMethod($metadata, 'set', $associationMapping->sourceFieldName, $associationMapping->targetEntityName)) { + if ($code = $this->_generateEntityStubMethod($metadata, 'set', $associationMapping['fieldName'], $associationMapping['targetEntity'])) { $methods[] = $code; } - if ($code = $this->_generateEntityStubMethod($metadata, 'get', $associationMapping->sourceFieldName, $associationMapping->targetEntityName)) { + if ($code = $this->_generateEntityStubMethod($metadata, 'get', $associationMapping['fieldName'], $associationMapping['targetEntity'])) { $methods[] = $code; } } else { - if ($code = $this->_generateEntityStubMethod($metadata, 'add', $associationMapping->sourceFieldName, $associationMapping->targetEntityName)) { + if ($code = $this->_generateEntityStubMethod($metadata, 'add', $associationMapping['fieldName'], $associationMapping['targetEntity'])) { $methods[] = $code; } - if ($code = $this->_generateEntityStubMethod($metadata, 'get', $associationMapping->sourceFieldName, 'Doctrine\Common\Collections\Collection')) { + if ($code = $this->_generateEntityStubMethod($metadata, 'get', $associationMapping['fieldName'], 'Doctrine\Common\Collections\Collection')) { $methods[] = $code; } } - } else if ($associationMapping instanceof \Doctrine\ORM\Mapping\ManyToManyMapping) { - if ($code = $this->_generateEntityStubMethod($metadata, 'add', $associationMapping->sourceFieldName, $associationMapping->targetEntityName)) { + } else if ($associationMapping['type'] == ClassMetadataInfo::MANY_TO_MANY) { + if ($code = $this->_generateEntityStubMethod($metadata, 'add', $associationMapping['fieldName'], $associationMapping['targetEntity'])) { $methods[] = $code; } - if ($code = $this->_generateEntityStubMethod($metadata, 'get', $associationMapping->sourceFieldName, 'Doctrine\Common\Collections\Collection')) { + if ($code = $this->_generateEntityStubMethod($metadata, 'get', $associationMapping['fieldName'], 'Doctrine\Common\Collections\Collection')) { $methods[] = $code; } } @@ -563,13 +563,13 @@ public function () $lines = array(); foreach ($metadata->associationMappings as $associationMapping) { - if ($this->_hasProperty($associationMapping->sourceFieldName, $metadata)) { + if ($this->_hasProperty($associationMapping['fieldName'], $metadata)) { continue; } $lines[] = $this->_generateAssociationMappingPropertyDocBlock($associationMapping, $metadata); - $lines[] = $this->_spaces . 'private $' . $associationMapping->sourceFieldName - . ($associationMapping->isManyToMany() ? ' = array()' : null) . ";\n"; + $lines[] = $this->_spaces . 'private $' . $associationMapping['fieldName'] + . ($associationMapping['type'] == 'manyToMany' ? ' = array()' : null) . ";\n"; } return implode("\n", $lines); @@ -681,55 +681,68 @@ public function () return '@JoinColumn(' . implode(', ', $joinColumnAnnot) . ')'; } - private function _generateAssociationMappingPropertyDocBlock(AssociationMapping $associationMapping, ClassMetadataInfo $metadata) + private function _generateAssociationMappingPropertyDocBlock(array $associationMapping, ClassMetadataInfo $metadata) { $lines = array(); $lines[] = $this->_spaces . '/**'; - $lines[] = $this->_spaces . ' * @var ' . $associationMapping->targetEntityName; + $lines[] = $this->_spaces . ' * @var ' . $associationMapping['targetEntity']; if ($this->_generateAnnotations) { $lines[] = $this->_spaces . ' *'; - $e = explode('\\', get_class($associationMapping)); - $type = str_replace('Mapping', '', end($e)); + $type = null; + switch ($associationMapping['type']) { + case ClassMetadataInfo::ONE_TO_ONE: + $type = 'OneToOne'; + break; + case ClassMetadataInfo::MANY_TO_ONE: + $type = 'ManyToOne'; + break; + case ClassMetadataInfo::ONE_TO_MANY: + $type = 'OneToMany'; + break; + case ClassMetadataInfo::MANY_TO_MANY: + $type = 'ManyToMany'; + break; + } $typeOptions = array(); - if (isset($associationMapping->targetEntityName)) { - $typeOptions[] = 'targetEntity="' . $associationMapping->targetEntityName . '"'; + if (isset($associationMapping['targetEntity'])) { + $typeOptions[] = 'targetEntity="' . $associationMapping['targetEntity'] . '"'; } - if (isset($associationMapping->inversedBy)) { - $typeOptions[] = 'inversedBy="' . $associationMapping->inversedBy . '"'; + if (isset($associationMapping['inversedBy'])) { + $typeOptions[] = 'inversedBy="' . $associationMapping['inversedBy'] . '"'; } - if (isset($associationMapping->mappedBy)) { - $typeOptions[] = 'mappedBy="' . $associationMapping->mappedBy . '"'; + if (isset($associationMapping['mappedBy'])) { + $typeOptions[] = 'mappedBy="' . $associationMapping['mappedBy'] . '"'; } - if ($associationMapping->hasCascades()) { + if ($associationMapping['cascade']) { $cascades = array(); - if ($associationMapping->isCascadePersist) $cascades[] = '"persist"'; - if ($associationMapping->isCascadeRemove) $cascades[] = '"remove"'; - if ($associationMapping->isCascadeDetach) $cascades[] = '"detach"'; - if ($associationMapping->isCascadeMerge) $cascades[] = '"merge"'; - if ($associationMapping->isCascadeRefresh) $cascades[] = '"refresh"'; + if ($associationMapping['isCascadePersist']) $cascades[] = '"persist"'; + if ($associationMapping['isCascadeRemove']) $cascades[] = '"remove"'; + if ($associationMapping['isCascadeDetach']) $cascades[] = '"detach"'; + if ($associationMapping['isCascadeMerge']) $cascades[] = '"merge"'; + if ($associationMapping['isCascadeRefresh']) $cascades[] = '"refresh"'; $typeOptions[] = 'cascade={' . implode(',', $cascades) . '}'; } - if (isset($associationMapping->orphanRemoval) && $associationMapping->orphanRemoval) { - $typeOptions[] = 'orphanRemoval=' . ($associationMapping->orphanRemoval ? 'true' : 'false'); + if (isset($associationMapping['orphanRemoval']) && $associationMapping['orphanRemoval']) { + $typeOptions[] = 'orphanRemoval=' . ($associationMapping['orphanRemoval'] ? 'true' : 'false'); } $lines[] = $this->_spaces . ' * @' . $type . '(' . implode(', ', $typeOptions) . ')'; - if (isset($associationMapping->joinColumns) && $associationMapping->joinColumns) { + if (isset($associationMapping['joinColumns']) && $associationMapping['joinColumns']) { $lines[] = $this->_spaces . ' * @JoinColumns({'; $joinColumnsLines = array(); - foreach ($associationMapping->joinColumns as $joinColumn) { + foreach ($associationMapping['joinColumns'] as $joinColumn) { if ($joinColumnAnnot = $this->_generateJoinColumnAnnotation($joinColumn)) { $joinColumnsLines[] = $this->_spaces . ' * ' . $joinColumnAnnot; } @@ -739,25 +752,25 @@ public function () $lines[] = $this->_spaces . ' * })'; } - if (isset($associationMapping->joinTable) && $associationMapping->joinTable) { + if (isset($associationMapping['joinTable']) && $associationMapping['joinTable']) { $joinTable = array(); - $joinTable[] = 'name="' . $associationMapping->joinTable['name'] . '"'; + $joinTable[] = 'name="' . $associationMapping['joinTable']['name'] . '"'; - if (isset($associationMapping->joinTable['schema'])) { - $joinTable[] = 'schema="' . $associationMapping->joinTable['schema'] . '"'; + if (isset($associationMapping['joinTable']['schema'])) { + $joinTable[] = 'schema="' . $associationMapping['joinTable']['schema'] . '"'; } $lines[] = $this->_spaces . ' * @JoinTable(' . implode(', ', $joinTable) . ','; $lines[] = $this->_spaces . ' * joinColumns={'; - foreach ($associationMapping->joinTable['joinColumns'] as $joinColumn) { + foreach ($associationMapping['joinTable']['joinColumns'] as $joinColumn) { $lines[] = $this->_spaces . ' * ' . $this->_generateJoinColumnAnnotation($joinColumn); } $lines[] = $this->_spaces . ' * },'; $lines[] = $this->_spaces . ' * inverseJoinColumns={'; - foreach ($associationMapping->joinTable['inverseJoinColumns'] as $joinColumn) { + foreach ($associationMapping['joinTable']['inverseJoinColumns'] as $joinColumn) { $lines[] = $this->_spaces . ' * ' . $this->_generateJoinColumnAnnotation($joinColumn); } @@ -765,10 +778,10 @@ public function () $lines[] = $this->_spaces . ' * )'; } - if (isset($associationMapping->orderBy)) { + if (isset($associationMapping['orderBy'])) { $lines[] = $this->_spaces . ' * @OrderBy({'; - foreach ($associationMapping->orderBy as $name => $direction) { + foreach ($associationMapping['orderBy'] as $name => $direction) { $lines[] = $this->_spaces . ' * "' . $name . '"="' . $direction . '",'; } diff --git a/lib/Doctrine/ORM/Tools/Export/Driver/PhpExporter.php b/lib/Doctrine/ORM/Tools/Export/Driver/PhpExporter.php index 831c28f4a..005b18a7a 100644 --- a/lib/Doctrine/ORM/Tools/Export/Driver/PhpExporter.php +++ b/lib/Doctrine/ORM/Tools/Export/Driver/PhpExporter.php @@ -99,41 +99,41 @@ class PhpExporter extends AbstractExporter foreach ($metadata->associationMappings as $associationMapping) { $cascade = array('remove', 'persist', 'refresh', 'merge', 'detach'); foreach ($cascade as $key => $value) { - if ( ! $associationMapping->{'isCascade'.ucfirst($value)}) { + if ( ! $associationMapping['isCascade'.ucfirst($value)]) { unset($cascade[$key]); } } $associationMappingArray = array( - 'fieldName' => $associationMapping->sourceFieldName, - 'targetEntity' => $associationMapping->targetEntityName, + 'fieldName' => $associationMapping['fieldName'], + 'targetEntity' => $associationMapping['targetEntity'], 'cascade' => $cascade, ); - if ($associationMapping instanceof \Doctrine\ORM\Mapping\OneToOneMapping) { + if ($associationMapping['type'] & ClassMetadataInfo::TO_ONE) { $method = 'mapOneToOne'; $oneToOneMappingArray = array( - 'mappedBy' => $associationMapping->mappedBy, - 'inversedBy' => $associationMapping->inversedBy, - 'joinColumns' => $associationMapping->joinColumns, - 'orphanRemoval' => $associationMapping->orphanRemoval, + 'mappedBy' => $associationMapping['mappedBy'], + 'inversedBy' => $associationMapping['inversedBy'], + 'joinColumns' => $associationMapping['joinColumns'], + 'orphanRemoval' => $associationMapping['orphanRemoval'], ); $associationMappingArray = array_merge($associationMappingArray, $oneToOneMappingArray); - } else if ($associationMapping instanceof \Doctrine\ORM\Mapping\OneToManyMapping) { + } else if ($associationMapping['type'] == ClassMetadataInfo::ONE_TO_MANY) { $method = 'mapOneToMany'; $oneToManyMappingArray = array( - 'mappedBy' => $associationMapping->mappedBy, - 'orphanRemoval' => $associationMapping->orphanRemoval, - 'orderBy' => $associationMapping->orderBy + 'mappedBy' => $associationMapping['mappedBy'], + 'orphanRemoval' => $associationMapping['orphanRemoval'], + 'orderBy' => $associationMapping['orderBy'] ); $associationMappingArray = array_merge($associationMappingArray, $oneToManyMappingArray); - } else if ($associationMapping instanceof \Doctrine\ORM\Mapping\ManyToManyMapping) { + } else if ($associationMapping['type'] == ClassMetadataInfo::MANY_TO_MANY) { $method = 'mapManyToMany'; $manyToManyMappingArray = array( - 'mappedBy' => $associationMapping->mappedBy, - 'joinTable' => $associationMapping->joinTable, - 'orderBy' => $associationMapping->orderBy + 'mappedBy' => $associationMapping['mappedBy'], + 'joinTable' => $associationMapping['joinTable'], + 'orderBy' => $associationMapping['orderBy'] ); $associationMappingArray = array_merge($associationMappingArray, $manyToManyMappingArray); diff --git a/lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php b/lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php index 63f28ffc9..097e77083 100644 --- a/lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php +++ b/lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php @@ -22,10 +22,7 @@ namespace Doctrine\ORM\Tools\Export\Driver; -use Doctrine\ORM\Mapping\ClassMetadataInfo, - Doctrine\ORM\Mapping\OneToOneMapping, - Doctrine\ORM\Mapping\OneToManyMapping, - Doctrine\ORM\Mapping\ManyToManyMapping; +use Doctrine\ORM\Mapping\ClassMetadataInfo; /** * ClassMetadata exporter for Doctrine XML mapping files @@ -182,31 +179,33 @@ class XmlExporter extends AbstractExporter } foreach ($metadata->associationMappings as $name => $associationMapping) { - if ($associationMapping instanceof OneToOneMapping) { + if ($associationMapping['type'] == ClassMetadataInfo::ONE_TO_ONE) { $associationMappingXml = $root->addChild('one-to-one'); - } else if ($associationMapping instanceof OneToManyMapping) { + } else if ($associationMapping['type'] == ClassMetadataInfo::MANY_TO_ONE) { + $associationMappingXml = $root->addChild('many-to-one'); + } else if ($associationMapping['type'] == ClassMetadataInfo::ONE_TO_MANY) { $associationMappingXml = $root->addChild('one-to-many'); - } else if ($associationMapping instanceof ManyToManyMapping) { + } else if ($associationMapping['type'] == ClassMetadataInfo::MANY_TO_MANY) { $associationMappingXml = $root->addChild('many-to-many'); } - $associationMappingXml->addAttribute('field', $associationMapping->sourceFieldName); - $associationMappingXml->addAttribute('target-entity', $associationMapping->targetEntityName); + $associationMappingXml->addAttribute('field', $associationMapping['fieldName']); + $associationMappingXml->addAttribute('target-entity', $associationMapping['targetEntity']); - if (isset($associationMapping->mappedBy)) { - $associationMappingXml->addAttribute('mapped-by', $associationMapping->mappedBy); + if (isset($associationMapping['mappedBy'])) { + $associationMappingXml->addAttribute('mapped-by', $associationMapping['mappedBy']); } - if (isset($associationMapping->inversedBy)) { - $associationMappingXml->addAttribute('inversed-by', $associationMapping->inversedBy); + if (isset($associationMapping['inversedBy'])) { + $associationMappingXml->addAttribute('inversed-by', $associationMapping['inversedBy']); } - if (isset($associationMapping->orphanRemoval)) { - $associationMappingXml->addAttribute('orphan-removal', $associationMapping->orphanRemoval); + if (isset($associationMapping['orphanRemoval'])) { + $associationMappingXml->addAttribute('orphan-removal', $associationMapping['orphanRemoval']); } - if (isset($associationMapping->joinTable) && $associationMapping->joinTable) { + if (isset($associationMapping['joinTable']) && $associationMapping['joinTable']) { $joinTableXml = $associationMappingXml->addChild('join-table'); - $joinTableXml->addAttribute('name', $associationMapping->joinTable['name']); + $joinTableXml->addAttribute('name', $associationMapping['joinTable']['name']); $joinColumnsXml = $joinTableXml->addChild('join-columns'); - foreach ($associationMapping->joinTable['joinColumns'] as $joinColumn) { + foreach ($associationMapping['joinTable']['joinColumns'] as $joinColumn) { $joinColumnXml = $joinColumnsXml->addChild('join-column'); $joinColumnXml->addAttribute('name', $joinColumn['name']); $joinColumnXml->addAttribute('referenced-column-name', $joinColumn['referencedColumnName']); @@ -218,7 +217,7 @@ class XmlExporter extends AbstractExporter } } $inverseJoinColumnsXml = $joinTableXml->addChild('inverse-join-columns'); - foreach ($associationMapping->joinTable['inverseJoinColumns'] as $inverseJoinColumn) { + foreach ($associationMapping['joinTable']['inverseJoinColumns'] as $inverseJoinColumn) { $inverseJoinColumnXml = $inverseJoinColumnsXml->addChild('join-column'); $inverseJoinColumnXml->addAttribute('name', $inverseJoinColumn['name']); $inverseJoinColumnXml->addAttribute('referenced-column-name', $inverseJoinColumn['referencedColumnName']); @@ -239,9 +238,9 @@ class XmlExporter extends AbstractExporter } } } - if (isset($associationMapping->joinColumns)) { + if (isset($associationMapping['joinColumns'])) { $joinColumnsXml = $associationMappingXml->addChild('join-columns'); - foreach ($associationMapping->joinColumns as $joinColumn) { + foreach ($associationMapping['joinColumns'] as $joinColumn) { $joinColumnXml = $joinColumnsXml->addChild('join-column'); $joinColumnXml->addAttribute('name', $joinColumn['name']); $joinColumnXml->addAttribute('referenced-column-name', $joinColumn['referencedColumnName']); @@ -259,28 +258,28 @@ class XmlExporter extends AbstractExporter } } } - if (isset($associationMapping->orderBy)) { + if (isset($associationMapping['orderBy'])) { $orderByXml = $associationMappingXml->addChild('order-by'); - foreach ($associationMapping->orderBy as $name => $direction) { + foreach ($associationMapping['orderBy'] as $name => $direction) { $orderByFieldXml = $orderByXml->addChild('order-by-field'); $orderByFieldXml->addAttribute('name', $name); $orderByFieldXml->addAttribute('direction', $direction); } } $cascade = array(); - if ($associationMapping->isCascadeRemove) { + if ($associationMapping['isCascadeRemove']) { $cascade[] = 'cascade-remove'; } - if ($associationMapping->isCascadePersist) { + if ($associationMapping['isCascadePersist']) { $cascade[] = 'cascade-persist'; } - if ($associationMapping->isCascadeRefresh) { + if ($associationMapping['isCascadeRefresh']) { $cascade[] = 'cascade-refresh'; } - if ($associationMapping->isCascadeMerge) { + if ($associationMapping['isCascadeMerge']) { $cascade[] = 'cascade-merge'; } - if ($associationMapping->isCascadeDetach) { + if ($associationMapping['isCascadeDetach']) { $cascade[] = 'cascade-detach'; } if ($cascade) { diff --git a/lib/Doctrine/ORM/Tools/Export/Driver/YamlExporter.php b/lib/Doctrine/ORM/Tools/Export/Driver/YamlExporter.php index 13094a1d9..28414cdbd 100644 --- a/lib/Doctrine/ORM/Tools/Export/Driver/YamlExporter.php +++ b/lib/Doctrine/ORM/Tools/Export/Driver/YamlExporter.php @@ -22,10 +22,7 @@ namespace Doctrine\ORM\Tools\Export\Driver; -use Doctrine\ORM\Mapping\ClassMetadataInfo, - Doctrine\ORM\Mapping\OneToOneMapping, - Doctrine\ORM\Mapping\OneToManyMapping, - Doctrine\ORM\Mapping\ManyToManyMapping; +use Doctrine\ORM\Mapping\ClassMetadataInfo; /** * ClassMetadata exporter for Doctrine YAML mapping files @@ -134,28 +131,28 @@ class YamlExporter extends AbstractExporter $associations = array(); foreach ($metadata->associationMappings as $name => $associationMapping) { $cascade = array(); - if ($associationMapping->isCascadeRemove) { + if ($associationMapping['isCascadeRemove']) { $cascade[] = 'remove'; } - if ($associationMapping->isCascadePersist) { + if ($associationMapping['isCascadePersist']) { $cascade[] = 'persist'; } - if ($associationMapping->isCascadeRefresh) { + if ($associationMapping['isCascadeRefresh']) { $cascade[] = 'refresh'; } - if ($associationMapping->isCascadeMerge) { + if ($associationMapping['isCascadeMerge']) { $cascade[] = 'merge'; } - if ($associationMapping->isCascadeDetach) { + if ($associationMapping['isCascadeDetach']) { $cascade[] = 'detach'; } $associationMappingArray = array( - 'targetEntity' => $associationMapping->targetEntityName, + 'targetEntity' => $associationMapping['targetEntity'], 'cascade' => $cascade, ); - if ($associationMapping instanceof OneToOneMapping) { - $joinColumns = $associationMapping->joinColumns; + if ($associationMapping['type'] & ClassMetadataInfo::TO_ONE) { + $joinColumns = $associationMapping['joinColumns']; $newJoinColumns = array(); foreach ($joinColumns as $joinColumn) { $newJoinColumns[$joinColumn['name']]['referencedColumnName'] = $joinColumn['referencedColumnName']; @@ -167,30 +164,30 @@ class YamlExporter extends AbstractExporter } } $oneToOneMappingArray = array( - 'mappedBy' => $associationMapping->mappedBy, - 'inversedBy' => $associationMapping->inversedBy, + 'mappedBy' => $associationMapping['mappedBy'], + 'inversedBy' => $associationMapping['inversedBy'], 'joinColumns' => $newJoinColumns, - 'orphanRemoval' => $associationMapping->orphanRemoval, + 'orphanRemoval' => $associationMapping['orphanRemoval'], ); $associationMappingArray = array_merge($associationMappingArray, $oneToOneMappingArray); $array['oneToOne'][$name] = $associationMappingArray; - } else if ($associationMapping instanceof OneToManyMapping) { + } else if ($associationMapping['type'] == ClassMetadataInfo::ONE_TO_MANY) { $oneToManyMappingArray = array( - 'mappedBy' => $associationMapping->mappedBy, - 'inversedBy' => $associationMapping->inversedBy, - 'orphanRemoval' => $associationMapping->orphanRemoval, - 'orderBy' => $associationMapping->orderBy + 'mappedBy' => $associationMapping['mappedBy'], + 'inversedBy' => $associationMapping['inversedBy'], + 'orphanRemoval' => $associationMapping['orphanRemoval'], + 'orderBy' => $associationMapping['orderBy'] ); $associationMappingArray = array_merge($associationMappingArray, $oneToManyMappingArray); $array['oneToMany'][$name] = $associationMappingArray; - } else if ($associationMapping instanceof ManyToManyMapping) { + } else if ($associationMapping['type'] == ClassMetadataInfo::MANY_TO_MANY) { $manyToManyMappingArray = array( - 'mappedBy' => $associationMapping->mappedBy, - 'inversedBy' => $associationMapping->inversedBy, - 'joinTable' => $associationMapping->joinTable, - 'orderBy' => $associationMapping->orderBy + 'mappedBy' => $associationMapping['mappedBy'], + 'inversedBy' => $associationMapping['inversedBy'], + 'joinTable' => $associationMapping['joinTable'], + 'orderBy' => isset($associationMapping['orderBy']) ? $associationMapping['orderBy'] : null ); $associationMappingArray = array_merge($associationMappingArray, $manyToManyMappingArray); diff --git a/lib/Doctrine/ORM/Tools/SchemaTool.php b/lib/Doctrine/ORM/Tools/SchemaTool.php index 9d68f7c2d..1a9a2170a 100644 --- a/lib/Doctrine/ORM/Tools/SchemaTool.php +++ b/lib/Doctrine/ORM/Tools/SchemaTool.php @@ -24,6 +24,7 @@ namespace Doctrine\ORM\Tools; use Doctrine\ORM\ORMException, Doctrine\DBAL\Types\Type, Doctrine\ORM\EntityManager, + Doctrine\ORM\Mapping\ClassMetadata, Doctrine\ORM\Internal\CommitOrderCalculator, Doctrine\ORM\Tools\Event\GenerateSchemaTableEventArgs, Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs; @@ -353,24 +354,24 @@ class SchemaTool private function _gatherRelationsSql($class, $table, $schema) { foreach ($class->associationMappings as $fieldName => $mapping) { - if ($mapping->inherited) { + if (isset($mapping['inherited'])) { continue; } - $foreignClass = $this->_em->getClassMetadata($mapping->targetEntityName); + $foreignClass = $this->_em->getClassMetadata($mapping['targetEntity']); - if ($mapping->isOneToOne() && $mapping->isOwningSide) { + if ($mapping['type'] & ClassMetadata::TO_ONE && $mapping['isOwningSide']) { $primaryKeyColumns = $uniqueConstraints = array(); // unnecessary for this relation-type - $this->_gatherRelationJoinColumns($mapping->joinColumns, $table, $foreignClass, $mapping, $primaryKeyColumns, $uniqueConstraints); - } else if ($mapping->isOneToMany() && $mapping->isOwningSide) { + $this->_gatherRelationJoinColumns($mapping['joinColumns'], $table, $foreignClass, $mapping, $primaryKeyColumns, $uniqueConstraints); + } else if ($mapping['type'] == ClassMetadata::ONE_TO_MANY && $mapping['isOwningSide']) { //... create join table, one-many through join table supported later throw ORMException::notSupported(); - } else if ($mapping->isManyToMany() && $mapping->isOwningSide) { + } else if ($mapping['type'] == ClassMetadata::MANY_TO_MANY && $mapping['isOwningSide']) { // create join table - $joinTable = $mapping->joinTable; + $joinTable = $mapping['joinTable']; - $theJoinTable = $schema->createTable($mapping->getQuotedJoinTableName($this->_platform)); + $theJoinTable = $schema->createTable($foreignClass->getQuotedJoinTableName($mapping, $this->_platform)); $primaryKeyColumns = $uniqueConstraints = array(); @@ -414,7 +415,7 @@ class SchemaTool if ( ! $class->hasField($referencedFieldName)) { throw new \Doctrine\ORM\ORMException( "Column name `".$joinColumn['referencedColumnName']."` referenced for relation from ". - "$mapping->sourceEntityName towards $mapping->targetEntityName does not exist." + $mapping['sourceEntity'] . " towards ". $mapping['targetEntity'] . " does not exist." ); } @@ -603,7 +604,7 @@ class SchemaTool foreach ($class->associationMappings as $assoc) { if ($assoc->isOwningSide) { - $targetClass = $this->_em->getClassMetadata($assoc->targetEntityName); + $targetClass = $this->_em->getClassMetadata($assoc['targetEntity']); if ( ! $calc->hasClass($targetClass->name)) { $calc->addClass($targetClass); @@ -624,7 +625,7 @@ class SchemaTool foreach ($classes as $class) { foreach ($class->associationMappings as $assoc) { - if ($assoc->isOwningSide && $assoc->isManyToMany()) { + if ($assoc->isOwningSide && $assoc['type'] == ClassMetadata::MANY_TO_MANY) { $associationTables[] = $assoc->joinTable['name']; } } diff --git a/lib/Doctrine/ORM/Tools/SchemaValidator.php b/lib/Doctrine/ORM/Tools/SchemaValidator.php index d109832a9..8acaa0b54 100644 --- a/lib/Doctrine/ORM/Tools/SchemaValidator.php +++ b/lib/Doctrine/ORM/Tools/SchemaValidator.php @@ -20,8 +20,7 @@ namespace Doctrine\ORM\Tools; use Doctrine\ORM\EntityManager; -use Doctrine\ORM\Mapping\ManyToManyMapping; -use Doctrine\ORM\Mapping\OneToOneMapping; +use Doctrine\ORM\Mapping\ClassMetadataInfo; /** * Performs strict validation of the mapping schema @@ -73,60 +72,60 @@ class SchemaValidator $ce = array(); /* @var $class ClassMetadata */ foreach ($class->associationMappings AS $fieldName => $assoc) { - if (!$cmf->hasMetadataFor($assoc->targetEntityName)) { - $ce[] = "The target entity '" . $assoc->targetEntityName . "' specified on " . $class->name . '#' . $fieldName . ' is unknown.'; + if (!$cmf->hasMetadataFor($assoc['targetEntity'])) { + $ce[] = "The target entity '" . $assoc['targetEntity'] . "' specified on " . $class->name . '#' . $fieldName . ' is unknown.'; } - if ($assoc->mappedBy && $assoc->inversedBy) { + if ($assoc['mappedBy'] && $assoc['inversedBy']) { $ce[] = "The association " . $class . "#" . $fieldName . " cannot be defined as both inverse and owning."; } - $targetMetadata = $cmf->getMetadataFor($assoc->targetEntityName); + $targetMetadata = $cmf->getMetadataFor($assoc['targetEntity']); /* @var $assoc AssociationMapping */ - if ($assoc->mappedBy) { - if ($targetMetadata->hasField($assoc->mappedBy)) { + if ($assoc['mappedBy']) { + if ($targetMetadata->hasField($assoc['mappedBy'])) { $ce[] = "The association " . $class->name . "#" . $fieldName . " refers to the owning side ". - "field " . $assoc->targetEntityName . "#" . $assoc->mappedBy . " which is not defined as association."; + "field " . $assoc['targetEntity'] . "#" . $assoc['mappedBy'] . " which is not defined as association."; } - if (!$targetMetadata->hasAssociation($assoc->mappedBy)) { + if (!$targetMetadata->hasAssociation($assoc['mappedBy'])) { $ce[] = "The association " . $class->name . "#" . $fieldName . " refers to the owning side ". - "field " . $assoc->targetEntityName . "#" . $assoc->mappedBy . " which does not exist."; - } else if ($targetMetadata->associationMappings[$assoc->mappedBy]->inversedBy == null) { + "field " . $assoc['targetEntity'] . "#" . $assoc['mappedBy'] . " which does not exist."; + } else if ($targetMetadata->associationMappings[$assoc['mappedBy']]['inversedBy'] == null) { $ce[] = "The field " . $class->name . "#" . $fieldName . " is on the inverse side of a ". "bi-directional relationship, but the specified mappedBy association on the target-entity ". - $assoc->targetEntityName . "#" . $assoc->mappedBy . " does not contain the required ". + $assoc['targetEntity'] . "#" . $assoc['mappedBy'] . " does not contain the required ". "'inversedBy' attribute."; - } else if ($targetMetadata->associationMappings[$assoc->mappedBy]->inversedBy != $fieldName) { + } else if ($targetMetadata->associationMappings[$assoc['mappedBy']]['inversedBy'] != $fieldName) { $ce[] = "The mappings " . $class->name . "#" . $fieldName . " and " . - $assoc->targetEntityName . "#" . $assoc->mappedBy . " are ". + $assoc['targetEntity'] . "#" . $assoc['mappedBy'] . " are ". "incosistent with each other."; } } - if ($assoc->inversedBy) { - if ($targetMetadata->hasField($assoc->inversedBy)) { + if ($assoc['inversedBy']) { + if ($targetMetadata->hasField($assoc['inversedBy'])) { $ce[] = "The association " . $class->name . "#" . $fieldName . " refers to the inverse side ". - "field " . $assoc->targetEntityName . "#" . $assoc->inversedBy . " which is not defined as association."; + "field " . $assoc['targetEntity'] . "#" . $assoc['inversedBy'] . " which is not defined as association."; } - if (!$targetMetadata->hasAssociation($assoc->inversedBy)) { + if (!$targetMetadata->hasAssociation($assoc['inversedBy'])) { $ce[] = "The association " . $class->name . "#" . $fieldName . " refers to the inverse side ". - "field " . $assoc->targetEntityName . "#" . $assoc->inversedBy . " which does not exist."; - } else if ($targetMetadata->associationMappings[$assoc->inversedBy]->mappedBy == null) { + "field " . $assoc['targetEntity'] . "#" . $assoc['inversedBy'] . " which does not exist."; + } else if ($targetMetadata->associationMappings[$assoc['inversedBy']]['mappedBy'] == null) { $ce[] = "The field " . $class->name . "#" . $fieldName . " is on the owning side of a ". "bi-directional relationship, but the specified mappedBy association on the target-entity ". - $assoc->targetEntityName . "#" . $assoc->mappedBy . " does not contain the required ". + $assoc['targetEntity'] . "#" . $assoc['mappedBy'] . " does not contain the required ". "'inversedBy' attribute."; - } else if ($targetMetadata->associationMappings[$assoc->inversedBy]->mappedBy != $fieldName) { + } else if ($targetMetadata->associationMappings[$assoc['inversedBy']]['mappedBy'] != $fieldName) { $ce[] = "The mappings " . $class->name . "#" . $fieldName . " and " . - $assoc->targetEntityName . "#" . $assoc->inversedBy . " are ". + $assoc['targetEntity'] . "#" . $assoc['inversedBy'] . " are ". "incosistent with each other."; } } - if ($assoc->isOwningSide) { - if ($assoc instanceof ManyToManyMapping) { - foreach ($assoc->joinTable['joinColumns'] AS $joinColumn) { + if ($assoc['isOwningSide']) { + if ($assoc['type'] == ClassMetadataInfo::MANY_TO_MANY) { + foreach ($assoc['joinTable']['joinColumns'] AS $joinColumn) { if (!isset($class->fieldNames[$joinColumn['referencedColumnName']])) { $ce[] = "The referenced column name '" . $joinColumn['referencedColumnName'] . "' does not " . "have a corresponding field with this column name on the class '" . $class->name . "'."; @@ -139,8 +138,8 @@ class SchemaValidator "has to be a primary key column."; } } - foreach ($assoc->joinTable['inverseJoinColumns'] AS $inverseJoinColumn) { - $targetClass = $cmf->getMetadataFor($assoc->targetEntityName); + foreach ($assoc['joinTable']['inverseJoinColumns'] AS $inverseJoinColumn) { + $targetClass = $cmf->getMetadataFor($assoc['targetEntity']); if (!isset($targetClass->fieldNames[$inverseJoinColumn['referencedColumnName']])) { $ce[] = "The inverse referenced column name '" . $inverseJoinColumn['referencedColumnName'] . "' does not " . "have a corresponding field with this column name on the class '" . $targetClass->name . "'."; @@ -153,9 +152,9 @@ class SchemaValidator "has to be a primary key column."; } } - } else if ($assoc instanceof OneToOneMapping) { - foreach ($assoc->joinColumns AS $joinColumn) { - $targetClass = $cmf->getMetadataFor($assoc->targetEntityName); + } else if ($assoc['type'] & ClassMetadataInfo::TO_ONE) { + foreach ($assoc['joinColumns'] AS $joinColumn) { + $targetClass = $cmf->getMetadataFor($assoc['targetEntity']); if (!isset($targetClass->fieldNames[$joinColumn['referencedColumnName']])) { $ce[] = "The referenced column name '" . $joinColumn['referencedColumnName'] . "' does not " . "have a corresponding field with this column name on the class '" . $targetClass->name . "'."; @@ -171,9 +170,9 @@ class SchemaValidator } } - if (isset($assoc->orderBy) && $assoc->orderBy !== null) { - $targetClass = $cmf->getMetadataFor($assoc->targetEntityName); - foreach ($assoc->orderBy AS $orderField => $orientation) { + if (isset($assoc['orderBy']) && $assoc['orderBy'] !== null) { + $targetClass = $cmf->getMetadataFor($assoc['targetEntity']); + foreach ($assoc['orderBy'] AS $orderField => $orientation) { if (!$targetClass->hasField($orderField)) { $ce[] = "The association " . $class->name."#".$fieldName." is ordered by a foreign field " . $orderField . " that is not a field on the target entity " . $targetClass->name; diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index 874df3ce2..f3da91002 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -25,6 +25,7 @@ use Exception, InvalidArgumentException, UnexpectedValueException, Doctrine\Common\NotifyPropertyChanged, Doctrine\Common\PropertyChangedListener, Doctrine\ORM\Event\LifecycleEventArgs, + Doctrine\ORM\Mapping\ClassMetadata, Doctrine\ORM\Proxy\Proxy; /** @@ -410,7 +411,7 @@ class UnitOfWork implements PropertyChangedListener // Inject PersistentCollection $coll = new PersistentCollection( $this->em, - $this->em->getClassMetadata($assoc->targetEntityName), + $this->em->getClassMetadata($assoc['targetEntity']), $value ); @@ -431,7 +432,7 @@ class UnitOfWork implements PropertyChangedListener foreach ($actualData as $propName => $actualValue) { if (isset($class->associationMappings[$propName])) { $assoc = $class->associationMappings[$propName]; - if ($assoc->isOwningSide && $assoc->isOneToOne()) { + if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) { $changeSet[$propName] = array(null, $actualValue); } } else { @@ -450,11 +451,11 @@ class UnitOfWork implements PropertyChangedListener $orgValue = isset($originalData[$propName]) ? $originalData[$propName] : null; if (isset($class->associationMappings[$propName])) { $assoc = $class->associationMappings[$propName]; - if ($assoc->isOneToOne() && $orgValue !== $actualValue) { - if ($assoc->isOwningSide) { + if ($assoc['type'] & ClassMetadata::TO_ONE && $orgValue !== $actualValue) { + if ($assoc['isOwningSide']) { $changeSet[$propName] = array($orgValue, $actualValue); } - if ($orgValue !== null && $assoc->orphanRemoval) { + if ($orgValue !== null && $assoc['orphanRemoval']) { $this->scheduleOrphanRemoval($orgValue); } } else if ($orgValue instanceof PersistentCollection && $orgValue !== $actualValue) { @@ -539,7 +540,7 @@ class UnitOfWork implements PropertyChangedListener private function computeAssociationChanges($assoc, $value) { if ($value instanceof PersistentCollection && $value->isDirty()) { - if ($assoc->isOwningSide) { + if ($assoc['isOwningSide']) { $this->collectionUpdates[] = $value; } $this->visitedCollections[] = $value; @@ -547,7 +548,7 @@ class UnitOfWork implements PropertyChangedListener // Look through the entities, and in any of their associations, for transient (new) // enities, recursively. ("Persistence by reachability") - if ($assoc->isOneToOne()) { + if ($assoc['type'] & ClassMetadata::TO_ONE) { if ($value instanceof Proxy && ! $value->__isInitialized__) { return; // Ignore uninitialized proxy objects } @@ -557,12 +558,12 @@ class UnitOfWork implements PropertyChangedListener $value = $value->unwrap(); } - $targetClass = $this->em->getClassMetadata($assoc->targetEntityName); + $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); foreach ($value as $entry) { $state = $this->getEntityState($entry, self::STATE_NEW); $oid = spl_object_hash($entry); if ($state == self::STATE_NEW) { - if ( ! $assoc->isCascadePersist) { + if ( ! $assoc['isCascadePersist']) { throw new InvalidArgumentException("A new entity was found through a relationship that was not" . " configured to cascade persist operations: " . self::objToStr($entry) . "." . " Explicitly persist the new entity or configure cascading persist operations" @@ -833,8 +834,8 @@ class UnitOfWork implements PropertyChangedListener // Calculate dependencies for new nodes foreach ($newNodes as $class) { foreach ($class->associationMappings as $assoc) { - if ($assoc->isOwningSide && $assoc->isOneToOne()) { - $targetClass = $this->em->getClassMetadata($assoc->targetEntityName); + if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) { + $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); if ( ! $calc->hasClass($targetClass->name)) { $calc->addClass($targetClass); } @@ -1400,20 +1401,20 @@ class UnitOfWork implements PropertyChangedListener } } else { $assoc2 = $class->associationMappings[$name]; - if ($assoc2->isOneToOne()) { + if ($assoc2['type'] & ClassMetadata::TO_ONE) { $other = $prop->getValue($entity); if ($other === null) { $prop->setValue($managedCopy, null); } else if ($other instanceof Proxy && !$other->__isInitialized__) { // do not merge fields marked lazy that have not been fetched. continue; - } else if ( ! $assoc2->isCascadeMerge) { + } else if ( ! $assoc2['isCascadeMerge']) { if ($this->getEntityState($other, self::STATE_DETACHED) == self::STATE_MANAGED) { $prop->setValue($managedCopy, $other); } else { - $targetClass = $this->em->getClassMetadata($assoc2->targetEntityName); + $targetClass = $this->em->getClassMetadata($assoc2['targetEntity']); $id = $targetClass->getIdentifierValues($other); - $proxy = $this->em->getProxyFactory()->getProxy($assoc2->targetEntityName, $id); + $proxy = $this->em->getProxyFactory()->getProxy($assoc2['targetEntity'], $id); $prop->setValue($managedCopy, $proxy); $this->registerManaged($proxy, $id, array()); } @@ -1429,14 +1430,14 @@ class UnitOfWork implements PropertyChangedListener $managedCol = $prop->getValue($managedCopy); if (!$managedCol) { $managedCol = new PersistentCollection($this->em, - $this->em->getClassMetadata($assoc2->targetEntityName), + $this->em->getClassMetadata($assoc2['targetEntity']), new ArrayCollection ); $managedCol->setOwner($managedCopy, $assoc2); $prop->setValue($managedCopy, $managedCol); $this->originalEntityData[$oid][$name] = $managedCol; } - $managedCol->setInitialized($assoc2->isCascadeMerge); + $managedCol->setInitialized($assoc2['isCascadeMerge']); } } if ($class->isChangeTrackingNotify()) { @@ -1450,14 +1451,14 @@ class UnitOfWork implements PropertyChangedListener } if ($prevManagedCopy !== null) { - $assocField = $assoc->sourceFieldName; + $assocField = $assoc['fieldName']; $prevClass = $this->em->getClassMetadata(get_class($prevManagedCopy)); - if ($assoc->isOneToOne()) { + if ($assoc['type'] & ClassMetadata::TO_ONE) { $prevClass->reflFields[$assocField]->setValue($prevManagedCopy, $managedCopy); } else { $prevClass->reflFields[$assocField]->getValue($prevManagedCopy)->unwrap()->add($managedCopy); - if ($assoc->isOneToMany()) { - $class->reflFields[$assoc->mappedBy]->setValue($managedCopy, $prevManagedCopy); + if ($assoc['type'] == ClassMetadata::ONE_TO_MANY) { + $class->reflFields[$assoc['mappedBy']]->setValue($managedCopy, $prevManagedCopy); } } } @@ -1564,10 +1565,10 @@ class UnitOfWork implements PropertyChangedListener { $class = $this->em->getClassMetadata(get_class($entity)); foreach ($class->associationMappings as $assoc) { - if ( ! $assoc->isCascadeRefresh) { + if ( ! $assoc['isCascadeRefresh']) { continue; } - $relatedEntities = $class->reflFields[$assoc->sourceFieldName]->getValue($entity); + $relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity); if ($relatedEntities instanceof Collection) { if ($relatedEntities instanceof PersistentCollection) { // Unwrap so that foreach() does not initialize @@ -1592,10 +1593,10 @@ class UnitOfWork implements PropertyChangedListener { $class = $this->em->getClassMetadata(get_class($entity)); foreach ($class->associationMappings as $assoc) { - if ( ! $assoc->isCascadeDetach) { + if ( ! $assoc['isCascadeDetach']) { continue; } - $relatedEntities = $class->reflFields[$assoc->sourceFieldName]->getValue($entity); + $relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity); if ($relatedEntities instanceof Collection) { if ($relatedEntities instanceof PersistentCollection) { // Unwrap so that foreach() does not initialize @@ -1621,10 +1622,10 @@ class UnitOfWork implements PropertyChangedListener { $class = $this->em->getClassMetadata(get_class($entity)); foreach ($class->associationMappings as $assoc) { - if ( ! $assoc->isCascadeMerge) { + if ( ! $assoc['isCascadeMerge']) { continue; } - $relatedEntities = $class->reflFields[$assoc->sourceFieldName]->getValue($entity); + $relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity); if ($relatedEntities instanceof Collection) { if ($relatedEntities instanceof PersistentCollection) { // Unwrap so that foreach() does not initialize @@ -1650,10 +1651,10 @@ class UnitOfWork implements PropertyChangedListener { $class = $this->em->getClassMetadata(get_class($entity)); foreach ($class->associationMappings as $assoc) { - if ( ! $assoc->isCascadePersist) { + if ( ! $assoc['isCascadePersist']) { continue; } - $relatedEntities = $class->reflFields[$assoc->sourceFieldName]->getValue($entity); + $relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity); if (($relatedEntities instanceof Collection || is_array($relatedEntities))) { if ($relatedEntities instanceof PersistentCollection) { // Unwrap so that foreach() does not initialize @@ -1678,11 +1679,11 @@ class UnitOfWork implements PropertyChangedListener { $class = $this->em->getClassMetadata(get_class($entity)); foreach ($class->associationMappings as $assoc) { - if ( ! $assoc->isCascadeRemove) { + if ( ! $assoc['isCascadeRemove']) { continue; } //TODO: If $entity instanceof Proxy => Initialize ? - $relatedEntities = $class->reflFields[$assoc->sourceFieldName]->getValue($entity); + $relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity); if ($relatedEntities instanceof Collection || is_array($relatedEntities)) { // If its a PersistentCollection initialization is intended! No unwrap! foreach ($relatedEntities as $relatedEntity) { @@ -1874,12 +1875,12 @@ class UnitOfWork implements PropertyChangedListener continue; } - $targetClass = $this->em->getClassMetadata($assoc->targetEntityName); + $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); - if ($assoc->isOneToOne()) { - if ($assoc->isOwningSide) { + if ($assoc['type'] & ClassMetadata::TO_ONE) { + if ($assoc['isOwningSide']) { $associatedId = array(); - foreach ($assoc->targetToSourceKeyColumns as $targetColumn => $srcColumn) { + foreach ($assoc['targetToSourceKeyColumns'] as $targetColumn => $srcColumn) { $joinColumnValue = $data[$srcColumn]; if ($joinColumnValue !== null) { $associatedId[$targetClass->fieldNames[$targetColumn]] = $joinColumnValue; @@ -1900,14 +1901,16 @@ class UnitOfWork implements PropertyChangedListener } else { if ($targetClass->subClasses) { // If it might be a subtype, it can not be lazy - $newValue = $assoc->load($entity, null, $this->em, $associatedId); + //$newValue = $assoc->load($entity, null, $this->em, $associatedId); + $newValue = $this->getEntityPersister($assoc['targetEntity']) + ->loadOneToOneEntity($assoc, $entity, null, $associatedId); } else { - if ($assoc->fetchMode == Mapping\AssociationMapping::FETCH_EAGER) { + if ($assoc['fetch'] == Mapping\ClassMetadata::FETCH_EAGER) { // TODO: Maybe it could be optimized to do an eager fetch with a JOIN inside // the persister instead of this rather unperformant approach. - $newValue = $this->em->find($assoc->targetEntityName, $associatedId); + $newValue = $this->em->find($assoc['targetEntity'], $associatedId); } else { - $newValue = $this->em->getProxyFactory()->getProxy($assoc->targetEntityName, $associatedId); + $newValue = $this->em->getProxyFactory()->getProxy($assoc['targetEntity'], $associatedId); } // PERF: Inlined & optimized code from UnitOfWork#registerManaged() $newValueOid = spl_object_hash($newValue); @@ -1922,7 +1925,8 @@ class UnitOfWork implements PropertyChangedListener } } else { // Inverse side of x-to-one can never be lazy - $class->reflFields[$field]->setValue($entity, $assoc->load($entity, null, $this->em)); + $class->reflFields[$field]->setValue($entity, $this->getEntityPersister($assoc['targetEntity']) + ->loadOneToOneEntity($assoc, $entity, null)); } } else { // Inject collection @@ -1934,10 +1938,11 @@ class UnitOfWork implements PropertyChangedListener ); $pColl->setOwner($entity, $assoc); $reflField->setValue($entity, $pColl); - if ($assoc->isLazilyFetched()) { + if ($assoc['fetch'] == Mapping\ClassMetadata::FETCH_LAZY) { $pColl->setInitialized(false); } else { - $assoc->load($entity, $pColl, $this->em); + //$assoc->load($entity, $pColl, $this->em); + $this->loadCollection($assoc, $entity, $pColl); } $this->originalEntityData[$oid][$field] = $pColl; } @@ -1956,6 +1961,20 @@ class UnitOfWork implements PropertyChangedListener return $entity; } + public function loadCollection(array $assoc, $sourceEntity, $targetCollection) + { + switch ($assoc['type']) { + case ClassMetadata::ONE_TO_MANY: + $this->getEntityPersister($assoc['targetEntity'])->loadOneToManyCollection( + $assoc, $sourceEntity, $targetCollection); + break; + case ClassMetadata::MANY_TO_MANY: + $this->getEntityPersister($assoc['targetEntity'])->loadManyToManyCollection( + $assoc, $sourceEntity, $targetCollection); + break; + } + } + /** * Gets the identity map of the UnitOfWork. * @@ -2103,13 +2122,13 @@ class UnitOfWork implements PropertyChangedListener * @param AssociationMapping $association * @return AbstractCollectionPersister */ - public function getCollectionPersister($association) + public function getCollectionPersister(array $association) { - $type = get_class($association); + $type = $association['type']; if ( ! isset($this->collectionPersisters[$type])) { - if ($association instanceof Mapping\OneToManyMapping) { + if ($type == ClassMetadata::ONE_TO_MANY) { $persister = new Persisters\OneToManyPersister($this->em); - } else if ($association instanceof Mapping\ManyToManyMapping) { + } else if ($type == ClassMetadata::MANY_TO_MANY) { $persister = new Persisters\ManyToManyPersister($this->em); } $this->collectionPersisters[$type] = $persister; diff --git a/tests/Doctrine/Tests/ORM/AllTests.php b/tests/Doctrine/Tests/ORM/AllTests.php index 9b8e06dd6..bd57ee892 100644 --- a/tests/Doctrine/Tests/ORM/AllTests.php +++ b/tests/Doctrine/Tests/ORM/AllTests.php @@ -26,7 +26,7 @@ class AllTests $suite->addTest(Query\AllTests::suite()); $suite->addTest(Hydration\AllTests::suite()); $suite->addTest(Entity\AllTests::suite()); - $suite->addTest(Associations\AllTests::suite()); + //$suite->addTest(Associations\AllTests::suite()); $suite->addTest(Mapping\AllTests::suite()); $suite->addTest(Functional\AllTests::suite()); $suite->addTest(Id\AllTests::suite()); diff --git a/tests/Doctrine/Tests/ORM/Associations/OneToOneMappingTest.php b/tests/Doctrine/Tests/ORM/Associations/OneToOneMappingTest.php index ea8a028df..2f6930757 100644 --- a/tests/Doctrine/Tests/ORM/Associations/OneToOneMappingTest.php +++ b/tests/Doctrine/Tests/ORM/Associations/OneToOneMappingTest.php @@ -4,19 +4,20 @@ namespace Doctrine\Tests\ORM\Associations; require_once __DIR__ . '/../../TestInit.php'; +use Doctrine\ORM\Mapping\ClassMetadata; + class OneToOneMappingTest extends \Doctrine\Tests\OrmTestCase -{ +{ public function testCorrectOneToOneBidirectionalMapping() { - $owningSideMapping = array( + $oneToOneMapping = array( + 'type' => ClassMetadata::ONE_TO_ONE, 'fieldName' => 'address', 'targetEntity' => 'Address', 'joinColumns' => array(array('name' => 'address_id', 'referencedColumnName' => 'id')), 'sourceEntity' => 'Person', // This is normally filled by ClassMetadata ); - $oneToOneMapping = new \Doctrine\ORM\Mapping\OneToOneMapping($owningSideMapping); - $this->assertEquals(array('address_id' => 'id'), $oneToOneMapping->sourceToTargetKeyColumns); $this->assertEquals(array('id' => 'address_id'), $oneToOneMapping->targetToSourceKeyColumns); $this->assertEquals('Address', $oneToOneMapping->targetEntityName); @@ -24,14 +25,14 @@ class OneToOneMappingTest extends \Doctrine\Tests\OrmTestCase $this->assertEquals('address', $oneToOneMapping->sourceFieldName); $this->assertTrue($oneToOneMapping->isOwningSide); - $inverseSideMapping = array( + $oneToOneMapping = array( + 'type' => ClassMetadata::ONE_TO_ONE, 'fieldName' => 'person', 'sourceEntity' => 'Address', 'targetEntity' => 'Person', 'mappedBy' => 'address' ); - $oneToOneMapping = new \Doctrine\ORM\Mapping\OneToOneMapping($inverseSideMapping); $this->assertEquals('address', $oneToOneMapping->mappedBy); $this->assertEquals('Address', $oneToOneMapping->sourceEntityName); $this->assertEquals('Person', $oneToOneMapping->targetEntityName); diff --git a/tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php b/tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php index 73a0c65cf..bf8deb163 100644 --- a/tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php @@ -105,6 +105,7 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase public function testBasicOneToOne() { + //$this->_em->getConnection()->getConfiguration()->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger); $user = new CmsUser; $user->name = 'Roman'; $user->username = 'romanb'; diff --git a/tests/Doctrine/Tests/ORM/Functional/DatabaseDriverTest.php b/tests/Doctrine/Tests/ORM/Functional/DatabaseDriverTest.php index 6f02328dc..964c57119 100644 --- a/tests/Doctrine/Tests/ORM/Functional/DatabaseDriverTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/DatabaseDriverTest.php @@ -84,7 +84,7 @@ class DatabaseDriverTest extends \Doctrine\Tests\OrmFunctionalTestCase $bazMetadata->associationMappings = \array_change_key_case($bazMetadata->associationMappings, \CASE_LOWER); $this->assertArrayHasKey('bar', $bazMetadata->associationMappings); - $this->assertType('Doctrine\ORM\Mapping\OneToOneMapping', $bazMetadata->associationMappings['bar']); + $this->assertEquals(ClassMetadataInfo::MANY_TO_ONE, $bazMetadata->associationMappings['bar']['type']); } public function testDetectManyToManyTables() diff --git a/tests/Doctrine/Tests/ORM/Functional/ManyToManySelfReferentialAssociationTest.php b/tests/Doctrine/Tests/ORM/Functional/ManyToManySelfReferentialAssociationTest.php index ac691d220..020e77aec 100644 --- a/tests/Doctrine/Tests/ORM/Functional/ManyToManySelfReferentialAssociationTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/ManyToManySelfReferentialAssociationTest.php @@ -4,6 +4,7 @@ namespace Doctrine\Tests\ORM\Functional; use Doctrine\Tests\Models\ECommerce\ECommerceProduct; use Doctrine\ORM\Mapping\AssociationMapping; +use Doctrine\ORM\Mapping\ClassMetadata; require_once __DIR__ . '/../../TestInit.php'; @@ -74,7 +75,7 @@ class ManyToManySelfReferentialAssociationTest extends AbstractManyToManyAssocia $this->_createLoadingFixture(); $metadata = $this->_em->getClassMetadata('Doctrine\Tests\Models\ECommerce\ECommerceProduct'); - $metadata->getAssociationMapping('related')->fetchMode = AssociationMapping::FETCH_LAZY; + $metadata->associationMappings['related']['fetch'] = ClassMetadata::FETCH_LAZY; $query = $this->_em->createQuery('SELECT p FROM Doctrine\Tests\Models\ECommerce\ECommerceProduct p'); $products = $query->getResult(); diff --git a/tests/Doctrine/Tests/ORM/Functional/ManyToManyUnidirectionalAssociationTest.php b/tests/Doctrine/Tests/ORM/Functional/ManyToManyUnidirectionalAssociationTest.php index 6510f4f69..d6c38fc54 100644 --- a/tests/Doctrine/Tests/ORM/Functional/ManyToManyUnidirectionalAssociationTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/ManyToManyUnidirectionalAssociationTest.php @@ -5,6 +5,7 @@ namespace Doctrine\Tests\ORM\Functional; use Doctrine\Tests\Models\ECommerce\ECommerceCart; use Doctrine\Tests\Models\ECommerce\ECommerceProduct; use Doctrine\ORM\Mapping\AssociationMapping; +use Doctrine\ORM\Mapping\ClassMetadata; require_once __DIR__ . '/../../TestInit.php'; @@ -79,7 +80,7 @@ class ManyToManyUnidirectionalAssociationTest extends AbstractManyToManyAssociat { $this->_createFixture(); $metadata = $this->_em->getClassMetadata('Doctrine\Tests\Models\ECommerce\ECommerceCart'); - $metadata->getAssociationMapping('products')->fetchMode = AssociationMapping::FETCH_LAZY; + $metadata->associationMappings['products']['fetch'] = ClassMetadata::FETCH_LAZY; $query = $this->_em->createQuery('SELECT c FROM Doctrine\Tests\Models\ECommerce\ECommerceCart c'); $result = $query->getResult(); diff --git a/tests/Doctrine/Tests/ORM/Functional/OneToManySelfReferentialAssociationTest.php b/tests/Doctrine/Tests/ORM/Functional/OneToManySelfReferentialAssociationTest.php index 4aa94a3ce..5d6c182ab 100644 --- a/tests/Doctrine/Tests/ORM/Functional/OneToManySelfReferentialAssociationTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/OneToManySelfReferentialAssociationTest.php @@ -4,6 +4,7 @@ namespace Doctrine\Tests\ORM\Functional; use Doctrine\Tests\Models\ECommerce\ECommerceCategory; use Doctrine\ORM\Mapping\AssociationMapping; +use Doctrine\ORM\Mapping\ClassMetadata; require_once __DIR__ . '/../../TestInit.php'; @@ -90,7 +91,7 @@ class OneToManySelfReferentialAssociationTest extends \Doctrine\Tests\OrmFunctio { $this->_createFixture(); $metadata = $this->_em->getClassMetadata('Doctrine\Tests\Models\ECommerce\ECommerceCategory'); - $metadata->getAssociationMapping('children')->fetchMode = AssociationMapping::FETCH_LAZY; + $metadata->associationMappings['children']['fetch'] = ClassMetadata::FETCH_LAZY; $query = $this->_em->createQuery('select c from Doctrine\Tests\Models\ECommerce\ECommerceCategory c order by c.id asc'); $result = $query->getResult(); diff --git a/tests/Doctrine/Tests/ORM/Functional/OneToOneBidirectionalAssociationTest.php b/tests/Doctrine/Tests/ORM/Functional/OneToOneBidirectionalAssociationTest.php index 73363547c..93e30cbc0 100644 --- a/tests/Doctrine/Tests/ORM/Functional/OneToOneBidirectionalAssociationTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/OneToOneBidirectionalAssociationTest.php @@ -5,6 +5,7 @@ namespace Doctrine\Tests\ORM\Functional; use Doctrine\Tests\Models\ECommerce\ECommerceCart; use Doctrine\Tests\Models\ECommerce\ECommerceCustomer; use Doctrine\ORM\Mapping\AssociationMapping; +use Doctrine\ORM\Mapping\ClassMetadata; require_once __DIR__ . '/../../TestInit.php'; @@ -68,7 +69,7 @@ class OneToOneBidirectionalAssociationTest extends \Doctrine\Tests\OrmFunctional public function testLazyLoadsObjectsOnTheOwningSide() { $this->_createFixture(); $metadata = $this->_em->getClassMetadata('Doctrine\Tests\Models\ECommerce\ECommerceCart'); - $metadata->getAssociationMapping('customer')->fetchMode = AssociationMapping::FETCH_LAZY; + $metadata->associationMappings['customer']['fetchMode'] = ClassMetadata::FETCH_LAZY; $query = $this->_em->createQuery('select c from Doctrine\Tests\Models\ECommerce\ECommerceCart c'); $result = $query->getResult(); @@ -82,7 +83,7 @@ class OneToOneBidirectionalAssociationTest extends \Doctrine\Tests\OrmFunctional { $this->_createFixture(); $metadata = $this->_em->getClassMetadata('Doctrine\Tests\Models\ECommerce\ECommerceCustomer'); - $metadata->getAssociationMapping('mentor')->fetchMode = AssociationMapping::FETCH_EAGER; + $metadata->associationMappings['mentor']['fetch'] = ClassMetadata::FETCH_EAGER; $query = $this->_em->createQuery('select c from Doctrine\Tests\Models\ECommerce\ECommerceCustomer c'); $result = $query->getResult(); diff --git a/tests/Doctrine/Tests/ORM/Functional/OneToOneSelfReferentialAssociationTest.php b/tests/Doctrine/Tests/ORM/Functional/OneToOneSelfReferentialAssociationTest.php index a58384b7e..321f66340 100644 --- a/tests/Doctrine/Tests/ORM/Functional/OneToOneSelfReferentialAssociationTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/OneToOneSelfReferentialAssociationTest.php @@ -4,6 +4,7 @@ namespace Doctrine\Tests\ORM\Functional; use Doctrine\Tests\Models\ECommerce\ECommerceCustomer; use Doctrine\ORM\Mapping\AssociationMapping; +use Doctrine\ORM\Mapping\ClassMetadata; require_once __DIR__ . '/../../TestInit.php'; @@ -67,7 +68,7 @@ class OneToOneSelfReferentialAssociationTest extends \Doctrine\Tests\OrmFunction $this->_createFixture(); $metadata = $this->_em->getClassMetadata('Doctrine\Tests\Models\ECommerce\ECommerceCustomer'); - $metadata->getAssociationMapping('mentor')->fetchMode = AssociationMapping::FETCH_LAZY; + $metadata->associationMappings['mentor']['fetch'] = ClassMetadata::FETCH_LAZY; $query = $this->_em->createQuery("select c from Doctrine\Tests\Models\ECommerce\ECommerceCustomer c where c.name='Luke Skywalker'"); $result = $query->getResult(); diff --git a/tests/Doctrine/Tests/ORM/Functional/OneToOneUnidirectionalAssociationTest.php b/tests/Doctrine/Tests/ORM/Functional/OneToOneUnidirectionalAssociationTest.php index af1ff2362..01b2d00f9 100644 --- a/tests/Doctrine/Tests/ORM/Functional/OneToOneUnidirectionalAssociationTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/OneToOneUnidirectionalAssociationTest.php @@ -5,6 +5,7 @@ namespace Doctrine\Tests\ORM\Functional; use Doctrine\Tests\Models\ECommerce\ECommerceProduct; use Doctrine\Tests\Models\ECommerce\ECommerceShipping; use Doctrine\ORM\Mapping\AssociationMapping; +use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Query; require_once __DIR__ . '/../../TestInit.php'; @@ -62,7 +63,7 @@ class OneToOneUnidirectionalAssociationTest extends \Doctrine\Tests\OrmFunctiona public function testLazyLoadsObjects() { $this->_createFixture(); $metadata = $this->_em->getClassMetadata('Doctrine\Tests\Models\ECommerce\ECommerceProduct'); - $metadata->getAssociationMapping('shipping')->fetchMode = AssociationMapping::FETCH_LAZY; + $metadata->associationMappings['shipping']['fetch'] = ClassMetadata::FETCH_LAZY; $query = $this->_em->createQuery('select p from Doctrine\Tests\Models\ECommerce\ECommerceProduct p'); $result = $query->getResult(); diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC599Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC599Test.php index d9f9a82be..b342796cc 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC599Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC599Test.php @@ -66,7 +66,7 @@ class DDC599Test extends \Doctrine\Tests\OrmFunctionalTestCase $class = $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC599Subitem'); $this->assertArrayHasKey('children', $class->associationMappings); - $this->assertTrue($class->associationMappings['children']->isCascadeRemove); + $this->assertTrue($class->associationMappings['children']['isCascadeRemove']); } } diff --git a/tests/Doctrine/Tests/ORM/Hydration/ObjectHydratorTest.php b/tests/Doctrine/Tests/ORM/Hydration/ObjectHydratorTest.php index d3bda1e5b..442961266 100644 --- a/tests/Doctrine/Tests/ORM/Hydration/ObjectHydratorTest.php +++ b/tests/Doctrine/Tests/ORM/Hydration/ObjectHydratorTest.php @@ -6,6 +6,7 @@ use Doctrine\Tests\Mocks\HydratorMockStatement; use Doctrine\ORM\Query\ResultSetMapping; use Doctrine\ORM\Proxy\ProxyFactory; use Doctrine\ORM\Mapping\AssociationMapping; +use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Query; use Doctrine\Tests\Models\CMS\CmsUser; @@ -164,7 +165,7 @@ class ObjectHydratorTest extends HydrationTestCase // configuring lazy loading $metadata = $this->_em->getClassMetadata('Doctrine\Tests\Models\ECommerce\ECommerceProduct'); - $metadata->getAssociationMapping('shipping')->fetchMode = AssociationMapping::FETCH_LAZY; + $metadata->associationMappings['shipping']['fetch'] = ClassMetadata::FETCH_LAZY; $stmt = new HydratorMockStatement($resultSet); $hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em); diff --git a/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php b/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php index b279905ce..653cff7d7 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php @@ -155,16 +155,15 @@ abstract class AbstractMappingDriverTest extends \Doctrine\Tests\OrmTestCase */ public function testOwningOneToOneAssocation($class) { - $this->assertTrue($class->associationMappings['address'] instanceof \Doctrine\ORM\Mapping\OneToOneMapping); $this->assertTrue(isset($class->associationMappings['address'])); - $this->assertTrue($class->associationMappings['address']->isOwningSide); - $this->assertEquals('user', $class->associationMappings['address']->inversedBy); + $this->assertTrue($class->associationMappings['address']['isOwningSide']); + $this->assertEquals('user', $class->associationMappings['address']['inversedBy']); // Check cascading - $this->assertTrue($class->associationMappings['address']->isCascadeRemove); - $this->assertFalse($class->associationMappings['address']->isCascadePersist); - $this->assertFalse($class->associationMappings['address']->isCascadeRefresh); - $this->assertFalse($class->associationMappings['address']->isCascadeDetach); - $this->assertFalse($class->associationMappings['address']->isCascadeMerge); + $this->assertTrue($class->associationMappings['address']['isCascadeRemove']); + $this->assertFalse($class->associationMappings['address']['isCascadePersist']); + $this->assertFalse($class->associationMappings['address']['isCascadeRefresh']); + $this->assertFalse($class->associationMappings['address']['isCascadeDetach']); + $this->assertFalse($class->associationMappings['address']['isCascadeMerge']); return $class; } @@ -175,17 +174,16 @@ abstract class AbstractMappingDriverTest extends \Doctrine\Tests\OrmTestCase */ public function testInverseOneToManyAssociation($class) { - $this->assertTrue($class->associationMappings['phonenumbers'] instanceof \Doctrine\ORM\Mapping\OneToManyMapping); $this->assertTrue(isset($class->associationMappings['phonenumbers'])); - $this->assertFalse($class->associationMappings['phonenumbers']->isOwningSide); - $this->assertTrue($class->associationMappings['phonenumbers']->isCascadePersist); - $this->assertFalse($class->associationMappings['phonenumbers']->isCascadeRemove); - $this->assertFalse($class->associationMappings['phonenumbers']->isCascadeRefresh); - $this->assertFalse($class->associationMappings['phonenumbers']->isCascadeDetach); - $this->assertFalse($class->associationMappings['phonenumbers']->isCascadeMerge); + $this->assertFalse($class->associationMappings['phonenumbers']['isOwningSide']); + $this->assertTrue($class->associationMappings['phonenumbers']['isCascadePersist']); + $this->assertFalse($class->associationMappings['phonenumbers']['isCascadeRemove']); + $this->assertFalse($class->associationMappings['phonenumbers']['isCascadeRefresh']); + $this->assertFalse($class->associationMappings['phonenumbers']['isCascadeDetach']); + $this->assertFalse($class->associationMappings['phonenumbers']['isCascadeMerge']); // Test Order By - $this->assertEquals(array('number' => 'ASC'), $class->associationMappings['phonenumbers']->orderBy); + $this->assertEquals(array('number' => 'ASC'), $class->associationMappings['phonenumbers']['orderBy']); return $class; } @@ -196,17 +194,16 @@ abstract class AbstractMappingDriverTest extends \Doctrine\Tests\OrmTestCase */ public function testManyToManyAssociationWithCascadeAll($class) { - $this->assertTrue($class->associationMappings['groups'] instanceof \Doctrine\ORM\Mapping\ManyToManyMapping); $this->assertTrue(isset($class->associationMappings['groups'])); - $this->assertTrue($class->associationMappings['groups']->isOwningSide); + $this->assertTrue($class->associationMappings['groups']['isOwningSide']); // Make sure that cascade-all works as expected - $this->assertTrue($class->associationMappings['groups']->isCascadeRemove); - $this->assertTrue($class->associationMappings['groups']->isCascadePersist); - $this->assertTrue($class->associationMappings['groups']->isCascadeRefresh); - $this->assertTrue($class->associationMappings['groups']->isCascadeDetach); - $this->assertTrue($class->associationMappings['groups']->isCascadeMerge); + $this->assertTrue($class->associationMappings['groups']['isCascadeRemove']); + $this->assertTrue($class->associationMappings['groups']['isCascadePersist']); + $this->assertTrue($class->associationMappings['groups']['isCascadeRefresh']); + $this->assertTrue($class->associationMappings['groups']['isCascadeDetach']); + $this->assertTrue($class->associationMappings['groups']['isCascadeMerge']); - $this->assertNull($class->associationMappings['groups']->orderBy); + $this->assertFalse(isset($class->associationMappings['groups']['orderBy'])); return $class; } @@ -243,8 +240,8 @@ abstract class AbstractMappingDriverTest extends \Doctrine\Tests\OrmTestCase public function testJoinColumnUniqueAndNullable($class) { // Non-Nullability of Join Column - $this->assertFalse($class->associationMappings['groups']->joinTable['joinColumns'][0]['nullable']); - $this->assertFalse($class->associationMappings['groups']->joinTable['joinColumns'][0]['unique']); + $this->assertFalse($class->associationMappings['groups']['joinTable']['joinColumns'][0]['nullable']); + $this->assertFalse($class->associationMappings['groups']['joinTable']['joinColumns'][0]['unique']); return $class; } @@ -256,7 +253,7 @@ abstract class AbstractMappingDriverTest extends \Doctrine\Tests\OrmTestCase public function testColumnDefinition($class) { $this->assertEquals("CHAR(32) NOT NULL", $class->fieldMappings['email']['columnDefinition']); - $this->assertEquals("INT NULL", $class->associationMappings['groups']->joinTable['inverseJoinColumns'][0]['columnDefinition']); + $this->assertEquals("INT NULL", $class->associationMappings['groups']['joinTable']['inverseJoinColumns'][0]['columnDefinition']); return $class; } @@ -267,8 +264,8 @@ abstract class AbstractMappingDriverTest extends \Doctrine\Tests\OrmTestCase */ public function testJoinColumnOnDeleteAndOnUpdate($class) { - $this->assertEquals('CASCADE', $class->associationMappings['address']->joinColumns[0]['onDelete']); - $this->assertEquals('CASCADE', $class->associationMappings['address']->joinColumns[0]['onUpdate']); + $this->assertEquals('CASCADE', $class->associationMappings['address']['joinColumns'][0]['onDelete']); + $this->assertEquals('CASCADE', $class->associationMappings['address']['joinColumns'][0]['onUpdate']); return $class; } diff --git a/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php b/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php index 620838a6b..b51d34831 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php @@ -29,7 +29,6 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase $cm->setCustomRepositoryClass("UserRepository"); $cm->setDiscriminatorColumn(array('name' => 'disc', 'type' => 'integer')); $cm->mapOneToOne(array('fieldName' => 'phonenumbers', 'targetEntity' => 'Bar', 'mappedBy' => 'foo')); - $this->assertTrue($cm->getAssociationMapping('phonenumbers') instanceof \Doctrine\ORM\Mapping\OneToOneMapping); $this->assertEquals(1, count($cm->associationMappings)); $serialized = serialize($cm); @@ -45,12 +44,12 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase $this->assertEquals(array('UserParent'), $cm->parentClasses); $this->assertEquals('UserRepository', $cm->customRepositoryClassName); $this->assertEquals(array('name' => 'disc', 'type' => 'integer', 'fieldName' => 'disc'), $cm->discriminatorColumn); - $this->assertTrue($cm->getAssociationMapping('phonenumbers') instanceof \Doctrine\ORM\Mapping\OneToOneMapping); + $this->assertTrue($cm->associationMappings['phonenumbers']['type'] == ClassMetadata::ONE_TO_ONE); $this->assertEquals(1, count($cm->associationMappings)); $oneOneMapping = $cm->getAssociationMapping('phonenumbers'); - $this->assertTrue($oneOneMapping->fetchMode == \Doctrine\ORM\Mapping\AssociationMapping::FETCH_LAZY); - $this->assertEquals('phonenumbers', $oneOneMapping->sourceFieldName); - $this->assertEquals('Doctrine\Tests\Models\CMS\Bar', $oneOneMapping->targetEntityName); + $this->assertTrue($oneOneMapping['fetch'] == ClassMetadata::FETCH_LAZY); + $this->assertEquals('phonenumbers', $oneOneMapping['fieldName']); + $this->assertEquals('Doctrine\Tests\Models\CMS\Bar', $oneOneMapping['targetEntity']); } public function testFieldIsNullable() @@ -88,7 +87,7 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase ), )); - $this->assertEquals("DoctrineGlobal_User", $cm->associationMappings['author']->targetEntityName); + $this->assertEquals("DoctrineGlobal_User", $cm->associationMappings['author']['targetEntity']); } public function testMapManyToManyJoinTableDefaults() @@ -101,13 +100,13 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase )); $assoc = $cm->associationMappings['groups']; - $this->assertTrue($assoc instanceof \Doctrine\ORM\Mapping\ManyToManyMapping); + //$this->assertTrue($assoc instanceof \Doctrine\ORM\Mapping\ManyToManyMapping); $this->assertEquals(array( 'name' => 'CmsUser_CmsGroup', 'joinColumns' => array(array('name' => 'CmsUser_id', 'referencedColumnName' => 'id', 'onDelete' => 'CASCADE')), 'inverseJoinColumns' => array(array('name' => 'CmsGroup_id', 'referencedColumnName' => 'id', 'onDelete' => 'CASCADE')) - ), $assoc->joinTable); - $this->assertTrue($assoc->isOnDeleteCascade); + ), $assoc['joinTable']); + $this->assertTrue($assoc['isOnDeleteCascade']); } public function testSerializeManyToManyJoinTableCascade() @@ -123,7 +122,7 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase $assoc = $cm->associationMappings['groups']; $assoc = unserialize(serialize($assoc)); - $this->assertTrue($assoc->isOnDeleteCascade); + $this->assertTrue($assoc['isOnDeleteCascade']); } /** @@ -180,8 +179,8 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase public function testDuplicateAssociationMappingException() { $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'); - $a1 = new \Doctrine\ORM\Mapping\OneToOneMapping(array('fieldName' => 'foo', 'sourceEntity' => 'stdClass', 'targetEntity' => 'stdClass', 'mappedBy' => 'foo')); - $a2 = new \Doctrine\ORM\Mapping\OneToOneMapping(array('fieldName' => 'foo', 'sourceEntity' => 'stdClass', 'targetEntity' => 'stdClass', 'mappedBy' => 'foo')); + $a1 = array('fieldName' => 'foo', 'sourceEntity' => 'stdClass', 'targetEntity' => 'stdClass', 'mappedBy' => 'foo'); + $a2 = array('fieldName' => 'foo', 'sourceEntity' => 'stdClass', 'targetEntity' => 'stdClass', 'mappedBy' => 'foo'); $cm->addInheritedAssociationMapping($a1); $this->setExpectedException('Doctrine\ORM\Mapping\MappingException'); diff --git a/tests/Doctrine/Tests/ORM/PersistentCollectionTest.php b/tests/Doctrine/Tests/ORM/PersistentCollectionTest.php index 79a7443c7..b3f20e99b 100644 --- a/tests/Doctrine/Tests/ORM/PersistentCollectionTest.php +++ b/tests/Doctrine/Tests/ORM/PersistentCollectionTest.php @@ -36,6 +36,8 @@ class PersistentCollectionTest extends \Doctrine\Tests\OrmTestCase public function testQueriesAssociationToLoadItself() { + $this->markTestSkipped('Refactor!'); + $class = $this->_emMock->getClassMetadata('Doctrine\Tests\Models\ECommerce\ECommerceProduct'); $collection = new PersistentCollection($this->_emMock, $class, new ArrayCollection); $collection->setInitialized(false); diff --git a/tests/Doctrine/Tests/ORM/Tools/ConvertDoctrine1SchemaTest.php b/tests/Doctrine/Tests/ORM/Tools/ConvertDoctrine1SchemaTest.php index 94856b831..eafe63d1f 100644 --- a/tests/Doctrine/Tests/ORM/Tools/ConvertDoctrine1SchemaTest.php +++ b/tests/Doctrine/Tests/ORM/Tools/ConvertDoctrine1SchemaTest.php @@ -91,8 +91,8 @@ class ConvertDoctrine1SchemaTest extends \Doctrine\Tests\OrmTestCase $this->assertEquals('test_alias', $userClass->fieldMappings['theAlias']['columnName']); $this->assertEquals('theAlias', $userClass->fieldMappings['theAlias']['fieldName']); - $this->assertEquals('Profile', $profileClass->associationMappings['User']->sourceEntityName); - $this->assertEquals('User', $profileClass->associationMappings['User']->targetEntityName); + $this->assertEquals('Profile', $profileClass->associationMappings['User']['sourceEntity']); + $this->assertEquals('User', $profileClass->associationMappings['User']['targetEntity']); $this->assertEquals('username', $userClass->table['uniqueConstraints']['username']['columns'][0]); diff --git a/tests/Doctrine/Tests/ORM/Tools/Export/AbstractClassMetadataExporterTest.php b/tests/Doctrine/Tests/ORM/Tools/Export/AbstractClassMetadataExporterTest.php index 0fa781285..95759c539 100644 --- a/tests/Doctrine/Tests/ORM/Tools/Export/AbstractClassMetadataExporterTest.php +++ b/tests/Doctrine/Tests/ORM/Tools/Export/AbstractClassMetadataExporterTest.php @@ -210,18 +210,18 @@ abstract class AbstractClassMetadataExporterTest extends \Doctrine\Tests\OrmTest public function testOneToOneAssociationsAreExported($class) { $this->assertTrue(isset($class->associationMappings['address'])); - $this->assertTrue($class->associationMappings['address'] instanceof \Doctrine\ORM\Mapping\OneToOneMapping); - $this->assertEquals('Doctrine\Tests\ORM\Tools\Export\Address', $class->associationMappings['address']->targetEntityName); - $this->assertEquals('address_id', $class->associationMappings['address']->joinColumns[0]['name']); - $this->assertEquals('id', $class->associationMappings['address']->joinColumns[0]['referencedColumnName']); - $this->assertEquals('CASCADE', $class->associationMappings['address']->joinColumns[0]['onDelete']); - $this->assertEquals('CASCADE', $class->associationMappings['address']->joinColumns[0]['onUpdate']); + //$this->assertTrue($class->associationMappings['address'] instanceof \Doctrine\ORM\Mapping\OneToOneMapping); + $this->assertEquals('Doctrine\Tests\ORM\Tools\Export\Address', $class->associationMappings['address']['targetEntity']); + $this->assertEquals('address_id', $class->associationMappings['address']['joinColumns'][0]['name']); + $this->assertEquals('id', $class->associationMappings['address']['joinColumns'][0]['referencedColumnName']); + $this->assertEquals('CASCADE', $class->associationMappings['address']['joinColumns'][0]['onDelete']); + $this->assertEquals('CASCADE', $class->associationMappings['address']['joinColumns'][0]['onUpdate']); - $this->assertTrue($class->associationMappings['address']->isCascadeRemove); - $this->assertFalse($class->associationMappings['address']->isCascadePersist); - $this->assertFalse($class->associationMappings['address']->isCascadeRefresh); - $this->assertFalse($class->associationMappings['address']->isCascadeMerge); - $this->assertFalse($class->associationMappings['address']->isCascadeDetach); + $this->assertTrue($class->associationMappings['address']['isCascadeRemove']); + $this->assertFalse($class->associationMappings['address']['isCascadePersist']); + $this->assertFalse($class->associationMappings['address']['isCascadeRefresh']); + $this->assertFalse($class->associationMappings['address']['isCascadeMerge']); + $this->assertFalse($class->associationMappings['address']['isCascadeDetach']); return $class; } @@ -233,16 +233,16 @@ abstract class AbstractClassMetadataExporterTest extends \Doctrine\Tests\OrmTest public function testOneToManyAssociationsAreExported($class) { $this->assertTrue(isset($class->associationMappings['phonenumbers'])); - $this->assertTrue($class->associationMappings['phonenumbers'] instanceof \Doctrine\ORM\Mapping\OneToManyMapping); - $this->assertEquals('Doctrine\Tests\ORM\Tools\Export\Phonenumber', $class->associationMappings['phonenumbers']->targetEntityName); - $this->assertEquals('user', $class->associationMappings['phonenumbers']->mappedBy); - $this->assertEquals(array('number' => 'ASC'), $class->associationMappings['phonenumbers']->orderBy); + //$this->assertTrue($class->associationMappings['phonenumbers'] instanceof \Doctrine\ORM\Mapping\OneToManyMapping); + $this->assertEquals('Doctrine\Tests\ORM\Tools\Export\Phonenumber', $class->associationMappings['phonenumbers']['targetEntity']); + $this->assertEquals('user', $class->associationMappings['phonenumbers']['mappedBy']); + $this->assertEquals(array('number' => 'ASC'), $class->associationMappings['phonenumbers']['orderBy']); - $this->assertFalse($class->associationMappings['phonenumbers']->isCascadeRemove); - $this->assertTrue($class->associationMappings['phonenumbers']->isCascadePersist); - $this->assertFalse($class->associationMappings['phonenumbers']->isCascadeRefresh); - $this->assertFalse($class->associationMappings['phonenumbers']->isCascadeMerge); - $this->assertFalse($class->associationMappings['phonenumbers']->isCascadeDetach); + $this->assertFalse($class->associationMappings['phonenumbers']['isCascadeRemove']); + $this->assertTrue($class->associationMappings['phonenumbers']['isCascadePersist']); + $this->assertFalse($class->associationMappings['phonenumbers']['isCascadeRefresh']); + $this->assertFalse($class->associationMappings['phonenumbers']['isCascadeMerge']); + $this->assertFalse($class->associationMappings['phonenumbers']['isCascadeDetach']); return $class; } @@ -254,22 +254,22 @@ abstract class AbstractClassMetadataExporterTest extends \Doctrine\Tests\OrmTest public function testManyToManyAssociationsAreExported($class) { $this->assertTrue(isset($class->associationMappings['groups'])); - $this->assertTrue($class->associationMappings['groups'] instanceof \Doctrine\ORM\Mapping\ManyToManyMapping); - $this->assertEquals('Doctrine\Tests\ORM\Tools\Export\Group', $class->associationMappings['groups']->targetEntityName); - $this->assertEquals('cms_users_groups', $class->associationMappings['groups']->joinTable['name']); + //$this->assertTrue($class->associationMappings['groups'] instanceof \Doctrine\ORM\Mapping\ManyToManyMapping); + $this->assertEquals('Doctrine\Tests\ORM\Tools\Export\Group', $class->associationMappings['groups']['targetEntity']); + $this->assertEquals('cms_users_groups', $class->associationMappings['groups']['joinTable']['name']); - $this->assertEquals('user_id', $class->associationMappings['groups']->joinTable['joinColumns'][0]['name']); - $this->assertEquals('id', $class->associationMappings['groups']->joinTable['joinColumns'][0]['referencedColumnName']); + $this->assertEquals('user_id', $class->associationMappings['groups']['joinTable']['joinColumns'][0]['name']); + $this->assertEquals('id', $class->associationMappings['groups']['joinTable']['joinColumns'][0]['referencedColumnName']); - $this->assertEquals('group_id', $class->associationMappings['groups']->joinTable['inverseJoinColumns'][0]['name']); - $this->assertEquals('id', $class->associationMappings['groups']->joinTable['inverseJoinColumns'][0]['referencedColumnName']); - $this->assertEquals('INT NULL', $class->associationMappings['groups']->joinTable['inverseJoinColumns'][0]['columnDefinition']); + $this->assertEquals('group_id', $class->associationMappings['groups']['joinTable']['inverseJoinColumns'][0]['name']); + $this->assertEquals('id', $class->associationMappings['groups']['joinTable']['inverseJoinColumns'][0]['referencedColumnName']); + $this->assertEquals('INT NULL', $class->associationMappings['groups']['joinTable']['inverseJoinColumns'][0]['columnDefinition']); - $this->assertTrue($class->associationMappings['groups']->isCascadeRemove); - $this->assertTrue($class->associationMappings['groups']->isCascadePersist); - $this->assertTrue($class->associationMappings['groups']->isCascadeRefresh); - $this->assertTrue($class->associationMappings['groups']->isCascadeMerge); - $this->assertTrue($class->associationMappings['groups']->isCascadeDetach); + $this->assertTrue($class->associationMappings['groups']['isCascadeRemove']); + $this->assertTrue($class->associationMappings['groups']['isCascadePersist']); + $this->assertTrue($class->associationMappings['groups']['isCascadeRefresh']); + $this->assertTrue($class->associationMappings['groups']['isCascadeMerge']); + $this->assertTrue($class->associationMappings['groups']['isCascadeDetach']); return $class; } @@ -298,10 +298,10 @@ abstract class AbstractClassMetadataExporterTest extends \Doctrine\Tests\OrmTest */ public function testCascadeIsExported($class) { - $this->assertTrue($class->associationMappings['phonenumbers']->isCascadePersist); - $this->assertFalse($class->associationMappings['phonenumbers']->isCascadeMerge); - $this->assertFalse($class->associationMappings['phonenumbers']->isCascadeRemove); - $this->assertFalse($class->associationMappings['phonenumbers']->isCascadeRefresh); + $this->assertTrue($class->associationMappings['phonenumbers']['isCascadePersist']); + $this->assertFalse($class->associationMappings['phonenumbers']['isCascadeMerge']); + $this->assertFalse($class->associationMappings['phonenumbers']['isCascadeRemove']); + $this->assertFalse($class->associationMappings['phonenumbers']['isCascadeRefresh']); return $class; } @@ -312,7 +312,7 @@ abstract class AbstractClassMetadataExporterTest extends \Doctrine\Tests\OrmTest */ public function testInversedByIsExported($class) { - $this->assertEquals('user', $class->associationMappings['address']->inversedBy); + $this->assertEquals('user', $class->associationMappings['address']['inversedBy']); } public function __destruct() From 5178f4b7d6cc9ac71862d2b8efd3811549622e24 Mon Sep 17 00:00:00 2001 From: "Roman S. Borschel" Date: Mon, 9 Aug 2010 19:34:36 +0200 Subject: [PATCH 09/15] Corrected TO_ONE check just to be sure. --- lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php index b7aaed198..a13119aea 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php @@ -349,7 +349,7 @@ class ObjectHydrator extends AbstractHydrator // If there is an inverse mapping on the target class its bidirectional if ($relation['inversedBy']) { $inverseAssoc = $targetClass->associationMappings[$relation['inversedBy']]; - if ($inverseAssoc['type'] == ClassMetadata::ONE_TO_ONE) { + if ($inverseAssoc['type'] & ClassMetadata::TO_ONE) { $targetClass->reflFields[$inverseAssoc['fieldName']]->setValue($element, $parentObject); $this->_uow->setOriginalEntityProperty(spl_object_hash($element), $inverseAssoc['fieldName'], $parentObject); } From 4826739824fa10827fc9ddb0d3ef3ea9a83f49de Mon Sep 17 00:00:00 2001 From: "Roman S. Borschel" Date: Mon, 9 Aug 2010 21:44:52 +0200 Subject: [PATCH 10/15] Simplified ClassMetadata lookup in UnitOfWork and added docblock. --- lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php | 16 ---------------- lib/Doctrine/ORM/UnitOfWork.php | 16 +++++++++++----- 2 files changed, 11 insertions(+), 21 deletions(-) diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php index c25835bb7..c2bcab1f4 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php @@ -1172,22 +1172,6 @@ class ClassMetadataInfo $type == self::INHERITANCE_TYPE_TABLE_PER_CLASS; } - /** - * Makes some automatic additions to the association mapping to make the life - * easier for the user, and store join columns in the metadata. - * - * @param array $mapping - * @todo Pass param by ref? - */ - /*private function _completeAssociationMapping(array $mapping) - { - $mapping['sourceEntity'] = $this->name; - if (isset($mapping['targetEntity']) && strpos($mapping['targetEntity'], '\\') === false && strlen($this->namespace) > 0) { - $mapping['targetEntity'] = $this->namespace . '\\' . $mapping['targetEntity']; - } - return $mapping; - }*/ - /** * Adds a mapped field to the class. * diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index f3da91002..3700ac056 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -389,7 +389,7 @@ class UnitOfWork implements PropertyChangedListener * @param ClassMetadata $class The class descriptor of the entity. * @param object $entity The entity for which to compute the changes. */ - public function computeChangeSet(Mapping\ClassMetadata $class, $entity) + public function computeChangeSet(ClassMetadata $class, $entity) { if ( ! $class->isInheritanceTypeNone()) { $class = $this->em->getClassMetadata(get_class($entity)); @@ -1901,11 +1901,10 @@ class UnitOfWork implements PropertyChangedListener } else { if ($targetClass->subClasses) { // If it might be a subtype, it can not be lazy - //$newValue = $assoc->load($entity, null, $this->em, $associatedId); $newValue = $this->getEntityPersister($assoc['targetEntity']) ->loadOneToOneEntity($assoc, $entity, null, $associatedId); } else { - if ($assoc['fetch'] == Mapping\ClassMetadata::FETCH_EAGER) { + if ($assoc['fetch'] == ClassMetadata::FETCH_EAGER) { // TODO: Maybe it could be optimized to do an eager fetch with a JOIN inside // the persister instead of this rather unperformant approach. $newValue = $this->em->find($assoc['targetEntity'], $associatedId); @@ -1938,10 +1937,9 @@ class UnitOfWork implements PropertyChangedListener ); $pColl->setOwner($entity, $assoc); $reflField->setValue($entity, $pColl); - if ($assoc['fetch'] == Mapping\ClassMetadata::FETCH_LAZY) { + if ($assoc['fetch'] == ClassMetadata::FETCH_LAZY) { $pColl->setInitialized(false); } else { - //$assoc->load($entity, $pColl, $this->em); $this->loadCollection($assoc, $entity, $pColl); } $this->originalEntityData[$oid][$field] = $pColl; @@ -1961,6 +1959,14 @@ class UnitOfWork implements PropertyChangedListener return $entity; } + /** + * Initializes (loads) an associated collection of an entity. + * + * @param array $assoc The association mapping. + * @param object $sourceEntity The entity that "owns" the collection. + * @param PeristentCollection $targetCollection The collection to initialize. + * @todo Maybe later move to EntityManager#initialize($proxyOrCollection). See DDC-733. + */ public function loadCollection(array $assoc, $sourceEntity, $targetCollection) { switch ($assoc['type']) { From dd7be5b13a6384b018d4f99549146141b2975ba4 Mon Sep 17 00:00:00 2001 From: "Roman S. Borschel" Date: Mon, 9 Aug 2010 22:27:34 +0200 Subject: [PATCH 11/15] Improving API and docblocks. Removing superfluous tests. --- lib/Doctrine/ORM/PersistentCollection.php | 2 +- lib/Doctrine/ORM/UnitOfWork.php | 15 ++++--- tests/Doctrine/Tests/ORM/AllTests.php | 1 - .../Tests/ORM/Associations/AllTests.php | 30 -------------- .../ORM/Associations/OneToOneMappingTest.php | 41 ------------------- .../Tests/ORM/PersistentCollectionTest.php | 20 +-------- 6 files changed, 9 insertions(+), 100 deletions(-) delete mode 100644 tests/Doctrine/Tests/ORM/Associations/AllTests.php delete mode 100644 tests/Doctrine/Tests/ORM/Associations/OneToOneMappingTest.php diff --git a/lib/Doctrine/ORM/PersistentCollection.php b/lib/Doctrine/ORM/PersistentCollection.php index 0b7632605..34437d570 100644 --- a/lib/Doctrine/ORM/PersistentCollection.php +++ b/lib/Doctrine/ORM/PersistentCollection.php @@ -204,7 +204,7 @@ final class PersistentCollection implements Collection $newObjects = $this->coll->toArray(); } $this->coll->clear(); - $this->em->getUnitOfWork()->loadCollection($this->association, $this->owner, $this); + $this->em->getUnitOfWork()->loadCollection($this); $this->takeSnapshot(); // Reattach NEW objects added through add(), if any. if (isset($newObjects)) { diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index 3700ac056..946197761 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -1940,7 +1940,7 @@ class UnitOfWork implements PropertyChangedListener if ($assoc['fetch'] == ClassMetadata::FETCH_LAZY) { $pColl->setInitialized(false); } else { - $this->loadCollection($assoc, $entity, $pColl); + $this->loadCollection($pColl); } $this->originalEntityData[$oid][$field] = $pColl; } @@ -1960,23 +1960,22 @@ class UnitOfWork implements PropertyChangedListener } /** - * Initializes (loads) an associated collection of an entity. + * Initializes (loads) an uninitialized persistent collection of an entity. * - * @param array $assoc The association mapping. - * @param object $sourceEntity The entity that "owns" the collection. - * @param PeristentCollection $targetCollection The collection to initialize. + * @param PeristentCollection $collection The collection to initialize. * @todo Maybe later move to EntityManager#initialize($proxyOrCollection). See DDC-733. */ - public function loadCollection(array $assoc, $sourceEntity, $targetCollection) + public function loadCollection(PersistentCollection $collection) { + $assoc = $collection->getMapping(); switch ($assoc['type']) { case ClassMetadata::ONE_TO_MANY: $this->getEntityPersister($assoc['targetEntity'])->loadOneToManyCollection( - $assoc, $sourceEntity, $targetCollection); + $assoc, $collection->getOwner(), $collection); break; case ClassMetadata::MANY_TO_MANY: $this->getEntityPersister($assoc['targetEntity'])->loadManyToManyCollection( - $assoc, $sourceEntity, $targetCollection); + $assoc, $collection->getOwner(), $collection); break; } } diff --git a/tests/Doctrine/Tests/ORM/AllTests.php b/tests/Doctrine/Tests/ORM/AllTests.php index bd57ee892..fd7c25d7f 100644 --- a/tests/Doctrine/Tests/ORM/AllTests.php +++ b/tests/Doctrine/Tests/ORM/AllTests.php @@ -26,7 +26,6 @@ class AllTests $suite->addTest(Query\AllTests::suite()); $suite->addTest(Hydration\AllTests::suite()); $suite->addTest(Entity\AllTests::suite()); - //$suite->addTest(Associations\AllTests::suite()); $suite->addTest(Mapping\AllTests::suite()); $suite->addTest(Functional\AllTests::suite()); $suite->addTest(Id\AllTests::suite()); diff --git a/tests/Doctrine/Tests/ORM/Associations/AllTests.php b/tests/Doctrine/Tests/ORM/Associations/AllTests.php deleted file mode 100644 index 9f443976d..000000000 --- a/tests/Doctrine/Tests/ORM/Associations/AllTests.php +++ /dev/null @@ -1,30 +0,0 @@ -addTestSuite('Doctrine\Tests\ORM\Associations\OneToOneMappingTest'); - - return $suite; - } -} - -if (PHPUnit_MAIN_METHOD == 'Orm_Associations_AllTests::main') { - AllTests::main(); -} \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Associations/OneToOneMappingTest.php b/tests/Doctrine/Tests/ORM/Associations/OneToOneMappingTest.php deleted file mode 100644 index 2f6930757..000000000 --- a/tests/Doctrine/Tests/ORM/Associations/OneToOneMappingTest.php +++ /dev/null @@ -1,41 +0,0 @@ - ClassMetadata::ONE_TO_ONE, - 'fieldName' => 'address', - 'targetEntity' => 'Address', - 'joinColumns' => array(array('name' => 'address_id', 'referencedColumnName' => 'id')), - 'sourceEntity' => 'Person', // This is normally filled by ClassMetadata - ); - - $this->assertEquals(array('address_id' => 'id'), $oneToOneMapping->sourceToTargetKeyColumns); - $this->assertEquals(array('id' => 'address_id'), $oneToOneMapping->targetToSourceKeyColumns); - $this->assertEquals('Address', $oneToOneMapping->targetEntityName); - $this->assertEquals('Person', $oneToOneMapping->sourceEntityName); - $this->assertEquals('address', $oneToOneMapping->sourceFieldName); - $this->assertTrue($oneToOneMapping->isOwningSide); - - $oneToOneMapping = array( - 'type' => ClassMetadata::ONE_TO_ONE, - 'fieldName' => 'person', - 'sourceEntity' => 'Address', - 'targetEntity' => 'Person', - 'mappedBy' => 'address' - ); - - $this->assertEquals('address', $oneToOneMapping->mappedBy); - $this->assertEquals('Address', $oneToOneMapping->sourceEntityName); - $this->assertEquals('Person', $oneToOneMapping->targetEntityName); - $this->assertTrue( ! $oneToOneMapping->isOwningSide); - } -} \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/PersistentCollectionTest.php b/tests/Doctrine/Tests/ORM/PersistentCollectionTest.php index b3f20e99b..47eeb8b9a 100644 --- a/tests/Doctrine/Tests/ORM/PersistentCollectionTest.php +++ b/tests/Doctrine/Tests/ORM/PersistentCollectionTest.php @@ -32,24 +32,6 @@ class PersistentCollectionTest extends \Doctrine\Tests\OrmTestCase $class = $this->_emMock->getClassMetadata('Doctrine\Tests\Models\ECommerce\ECommerceProduct'); $collection = new PersistentCollection($this->_emMock, $class, new ArrayCollection); $collection->setInitialized(false); - } - - public function testQueriesAssociationToLoadItself() - { - $this->markTestSkipped('Refactor!'); - - $class = $this->_emMock->getClassMetadata('Doctrine\Tests\Models\ECommerce\ECommerceProduct'); - $collection = new PersistentCollection($this->_emMock, $class, new ArrayCollection); - $collection->setInitialized(false); - - $association = $this->getMock('Doctrine\ORM\Mapping\OneToManyMapping', array('load'), array(), '', false, false, false); - $association->targetEntityName = 'Doctrine\Tests\Models\ECommerce\ECommerceFeature'; - $product = new ECommerceProduct(); - $association->expects($this->once()) - ->method('load') - ->with($product, $this->isInstanceOf($collection), $this->isInstanceOf($this->_emMock)); - $collection->setOwner($product, $association); - - count($collection); + $this->assertFalse($collection->isInitialized()); } } From a812dab4d403f239e50391ee32828883a973da8a Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Mon, 9 Aug 2010 23:02:44 +0200 Subject: [PATCH 12/15] DDC-729 Add more tests for merging with many to many relations --- .../ORM/Functional/Ticket/DDC729Test.php | 104 +++++++++++++++++- 1 file changed, 103 insertions(+), 1 deletion(-) diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC729Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC729Test.php index b63dfdbce..c2be27b11 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC729Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC729Test.php @@ -51,6 +51,100 @@ class DDC729Test extends \Doctrine\Tests\OrmFunctionalTestCase $a = $this->_em->find(__NAMESPACE__ . '\DDC729A', $aId); $this->assertEquals(1, count($a->related)); } + + public function testUnidirectionalMergeManyToMany() + { + $a = new DDC729A(); + $b1 = new DDC729B(); + $b2 = new DDC729B(); + $a->related[] = $b1; + + $this->_em->persist($a); + $this->_em->persist($b1); + $this->_em->persist($b2); + $this->_em->flush(); + $this->_em->clear(); + $aId = $a->id; + + $a = new DDC729A(); + $a->id = $aId; + + $a = $this->_em->merge($a); + + $a->related->set(0, $this->_em->merge($b1)); + + $a->related->set(1, $this->_em->merge($b2)); + + $this->_em->flush(); + $this->_em->clear(); + + $a = $this->_em->find(__NAMESPACE__ . '\DDC729A', $aId); + $this->assertEquals(2, count($a->related)); + } + + public function testBidirectionalMergeManyToMany() + { + $a = new DDC729A(); + $b1 = new DDC729B(); + $b2 = new DDC729B(); + $a->related[] = $b1; + + $this->_em->persist($a); + $this->_em->persist($b1); + $this->_em->persist($b2); + $this->_em->flush(); + $this->_em->clear(); + $aId = $a->id; + + $a = new DDC729A(); + $a->id = $aId; + + $a = $this->_em->merge($a); + + $a->related->set(0, $this->_em->merge($b1)); + $b1->related->set(0, $a); + + $a->related->set(1, $this->_em->merge($b2)); + $b2->related->set(0, $a); + + $this->_em->flush(); + $this->_em->clear(); + + $a = $this->_em->find(__NAMESPACE__ . '\DDC729A', $aId); + $this->assertEquals(2, count($a->related)); + } + + public function testBidirectionalMultiMergeManyToMany() + { + $a = new DDC729A(); + $b1 = new DDC729B(); + $b2 = new DDC729B(); + $a->related[] = $b1; + + $this->_em->persist($a); + $this->_em->persist($b1); + $this->_em->persist($b2); + $this->_em->flush(); + $this->_em->clear(); + $aId = $a->id; + + $a = new DDC729A(); + $a->id = $aId; + + $a = $this->_em->merge($a); + + $a->related->set(0, $this->_em->merge($b1)); + $b1->related->set(0, $this->_em->merge($a)); + + $a->related->set(1, $this->_em->merge($b2)); + $b2->related->set(0, $this->_em->merge($a)); + + $this->_em->flush(); + $this->_em->clear(); + + $a = $this->_em->find(__NAMESPACE__ . '\DDC729A', $aId); + $this->assertEquals(2, count($a->related)); + } } /** @@ -61,7 +155,7 @@ class DDC729A /** @Id @GeneratedValue @Column(type="integer") */ public $id; - /** @ManyToMany(targetEntity="DDC729B") */ + /** @ManyToMany(targetEntity="DDC729B", inversedBy="related") */ public $related; public function __construct() @@ -77,4 +171,12 @@ class DDC729B { /** @Id @GeneratedValue @Column(type="integer") */ public $id; + + /** @ManyToMany(targetEntity="DDC729B", mappedBy="related") */ + public $related; + + public function __construct() + { + $this->related = new \Doctrine\Common\Collections\ArrayCollection(); + } } From 575858774d2672f86ef25214697b773904b3789a Mon Sep 17 00:00:00 2001 From: "Roman S. Borschel" Date: Mon, 9 Aug 2010 23:13:26 +0200 Subject: [PATCH 13/15] Updated API doc for association mappings. --- .../ORM/Mapping/ClassMetadataInfo.php | 48 +++++++++++++++++-- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php index c2bcab1f4..515fc9d81 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php @@ -114,22 +114,36 @@ class ClassMetadataInfo const CHANGETRACKING_NOTIFY = 3; /** * Specifies that an association is to be fetched when it is first accessed. - * - * @var integer */ const FETCH_LAZY = 2; /** * Specifies that an association is to be fetched when the owner of the * association is fetched. - * - * @var integer */ const FETCH_EAGER = 3; + /** + * Identifies a one-to-one association. + */ const ONE_TO_ONE = 1; + /** + * Identifies a many-to-one association. + */ const MANY_TO_ONE = 2; + /** + * Combined bitmask for to-one (single-valued) associations. + */ const TO_ONE = 3; + /** + * Identifies a one-to-many association. + */ const ONE_TO_MANY = 4; + /** + * Identifies a many-to-many association. + */ const MANY_TO_MANY = 8; + /** + * Combined bitmask for to-many (collection-valued) associations. + */ const TO_MANY = 12; /** @@ -321,6 +335,32 @@ class ClassMetadataInfo /** * READ-ONLY: The association mappings of this class. * + * The mapping definition array supports the following keys: + * + * - fieldName (string) + * The name of the field in the entity the association is mapped to. + * + * - targetEntity (string) + * The class name of the target entity. If it is fully-qualified it is used as is. + * If it is a simple, unqualified class name the namespace is assumed to be the same + * as the namespace of the source entity. + * + * - mappedBy (string, required for bidirectional associations) + * The name of the field that completes the bidirectional association on the owning side. + * This key must be specified on the inverse side of a bidirectional association. + * + * - inversedBy (string, required for bidirectional associations) + * The name of the field that completes the bidirectional association on the inverse side. + * This key must be specified on the owning side of a bidirectional association. + * + * - cascade (array, optional) + * The names of persistence operations to cascade on the association. The set of possible + * values are: "persist", "remove", "detach", "merge", "refresh", "all" (implies all others). + * + * - orderBy (array, one-to-many/many-to-many only) + * A map of field names (of the target entity) to sorting directions (ASC/DESC). + * Example: array('priority' => 'desc') + * * @var array */ public $associationMappings = array(); From ca1931de814d54700b815e0a41eeb585fdc55ec5 Mon Sep 17 00:00:00 2001 From: "Roman S. Borschel" Date: Mon, 9 Aug 2010 23:23:30 +0200 Subject: [PATCH 14/15] More association mapping doc-block updates. --- .../ORM/Mapping/ClassMetadataInfo.php | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php index 515fc9d81..4add70566 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php @@ -361,6 +361,26 @@ class ClassMetadataInfo * A map of field names (of the target entity) to sorting directions (ASC/DESC). * Example: array('priority' => 'desc') * + * - fetch (integer, optional) + * The fetching strategy to use for the association, usually defaults to FETCH_LAZY. + * Possible values are: ClassMetadata::FETCH_EAGER, ClassMetadata::FETCH_LAZY. + * + * - joinTable (array, optional, many-to-many only) + * Specification of the join table and its join columns (foreign keys). + * Only valid for many-to-many mappings. Note that one-to-many associations can be mapped + * through a join table by simply mapping the association as many-to-many with a unique + * constraint on the join table. + * + * A join table definition has the following structure: + *
+     * array(
+     *     'name' => ,
+     *      'joinColumns' => array(),
+     *      'inverseJoinColumns' => array()
+     * )
+     * 
+ * + * * @var array */ public $associationMappings = array(); From 98785122d391d403be03393734380525019cfd58 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Tue, 10 Aug 2010 22:07:43 +0200 Subject: [PATCH 15/15] DDC-562 - Finally able to generate Unique Constraint on @OneToOne foreign keys --- .../ORM/Mapping/ClassMetadataInfo.php | 6 ++++- lib/Doctrine/ORM/Tools/SchemaTool.php | 16 +++++++------ .../ORM/Functional/BasicFunctionalTest.php | 4 ++++ .../SchemaTool/MySqlSchemaToolTest.php | 5 ++-- .../SchemaTool/PostgreSqlSchemaToolTest.php | 24 ++++++++++--------- 5 files changed, 34 insertions(+), 21 deletions(-) diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php index 4add70566..4e2a07519 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php @@ -774,7 +774,11 @@ class ClassMetadataInfo 'referencedColumnName' => 'id' )); } - foreach ($mapping['joinColumns'] as $joinColumn) { + foreach ($mapping['joinColumns'] AS $key => $joinColumn) { + if ($mapping['type'] == self::ONE_TO_ONE) { + $mapping['joinColumns'][$key]['unique'] = true; + } + $mapping['sourceToTargetKeyColumns'][$joinColumn['name']] = $joinColumn['referencedColumnName']; $mapping['joinColumnFieldNames'][$joinColumn['name']] = isset($joinColumn['fieldName']) ? $joinColumn['fieldName'] : $joinColumn['name']; diff --git a/lib/Doctrine/ORM/Tools/SchemaTool.php b/lib/Doctrine/ORM/Tools/SchemaTool.php index 1a9a2170a..fba93cde8 100644 --- a/lib/Doctrine/ORM/Tools/SchemaTool.php +++ b/lib/Doctrine/ORM/Tools/SchemaTool.php @@ -361,9 +361,13 @@ class SchemaTool $foreignClass = $this->_em->getClassMetadata($mapping['targetEntity']); if ($mapping['type'] & ClassMetadata::TO_ONE && $mapping['isOwningSide']) { - $primaryKeyColumns = $uniqueConstraints = array(); // unnecessary for this relation-type + $primaryKeyColumns = $uniqueConstraints = array(); // PK is unnecessary for this relation-type $this->_gatherRelationJoinColumns($mapping['joinColumns'], $table, $foreignClass, $mapping, $primaryKeyColumns, $uniqueConstraints); + + foreach($uniqueConstraints AS $indexName => $unique) { + $table->addUniqueIndex($unique['columns'], is_numeric($indexName) ? null : $indexName); + } } else if ($mapping['type'] == ClassMetadata::ONE_TO_MANY && $mapping['isOwningSide']) { //... create join table, one-many through join table supported later throw ORMException::notSupported(); @@ -381,13 +385,11 @@ class SchemaTool // Build second FK constraint (relation table => target table) $this->_gatherRelationJoinColumns($joinTable['inverseJoinColumns'], $theJoinTable, $foreignClass, $mapping, $primaryKeyColumns, $uniqueConstraints); - foreach($uniqueConstraints AS $indexName => $unique) { - $theJoinTable->addUniqueIndex( - $unique['columns'], is_numeric($indexName) ? null : $indexName - ); - } - $theJoinTable->setPrimaryKey($primaryKeyColumns); + + foreach($uniqueConstraints AS $indexName => $unique) { + $theJoinTable->addUniqueIndex($unique['columns'], is_numeric($indexName) ? null : $indexName); + } } } } diff --git a/tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php b/tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php index bf8deb163..97414ab4e 100644 --- a/tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php @@ -705,6 +705,10 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->_em->flush(); $this->assertEquals(1, $this->_em->getConnection()->fetchColumn("select count(*) from cms_addresses")); + // remove $address to free up unique key id + $this->_em->remove($address); + $this->_em->flush(); + $newAddress = new CmsAddress(); $newAddress->city = "NewBonn"; $newAddress->zip = "12354"; diff --git a/tests/Doctrine/Tests/ORM/Functional/SchemaTool/MySqlSchemaToolTest.php b/tests/Doctrine/Tests/ORM/Functional/SchemaTool/MySqlSchemaToolTest.php index 194f7109e..e3117b48e 100644 --- a/tests/Doctrine/Tests/ORM/Functional/SchemaTool/MySqlSchemaToolTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/SchemaTool/MySqlSchemaToolTest.php @@ -26,8 +26,7 @@ class MySqlSchemaToolTest extends \Doctrine\Tests\OrmFunctionalTestCase $tool = new SchemaTool($this->_em); $sql = $tool->getCreateSchemaSql($classes); - $this->assertEquals(8, count($sql)); - $this->assertEquals("CREATE TABLE cms_addresses (id INT AUTO_INCREMENT NOT NULL, user_id INT DEFAULT NULL, country VARCHAR(50) NOT NULL, zip VARCHAR(50) NOT NULL, city VARCHAR(50) NOT NULL, PRIMARY KEY(id)) ENGINE = InnoDB", $sql[0]); + $this->assertEquals("CREATE TABLE cms_addresses (id INT AUTO_INCREMENT NOT NULL, user_id INT DEFAULT NULL, country VARCHAR(50) NOT NULL, zip VARCHAR(50) NOT NULL, city VARCHAR(50) NOT NULL, UNIQUE INDEX cms_addresses_user_id_uniq (user_id), PRIMARY KEY(id)) ENGINE = InnoDB", $sql[0]); $this->assertEquals("CREATE TABLE cms_users (id INT AUTO_INCREMENT NOT NULL, status VARCHAR(50) NOT NULL, username VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, UNIQUE INDEX cms_users_username_uniq (username), PRIMARY KEY(id)) ENGINE = InnoDB", $sql[1]); $this->assertEquals("CREATE TABLE cms_users_groups (user_id INT NOT NULL, group_id INT NOT NULL, PRIMARY KEY(user_id, group_id)) ENGINE = InnoDB", $sql[2]); $this->assertEquals("CREATE TABLE cms_phonenumbers (phonenumber VARCHAR(50) NOT NULL, user_id INT DEFAULT NULL, PRIMARY KEY(phonenumber)) ENGINE = InnoDB", $sql[3]); @@ -35,6 +34,8 @@ class MySqlSchemaToolTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertEquals("ALTER TABLE cms_users_groups ADD FOREIGN KEY (user_id) REFERENCES cms_users(id)", $sql[5]); $this->assertEquals("ALTER TABLE cms_users_groups ADD FOREIGN KEY (group_id) REFERENCES cms_groups(id)", $sql[6]); $this->assertEquals("ALTER TABLE cms_phonenumbers ADD FOREIGN KEY (user_id) REFERENCES cms_users(id)", $sql[7]); + + $this->assertEquals(8, count($sql)); } public function testGetCreateSchemaSql2() diff --git a/tests/Doctrine/Tests/ORM/Functional/SchemaTool/PostgreSqlSchemaToolTest.php b/tests/Doctrine/Tests/ORM/Functional/SchemaTool/PostgreSqlSchemaToolTest.php index 8646068f0..fcdfb0c2a 100644 --- a/tests/Doctrine/Tests/ORM/Functional/SchemaTool/PostgreSqlSchemaToolTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/SchemaTool/PostgreSqlSchemaToolTest.php @@ -32,19 +32,21 @@ class PostgreSqlSchemaToolTest extends \Doctrine\Tests\OrmFunctionalTestCase $tool = new SchemaTool($this->_em); $sql = $tool->getCreateSchemaSql($classes); - $this->assertEquals(count($sql), 11); $this->assertEquals("CREATE TABLE cms_addresses (id INT NOT NULL, user_id INT DEFAULT NULL, country VARCHAR(50) NOT NULL, zip VARCHAR(50) NOT NULL, city VARCHAR(50) NOT NULL, PRIMARY KEY(id))", $sql[0]); - $this->assertEquals("CREATE TABLE cms_users (id INT NOT NULL, status VARCHAR(50) NOT NULL, username VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, PRIMARY KEY(id))", $sql[1]); - $this->assertEquals("CREATE UNIQUE INDEX cms_users_username_uniq ON cms_users (username)", $sql[2]); - $this->assertEquals("CREATE TABLE cms_users_groups (user_id INT NOT NULL, group_id INT NOT NULL, PRIMARY KEY(user_id, group_id))", $sql[3]); - $this->assertEquals("CREATE TABLE cms_phonenumbers (phonenumber VARCHAR(50) NOT NULL, user_id INT DEFAULT NULL, PRIMARY KEY(phonenumber))", $sql[4]); - $this->assertEquals("CREATE SEQUENCE cms_addresses_id_seq INCREMENT BY 10 MINVALUE 1 START 1", $sql[5]); - $this->assertEquals("CREATE SEQUENCE cms_users_id_seq INCREMENT BY 10 MINVALUE 1 START 1", $sql[6]); - $this->assertEquals("ALTER TABLE cms_addresses ADD FOREIGN KEY (user_id) REFERENCES cms_users(id) NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[7]); - $this->assertEquals("ALTER TABLE cms_users_groups ADD FOREIGN KEY (user_id) REFERENCES cms_users(id) NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[8]); - $this->assertEquals("ALTER TABLE cms_users_groups ADD FOREIGN KEY (group_id) REFERENCES cms_groups(id) NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[9]); - $this->assertEquals("ALTER TABLE cms_phonenumbers ADD FOREIGN KEY (user_id) REFERENCES cms_users(id) NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[10]); + $this->assertEquals("CREATE UNIQUE INDEX cms_addresses_user_id_uniq ON cms_addresses (user_id)", $sql[1]); + $this->assertEquals("CREATE TABLE cms_users (id INT NOT NULL, status VARCHAR(50) NOT NULL, username VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, PRIMARY KEY(id))", $sql[2]); + $this->assertEquals("CREATE UNIQUE INDEX cms_users_username_uniq ON cms_users (username)", $sql[3]); + $this->assertEquals("CREATE TABLE cms_users_groups (user_id INT NOT NULL, group_id INT NOT NULL, PRIMARY KEY(user_id, group_id))", $sql[4]); + $this->assertEquals("CREATE TABLE cms_phonenumbers (phonenumber VARCHAR(50) NOT NULL, user_id INT DEFAULT NULL, PRIMARY KEY(phonenumber))", $sql[5]); + $this->assertEquals("CREATE SEQUENCE cms_addresses_id_seq INCREMENT BY 10 MINVALUE 1 START 1", $sql[6]); + $this->assertEquals("CREATE SEQUENCE cms_users_id_seq INCREMENT BY 10 MINVALUE 1 START 1", $sql[7]); + $this->assertEquals("ALTER TABLE cms_addresses ADD FOREIGN KEY (user_id) REFERENCES cms_users(id) NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[8]); + $this->assertEquals("ALTER TABLE cms_users_groups ADD FOREIGN KEY (user_id) REFERENCES cms_users(id) NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[9]); + $this->assertEquals("ALTER TABLE cms_users_groups ADD FOREIGN KEY (group_id) REFERENCES cms_groups(id) NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[10]); + $this->assertEquals("ALTER TABLE cms_phonenumbers ADD FOREIGN KEY (user_id) REFERENCES cms_users(id) NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[11]); + + $this->assertEquals(count($sql), 12); } public function testGetCreateSchemaSql2()