From 30ed439111ea3def133ab05b9ba398b24a952b8b Mon Sep 17 00:00:00 2001 From: romanb Date: Fri, 18 Dec 2009 12:30:19 +0000 Subject: [PATCH] [2.0][DDC-144][DDC-113] Fixed. --- lib/Doctrine/ORM/Event/LifecycleEventArgs.php | 5 ++ .../ORM/Mapping/Driver/AnnotationDriver.php | 2 +- .../Persisters/JoinedSubclassPersister.php | 58 +++++++++++------ .../Persisters/StandardEntityPersister.php | 2 + lib/Doctrine/ORM/UnitOfWork.php | 17 ++--- .../Schema/SqliteSchemaManagerTest.php | 14 ++-- .../Tests/Mocks/SchemaManagerMock.php | 2 + .../ORM/Functional/LifecycleCallbackTest.php | 12 +++- .../ORM/Functional/Ticket/DDC144Test.php | 65 +++++++++++++++++++ 9 files changed, 141 insertions(+), 36 deletions(-) create mode 100644 tests/Doctrine/Tests/ORM/Functional/Ticket/DDC144Test.php diff --git a/lib/Doctrine/ORM/Event/LifecycleEventArgs.php b/lib/Doctrine/ORM/Event/LifecycleEventArgs.php index 612a31f2d..d753e869e 100644 --- a/lib/Doctrine/ORM/Event/LifecycleEventArgs.php +++ b/lib/Doctrine/ORM/Event/LifecycleEventArgs.php @@ -16,4 +16,9 @@ class LifecycleEventArgs extends \Doctrine\Common\EventArgs { return $this->_entity; } + + public function getEntityManager() + { + return $this->_em; + } } \ No newline at end of file diff --git a/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php b/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php index 11a1e3e95..ff4729ec7 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php @@ -79,7 +79,7 @@ class AnnotationDriver implements Driver } else if (isset($classAnnotations['Doctrine\ORM\Mapping\MappedSuperclass'])) { $metadata->isMappedSuperclass = true; } else { - throw DoctrineException::classIsNotAValidEntityOrMapperSuperClass($className); + throw DoctrineException::classIsNotAValidEntityOrMappedSuperClass($className); } // Evaluate DoctrineTable annotation diff --git a/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php b/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php index 002beeccf..7242b26b3 100644 --- a/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php +++ b/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php @@ -64,7 +64,7 @@ class JoinedSubclassPersister extends StandardEntityPersister */ private function _getVersionedClassMetadata() { - if ($isVersioned = $this->_class->isVersioned) { + if ($this->_class->isVersioned) { if (isset($this->_class->fieldMappings[$this->_class->versionField]['inherited'])) { $definingClassName = $this->_class->fieldMappings[$this->_class->versionField]['inherited']; $versionedClass = $this->_em->getClassMetadata($definingClassName); @@ -125,43 +125,59 @@ class JoinedSubclassPersister extends StandardEntityPersister $idGen = $this->_class->idGenerator; $isPostInsertId = $idGen->isPostInsertGenerator(); - // Prepare statements for all tables - $stmts = $classes = array(); - $stmts[$this->_class->primaryTable['name']] = $this->_conn->prepare($this->getInsertSql()); + // Prepare statement for the root table + $rootClass = $this->_class->name == $this->_class->rootEntityName ? + $this->_class : $this->_em->getClassMetadata($this->_class->rootEntityName); + $rootPersister = $this->_em->getUnitOfWork()->getEntityPersister($rootClass->name); + $rootTableName = $rootClass->primaryTable['name']; + $rootTableStmt = $this->_conn->prepare($rootPersister->getInsertSql()); if ($this->_sqlLogger !== null) { - $sql[$this->_class->primaryTable['name']] = $this->getInsertSql(); + $sql = array(); + $sql[$rootTableName] = $rootPersister->getInsertSql(); + } + + // Prepare statements for sub tables. + $subTableStmts = array(); + if ($rootClass !== $this->_class) { + $subTableStmts[$this->_class->primaryTable['name']] = $this->_conn->prepare($this->getInsertSql()); + if ($this->_sqlLogger !== null) { + $sql[$this->_class->primaryTable['name']] = $this->getInsertSql(); + } } foreach ($this->_class->parentClasses as $parentClassName) { $parentClass = $this->_em->getClassMetadata($parentClassName); - $parentPersister = $this->_em->getUnitOfWork()->getEntityPersister($parentClassName); - $stmts[$parentClass->primaryTable['name']] = $this->_conn->prepare($parentPersister->getInsertSql()); - if ($this->_sqlLogger !== null) { - $sql[$parentClass->primaryTable['name']] = $parentPersister->getInsertSql(); + $parentTableName = $parentClass->primaryTable['name']; + if ($parentClass !== $rootClass) { + $parentPersister = $this->_em->getUnitOfWork()->getEntityPersister($parentClassName); + $subTableStmts[$parentTableName] = $this->_conn->prepare($parentPersister->getInsertSql()); + if ($this->_sqlLogger !== null) { + $sql[$parentTableName] = $parentPersister->getInsertSql(); + } } } - $rootTableName = $this->_em->getClassMetadata($this->_class->rootEntityName)->primaryTable['name']; - + + // Execute all inserts. For each entity: + // 1) Insert on root table + // 2) Insert on sub tables foreach ($this->_queuedInserts as $entity) { $insertData = array(); $this->_prepareData($entity, $insertData, true); // Execute insert on root table - $stmt = $stmts[$rootTableName]; $paramIndex = 1; if ($this->_sqlLogger !== null) { $params = array(); foreach ($insertData[$rootTableName] as $columnName => $value) { $params[$paramIndex] = $value; - $stmt->bindValue($paramIndex++, $value); + $rootTableStmt->bindValue($paramIndex++, $value); } $this->_sqlLogger->logSql($sql[$rootTableName], $params); } else { foreach ($insertData[$rootTableName] as $columnName => $value) { - $stmt->bindValue($paramIndex++, $value); + $rootTableStmt->bindValue($paramIndex++, $value); } } - $stmt->execute(); - unset($insertData[$rootTableName]); + $rootTableStmt->execute(); if ($isPostInsertId) { $id = $idGen->generate($this->_em, $entity); @@ -170,9 +186,10 @@ class JoinedSubclassPersister extends StandardEntityPersister $id = $this->_em->getUnitOfWork()->getEntityIdentifier($entity); } - // Execute inserts on subtables - foreach ($insertData as $tableName => $data) { - $stmt = $stmts[$tableName]; + // Execute inserts on subtables. + // The order doesn't matter because all child tables link to the root table via FK. + foreach ($subTableStmts as $tableName => $stmt) { + $data = isset($insertData[$tableName]) ? $insertData[$tableName] : array(); $paramIndex = 1; if ($this->_sqlLogger !== null) { $params = array(); @@ -197,7 +214,8 @@ class JoinedSubclassPersister extends StandardEntityPersister } } - foreach ($stmts as $stmt) { + $rootTableStmt->closeCursor(); + foreach ($subTableStmts as $stmt) { $stmt->closeCursor(); } diff --git a/lib/Doctrine/ORM/Persisters/StandardEntityPersister.php b/lib/Doctrine/ORM/Persisters/StandardEntityPersister.php index a7bfc12c9..fa88139f3 100644 --- a/lib/Doctrine/ORM/Persisters/StandardEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/StandardEntityPersister.php @@ -168,6 +168,8 @@ class StandardEntityPersister $stmt->bindValue($paramIndex++, $value); } } + } else if ($this->_sqlLogger !== null) { + $this->_sqlLogger->logSql($this->getInsertSql()); } $stmt->execute(); diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index 6964fc62f..c9455d1c8 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -370,12 +370,6 @@ class UnitOfWork implements PropertyChangedListener * Computes all the changes that have been done to entities and collections * since the last commit and stores these changes in the _entityChangeSet map * temporarily for access by the persisters, until the UoW commit is finished. - * - * @param array $entities The entities for which to compute the changesets. If this - * parameter is not specified, the changesets of all entities in the identity - * map are computed if automatic dirty checking is enabled (the default). - * If automatic dirty checking is disabled, only those changesets will be - * computed that have been scheduled through scheduleForDirtyCheck(). */ public function computeChangeSets() { @@ -579,6 +573,13 @@ class UnitOfWork implements PropertyChangedListener $state = $this->getEntityState($entry, self::STATE_NEW); $oid = spl_object_hash($entry); if ($state == self::STATE_NEW) { + if (isset($targetClass->lifecycleCallbacks[Events::prePersist])) { + $targetClass->invokeLifecycleCallbacks(Events::prePersist, $entry); + } + if ($this->_evm->hasListeners(Events::prePersist)) { + $this->_evm->dispatchEvent(Events::prePersist, new LifecycleEventArgs($entry)); + } + // Get identifier, if possible (not post-insert) $idGen = $targetClass->idGenerator; if ( ! $idGen->isPostInsertGenerator()) { @@ -696,7 +697,7 @@ class UnitOfWork implements PropertyChangedListener } } } - + $postInsertIds = $persister->executeInserts(); if ($postInsertIds) { @@ -866,7 +867,7 @@ class UnitOfWork implements PropertyChangedListener } $this->_entityInsertions[$oid] = $entity; - + if (isset($this->_entityIdentifiers[$oid])) { $this->addToIdentityMap($entity); } diff --git a/tests/Doctrine/Tests/DBAL/Functional/Schema/SqliteSchemaManagerTest.php b/tests/Doctrine/Tests/DBAL/Functional/Schema/SqliteSchemaManagerTest.php index e9ccfa647..d6f174cac 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Schema/SqliteSchemaManagerTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Schema/SqliteSchemaManagerTest.php @@ -72,18 +72,24 @@ class SqliteSchemaManagerTest extends SchemaManagerFunctionalTestCase /** * @expectedException \Exception */ - public function testCreateSequence() + // This test is not correct. createSequence expects an object. + // PHPUnit wrapping the PHP error in an exception hides this but it shows up + // when the tests are run in the build (phing). + /*public function testCreateSequence() { $this->_sm->createSequence('seqname', 1, 1); - } + }*/ /** * @expectedException \Exception */ - public function testCreateForeignKey() + // This test is not correct. createForeignKey expects an object. + // PHPUnit wrapping the PHP error in an exception hides this but it shows up + // when the tests are run in the build (phing). + /*public function testCreateForeignKey() { $this->_sm->createForeignKey('table', array()); - } + }*/ /** * @expectedException \Exception diff --git a/tests/Doctrine/Tests/Mocks/SchemaManagerMock.php b/tests/Doctrine/Tests/Mocks/SchemaManagerMock.php index 771ae7fa4..d78376e79 100644 --- a/tests/Doctrine/Tests/Mocks/SchemaManagerMock.php +++ b/tests/Doctrine/Tests/Mocks/SchemaManagerMock.php @@ -8,4 +8,6 @@ class SchemaManagerMock extends \Doctrine\DBAL\Schema\AbstractSchemaManager { parent::__construct($conn); } + + protected function _getPortableTableColumnDefinition($tableColumn) {} } \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Functional/LifecycleCallbackTest.php b/tests/Doctrine/Tests/ORM/Functional/LifecycleCallbackTest.php index de10c8ed3..63ff7dd34 100644 --- a/tests/Doctrine/Tests/ORM/Functional/LifecycleCallbackTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/LifecycleCallbackTest.php @@ -86,14 +86,20 @@ class LifecycleCallbackTest extends \Doctrine\Tests\OrmFunctionalTestCase */ public function testCascadedEntitiesCallsPrePersist() { + //$this->_em->getConnection()->getConfiguration()->setSqlLogger(new \Doctrine\DBAL\Logging\EchoSqlLogger); + $e1 = new LifecycleCallbackTestEntity; $e2 = new LifecycleCallbackTestEntity; $c = new LifecycleCallbackCascader(); + $this->_em->persist($c); + $c->entities[] = $e1; $c->entities[] = $e2; - - $this->_em->persist($c); + $e1->cascader = $c; + $e2->cascader = $c; + + //$this->_em->persist($c); $this->_em->flush(); $this->assertTrue($e1->prePersistCallbackInvoked); @@ -184,7 +190,7 @@ class LifecycleCallbackCascader private $id; /** - * @OneToMany(targetEntity="LifecycleCallbackTestEntity", mappedBy="product", cascade={"persist"}) + * @OneToMany(targetEntity="LifecycleCallbackTestEntity", mappedBy="cascader", cascade={"persist"}) */ public $entities; diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC144Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC144Test.php new file mode 100644 index 000000000..63f2ee4e8 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC144Test.php @@ -0,0 +1,65 @@ +_em->getConnection()->getConfiguration()->setSqlLogger(new \Doctrine\DBAL\Logging\EchoSqlLogger); + + $this->_schemaTool->createSchema(array( + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC144FlowElement'), + // $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC144Expression'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC144Operand'), + )); + + } + + /** + * @group DDC-144 + */ + public function testIssue() + { + + $operand = new DDC144Operand; + $operand->property = 'flowValue'; + $operand->operandProperty = 'operandValue'; + $this->_em->persist($operand); + $this->_em->flush(); + + } +} + +/** + * @Entity + * @Table(name="ddc144_flowelements") + * @InheritanceType("JOINED") + * @DiscriminatorColumn(type="string", name="discr") + * @DiscriminatorMap({"flowelement" = "DDC144FlowElement", "operand" = "DDC144Operand"}) + */ +class DDC144FlowElement { + /** + * @Id @Column(type="integer") @GeneratedValue(strategy="AUTO") + * @var integer + */ + public $id; + /** @Column(type="string") */ + public $property; +} + +// /** @Entity @Table(name="ddc144_expressions") */ +abstract class DDC144Expression extends DDC144FlowElement { + abstract function method(); +} +/** @Entity @Table(name="ddc144_operands") */ +class DDC144Operand extends DDC144Expression { + /** @Column(type="string") */ + public $operandProperty; + function method() {} +} + +