From fb055ca75d3ee4f126f17869cc6cb645d3bb5cd2 Mon Sep 17 00:00:00 2001 From: Thomas Rothe Date: Sun, 16 Dec 2012 18:20:10 +0100 Subject: [PATCH 001/332] fixed problems with joined inheritance and composite keys SchemaTool now creates all Id columns not just only the first one. Insert statement for child entity now contains parameter for additional key columns only once. --- .../Persisters/JoinedSubclassPersister.php | 5 +- lib/Doctrine/ORM/Tools/SchemaTool.php | 141 ++++++++++++++---- .../JoinedChildClass.php | 21 +++ .../JoinedRootClass.php | 25 ++++ .../JoinedTableCompositeKeyTest.php | 64 ++++++++ .../Doctrine/Tests/OrmFunctionalTestCase.php | 10 ++ 6 files changed, 233 insertions(+), 33 deletions(-) create mode 100644 tests/Doctrine/Tests/Models/CompositeKeyInheritance/JoinedChildClass.php create mode 100644 tests/Doctrine/Tests/Models/CompositeKeyInheritance/JoinedRootClass.php create mode 100644 tests/Doctrine/Tests/ORM/Functional/JoinedTableCompositeKeyTest.php diff --git a/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php b/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php index 58aa7df17..8ad88c1d8 100644 --- a/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php +++ b/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php @@ -182,6 +182,7 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister // 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) { + /** @var \Doctrine\DBAL\Statement $stmt */ $paramIndex = 1; $data = isset($insertData[$tableName]) ? $insertData[$tableName] @@ -194,7 +195,9 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister } foreach ($data as $columnName => $value) { - $stmt->bindValue($paramIndex++, $value, $this->columnTypes[$columnName]); + if (!isset($id[$columnName])) { + $stmt->bindValue($paramIndex++, $value, $this->columnTypes[$columnName]); + } } $stmt->execute(); diff --git a/lib/Doctrine/ORM/Tools/SchemaTool.php b/lib/Doctrine/ORM/Tools/SchemaTool.php index 6a5319b1c..f917b9d06 100644 --- a/lib/Doctrine/ORM/Tools/SchemaTool.php +++ b/lib/Doctrine/ORM/Tools/SchemaTool.php @@ -91,7 +91,7 @@ class SchemaTool foreach ($createSchemaSql as $sql) { try { $conn->executeQuery($sql); - } catch(\Exception $e) { + } catch (\Exception $e) { throw ToolsException::schemaToolFailure($sql, $e); } } @@ -147,6 +147,7 @@ class SchemaTool $blacklistedFks = array(); foreach ($classes as $class) { + /** @var \Doctrine\ORM\Mapping\ClassMetadata $class */ if ($this->processingNotRequired($class, $processedClasses)) { continue; } @@ -154,7 +155,7 @@ class SchemaTool $table = $schema->createTable($this->quoteStrategy->getTableName($class, $this->platform)); if ($class->isInheritanceTypeSingleTable()) { - $columns = $this->gatherColumns($class, $table); + $this->gatherColumns($class, $table); $this->gatherRelationsSql($class, $table, $schema, $addedFks, $blacklistedFks); // Add the discriminator column @@ -177,7 +178,11 @@ class SchemaTool $pkColumns = array(); foreach ($class->fieldMappings as $fieldName => $mapping) { if ( ! isset($mapping['inherited'])) { - $columnName = $this->quoteStrategy->getColumnName($mapping['fieldName'], $class, $this->platform); + $columnName = $this->quoteStrategy->getColumnName( + $mapping['fieldName'], + $class, + $this->platform + ); $this->gatherColumn($class, $mapping, $table); if ($class->isIdentifier($fieldName)) { @@ -193,20 +198,36 @@ class SchemaTool $this->addDiscriminatorColumnDefinition($class, $table); } else { // Add an ID FK column to child tables - /* @var \Doctrine\ORM\Mapping\ClassMetadata $class */ - $idMapping = $class->fieldMappings[$class->identifier[0]]; - $this->gatherColumn($class, $idMapping, $table); - $columnName = $this->quoteStrategy->getColumnName($class->identifier[0], $class, $this->platform); - // TODO: This seems rather hackish, can we optimize it? - $table->getColumn($columnName)->setAutoincrement(false); + $inheritedKeyColumns = array(); + foreach ($class->identifier as $identifierField) { + $idMapping = $class->fieldMappings[$identifierField]; + if (isset($idMapping['inherited'])) { + $this->gatherColumn($class, $idMapping, $table); + $columnName = $this->quoteStrategy->getColumnName( + $identifierField, + $class, + $this->platform + ); + // TODO: This seems rather hackish, can we optimize it? + $table->getColumn($columnName)->setAutoincrement(false); - $pkColumns[] = $columnName; + $pkColumns[] = $columnName; + $inheritedKeyColumns[] = $columnName; + } + } + if (!empty($inheritedKeyColumns)) { + // Add a FK constraint on the ID column + $table->addForeignKeyConstraint( + $this->quoteStrategy->getTableName( + $this->em->getClassMetadata($class->rootEntityName), + $this->platform + ), + $inheritedKeyColumns, + $inheritedKeyColumns, + array('onDelete' => 'CASCADE') + ); + } - // Add a FK constraint on the ID column - $table->addForeignKeyConstraint( - $this->quoteStrategy->getTableName($this->em->getClassMetadata($class->rootEntityName), $this->platform), - array($columnName), array($columnName), array('onDelete' => 'CASCADE') - ); } $table->setPrimaryKey($pkColumns); @@ -268,7 +289,10 @@ class SchemaTool } if ($eventManager->hasListeners(ToolEvents::postGenerateSchemaTable)) { - $eventManager->dispatchEvent(ToolEvents::postGenerateSchemaTable, new GenerateSchemaTableEventArgs($class, $schema, $table)); + $eventManager->dispatchEvent( + ToolEvents::postGenerateSchemaTable, + new GenerateSchemaTableEventArgs($class, $schema, $table) + ); } } @@ -277,7 +301,10 @@ class SchemaTool } if ($eventManager->hasListeners(ToolEvents::postGenerateSchema)) { - $eventManager->dispatchEvent(ToolEvents::postGenerateSchema, new GenerateSchemaEventArgs($this->em, $schema)); + $eventManager->dispatchEvent( + ToolEvents::postGenerateSchema, + new GenerateSchemaEventArgs($this->em, $schema) + ); } return $schema; @@ -296,7 +323,9 @@ class SchemaTool { $discrColumn = $class->discriminatorColumn; - if ( ! isset($discrColumn['type']) || (strtolower($discrColumn['type']) == 'string' && $discrColumn['length'] === null)) { + if ( ! isset($discrColumn['type']) || + (strtolower($discrColumn['type']) == 'string' && $discrColumn['length'] === null) + ) { $discrColumn['type'] = 'string'; $discrColumn['length'] = 255; } @@ -339,7 +368,7 @@ class SchemaTool // For now, this is a hack required for single table inheritence, since this method is called // twice by single table inheritence relations - if(!$table->hasIndex('primary')) { + if (!$table->hasIndex('primary')) { //$table->setPrimaryKey($pkColumns); } } @@ -367,7 +396,7 @@ class SchemaTool $options['platformOptions'] = array(); $options['platformOptions']['version'] = $class->isVersioned && $class->versionField == $mapping['fieldName'] ? true : false; - if(strtolower($columnType) == 'string' && $options['length'] === null) { + if (strtolower($columnType) == 'string' && $options['length'] === null) { $options['length'] = 255; } @@ -452,9 +481,18 @@ class SchemaTool if ($mapping['type'] & ClassMetadata::TO_ONE && $mapping['isOwningSide']) { $primaryKeyColumns = $uniqueConstraints = array(); // PK is unnecessary for this relation-type - $this->_gatherRelationJoinColumns($mapping['joinColumns'], $table, $foreignClass, $mapping, $primaryKeyColumns, $uniqueConstraints, $addedFks, $blacklistedFks); + $this->gatherRelationJoinColumns( + $mapping['joinColumns'], + $table, + $foreignClass, + $mapping, + $primaryKeyColumns, + $uniqueConstraints, + $addedFks, + $blacklistedFks + ); - foreach($uniqueConstraints as $indexName => $unique) { + foreach ($uniqueConstraints as $indexName => $unique) { $table->addUniqueIndex($unique['columns'], is_numeric($indexName) ? null : $indexName); } } elseif ($mapping['type'] == ClassMetadata::ONE_TO_MANY && $mapping['isOwningSide']) { @@ -464,19 +502,39 @@ class SchemaTool // create join table $joinTable = $mapping['joinTable']; - $theJoinTable = $schema->createTable($this->quoteStrategy->getJoinTableName($mapping, $foreignClass, $this->platform)); + $theJoinTable = $schema->createTable( + $this->quoteStrategy->getJoinTableName($mapping, $foreignClass, $this->platform) + ); $primaryKeyColumns = $uniqueConstraints = array(); // Build first FK constraint (relation table => source table) - $this->_gatherRelationJoinColumns($joinTable['joinColumns'], $theJoinTable, $class, $mapping, $primaryKeyColumns, $uniqueConstraints, $addedFks, $blacklistedFks); + $this->gatherRelationJoinColumns( + $joinTable['joinColumns'], + $theJoinTable, + $class, + $mapping, + $primaryKeyColumns, + $uniqueConstraints, + $addedFks, + $blacklistedFks + ); // Build second FK constraint (relation table => target table) - $this->_gatherRelationJoinColumns($joinTable['inverseJoinColumns'], $theJoinTable, $foreignClass, $mapping, $primaryKeyColumns, $uniqueConstraints, $addedFks, $blacklistedFks); + $this->gatherRelationJoinColumns( + $joinTable['inverseJoinColumns'], + $theJoinTable, + $foreignClass, + $mapping, + $primaryKeyColumns, + $uniqueConstraints, + $addedFks, + $blacklistedFks + ); $theJoinTable->setPrimaryKey($primaryKeyColumns); - foreach($uniqueConstraints as $indexName => $unique) { + foreach ($uniqueConstraints as $indexName => $unique) { $theJoinTable->addUniqueIndex($unique['columns'], is_numeric($indexName) ? null : $indexName); } } @@ -507,7 +565,8 @@ class SchemaTool if (in_array($referencedColumnName, $class->getIdentifierColumnNames())) { // it seems to be an entity as foreign key foreach ($class->getIdentifierFieldNames() as $fieldName) { - if ($class->hasAssociation($fieldName) && $class->getSingleAssociationJoinColumnName($fieldName) == $referencedColumnName) { + if ($class->hasAssociation($fieldName) + && $class->getSingleAssociationJoinColumnName($fieldName) == $referencedColumnName) { return $this->getDefiningClass( $this->em->getClassMetadata($class->associationMappings[$fieldName]['targetEntity']), $class->getSingleAssociationReferencedJoinColumnName($fieldName) @@ -531,8 +590,16 @@ class SchemaTool * @param array $addedFks * @param array $blacklistedFks */ - private function _gatherRelationJoinColumns($joinColumns, $theJoinTable, $class, $mapping, &$primaryKeyColumns, &$uniqueConstraints, &$addedFks, &$blacklistedFks) - { + private function gatherRelationJoinColumns( + $joinColumns, + $theJoinTable, + $class, + $mapping, + &$primaryKeyColumns, + &$uniqueConstraints, + &$addedFks, + &$blacklistedFks + ) { $localColumns = array(); $foreignColumns = array(); $fkOptions = array(); @@ -540,7 +607,10 @@ class SchemaTool foreach ($joinColumns as $joinColumn) { - list($definingClass, $referencedFieldName) = $this->getDefiningClass($class, $joinColumn['referencedColumnName']); + list($definingClass, $referencedFieldName) = $this->getDefiningClass( + $class, + $joinColumn['referencedColumnName'] + ); if ( ! $definingClass) { throw new \Doctrine\ORM\ORMException( @@ -550,7 +620,11 @@ class SchemaTool } $quotedColumnName = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); - $quotedRefColumnName = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $class, $this->platform); + $quotedRefColumnName = $this->quoteStrategy->getReferencedJoinColumnName( + $joinColumn, + $class, + $this->platform + ); $primaryKeyColumns[] = $quotedColumnName; $localColumns[] = $quotedColumnName; @@ -617,7 +691,10 @@ class SchemaTool } elseif (!isset($blacklistedFks[$compositeName])) { $addedFks[$compositeName] = array('foreignTableName' => $foreignTableName, 'foreignColumns' => $foreignColumns); $theJoinTable->addUnnamedForeignKeyConstraint( - $foreignTableName, $localColumns, $foreignColumns, $fkOptions + $foreignTableName, + $localColumns, + $foreignColumns, + $fkOptions ); } } diff --git a/tests/Doctrine/Tests/Models/CompositeKeyInheritance/JoinedChildClass.php b/tests/Doctrine/Tests/Models/CompositeKeyInheritance/JoinedChildClass.php new file mode 100644 index 000000000..ba5973647 --- /dev/null +++ b/tests/Doctrine/Tests/Models/CompositeKeyInheritance/JoinedChildClass.php @@ -0,0 +1,21 @@ +useModelSet('compositekeyinheritance'); + parent::setUp(); + + } + + /** + * + */ + public function testInsertWithCompositeKey() + { + $childEntity = new JoinedChildClass(); + $this->_em->persist($childEntity); + $this->_em->flush(); + + $this->_em->clear(); + + $entity = $this->findEntity(); + $this->assertEquals($childEntity, $entity); + } + + /** + * + */ + public function testUpdateWithCompositeKey() + { + $childEntity = new JoinedChildClass(); + $this->_em->persist($childEntity); + $this->_em->flush(); + + $this->_em->clear(); + + $entity = $this->findEntity(); + $entity->extension = 'ext-new'; + $this->_em->persist($entity); + $this->_em->flush(); + + $this->_em->clear(); + + $persistedEntity = $this->findEntity(); + $this->assertEquals($entity, $persistedEntity); + } + + /** + * @return \Doctrine\Tests\Models\CompositeKeyInheritance\JoinedChildClass + */ + private function findEntity() + { + return $this->_em->find( + 'Doctrine\Tests\Models\CompositeKeyInheritance\JoinedRootClass', + array('keyPart1' => 'part-1', 'keyPart2' => 'part-2') + ); + } +} diff --git a/tests/Doctrine/Tests/OrmFunctionalTestCase.php b/tests/Doctrine/Tests/OrmFunctionalTestCase.php index 4b72a9a27..872f86c81 100644 --- a/tests/Doctrine/Tests/OrmFunctionalTestCase.php +++ b/tests/Doctrine/Tests/OrmFunctionalTestCase.php @@ -124,6 +124,10 @@ abstract class OrmFunctionalTestCase extends OrmTestCase 'Doctrine\Tests\Models\CustomType\CustomTypeParent', 'Doctrine\Tests\Models\CustomType\CustomTypeUpperCase', ), + 'compositekeyinheritance' => array( + 'Doctrine\Tests\Models\CompositeKeyInheritance\JoinedRootClass', + 'Doctrine\Tests\Models\CompositeKeyInheritance\JoinedChildClass', + ) ); protected function useModelSet($setName) @@ -239,6 +243,12 @@ abstract class OrmFunctionalTestCase extends OrmTestCase $conn->executeUpdate('DELETE FROM customtype_uppercases'); } + if (isset($this->_usedModelSets['compositekeyinheritance'])) { + $conn->executeUpdate('DELETE FROM JoinedChildClass'); + $conn->executeUpdate('DELETE FROM JoinedRootClass'); + } + + $this->_em->clear(); } From 86c33d78d08afa3ed0b759884e09d497878094e5 Mon Sep 17 00:00:00 2001 From: Thomas Rothe Date: Mon, 17 Dec 2012 11:01:20 +0100 Subject: [PATCH 002/332] inheritance with composite keys added tests for single table inheritance --- .../SingleChildClass.php | 21 ++++++ .../SingleRootClass.php | 25 ++++++++ .../SingleTableCompositeKeyTest.php | 64 +++++++++++++++++++ .../Doctrine/Tests/OrmFunctionalTestCase.php | 3 + 4 files changed, 113 insertions(+) create mode 100644 tests/Doctrine/Tests/Models/CompositeKeyInheritance/SingleChildClass.php create mode 100644 tests/Doctrine/Tests/Models/CompositeKeyInheritance/SingleRootClass.php create mode 100644 tests/Doctrine/Tests/ORM/Functional/SingleTableCompositeKeyTest.php diff --git a/tests/Doctrine/Tests/Models/CompositeKeyInheritance/SingleChildClass.php b/tests/Doctrine/Tests/Models/CompositeKeyInheritance/SingleChildClass.php new file mode 100644 index 000000000..05579bce4 --- /dev/null +++ b/tests/Doctrine/Tests/Models/CompositeKeyInheritance/SingleChildClass.php @@ -0,0 +1,21 @@ +useModelSet('compositekeyinheritance'); + parent::setUp(); + + } + + /** + * + */ + public function testInsertWithCompositeKey() + { + $childEntity = new SingleChildClass(); + $this->_em->persist($childEntity); + $this->_em->flush(); + + $this->_em->clear(); + + $entity = $this->findEntity(); + $this->assertEquals($childEntity, $entity); + } + + /** + * + */ + public function testUpdateWithCompositeKey() + { + $childEntity = new SingleChildClass(); + $this->_em->persist($childEntity); + $this->_em->flush(); + + $this->_em->clear(); + + $entity = $this->findEntity(); + $entity->extension = 'ext-new'; + $this->_em->persist($entity); + $this->_em->flush(); + + $this->_em->clear(); + + $persistedEntity = $this->findEntity(); + $this->assertEquals($entity, $persistedEntity); + } + + /** + * @return \Doctrine\Tests\Models\CompositeKeyInheritance\JoinedChildClass + */ + private function findEntity() + { + return $this->_em->find( + 'Doctrine\Tests\Models\CompositeKeyInheritance\SingleRootClass', + array('keyPart1' => 'part-1', 'keyPart2' => 'part-2') + ); + } +} diff --git a/tests/Doctrine/Tests/OrmFunctionalTestCase.php b/tests/Doctrine/Tests/OrmFunctionalTestCase.php index 872f86c81..18fd19297 100644 --- a/tests/Doctrine/Tests/OrmFunctionalTestCase.php +++ b/tests/Doctrine/Tests/OrmFunctionalTestCase.php @@ -127,6 +127,8 @@ abstract class OrmFunctionalTestCase extends OrmTestCase 'compositekeyinheritance' => array( 'Doctrine\Tests\Models\CompositeKeyInheritance\JoinedRootClass', 'Doctrine\Tests\Models\CompositeKeyInheritance\JoinedChildClass', + 'Doctrine\Tests\Models\CompositeKeyInheritance\SingleRootClass', + 'Doctrine\Tests\Models\CompositeKeyInheritance\SingleChildClass', ) ); @@ -246,6 +248,7 @@ abstract class OrmFunctionalTestCase extends OrmTestCase if (isset($this->_usedModelSets['compositekeyinheritance'])) { $conn->executeUpdate('DELETE FROM JoinedChildClass'); $conn->executeUpdate('DELETE FROM JoinedRootClass'); + $conn->executeUpdate('DELETE FROM SingleRootClass'); } From 35562d3a4d70a2bc0f215ed8fcccfa76b1c66064 Mon Sep 17 00:00:00 2001 From: Benjamin Morel Date: Tue, 12 Feb 2013 15:51:24 +0000 Subject: [PATCH 003/332] Unit tests: cleaned up outdated / unused / commented out code. --- tests/Doctrine/Tests/Mocks/DriverMock.php | 14 -------- .../Tests/Mocks/EntityManagerMock.php | 22 ------------- .../Tests/Mocks/EntityPersisterMock.php | 19 ----------- .../Tests/Mocks/HydratorMockStatement.php | 17 +--------- .../Tests/Mocks/IdentityIdGeneratorMock.php | 16 ---------- tests/Doctrine/Tests/Mocks/SequenceMock.php | 32 ------------------- tests/Doctrine/Tests/Mocks/UnitOfWorkMock.php | 22 ------------- tests/Doctrine/Tests/ORM/UnitOfWorkTest.php | 7 ---- 8 files changed, 1 insertion(+), 148 deletions(-) delete mode 100644 tests/Doctrine/Tests/Mocks/IdentityIdGeneratorMock.php diff --git a/tests/Doctrine/Tests/Mocks/DriverMock.php b/tests/Doctrine/Tests/Mocks/DriverMock.php index c7a101094..af9f869ce 100644 --- a/tests/Doctrine/Tests/Mocks/DriverMock.php +++ b/tests/Doctrine/Tests/Mocks/DriverMock.php @@ -25,20 +25,6 @@ class DriverMock implements \Doctrine\DBAL\Driver return new DriverConnectionMock(); } - /** - * Constructs the Sqlite PDO DSN. - * - * @param array $params - * - * @return string The DSN. - * - * @override - */ - protected function _constructPdoDsn(array $params) - { - return ""; - } - /** * {@inheritdoc} */ diff --git a/tests/Doctrine/Tests/Mocks/EntityManagerMock.php b/tests/Doctrine/Tests/Mocks/EntityManagerMock.php index 91f1c9daa..b27e96c4b 100644 --- a/tests/Doctrine/Tests/Mocks/EntityManagerMock.php +++ b/tests/Doctrine/Tests/Mocks/EntityManagerMock.php @@ -38,11 +38,6 @@ class EntityManagerMock extends \Doctrine\ORM\EntityManager */ private $_proxyFactoryMock; - /** - * @var array - */ - private $_idGenerators = array(); - /** * {@inheritdoc} */ @@ -103,21 +98,4 @@ class EntityManagerMock extends \Doctrine\ORM\EntityManager return new EntityManagerMock($conn, $config, $eventManager); } -/* - public function setIdGenerator($className, $generator) - { - $this->_idGenerators[$className] = $generator; - } -*/ - /** @override */ -/* public function getIdGenerator($className) - { - - if (isset($this->_idGenerators[$className])) { - return $this->_idGenerators[$className]; - } - - return parent::getIdGenerator($className); - } - */ } diff --git a/tests/Doctrine/Tests/Mocks/EntityPersisterMock.php b/tests/Doctrine/Tests/Mocks/EntityPersisterMock.php index 862000eb5..41b74607d 100644 --- a/tests/Doctrine/Tests/Mocks/EntityPersisterMock.php +++ b/tests/Doctrine/Tests/Mocks/EntityPersisterMock.php @@ -42,25 +42,6 @@ class EntityPersisterMock extends \Doctrine\ORM\Persisters\BasicEntityPersister */ private $existsCalled = false; - /** - * @param object $entity - * - * @return mixed - * - * @override - */ - public function insert($entity) - { - $this->inserts[] = $entity; - if ( ! is_null($this->mockIdGeneratorType) && $this->mockIdGeneratorType == \Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_IDENTITY - || $this->class->isIdGeneratorIdentity()) { - $id = $this->identityColumnValueCounter++; - $this->postInsertIds[$id] = $entity; - return $id; - } - return null; - } - /** * @param object $entity * diff --git a/tests/Doctrine/Tests/Mocks/HydratorMockStatement.php b/tests/Doctrine/Tests/Mocks/HydratorMockStatement.php index 6c9ff6b8b..9d06ba6b8 100644 --- a/tests/Doctrine/Tests/Mocks/HydratorMockStatement.php +++ b/tests/Doctrine/Tests/Mocks/HydratorMockStatement.php @@ -68,21 +68,6 @@ class HydratorMockStatement implements \IteratorAggregate, \Doctrine\DBAL\Driver return true; } - /** - * @param array $resultSet - * - * @return void - */ - public function setResultSet(array $resultSet) - { - reset($resultSet); - $this->_resultSet = $resultSet; - } - - public function bindColumn($column, &$param, $type = null) - { - } - /** * {@inheritdoc} */ @@ -93,7 +78,7 @@ class HydratorMockStatement implements \IteratorAggregate, \Doctrine\DBAL\Driver /** * {@inheritdoc} */ - public function bindParam($column, &$variable, $type = null, $length = null, $driverOptions = array()) + public function bindParam($column, &$variable, $type = null) { } diff --git a/tests/Doctrine/Tests/Mocks/IdentityIdGeneratorMock.php b/tests/Doctrine/Tests/Mocks/IdentityIdGeneratorMock.php deleted file mode 100644 index 868b7fe44..000000000 --- a/tests/Doctrine/Tests/Mocks/IdentityIdGeneratorMock.php +++ /dev/null @@ -1,16 +0,0 @@ -_mockPostInsertId = $id; - } -} diff --git a/tests/Doctrine/Tests/Mocks/SequenceMock.php b/tests/Doctrine/Tests/Mocks/SequenceMock.php index 8612e0d27..4f5123004 100644 --- a/tests/Doctrine/Tests/Mocks/SequenceMock.php +++ b/tests/Doctrine/Tests/Mocks/SequenceMock.php @@ -22,30 +22,6 @@ class SequenceMock extends \Doctrine\ORM\Id\SequenceGenerator return $this->_sequenceNumber++; } - /** - * @override - */ - public function nextId($seqName, $ondemand = true) - { - return $this->_sequenceNumber++; - } - - /** - * @override - */ - public function lastInsertId($table = null, $field = null) - { - return $this->_sequenceNumber - 1; - } - - /** - * @override - */ - public function currId($seqName) - { - return $this->_sequenceNumber; - } - /* Mock API */ /** @@ -55,12 +31,4 @@ class SequenceMock extends \Doctrine\ORM\Id\SequenceGenerator { $this->_sequenceNumber = 0; } - - /** - * @return void - */ - public function autoinc() - { - $this->_sequenceNumber++; - } } diff --git a/tests/Doctrine/Tests/Mocks/UnitOfWorkMock.php b/tests/Doctrine/Tests/Mocks/UnitOfWorkMock.php index 95c511c70..0dd0671d0 100644 --- a/tests/Doctrine/Tests/Mocks/UnitOfWorkMock.php +++ b/tests/Doctrine/Tests/Mocks/UnitOfWorkMock.php @@ -52,28 +52,6 @@ class UnitOfWorkMock extends \Doctrine\ORM\UnitOfWork $this->_persisterMock[$entityName] = $persister; } - /** - * @param object $entity - * @param array $mockChangeSet - * - * @return void - */ - public function setDataChangeSet($entity, array $mockChangeSet) - { - $this->_mockDataChangeSets[spl_object_hash($entity)] = $mockChangeSet; - } - - /** - * @param object $entity - * @param mixed $state - * - * @return void - */ - public function setEntityState($entity, $state) - { - $this->_entityStates[spl_object_hash($entity)] = $state; - } - /** * {@inheritdoc} */ diff --git a/tests/Doctrine/Tests/ORM/UnitOfWorkTest.php b/tests/Doctrine/Tests/ORM/UnitOfWorkTest.php index e091483e3..9a645d52e 100644 --- a/tests/Doctrine/Tests/ORM/UnitOfWorkTest.php +++ b/tests/Doctrine/Tests/ORM/UnitOfWorkTest.php @@ -7,7 +7,6 @@ use Doctrine\Tests\Mocks\ConnectionMock; use Doctrine\Tests\Mocks\EntityManagerMock; use Doctrine\Tests\Mocks\UnitOfWorkMock; use Doctrine\Tests\Mocks\EntityPersisterMock; -use Doctrine\Tests\Mocks\IdentityIdGeneratorMock; use Doctrine\Tests\Models\Forum\ForumUser; use Doctrine\Tests\Models\Forum\ForumAvatar; @@ -54,8 +53,6 @@ class UnitOfWorkTest extends \Doctrine\Tests\OrmTestCase // Setup fake persister and id generator for identity generation $userPersister = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata("Doctrine\Tests\Models\Forum\ForumUser")); $this->_unitOfWork->setEntityPersister('Doctrine\Tests\Models\Forum\ForumUser', $userPersister); - //$idGeneratorMock = new IdentityIdGeneratorMock($this->_emMock); - //$this->_emMock->setIdGenerator('Doctrine\Tests\Models\Forum\ForumUser', $idGeneratorMock); $userPersister->setMockIdGeneratorType(\Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_IDENTITY); // Test @@ -96,14 +93,10 @@ class UnitOfWorkTest extends \Doctrine\Tests\OrmTestCase //ForumUser $userPersister = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata("Doctrine\Tests\Models\Forum\ForumUser")); $this->_unitOfWork->setEntityPersister('Doctrine\Tests\Models\Forum\ForumUser', $userPersister); - //$userIdGeneratorMock = new IdentityIdGeneratorMock($this->_emMock); - //$this->_emMock->setIdGenerator('Doctrine\Tests\Models\Forum\ForumUser', $userIdGeneratorMock); $userPersister->setMockIdGeneratorType(\Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_IDENTITY); // ForumAvatar $avatarPersister = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata("Doctrine\Tests\Models\Forum\ForumAvatar")); $this->_unitOfWork->setEntityPersister('Doctrine\Tests\Models\Forum\ForumAvatar', $avatarPersister); - //$avatarIdGeneratorMock = new IdentityIdGeneratorMock($this->_emMock); - //$this->_emMock->setIdGenerator('Doctrine\Tests\Models\Forum\ForumAvatar', $avatarIdGeneratorMock); $avatarPersister->setMockIdGeneratorType(\Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_IDENTITY); // Test From 5206566707971db0b68d5256958e68b820dbab78 Mon Sep 17 00:00:00 2001 From: Abdellatif Ait boudad Date: Tue, 26 Feb 2013 00:02:34 +0000 Subject: [PATCH 004/332] Remove dead code --- lib/Doctrine/ORM/Event/OnFlushEventArgs.php | 24 --------------------- 1 file changed, 24 deletions(-) diff --git a/lib/Doctrine/ORM/Event/OnFlushEventArgs.php b/lib/Doctrine/ORM/Event/OnFlushEventArgs.php index ea5d0566e..2dd05a5fa 100644 --- a/lib/Doctrine/ORM/Event/OnFlushEventArgs.php +++ b/lib/Doctrine/ORM/Event/OnFlushEventArgs.php @@ -38,9 +38,6 @@ class OnFlushEventArgs extends EventArgs */ private $em; - //private $entitiesToPersist = array(); - //private $entitiesToRemove = array(); - /** * Constructor. * @@ -61,25 +58,4 @@ class OnFlushEventArgs extends EventArgs return $this->em; } - /* - public function addEntityToPersist($entity) - { - - } - - public function addEntityToRemove($entity) - { - - } - - public function addEntityToUpdate($entity) - { - - } - - public function getEntitiesToPersist() - { - return $this->_entitiesToPersist; - } - */ } From f9519479fcbad6cd8c7fd5e16e9f3141c4b5be82 Mon Sep 17 00:00:00 2001 From: Norbert Orzechowicz Date: Thu, 28 Feb 2013 10:10:28 +0100 Subject: [PATCH 005/332] Fix SimpleObjectHydrator behavior when column not exists in fieldMappings, relationMappings and metaMappings --- .../Hydration/SimpleObjectHydrator.php | 5 +++- .../Hydration/SimpleObjectHydratorTest.php | 24 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Internal/Hydration/SimpleObjectHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/SimpleObjectHydrator.php index abd29e76f..249b60462 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/SimpleObjectHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/SimpleObjectHydrator.php @@ -176,10 +176,13 @@ class SimpleObjectHydrator extends AbstractHydrator // One solution is to load the association, but it might require extra efforts. return array('name' => $column); - default: + case (isset($this->_rsm->metaMappings[$column])): return array( 'name' => $this->_rsm->metaMappings[$column] ); + + default: + return null; } } } diff --git a/tests/Doctrine/Tests/ORM/Hydration/SimpleObjectHydratorTest.php b/tests/Doctrine/Tests/ORM/Hydration/SimpleObjectHydratorTest.php index 80f90e7ff..400b5829f 100644 --- a/tests/Doctrine/Tests/ORM/Hydration/SimpleObjectHydratorTest.php +++ b/tests/Doctrine/Tests/ORM/Hydration/SimpleObjectHydratorTest.php @@ -34,4 +34,28 @@ class SimpleObjectHydratorTest extends HydrationTestCase $hydrator = new \Doctrine\ORM\Internal\Hydration\SimpleObjectHydrator($this->_em); $hydrator->hydrateAll($stmt, $rsm); } + + public function testExtraFieldInResultSetShouldBeIgnore() + { + $rsm = new ResultSetMapping; + $rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsAddress', 'a'); + $rsm->addFieldResult('a', 'a__id', 'id'); + $rsm->addFieldResult('a', 'a__city', 'city'); + $resultSet = array( + array( + 'a__id' => '1', + 'a__city' => 'Cracow', + 'doctrine_rownum' => '1' + ), + ); + + $expectedEntity = new \Doctrine\Tests\Models\CMS\CmsAddress(); + $expectedEntity->id = 1; + $expectedEntity->city = 'Cracow'; + + $stmt = new HydratorMockStatement($resultSet); + $hydrator = new \Doctrine\ORM\Internal\Hydration\SimpleObjectHydrator($this->_em); + $result = $hydrator->hydrateAll($stmt, $rsm); + $this->assertEquals($result[0], $expectedEntity); + } } From 20b46fe17f785cee164e9cb55f7f1bc7401616fc Mon Sep 17 00:00:00 2001 From: Matthieu Napoli Date: Fri, 1 Mar 2013 15:11:21 +0100 Subject: [PATCH 006/332] Fixed broken code block in documentation --- docs/en/cookbook/resolve-target-entity-listener.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/en/cookbook/resolve-target-entity-listener.rst b/docs/en/cookbook/resolve-target-entity-listener.rst index c3f866532..c77459183 100644 --- a/docs/en/cookbook/resolve-target-entity-listener.rst +++ b/docs/en/cookbook/resolve-target-entity-listener.rst @@ -1,4 +1,4 @@ -Keeping your Modules independent +Keeping your Modules independents ================================= .. versionadded:: 2.2 @@ -112,7 +112,7 @@ An InvoiceSubjectInterface Next, we need to configure the listener. Add this to the area you set up Doctrine. You must set this up in the way outlined below, otherwise you can not be guaranteed that -the targetEntity resolution will occur reliably:: +the targetEntity resolution will occur reliably: .. code-block:: php From 32dd7f1a0eb4455596ad099b4167cd33b487da04 Mon Sep 17 00:00:00 2001 From: Matthieu Napoli Date: Fri, 1 Mar 2013 15:16:59 +0100 Subject: [PATCH 007/332] Missing link to a cookbook in the docs The cookbook existed in the docs but there was no link to it in the docs. --- docs/en/index.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/en/index.rst b/docs/en/index.rst index 9536ee56d..69a3cacf8 100644 --- a/docs/en/index.rst +++ b/docs/en/index.rst @@ -113,7 +113,8 @@ Cookbook :doc:`Using Wakeup Or Clone ` | :doc:`Working with DateTime ` | :doc:`Validation ` | - :doc:`Entities in the Session ` + :doc:`Entities in the Session ` | + :doc:`Keeping your Modules independents ` * **Integration into Frameworks/Libraries** :doc:`CodeIgniter ` From 1846f5845cc803f6ecda8e0ddad02d73951be3f8 Mon Sep 17 00:00:00 2001 From: MDrollette Date: Fri, 1 Mar 2013 11:58:51 -0600 Subject: [PATCH 008/332] alias the proxy class on import to avoid "already exists" error --- lib/Doctrine/ORM/Proxy/ProxyFactory.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/Doctrine/ORM/Proxy/ProxyFactory.php b/lib/Doctrine/ORM/Proxy/ProxyFactory.php index f23b1063f..c8964804e 100644 --- a/lib/Doctrine/ORM/Proxy/ProxyFactory.php +++ b/lib/Doctrine/ORM/Proxy/ProxyFactory.php @@ -23,7 +23,7 @@ use Doctrine\Common\Persistence\Mapping\ClassMetadata; use Doctrine\Common\Proxy\AbstractProxyFactory; use Doctrine\Common\Proxy\ProxyDefinition; use Doctrine\Common\Util\ClassUtils; -use Doctrine\Common\Proxy\Proxy; +use Doctrine\Common\Proxy\Proxy as BaseProxy; use Doctrine\Common\Proxy\ProxyGenerator; use Doctrine\ORM\ORMInvalidArgumentException; use Doctrine\ORM\Persisters\BasicEntityPersister; @@ -116,7 +116,7 @@ class ProxyFactory extends AbstractProxyFactory private function createInitializer(ClassMetadata $classMetadata, BasicEntityPersister $entityPersister) { if ($classMetadata->getReflectionClass()->hasMethod('__wakeup')) { - return function (Proxy $proxy) use ($entityPersister, $classMetadata) { + return function (BaseProxy $proxy) use ($entityPersister, $classMetadata) { $proxy->__setInitializer(null); $proxy->__setCloner(null); @@ -141,7 +141,7 @@ class ProxyFactory extends AbstractProxyFactory }; } - return function (Proxy $proxy) use ($entityPersister, $classMetadata) { + return function (BaseProxy $proxy) use ($entityPersister, $classMetadata) { $proxy->__setInitializer(null); $proxy->__setCloner(null); @@ -177,7 +177,7 @@ class ProxyFactory extends AbstractProxyFactory */ private function createCloner(ClassMetadata $classMetadata, BasicEntityPersister $entityPersister) { - return function (Proxy $proxy) use ($entityPersister, $classMetadata) { + return function (BaseProxy $proxy) use ($entityPersister, $classMetadata) { if ($proxy->__isInitialized()) { return; } From 159daa9985edfdecd73d490f79a614baba103de2 Mon Sep 17 00:00:00 2001 From: Matthieu Napoli Date: Sat, 2 Mar 2013 18:40:58 +0100 Subject: [PATCH 009/332] Reverted incorrect typo fix -_- --- docs/en/cookbook/resolve-target-entity-listener.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/cookbook/resolve-target-entity-listener.rst b/docs/en/cookbook/resolve-target-entity-listener.rst index c77459183..71b746602 100644 --- a/docs/en/cookbook/resolve-target-entity-listener.rst +++ b/docs/en/cookbook/resolve-target-entity-listener.rst @@ -1,4 +1,4 @@ -Keeping your Modules independents +Keeping your Modules independent ================================= .. versionadded:: 2.2 From 2eff096dddf8d56469e6877fcd40b9d06ae98e61 Mon Sep 17 00:00:00 2001 From: Matthieu Napoli Date: Sat, 2 Mar 2013 18:42:38 +0100 Subject: [PATCH 010/332] Typo --- docs/en/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/index.rst b/docs/en/index.rst index 69a3cacf8..f0f94c3c1 100644 --- a/docs/en/index.rst +++ b/docs/en/index.rst @@ -114,7 +114,7 @@ Cookbook :doc:`Working with DateTime ` | :doc:`Validation ` | :doc:`Entities in the Session ` | - :doc:`Keeping your Modules independents ` + :doc:`Keeping your Modules independent ` * **Integration into Frameworks/Libraries** :doc:`CodeIgniter ` From e809f9c26666e675fea1ab350296573c8963d815 Mon Sep 17 00:00:00 2001 From: Alex Carol Date: Sun, 3 Mar 2013 23:14:58 +0100 Subject: [PATCH 011/332] Removed unnecessary Date: Wed, 6 Mar 2013 10:48:47 +0100 Subject: [PATCH 012/332] Added $isIdentifierColumn documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I've added the documentation of the argument $isIdentifierColumn since in case of foreign composite keys it doesn't  --- docs/en/reference/native-sql.rst | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/en/reference/native-sql.rst b/docs/en/reference/native-sql.rst index 6bf8b9dee..b8a91000e 100644 --- a/docs/en/reference/native-sql.rst +++ b/docs/en/reference/native-sql.rst @@ -203,11 +203,12 @@ detail: /** * Adds a meta column (foreign key or discriminator column) to the result set. * - * @param string $alias - * @param string $columnAlias - * @param string $columnName + * @param string $alias + * @param string $columnAlias + * @param string $columnName + * @param boolean $isIdentifierColumn */ - public function addMetaResult($alias, $columnAlias, $columnName) + public function addMetaResult($alias, $columnAlias, $columnName, $isIdentifierColumn = false) The first parameter is the alias of the entity result to which the meta column belongs. A meta result column (foreign key or @@ -215,6 +216,8 @@ discriminator column) always belongs to to an entity result. The second parameter is the column alias/name of the column in the SQL result set and the third parameter is the column name used in the mapping. +The fourth parameter should be set to true in case the primary key +of the entity is the foreign key you're adding. Discriminator Column ~~~~~~~~~~~~~~~~~~~~ From acc8b61cd151382ddf23967abc93b68d3f5816ac Mon Sep 17 00:00:00 2001 From: Lars Strojny Date: Mon, 26 Nov 2012 01:03:46 +0100 Subject: [PATCH 013/332] Adding EntityManagerDecorator base class as an extension point for EntityManager --- .../ORM/Decorator/EntityManagerDecorator.php | 271 ++++++++++++++++++ lib/Doctrine/ORM/EntityManager.php | 4 +- lib/Doctrine/ORM/EntityManagerInterface.php | 60 ++++ .../Decorator/EntityManagerDecoratorTest.php | 66 +++++ 4 files changed, 399 insertions(+), 2 deletions(-) create mode 100644 lib/Doctrine/ORM/Decorator/EntityManagerDecorator.php create mode 100644 lib/Doctrine/ORM/EntityManagerInterface.php create mode 100644 tests/Doctrine/Tests/ORM/Decorator/EntityManagerDecoratorTest.php diff --git a/lib/Doctrine/ORM/Decorator/EntityManagerDecorator.php b/lib/Doctrine/ORM/Decorator/EntityManagerDecorator.php new file mode 100644 index 000000000..dc123118f --- /dev/null +++ b/lib/Doctrine/ORM/Decorator/EntityManagerDecorator.php @@ -0,0 +1,271 @@ +. + */ + +namespace Doctrine\ORM\Decorator; + +use Doctrine\DBAL\LockMode; +use Doctrine\ORM\Query\ResultSetMapping; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\Common\Persistence\ObjectManagerDecorator; + +/** + * Base class for EntityManager decorators + * + * @since 2.4 + * @author Lars Strojny wrapped = $wrapped; + } + + /** + * {@inheritdoc} + */ + public function getConnection() + { + return $this->wrapped->getConnection(); + } + + /** + * {@inheritdoc} + */ + public function getExpressionBuilder() + { + return $this->wrapped->getExpressionBuilder(); + } + + /** + * {@inheritdoc} + */ + public function beginTransaction() + { + return $this->wrapped->beginTransaction(); + } + + /** + * {@inheritdoc} + */ + public function transactional($func) + { + return $this->wrapped->transactional($func); + } + + /** + * {@inheritdoc} + */ + public function commit() + { + return $this->wrapped->commit(); + } + + /** + * {@inheritdoc} + */ + public function rollback() + { + return $this->wrapped->rollback(); + } + + /** + * {@inheritdoc} + */ + public function createQuery($dql = '') + { + return $this->wrapped->createQuery($dql); + } + + /** + * {@inheritdoc} + */ + public function createNamedQuery($name) + { + return $this->wrapped->createNamedQuery($name); + } + + /** + * {@inheritdoc} + */ + public function createNativeQuery($sql, ResultSetMapping $rsm) + { + return $this->wrapped->createNativeQuery($sql, $rsm); + } + + /** + * {@inheritdoc} + */ + public function createNamedNativeQuery($name) + { + return $this->wrapped->createNamedNativeQuery($name); + } + + /** + * {@inheritdoc} + */ + public function createQueryBuilder() + { + return $this->wrapped->createQueryBuilder(); + } + + /** + * {@inheritdoc} + */ + public function getReference($entityName, $id) + { + return $this->wrapped->getReference($entityName, $id); + } + + /** + * {@inheritdoc} + */ + public function getPartialReference($entityName, $identifier) + { + return $this->wrapped->getPartialReference($entityName, $identifier); + } + + /** + * {@inheritdoc} + */ + public function close() + { + return $this->wrapped->close(); + } + + /** + * {@inheritdoc} + */ + public function copy($entity, $deep = false) + { + return $this->wrapped->copy($entity, $deep); + } + + /** + * {@inheritdoc} + */ + public function lock($entity, $lockMode, $lockVersion = null) + { + return $this->wrapped->lock($entity, $lockMode, $lockVersion); + } + + /** + * {@inheritdoc} + */ + public function find($entityName, $id, $lockMode = LockMode::NONE, $lockVersion = null) + { + return $this->wrapped->find($entityName, $id, $lockMode, $lockVersion); + } + + /** + * {@inheritdoc} + */ + public function flush($entity = null) + { + return $this->wrapped->flush($entity); + } + + /** + * {@inheritdoc} + */ + public function getEventManager() + { + return $this->wrapped->getEventManager(); + } + + /** + * {@inheritdoc} + */ + public function getConfiguration() + { + return $this->wrapped->getConfiguration(); + } + + /** + * {@inheritdoc} + */ + public function isOpen() + { + return $this->wrapped->isOpen(); + } + + /** + * {@inheritdoc} + */ + public function getUnitOfWork() + { + return $this->wrapped->getUnitOfWork(); + } + + /** + * {@inheritdoc} + */ + public function getHydrator($hydrationMode) + { + return $this->wrapped->getHydrator($hydrationMode); + } + + /** + * {@inheritdoc} + */ + public function newHydrator($hydrationMode) + { + return $this->wrapped->newHydrator($hydrationMode); + } + + /** + * {@inheritdoc} + */ + public function getProxyFactory() + { + return $this->wrapped->getProxyFactory(); + } + + /** + * {@inheritdoc} + */ + public function getFilters() + { + return $this->wrapped->getFilters(); + } + + /** + * {@inheritdoc} + */ + public function isFiltersStateClean() + { + return $this->wrapped->isFiltersStateClean(); + } + + /** + * {@inheritdoc} + */ + public function hasFilters() + { + return $this->wrapped->hasFilters(); + } +} diff --git a/lib/Doctrine/ORM/EntityManager.php b/lib/Doctrine/ORM/EntityManager.php index 4da4abed4..53cbf1926 100644 --- a/lib/Doctrine/ORM/EntityManager.php +++ b/lib/Doctrine/ORM/EntityManager.php @@ -40,7 +40,7 @@ use Doctrine\Common\Util\ClassUtils; * @author Jonathan Wage * @author Roman Borschel */ -class EntityManager implements ObjectManager +/* final */class EntityManager implements EntityManagerInterface { /** * The used Configuration. @@ -286,7 +286,7 @@ class EntityManager implements ObjectManager * * @return \Doctrine\ORM\Query */ - public function createQuery($dql = "") + public function createQuery($dql = '') { $query = new Query($this); diff --git a/lib/Doctrine/ORM/EntityManagerInterface.php b/lib/Doctrine/ORM/EntityManagerInterface.php new file mode 100644 index 000000000..d72f7cd0c --- /dev/null +++ b/lib/Doctrine/ORM/EntityManagerInterface.php @@ -0,0 +1,60 @@ +. + */ + +namespace Doctrine\ORM; + +use Doctrine\Common\Persistence\ObjectManager; +use Doctrine\DBAL\LockMode; +use Doctrine\ORM\Query\ResultSetMapping; + +/** + * EntityManager interface + * + * @since 2.4 + * @author Lars Strojny wrapped = $this->getMock('Doctrine\ORM\EntityManagerInterface'); + $this->decorator = $this->getMockBuilder('Doctrine\ORM\Decorator\EntityManagerDecorator') + ->setConstructorArgs(array($this->wrapped)) + ->setMethods(null) + ->getMock(); + } + + public function getMethodParameters() + { + $class = new \ReflectionClass('Doctrine\ORM\EntityManager'); + + $methods = array(); + foreach ($class->getMethods() as $method) { + if ($method->isConstructor() || $method->isStatic() || !$method->isPublic()) { + continue; + } + + /** Special case EntityManager::createNativeQuery() */ + if ($method->getName() === 'createNativeQuery') { + $methods[] = array($method->getName(), array('name', new ResultSetMapping())); + continue; + } + + if ($method->getNumberOfRequiredParameters() === 0) { + $methods[] = array($method->getName(), array()); + } elseif ($method->getNumberOfRequiredParameters() > 0) { + $methods[] = array($method->getName(), array_fill(0, $method->getNumberOfRequiredParameters(), 'req') ?: array()); + } + if ($method->getNumberOfParameters() != $method->getNumberOfRequiredParameters()) { + $methods[] = array($method->getName(), array_fill(0, $method->getNumberOfParameters(), 'all') ?: array()); + } + } + + return $methods; + } + + /** + * @dataProvider getMethodParameters + */ + public function testAllMethodCallsAreDelegatedToTheWrappedInstance($method, array $parameters) + { + $stub = $this->wrapped + ->expects($this->once()) + ->method($method) + ->will($this->returnValue('INNER VALUE FROM ' . $method)); + + call_user_func_array(array($stub, 'with'), $parameters); + + $this->assertSame('INNER VALUE FROM ' . $method, call_user_func_array(array($this->decorator, $method), $parameters)); + } +} From 760623346c9423dac76ff541d81066018ee28035 Mon Sep 17 00:00:00 2001 From: Jan Kramer Date: Tue, 5 Mar 2013 15:02:20 +0100 Subject: [PATCH 014/332] Added 'contains' comparison --- composer.json | 1 + composer.lock | 58 ++++++++++++------- .../ORM/Persisters/BasicEntityPersister.php | 19 +++--- .../ORM/Persisters/SqlValueVisitor.php | 18 +++++- .../ORM/Functional/EntityRepositoryTest.php | 16 +++++ 5 files changed, 80 insertions(+), 32 deletions(-) diff --git a/composer.json b/composer.json index 2c00492dd..89d62f2f6 100644 --- a/composer.json +++ b/composer.json @@ -15,6 +15,7 @@ "require": { "php": ">=5.3.2", "ext-pdo": "*", + "doctrine/collections": "~1.1", "doctrine/dbal": ">=2.4-dev,<2.5-dev", "symfony/console": "2.*" }, diff --git a/composer.lock b/composer.lock index 6b3fcb3d0..23205bf9a 100644 --- a/composer.lock +++ b/composer.lock @@ -1,5 +1,5 @@ { - "hash": "eff8840dfb1a83e6e1aef32b8031ac7c", + "hash": "acae8c9f654bd03696631d5eac8d16ad", "packages": [ { "name": "doctrine/annotations", @@ -22,7 +22,6 @@ "require-dev": { "doctrine/cache": "1.*" }, - "time": "2013-01-12 19:23:32", "type": "library", "autoload": { "psr-0": { @@ -65,7 +64,8 @@ "annotations", "docblock", "parser" - ] + ], + "time": "2013-01-12 19:23:32" }, { "name": "doctrine/cache", @@ -84,7 +84,6 @@ "require": { "php": ">=5.3.2" }, - "time": "2013-01-10 22:43:46", "type": "library", "autoload": { "psr-0": { @@ -126,27 +125,32 @@ "keywords": [ "cache", "caching" - ] + ], + "time": "2013-01-10 22:43:46" }, { "name": "doctrine/collections", - "version": "v1.0", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/doctrine/collections.git", - "reference": "v1.0" + "reference": "560f29c39cfcfbcd210e5d549d993a39d898b04b" }, "dist": { "type": "zip", - "url": "https://github.com/doctrine/collections/archive/v1.0.zip", - "reference": "v1.0", + "url": "https://api.github.com/repos/doctrine/collections/zipball/560f29c39cfcfbcd210e5d549d993a39d898b04b", + "reference": "560f29c39cfcfbcd210e5d549d993a39d898b04b", "shasum": "" }, "require": { "php": ">=5.3.2" }, - "time": "2013-01-12 16:36:50", "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, "autoload": { "psr-0": { "Doctrine\\Common\\Collections\\": "lib/" @@ -188,7 +192,8 @@ "array", "collections", "iterator" - ] + ], + "time": "2013-03-07 12:15:54" }, { "name": "doctrine/common", @@ -212,7 +217,6 @@ "doctrine/lexer": "1.*", "php": ">=5.3.2" }, - "time": "2013-01-29 12:48:56", "type": "library", "extra": { "branch-alias": { @@ -262,7 +266,8 @@ "eventmanager", "persistence", "spl" - ] + ], + "time": "2013-01-29 12:48:56" }, { "name": "doctrine/dbal", @@ -288,7 +293,6 @@ "suggest": { "symfony/console": "For helpful console commands such as SQL execution and import of files." }, - "time": "2013-02-09 23:28:29", "type": "library", "extra": { "branch-alias": { @@ -331,7 +335,8 @@ "dbal", "persistence", "queryobject" - ] + ], + "time": "2013-02-09 23:28:29" }, { "name": "doctrine/inflector", @@ -350,7 +355,6 @@ "require": { "php": ">=5.3.2" }, - "time": "2013-01-10 21:49:15", "type": "library", "autoload": { "psr-0": { @@ -394,7 +398,8 @@ "pluarlize", "singuarlize", "string" - ] + ], + "time": "2013-01-10 21:49:15" }, { "name": "doctrine/lexer", @@ -413,7 +418,6 @@ "require": { "php": ">=5.3.2" }, - "time": "2013-01-12 18:59:04", "type": "library", "autoload": { "psr-0": { @@ -446,7 +450,8 @@ "keywords": [ "lexer", "parser" - ] + ], + "time": "2013-01-12 18:59:04" }, { "name": "symfony/console", @@ -466,7 +471,6 @@ "require": { "php": ">=5.3.3" }, - "time": "2013-01-31 21:39:01", "type": "library", "extra": { "branch-alias": { @@ -493,15 +497,25 @@ } ], "description": "Symfony Console Component", - "homepage": "http://symfony.com" + "homepage": "http://symfony.com", + "time": "2013-01-31 21:39:01" } ], - "packages-dev": null, + "packages-dev": [ + + ], "aliases": [ ], "minimum-stability": "dev", "stability-flags": [ + ], + "platform": { + "php": ">=5.3.2", + "ext-pdo": "*" + }, + "platform-dev": [ + ] } diff --git a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php index d5543dad4..649ad2e8e 100644 --- a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php @@ -84,15 +84,16 @@ class BasicEntityPersister * @var array */ static private $comparisonMap = array( - Comparison::EQ => '= %s', - Comparison::IS => 'IS %s', - Comparison::NEQ => '!= %s', - Comparison::GT => '> %s', - Comparison::GTE => '>= %s', - Comparison::LT => '< %s', - Comparison::LTE => '<= %s', - Comparison::IN => 'IN (%s)', - Comparison::NIN => 'NOT IN (%s)', + Comparison::EQ => '= %s', + Comparison::IS => 'IS %s', + Comparison::NEQ => '!= %s', + Comparison::GT => '> %s', + Comparison::GTE => '>= %s', + Comparison::LT => '< %s', + Comparison::LTE => '<= %s', + Comparison::IN => 'IN (%s)', + Comparison::NIN => 'NOT IN (%s)', + Comparison::CONTAINS => 'LIKE %s', ); /** diff --git a/lib/Doctrine/ORM/Persisters/SqlValueVisitor.php b/lib/Doctrine/ORM/Persisters/SqlValueVisitor.php index 1d805c041..79e5150e5 100644 --- a/lib/Doctrine/ORM/Persisters/SqlValueVisitor.php +++ b/lib/Doctrine/ORM/Persisters/SqlValueVisitor.php @@ -50,7 +50,7 @@ class SqlValueVisitor extends ExpressionVisitor */ public function walkComparison(Comparison $comparison) { - $value = $comparison->getValue()->getValue(); + $value = $this->getValueFromComparison($comparison); $field = $comparison->getField(); $this->values[] = $value; @@ -92,4 +92,20 @@ class SqlValueVisitor extends ExpressionVisitor { return array($this->values, $this->types); } + + /** + * Returns the value from a Comparison. In case of a CONTAINS comparison, + * the value is wrapped in %-signs, because it will be used in a LIKE clause. + * + * @param \Doctrine\Common\Collections\Expr\Comparison $comparison + * @return mixed + */ + protected function getValueFromComparison(Comparison $comparison) + { + $value = $comparison->getValue()->getValue(); + + return $comparison->getOperator() == Comparison::CONTAINS + ? "%{$value}%" + : $value; + } } diff --git a/tests/Doctrine/Tests/ORM/Functional/EntityRepositoryTest.php b/tests/Doctrine/Tests/ORM/Functional/EntityRepositoryTest.php index 5be4615fb..7874435bd 100644 --- a/tests/Doctrine/Tests/ORM/Functional/EntityRepositoryTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/EntityRepositoryTest.php @@ -781,6 +781,22 @@ class EntityRepositoryTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertEquals(4, count($users)); } + public function testMatchingCriteriaContainsComparison() + { + $this->loadFixture(); + + $repository = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsUser'); + + $users = $repository->matching(new Criteria(Criteria::expr()->contains('name', 'Foobar'))); + $this->assertEquals(0, count($users)); + + $users = $repository->matching(new Criteria(Criteria::expr()->contains('name', 'Rom'))); + $this->assertEquals(1, count($users)); + + $users = $repository->matching(new Criteria(Criteria::expr()->contains('status', 'dev'))); + $this->assertEquals(2, count($users)); + } + /** * @group DDC-2055 */ From 9d5e7eb6e9f8f422f3d7cb46d23f1ce50b0250d7 Mon Sep 17 00:00:00 2001 From: Jan Kramer Date: Thu, 7 Mar 2013 14:12:10 +0100 Subject: [PATCH 015/332] Added composer.lock to .gitignore --- .gitignore | 1 + composer.lock | 521 -------------------------------------------------- 2 files changed, 1 insertion(+), 521 deletions(-) delete mode 100644 composer.lock diff --git a/.gitignore b/.gitignore index 240207aee..490413d15 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ lib/Doctrine/DBAL .project .idea vendor/ +composer.lock diff --git a/composer.lock b/composer.lock deleted file mode 100644 index 23205bf9a..000000000 --- a/composer.lock +++ /dev/null @@ -1,521 +0,0 @@ -{ - "hash": "acae8c9f654bd03696631d5eac8d16ad", - "packages": [ - { - "name": "doctrine/annotations", - "version": "v1.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/annotations.git", - "reference": "v1.0" - }, - "dist": { - "type": "zip", - "url": "https://github.com/doctrine/annotations/archive/v1.0.zip", - "reference": "v1.0", - "shasum": "" - }, - "require": { - "doctrine/lexer": "1.*", - "php": ">=5.3.2" - }, - "require-dev": { - "doctrine/cache": "1.*" - }, - "type": "library", - "autoload": { - "psr-0": { - "Doctrine\\Common\\Annotations\\": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com", - "homepage": "http://www.jwage.com/" - }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com", - "homepage": "http://www.instaclick.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com", - "homepage": "https://github.com/schmittjoh", - "role": "Developer of wrapped JMSSerializerBundle" - } - ], - "description": "Docblock Annotations Parser", - "homepage": "http://www.doctrine-project.org", - "keywords": [ - "annotations", - "docblock", - "parser" - ], - "time": "2013-01-12 19:23:32" - }, - { - "name": "doctrine/cache", - "version": "v1.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/cache.git", - "reference": "v1.0" - }, - "dist": { - "type": "zip", - "url": "https://github.com/doctrine/cache/archive/v1.0.zip", - "reference": "v1.0", - "shasum": "" - }, - "require": { - "php": ">=5.3.2" - }, - "type": "library", - "autoload": { - "psr-0": { - "Doctrine\\Common\\Cache\\": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com", - "homepage": "http://www.jwage.com/" - }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com", - "homepage": "http://www.instaclick.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com", - "homepage": "https://github.com/schmittjoh", - "role": "Developer of wrapped JMSSerializerBundle" - } - ], - "description": "Caching library offering an object-oriented API for many cache backends", - "homepage": "http://www.doctrine-project.org", - "keywords": [ - "cache", - "caching" - ], - "time": "2013-01-10 22:43:46" - }, - { - "name": "doctrine/collections", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/doctrine/collections.git", - "reference": "560f29c39cfcfbcd210e5d549d993a39d898b04b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/collections/zipball/560f29c39cfcfbcd210e5d549d993a39d898b04b", - "reference": "560f29c39cfcfbcd210e5d549d993a39d898b04b", - "shasum": "" - }, - "require": { - "php": ">=5.3.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1.x-dev" - } - }, - "autoload": { - "psr-0": { - "Doctrine\\Common\\Collections\\": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com", - "homepage": "http://www.jwage.com/" - }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com", - "homepage": "http://www.instaclick.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com", - "homepage": "https://github.com/schmittjoh", - "role": "Developer of wrapped JMSSerializerBundle" - } - ], - "description": "Collections Abstraction library", - "homepage": "http://www.doctrine-project.org", - "keywords": [ - "array", - "collections", - "iterator" - ], - "time": "2013-03-07 12:15:54" - }, - { - "name": "doctrine/common", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/doctrine/common", - "reference": "53859ae1c84ccf1a5aa58c8379c69cd9adedf03a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/common/zipball/53859ae1c84ccf1a5aa58c8379c69cd9adedf03a", - "reference": "53859ae1c84ccf1a5aa58c8379c69cd9adedf03a", - "shasum": "" - }, - "require": { - "doctrine/annotations": "1.*", - "doctrine/cache": "1.*", - "doctrine/collections": "1.*", - "doctrine/inflector": "1.*", - "doctrine/lexer": "1.*", - "php": ">=5.3.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.4.x-dev" - } - }, - "autoload": { - "psr-0": { - "Doctrine\\Common\\": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com", - "homepage": "http://www.jwage.com/" - }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com", - "homepage": "http://www.instaclick.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com", - "homepage": "https://github.com/schmittjoh", - "role": "Developer of wrapped JMSSerializerBundle" - } - ], - "description": "Common Library for Doctrine projects", - "homepage": "http://www.doctrine-project.org", - "keywords": [ - "annotations", - "collections", - "eventmanager", - "persistence", - "spl" - ], - "time": "2013-01-29 12:48:56" - }, - { - "name": "doctrine/dbal", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/doctrine/dbal", - "reference": "eb6ee9a86421ba534d7c5514b190d1d06b30d4b1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/eb6ee9a86421ba534d7c5514b190d1d06b30d4b1", - "reference": "eb6ee9a86421ba534d7c5514b190d1d06b30d4b1", - "shasum": "" - }, - "require": { - "doctrine/common": "2.4.x-dev", - "php": ">=5.3.2" - }, - "require-dev": { - "symfony/console": "2.*" - }, - "suggest": { - "symfony/console": "For helpful console commands such as SQL execution and import of files." - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.4.x-dev" - } - }, - "autoload": { - "psr-0": { - "Doctrine\\DBAL\\": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com", - "homepage": "http://www.jwage.com/" - }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com", - "homepage": "http://www.instaclick.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - } - ], - "description": "Database Abstraction Layer", - "homepage": "http://www.doctrine-project.org", - "keywords": [ - "database", - "dbal", - "persistence", - "queryobject" - ], - "time": "2013-02-09 23:28:29" - }, - { - "name": "doctrine/inflector", - "version": "v1.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/inflector.git", - "reference": "v1.0" - }, - "dist": { - "type": "zip", - "url": "https://github.com/doctrine/inflector/archive/v1.0.zip", - "reference": "v1.0", - "shasum": "" - }, - "require": { - "php": ">=5.3.2" - }, - "type": "library", - "autoload": { - "psr-0": { - "Doctrine\\Common\\Inflector\\": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com", - "homepage": "http://www.jwage.com/" - }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com", - "homepage": "http://www.instaclick.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com", - "homepage": "https://github.com/schmittjoh", - "role": "Developer of wrapped JMSSerializerBundle" - } - ], - "description": "Common String Manipulations with regard to casing and singular/plural rules.", - "homepage": "http://www.doctrine-project.org", - "keywords": [ - "inflection", - "pluarlize", - "singuarlize", - "string" - ], - "time": "2013-01-10 21:49:15" - }, - { - "name": "doctrine/lexer", - "version": "v1.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/lexer.git", - "reference": "v1.0" - }, - "dist": { - "type": "zip", - "url": "https://github.com/doctrine/lexer/archive/v1.0.zip", - "reference": "v1.0", - "shasum": "" - }, - "require": { - "php": ">=5.3.2" - }, - "type": "library", - "autoload": { - "psr-0": { - "Doctrine\\Common\\Lexer\\": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com", - "homepage": "http://www.instaclick.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com", - "homepage": "https://github.com/schmittjoh", - "role": "Developer of wrapped JMSSerializerBundle" - } - ], - "description": "Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.", - "homepage": "http://www.doctrine-project.org", - "keywords": [ - "lexer", - "parser" - ], - "time": "2013-01-12 18:59:04" - }, - { - "name": "symfony/console", - "version": "dev-master", - "target-dir": "Symfony/Component/Console", - "source": { - "type": "git", - "url": "https://github.com/symfony/Console", - "reference": "f65e34d058f0990a724f78e8d091dc0a20e439ac" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/Console/zipball/f65e34d058f0990a724f78e8d091dc0a20e439ac", - "reference": "f65e34d058f0990a724f78e8d091dc0a20e439ac", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.3-dev" - } - }, - "autoload": { - "psr-0": { - "Symfony\\Component\\Console\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - } - ], - "description": "Symfony Console Component", - "homepage": "http://symfony.com", - "time": "2013-01-31 21:39:01" - } - ], - "packages-dev": [ - - ], - "aliases": [ - - ], - "minimum-stability": "dev", - "stability-flags": [ - - ], - "platform": { - "php": ">=5.3.2", - "ext-pdo": "*" - }, - "platform-dev": [ - - ] -} From a2cd0f5804a6a76d6e9e39fc40084a286e5ccdfc Mon Sep 17 00:00:00 2001 From: Pascal Borreli Date: Mon, 11 Mar 2013 00:08:58 +0000 Subject: [PATCH 016/332] Fixed typos --- UPGRADE.md | 8 ++--- docs/en/cookbook/entities-in-session.rst | 2 +- docs/en/reference/faq.rst | 2 +- .../reference/working-with-associations.rst | 2 +- docs/en/tutorials/composite-primary-keys.rst | 4 +-- lib/Doctrine/ORM/AbstractQuery.php | 2 +- lib/Doctrine/ORM/Events.php | 2 +- .../Internal/Hydration/AbstractHydrator.php | 8 ++--- .../Internal/Hydration/HydrationException.php | 2 +- .../Mapping/Builder/AssociationBuilder.php | 2 +- .../Mapping/Builder/ClassMetadataBuilder.php | 22 ++++++------ .../ORM/Mapping/ClassMetadataFactory.php | 4 +-- .../ORM/Mapping/ClassMetadataInfo.php | 20 +++++------ .../Mapping/DefaultEntityListenerResolver.php | 2 +- .../ORM/Mapping/Driver/AnnotationDriver.php | 4 +-- .../ORM/Mapping/Driver/DatabaseDriver.php | 6 ++-- lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php | 2 +- lib/Doctrine/ORM/Mapping/MappingException.php | 12 +++---- .../ORM/ORMInvalidArgumentException.php | 2 +- lib/Doctrine/ORM/OptimisticLockException.php | 2 +- lib/Doctrine/ORM/PersistentCollection.php | 2 +- .../ORM/Persisters/BasicEntityPersister.php | 8 ++--- .../Persisters/JoinedSubclassPersister.php | 12 +++---- .../ORM/Persisters/SingleTablePersister.php | 2 +- lib/Doctrine/ORM/Query.php | 2 +- lib/Doctrine/ORM/Query/Expr.php | 4 +-- .../ORM/Query/ParameterTypeInferer.php | 2 +- lib/Doctrine/ORM/Query/Parser.php | 16 ++++----- lib/Doctrine/ORM/Query/QueryException.php | 10 +++--- lib/Doctrine/ORM/Query/SqlWalker.php | 12 +++---- lib/Doctrine/ORM/Query/TreeWalkerAdapter.php | 2 +- .../Console/Command/ConvertMappingCommand.php | 4 +-- .../Command/GenerateEntitiesCommand.php | 2 +- lib/Doctrine/ORM/Tools/SchemaTool.php | 4 +-- lib/Doctrine/ORM/Tools/ToolEvents.php | 2 +- lib/Doctrine/ORM/UnitOfWork.php | 4 +-- phpunit.xml.dist | 2 +- run-all.sh | 2 +- .../Tests/Models/StockExchange/Bond.php | 4 +-- .../ORM/Functional/AdvancedDqlQueryTest.php | 2 +- .../ORM/Functional/BasicFunctionalTest.php | 2 +- .../Functional/ClassTableInheritanceTest.php | 2 +- .../Functional/CompositePrimaryKeyTest.php | 2 +- .../ORM/Functional/DefaultValuesTest.php | 2 +- .../ORM/Functional/EntityRepositoryTest.php | 4 +-- .../Functional/ExtraLazyCollectionTest.php | 2 +- .../ORM/Functional/IndexByAssociationTest.php | 20 +++++------ .../Tests/ORM/Functional/Locking/LockTest.php | 2 +- ...nyToManySelfReferentialAssociationTest.php | 2 +- .../Tests/ORM/Functional/NewOperatorTest.php | 2 +- ...OneToManyUnidirectionalAssociationTest.php | 2 +- .../Functional/OneToOneOrphanRemovalTest.php | 2 +- .../Functional/SingleTableInheritanceTest.php | 6 ++-- .../ORM/Functional/Ticket/DDC117Test.php | 4 +-- .../ORM/Functional/Ticket/DDC142Test.php | 2 +- .../ORM/Functional/Ticket/DDC1526Test.php | 2 +- .../ORM/Functional/Ticket/DDC1719Test.php | 4 +-- .../ORM/Functional/Ticket/DDC1843Test.php | 6 ++-- .../ORM/Functional/Ticket/DDC1885Test.php | 4 +-- .../ORM/Functional/Ticket/DDC501Test.php | 2 +- .../ORM/Functional/Ticket/DDC599Test.php | 2 +- .../ORM/Functional/Ticket/DDC618Test.php | 6 ++-- .../ORM/Functional/Ticket/DDC758Test.php | 2 +- .../ORM/Functional/Ticket/DDC933Test.php | 2 +- .../ORM/Mapping/AbstractMappingDriverTest.php | 10 +++--- .../ORM/Mapping/AnnotationDriverTest.php | 2 +- .../Mapping/BasicInheritanceMappingTest.php | 34 +++++++++---------- .../ORM/Mapping/ClassMetadataFactoryTest.php | 4 +-- .../Tests/ORM/Mapping/ClassMetadataTest.php | 14 ++++---- .../Performance/HydrationPerformanceTest.php | 2 +- .../ORM/Query/SelectSqlGenerationTest.php | 2 +- .../ORM/Query/UpdateSqlGenerationTest.php | 4 +-- .../AbstractClassMetadataExporterTest.php | 2 +- .../Tests/ORM/Tools/SchemaToolTest.php | 2 +- .../Doctrine/Tests/OrmFunctionalTestCase.php | 2 +- tests/NativePhpunitTask.php | 2 +- tests/dbproperties.xml.dev | 2 +- 77 files changed, 188 insertions(+), 188 deletions(-) diff --git a/UPGRADE.md b/UPGRADE.md index dacc3ffd6..e80aa55ad 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -126,7 +126,7 @@ from 2.0 have to configure the annotation driver if they don't use `Configuratio $config->setMetadataDriverImpl($driver); -## Scalar mappings can now be ommitted from DQL result +## Scalar mappings can now be omitted from DQL result You are now allowed to mark scalar SELECT expressions as HIDDEN an they are not hydrated anymore. Example: @@ -307,7 +307,7 @@ them for batch updates like SchemaTool and other commands. However the annotations driver being a default driver does not really help that much anyways. -Therefore we decided to break backwards compability in this issue and drop +Therefore we decided to break backwards compatibility in this issue and drop the support for Annotations as Default Driver and require our users to specify the driver explicitly (which allows us to ask for the path to all entities). @@ -366,7 +366,7 @@ apologize for the inconvenience. ## Default Property for Field Mappings The "default" option for database column defaults has been removed. If desired, database column defaults can -be implemented by using the columnDefinition attribute of the @Column annotation (or the approriate XML and YAML equivalents). +be implemented by using the columnDefinition attribute of the @Column annotation (or the appropriate XML and YAML equivalents). Prefer PHP default values, if possible. ## Selecting Partial Objects @@ -451,7 +451,7 @@ With new required method AbstractTask::buildDocumentation, its implementation de * "doctrine schema-tool --drop" now always drops the complete database instead of only those tables defined by the current database model. The previous method had - problems when foreign keys of orphaned tables pointed to tables that were schedulded + problems when foreign keys of orphaned tables pointed to tables that were scheduled for deletion. * Use "doctrine schema-tool --update" to get a save incremental update for your database schema without deleting any unused tables, sequences or foreign keys. diff --git a/docs/en/cookbook/entities-in-session.rst b/docs/en/cookbook/entities-in-session.rst index 2fd771e5d..664cff53f 100644 --- a/docs/en/cookbook/entities-in-session.rst +++ b/docs/en/cookbook/entities-in-session.rst @@ -64,5 +64,5 @@ object or implement the __sleep() magic method on your entity. When you called detach on your objects they get "unmanaged" with that entity manager. This means you cannot use them as part of write operations - during ``EntityManagr#flush()`` anymore in this request. + during ``EntityManager#flush()`` anymore in this request. diff --git a/docs/en/reference/faq.rst b/docs/en/reference/faq.rst index 5d57c24f7..800b168bd 100644 --- a/docs/en/reference/faq.rst +++ b/docs/en/reference/faq.rst @@ -162,7 +162,7 @@ is supposed to kick-start you, but not towards 100%. Why does the EntityGenerator not generate inheritance correctly? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Just from the details of the discriminator map the EntityGenerator cannot guess the inheritance hierachy. +Just from the details of the discriminator map the EntityGenerator cannot guess the inheritance hierarchy. This is why the generation of inherited entities does not fully work. You have to adjust some additional code to get this one working correctly. diff --git a/docs/en/reference/working-with-associations.rst b/docs/en/reference/working-with-associations.rst index 253edad7e..7f46eba6a 100644 --- a/docs/en/reference/working-with-associations.rst +++ b/docs/en/reference/working-with-associations.rst @@ -689,7 +689,7 @@ interchangeably, independent of in-memory or sql-backed collections. */ public function setMaxResults($maxResults); public function getOrderings(); - public function getWhereExpresion(); + public function getWhereExpression(); public function getFirstResult(); public function getMaxResults(); } diff --git a/docs/en/tutorials/composite-primary-keys.rst b/docs/en/tutorials/composite-primary-keys.rst index 82a5210cb..dd4e49e04 100644 --- a/docs/en/tutorials/composite-primary-keys.rst +++ b/docs/en/tutorials/composite-primary-keys.rst @@ -19,7 +19,7 @@ the ID fields have to have their values set before you call ``EntityManager#pers Primitive Types only ~~~~~~~~~~~~~~~~~~~~ -Even in version 2.0 you can have composite keys as long as they only consist of the primative types +Even in version 2.0 you can have composite keys as long as they only consist of the primitive types ``integer`` and ``string``. Suppose you want to create a database of cars and use the model-name and year of production as primary keys: @@ -129,7 +129,7 @@ of one or many parent entities. - Dynamic Attributes of an Entity (for example Article). Each Article has many attributes with primary key "article_id" and "attribute_name". -- Address object of a Person, the primary key of the adress is "user_id". This is not a case of a composite primary +- Address object of a Person, the primary key of the address is "user_id". This is not a case of a composite primary key, but the identity is derived through a foreign entity and a foreign key. - Join Tables with metadata can be modelled as Entity, for example connections between two articles with a little description and a score. diff --git a/lib/Doctrine/ORM/AbstractQuery.php b/lib/Doctrine/ORM/AbstractQuery.php index be1a0a1fb..52f6ef507 100644 --- a/lib/Doctrine/ORM/AbstractQuery.php +++ b/lib/Doctrine/ORM/AbstractQuery.php @@ -386,7 +386,7 @@ abstract class AbstractQuery } /** - * Defines a cache driver to be used for caching result sets and implictly enables caching. + * Defines a cache driver to be used for caching result sets and implicitly enables caching. * * @param \Doctrine\Common\Cache\Cache|null $resultCacheDriver Cache driver * diff --git a/lib/Doctrine/ORM/Events.php b/lib/Doctrine/ORM/Events.php index 28fdcd9a9..8c13fa2d5 100644 --- a/lib/Doctrine/ORM/Events.php +++ b/lib/Doctrine/ORM/Events.php @@ -122,7 +122,7 @@ final class Events /** * The preFlush event occurs when the EntityManager#flush() operation is invoked, - * but before any changes to managed entites have been calculated. This event is + * but before any changes to managed entities have been calculated. This event is * always raised right after EntityManager#flush() call. */ const preFlush = 'preFlush'; diff --git a/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php index 1254bacb4..afd4b41a1 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php @@ -168,7 +168,7 @@ abstract class AbstractHydrator } /** - * Excutes one-time preparation tasks, once each time hydration is started + * Executes one-time preparation tasks, once each time hydration is started * through {@link hydrateAll} or {@link iterate()}. * * @return void @@ -178,7 +178,7 @@ abstract class AbstractHydrator } /** - * Excutes one-time cleanup tasks at the end of a hydration that was initiated + * Executes one-time cleanup tasks at the end of a hydration that was initiated * through {@link hydrateAll} or {@link iterate()}. * * @return void @@ -223,7 +223,7 @@ abstract class AbstractHydrator * Puts the elements of a result row into a new array, grouped by the dql alias * they belong to. The column names in the result set are mapped to their * field names during this procedure as well as any necessary conversions on - * the values applied. Scalar values are kept in a specfic key 'scalars'. + * the values applied. Scalar values are kept in a specific key 'scalars'. * * @param array $data SQL Result Row. * @param array &$cache Cache for column to field result information. @@ -321,7 +321,7 @@ abstract class AbstractHydrator } // in an inheritance hierarchy the same field could be defined several times. - // We overwrite this value so long we dont have a non-null value, that value we keep. + // We overwrite this value so long we don't have a non-null value, that value we keep. // Per definition it cannot be that a field is defined several times and has several values. if (isset($rowData[$dqlAlias][$cache[$key]['fieldName']]) && $value === null) { continue; diff --git a/lib/Doctrine/ORM/Internal/Hydration/HydrationException.php b/lib/Doctrine/ORM/Internal/Hydration/HydrationException.php index 496942e94..c1c13c1f7 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/HydrationException.php +++ b/lib/Doctrine/ORM/Internal/Hydration/HydrationException.php @@ -49,7 +49,7 @@ class HydrationException extends \Doctrine\ORM\ORMException public static function emptyDiscriminatorValue($dqlAlias) { return new self("The DQL alias '" . $dqlAlias . "' contains an entity ". - "of an inheritance hierachy with an empty discriminator value. This means " . + "of an inheritance hierarchy with an empty discriminator value. This means " . "that the database contains inconsistent data with an empty " . "discriminator value in a table row." ); diff --git a/lib/Doctrine/ORM/Mapping/Builder/AssociationBuilder.php b/lib/Doctrine/ORM/Mapping/Builder/AssociationBuilder.php index 93179a406..942a662e4 100644 --- a/lib/Doctrine/ORM/Mapping/Builder/AssociationBuilder.php +++ b/lib/Doctrine/ORM/Mapping/Builder/AssociationBuilder.php @@ -200,7 +200,7 @@ class AssociationBuilder } else if ($this->type == ClassMetadata::ONE_TO_ONE) { $cm->mapOneToOne($mapping); } else { - throw new \InvalidArgumentException("Type should be a ToOne Assocation here"); + throw new \InvalidArgumentException("Type should be a ToOne Association here"); } return $this->builder; } diff --git a/lib/Doctrine/ORM/Mapping/Builder/ClassMetadataBuilder.php b/lib/Doctrine/ORM/Mapping/Builder/ClassMetadataBuilder.php index a634f1678..774c9e06e 100644 --- a/lib/Doctrine/ORM/Mapping/Builder/ClassMetadataBuilder.php +++ b/lib/Doctrine/ORM/Mapping/Builder/ClassMetadataBuilder.php @@ -163,7 +163,7 @@ class ClassMetadataBuilder } /** - * Sets class as root of a joined table inheritance hierachy. + * Sets class as root of a joined table inheritance hierarchy. * * @return ClassMetadataBuilder */ @@ -175,7 +175,7 @@ class ClassMetadataBuilder } /** - * Sets class as root of a single table inheritance hierachy. + * Sets class as root of a single table inheritance hierarchy. * * @return ClassMetadataBuilder */ @@ -207,7 +207,7 @@ class ClassMetadataBuilder } /** - * Adds a subclass to this inheritance hierachy. + * Adds a subclass to this inheritance hierarchy. * * @param string $name * @param string $class @@ -319,7 +319,7 @@ class ClassMetadataBuilder } /** - * Creates a ManyToOne Assocation Builder. + * Creates a ManyToOne Association Builder. * * Note: This method does not add the association, you have to call build() on the AssociationBuilder. * @@ -361,7 +361,7 @@ class ClassMetadataBuilder } /** - * Adds simple inverse one-to-one assocation. + * Adds simple inverse one-to-one association. * * @param string $name * @param string $targetEntity @@ -378,7 +378,7 @@ class ClassMetadataBuilder } /** - * Adds simple owning one-to-one assocation. + * Adds simple owning one-to-one association. * * @param string $name * @param string $targetEntity @@ -398,7 +398,7 @@ class ClassMetadataBuilder } /** - * Creates a ManyToMany Assocation Builder. + * Creates a ManyToMany Association Builder. * * @param string $name * @param string $targetEntity @@ -418,7 +418,7 @@ class ClassMetadataBuilder } /** - * Adds a simple owning many to many assocation. + * Adds a simple owning many to many association. * * @param string $name * @param string $targetEntity @@ -438,7 +438,7 @@ class ClassMetadataBuilder } /** - * Adds a simple inverse many to many assocation. + * Adds a simple inverse many to many association. * * @param string $name * @param string $targetEntity @@ -455,7 +455,7 @@ class ClassMetadataBuilder } /** - * Creates a one to many assocation builder. + * Creates a one to many association builder. * * @param string $name * @param string $targetEntity @@ -475,7 +475,7 @@ class ClassMetadataBuilder } /** - * Adds simple OneToMany assocation. + * Adds simple OneToMany association. * * @param string $name * @param string $targetEntity diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php index 4ea082d31..242a9099f 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php @@ -193,7 +193,7 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory } $class->validateIdentifier(); - $class->validateAssocations(); + $class->validateAssociations(); $class->validateLifecycleCallbacks($this->getReflectionService()); // verify inheritance @@ -319,7 +319,7 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory foreach ($parentClass->associationMappings as $field => $mapping) { if ($parentClass->isMappedSuperclass) { if ($mapping['type'] & ClassMetadata::TO_MANY && !$mapping['isOwningSide']) { - throw MappingException::illegalToManyAssocationOnMappedSuperclass($parentClass->name, $field); + throw MappingException::illegalToManyAssociationOnMappedSuperclass($parentClass->name, $field); } $mapping['sourceEntity'] = $subClass->name; } diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php index fc808d3e5..1396d2a11 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php @@ -509,7 +509,7 @@ class ClassMetadataInfo implements ClassMetadata public $isIdentifierComposite = false; /** - * READ-ONLY: Flag indicating wheather the identifier/primary key contains at least one foreign key association. + * READ-ONLY: Flag indicating whether the identifier/primary key contains at least one foreign key association. * * This flag is necessary because some code blocks require special treatment of this cases. * @@ -631,7 +631,7 @@ class ClassMetadataInfo implements ClassMetadata } /** - * Gets the ReflectionPropertys of the mapped class. + * Gets the ReflectionProperties of the mapped class. * * @return array An array of ReflectionProperty instances. */ @@ -938,7 +938,7 @@ class ClassMetadataInfo implements ClassMetadata * * @throws MappingException */ - public function validateAssocations() + public function validateAssociations() { foreach ($this->associationMappings as $mapping) { if ( ! ClassLoader::classExists($mapping['targetEntity']) ) { @@ -1437,7 +1437,7 @@ class ClassMetadataInfo implements ClassMetadata )); } - $uniqueContraintColumns = array(); + $uniqueConstraintColumns = array(); foreach ($mapping['joinColumns'] as &$joinColumn) { if ($mapping['type'] === self::ONE_TO_ONE && ! $this->isInheritanceTypeSingleTable()) { if (count($mapping['joinColumns']) == 1) { @@ -1445,7 +1445,7 @@ class ClassMetadataInfo implements ClassMetadata $joinColumn['unique'] = true; } } else { - $uniqueContraintColumns[] = $joinColumn['name']; + $uniqueConstraintColumns[] = $joinColumn['name']; } } @@ -1472,12 +1472,12 @@ class ClassMetadataInfo implements ClassMetadata ? $joinColumn['fieldName'] : $joinColumn['name']; } - if ($uniqueContraintColumns) { + if ($uniqueConstraintColumns) { if ( ! $this->table) { throw new RuntimeException("ClassMetadataInfo::setTable() has to be called before defining a one to one relationship."); } $this->table['uniqueConstraints'][$mapping['fieldName']."_uniq"] = array( - 'columns' => $uniqueContraintColumns + 'columns' => $uniqueConstraintColumns ); } @@ -1488,7 +1488,7 @@ class ClassMetadataInfo implements ClassMetadata $mapping['isCascadeRemove'] = $mapping['orphanRemoval'] ? true : $mapping['isCascadeRemove']; if (isset($mapping['id']) && $mapping['id'] === true && !$mapping['isOwningSide']) { - throw MappingException::illegalInverseIdentifierAssocation($this->name, $mapping['fieldName']); + throw MappingException::illegalInverseIdentifierAssociation($this->name, $mapping['fieldName']); } return $mapping; @@ -2051,7 +2051,7 @@ class ClassMetadataInfo implements ClassMetadata } /** - * Checks if this entity is the root in any entity-inheritance-hierachy. + * Checks if this entity is the root in any entity-inheritance-hierarchy. * * @return bool */ @@ -2427,7 +2427,7 @@ class ClassMetadataInfo implements ClassMetadata * @deprecated Deprecated since version 2.4 in favor of \Doctrine\ORM\Event\ListenersInvoker * * @param string $lifecycleEvent The lifecycle event. - * @param object $entity The Entity on which the event occured. + * @param object $entity The Entity on which the event occurred. * * @return void */ diff --git a/lib/Doctrine/ORM/Mapping/DefaultEntityListenerResolver.php b/lib/Doctrine/ORM/Mapping/DefaultEntityListenerResolver.php index 78c93791b..75658547e 100644 --- a/lib/Doctrine/ORM/Mapping/DefaultEntityListenerResolver.php +++ b/lib/Doctrine/ORM/Mapping/DefaultEntityListenerResolver.php @@ -21,7 +21,7 @@ namespace Doctrine\ORM\Mapping; /** - * The default DefaultEntityListene + * The default DefaultEntityListener * * @since 2.4 * @author Fabio B. Silva diff --git a/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php b/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php index a4bea98ea..9e1c734cc 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php @@ -247,7 +247,7 @@ class AnnotationDriver extends AbstractAnnotationDriver $mapping = array(); $mapping['fieldName'] = $property->getName(); - // Check for JoinColummn/JoinColumns annotations + // Check for JoinColumn/JoinColumns annotations $joinColumns = array(); if ($joinColumnAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\JoinColumn')) { @@ -375,7 +375,7 @@ class AnnotationDriver extends AbstractAnnotationDriver $override = array(); $fieldName = $associationOverride->name; - // Check for JoinColummn/JoinColumns annotations + // Check for JoinColumn/JoinColumns annotations if ($associationOverride->joinColumns) { $joinColumns = array(); foreach ($associationOverride->joinColumns as $joinColumn) { diff --git a/lib/Doctrine/ORM/Mapping/Driver/DatabaseDriver.php b/lib/Doctrine/ORM/Mapping/Driver/DatabaseDriver.php index e1f75da6d..15a4e0d2c 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/DatabaseDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/DatabaseDriver.php @@ -84,7 +84,7 @@ class DatabaseDriver implements MappingDriver } /** - * Sets tables manually instead of relying on the reverse engeneering capabilities of SchemaManager. + * Sets tables manually instead of relying on the reverse engineering capabilities of SchemaManager. * * @param array $entityTables * @param array $manyToManyTables @@ -252,7 +252,7 @@ class DatabaseDriver implements MappingDriver if (!$otherFk) { // the definition of this many to many table does not contain - // enough foreign key information to continue reverse engeneering. + // enough foreign key information to continue reverse engineering. continue; } @@ -319,7 +319,7 @@ class DatabaseDriver implements MappingDriver ); } - //Here we need to check if $cols are the same as $primaryKeyColums + //Here we need to check if $cols are the same as $primaryKeyColumns if (!array_diff($cols,$primaryKeyColumns)) { $metadata->mapOneToOne($associationMapping); } else { diff --git a/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php b/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php index 26d1becd0..461038932 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php @@ -224,7 +224,7 @@ class XmlDriver extends FileDriver $metadata->table['options'] = $this->_parseOptions($xmlRoot->options->children()); } - // The mapping assignement is done in 2 times as a bug might occurs on some php/xml lib versions + // The mapping assignment is done in 2 times as a bug might occurs on some php/xml lib versions // The internal SimpleXmlIterator get resetted, to this generate a duplicate field exception $mappings = array(); // Evaluate mappings diff --git a/lib/Doctrine/ORM/Mapping/MappingException.php b/lib/Doctrine/ORM/Mapping/MappingException.php index 61ecb4d11..a8345bbd6 100644 --- a/lib/Doctrine/ORM/Mapping/MappingException.php +++ b/lib/Doctrine/ORM/Mapping/MappingException.php @@ -529,7 +529,7 @@ class MappingException extends \Doctrine\ORM\ORMException */ public static function cannotVersionIdField($className, $fieldName) { - return new self("Setting Id field '$fieldName' as versionale in entity class '$className' is not supported."); + return new self("Setting Id field '$fieldName' as versionable in entity class '$className' is not supported."); } /** @@ -561,7 +561,7 @@ class MappingException extends \Doctrine\ORM\ORMException * * @return MappingException */ - public static function illegalToManyAssocationOnMappedSuperclass($className, $field) + public static function illegalToManyAssociationOnMappedSuperclass($className, $field) { return new self("It is illegal to put an inverse side one-to-many or many-to-many association on mapped superclass '".$className."#".$field."'."); } @@ -632,7 +632,7 @@ class MappingException extends \Doctrine\ORM\ORMException * * @return MappingException */ - public static function illegalInverseIdentifierAssocation($className, $field) + public static function illegalInverseIdentifierAssociation($className, $field) { return new self("An inverse association is not allowed to be identifier in '$className#$field'."); } @@ -643,7 +643,7 @@ class MappingException extends \Doctrine\ORM\ORMException * * @return MappingException */ - public static function illegalToManyIdentifierAssoaction($className, $field) + public static function illegalToManyIdentifierAssociation($className, $field) { return new self("Many-to-many or one-to-many associations are not allowed to be identifier in '$className#$field'."); } @@ -668,8 +668,8 @@ class MappingException extends \Doctrine\ORM\ORMException { return new self( "Entity '" . $className . "' has to be part of the discriminator map of '" . $rootClassName . "' " . - "to be properly mapped in the inheritance hierachy. Alternatively you can make '".$className."' an abstract class " . - "to avoid this exception from occuring." + "to be properly mapped in the inheritance hierarchy. Alternatively you can make '".$className."' an abstract class " . + "to avoid this exception from occurring." ); } diff --git a/lib/Doctrine/ORM/ORMInvalidArgumentException.php b/lib/Doctrine/ORM/ORMInvalidArgumentException.php index 1773df291..cb5037679 100644 --- a/lib/Doctrine/ORM/ORMInvalidArgumentException.php +++ b/lib/Doctrine/ORM/ORMInvalidArgumentException.php @@ -176,7 +176,7 @@ class ORMInvalidArgumentException extends \InvalidArgumentException public static function invalidCompositeIdentifier() { return new self("Binding an entity with a composite primary key to a query is not supported. " . - "You should split the parameter into the explicit fields and bind them seperately."); + "You should split the parameter into the explicit fields and bind them separately."); } /** diff --git a/lib/Doctrine/ORM/OptimisticLockException.php b/lib/Doctrine/ORM/OptimisticLockException.php index 6f1a57631..57ea66bde 100644 --- a/lib/Doctrine/ORM/OptimisticLockException.php +++ b/lib/Doctrine/ORM/OptimisticLockException.php @@ -71,7 +71,7 @@ class OptimisticLockException extends ORMException * * @return OptimisticLockException */ - public static function lockFailedVersionMissmatch($entity, $expectedLockVersion, $actualLockVersion) + public static function lockFailedVersionMismatch($entity, $expectedLockVersion, $actualLockVersion) { return new self("The optimistic lock failed, version " . $expectedLockVersion . " was expected, but is actually ".$actualLockVersion, $entity); } diff --git a/lib/Doctrine/ORM/PersistentCollection.php b/lib/Doctrine/ORM/PersistentCollection.php index dbfac3716..33040d993 100644 --- a/lib/Doctrine/ORM/PersistentCollection.php +++ b/lib/Doctrine/ORM/PersistentCollection.php @@ -843,7 +843,7 @@ final class PersistentCollection implements Collection, Selectable } if ($this->association['type'] !== ClassMetadata::ONE_TO_MANY) { - throw new \RuntimeException("Matching Criteria on PersistentCollection only works on OneToMany assocations at the moment."); + throw new \RuntimeException("Matching Criteria on PersistentCollection only works on OneToMany associations at the moment."); } // If there are NEW objects we have to check if any of them matches the criteria diff --git a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php index d5543dad4..c6bd8e847 100644 --- a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php @@ -37,7 +37,7 @@ use Doctrine\Common\Collections\Criteria; use Doctrine\Common\Collections\Expr\Comparison; /** - * A BasicEntityPersiter maps an entity to a single table in a relational database. + * A BasicEntityPersister maps an entity to a single table in a relational database. * * A persister is always responsible for a single entity type. * @@ -177,8 +177,8 @@ class BasicEntityPersister protected $selectColumnListSql; /** - * The JOIN SQL fragement used to eagerly load all many-to-one and one-to-one - * associations configured as FETCH_EAGER, aswell as all inverse one-to-one associations. + * The JOIN SQL fragment used to eagerly load all many-to-one and one-to-one + * associations configured as FETCH_EAGER, as well as all inverse one-to-one associations. * * @var string */ @@ -499,7 +499,7 @@ class BasicEntityPersister } // @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! + // like self-referential relationship between different levels of an inheritance hierarchy? I hope not! $selfReferential = ($mapping['targetEntity'] == $mapping['sourceEntity']); $class = $this->class; $association = $mapping; diff --git a/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php b/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php index 4989c7919..7a2f16a1a 100644 --- a/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php +++ b/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php @@ -302,31 +302,31 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister // INNER JOIN parent tables foreach ($this->class->parentClasses as $parentClassName) { - $contitions = array(); + $conditions = array(); $parentClass = $this->em->getClassMetadata($parentClassName); $tableAlias = $this->getSQLTableAlias($parentClassName); $joinSql .= ' INNER JOIN ' . $this->quoteStrategy->getTableName($parentClass, $this->platform) . ' ' . $tableAlias . ' ON '; foreach ($identifierColumn as $idColumn) { - $contitions[] = $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn; + $conditions[] = $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn; } - $joinSql .= implode(' AND ', $contitions); + $joinSql .= implode(' AND ', $conditions); } // OUTER JOIN sub tables foreach ($this->class->subClasses as $subClassName) { - $contitions = array(); + $conditions = array(); $subClass = $this->em->getClassMetadata($subClassName); $tableAlias = $this->getSQLTableAlias($subClassName); $joinSql .= ' LEFT JOIN ' . $this->quoteStrategy->getTableName($subClass, $this->platform) . ' ' . $tableAlias . ' ON '; foreach ($identifierColumn as $idColumn) { - $contitions[] = $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn; + $conditions[] = $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn; } - $joinSql .= implode(' AND ', $contitions); + $joinSql .= implode(' AND ', $conditions); } if ($assoc != null && $assoc['type'] == ClassMetadata::MANY_TO_MANY) { diff --git a/lib/Doctrine/ORM/Persisters/SingleTablePersister.php b/lib/Doctrine/ORM/Persisters/SingleTablePersister.php index 0d7abc8d9..e157a90a2 100644 --- a/lib/Doctrine/ORM/Persisters/SingleTablePersister.php +++ b/lib/Doctrine/ORM/Persisters/SingleTablePersister.php @@ -178,7 +178,7 @@ class SingleTablePersister extends AbstractEntityInheritancePersister { // Ensure that the filters are applied to the root entity of the inheritance tree $targetEntity = $this->em->getClassMetadata($targetEntity->rootEntityName); - // we dont care about the $targetTableAlias, in a STI there is only one table. + // we don't care about the $targetTableAlias, in a STI there is only one table. return parent::generateFilterConditionSQL($targetEntity, $targetTableAlias); } diff --git a/lib/Doctrine/ORM/Query.php b/lib/Doctrine/ORM/Query.php index 8b8d40111..107055ed3 100644 --- a/lib/Doctrine/ORM/Query.php +++ b/lib/Doctrine/ORM/Query.php @@ -327,7 +327,7 @@ final class Query extends AbstractQuery } if (count($sqlParams) != count($types)) { - throw QueryException::parameterTypeMissmatch(); + throw QueryException::parameterTypeMismatch(); } if ($sqlParams) { diff --git a/lib/Doctrine/ORM/Query/Expr.php b/lib/Doctrine/ORM/Query/Expr.php index 90c706137..48fb790b6 100644 --- a/lib/Doctrine/ORM/Query/Expr.php +++ b/lib/Doctrine/ORM/Query/Expr.php @@ -350,8 +350,8 @@ class Expr * When converted to string, it will generated a * . Example: * * [php] - * // u.salary * u.percentAnualSalaryIncrease - * $q->expr()->prod('u.salary', 'u.percentAnualSalaryIncrease') + * // u.salary * u.percentAnnualSalaryIncrease + * $q->expr()->prod('u.salary', 'u.percentAnnualSalaryIncrease') * * @param mixed $x Left expression. * @param mixed $y Right expression. diff --git a/lib/Doctrine/ORM/Query/ParameterTypeInferer.php b/lib/Doctrine/ORM/Query/ParameterTypeInferer.php index 602dde6d8..462f971ba 100644 --- a/lib/Doctrine/ORM/Query/ParameterTypeInferer.php +++ b/lib/Doctrine/ORM/Query/ParameterTypeInferer.php @@ -23,7 +23,7 @@ use Doctrine\DBAL\Connection; use Doctrine\DBAL\Types\Type; /** - * Provides an enclosed support for parameter infering. + * Provides an enclosed support for parameter inferring. * * @link www.doctrine-project.org * @since 2.0 diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index 460ebea46..e7a0e3ed2 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -305,7 +305,7 @@ class Parser * * @return void * - * @throws QueryException If the tokens dont match. + * @throws QueryException If the tokens don't match. */ public function match($token) { @@ -644,7 +644,7 @@ class Parser } if ($class->getConstructor() === null) { - $this->semanticalError(sprintf('Class "%s" has not a valid contructor.', $className), $token); + $this->semanticalError(sprintf('Class "%s" has not a valid constructor.', $className), $token); } if ($class->getConstructor()->getNumberOfRequiredParameters() > count($args)) { @@ -2373,7 +2373,7 @@ class Parser return $condPrimary; } - // Peek beyond the matching closing paranthesis ')' + // Peek beyond the matching closing parenthesis ')' $peek = $this->peekBeyondClosingParenthesis(); if (in_array($peek['value'], array("=", "<", "<=", "<>", ">", ">=", "!=")) || @@ -2445,7 +2445,7 @@ class Parser $token = $this->lexer->peek(); } - // We need to go even further in case of IS (differenciate between NULL and EMPTY) + // We need to go even further in case of IS (differentiate between NULL and EMPTY) $lookahead = $this->lexer->peek(); } @@ -2495,19 +2495,19 @@ class Parser */ public function EmptyCollectionComparisonExpression() { - $emptyColletionCompExpr = new AST\EmptyCollectionComparisonExpression( + $emptyCollectionCompExpr = new AST\EmptyCollectionComparisonExpression( $this->CollectionValuedPathExpression() ); $this->match(Lexer::T_IS); if ($this->lexer->isNextToken(Lexer::T_NOT)) { $this->match(Lexer::T_NOT); - $emptyColletionCompExpr->not = true; + $emptyCollectionCompExpr->not = true; } $this->match(Lexer::T_EMPTY); - return $emptyColletionCompExpr; + return $emptyCollectionCompExpr; } /** @@ -2800,7 +2800,7 @@ class Parser } if ($peek['value'] == '(') { - // do NOT directly go to FunctionsReturningString() because it doesnt check for custom functions. + // do NOT directly go to FunctionsReturningString() because it doesn't check for custom functions. return $this->FunctionDeclaration(); } diff --git a/lib/Doctrine/ORM/Query/QueryException.php b/lib/Doctrine/ORM/Query/QueryException.php index 06a622644..da0d2d50c 100644 --- a/lib/Doctrine/ORM/Query/QueryException.php +++ b/lib/Doctrine/ORM/Query/QueryException.php @@ -123,9 +123,9 @@ class QueryException extends \Doctrine\ORM\ORMException /** * @return QueryException */ - public static function parameterTypeMissmatch() + public static function parameterTypeMismatch() { - return new self("DQL Query parameter and type numbers missmatch, but have to be exactly equal."); + return new self("DQL Query parameter and type numbers mismatch, but have to be exactly equal."); } /** @@ -159,7 +159,7 @@ class QueryException extends \Doctrine\ORM\ORMException { return new self( "Invalid query operation: Not allowed to iterate over fetch join collections ". - "in class ".$assoc['sourceEntity']." assocation ".$assoc['fieldName'] + "in class ".$assoc['sourceEntity']." association ".$assoc['fieldName'] ); } @@ -184,7 +184,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['fieldName'].". ". + "conditions in class ".$assoc['sourceEntityName']." association ".$assoc['fieldName'].". ". "Use WITH to append additional join conditions to the association." ); } @@ -234,7 +234,7 @@ class QueryException extends \Doctrine\ORM\ORMException public static function instanceOfUnrelatedClass($className, $rootClass) { return new self("Cannot check if a child of '" . $rootClass . "' is instanceof '" . $className . "', " . - "inheritance hierachy exists between these two classes."); + "inheritance hierarchy exists between these two classes."); } /** diff --git a/lib/Doctrine/ORM/Query/SqlWalker.php b/lib/Doctrine/ORM/Query/SqlWalker.php index 81660fd83..30a39924e 100644 --- a/lib/Doctrine/ORM/Query/SqlWalker.php +++ b/lib/Doctrine/ORM/Query/SqlWalker.php @@ -1042,7 +1042,7 @@ class SqlWalker implements TreeWalker // Handle WITH clause if (($condExpr = $join->conditionalExpression) !== null) { - // Phase 2 AST optimization: Skip processment of ConditionalExpression + // Phase 2 AST optimization: Skip processing of ConditionalExpression // if only one ConditionalTerm is defined $sql .= ' AND (' . $this->walkConditionalExpression($condExpr) . ')'; } @@ -1691,7 +1691,7 @@ class SqlWalker implements TreeWalker */ public function walkConditionalExpression($condExpr) { - // Phase 2 AST optimization: Skip processment of ConditionalExpression + // Phase 2 AST optimization: Skip processing of ConditionalExpression // if only one ConditionalTerm is defined if ( ! ($condExpr instanceof AST\ConditionalExpression)) { return $this->walkConditionalTerm($condExpr); @@ -1705,7 +1705,7 @@ class SqlWalker implements TreeWalker */ public function walkConditionalTerm($condTerm) { - // Phase 2 AST optimization: Skip processment of ConditionalTerm + // Phase 2 AST optimization: Skip processing of ConditionalTerm // if only one ConditionalFactor is defined if ( ! ($condTerm instanceof AST\ConditionalTerm)) { return $this->walkConditionalFactor($condTerm); @@ -1719,7 +1719,7 @@ class SqlWalker implements TreeWalker */ public function walkConditionalFactor($factor) { - // Phase 2 AST optimization: Skip processment of ConditionalFactor + // Phase 2 AST optimization: Skip processing of ConditionalFactor // if only one ConditionalPrimary is defined return ( ! ($factor instanceof AST\ConditionalFactor)) ? $this->walkConditionalPrimary($factor) @@ -2114,7 +2114,7 @@ class SqlWalker implements TreeWalker : $term; } - // Phase 2 AST optimization: Skip processment of ArithmeticTerm + // Phase 2 AST optimization: Skip processing of ArithmeticTerm // if only one ArithmeticFactor is defined if ( ! ($term instanceof AST\ArithmeticTerm)) { return $this->walkArithmeticFactor($term); @@ -2132,7 +2132,7 @@ class SqlWalker implements TreeWalker return $factor; } - // Phase 2 AST optimization: Skip processment of ArithmeticFactor + // Phase 2 AST optimization: Skip processing of ArithmeticFactor // if only one ArithmeticPrimary is defined if ( ! ($factor instanceof AST\ArithmeticFactor)) { return $this->walkArithmeticPrimary($factor); diff --git a/lib/Doctrine/ORM/Query/TreeWalkerAdapter.php b/lib/Doctrine/ORM/Query/TreeWalkerAdapter.php index c285739d8..e95155c13 100644 --- a/lib/Doctrine/ORM/Query/TreeWalkerAdapter.php +++ b/lib/Doctrine/ORM/Query/TreeWalkerAdapter.php @@ -90,7 +90,7 @@ abstract class TreeWalkerAdapter implements TreeWalker } /** - * Retrieves the Query Instance reponsible for the current walkers execution. + * Retrieves the Query Instance responsible for the current walkers execution. * * @return \Doctrine\ORM\AbstractQuery */ diff --git a/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php index df561d583..cec5d26a3 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php @@ -86,10 +86,10 @@ class ConvertMappingCommand extends Command Convert mapping information between supported formats. This is an execute one-time command. It should not be necessary for -you to call this method multiple times, escpecially when using the --from-database +you to call this method multiple times, especially when using the --from-database flag. -Converting an existing databsae schema into mapping files only solves about 70-80% +Converting an existing database schema into mapping files only solves about 70-80% of the necessary mapping information. Additionally the detection from an existing database cannot detect inverse associations, inheritance types, entities with foreign keys as primary keys and many of the diff --git a/lib/Doctrine/ORM/Tools/Console/Command/GenerateEntitiesCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/GenerateEntitiesCommand.php index 377841f1e..764e89500 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/GenerateEntitiesCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/GenerateEntitiesCommand.php @@ -91,7 +91,7 @@ to error and we suggest you use code repositories such as GIT or SVN to make backups of your code. It makes sense to generate the entity code if you are using entities as Data -Access Objects only and dont put much additional logic on them. If you are +Access Objects only and don't put much additional logic on them. If you are however putting much more logic on the entities you should refrain from using the entity-generator and code your entities manually. diff --git a/lib/Doctrine/ORM/Tools/SchemaTool.php b/lib/Doctrine/ORM/Tools/SchemaTool.php index 02ef1e7c7..f66a49d15 100644 --- a/lib/Doctrine/ORM/Tools/SchemaTool.php +++ b/lib/Doctrine/ORM/Tools/SchemaTool.php @@ -500,7 +500,7 @@ class SchemaTool * Gets the class metadata that is responsible for the definition of the referenced column name. * * Previously this was a simple task, but with DDC-117 this problem is actually recursive. If its - * not a simple field, go through all identifier field names that are associations recursivly and + * not a simple field, go through all identifier field names that are associations recursively and * find that referenced column name. * * TODO: Is there any way to make this code more pleasing? @@ -643,7 +643,7 @@ class SchemaTool /** * Drops the database schema for the given classes. * - * In any way when an exception is thrown it is supressed since drop was + * In any way when an exception is thrown it is suppressed since drop was * issued for all classes of the schema and some probably just don't exist. * * @param array $classes diff --git a/lib/Doctrine/ORM/Tools/ToolEvents.php b/lib/Doctrine/ORM/Tools/ToolEvents.php index 7aa98d9a6..aebb5d8f3 100644 --- a/lib/Doctrine/ORM/Tools/ToolEvents.php +++ b/lib/Doctrine/ORM/Tools/ToolEvents.php @@ -23,7 +23,7 @@ class ToolEvents { /** * The postGenerateSchemaTable event occurs in SchemaTool#getSchemaFromMetadata() - * whenever an entity class is transformed into its table representation. It recieves + * whenever an entity class is transformed into its table representation. It receives * the current non-complete Schema instance, the Entity Metadata Class instance and * the Schema Table instance of this entity. * diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index 3a3f14e59..4c3f3720c 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -1822,7 +1822,7 @@ class UnitOfWork implements PropertyChangedListener // Throw exception if versions dont match. if ($managedCopyVersion != $entityVersion) { - throw OptimisticLockException::lockFailedVersionMissmatch($entity, $entityVersion, $managedCopyVersion); + throw OptimisticLockException::lockFailedVersionMismatch($entity, $entityVersion, $managedCopyVersion); } } @@ -2281,7 +2281,7 @@ class UnitOfWork implements PropertyChangedListener $entityVersion = $class->reflFields[$class->versionField]->getValue($entity); if ($entityVersion != $lockVersion) { - throw OptimisticLockException::lockFailedVersionMissmatch($entity, $lockVersion, $entityVersion); + throw OptimisticLockException::lockFailedVersionMismatch($entity, $lockVersion, $entityVersion); } break; diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 3ab5edbae..e6fc54315 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -2,7 +2,7 @@ + + + + + + + + + + + + .. code-block:: yaml + + # config/yaml/Product.dcm.yml + Product: + type: entity + table: products + id: + id: + type: integer + generator: + strategy: AUTO + fields: + name: + type: string + +The top-level ``entity`` definition tag specifies information about +the class and table-name. The primitive type ``Product::$name`` is +defined as ``field`` attributes. The Id property is defined with +the ``id`` tag. The id has a ``generator`` tag nested inside which +defines that the primary key generation mechanism automatically +uses the database platforms native id generation strategy, for +example AUTO INCREMENT in the case of MySql or Sequences in the +case of PostgreSql and Oracle. + +You have to update the database now, because we have a first Entity now: + +:: + + $ php vendor/bin/doctrine orm:schema-tool:update + +Now create a simple script to create a new product: + +.. code-block:: php + + setName($newProductName); + + $entityManager->persist($product); + $entityManager->flush(); + + echo "Created Product with ID " . $product->getId() . "\n"; + +Call this script to see how new products are created: + +:: + + $ php create_product.php ORM + $ php create_product.php DBAL + +What is happening here? In the code using the Product is pretty standard OOP. +The interesting bits are the communication with the ``EntityManager``. To +notify the EntityManager that a new entity should be inserted into the database +you have to call ``persist()``. However the EntityManager does not act on this +command, its merely notified. You have to explicitly call ``flush()`` to have +the EntityManager write those two entities to the database. + +You might wonder why does this distinction between persist notification and +flush exist: Doctrine 2 uses the UnitOfWork pattern to aggregate all writes +(INSERT, UPDATE, DELETE) into one single transaction, which is executed when +flush is called. Using this approach the write-performance is significantly +better than in a scenario where updates are done for each entity in isolation. +In more complex scenarios than the previous two, you are free to request +updates on many different entities and all flush them at once. + +Doctrine's UnitOfWork detects entities that have changed after retrieval from +the database automatically when the flush operation is called, so that you only +have to keep track of those entities that are new or to be removed and pass +them to ``EntityManager#persist()`` and ``EntityManager#remove()`` +respectively. + +We want to see a list of all the products now, so lets create a new script for +this: + +.. code-block:: php + + getRepository('Product'); + $products = $productRepository->findAll(); + + foreach ($products as $product) { + echo sprintf("-%s\n", $product->getName()); + } + +The ``EntityRepository`` fetched through the ``EntityManager#getRepository()`` +method exists for every entity and is provided by Doctrine. It contains +some finder methods such as ``findAll()`` we used here. + +Lets display the name of a product based on its ID: + +.. code-block:: php + + find('Product', $id); + + echo sprintf("-%s\n", $product->getName()); + +Adding Bug and User Entities +---------------------------- + +We continue with the bug tracker domain, by creating the missing +classes ``Bug`` and ``User`` and putting them into +`src/Bug.php` and `src/User.php` +respectively. + +.. code-block:: php + + `. - - An entity contains persistable properties. A persistable property - is an instance variable of the entity that is saved into and retrieved from the database - by Doctrine's data mapping capabilities. - -Note how all properties have getter and setter methods defined except -`$id`. To access data from entities Doctrine 2 uses the Reflection API, so it -is possible for Doctrine to access the value of `$id`. You don't have to -take Doctrine into account when designing access to the state of your objects. - All of the properties so far are scalar values, for example the 3 ID fields of the entities, their names, description, status and change dates. @@ -296,7 +556,6 @@ persistence. Debug::dump() method just ignores any occurrences of it in Proxy instances. - Because we only work with collections for the references we must be careful to implement a bidirectional reference in the domain model. The concept of owning or inverse side of a relation is central to @@ -448,112 +707,8 @@ the database that points from Bugs to Products. } We are now finished with the domain model given the requirements. -From the simple model with public properties only we had to do -quite some work to get to a model where we encapsulated the -references between the objects to make sure we don't break its -consistent state when using Doctrine. - -However up to now the assumptions Doctrine imposed on our business -objects have not restricting us much in our domain modelling -capabilities. Actually we would have encapsulated access to all the -properties anyways by using object-oriented best-practices. - -Metadata Mappings for our Entities ----------------------------------- - -Up to now we have only implemented our Entities as Data-Structures -without actually telling Doctrine how to persist them in the -database. If perfect in-memory databases would exist, we could now -finish the application using these entities by implementing code to -fulfil all the requirements. However the world isn't perfect and we -have to persist our entities in some storage to make sure we don't -loose their state. Doctrine currently serves Relational Database -Management Systems. In the future we are thinking to support NoSQL -vendors like CouchDb or MongoDb, however this is still far in the -future. - -The next step for persistence with Doctrine is to describe the -structure of our domain model entities to Doctrine using a metadata -language. The metadata language describes how entities, their -properties and references should be persisted and what constraints -should be applied to them. - -Metadata for entities are loaded using a -``Doctrine\ORM\Mapping\Driver\Driver`` implementation and Doctrine -2 already comes with XML, YAML and Annotations Drivers. This -Getting Started Guide will show the mappings for all Mapping Drivers. -References in the text will be made to the XML mapping. - -Since we haven't namespaced our three entities, we have to -implement three mapping files called Bug.dcm.xml, Product.dcm.xml -and User.dcm.xml (or .yml) and put them into a distinct folder for mapping -configurations. For the annotations driver we need to use -doc-block comments on the entity classes themselves. - -The first discussed definition will be for the Product, since it is -the most simple one: - -.. configuration-block:: - - .. code-block:: php - - - - - - - - - - - - - - .. code-block:: yaml - - # config/yaml/Product.dcm.yml - Product: - type: entity - table: products - id: - id: - type: integer - generator: - strategy: AUTO - fields: - name: - type: string - -The top-level ``entity`` definition tag specifies information about -the class and table-name. The primitive type ``Product::$name`` is -defined as ``field`` attributes. The Id property is defined with -the ``id`` tag. The id has a ``generator`` tag nested inside which -defines that the primary key generation mechanism automatically -uses the database platforms native id generation strategy, for -example AUTO INCREMENT in the case of MySql or Sequences in the -case of PostgreSql and Oracle. - -We then go on specifying the definition of a Bug: +Now we continue adding metadata mappings for the ``User`` and ``Bug`` +as we did for the ``Product`` before: .. configuration-block:: .. code-block:: php @@ -654,7 +809,7 @@ We then go on specifying the definition of a Bug: targetEntity: Product -Here again we have the entity, id and primitive type definitions. +Here we have the entity, id and primitive type definitions. The column names are used from the Zend\_Db\_Table examples and have different names than the properties on the Bug class. Additionally for the "created" field it is specified that it is of @@ -774,118 +929,10 @@ class that holds the owning sides. This example has a fair overview of the most basic features of the metadata definition language. -Obtaining the EntityManager ---------------------------- - -Doctrine's public interface is the EntityManager, it provides the -access point to the complete lifecycle management of your entities -and transforms entities from and back to persistence. You have to -configure and create it to use your entities with Doctrine 2. I -will show the configuration steps and then discuss them step by -step: - -.. code-block:: php - - 'pdo_sqlite', - 'path' => __DIR__ . '/db.sqlite', - ); - - // obtaining the entity manager - $entityManager = EntityManager::create($conn, $config); - -The first require statement sets up the autoloading capabilities of Doctrine -using the Composer autoload. - -The second block consists of the instantiation of the ORM -``Configuration`` object using the Setup helper. It assumes a bunch -of defaults that you don't have to bother about for now. You can -read up on the configuration details in the -:doc:`reference chapter on configuration <../reference/configuration>`. - -The third block shows the configuration options required to connect -to a database, in my case a file-based sqlite database. All the -configuration options for all the shipped drivers are given in the -`DBAL Configuration section of the manual `_. - -The last block shows how the ``EntityManager`` is obtained from a -factory method. - -Generating the Database Schema +Implementing more Requirements ------------------------------ -Now that we have defined the Metadata Mappings and bootstrapped the -EntityManager we want to generate the relational database schema -from it. Doctrine has a Command-Line-Interface that allows you to -access the SchemaTool, a component that generates the required -tables to work with the metadata. - -For the command-line tool to work a cli-config.php file has to be -present in the project root directory, where you will execute the -doctrine command. Its a fairly simple file: - -.. code-block:: php - - new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($entityManager) - )); - -You can then change into your project directory and call the -Doctrine command-line tool: - -:: - - $ cd project/ - $ php vendor/bin/doctrine orm:schema-tool:create - -During the development you probably need to re-create the database -several times when changing the Entity metadata. You can then -either re-create the database: - -:: - - $ php vendor/bin/doctrine orm:schema-tool:drop --force - $ php vendor/bin/doctrine orm:schema-tool:create - -Or use the update functionality: - -:: - - $ php vendor/bin/doctrine orm:schema-tool:update --force - -The updating of databases uses a Diff Algorithm for a given -Database Schema, a cornerstone of the ``Doctrine\DBAL`` package, -which can even be used without the Doctrine ORM package. However -its not available in SQLite since it does not support ALTER TABLE. - -Writing Entities into the Database ----------------------------------- - -.. note:: - - This tutorial assumes you call all the example scripts from the CLI. - -Having created the schema we can now start and save entities in the -database. For starters we need a create user use-case: +For starters we need a create user entities: .. code-block:: php @@ -903,59 +950,14 @@ database. For starters we need a create user use-case: echo "Created User with ID " . $user->getId() . "\n"; -Products can also be created: - -.. code-block:: php - - setName($newProductName); - - $entityManager->persist($product); - $entityManager->flush(); - - echo "Created Product with ID " . $product->getId() . "\n"; - Now call: :: $ php create_user.php beberlei - $ php create_product.php MyProduct -So what is happening in those two snippets? In both examples the -code that works on User and Product is pretty standard OOP. The interesting bits are the -communication with the ``EntityManager``. To notify the -EntityManager that a new entity should be inserted into the -database you have to call ``persist()``. However the EntityManager -does not act on this command, its merely notified. You have to explicitly -call ``flush()`` to have the EntityManager write those two entities -to the database. - -You might wonder why does this distinction between persist -notification and flush exist: Doctrine 2 uses the UnitOfWork -pattern to aggregate all writes (INSERT, UPDATE, DELETE) into one -single transaction, which is executed when flush is called. -Using this approach the write-performance is significantly better -than in a scenario where updates are done for each entity in -isolation. In more complex scenarios than the previous two, you are -free to request updates on many different entities and all flush -them at once. - -Doctrine's UnitOfWork detects entities that have changed after -retrieval from the database automatically when the flush operation -is called, so that you only have to keep track of those entities -that are new or to be removed and pass them to -``EntityManager#persist()`` and ``EntityManager#remove()`` -respectively. - -We are now getting to the "Create a New Bug" requirement and the -code for this scenario may look like this: +We now have the data to create a bug and the code for this scenario may look +like this: .. code-block:: php @@ -1462,4 +1464,3 @@ will be added to this tutorial incrementally, topics will include: Additional details on all the topics discussed here can be found in the respective manual chapters. - From c399dcfe58865b839309a710d39c36a4faf9cd32 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 17 Mar 2013 19:18:49 +0100 Subject: [PATCH 042/332] Add simpler section for 2.4 CLI integration --- docs/en/reference/configuration.rst | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/docs/en/reference/configuration.rst b/docs/en/reference/configuration.rst index 4743971fe..eb4b0a497 100644 --- a/docs/en/reference/configuration.rst +++ b/docs/en/reference/configuration.rst @@ -107,6 +107,23 @@ You need to register your applications EntityManager to the console tool to make use of the tasks by creating a ``cli-config.php`` file with the following content: +On Doctrine 2.4 and above: + +.. code-block:: php + + new \Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper($em->getConnection()), 'em' => new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($em) From acae1aeaaa8465a562f2ecc9ba40cf7c1da24f58 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 17 Mar 2013 19:43:09 +0100 Subject: [PATCH 043/332] Fix link in docs --- docs/en/reference/configuration.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/en/reference/configuration.rst b/docs/en/reference/configuration.rst index eb4b0a497..d8e718b5c 100644 --- a/docs/en/reference/configuration.rst +++ b/docs/en/reference/configuration.rst @@ -79,12 +79,13 @@ Or if you prefer YAML: Inside the ``Setup`` methods several assumptions are made: -- If `$devMode` is true always use an ``ArrayCache`` and set ``setAutoGenerateProxyClasses(true)``. +- If `$devMode` is true always use an ``ArrayCache`` (in-memory) and regenerate proxies on every request. - If `$devMode` is false, check for Caches in the order APC, Xcache, Memcache (127.0.0.1:11211), Redis (127.0.0.1:6379) unless `$cache` is passed as fourth argument. -- If `$devMode` is false, set ``setAutoGenerateProxyClasses(false)`` +- If `$devMode` is false, set then proxy classes have to be explicitly created + through the command line. - If third argument `$proxyDir` is not set, use the systems temporary directory. -If you want to configure Doctrine in more detail, take a look at the `Advanced +If you want to configure Doctrine in more detail, take a look at the :doc:`Advanced Configuration ` section. .. note:: From 39ea24675da38a160cecf2a131b6917ea0bbde3d Mon Sep 17 00:00:00 2001 From: "Fabio B. Silva" Date: Sun, 17 Mar 2013 16:59:33 -0300 Subject: [PATCH 044/332] Fix DDC-2090 --- .../Query/Exec/MultiTableUpdateExecutor.php | 29 ++--- .../ORM/Functional/Ticket/DDC2090Test.php | 110 ++++++++++++++++++ 2 files changed, 120 insertions(+), 19 deletions(-) create mode 100644 tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2090Test.php diff --git a/lib/Doctrine/ORM/Query/Exec/MultiTableUpdateExecutor.php b/lib/Doctrine/ORM/Query/Exec/MultiTableUpdateExecutor.php index cc15cea92..8ceefe3a9 100644 --- a/lib/Doctrine/ORM/Query/Exec/MultiTableUpdateExecutor.php +++ b/lib/Doctrine/ORM/Query/Exec/MultiTableUpdateExecutor.php @@ -124,19 +124,8 @@ class MultiTableUpdateExecutor extends AbstractSqlExecutor $updateSql .= $sqlWalker->walkUpdateItem($updateItem); - //FIXME: parameters can be more deeply nested. traverse the tree. - //FIXME (URGENT): With query cache the parameter is out of date. Move to execute() stage. if ($newValue instanceof AST\InputParameter) { - $parameterName = $newValue->name; - $parameter = $sqlWalker->getQuery()->getParameter($parameterName); - - $value = $sqlWalker->getQuery()->processParameterValue($parameter->getValue()); - $type = ($parameter->getValue() === $value) - ? $parameter->getType() - : ParameterTypeInferer::inferType($value); - - $this->_sqlParameters[$i]['parameters'][] = $value; - $this->_sqlParameters[$i]['types'][] = $type; + $this->_sqlParameters[$i][] = $newValue->name; ++$this->_numParametersInUpdateClause; } @@ -188,16 +177,18 @@ class MultiTableUpdateExecutor extends AbstractSqlExecutor ); // Execute UPDATE statements - for ($i=0, $count=count($this->_sqlStatements); $i<$count; ++$i) { - $parameters = array(); - $types = array(); + foreach ($this->_sqlStatements as $key => $statement) { + $paramValues = array(); + $paramTypes = array(); - if (isset($this->_sqlParameters[$i])) { - $parameters = isset($this->_sqlParameters[$i]['parameters']) ? $this->_sqlParameters[$i]['parameters'] : array(); - $types = isset($this->_sqlParameters[$i]['types']) ? $this->_sqlParameters[$i]['types'] : array(); + if (isset($this->_sqlParameters[$key])) { + foreach ($this->_sqlParameters[$key] as $parameterKey => $parameterName) { + $paramValues[] = $params[$parameterKey]; + $paramTypes[] = isset($types[$parameterKey]) ? $types[$parameterKey] : ParameterTypeInferer::inferType($params[$parameterKey]); + } } - $conn->executeUpdate($this->_sqlStatements[$i], $parameters, $types); + $conn->executeUpdate($statement, $paramValues, $paramTypes); } } catch (\Exception $exception) { // FAILURE! Drop temporary table to avoid possible collisions diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2090Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2090Test.php new file mode 100644 index 000000000..c5da3deae --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2090Test.php @@ -0,0 +1,110 @@ +useModelSet('company'); + parent::setUp(); + } + + public function testIssue() + { + $className = 'Doctrine\Tests\Models\Company\CompanyEmployee'; + $date1 = new \DateTime('2011-11-11 11:11:11'); + $date2 = new \DateTime('2012-12-12 12:12:12'); + $employee1 = new CompanyEmployee; + $employee2 = new CompanyEmployee; + + $employee1->setName("Fabio B. Silva"); + $employee1->setStartDate(new \DateTime('yesterday')); + $employee1->setDepartment("R&D"); + $employee1->setSalary(100); + + $employee2->setName("Doctrine Bot"); + $employee1->setStartDate(new \DateTime('yesterday')); + $employee2->setDepartment("QA"); + $employee2->setSalary(100); + + $this->_em->persist($employee1); + $this->_em->persist($employee2); + $this->_em->flush(); + $this->_em->clear(); + + $this->_em->createQueryBuilder() + ->update($className, 'e') + ->set('e.startDate', ':date') + ->set('e.salary', ':salary') + ->where('e = :e') + ->setParameters(array( + 'e' => $employee1, + 'date' => $date1, + 'salary' => 101, + )) + ->getQuery() + ->useQueryCache(true) + ->execute(); + + $this->_em->createQueryBuilder() + ->update($className, 'e') + ->set('e.startDate', ':date') + ->set('e.salary', ':salary') + ->where('e = :e') + ->setParameters(array( + 'e' => $employee2, + 'date' => $date2, + 'salary' => 102, + )) + ->getQuery() + ->useQueryCache(true) + ->execute(); + + $this->_em->clear(); + + $e1 = $this->_em->find($className, $employee1->getId()); + $e2 = $this->_em->find($className, $employee2->getId()); + + $this->assertEquals(101, $e1->getSalary()); + $this->assertEquals(102, $e2->getSalary()); + $this->assertEquals($date1, $e1->getStartDate()); + $this->assertEquals($date2, $e2->getStartDate()); + + $this->_em->createQueryBuilder() + ->update($className, 'e') + ->set('e.startDate', '?1') + ->set('e.salary', '?2') + ->where('e = ?0') + ->setParameters(array($employee1, $date1, 101)) + ->getQuery() + ->useQueryCache(true) + ->execute(); + + $this->_em->createQueryBuilder() + ->update($className, 'e') + ->set('e.startDate', '?1') + ->set('e.salary', '?2') + ->where('e = ?0') + ->setParameters(array($employee2, $date2, 102)) + ->getQuery() + ->useQueryCache(true) + ->execute(); + + + $this->_em->clear(); + + $e1 = $this->_em->find($className, $employee1->getId()); + $e2 = $this->_em->find($className, $employee2->getId()); + + $this->assertEquals(101, $e1->getSalary()); + $this->assertEquals(102, $e2->getSalary()); + $this->assertEquals($date1, $e1->getStartDate()); + $this->assertEquals($date2, $e2->getStartDate()); + } +} \ No newline at end of file From 220f3676582ac2f794e7036cae9d0012c07870f4 Mon Sep 17 00:00:00 2001 From: David Buchmann Date: Wed, 20 Mar 2013 00:46:11 +0100 Subject: [PATCH 045/332] adding some more doc and examples for lifecycle event listeners and subscribers --- docs/en/reference/events.rst | 169 +++++++++++++++++++++++++---------- 1 file changed, 120 insertions(+), 49 deletions(-) diff --git a/docs/en/reference/events.rst b/docs/en/reference/events.rst index df8c594bc..ca3578cde 100644 --- a/docs/en/reference/events.rst +++ b/docs/en/reference/events.rst @@ -2,7 +2,9 @@ Events ====== Doctrine 2 features a lightweight event system that is part of the -Common package. +Common package. Doctrine uses it to dispatch system events, mainly +:ref:`lifecycle events `. +You can also use it for your own custom events. The Event System ---------------- @@ -27,28 +29,28 @@ Now we can add some event listeners to the ``$evm``. Let's create a { const preFoo = 'preFoo'; const postFoo = 'postFoo'; - + private $_evm; - + public $preFooInvoked = false; public $postFooInvoked = false; - + public function __construct($evm) { $evm->addEventListener(array(self::preFoo, self::postFoo), $this); } - + public function preFoo(EventArgs $e) { $this->preFooInvoked = true; } - + public function postFoo(EventArgs $e) { $this->postFooInvoked = true; } } - + // Create a new instance $test = new EventTest($evm); @@ -80,22 +82,30 @@ array of events it should be subscribed to. class TestEventSubscriber implements \Doctrine\Common\EventSubscriber { public $preFooInvoked = false; - + public function preFoo() { $this->preFooInvoked = true; } - + public function getSubscribedEvents() { return array(TestEvent::preFoo); } } - + $eventSubscriber = new TestEventSubscriber(); $evm->addEventSubscriber($eventSubscriber); -Now when you dispatch an event any event subscribers will be +.. note:: + + If you are familiar with the Symfony2 event manager, note that + the array returned by getSubscribedEvents is different. For + doctrine, the array values must be the event names, and the + names are used as method names that will be called when the + event occurs. + +Now when you dispatch an event, any event subscribers will be notified for that event. .. code-block:: php @@ -133,6 +143,8 @@ several reasons: An example for a correct notation can be found in the example ``EventTest`` above. +.. _reference-events-lifecycle-events: + Lifecycle Events ---------------- @@ -164,7 +176,7 @@ the life-time of their registered entities. - loadClassMetadata - The loadClassMetadata event occurs after the mapping metadata for a class has been loaded from a mapping source (annotations/xml/yaml). -- preFlush - The preFlush event occurs at the very beginning of a flush +- preFlush - The preFlush event occurs at the very beginning of a flush operation. This event is not a lifecycle callback. - onFlush - The onFlush event occurs after the change-sets of all managed entities are computed. This event is not a lifecycle @@ -198,7 +210,7 @@ listeners: - Lifecycle Callbacks are methods on the entity classes that are called when the event is triggered. They receives some kind of ``EventArgs``. -- Lifecycle Event Listeners are classes with specific callback +- Lifecycle Event Listeners and Subscribers are classes with specific callback methods that receives some kind of ``EventArgs`` instance which give access to the entity, EntityManager or other relevant data. @@ -222,44 +234,44 @@ event occurs. .. code-block:: php createdAt = date('Y-m-d H:i:s'); } - + /** @PrePersist */ public function doOtherStuffOnPrePersist() { $this->value = 'changed from prePersist callback!'; } - + /** @PostPersist */ public function doStuffOnPostPersist() { $this->value = 'changed from postPersist callback!'; } - + /** @PostLoad */ public function doStuffOnPostLoad() { $this->value = 'changed from postLoad callback!'; } - + /** @PreUpdate */ public function doStuffOnPreUpdate() { @@ -283,28 +295,28 @@ can do it with the following. type: string(50) lifecycleCallbacks: prePersist: [ doStuffOnPrePersist, doOtherStuffOnPrePersistToo ] - postPersist: [ doStuffOnPostPersist ] + postPersist: [ doStuffOnPostPersist ] XML would look something like this: .. code-block:: xml - + - + - + - + - + You just need to make sure a public ``doStuffOnPrePersist()`` and @@ -315,16 +327,16 @@ model. getObject(); + $entityManager = $args->getObjectManager(); + + // perhaps you only want to act on some "Product" entity + if ($entity instanceof Product) { + // do something with the Product + } + } + } + +A lifecycle event subscriber may looks like this: + +.. code-block:: php + + getObject(); + $entityManager = $args->getObjectManager(); + + // perhaps you only want to act on some "Product" entity + if ($entity instanceof Product) { + // do something with the Product + } + } + +.. note:: + + Lifecycle events are triggered for all entities. It is the responsibility + of the listeners and subscribers to check if the entity is of a type + it wants to handle. + +To register an event listener or subscriber, you have to hook it into the EventManager that is passed to the EntityManager factory: .. code-block:: php @@ -380,7 +451,7 @@ EventManager that is passed to the EntityManager factory: $eventManager = new EventManager(); $eventManager->addEventListener(array(Events::preUpdate), new MyEventListener()); $eventManager->addEventSubscriber(new MyEventSubscriber()); - + $entityManager = EntityManager::create($dbOpts, $config, $eventManager); You can also retrieve the event manager instance after the @@ -451,8 +522,8 @@ called during a flush operation. preFlush ~~~~~~~~ -``preFlush`` is called at ``EntityManager#flush()`` before -anything else. ``EntityManager#flush()`` can be called safely +``preFlush`` is called at ``EntityManager#flush()`` before +anything else. ``EntityManager#flush()`` can be called safely inside its listeners. .. code-block:: php @@ -497,25 +568,25 @@ mentioned sets. See this example: { $em = $eventArgs->getEntityManager(); $uow = $em->getUnitOfWork(); - + foreach ($uow->getScheduledEntityInsertions() AS $entity) { - + } - + foreach ($uow->getScheduledEntityUpdates() AS $entity) { - + } - + foreach ($uow->getScheduledEntityDeletions() AS $entity) { - + } - + foreach ($uow->getScheduledCollectionDeletions() AS $col) { - + } - + foreach ($uow->getScheduledCollectionUpdates() AS $col) { - + } } } @@ -615,7 +686,7 @@ lifecycle callback when there are expensive validations to call: } } } - + private function validateCreditCard($no) { // throw an exception to interrupt flush event. Transaction will be rolled back. @@ -788,7 +859,7 @@ you should map the listener method using the event type mapping. postRemove: [postRemoveHandler] preRemove: [preRemoveHandler] # .... - + Entity listeners resolver @@ -867,7 +938,7 @@ process and manipulate the instance. $metadataFactory = $em->getMetadataFactory(); $evm = $em->getEventManager(); $evm->addEventListener(Events::loadClassMetadata, $test); - + class EventTest { public function loadClassMetadata(\Doctrine\ORM\Event\LoadClassMetadataEventArgs $eventArgs) From 204c1afe9ad5bc30afd11ba5800c900c825ba45a Mon Sep 17 00:00:00 2001 From: David Buchmann Date: Thu, 21 Mar 2013 10:43:01 +0100 Subject: [PATCH 046/332] cleanup event subscriber note --- docs/en/reference/events.rst | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/docs/en/reference/events.rst b/docs/en/reference/events.rst index ca3578cde..517f2ec5b 100644 --- a/docs/en/reference/events.rst +++ b/docs/en/reference/events.rst @@ -99,11 +99,9 @@ array of events it should be subscribed to. .. note:: - If you are familiar with the Symfony2 event manager, note that - the array returned by getSubscribedEvents is different. For - doctrine, the array values must be the event names, and the - names are used as method names that will be called when the - event occurs. + The array to return in the ``getSubscribedEvents`` method is a simple array + with the values being the event names. The subscriber must have a method + that is named exactly like the event. Now when you dispatch an event, any event subscribers will be notified for that event. @@ -413,6 +411,7 @@ A lifecycle event subscriber may looks like this: .. code-block:: php Date: Thu, 21 Mar 2013 15:49:00 +0100 Subject: [PATCH 047/332] Import EntityManager in ConsoleRunner --- lib/Doctrine/ORM/Tools/Console/ConsoleRunner.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Doctrine/ORM/Tools/Console/ConsoleRunner.php b/lib/Doctrine/ORM/Tools/Console/ConsoleRunner.php index f263a4f0b..077ae8930 100644 --- a/lib/Doctrine/ORM/Tools/Console/ConsoleRunner.php +++ b/lib/Doctrine/ORM/Tools/Console/ConsoleRunner.php @@ -22,6 +22,7 @@ namespace Doctrine\ORM\Tools\Console; use Symfony\Component\Console\Application; use Symfony\Component\Console\Helper\HelperSet; use Doctrine\ORM\Version; +use Doctrine\ORM\EntityManager; use Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper; use Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper; From 94ceb0e410ea995e858fa18246cf53ee53d02c59 Mon Sep 17 00:00:00 2001 From: Chuan Ma Date: Fri, 22 Mar 2013 17:52:37 -0400 Subject: [PATCH 048/332] Document: delete the doc for a number of cache provider methods that don't exist --- docs/en/reference/caching.rst | 54 ----------------------------------- 1 file changed, 54 deletions(-) diff --git a/docs/en/reference/caching.rst b/docs/en/reference/caching.rst index a008aa558..1e87efaff 100644 --- a/docs/en/reference/caching.rst +++ b/docs/en/reference/caching.rst @@ -211,49 +211,6 @@ By Cache ID delete('my_array'); -You can also pass wild cards to the ``delete()`` method and it will -return an array of IDs that were matched and deleted. - -.. code-block:: php - - delete('users_*'); - -By Regular Expression -^^^^^^^^^^^^^^^^^^^^^ - -If you need a little more control than wild cards you can use a PHP -regular expression to delete cache entries. - -.. code-block:: php - - deleteByRegex('/users_.*/'); - -By Prefix -^^^^^^^^^ - -Because regular expressions are kind of slow, if simply deleting by -a prefix or suffix is sufficient, it is recommended that you do -that instead of using a regular expression because it will be much -faster if you have many cache entries. - -.. code-block:: php - - deleteByPrefix('users_'); - -By Suffix -^^^^^^^^^ - -Just like we did above with the prefix you can do the same with a -suffix. - -.. code-block:: php - - deleteBySuffix('_my_account'); - All ^^^ @@ -265,17 +222,6 @@ the ``deleteAll()`` method. deleteAll(); -Counting -~~~~~~~~ - -If you want to count how many entries are stored in the cache -driver instance you can use the ``count()`` method. - -.. code-block:: php - - count(); - Namespaces ~~~~~~~~~~ From 10c48bad7b79af84855f7c63590f1f426770ed6b Mon Sep 17 00:00:00 2001 From: Fran Moreno Date: Sun, 24 Mar 2013 02:07:05 +0100 Subject: [PATCH 049/332] [Docs] Fix field name in inversedby parameter --- docs/en/reference/working-with-associations.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/reference/working-with-associations.rst b/docs/en/reference/working-with-associations.rst index 7f46eba6a..0efba84f2 100644 --- a/docs/en/reference/working-with-associations.rst +++ b/docs/en/reference/working-with-associations.rst @@ -93,7 +93,7 @@ information about its type and if it's the owning or inverse side. /** * Bidirectional - Many Comments are authored by one user (OWNING SIDE) * - * @ManyToOne(targetEntity="User", inversedBy="authoredComments") + * @ManyToOne(targetEntity="User", inversedBy="commentsAuthored") */ private $author; } From d4061ff41b811666c68923a70d35f71dc9204486 Mon Sep 17 00:00:00 2001 From: Dustin Moorman Date: Sun, 24 Mar 2013 03:16:00 -0500 Subject: [PATCH 050/332] Fixed typos in Doctrine Mapping Types section. --- docs/en/reference/basic-mapping.rst | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/en/reference/basic-mapping.rst b/docs/en/reference/basic-mapping.rst index adf2ff0dd..2909048dd 100644 --- a/docs/en/reference/basic-mapping.rst +++ b/docs/en/reference/basic-mapping.rst @@ -138,32 +138,32 @@ Doctrine Mapping Types ---------------------- A Doctrine Mapping Type defines the mapping between a PHP type and -an SQL type. All Doctrine Mapping Types that ship with Doctrine are +a SQL type. All Doctrine Mapping Types that ship with Doctrine are fully portable between different RDBMS. You can even write your own custom mapping types that might or might not be portable, which is explained later in this chapter. For example, the Doctrine Mapping Type ``string`` defines the -mapping from a PHP string to an SQL VARCHAR (or VARCHAR2 etc. +mapping from a PHP string to a SQL VARCHAR (or VARCHAR2 etc. depending on the RDBMS brand). Here is a quick overview of the built-in mapping types: -- ``string``: Type that maps an SQL VARCHAR to a PHP string. -- ``integer``: Type that maps an SQL INT to a PHP integer. +- ``string``: Type that maps a SQL VARCHAR to a PHP string. +- ``integer``: Type that maps a SQL INT to a PHP integer. - ``smallint``: Type that maps a database SMALLINT to a PHP integer. - ``bigint``: Type that maps a database BIGINT to a PHP string. -- ``boolean``: Type that maps an SQL boolean to a PHP boolean. -- ``decimal``: Type that maps an SQL DECIMAL to a PHP string. -- ``date``: Type that maps an SQL DATETIME to a PHP DateTime +- ``boolean``: Type that maps a SQL boolean to a PHP boolean. +- ``decimal``: Type that maps a SQL DECIMAL to a PHP string. +- ``date``: Type that maps a SQL DATETIME to a PHP DateTime object. -- ``time``: Type that maps an SQL TIME to a PHP DateTime object. -- ``datetime``: Type that maps an SQL DATETIME/TIMESTAMP to a PHP +- ``time``: Type that maps a SQL TIME to a PHP DateTime object. +- ``datetime``: Type that maps a SQL DATETIME/TIMESTAMP to a PHP DateTime object. -- ``datetimetz``: Type that maps an SQL DATETIME/TIMESTAMP to a PHP +- ``datetimetz``: Type that maps a SQL DATETIME/TIMESTAMP to a PHP DateTime object with timezone. -- ``text``: Type that maps an SQL CLOB to a PHP string. +- ``text``: Type that maps a SQL CLOB to a PHP string. - ``object``: Type that maps a SQL CLOB to a PHP object using ``serialize()`` and ``unserialize()`` - ``array``: Type that maps a SQL CLOB to a PHP array using @@ -178,7 +178,7 @@ built-in mapping types: decimal points as separator. - ``guid``: Type that maps a database GUID/UUID to a PHP string. Defaults to varchar but uses a specific type if the platform supports it. -- ``blob``: Type that maps an SQL BLOB to a PHP resource stream +- ``blob``: Type that maps a SQL BLOB to a PHP resource stream .. note:: From 57020322cb3c9788c8f8f673177727da8055e245 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Sun, 24 Mar 2013 19:40:53 +0100 Subject: [PATCH 051/332] Adding failing test for DDC-2359 Doctrine\ORM\Mapping\ClassMetadataFactory#wakeupReflection is called twice --- .../ORM/Functional/Ticket/DDC2359Test.php | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2359Test.php diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2359Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2359Test.php new file mode 100644 index 000000000..b9670d27a --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2359Test.php @@ -0,0 +1,55 @@ +getMock('Doctrine\\Common\\Persistence\\Mapping\\Driver\\MappingDriver'); + $mockMetadata = $this->getMock('Doctrine\\ORM\\Mapping\\ClassMetadata', array(), array(), '', false); + $entityManager = $this->getMock('Doctrine\\ORM\\EntityManager', array(), array(), '', false); + /* @var $metadataFactory \Doctrine\ORM\Mapping\ClassMetadataFactory|\PHPUnit_Framework_MockObject_MockObject */ + $metadataFactory = $this->getMock( + 'Doctrine\\ORM\\Mapping\\ClassMetadataFactory', + array('newClassMetadataInstance', 'wakeupReflection') + ); + $configuration = $this->getMock('Doctrine\\ORM\\Configuration'); + $connection = $this->getMock('Doctrine\\DBAL\\Connection', array(), array(), '', false); + + $configuration + ->expects($this->any()) + ->method('getMetadataDriverImpl') + ->will($this->returnValue($mockDriver)); + $entityManager->expects($this->any())->method('getConfiguration')->will($this->returnValue($configuration)); + $entityManager->expects($this->any())->method('getConnection')->will($this->returnValue($connection)); + $entityManager + ->expects($this->any()) + ->method('getEventManager') + ->will($this->returnValue($this->getMock('Doctrine\\Common\\EventManager'))); + + $metadataFactory->expects($this->any())->method('newClassMetadataInstance')->will($this->returnValue($mockMetadata)); + $metadataFactory->expects($this->once())->method('wakeupReflection'); + + $metadataFactory->setEntityManager($entityManager); + + $this->assertSame($mockMetadata, $metadataFactory->getMetadataFor(__NAMESPACE__ . '\\DDC2359Foo')); + } +} + +/** @Entity */ +class DDC2359Foo +{ + /** @Id @Column(type="integer") @GeneratedValue */ + public $id; +} \ No newline at end of file From 7afe5af73a4422d06283b67a5f9c55e3866cc128 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Sun, 24 Mar 2013 19:42:50 +0100 Subject: [PATCH 052/332] Fix for DDC-2359 --- lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php index 242a9099f..a1da3ffcf 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php @@ -171,7 +171,6 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory $this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs); } - $this->wakeupReflection($class, $this->getReflectionService()); $this->validateRuntimeMetadata($class, $parent); } From 827f1f84cb3856d72c53e8cbfd0c90eeef04f7d9 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 24 Mar 2013 19:58:19 +0100 Subject: [PATCH 053/332] Release 2.4.0-BETA1 --- composer.json | 4 ++-- lib/Doctrine/ORM/Version.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index 89d62f2f6..cb46a2378 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "doctrine/orm", - "type": "library", + "type": "library","version":"2.4.0-BETA1", "description": "Object-Relational-Mapper for PHP", "keywords": ["orm", "database"], "homepage": "http://www.doctrine-project.org", @@ -16,7 +16,7 @@ "php": ">=5.3.2", "ext-pdo": "*", "doctrine/collections": "~1.1", - "doctrine/dbal": ">=2.4-dev,<2.5-dev", + "doctrine/dbal": ">=2.4-beta,<2.5-dev", "symfony/console": "2.*" }, "suggest": { diff --git a/lib/Doctrine/ORM/Version.php b/lib/Doctrine/ORM/Version.php index 4dba7902f..522f68b6f 100644 --- a/lib/Doctrine/ORM/Version.php +++ b/lib/Doctrine/ORM/Version.php @@ -36,7 +36,7 @@ class Version /** * Current Doctrine Version */ - const VERSION = '2.4.0-DEV'; + const VERSION = '2.4.0-BETA1'; /** * Compares a Doctrine version with the current one. From 7f26e9ac27024682cabcb435452690174d1ba7bc Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 24 Mar 2013 19:58:19 +0100 Subject: [PATCH 054/332] Bump dev version to 2.4.0 --- composer.json | 2 +- lib/Doctrine/ORM/Version.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index cb46a2378..52960133d 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "doctrine/orm", - "type": "library","version":"2.4.0-BETA1", + "type": "library", "description": "Object-Relational-Mapper for PHP", "keywords": ["orm", "database"], "homepage": "http://www.doctrine-project.org", diff --git a/lib/Doctrine/ORM/Version.php b/lib/Doctrine/ORM/Version.php index 522f68b6f..4dba7902f 100644 --- a/lib/Doctrine/ORM/Version.php +++ b/lib/Doctrine/ORM/Version.php @@ -36,7 +36,7 @@ class Version /** * Current Doctrine Version */ - const VERSION = '2.4.0-BETA1'; + const VERSION = '2.4.0-DEV'; /** * Compares a Doctrine version with the current one. From 3e8dd1e45c083eca14886a50d71d61d0d6b69df2 Mon Sep 17 00:00:00 2001 From: Aaron Muylaert Date: Tue, 26 Mar 2013 16:47:41 +0100 Subject: [PATCH 055/332] Update query-builder.rst Fix typo in one of the orderBy examples. --- docs/en/reference/query-builder.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/reference/query-builder.rst b/docs/en/reference/query-builder.rst index 7d9ee9a8a..8029822b5 100644 --- a/docs/en/reference/query-builder.rst +++ b/docs/en/reference/query-builder.rst @@ -222,7 +222,7 @@ alternative syntax is available: $qb->select('u') ->from('User u') ->where('u.id = :identifier') - ->orderBy('u.name ASC'); + ->orderBy('u.name', 'ASC'); ->setParameter('identifier', 100); // Sets :identifier to 100, and thus we will fetch a user with u.id = 100 Note that numeric placeholders start with a ? followed by a number From 521276f1ede77794f71b3e4cd3c315ac668ca412 Mon Sep 17 00:00:00 2001 From: Adam Prager Date: Tue, 26 Mar 2013 20:52:57 +0100 Subject: [PATCH 056/332] entity generator - ignore trait properties and methods --- lib/Doctrine/ORM/Tools/EntityGenerator.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/lib/Doctrine/ORM/Tools/EntityGenerator.php b/lib/Doctrine/ORM/Tools/EntityGenerator.php index 082720bf7..945bbd935 100644 --- a/lib/Doctrine/ORM/Tools/EntityGenerator.php +++ b/lib/Doctrine/ORM/Tools/EntityGenerator.php @@ -709,6 +709,15 @@ public function __construct() } } + // check traits for existing property + $reflClass = new \ReflectionClass($metadata->name); + + foreach ($reflClass->getTraits() as $trait) { + if ($trait->hasProperty($property)) { + return true; + } + } + return ( isset($this->staticReflection[$metadata->name]) && in_array($property, $this->staticReflection[$metadata->name]['properties']) @@ -732,6 +741,15 @@ public function __construct() } } + // check traits for existing method + $reflClass = new \ReflectionClass($metadata->name); + + foreach ($reflClass->getTraits() as $trait) { + if ($trait->hasMethod($method)) { + return true; + } + } + return ( isset($this->staticReflection[$metadata->name]) && in_array($method, $this->staticReflection[$metadata->name]['methods']) From 02d34bbba6e3055b6b72918a3f08d43d4f29469e Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Tue, 19 Feb 2013 19:04:13 +0100 Subject: [PATCH 057/332] [DDC-93] Started ValueObjectsTest --- .../Tests/ORM/Functional/ValueObjectsTest.php | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 tests/Doctrine/Tests/ORM/Functional/ValueObjectsTest.php diff --git a/tests/Doctrine/Tests/ORM/Functional/ValueObjectsTest.php b/tests/Doctrine/Tests/ORM/Functional/ValueObjectsTest.php new file mode 100644 index 000000000..916f06bf9 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/ValueObjectsTest.php @@ -0,0 +1,25 @@ + Date: Tue, 26 Mar 2013 21:14:52 +0100 Subject: [PATCH 058/332] only use already existing reflections --- lib/Doctrine/ORM/Tools/EntityGenerator.php | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/lib/Doctrine/ORM/Tools/EntityGenerator.php b/lib/Doctrine/ORM/Tools/EntityGenerator.php index 945bbd935..97a40e8ad 100644 --- a/lib/Doctrine/ORM/Tools/EntityGenerator.php +++ b/lib/Doctrine/ORM/Tools/EntityGenerator.php @@ -710,11 +710,13 @@ public function __construct() } // check traits for existing property - $reflClass = new \ReflectionClass($metadata->name); + if (isset($this->staticReflection[$metadata->name])) { + $reflClass = $this->staticReflection[$metadata->name]; - foreach ($reflClass->getTraits() as $trait) { - if ($trait->hasProperty($property)) { - return true; + foreach ($reflClass->getTraits() as $trait) { + if ($trait->hasProperty($property)) { + return true; + } } } @@ -742,11 +744,13 @@ public function __construct() } // check traits for existing method - $reflClass = new \ReflectionClass($metadata->name); + if (isset($this->staticReflection[$metadata->name])) { + $reflClass = $this->staticReflection[$metadata->name]; - foreach ($reflClass->getTraits() as $trait) { - if ($trait->hasMethod($method)) { - return true; + foreach ($reflClass->getTraits() as $trait) { + if ($trait->hasMethod($method)) { + return true; + } } } From 8e3e2e770a2271caf15899cb3f9a840d94443fa7 Mon Sep 17 00:00:00 2001 From: Adam Prager Date: Tue, 26 Mar 2013 21:17:59 +0100 Subject: [PATCH 059/332] Revert "only use already existing reflections" This reverts commit bb5bdcf0f4ccd6fc83692490ec70a697c90573fb. --- lib/Doctrine/ORM/Tools/EntityGenerator.php | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/lib/Doctrine/ORM/Tools/EntityGenerator.php b/lib/Doctrine/ORM/Tools/EntityGenerator.php index 97a40e8ad..945bbd935 100644 --- a/lib/Doctrine/ORM/Tools/EntityGenerator.php +++ b/lib/Doctrine/ORM/Tools/EntityGenerator.php @@ -710,13 +710,11 @@ public function __construct() } // check traits for existing property - if (isset($this->staticReflection[$metadata->name])) { - $reflClass = $this->staticReflection[$metadata->name]; + $reflClass = new \ReflectionClass($metadata->name); - foreach ($reflClass->getTraits() as $trait) { - if ($trait->hasProperty($property)) { - return true; - } + foreach ($reflClass->getTraits() as $trait) { + if ($trait->hasProperty($property)) { + return true; } } @@ -744,13 +742,11 @@ public function __construct() } // check traits for existing method - if (isset($this->staticReflection[$metadata->name])) { - $reflClass = $this->staticReflection[$metadata->name]; + $reflClass = new \ReflectionClass($metadata->name); - foreach ($reflClass->getTraits() as $trait) { - if ($trait->hasMethod($method)) { - return true; - } + foreach ($reflClass->getTraits() as $trait) { + if ($trait->hasMethod($method)) { + return true; } } From 8898c91dfc1c6a433fc6cb1de5235c35a46f25b7 Mon Sep 17 00:00:00 2001 From: Adam Prager Date: Tue, 26 Mar 2013 21:28:09 +0100 Subject: [PATCH 060/332] only check for traits when class exists --- lib/Doctrine/ORM/Tools/EntityGenerator.php | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/lib/Doctrine/ORM/Tools/EntityGenerator.php b/lib/Doctrine/ORM/Tools/EntityGenerator.php index 945bbd935..2946935b3 100644 --- a/lib/Doctrine/ORM/Tools/EntityGenerator.php +++ b/lib/Doctrine/ORM/Tools/EntityGenerator.php @@ -710,11 +710,13 @@ public function __construct() } // check traits for existing property - $reflClass = new \ReflectionClass($metadata->name); + if (class_exists($metadata->name)) { + $reflClass = new \ReflectionClass($metadata->name); - foreach ($reflClass->getTraits() as $trait) { - if ($trait->hasProperty($property)) { - return true; + foreach ($reflClass->getTraits() as $trait) { + if ($trait->hasProperty($property)) { + return true; + } } } @@ -742,11 +744,13 @@ public function __construct() } // check traits for existing method - $reflClass = new \ReflectionClass($metadata->name); + if (class_exists($metadata->name)) { + $reflClass = new \ReflectionClass($metadata->name); - foreach ($reflClass->getTraits() as $trait) { - if ($trait->hasMethod($method)) { - return true; + foreach ($reflClass->getTraits() as $trait) { + if ($trait->hasMethod($method)) { + return true; + } } } From 32988b3cdf050f2531c4d4f6d85971f896cd8303 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Tue, 26 Mar 2013 21:45:15 +0100 Subject: [PATCH 061/332] [DDC-93] Parse @Embedded and @Embeddable during SchemaTool processing to make parsing work. --- .../ORM/Mapping/ClassMetadataInfo.php | 13 ++++- .../ORM/Mapping/Driver/AnnotationDriver.php | 2 + .../Mapping/Driver/DoctrineAnnotations.php | 4 +- lib/Doctrine/ORM/Mapping/Embeddable.php | 28 ++++++++++ lib/Doctrine/ORM/Mapping/Embedded.php | 32 +++++++++++ lib/Doctrine/ORM/Tools/SchemaTool.php | 1 + .../Tests/ORM/Functional/ValueObjectsTest.php | 53 ++++++++++++++++++- 7 files changed, 130 insertions(+), 3 deletions(-) create mode 100644 lib/Doctrine/ORM/Mapping/Embeddable.php create mode 100644 lib/Doctrine/ORM/Mapping/Embedded.php diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php index 1e038d659..63c324a62 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php @@ -246,6 +246,13 @@ class ClassMetadataInfo implements ClassMetadata */ public $isMappedSuperclass = false; + /** + * READ-ONLY: Wheather this class describes the mapping of an embeddable class. + * + * @var boolean + */ + public $isEmbeddedClass = false; + /** * READ-ONLY: The names of the parent classes (ancestors). * @@ -921,8 +928,12 @@ class ClassMetadataInfo implements ClassMetadata */ public function validateIdentifier() { + if ($this->isMappedSuperclass || $this->isEmbeddedClass) { + return; + } + // Verify & complete identifier mapping - if ( ! $this->identifier && ! $this->isMappedSuperclass) { + if ( ! $this->identifier) { throw MappingException::identifierRequired($this->name); } diff --git a/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php b/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php index 9e1c734cc..56b693e67 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php @@ -85,6 +85,8 @@ class AnnotationDriver extends AbstractAnnotationDriver $mappedSuperclassAnnot = $classAnnotations['Doctrine\ORM\Mapping\MappedSuperclass']; $metadata->setCustomRepositoryClass($mappedSuperclassAnnot->repositoryClass); $metadata->isMappedSuperclass = true; + } else if (isset($classAnnotations['Doctrine\ORM\Mapping\Embeddable'])) { + $metadata->isEmbeddedClass = true; } else { throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className); } diff --git a/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php b/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php index 14abadb9e..3ba65f772 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php +++ b/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php @@ -19,6 +19,8 @@ require_once __DIR__.'/../Annotation.php'; require_once __DIR__.'/../Entity.php'; +require_once __DIR__.'/../Embeddable.php'; +require_once __DIR__.'/../Embedded.php'; require_once __DIR__.'/../MappedSuperclass.php'; require_once __DIR__.'/../InheritanceType.php'; require_once __DIR__.'/../DiscriminatorColumn.php'; @@ -64,4 +66,4 @@ require_once __DIR__.'/../AssociationOverride.php'; require_once __DIR__.'/../AssociationOverrides.php'; require_once __DIR__.'/../AttributeOverride.php'; require_once __DIR__.'/../AttributeOverrides.php'; -require_once __DIR__.'/../EntityListeners.php'; \ No newline at end of file +require_once __DIR__.'/../EntityListeners.php'; diff --git a/lib/Doctrine/ORM/Mapping/Embeddable.php b/lib/Doctrine/ORM/Mapping/Embeddable.php new file mode 100644 index 000000000..34cdcd1f1 --- /dev/null +++ b/lib/Doctrine/ORM/Mapping/Embeddable.php @@ -0,0 +1,28 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("PROPERTY") + */ +final class Embeddable implements Annotation +{ +} diff --git a/lib/Doctrine/ORM/Mapping/Embedded.php b/lib/Doctrine/ORM/Mapping/Embedded.php new file mode 100644 index 000000000..c3dcb0837 --- /dev/null +++ b/lib/Doctrine/ORM/Mapping/Embedded.php @@ -0,0 +1,32 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("CLASS") + */ +final class Embedded implements Annotation +{ + /** + * @var string + */ + public $class; +} diff --git a/lib/Doctrine/ORM/Tools/SchemaTool.php b/lib/Doctrine/ORM/Tools/SchemaTool.php index f66a49d15..a8baa02aa 100644 --- a/lib/Doctrine/ORM/Tools/SchemaTool.php +++ b/lib/Doctrine/ORM/Tools/SchemaTool.php @@ -126,6 +126,7 @@ class SchemaTool return ( isset($processedClasses[$class->name]) || $class->isMappedSuperclass || + $class->isEmbeddedClass || ($class->isInheritanceTypeSingleTable() && $class->name != $class->rootEntityName) ); } diff --git a/tests/Doctrine/Tests/ORM/Functional/ValueObjectsTest.php b/tests/Doctrine/Tests/ORM/Functional/ValueObjectsTest.php index 916f06bf9..ac0f332a1 100644 --- a/tests/Doctrine/Tests/ORM/Functional/ValueObjectsTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/ValueObjectsTest.php @@ -2,10 +2,37 @@ namespace Doctrine\Tests\ORM\Functional; +/** + * @group DDC-93 + */ class ValueObjectsTest extends \Doctrine\Tests\OrmFunctionalTestCase { public function setUp() { + parent::setUp(); + + $this->_schemaTool->createSchema(array( + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC93Person'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC93Address'), + )); + } + + public function testMetadata() + { + $person = new DDC93Person(); + $person->name = "Tara"; + $person->address = new DDC93Address(); + $person->address->street = "United States of Tara Street"; + $person->address->zip = "12345"; + $person->address->city = "funkytown"; + + $this->_em->persist($person); + $this->_em->flush(); + + $this->_em->clear(); + + $person = $this->_em->find(DDC93Person::CLASSNAME, $person->id); + $this->assertInstanceOf(DDC93Address::CLASSNAME, $person->address); } } @@ -14,12 +41,36 @@ class ValueObjectsTest extends \Doctrine\Tests\OrmFunctionalTestCase */ class DDC93Person { + const CLASSNAME = __CLASS__; + /** @Id @GeneratedValue @Column(type="integer") */ public $id; /** @Column(type="string") */ public $name; - /** @Embedded */ + /** @Embedded(class="DDC93Address") */ public $address; } + +/** + * @Embeddable + */ +class DDC93Address +{ + const CLASSNAME = __CLASS__; + + /** + * @Column(type="string") + */ + public $street; + /** + * @Column(type="string") + */ + public $zip; + /** + * @Column(type="string") + */ + public $city; +} + From 0204a8b69a33883c83a26be108b528fac320c729 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 27 Mar 2013 00:10:30 +0100 Subject: [PATCH 062/332] [DDC-93] Implement first working version of value objects using a ReflectionProxy object, bypassing changes to UnitOfWork, Persisters and Hydrators. --- .../ORM/Mapping/ClassMetadataFactory.php | 5 ++ .../ORM/Mapping/ClassMetadataInfo.php | 70 +++++++++++++++++-- .../ORM/Mapping/Driver/AnnotationDriver.php | 3 + lib/Doctrine/ORM/Mapping/ReflectionProxy.php | 64 +++++++++++++++++ .../Tests/ORM/Functional/ValueObjectsTest.php | 4 ++ 5 files changed, 140 insertions(+), 6 deletions(-) create mode 100644 lib/Doctrine/ORM/Mapping/ReflectionProxy.php diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php index a1da3ffcf..d661eb7a3 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php @@ -136,6 +136,11 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory $this->completeIdGeneratorMapping($class); } + foreach ($class->embeddedClasses as $property => $embeddableClass) { + $embeddableMetadata = $this->getMetadataFor($embeddableClass); + $class->inlineEmbeddable($property, $embeddableMetadata); + } + if ($parent && $parent->isInheritanceTypeSingleTable()) { $class->setPrimaryTable($parent->table); } diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php index 63c324a62..9ab80a72c 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php @@ -267,6 +267,13 @@ class ClassMetadataInfo implements ClassMetadata */ public $subClasses = array(); + /** + * READ-ONLY: The names of all embedded classes based on properties. + * + * @var array + */ + public $embeddedClasses = array(); + /** * READ-ONLY: The named queries allowed to be called directly from Repository. * @@ -887,6 +894,15 @@ class ClassMetadataInfo implements ClassMetadata $this->reflClass = $reflService->getClass($this->name); foreach ($this->fieldMappings as $field => $mapping) { + if (isset($mapping['declaredField'])) { + $this->reflFields[$field] = new ReflectionProxy( + $reflService->getAccessibleProperty($this->name, $mapping['declaredField']), + $reflService->getAccessibleProperty($this->embeddedClasses[$mapping['declaredField']], $mapping['originalField']), + $this->embeddedClasses[$mapping['declaredField']] + ); + continue; + } + $this->reflFields[$field] = isset($mapping['declared']) ? $reflService->getAccessibleProperty($mapping['declared'], $field) : $reflService->getAccessibleProperty($this->name, $field); @@ -2166,9 +2182,8 @@ class ClassMetadataInfo implements ClassMetadata public function mapField(array $mapping) { $this->_validateAndCompleteFieldMapping($mapping); - if (isset($this->fieldMappings[$mapping['fieldName']]) || isset($this->associationMappings[$mapping['fieldName']])) { - throw MappingException::duplicateFieldMapping($this->name, $mapping['fieldName']); - } + $this->assertFieldNotMapped($mapping['fieldName']); + $this->fieldMappings[$mapping['fieldName']] = $mapping; } @@ -2416,9 +2431,7 @@ class ClassMetadataInfo implements ClassMetadata { $sourceFieldName = $assocMapping['fieldName']; - if (isset($this->fieldMappings[$sourceFieldName]) || isset($this->associationMappings[$sourceFieldName])) { - throw MappingException::duplicateFieldMapping($this->name, $sourceFieldName); - } + $this->assertFieldNotMapped($sourceFieldName); $this->associationMappings[$sourceFieldName] = $assocMapping; } @@ -3030,4 +3043,49 @@ class ClassMetadataInfo implements ClassMetadata return $className; } + + /** + * Map Embedded Class + * + * @array $mapping + * @return void + */ + public function mapEmbedded(array $mapping) + { + $this->assertFieldNotMapped($mapping['fieldName']); + + $this->embeddedClasses[$mapping['fieldName']] = $this->fullyQualifiedClassName($mapping['class']); + } + + /** + * Inline the embeddable class + * + * @param string $property + * @param ClassMetadataInfo $embeddable + */ + public function inlineEmbeddable($property, ClassMetadataInfo $embeddable) + { + foreach ($embeddable->fieldMappings as $fieldMapping) { + $fieldMapping['declaredField'] = $property; + $fieldMapping['originalField'] = $fieldMapping['fieldName']; + $fieldMapping['fieldName'] = $property . $fieldMapping['fieldName']; + $fieldMapping['columnName'] = $property . "_" . $fieldMapping['columnName']; + + $this->mapField($fieldMapping); + } + } + + /** + * @param string $fieldName + * @throws MappingException + */ + private function assertFieldNotMapped($fieldName) + { + if (isset($this->fieldMappings[$fieldName]) || + isset($this->associationMappings[$fieldName]) || + isset($this->embeddedClasses[$fieldName])) { + + throw MappingException::duplicateFieldMapping($this->name, $fieldName); + } + } } diff --git a/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php b/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php index 56b693e67..2529edeed 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php @@ -366,6 +366,9 @@ class AnnotationDriver extends AbstractAnnotationDriver } $metadata->mapManyToMany($mapping); + } else if ($embeddedAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\Embedded')) { + $mapping['class'] = $embeddedAnnot->class; + $metadata->mapEmbedded($mapping); } } diff --git a/lib/Doctrine/ORM/Mapping/ReflectionProxy.php b/lib/Doctrine/ORM/Mapping/ReflectionProxy.php new file mode 100644 index 000000000..9d391d9a8 --- /dev/null +++ b/lib/Doctrine/ORM/Mapping/ReflectionProxy.php @@ -0,0 +1,64 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * Acts as a proxy to a nested Property structure, making it look like + * just a single scalar property. + * + * This way value objects "just work" without UnitOfWork, Persisters or Hydrators + * needing any changes. + */ +class ReflectionProxy +{ + private $parentProperty; + private $childProperty; + private $class; + + public function __construct($parentProperty, $childProperty, $class) + { + $this->parentProperty = $parentProperty; + $this->childProperty = $childProperty; + $this->class = $class; + } + + public function getValue($object) + { + $embeddedObject = $this->parentProperty->getValue($object); + + if ($embeddedObject === null) { + return null; + } + + return $this->childProperty->getValue($embeddedObject); + } + + public function setValue($object, $value) + { + $embeddedObject = $this->parentProperty->getValue($object); + + if ($embeddedObject === null) { + $embeddedObject = new $this->class; // TODO + $this->parentProperty->setValue($object, $embeddedObject); + } + + $this->childProperty->setValue($embeddedObject, $value); + } +} diff --git a/tests/Doctrine/Tests/ORM/Functional/ValueObjectsTest.php b/tests/Doctrine/Tests/ORM/Functional/ValueObjectsTest.php index ac0f332a1..26f129c71 100644 --- a/tests/Doctrine/Tests/ORM/Functional/ValueObjectsTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/ValueObjectsTest.php @@ -32,7 +32,11 @@ class ValueObjectsTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->_em->clear(); $person = $this->_em->find(DDC93Person::CLASSNAME, $person->id); + $this->assertInstanceOf(DDC93Address::CLASSNAME, $person->address); + $this->assertEquals('United States of Tara Street', $person->address->street); + $this->assertEquals('12345', $person->address->zip); + $this->assertEquals('funkytown', $person->address->city); } } From 011776f02ffa279afc0016b2de76b662b87dfc95 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 27 Mar 2013 00:18:21 +0100 Subject: [PATCH 063/332] [DDC-93] Add some TODOs in code. --- lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php index 9ab80a72c..8a444d9ae 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php @@ -3068,8 +3068,8 @@ class ClassMetadataInfo implements ClassMetadata foreach ($embeddable->fieldMappings as $fieldMapping) { $fieldMapping['declaredField'] = $property; $fieldMapping['originalField'] = $fieldMapping['fieldName']; - $fieldMapping['fieldName'] = $property . $fieldMapping['fieldName']; - $fieldMapping['columnName'] = $property . "_" . $fieldMapping['columnName']; + $fieldMapping['fieldName'] = $property . "." . $fieldMapping['fieldName']; // TODO: Change DQL parser to accept this dot notation + $fieldMapping['columnName'] = $property . "_" . $fieldMapping['columnName']; // TODO: Use naming strategy $this->mapField($fieldMapping); } From 9797177193b4757df7d3f06cd60c7e06e559aa74 Mon Sep 17 00:00:00 2001 From: Adam Prager Date: Wed, 27 Mar 2013 02:48:35 +0100 Subject: [PATCH 064/332] check if ReflectionClass::getTraits() method exists --- lib/Doctrine/ORM/Tools/EntityGenerator.php | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/Doctrine/ORM/Tools/EntityGenerator.php b/lib/Doctrine/ORM/Tools/EntityGenerator.php index 2946935b3..6aad04fb6 100644 --- a/lib/Doctrine/ORM/Tools/EntityGenerator.php +++ b/lib/Doctrine/ORM/Tools/EntityGenerator.php @@ -713,9 +713,11 @@ public function __construct() if (class_exists($metadata->name)) { $reflClass = new \ReflectionClass($metadata->name); - foreach ($reflClass->getTraits() as $trait) { - if ($trait->hasProperty($property)) { - return true; + if (method_exists($reflClass, 'getTraits')) { + foreach ($reflClass->getTraits() as $trait) { + if ($trait->hasProperty($property)) { + return true; + } } } } @@ -747,9 +749,11 @@ public function __construct() if (class_exists($metadata->name)) { $reflClass = new \ReflectionClass($metadata->name); - foreach ($reflClass->getTraits() as $trait) { - if ($trait->hasMethod($method)) { - return true; + if (method_exists($reflClass, 'getTraits')) { + foreach ($reflClass->getTraits() as $trait) { + if ($trait->hasMethod($method)) { + return true; + } } } } From 879ab6e52b1d20565074b5abfeb995926505461a Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 27 Mar 2013 07:44:47 +0100 Subject: [PATCH 065/332] [DDC-93] Show CRUD with value objects with current change tracking assumptions. --- .../Tests/ORM/Functional/ValueObjectsTest.php | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/tests/Doctrine/Tests/ORM/Functional/ValueObjectsTest.php b/tests/Doctrine/Tests/ORM/Functional/ValueObjectsTest.php index 26f129c71..d1f81189c 100644 --- a/tests/Doctrine/Tests/ORM/Functional/ValueObjectsTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/ValueObjectsTest.php @@ -17,7 +17,7 @@ class ValueObjectsTest extends \Doctrine\Tests\OrmFunctionalTestCase )); } - public function testMetadata() + public function testCRUD() { $person = new DDC93Person(); $person->name = "Tara"; @@ -26,17 +26,40 @@ class ValueObjectsTest extends \Doctrine\Tests\OrmFunctionalTestCase $person->address->zip = "12345"; $person->address->city = "funkytown"; + // 1. check saving value objects works $this->_em->persist($person); $this->_em->flush(); $this->_em->clear(); + // 2. check loading value objects works $person = $this->_em->find(DDC93Person::CLASSNAME, $person->id); $this->assertInstanceOf(DDC93Address::CLASSNAME, $person->address); $this->assertEquals('United States of Tara Street', $person->address->street); $this->assertEquals('12345', $person->address->zip); $this->assertEquals('funkytown', $person->address->city); + + // 3. check changing value objects works + $person->address->street = "Street"; + $person->address->zip = "54321"; + $person->address->city = "another town"; + $this->_em->flush(); + + $this->_em->clear(); + + $person = $this->_em->find(DDC93Person::CLASSNAME, $person->id); + + $this->assertEquals('Street', $person->address->street); + $this->assertEquals('54321', $person->address->zip); + $this->assertEquals('another town', $person->address->city); + + // 4. check deleting works + $personId = $person->id;; + $this->_em->remove($person); + $this->_em->flush(); + + $this->assertNull($this->_em->find(DDC93Person::CLASSNAME, $personId)); } } From 9613f1d8cb40c552ea5e57ad12b3adf0a654d167 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 27 Mar 2013 21:45:16 +0100 Subject: [PATCH 066/332] [DDC-93] Rename ReflectionProxy to ReflectionEmbeddedProperty, Add DQL test with Object and Array Hydration. --- .../ORM/Mapping/ClassMetadataInfo.php | 2 +- ...oxy.php => ReflectionEmbeddedProperty.php} | 4 +- .../Tests/ORM/Functional/ValueObjectsTest.php | 48 +++++++++++++++++-- 3 files changed, 48 insertions(+), 6 deletions(-) rename lib/Doctrine/ORM/Mapping/{ReflectionProxy.php => ReflectionEmbeddedProperty.php} (96%) diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php index 8a444d9ae..6a2931fe6 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php @@ -895,7 +895,7 @@ class ClassMetadataInfo implements ClassMetadata foreach ($this->fieldMappings as $field => $mapping) { if (isset($mapping['declaredField'])) { - $this->reflFields[$field] = new ReflectionProxy( + $this->reflFields[$field] = new ReflectionEmbeddedProperty( $reflService->getAccessibleProperty($this->name, $mapping['declaredField']), $reflService->getAccessibleProperty($this->embeddedClasses[$mapping['declaredField']], $mapping['originalField']), $this->embeddedClasses[$mapping['declaredField']] diff --git a/lib/Doctrine/ORM/Mapping/ReflectionProxy.php b/lib/Doctrine/ORM/Mapping/ReflectionEmbeddedProperty.php similarity index 96% rename from lib/Doctrine/ORM/Mapping/ReflectionProxy.php rename to lib/Doctrine/ORM/Mapping/ReflectionEmbeddedProperty.php index 9d391d9a8..b68d7d818 100644 --- a/lib/Doctrine/ORM/Mapping/ReflectionProxy.php +++ b/lib/Doctrine/ORM/Mapping/ReflectionEmbeddedProperty.php @@ -25,8 +25,10 @@ namespace Doctrine\ORM\Mapping; * * This way value objects "just work" without UnitOfWork, Persisters or Hydrators * needing any changes. + * + * TODO: Move this class into Common\Reflection */ -class ReflectionProxy +class ReflectionEmbeddedProperty { private $parentProperty; private $childProperty; diff --git a/tests/Doctrine/Tests/ORM/Functional/ValueObjectsTest.php b/tests/Doctrine/Tests/ORM/Functional/ValueObjectsTest.php index d1f81189c..89a51a406 100644 --- a/tests/Doctrine/Tests/ORM/Functional/ValueObjectsTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/ValueObjectsTest.php @@ -11,10 +11,13 @@ class ValueObjectsTest extends \Doctrine\Tests\OrmFunctionalTestCase { parent::setUp(); - $this->_schemaTool->createSchema(array( - $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC93Person'), - $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC93Address'), - )); + try { + $this->_schemaTool->createSchema(array( + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC93Person'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC93Address'), + )); + } catch(\Exception $e) { + } } public function testCRUD() @@ -61,6 +64,43 @@ class ValueObjectsTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertNull($this->_em->find(DDC93Person::CLASSNAME, $personId)); } + + public function testLoadDql() + { + for ($i = 0; $i < 3; $i++) { + $person = new DDC93Person(); + $person->name = "Donkey Kong$i"; + $person->address = new DDC93Address(); + $person->address->street = "Tree"; + $person->address->zip = "12345"; + $person->address->city = "funkytown"; + + $this->_em->persist($person); + } + + $this->_em->flush(); + $this->_em->clear(); + + $dql = "SELECT p FROM " . __NAMESPACE__ . "\DDC93Person p"; + $persons = $this->_em->createQuery($dql)->getResult(); + + $this->assertCount(3, $persons); + foreach ($persons as $person) { + $this->assertInstanceOf(DDC93Address::CLASSNAME, $person->address); + $this->assertEquals('Tree', $person->address->street); + $this->assertEquals('12345', $person->address->zip); + $this->assertEquals('funkytown', $person->address->city); + } + + $dql = "SELECT p FROM " . __NAMESPACE__ . "\DDC93Person p"; + $persons = $this->_em->createQuery($dql)->getArrayResult(); + + foreach ($persons as $person) { + $this->assertEquals('Tree', $person['address.street']); + $this->assertEquals('12345', $person['address.zip']); + $this->assertEquals('funkytown', $person['address.city']); + } + } } /** From 51bcda51c5593155697f1752b49f88916bdaaaeb Mon Sep 17 00:00:00 2001 From: Dustin Moorman Date: Wed, 27 Mar 2013 22:43:58 -0500 Subject: [PATCH 067/332] Fixed typos in documentation. --- docs/en/reference/dql-doctrine-query-language.rst | 4 ++-- docs/en/reference/faq.rst | 2 +- docs/en/reference/filters.rst | 2 +- docs/en/reference/native-sql.rst | 12 ++++++------ docs/en/reference/transactions-and-concurrency.rst | 2 +- docs/en/reference/unitofwork.rst | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/en/reference/dql-doctrine-query-language.rst b/docs/en/reference/dql-doctrine-query-language.rst index ba694a101..c140d5599 100644 --- a/docs/en/reference/dql-doctrine-query-language.rst +++ b/docs/en/reference/dql-doctrine-query-language.rst @@ -909,7 +909,7 @@ Query Result Formats The format in which the result of a DQL SELECT query is returned can be influenced by a so-called ``hydration mode``. A hydration -mode specifies a particular way in which an SQL result set is +mode specifies a particular way in which a SQL result set is transformed. Each hydration mode has its own dedicated method on the Query class. Here they are: @@ -1290,7 +1290,7 @@ userland: Query Cache (DQL Query Only) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Parsing a DQL query and converting it into an SQL query against the +Parsing a DQL query and converting it into a SQL query against the underlying database platform obviously has some overhead in contrast to directly executing Native SQL queries. That is why there is a dedicated Query Cache for caching the DQL parser diff --git a/docs/en/reference/faq.rst b/docs/en/reference/faq.rst index 800b168bd..28e3a42c4 100644 --- a/docs/en/reference/faq.rst +++ b/docs/en/reference/faq.rst @@ -147,7 +147,7 @@ Why does Doctrine not create proxy objects for my inheritance hierarchy? If you set a many-to-one or one-to-one association target-entity to any parent class of an inheritance hierarchy Doctrine does not know what PHP class the foreign is actually of. -To find this out it has to execute an SQL query to look this information up in the database. +To find this out it has to execute a SQL query to look this information up in the database. EntityGenerator --------------- diff --git a/docs/en/reference/filters.rst b/docs/en/reference/filters.rst index 03623806f..a5c0ee4cf 100644 --- a/docs/en/reference/filters.rst +++ b/docs/en/reference/filters.rst @@ -7,7 +7,7 @@ Doctrine 2.2 features a filter system that allows the developer to add SQL to the conditional clauses of queries, regardless the place where the SQL is generated (e.g. from a DQL query, or by loading associated entities). -The filter functionality works on SQL level. Whether an SQL query is generated +The filter functionality works on SQL level. Whether a SQL query is generated in a Persister, during lazy loading, in extra lazy collections or from DQL. Each time the system iterates over all the enabled filters, adding a new SQL part as a filter returns. diff --git a/docs/en/reference/native-sql.rst b/docs/en/reference/native-sql.rst index b8a91000e..21f0a6ca9 100644 --- a/docs/en/reference/native-sql.rst +++ b/docs/en/reference/native-sql.rst @@ -3,7 +3,7 @@ Native SQL A ``NativeQuery`` lets you execute native SELECT SQL statements, mapping the results according to your specifications. Such a specification that -describes how an SQL result set is mapped to a Doctrine result is +describes how a SQL result set is mapped to a Doctrine result is represented by a ``ResultSetMapping``. It describes how each column of the database result should be mapped by Doctrine in terms of the object graph. This allows you to map arbitrary SQL code to objects, @@ -136,7 +136,7 @@ joined entity result. Field results ~~~~~~~~~~~~~ -A field result describes the mapping of a single column in an SQL +A field result describes the mapping of a single column in a SQL result set to a field in an entity. As such, field results are inherently bound to entity results. You add a field result through ``ResultSetMapping#addFieldResult()``. Again, let's examine the @@ -165,7 +165,7 @@ column should be set. Scalar results ~~~~~~~~~~~~~~ -A scalar result describes the mapping of a single column in an SQL +A scalar result describes the mapping of a single column in a SQL result set to a scalar value in the Doctrine result. Scalar results are typically used for aggregate values but any column in the SQL result set can be mapped as a scalar value. To add a scalar result @@ -190,7 +190,7 @@ of the column will be placed in the transformed Doctrine result. Meta results ~~~~~~~~~~~~ -A meta result describes a single column in an SQL result set that +A meta result describes a single column in a SQL result set that is either a foreign key or a discriminator column. These columns are essential for Doctrine to properly construct objects out of SQL result sets. To add a column as a meta result use @@ -546,12 +546,12 @@ it represents the name of a defined @SqlResultSetMapping. Things to note: - The resultset mapping declares the entities retrieved by this native query. - - Each field of the entity is bound to an SQL alias (or column name). + - Each field of the entity is bound to a SQL alias (or column name). - All fields of the entity including the ones of subclasses and the foreign key columns of related entities have to be present in the SQL query. - Field definitions are optional provided that they map to the same column name as the one declared on the class property. - - ``__CLASS__`` is a alias for the mapped class + - ``__CLASS__`` is an alias for the mapped class In the above example, diff --git a/docs/en/reference/transactions-and-concurrency.rst b/docs/en/reference/transactions-and-concurrency.rst index 0a752ad5b..1b06156e9 100644 --- a/docs/en/reference/transactions-and-concurrency.rst +++ b/docs/en/reference/transactions-and-concurrency.rst @@ -182,7 +182,7 @@ example we'll use an integer. // ... } -Alternatively a datetime type can be used (which maps to an SQL +Alternatively a datetime type can be used (which maps to a SQL timestamp or datetime): .. code-block:: php diff --git a/docs/en/reference/unitofwork.rst b/docs/en/reference/unitofwork.rst index 48f1029c6..f01c3f91d 100644 --- a/docs/en/reference/unitofwork.rst +++ b/docs/en/reference/unitofwork.rst @@ -117,7 +117,7 @@ that consume new memory. Now whenever you call ``EntityManager#flush`` Doctrine will iterate over the Identity Map and for each object compares the original property and association values with the values that are currently set on the object. If changes are -detected then the object is queued for an SQL UPDATE operation. Only the fields +detected then the object is queued for a SQL UPDATE operation. Only the fields that actually changed are updated. This process has an obvious performance impact. The larger the size of the From 2e8272e18f13a9e3a9653ab1b62301400f15a442 Mon Sep 17 00:00:00 2001 From: Valentin Ferriere Date: Fri, 29 Mar 2013 16:39:55 +0100 Subject: [PATCH 068/332] Update association-mapping.rst --- docs/en/reference/association-mapping.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/reference/association-mapping.rst b/docs/en/reference/association-mapping.rst index 3a597f6db..f41bb610f 100644 --- a/docs/en/reference/association-mapping.rst +++ b/docs/en/reference/association-mapping.rst @@ -83,6 +83,7 @@ Generated MySQL Schema: CREATE TABLE Product ( id INT AUTO_INCREMENT NOT NULL, shipping_id INT DEFAULT NULL, + UNIQUE INDEX UNIQ_6FBC94267FE4B2B (shipping_id), PRIMARY KEY(id) ) ENGINE = InnoDB; CREATE TABLE Shipping ( From 937ba6385e6823c183daf2230b1447dbbbedfcec Mon Sep 17 00:00:00 2001 From: Adam Prager Date: Sun, 31 Mar 2013 00:47:24 +0100 Subject: [PATCH 069/332] fixed code duplication issue --- lib/Doctrine/ORM/Tools/EntityGenerator.php | 44 +++++++++++++--------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/lib/Doctrine/ORM/Tools/EntityGenerator.php b/lib/Doctrine/ORM/Tools/EntityGenerator.php index 6aad04fb6..948a4e689 100644 --- a/lib/Doctrine/ORM/Tools/EntityGenerator.php +++ b/lib/Doctrine/ORM/Tools/EntityGenerator.php @@ -710,15 +710,9 @@ public function __construct() } // check traits for existing property - if (class_exists($metadata->name)) { - $reflClass = new \ReflectionClass($metadata->name); - - if (method_exists($reflClass, 'getTraits')) { - foreach ($reflClass->getTraits() as $trait) { - if ($trait->hasProperty($property)) { - return true; - } - } + foreach ($this->getTraits($metadata) as $trait) { + if ($trait->hasProperty($property)) { + return true; } } @@ -746,15 +740,9 @@ public function __construct() } // check traits for existing method - if (class_exists($metadata->name)) { - $reflClass = new \ReflectionClass($metadata->name); - - if (method_exists($reflClass, 'getTraits')) { - foreach ($reflClass->getTraits() as $trait) { - if ($trait->hasMethod($method)) { - return true; - } - } + foreach ($this->getTraits($metadata) as $trait) { + if ($trait->hasMethod($method)) { + return true; } } @@ -764,6 +752,26 @@ public function __construct() ); } + /** + * @param ClassMetadataInfo $metadata + * + * @return array + */ + protected function getTraits(ClassMetadataInfo $metadata) + { + if ($metadata->reflClass != null || class_exists($metadata->name)) { + $reflClass = $metadata->reflClass == null + ? new \ReflectionClass($metadata->name) + : $metadata->reflClass; + + if (method_exists($reflClass, 'getTraits')) { + return $reflClass->getTraits(); + } + } + + return array(); + } + /** * @param ClassMetadataInfo $metadata * From b3414e3c1ab70050d2b0a206e74bf827cb39f8f4 Mon Sep 17 00:00:00 2001 From: Adam Prager Date: Sun, 31 Mar 2013 00:47:45 +0100 Subject: [PATCH 070/332] added unit test --- .../Tests/ORM/Tools/EntityGeneratorTest.php | 58 ++++++++++++++----- tests/Doctrine/Tests/TestInit.php | 1 + tools/sandbox/Entities/TraitedUser.php | 34 +++++++++++ .../sandbox/Entities/Traits/AddressTrait.php | 25 ++++++++ 4 files changed, 104 insertions(+), 14 deletions(-) create mode 100644 tools/sandbox/Entities/TraitedUser.php create mode 100644 tools/sandbox/Entities/Traits/AddressTrait.php diff --git a/tests/Doctrine/Tests/ORM/Tools/EntityGeneratorTest.php b/tests/Doctrine/Tests/ORM/Tools/EntityGeneratorTest.php index a55b603d5..694918d13 100644 --- a/tests/Doctrine/Tests/ORM/Tools/EntityGeneratorTest.php +++ b/tests/Doctrine/Tests/ORM/Tools/EntityGeneratorTest.php @@ -2,10 +2,12 @@ namespace Doctrine\Tests\ORM\Tools; -use Doctrine\ORM\Tools\SchemaTool, - Doctrine\ORM\Tools\EntityGenerator, - Doctrine\ORM\Tools\Export\ClassMetadataExporter, - Doctrine\ORM\Mapping\ClassMetadataInfo; +use Doctrine\ORM\Tools\SchemaTool; +use Doctrine\ORM\Tools\EntityGenerator; +use Doctrine\ORM\Tools\Export\ClassMetadataExporter; +use Doctrine\ORM\Mapping\ClassMetadataInfo; +use Doctrine\ORM\Mapping\ClassMetadataFactory; +use Entities\TraitedUser; require_once __DIR__ . '/../../TestInit.php'; @@ -282,7 +284,7 @@ class EntityGeneratorTest extends \Doctrine\Tests\OrmTestCase $filename = $this->_tmpDir . DIRECTORY_SEPARATOR . $this->_namespace . DIRECTORY_SEPARATOR . 'DDC1784Entity.php'; - + $this->assertFileExists($filename); require_once $filename; @@ -330,7 +332,7 @@ class EntityGeneratorTest extends \Doctrine\Tests\OrmTestCase $property = new \ReflectionProperty($metadata->name, 'centroCustos'); $docComment = $property->getDocComment(); - + //joinColumns $this->assertContains('@JoinColumn(name="idorcamento", referencedColumnName="idorcamento"),', $docComment); $this->assertContains('@JoinColumn(name="idunidade", referencedColumnName="idunidade")', $docComment); @@ -436,7 +438,7 @@ class EntityGeneratorTest extends \Doctrine\Tests\OrmTestCase $entity = new $metadata->name; $reflClass = new \ReflectionClass($metadata->name); - + $type = $field['phpType']; $name = $field['fieldName']; $value = $field['value']; @@ -451,6 +453,34 @@ class EntityGeneratorTest extends \Doctrine\Tests\OrmTestCase $this->assertEquals($value, $entity->{$getter}()); } + /** + * @group DDC-2372 + */ + public function testTraitPropertiesAndMethodsAreNotDuplicated() + { + if (function_exists('trait_exists')) { + $cmf = new ClassMetadataFactory(); + $em = $this->_getTestEntityManager(); + $cmf->setEntityManager($em); + + $user = new TraitedUser(); + $metadata = $cmf->getMetadataFor(get_class($user)); + $metadata->name = $this->_namespace . "\TraitedUser"; + $metadata->namespace = $this->_namespace; + + $this->_generator->writeEntityClass($metadata, $this->_tmpDir); + + $this->assertFileExists($this->_tmpDir . "/" . $this->_namespace . "/TraitedUser.php"); + require $this->_tmpDir . "/" . $this->_namespace . "/TraitedUser.php"; + + $reflClass = new \ReflectionClass($metadata->name); + + $this->assertSame($reflClass->hasProperty('address'), false); + $this->assertSame($reflClass->hasMethod('setAddress'), false); + $this->assertSame($reflClass->hasMethod('getAddress'), false); + } + } + /** * @return array */ @@ -470,43 +500,43 @@ class EntityGeneratorTest extends \Doctrine\Tests\OrmTestCase 'value' => new \DateTime )), array(array( - 'fieldName' => 'date', + 'fieldName' => 'date', 'phpType' => '\\DateTime', 'dbType' => 'date', 'value' => new \DateTime )), array(array( - 'fieldName' => 'time', + 'fieldName' => 'time', 'phpType' => '\DateTime', 'dbType' => 'time', 'value' => new \DateTime )), array(array( - 'fieldName' => 'object', + 'fieldName' => 'object', 'phpType' => '\stdClass', 'dbType' => 'object', 'value' => new \stdClass() )), array(array( - 'fieldName' => 'bigint', + 'fieldName' => 'bigint', 'phpType' => 'integer', 'dbType' => 'bigint', 'value' => 11 )), array(array( - 'fieldName' => 'smallint', + 'fieldName' => 'smallint', 'phpType' => 'integer', 'dbType' => 'smallint', 'value' => 22 )), array(array( - 'fieldName' => 'text', + 'fieldName' => 'text', 'phpType' => 'string', 'dbType' => 'text', 'value' => 'text' )), array(array( - 'fieldName' => 'blob', + 'fieldName' => 'blob', 'phpType' => 'string', 'dbType' => 'blob', 'value' => 'blob' diff --git a/tests/Doctrine/Tests/TestInit.php b/tests/Doctrine/Tests/TestInit.php index c257a75b6..ac7f7354b 100644 --- a/tests/Doctrine/Tests/TestInit.php +++ b/tests/Doctrine/Tests/TestInit.php @@ -17,6 +17,7 @@ if (file_exists(__DIR__ . '/../../../vendor/autoload.php')) { } /* @var $classLoader \Composer\Autoload\ClassLoader */ +$classLoader->add('Entities', __DIR__ . '/../../../tools/sandbox'); $classLoader->add('Doctrine\\Tests\\', __DIR__ . '/../../'); unset($classLoader); diff --git a/tools/sandbox/Entities/TraitedUser.php b/tools/sandbox/Entities/TraitedUser.php new file mode 100644 index 000000000..fe0b68754 --- /dev/null +++ b/tools/sandbox/Entities/TraitedUser.php @@ -0,0 +1,34 @@ +id; + } + + public function getName() + { + return $this->name; + } + + public function setName($name) + { + $this->name = $name; + } +} \ No newline at end of file diff --git a/tools/sandbox/Entities/Traits/AddressTrait.php b/tools/sandbox/Entities/Traits/AddressTrait.php new file mode 100644 index 000000000..9fb3e23a5 --- /dev/null +++ b/tools/sandbox/Entities/Traits/AddressTrait.php @@ -0,0 +1,25 @@ +address; + } + + public function setAddress(Address $address) + { + if ($this->address !== $address) { + $this->address = $address; + $address->setUser($this); + } + } +} \ No newline at end of file From 6fc18e330d8ecd57dc9a4af871d6cc30cffd3f7d Mon Sep 17 00:00:00 2001 From: Asmir Mustafic Date: Tue, 2 Apr 2013 10:04:15 +0200 Subject: [PATCH 071/332] indexby metadata column --- lib/Doctrine/ORM/Query/ResultSetMapping.php | 2 +- .../Tests/ORM/Hydration/ResultSetMappingTest.php | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Query/ResultSetMapping.php b/lib/Doctrine/ORM/Query/ResultSetMapping.php index 50dacea41..b06b68385 100644 --- a/lib/Doctrine/ORM/Query/ResultSetMapping.php +++ b/lib/Doctrine/ORM/Query/ResultSetMapping.php @@ -211,7 +211,7 @@ class ResultSetMapping { $found = false; - foreach ($this->fieldMappings as $columnName => $columnFieldName) { + foreach (array_merge($this->metaMappings, $this->fieldMappings) as $columnName => $columnFieldName) { if ( ! ($columnFieldName === $fieldName && $this->columnOwnerMap[$columnName] === $alias)) continue; $this->addIndexByColumn($alias, $columnName); diff --git a/tests/Doctrine/Tests/ORM/Hydration/ResultSetMappingTest.php b/tests/Doctrine/Tests/ORM/Hydration/ResultSetMappingTest.php index 801937f2d..5a198a944 100644 --- a/tests/Doctrine/Tests/ORM/Hydration/ResultSetMappingTest.php +++ b/tests/Doctrine/Tests/ORM/Hydration/ResultSetMappingTest.php @@ -255,5 +255,18 @@ class ResultSetMappingTest extends \Doctrine\Tests\OrmTestCase $this->assertEquals('Doctrine\Tests\Models\CMS\CmsUser', $rsm->getDeclaringClass('status')); $this->assertEquals('Doctrine\Tests\Models\CMS\CmsUser', $rsm->getDeclaringClass('username')); } + public function testIndexByMetadataColumn() + { + $rms = $this->_rsm; + $rms->addEntityResult('Doctrine\Tests\Models\Legacy\LegacyUser', 'u') + ->addJoinedEntityResult('Doctrine\Tests\Models\Legacy', 'lu', 'u', '_references') + ->addMetaResult('lu', '_source', '_source', true) + ->addMetaResult('lu', '_target', '_target', true) + ->addIndexBy('lu', '_source'); + + + $this->assertTrue($rms->hasIndexBy('lu')); + + } } From 3196b0c05a9cd4507463a82b706f691f686a8224 Mon Sep 17 00:00:00 2001 From: Asmir Mustafic Date: Tue, 2 Apr 2013 10:14:24 +0200 Subject: [PATCH 072/332] missing new line --- tests/Doctrine/Tests/ORM/Hydration/ResultSetMappingTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/Doctrine/Tests/ORM/Hydration/ResultSetMappingTest.php b/tests/Doctrine/Tests/ORM/Hydration/ResultSetMappingTest.php index 5a198a944..fac93b6e6 100644 --- a/tests/Doctrine/Tests/ORM/Hydration/ResultSetMappingTest.php +++ b/tests/Doctrine/Tests/ORM/Hydration/ResultSetMappingTest.php @@ -255,6 +255,7 @@ class ResultSetMappingTest extends \Doctrine\Tests\OrmTestCase $this->assertEquals('Doctrine\Tests\Models\CMS\CmsUser', $rsm->getDeclaringClass('status')); $this->assertEquals('Doctrine\Tests\Models\CMS\CmsUser', $rsm->getDeclaringClass('username')); } + public function testIndexByMetadataColumn() { $rms = $this->_rsm; From 3e8796f78188e193aac7386b38b7e17da3df5718 Mon Sep 17 00:00:00 2001 From: denkiryokuhatsuden Date: Tue, 2 Apr 2013 18:54:55 +0900 Subject: [PATCH 073/332] Add hidden field ordering for postgresql In postgresql environment, when some hidden fields are used in orderBy clause, they're not property added because $rsm->scalarMappings don't have information about them. --- .../Pagination/LimitSubqueryOutputWalker.php | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php b/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php index 3943cb5e6..a0a22b03b 100644 --- a/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php +++ b/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php @@ -91,7 +91,25 @@ class LimitSubqueryOutputWalker extends SqlWalker */ public function walkSelectStatement(SelectStatement $AST) { - $innerSql = parent::walkSelectStatement($AST); + if ($this->platform instanceof PostgreSqlPlatform) { + // Set every select expression as visible(hidden = false) to + // make $AST to have scalar mappings properly + $hiddens = array(); + foreach ($AST->selectClause->selectExpressions as $idx => $expr) { + $hiddens[$idx] = $expr->hiddenAliasResultVariable; + $expr->hiddenAliasResultVariable = false; + } + + $innerSql = parent::walkSelectStatement($AST); + + // Restore hiddens + foreach ($AST->selectClause->selectExpressions as $idx => $expr) { + $expr->hiddenAliasResultVariable = $hiddens[$idx]; + } + } else { + $innerSql = parent::walkSelectStatement($AST); + } + // Find out the SQL alias of the identifier column of the root entity. // It may be possible to make this work with multiple root entities but that From 786d904328d26d80cfd829a73c4e36b5c1e8ff2a Mon Sep 17 00:00:00 2001 From: denkiryokuhatsuden Date: Wed, 3 Apr 2013 17:14:31 +0900 Subject: [PATCH 074/332] Revert "Add hidden field ordering for postgresql" This reverts commit 3e8796f78188e193aac7386b38b7e17da3df5718. --- .../Pagination/LimitSubqueryOutputWalker.php | 20 +------------------ 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php b/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php index a0a22b03b..3943cb5e6 100644 --- a/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php +++ b/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php @@ -91,25 +91,7 @@ class LimitSubqueryOutputWalker extends SqlWalker */ public function walkSelectStatement(SelectStatement $AST) { - if ($this->platform instanceof PostgreSqlPlatform) { - // Set every select expression as visible(hidden = false) to - // make $AST to have scalar mappings properly - $hiddens = array(); - foreach ($AST->selectClause->selectExpressions as $idx => $expr) { - $hiddens[$idx] = $expr->hiddenAliasResultVariable; - $expr->hiddenAliasResultVariable = false; - } - - $innerSql = parent::walkSelectStatement($AST); - - // Restore hiddens - foreach ($AST->selectClause->selectExpressions as $idx => $expr) { - $expr->hiddenAliasResultVariable = $hiddens[$idx]; - } - } else { - $innerSql = parent::walkSelectStatement($AST); - } - + $innerSql = parent::walkSelectStatement($AST); // Find out the SQL alias of the identifier column of the root entity. // It may be possible to make this work with multiple root entities but that From e54c11e3bb9c510340957d7c2e91c86433eef98b Mon Sep 17 00:00:00 2001 From: denkiryokuhatsuden Date: Wed, 3 Apr 2013 17:21:51 +0900 Subject: [PATCH 075/332] Add test for postgresql hidden scalar sorting --- .../LimitSubqueryOutputWalkerTest.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/Doctrine/Tests/ORM/Tools/Pagination/LimitSubqueryOutputWalkerTest.php b/tests/Doctrine/Tests/ORM/Tools/Pagination/LimitSubqueryOutputWalkerTest.php index f19b8520b..6c533084f 100644 --- a/tests/Doctrine/Tests/ORM/Tools/Pagination/LimitSubqueryOutputWalkerTest.php +++ b/tests/Doctrine/Tests/ORM/Tools/Pagination/LimitSubqueryOutputWalkerTest.php @@ -74,6 +74,25 @@ class LimitSubqueryOutputWalkerTest extends PaginationTestCase $this->entityManager->getConnection()->setDatabasePlatform($odp); } + public function testLimitSubqueryWithHiddenScalarSortPg() + { + $odp = $this->entityManager->getConnection()->getDatabasePlatform(); + $this->entityManager->getConnection()->setDatabasePlatform(new \Doctrine\DBAL\Platforms\PostgreSqlPlatform); + + $query = $this->entityManager->createQuery( + 'SELECT u, g, COUNT(g.id) AS hidden g_quantity FROM Doctrine\Tests\ORM\Tools\Pagination\User u JOIN u.groups g ORDER BY g_quantity, u.id DESC' + ); + $limitQuery = clone $query; + $limitQuery->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, 'Doctrine\ORM\Tools\Pagination\LimitSubqueryOutputWalker'); + + $this->assertEquals( + "SELECT DISTINCT id1, sclr0 FROM (SELECT COUNT(g0_.id) AS sclr0, u1_.id AS id1, g0_.id AS id2 FROM User u1_ INNER JOIN user_group u2_ ON u1_.id = u2_.user_id INNER JOIN groups g0_ ON g0_.id = u2_.group_id ORDER BY sclr0 ASC, u1_.id DESC) dctrn_result ORDER BY sclr0 ASC, id1 DESC", + $limitQuery->getSql() + ); + + $this->entityManager->getConnection()->setDatabasePlatform($odp); + } + public function testLimitSubqueryPg() { $odp = $this->entityManager->getConnection()->getDatabasePlatform(); From 7af84e79e5a2a54120cf81da7a0f17c98dfe87fa Mon Sep 17 00:00:00 2001 From: denkiryokuhatsuden Date: Wed, 3 Apr 2013 17:22:31 +0900 Subject: [PATCH 076/332] Fixed postgresql hidden scalar sort --- .../Pagination/LimitSubqueryOutputWalker.php | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php b/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php index 3943cb5e6..a0a22b03b 100644 --- a/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php +++ b/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php @@ -91,7 +91,25 @@ class LimitSubqueryOutputWalker extends SqlWalker */ public function walkSelectStatement(SelectStatement $AST) { - $innerSql = parent::walkSelectStatement($AST); + if ($this->platform instanceof PostgreSqlPlatform) { + // Set every select expression as visible(hidden = false) to + // make $AST to have scalar mappings properly + $hiddens = array(); + foreach ($AST->selectClause->selectExpressions as $idx => $expr) { + $hiddens[$idx] = $expr->hiddenAliasResultVariable; + $expr->hiddenAliasResultVariable = false; + } + + $innerSql = parent::walkSelectStatement($AST); + + // Restore hiddens + foreach ($AST->selectClause->selectExpressions as $idx => $expr) { + $expr->hiddenAliasResultVariable = $hiddens[$idx]; + } + } else { + $innerSql = parent::walkSelectStatement($AST); + } + // Find out the SQL alias of the identifier column of the root entity. // It may be possible to make this work with multiple root entities but that From 1278b79c79da6a871ed41c11d97ead14d77ae53a Mon Sep 17 00:00:00 2001 From: Anthony Date: Wed, 3 Apr 2013 13:31:07 -0300 Subject: [PATCH 077/332] Added yml example in ordered-associations.rst And modified it to be in a configuration-block instead of separate code-block --- docs/en/tutorials/ordered-associations.rst | 65 ++++++++++++++-------- 1 file changed, 42 insertions(+), 23 deletions(-) diff --git a/docs/en/tutorials/ordered-associations.rst b/docs/en/tutorials/ordered-associations.rst index e2a48ffcb..121bd145d 100644 --- a/docs/en/tutorials/ordered-associations.rst +++ b/docs/en/tutorials/ordered-associations.rst @@ -12,32 +12,51 @@ collection. Additional to any ``@OneToMany`` or ``@ManyToMany`` annotation you can specify the ``@OrderBy`` in the following way: -.. code-block:: php +.. configuration-block:: - - - - - - - - - + .. code-block:: xml + + + + + + + + + + + + .. code-block:: yaml + + User: + type: entity + manyToMany: + groups: + orderBy: { 'name': 'ASC' } + targetEntity: Group + joinTable: + name: users_groups + joinColumns: + user_id: + referencedColumnName: id + inverseJoinColumns: + group_id: + referencedColumnName: id The DQL Snippet in OrderBy is only allowed to consist of unqualified, unquoted field names and of an optional ASC/DESC From 3b7b457d35657911d551ef69639e4a9db111501c Mon Sep 17 00:00:00 2001 From: Adam Prager Date: Thu, 4 Apr 2013 20:07:21 +0200 Subject: [PATCH 078/332] minor fixes --- lib/Doctrine/ORM/Tools/EntityGenerator.php | 6 +++--- tests/Doctrine/Tests/ORM/Tools/EntityGeneratorTest.php | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/Doctrine/ORM/Tools/EntityGenerator.php b/lib/Doctrine/ORM/Tools/EntityGenerator.php index 948a4e689..1c55eb1bc 100644 --- a/lib/Doctrine/ORM/Tools/EntityGenerator.php +++ b/lib/Doctrine/ORM/Tools/EntityGenerator.php @@ -759,12 +759,12 @@ public function __construct() */ protected function getTraits(ClassMetadataInfo $metadata) { - if ($metadata->reflClass != null || class_exists($metadata->name)) { - $reflClass = $metadata->reflClass == null + if ($metadata->reflClass !== null || class_exists($metadata->name)) { + $reflClass = $metadata->reflClass === null ? new \ReflectionClass($metadata->name) : $metadata->reflClass; - if (method_exists($reflClass, 'getTraits')) { + if (PHP_VERSION_ID >= 50400) { return $reflClass->getTraits(); } } diff --git a/tests/Doctrine/Tests/ORM/Tools/EntityGeneratorTest.php b/tests/Doctrine/Tests/ORM/Tools/EntityGeneratorTest.php index 694918d13..cd6430a2a 100644 --- a/tests/Doctrine/Tests/ORM/Tools/EntityGeneratorTest.php +++ b/tests/Doctrine/Tests/ORM/Tools/EntityGeneratorTest.php @@ -458,7 +458,7 @@ class EntityGeneratorTest extends \Doctrine\Tests\OrmTestCase */ public function testTraitPropertiesAndMethodsAreNotDuplicated() { - if (function_exists('trait_exists')) { + if (PHP_VERSION_ID >= 50400) { $cmf = new ClassMetadataFactory(); $em = $this->_getTestEntityManager(); $cmf->setEntityManager($em); From 64b2ecfefc8a6db94a0ec931dfc1e8012a5eb8a2 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Thu, 4 Apr 2013 20:20:23 +0200 Subject: [PATCH 079/332] [DDC-2224] Rewrite instanceof feature with parameter needle ClassMetadata breaks caching of queries. --- lib/Doctrine/ORM/AbstractQuery.php | 4 +++ lib/Doctrine/ORM/Query/SqlWalker.php | 29 +++++++------------ tests/Doctrine/Tests/ORM/Query/QueryTest.php | 12 ++++++++ .../ORM/Query/SelectSqlGenerationTest.php | 2 +- 4 files changed, 27 insertions(+), 20 deletions(-) diff --git a/lib/Doctrine/ORM/AbstractQuery.php b/lib/Doctrine/ORM/AbstractQuery.php index 52f6ef507..18d563b10 100644 --- a/lib/Doctrine/ORM/AbstractQuery.php +++ b/lib/Doctrine/ORM/AbstractQuery.php @@ -284,6 +284,10 @@ abstract class AbstractQuery } } + if ($value instanceof Mapping\ClassMetadata) { + return $value->name; + } + return $value; } diff --git a/lib/Doctrine/ORM/Query/SqlWalker.php b/lib/Doctrine/ORM/Query/SqlWalker.php index 30a39924e..08d5e0cda 100644 --- a/lib/Doctrine/ORM/Query/SqlWalker.php +++ b/lib/Doctrine/ORM/Query/SqlWalker.php @@ -1932,31 +1932,22 @@ class SqlWalker implements TreeWalker foreach ($instanceOfExpr->value as $parameter) { if ($parameter instanceof AST\InputParameter) { - // We need to modify the parameter value to be its correspondent mapped value - $dqlParamKey = $parameter->name; - $dqlParam = $this->query->getParameter($dqlParamKey); - $paramValue = $this->query->processParameterValue($dqlParam->getValue()); - - if ( ! ($paramValue instanceof \Doctrine\ORM\Mapping\ClassMetadata)) { - throw QueryException::invalidParameterType('ClassMetadata', get_class($paramValue)); - } - - $entityClassName = $paramValue->name; + $sqlParameterList[] = $this->walkInputParameter($parameter); } else { // Get name from ClassMetadata to resolve aliases. $entityClassName = $this->em->getClassMetadata($parameter)->name; - } - if ($entityClassName == $class->name) { - $sqlParameterList[] = $this->conn->quote($class->discriminatorValue); - } else { - $discrMap = array_flip($class->discriminatorMap); + if ($entityClassName == $class->name) { + $sqlParameterList[] = $this->conn->quote($class->discriminatorValue); + } else { + $discrMap = array_flip($class->discriminatorMap); - if (!isset($discrMap[$entityClassName])) { - throw QueryException::instanceOfUnrelatedClass($entityClassName, $class->rootEntityName); + if (!isset($discrMap[$entityClassName])) { + throw QueryException::instanceOfUnrelatedClass($entityClassName, $class->rootEntityName); + } + + $sqlParameterList[] = $this->conn->quote($discrMap[$entityClassName]); } - - $sqlParameterList[] = $this->conn->quote($discrMap[$entityClassName]); } } diff --git a/tests/Doctrine/Tests/ORM/Query/QueryTest.php b/tests/Doctrine/Tests/ORM/Query/QueryTest.php index de160d096..9617fa873 100644 --- a/tests/Doctrine/Tests/ORM/Query/QueryTest.php +++ b/tests/Doctrine/Tests/ORM/Query/QueryTest.php @@ -163,4 +163,16 @@ class QueryTest extends \Doctrine\Tests\OrmTestCase $this->assertEquals('cities', $parameter->getName()); $this->assertEquals($cities, $parameter->getValue()); } + + /** + * @group DDC-2224 + */ + public function testProcessParameterValueClassMetadata() + { + $query = $this->_em->createQuery("SELECT a FROM Doctrine\Tests\Models\CMS\CmsAddress a WHERE a.city IN (:cities)"); + $this->assertEquals( + 'Doctrine\Tests\Models\CMS\CmsAddress', + $query->processParameterValue($this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsAddress')) + ); + } } diff --git a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php index fccacbdab..cb4aac47e 100644 --- a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php +++ b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php @@ -482,7 +482,7 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase { $this->assertSqlGeneration( "SELECT u FROM Doctrine\Tests\Models\Company\CompanyPerson u WHERE u INSTANCE OF ?1", - "SELECT c0_.id AS id0, c0_.name AS name1, c0_.discr AS discr2 FROM company_persons c0_ WHERE c0_.discr IN ('employee')", + "SELECT c0_.id AS id0, c0_.name AS name1, c0_.discr AS discr2 FROM company_persons c0_ WHERE c0_.discr IN (?)", array(), array(1 => $this->_em->getClassMetadata('Doctrine\Tests\Models\Company\CompanyEmployee')) ); } From 30b050b44ce20631ab9931ba2ea97044595f0c15 Mon Sep 17 00:00:00 2001 From: Pascal Borreli Date: Sat, 6 Apr 2013 14:11:52 +0000 Subject: [PATCH 080/332] Fixed typos --- lib/Doctrine/ORM/AbstractQuery.php | 2 +- lib/Doctrine/ORM/Event/ListenersInvoker.php | 2 +- lib/Doctrine/ORM/Id/AssignedGenerator.php | 2 +- lib/Doctrine/ORM/Id/BigIntegerIdentityGenerator.php | 6 +++--- lib/Doctrine/ORM/Id/IdentityGenerator.php | 6 +++--- lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php | 1 + lib/Doctrine/ORM/Mapping/EntityListenerResolver.php | 2 +- lib/Doctrine/ORM/Mapping/MappingException.php | 2 +- .../ORM/Persisters/AbstractCollectionPersister.php | 4 ++-- lib/Doctrine/ORM/Query/AST/Functions/FunctionNode.php | 2 +- lib/Doctrine/ORM/Query/AST/Functions/IdentityFunction.php | 4 ++-- lib/Doctrine/ORM/Query/Parser.php | 8 ++++---- lib/Doctrine/ORM/Query/SqlWalker.php | 2 +- lib/Doctrine/ORM/Tools/Console/ConsoleRunner.php | 1 + lib/Doctrine/ORM/Tools/EntityGenerator.php | 4 ++-- lib/Doctrine/ORM/UnitOfWork.php | 6 +++--- 16 files changed, 28 insertions(+), 26 deletions(-) diff --git a/lib/Doctrine/ORM/AbstractQuery.php b/lib/Doctrine/ORM/AbstractQuery.php index 18d563b10..b32310512 100644 --- a/lib/Doctrine/ORM/AbstractQuery.php +++ b/lib/Doctrine/ORM/AbstractQuery.php @@ -309,7 +309,7 @@ abstract class AbstractQuery /** * Allows to translate entity namespaces to full qualified names. * - * @param EntityManager $em + * @param Query\ResultSetMapping $rsm * * @return void */ diff --git a/lib/Doctrine/ORM/Event/ListenersInvoker.php b/lib/Doctrine/ORM/Event/ListenersInvoker.php index 96fa4e67a..125855bef 100644 --- a/lib/Doctrine/ORM/Event/ListenersInvoker.php +++ b/lib/Doctrine/ORM/Event/ListenersInvoker.php @@ -91,7 +91,7 @@ class ListenersInvoker * * @param \Doctrine\ORM\Mapping\ClassMetadata $metadata The entity metadata. * @param string $eventName The entity lifecycle event. - * @param object $entity The Entity on which the event occured. + * @param object $entity The Entity on which the event occurred. * @param \Doctrine\Common\EventArgs $event The Event args. * @param integer $invoke Bitmask to invoke listeners. */ diff --git a/lib/Doctrine/ORM/Id/AssignedGenerator.php b/lib/Doctrine/ORM/Id/AssignedGenerator.php index 121cc8fd0..4e3321593 100644 --- a/lib/Doctrine/ORM/Id/AssignedGenerator.php +++ b/lib/Doctrine/ORM/Id/AssignedGenerator.php @@ -23,7 +23,7 @@ use Doctrine\ORM\EntityManager; use Doctrine\ORM\ORMException; /** - * Special generator for application-assigned identifiers (doesnt really generate anything). + * Special generator for application-assigned identifiers (doesn't really generate anything). * * @since 2.0 * @author Benjamin Eberlei diff --git a/lib/Doctrine/ORM/Id/BigIntegerIdentityGenerator.php b/lib/Doctrine/ORM/Id/BigIntegerIdentityGenerator.php index e387023b4..1b4ed9a06 100644 --- a/lib/Doctrine/ORM/Id/BigIntegerIdentityGenerator.php +++ b/lib/Doctrine/ORM/Id/BigIntegerIdentityGenerator.php @@ -38,9 +38,9 @@ class BigIntegerIdentityGenerator extends AbstractIdGenerator /** * Constructor. * - * @param string|null $seqName The name of the sequence to pass to lastInsertId() - * to obtain the last generated identifier within the current - * database session/connection, if any. + * @param string|null $sequenceName The name of the sequence to pass to lastInsertId() + * to obtain the last generated identifier within the current + * database session/connection, if any. */ public function __construct($sequenceName = null) { diff --git a/lib/Doctrine/ORM/Id/IdentityGenerator.php b/lib/Doctrine/ORM/Id/IdentityGenerator.php index d0bf327bb..7945c6936 100644 --- a/lib/Doctrine/ORM/Id/IdentityGenerator.php +++ b/lib/Doctrine/ORM/Id/IdentityGenerator.php @@ -38,9 +38,9 @@ class IdentityGenerator extends AbstractIdGenerator /** * Constructor. * - * @param string|null $seqName The name of the sequence to pass to lastInsertId() - * to obtain the last generated identifier within the current - * database session/connection, if any. + * @param string|null $sequenceName The name of the sequence to pass to lastInsertId() + * to obtain the last generated identifier within the current + * database session/connection, if any. */ public function __construct($sequenceName = null) { diff --git a/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php b/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php index 9e1c734cc..f9aaddba7 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php @@ -533,6 +533,7 @@ class AnnotationDriver extends AbstractAnnotationDriver /** * Parse the given JoinColumn as array * + * @param JoinColumn $joinColumn * @return array */ private function joinColumnToArray(JoinColumn $joinColumn) diff --git a/lib/Doctrine/ORM/Mapping/EntityListenerResolver.php b/lib/Doctrine/ORM/Mapping/EntityListenerResolver.php index bbf498dc3..2d5ecf714 100644 --- a/lib/Doctrine/ORM/Mapping/EntityListenerResolver.php +++ b/lib/Doctrine/ORM/Mapping/EntityListenerResolver.php @@ -49,7 +49,7 @@ interface EntityListenerResolver /** * Register a entity listener instance. * - * @return object An entity listener + * @param object $object An entity listener */ function register($object); } \ No newline at end of file diff --git a/lib/Doctrine/ORM/Mapping/MappingException.php b/lib/Doctrine/ORM/Mapping/MappingException.php index a8345bbd6..a9d5d1214 100644 --- a/lib/Doctrine/ORM/Mapping/MappingException.php +++ b/lib/Doctrine/ORM/Mapping/MappingException.php @@ -685,8 +685,8 @@ class MappingException extends \Doctrine\ORM\ORMException } /** + * @param string $listenerName * @param string $className - * @param string $methodName * * @return \Doctrine\ORM\Mapping\MappingException */ diff --git a/lib/Doctrine/ORM/Persisters/AbstractCollectionPersister.php b/lib/Doctrine/ORM/Persisters/AbstractCollectionPersister.php index e1cad36bc..fdc54aee1 100644 --- a/lib/Doctrine/ORM/Persisters/AbstractCollectionPersister.php +++ b/lib/Doctrine/ORM/Persisters/AbstractCollectionPersister.php @@ -98,7 +98,7 @@ abstract class AbstractCollectionPersister * * @param \Doctrine\ORM\PersistentCollection $coll * - * @return void + * @return string */ abstract protected function getDeleteSQL(PersistentCollection $coll); @@ -108,7 +108,7 @@ abstract class AbstractCollectionPersister * * @param \Doctrine\ORM\PersistentCollection $coll * - * @return void + * @return array */ abstract protected function getDeleteSQLParameters(PersistentCollection $coll); diff --git a/lib/Doctrine/ORM/Query/AST/Functions/FunctionNode.php b/lib/Doctrine/ORM/Query/AST/Functions/FunctionNode.php index 6e9b95afc..2f33c9da3 100644 --- a/lib/Doctrine/ORM/Query/AST/Functions/FunctionNode.php +++ b/lib/Doctrine/ORM/Query/AST/Functions/FunctionNode.php @@ -22,7 +22,7 @@ namespace Doctrine\ORM\Query\AST\Functions; use Doctrine\ORM\Query\AST\Node; /** - * Abtract Function Node. + * Abstract Function Node. * * * @link www.doctrine-project.org diff --git a/lib/Doctrine/ORM/Query/AST/Functions/IdentityFunction.php b/lib/Doctrine/ORM/Query/AST/Functions/IdentityFunction.php index 7e1d75aee..5f51a9044 100644 --- a/lib/Doctrine/ORM/Query/AST/Functions/IdentityFunction.php +++ b/lib/Doctrine/ORM/Query/AST/Functions/IdentityFunction.php @@ -83,9 +83,9 @@ class IdentityFunction extends FunctionNode } $tableAlias = $sqlWalker->getSQLTableAlias($class->getTableName(), $dqlAlias); - $columName = $quoteStrategy->getJoinColumnName($joinColumn, $targetEntity, $platform); + $columnName = $quoteStrategy->getJoinColumnName($joinColumn, $targetEntity, $platform); - return $tableAlias . '.' . $columName; + return $tableAlias . '.' . $columnName; } /** diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index e7a0e3ed2..79f18a4cd 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -2,7 +2,7 @@ /* * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHARNTABILITY AND FITNESS FOR + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT @@ -1064,7 +1064,7 @@ class Parser // Creating AST node $pathExpr = new AST\PathExpression($expectedTypes, $identVariable, $field); - // Defer PathExpression validation if requested to be defered + // Defer PathExpression validation if requested to be deferred $this->deferredPathExpressions[] = array( 'expression' => $pathExpr, 'nestingLevel' => $this->nestingLevel, @@ -2413,7 +2413,7 @@ class Parser } if ($token['type'] === Lexer::T_IDENTIFIER || $token['type'] === Lexer::T_INPUT_PARAMETER || $this->isFunction()) { - // Peek beyond the matching closing paranthesis. + // Peek beyond the matching closing parenthesis. $beyond = $this->lexer->peek(); switch ($peek['value']) { @@ -2765,7 +2765,7 @@ class Parser * StringExpression ::= StringPrimary | "(" Subselect ")" * * @return \Doctrine\ORM\Query\AST\StringPrimary | - * \Doctrine]ORM\Query\AST\Subselect + * \Doctrine\ORM\Query\AST\Subselect */ public function StringExpression() { diff --git a/lib/Doctrine/ORM/Query/SqlWalker.php b/lib/Doctrine/ORM/Query/SqlWalker.php index 08d5e0cda..cb51a9228 100644 --- a/lib/Doctrine/ORM/Query/SqlWalker.php +++ b/lib/Doctrine/ORM/Query/SqlWalker.php @@ -428,7 +428,7 @@ class SqlWalker implements TreeWalker $conn = $this->em->getConnection(); $values = array(); - if ($class->discriminatorValue !== null) { // discrimnators can be 0 + if ($class->discriminatorValue !== null) { // discriminators can be 0 $values[] = $conn->quote($class->discriminatorValue); } diff --git a/lib/Doctrine/ORM/Tools/Console/ConsoleRunner.php b/lib/Doctrine/ORM/Tools/Console/ConsoleRunner.php index 077ae8930..365fcb415 100644 --- a/lib/Doctrine/ORM/Tools/Console/ConsoleRunner.php +++ b/lib/Doctrine/ORM/Tools/Console/ConsoleRunner.php @@ -35,6 +35,7 @@ class ConsoleRunner /** * Create a Symfony Console HelperSet * + * @param EntityManager $entityManager * @return HelperSet */ public static function createHelperSet(EntityManager $entityManager) diff --git a/lib/Doctrine/ORM/Tools/EntityGenerator.php b/lib/Doctrine/ORM/Tools/EntityGenerator.php index 082720bf7..98a65fb98 100644 --- a/lib/Doctrine/ORM/Tools/EntityGenerator.php +++ b/lib/Doctrine/ORM/Tools/EntityGenerator.php @@ -475,7 +475,7 @@ public function __construct() public function setFieldVisibility($visibility) { if ($visibility !== self::FIELD_VISIBLE_PRIVATE && $visibility !== self::FIELD_VISIBLE_PROTECTED) { - throw new \InvalidArgumentException('Invalid provided visibilty (only private and protected are allowed): ' . $visibility); + throw new \InvalidArgumentException('Invalid provided visibility (only private and protected are allowed): ' . $visibility); } $this->fieldVisibility = $visibility; @@ -1480,7 +1480,7 @@ public function __construct() /** * @param integer $type The generator to use for the mapped class. * - * @return string The literal string for the generetor type. + * @return string The literal string for the generator type. * * @throws \InvalidArgumentException When the generator type does not exists. */ diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index 4c3f3720c..928c90f2c 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -597,7 +597,7 @@ class UnitOfWork implements PropertyChangedListener $orgValue = $originalData[$propName]; - // skip if value havent changed + // skip if value haven't changed if ($orgValue === $actualValue) { continue; } @@ -1055,7 +1055,7 @@ class UnitOfWork implements PropertyChangedListener $calc = $this->getCommitOrderCalculator(); // See if there are any new classes in the changeset, that are not in the - // commit order graph yet (dont have a node). + // commit order graph yet (don't have a node). // We have to inspect changeSet to be able to correctly build dependencies. // It is not possible to use IdentityMap here because post inserted ids // are not yet available. @@ -1820,7 +1820,7 @@ class UnitOfWork implements PropertyChangedListener $managedCopyVersion = $reflField->getValue($managedCopy); $entityVersion = $reflField->getValue($entity); - // Throw exception if versions dont match. + // Throw exception if versions don't match. if ($managedCopyVersion != $entityVersion) { throw OptimisticLockException::lockFailedVersionMismatch($entity, $entityVersion, $managedCopyVersion); } From 8109db02b5d4c0f986af77eb7aa5a442d72a8fd9 Mon Sep 17 00:00:00 2001 From: "Fabio B. Silva" Date: Sat, 6 Apr 2013 13:02:43 -0300 Subject: [PATCH 081/332] Document Parenthesis BC break. --- UPGRADE.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/UPGRADE.md b/UPGRADE.md index e80aa55ad..df174de76 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -11,6 +11,23 @@ Before 2.4 the postFlush and onFlush events were only called when there were actually entities that changed. Now these events are called no matter if there are entities in the UoW or changes are found. +## Parenthesis are now considered in arithmetic expression + +Before 2.4 parenthesis are not considered in arithmetic primary expression. +That's conceptually wrong, since it might result in wrong values. For example: + +The DQL: + + SELECT 100/(2*2) FROM MyEntity + +Before 2.4 it generates the SQL: + + SELECT 100/2*2 my_entity + +Now parenthesis are considered, the previous DQL will generate: + + SELECT (100/2*2) my_entity + # Upgrade to 2.3 ## EntityManager#find() not calls EntityRepository#find() anymore From 2ad65656326373420f1e14513f6b8b9c923cd838 Mon Sep 17 00:00:00 2001 From: "Fabio B. Silva" Date: Sat, 6 Apr 2013 14:53:36 -0300 Subject: [PATCH 082/332] Fix parenthesis example --- UPGRADE.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/UPGRADE.md b/UPGRADE.md index df174de76..fe5f52528 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -18,15 +18,15 @@ That's conceptually wrong, since it might result in wrong values. For example: The DQL: - SELECT 100/(2*2) FROM MyEntity + SELECT 100 / ( 2 * 2 ) FROM MyEntity Before 2.4 it generates the SQL: - SELECT 100/2*2 my_entity + SELECT 100 / 2 * 2 FROM my_entity Now parenthesis are considered, the previous DQL will generate: - SELECT (100/2*2) my_entity + SELECT 100 / (2 * 2) FROM my_entity # Upgrade to 2.3 From b2e29eaf970da01e0daefb879e786d29d852345f Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 7 Apr 2013 14:01:27 +0200 Subject: [PATCH 083/332] Rework NativeSQL doc chapter and document SELECT clause generation (ref DDC-2055). --- docs/en/conf.py | 9 ++- docs/en/reference/native-sql.rst | 126 ++++++++++++++++++++----------- 2 files changed, 89 insertions(+), 46 deletions(-) diff --git a/docs/en/conf.py b/docs/en/conf.py index 5b35389ce..4e0c87b50 100644 --- a/docs/en/conf.py +++ b/docs/en/conf.py @@ -22,7 +22,7 @@ sys.path.append(os.path.abspath('_exts')) # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['configurationblock'] +extensions = ['configurationblock', 'sphinx.ext.linkcode'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -192,3 +192,10 @@ latex_documents = [ # If false, no module index is generated. #latex_use_modindex = True + +primary_domain = "dcorm" + +def linkcode_resolve(domain, info): + if domain == 'dcorm': + return 'http://' + return None diff --git a/docs/en/reference/native-sql.rst b/docs/en/reference/native-sql.rst index 21f0a6ca9..cc59f1630 100644 --- a/docs/en/reference/native-sql.rst +++ b/docs/en/reference/native-sql.rst @@ -1,17 +1,21 @@ Native SQL ========== -A ``NativeQuery`` lets you execute native SELECT SQL statements, mapping the results -according to your specifications. Such a specification that -describes how a SQL result set is mapped to a Doctrine result is -represented by a ``ResultSetMapping``. It describes how each column -of the database result should be mapped by Doctrine in terms of the -object graph. This allows you to map arbitrary SQL code to objects, -such as highly vendor-optimized SQL or stored-procedures. +With ``NativeQuery`` you can execute native SELECT SQL statements +and map the results to Doctrine entities or any other result format +supported by Doctrine. -Because writing ``ResultSetMapping`` is not so simple, there is a convenience -wrapper around it called a ``ResultSetMappingBuilder``. The last section -of this chapter describes its usage. +In order to make this mapping possible, you need to describe +to Doctrine what columns in the result map to which entity property. +This description is represented by a ``ResultSetMapping`` object. + +With this feature you can map arbitrary SQL code to objects, such as highly +vendor-optimized SQL or stored-procedures. + +Writing ``ResultSetMapping`` from scratch is complex, but there is a convenience +wrapper around it called a ``ResultSetMappingBuilder``. It can generate +the mappings for you based on Entities and even generates the ``SELECT`` +clause based on this information for you. .. note:: @@ -25,14 +29,75 @@ The NativeQuery class --------------------- To create a ``NativeQuery`` you use the method -``EntityManager#createNativeQuery($sql, $resultSetMapping)``. As -you can see in the signature of this method, it expects 2 -ingredients: The SQL you want to execute and the -``ResultSetMapping`` that describes how the results will be +``EntityManager#createNativeQuery($sql, $resultSetMapping)``. As you can see in +the signature of this method, it expects 2 ingredients: The SQL you want to +execute and the ``ResultSetMapping`` that describes how the results will be mapped. -Once you obtained an instance of a ``NativeQuery``, you can bind -parameters to it and finally execute it. +Once you obtained an instance of a ``NativeQuery``, you can bind parameters to +it with the same API that ``Query`` has and execute it. + +.. code-block:: php + + createNativeQuery('SELECT id, name, discr FROM users WHERE name = ?', $rsm); + $query->setParameter(1, 'romanb'); + + $users = $query->getResult(); + +ResultSetMappingBuilder +----------------------- + +An easy start into ResultSet mapping is the ``ResultSetMappingBuilder`` object. +This has several benefits: + +- The builder takes care of automatically updating your ``ResultSetMapping`` + when the fields or associations change on the metadata of an entity. +- You can generate the required ``SELECT`` expression for a builder + by converting it to a string. +- The API is much simpler than the usual ``ResultSetMapping`` API. + +One downside is that the builder API does not yet support entities +with inheritance hierachies. + +.. code-block:: php + + addRootEntityFromClassMetadata('MyProject\User', 'u'); + $rsm->addJoinedEntityFromClassMetadata('MyProject\Address', 'a', 'u', 'address', array('id' => 'address_id')); + +The builder extends the ``ResultSetMapping`` class and as such has all the functionality of it as well. + +..versionadded:: 2.4 + +Starting with Doctrine ORM 2.4 you can generate the ``SELECT`` clause +from a ``ResultSetMappingBuilder``. You can either cast the builder +object to ``(string)`` and the DQL aliases are used as SQL table aliases +or use the ``generateSelectClause($tableAliases)`` method and pass +a mapping from DQL alias (key) to SQL alias (value) + +.. code-block:: php + + generateSelectClause(array( + 'u' => 't1', + 'g' => 't2' + )); + $sql = "SELECT " . $selectClause . " FROM users t1 JOIN groups t2 ON t1.group_id = t2.id"; + The ResultSetMapping -------------------- @@ -369,35 +434,6 @@ are actually a subtype of User. When using DQL, Doctrine automatically includes the necessary joins for this mapping strategy but with native SQL it is your responsibility. -ResultSetMappingBuilder ------------------------ - -There are some downsides with Native SQL queries. The primary one is that you have to adjust all result set mapping -definitions if names of columns change. In DQL this is detected dynamically when the Query is regenerated with -the current metadata. - -To avoid this hassle you can use the ``ResultSetMappingBuilder`` class. It allows to add all columns of an entity -to a result set mapping. To avoid clashes you can optionally rename specific columns when you are doing the same -in your sQL statement: - -.. code-block:: php - - addRootEntityFromClassMetadata('MyProject\User', 'u'); - $rsm->addJoinedEntityFromClassMetadata('MyProject\Address', 'a', 'u', 'address', array('id' => 'address_id')); - -For entities with more columns the builder is very convenient to use. It extends the ``ResultSetMapping`` class -and as such has all the functionality of it as well. Currently the ``ResultSetMappingBuilder`` does not support -entities with inheritance. - - Named Native Query ------------------ From 34374db56ec777911868f28d6914491b1804a689 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 7 Apr 2013 14:02:47 +0200 Subject: [PATCH 084/332] Enhance documentation on IDENTITY() for composite keys (ref DDC-2202) --- docs/en/reference/dql-doctrine-query-language.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/en/reference/dql-doctrine-query-language.rst b/docs/en/reference/dql-doctrine-query-language.rst index c140d5599..b82cbabda 100644 --- a/docs/en/reference/dql-doctrine-query-language.rst +++ b/docs/en/reference/dql-doctrine-query-language.rst @@ -427,13 +427,14 @@ Get all users visible on a given website that have chosen certain gender: createQuery('SELECT u FROM User u WHERE u.gender IN (SELECT IDENTITY(agl.gender) FROM Site s JOIN s.activeGenderList agl WHERE s.id = ?1)'); -IDENTITY() DQL Function when the association has a composite primary key: +Starting with 2.4, the IDENTITY() DQL function also works for composite primary keys: .. code-block:: php createQuery('SELECT IDENTITY(c.location, 'latitude') AS latitude, IDENTITY(c.location, 'longitude') AS longitude FROM Checkpoint c WHERE c.user = ?1'); + Partial Object Syntax ^^^^^^^^^^^^^^^^^^^^^ From d1f8e18d023bd50b0144d20fd8adbd6e49a6f450 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 7 Apr 2013 14:05:42 +0200 Subject: [PATCH 085/332] Enhance documentation on NEW() keyword. (ref DDC-1574) --- docs/en/reference/dql-doctrine-query-language.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/en/reference/dql-doctrine-query-language.rst b/docs/en/reference/dql-doctrine-query-language.rst index b82cbabda..856a4b077 100644 --- a/docs/en/reference/dql-doctrine-query-language.rst +++ b/docs/en/reference/dql-doctrine-query-language.rst @@ -464,14 +464,15 @@ You use the partial syntax when joining as well: "NEW" Operator Syntax ^^^^^^^^^^^^^^^^^^^^^ -Using the ``NEW`` operator you can construct DTOs from queries. +.. versionadded:: 2.4 + +Using the ``NEW`` operator you can construct Data Transfer Objects (DTOs) directly from DQL queries. - When using ``SELECT NEW`` you don't need to specify a mapped entity. -- You can specify any PHP class, it's only require that you have a matching constructor in your class. +- You can specify any PHP class, it's only require that the constructor of this class matches the ``NEW`` statement. - This approach involves determining exactly which columns you really need, and instantiating data-transfer object that containing a constructor with those arguments. - If you want to select data-transfer objects you should create a class: .. code-block:: php From b7b107b08ac22ec385b6461538832bd25e6f6547 Mon Sep 17 00:00:00 2001 From: Adam Prager Date: Sun, 7 Apr 2013 16:26:05 +0200 Subject: [PATCH 086/332] moved test entities from sandbox --- .../Tests/Models/DDC2372/DDC2372Address.php | 45 +++++++++++++++++++ .../Tests/Models/DDC2372/DDC2372User.php | 10 ++--- .../DDC2372/Traits/DDC2372AddressTrait.php | 6 +-- .../Tests/ORM/Tools/EntityGeneratorTest.php | 10 ++--- tests/Doctrine/Tests/TestInit.php | 1 - 5 files changed, 58 insertions(+), 14 deletions(-) create mode 100644 tests/Doctrine/Tests/Models/DDC2372/DDC2372Address.php rename tools/sandbox/Entities/TraitedUser.php => tests/Doctrine/Tests/Models/DDC2372/DDC2372User.php (68%) rename tools/sandbox/Entities/Traits/AddressTrait.php => tests/Doctrine/Tests/Models/DDC2372/Traits/DDC2372AddressTrait.php (69%) diff --git a/tests/Doctrine/Tests/Models/DDC2372/DDC2372Address.php b/tests/Doctrine/Tests/Models/DDC2372/DDC2372Address.php new file mode 100644 index 000000000..26144c61d --- /dev/null +++ b/tests/Doctrine/Tests/Models/DDC2372/DDC2372Address.php @@ -0,0 +1,45 @@ +id; + } + + public function getStreet() + { + return $this->street; + } + + public function setStreet($street) + { + $this->street = $street; + } + + public function getUser() + { + return $this->user; + } + + public function setUser(User $user) + { + if ($this->user !== $user) { + $this->user = $user; + $user->setAddress($this); + } + } +} \ No newline at end of file diff --git a/tools/sandbox/Entities/TraitedUser.php b/tests/Doctrine/Tests/Models/DDC2372/DDC2372User.php similarity index 68% rename from tools/sandbox/Entities/TraitedUser.php rename to tests/Doctrine/Tests/Models/DDC2372/DDC2372User.php index fe0b68754..4df0df6a5 100644 --- a/tools/sandbox/Entities/TraitedUser.php +++ b/tests/Doctrine/Tests/Models/DDC2372/DDC2372User.php @@ -1,13 +1,13 @@ _getTestEntityManager(); $cmf->setEntityManager($em); - $user = new TraitedUser(); + $user = new DDC2372User(); $metadata = $cmf->getMetadataFor(get_class($user)); - $metadata->name = $this->_namespace . "\TraitedUser"; + $metadata->name = $this->_namespace . "\DDC2372User"; $metadata->namespace = $this->_namespace; $this->_generator->writeEntityClass($metadata, $this->_tmpDir); - $this->assertFileExists($this->_tmpDir . "/" . $this->_namespace . "/TraitedUser.php"); - require $this->_tmpDir . "/" . $this->_namespace . "/TraitedUser.php"; + $this->assertFileExists($this->_tmpDir . "/" . $this->_namespace . "/DDC2372User.php"); + require $this->_tmpDir . "/" . $this->_namespace . "/DDC2372User.php"; $reflClass = new \ReflectionClass($metadata->name); diff --git a/tests/Doctrine/Tests/TestInit.php b/tests/Doctrine/Tests/TestInit.php index ac7f7354b..c257a75b6 100644 --- a/tests/Doctrine/Tests/TestInit.php +++ b/tests/Doctrine/Tests/TestInit.php @@ -17,7 +17,6 @@ if (file_exists(__DIR__ . '/../../../vendor/autoload.php')) { } /* @var $classLoader \Composer\Autoload\ClassLoader */ -$classLoader->add('Entities', __DIR__ . '/../../../tools/sandbox'); $classLoader->add('Doctrine\\Tests\\', __DIR__ . '/../../'); unset($classLoader); From e561f47cb2205565eb873f0643637477bfcfc2ff Mon Sep 17 00:00:00 2001 From: Stefan Kleff Date: Mon, 8 Apr 2013 11:27:22 +0200 Subject: [PATCH 087/332] Added constant --- .../ORM/Internal/Hydration/ObjectHydrator.php | 7 ++++--- lib/Doctrine/ORM/Persisters/BasicEntityPersister.php | 10 +++++----- lib/Doctrine/ORM/UnitOfWork.php | 11 +++++++++-- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php index 04d741cbb..fafc497c0 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php @@ -19,6 +19,7 @@ namespace Doctrine\ORM\Internal\Hydration; +use Doctrine\ORM\UnitOfWork; use PDO; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\PersistentCollection; @@ -94,8 +95,8 @@ class ObjectHydrator extends AbstractHydrator $this->resultCounter = 0; - if ( ! isset($this->_hints['deferEagerLoad'])) { - $this->_hints['deferEagerLoad'] = true; + if ( ! isset($this->_hints[UnitOfWork::HINT_DEFEREAGERLOAD])) { + $this->_hints[UnitOfWork::HINT_DEFEREAGERLOAD] = true; } foreach ($this->_rsm->aliasMap as $dqlAlias => $className) { @@ -152,7 +153,7 @@ class ObjectHydrator extends AbstractHydrator */ protected function cleanup() { - $eagerLoad = (isset($this->_hints['deferEagerLoad'])) && $this->_hints['deferEagerLoad'] == true; + $eagerLoad = (isset($this->_hints[UnitOfWork::HINT_DEFEREAGERLOAD])) && $this->_hints[UnitOfWork::HINT_DEFEREAGERLOAD] == true; parent::cleanup(); diff --git a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php index 570f27ea8..894ac3ffe 100644 --- a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php @@ -851,7 +851,7 @@ class BasicEntityPersister $stmt = $this->conn->executeQuery($query, $params, $types); $hydrator = $this->em->newHydrator(($this->selectJoinSql) ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT); - return $hydrator->hydrateAll($stmt, $this->rsm, array('deferEagerLoad' => true)); + return $hydrator->hydrateAll($stmt, $this->rsm, array(UnitOfWork::HINT_DEFEREAGERLOAD => true)); } /** @@ -908,7 +908,7 @@ class BasicEntityPersister $hydrator = $this->em->newHydrator(($this->selectJoinSql) ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT); - return $hydrator->hydrateAll($stmt, $this->rsm, array('deferEagerLoad' => true)); + return $hydrator->hydrateAll($stmt, $this->rsm, array(UnitOfWork::HINT_DEFEREAGERLOAD => true)); } /** @@ -939,7 +939,7 @@ class BasicEntityPersister private function loadArrayFromStatement($assoc, $stmt) { $rsm = $this->rsm; - $hints = array('deferEagerLoad' => true); + $hints = array(UnitOfWork::HINT_DEFEREAGERLOAD => true); if (isset($assoc['indexBy'])) { $rsm = clone ($this->rsm); // this is necessary because the "default rsm" should be changed. @@ -962,8 +962,8 @@ class BasicEntityPersister { $rsm = $this->rsm; $hints = array( - 'deferEagerLoad' => true, - 'collection' => $coll + UnitOfWork::HINT_DEFEREAGERLOAD => true, + 'collection' => $coll ); if (isset($assoc['indexBy'])) { diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index 4c3f3720c..fa569caf7 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -76,6 +76,13 @@ class UnitOfWork implements PropertyChangedListener */ const STATE_REMOVED = 4; + /** + * Hint used to collect all primary keys of associated entities during hydration + * and execute it in a dedicated query afterwards + * @see https://doctrine-orm.readthedocs.org/en/latest/reference/dql-doctrine-query-language.html?highlight=eager#temporarily-change-fetch-mode-in-dql + */ + const HINT_DEFEREAGERLOAD = 'deferEagerLoad'; + /** * The identity map that holds references to all managed entities that have * an identity. The entities are grouped by their class name. @@ -2616,7 +2623,7 @@ class UnitOfWork implements PropertyChangedListener // this association is marked as eager fetch, and its an uninitialized proxy (wtf!) // then we can append this entity for eager loading! if ($hints['fetchMode'][$class->name][$field] == ClassMetadata::FETCH_EAGER && - isset($hints['deferEagerLoad']) && + isset($hints[self::HINT_DEFEREAGERLOAD]) && !$targetClass->isIdentifierComposite && $newValue instanceof Proxy && $newValue->__isInitialized__ === false) { @@ -2641,7 +2648,7 @@ class UnitOfWork implements PropertyChangedListener break; // Deferred eager load only works for single identifier classes - case (isset($hints['deferEagerLoad']) && ! $targetClass->isIdentifierComposite): + case (isset($hints[self::HINT_DEFEREAGERLOAD]) && ! $targetClass->isIdentifierComposite): // TODO: Is there a faster approach? $this->eagerLoadingEntities[$targetClass->rootEntityName][$relatedIdHash] = current($associatedId); From 142c20aad1413e64082a5a9a2e5ee49617e3a1a0 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Tue, 9 Apr 2013 00:00:16 +0200 Subject: [PATCH 088/332] Work on the Tutorial --- docs/en/tutorials/getting-started.rst | 175 ++++++++++++++++++-------- 1 file changed, 124 insertions(+), 51 deletions(-) diff --git a/docs/en/tutorials/getting-started.rst b/docs/en/tutorials/getting-started.rst index f34590de2..2faea8218 100644 --- a/docs/en/tutorials/getting-started.rst +++ b/docs/en/tutorials/getting-started.rst @@ -258,8 +258,8 @@ entity definition in there: } Note how the properties have getter and setter methods defined except -`$id`. To access data from entities Doctrine 2 uses the Reflection API, so it -is possible for Doctrine to access the value of `$id`. You don't have to +``$id``. To access data from entities Doctrine 2 uses the Reflection API, so it +is possible for Doctrine to access the value of ``$id``. You don't have to take Doctrine into account when designing access to the state of your objects. The next step for persistence with Doctrine is to describe the @@ -268,9 +268,8 @@ language. The metadata language describes how entities, their properties and references should be persisted and what constraints should be applied to them. -Metadata for entities are configured using a -XML, YAML or Docblock Annotations. This -Getting Started Guide will show the mappings for all Mapping Drivers. +Metadata for entities are configured using a XML, YAML or Docblock Annotations. +This Getting Started Guide will show the mappings for all Mapping Drivers. References in the text will be made to the XML mapping. .. configuration-block:: @@ -337,9 +336,12 @@ You have to update the database now, because we have a first Entity now: :: - $ php vendor/bin/doctrine orm:schema-tool:update + $ php vendor/bin/doctrine orm:schema-tool:update --force --dump-sql -Now create a simple script to create a new product: +Specifying both flags ``--force`` and ``-dump-sql`` prints and executes the DDL +statements. + +Now create a new script that will insert products into the database: .. code-block:: php @@ -357,41 +359,37 @@ Now create a simple script to create a new product: echo "Created Product with ID " . $product->getId() . "\n"; -Call this script to see how new products are created: +Call this script from the command line to see how new products are created: :: $ php create_product.php ORM $ php create_product.php DBAL -What is happening here? In the code using the Product is pretty standard OOP. -The interesting bits are the communication with the ``EntityManager``. To +What is happening here? Using the ``Product`` is pretty standard OOP. +The interesting bits are the use of the ``EntityManager`` service. To notify the EntityManager that a new entity should be inserted into the database -you have to call ``persist()``. However the EntityManager does not act on this -command, its merely notified. You have to explicitly call ``flush()`` to have -the EntityManager write those two entities to the database. +you have to call ``persist()``. To intiate a transaction to actually perform +the insertion, You have to explicitly call ``flush()`` on the ``EntityManager``. -You might wonder why does this distinction between persist notification and -flush exist: Doctrine 2 uses the UnitOfWork pattern to aggregate all writes +This distinction between persist and flush is allows to aggregate all writes (INSERT, UPDATE, DELETE) into one single transaction, which is executed when -flush is called. Using this approach the write-performance is significantly +flush is called. Using this approach the write-performance is significantly better than in a scenario where updates are done for each entity in isolation. -In more complex scenarios than the previous two, you are free to request -updates on many different entities and all flush them at once. -Doctrine's UnitOfWork detects entities that have changed after retrieval from -the database automatically when the flush operation is called, so that you only -have to keep track of those entities that are new or to be removed and pass -them to ``EntityManager#persist()`` and ``EntityManager#remove()`` -respectively. +Doctrine follows the UnitOfWork pattern which additionally detects all entities +that were fetched and have changed during the request. You don't have to keep track of +entities yourself, when Doctrine already knowns about them. -We want to see a list of all the products now, so lets create a new script for -this: +As a next step we want to fetch a list of all the products. Let's create a +new script for this: .. code-block:: php getRepository('Product'); $products = $productRepository->findAll(); @@ -399,65 +397,144 @@ this: echo sprintf("-%s\n", $product->getName()); } -The ``EntityRepository`` fetched through the ``EntityManager#getRepository()`` -method exists for every entity and is provided by Doctrine. It contains -some finder methods such as ``findAll()`` we used here. +The ``EntityManager#getRepository()`` method can create a finder object (called +repository) for every entity. It is provided by Doctrine and contains some +finder methods such as ``findAll()``. -Lets display the name of a product based on its ID: +Let's continue with displaying the name of a product based on its ID: .. code-block:: php + require_once "bootstrap.php"; + $id = $argv[1]; $product = $entityManager->find('Product', $id); + if ($product === null) { + echo "No product found.\n"; + exit(1); + } + echo sprintf("-%s\n", $product->getName()); +Updating a product name demonstrates the functionality UnitOfWork of pattern +discussed before. We only need to find a product entity and all changes to its +properties are written to the database: + +.. code-block:: php + + + require_once "bootstrap.php"; + + $id = $argv[1]; + $newName = $argv[2]; + + $product = $entityManager->find('Product', $id); + + if ($product === null) { + echo "Product $id does not exist.\n"; + exit(1); + } + + $product->setName($newName); + + $entityManager->flush(); + +After calling this script on one of the existing products, you can verify the +product name changed by calling the ``show_product.php`` script. + Adding Bug and User Entities ---------------------------- -We continue with the bug tracker domain, by creating the missing -classes ``Bug`` and ``User`` and putting them into -`src/Bug.php` and `src/User.php` -respectively. +We continue with the bug tracker domain, by creating the missing classes +``Bug`` and ``User`` and putting them into ``src/Bug.php`` and +``src/User.php`` respectively. .. code-block:: php id; + } + + public function getDescription() + { + return $this->description; + } + + public function setDescription($description) + { + $this->description = $description; + } + + public function setCreated(DateTime $created) + { + $this->created = $created; + } + + public function getCreated() + { + return $this->created; + } + + public function setStatus($status) + { + $this->status = $status; + } + + public function getStatus() + { + return $this->status; + } + } .. code-block:: php Date: Tue, 9 Apr 2013 17:00:06 -0300 Subject: [PATCH 089/332] Fix Oracle subquery ordering lost bug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See http://www.doctrine-project.org/jira/browse/DDC-1800 for a full description. --- .../Tools/Pagination/LimitSubqueryOutputWalker.php | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php b/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php index 3943cb5e6..274dac271 100644 --- a/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php +++ b/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php @@ -137,9 +137,16 @@ class LimitSubqueryOutputWalker extends SqlWalker )); } - // Build the counter query. - $sql = sprintf('SELECT DISTINCT %s FROM (%s) dctrn_result', - implode(', ', $sqlIdentifier), $innerSql); + // Build the counter query + if ($this->platform instanceof OraclePlatform) { + // Ordering is lost in Oracle with subqueries + // http://www.doctrine-project.org/jira/browse/DDC-1800 + $sql = sprintf('SELECT DISTINCT %s, ROWNUM FROM (%s) dctrn_result ORDER BY ROWNUM ASC', + implode(', ', $sqlIdentifier), $innerSql); + } else { + $sql = sprintf('SELECT DISTINCT %s FROM (%s) dctrn_result', + implode(', ', $sqlIdentifier), $innerSql); + } if ($this->platform instanceof PostgreSqlPlatform) { //http://www.doctrine-project.org/jira/browse/DDC-1958 From 4bafcc5b317a1feb8f05febf7985d8cfd87d31d7 Mon Sep 17 00:00:00 2001 From: Raymond Kolbe Date: Tue, 9 Apr 2013 17:30:54 -0300 Subject: [PATCH 090/332] Fix Oracle subquery ordering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See http://www.doctrine-project.org/jira/browse/DDC-1800 and http://www.doctrine-project.org/jira/browse/DDC-1958#comment-19969 --- .../Pagination/LimitSubqueryOutputWalker.php | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php b/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php index 274dac271..a6c24ee34 100644 --- a/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php +++ b/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php @@ -16,6 +16,7 @@ namespace Doctrine\ORM\Tools\Pagination; use Doctrine\ORM\Query\SqlWalker; use Doctrine\ORM\Query\AST\SelectStatement; use Doctrine\DBAL\Platforms\PostgreSqlPlatform; +use Doctrine\DBAL\Platforms\OraclePlatform; /** * Wraps the query in order to select root entity IDs for pagination. @@ -138,19 +139,13 @@ class LimitSubqueryOutputWalker extends SqlWalker } // Build the counter query - if ($this->platform instanceof OraclePlatform) { - // Ordering is lost in Oracle with subqueries - // http://www.doctrine-project.org/jira/browse/DDC-1800 - $sql = sprintf('SELECT DISTINCT %s, ROWNUM FROM (%s) dctrn_result ORDER BY ROWNUM ASC', - implode(', ', $sqlIdentifier), $innerSql); - } else { - $sql = sprintf('SELECT DISTINCT %s FROM (%s) dctrn_result', - implode(', ', $sqlIdentifier), $innerSql); - } + $sql = sprintf('SELECT DISTINCT %s FROM (%s) dctrn_result', + implode(', ', $sqlIdentifier), $innerSql); - if ($this->platform instanceof PostgreSqlPlatform) { + if ($this->platform instanceof PostgreSqlPlatform || + $this->platform instanceof OraclePlatform) { //http://www.doctrine-project.org/jira/browse/DDC-1958 - $this->getPostgresqlSql($AST, $sqlIdentifier, $innerSql, $sql); + $this->preserveSqlOrdering($AST, $sqlIdentifier, $innerSql, $sql); } // Apply the limit and offset. @@ -168,9 +163,9 @@ class LimitSubqueryOutputWalker extends SqlWalker return $sql; } - + /** - * Generates new SQL for postgresql if necessary. + * Generates new SQL for Postgresql or Oracle if necessary. * * @param SelectStatement $AST * @param array $sqlIdentifier @@ -179,7 +174,7 @@ class LimitSubqueryOutputWalker extends SqlWalker * * @return void */ - public function getPostgresqlSql(SelectStatement $AST, array $sqlIdentifier, $innerSql, &$sql) + public function preserveSqlOrdering(SelectStatement $AST, array $sqlIdentifier, $innerSql, &$sql) { // For every order by, find out the SQL alias by inspecting the ResultSetMapping. $sqlOrderColumns = array(); From 27e23faa5a74c3eb4500d470d097e86d1962ca48 Mon Sep 17 00:00:00 2001 From: Raymond Kolbe Date: Wed, 10 Apr 2013 13:07:09 -0300 Subject: [PATCH 091/332] Accompanying tests for PR #646 --- .../LimitSubqueryOutputWalkerTest.php | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/tests/Doctrine/Tests/ORM/Tools/Pagination/LimitSubqueryOutputWalkerTest.php b/tests/Doctrine/Tests/ORM/Tools/Pagination/LimitSubqueryOutputWalkerTest.php index f19b8520b..07ce5afd9 100644 --- a/tests/Doctrine/Tests/ORM/Tools/Pagination/LimitSubqueryOutputWalkerTest.php +++ b/tests/Doctrine/Tests/ORM/Tools/Pagination/LimitSubqueryOutputWalkerTest.php @@ -83,6 +83,82 @@ class LimitSubqueryOutputWalkerTest extends PaginationTestCase $this->entityManager->getConnection()->setDatabasePlatform($odp); } + + public function testLimitSubqueryWithSortOracle() + { + $odp = $this->entityManager->getConnection()->getDatabasePlatform(); + $this->entityManager->getConnection()->setDatabasePlatform(new \Doctrine\DBAL\Platforms\OraclePlatform); + + $query = $this->entityManager->createQuery( + 'SELECT p, c, a FROM Doctrine\Tests\ORM\Tools\Pagination\MyBlogPost p JOIN p.category c JOIN p.author a ORDER BY p.title'); + $query->expireQueryCache(true); + $limitQuery = clone $query; + $limitQuery->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, 'Doctrine\ORM\Tools\Pagination\LimitSubqueryOutputWalker'); + + $this->assertEquals( + "SELECT DISTINCT ID0, TITLE1 FROM (SELECT m0_.id AS ID0, m0_.title AS TITLE1, c1_.id AS ID2, a2_.id AS ID3, a2_.name AS NAME4, m0_.author_id AS AUTHOR_ID5, m0_.category_id AS CATEGORY_ID6 FROM MyBlogPost m0_ INNER JOIN Category c1_ ON m0_.category_id = c1_.id INNER JOIN Author a2_ ON m0_.author_id = a2_.id ORDER BY m0_.title ASC) dctrn_result ORDER BY TITLE1 ASC", $limitQuery->getSql() + ); + + $this->entityManager->getConnection()->setDatabasePlatform($odp); + } + + public function testLimitSubqueryWithScalarSortOracle() + { + $odp = $this->entityManager->getConnection()->getDatabasePlatform(); + $this->entityManager->getConnection()->setDatabasePlatform(new \Doctrine\DBAL\Platforms\OraclePlatform); + + $query = $this->entityManager->createQuery( + 'SELECT u, g, COUNT(g.id) AS g_quantity FROM Doctrine\Tests\ORM\Tools\Pagination\User u JOIN u.groups g ORDER BY g_quantity' + ); + $query->expireQueryCache(true); + $limitQuery = clone $query; + $limitQuery->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, 'Doctrine\ORM\Tools\Pagination\LimitSubqueryOutputWalker'); + + $this->assertEquals( + "SELECT DISTINCT ID1, SCLR0 FROM (SELECT COUNT(g0_.id) AS SCLR0, u1_.id AS ID1, g0_.id AS ID2 FROM User u1_ INNER JOIN user_group u2_ ON u1_.id = u2_.user_id INNER JOIN groups g0_ ON g0_.id = u2_.group_id ORDER BY SCLR0 ASC) dctrn_result ORDER BY SCLR0 ASC", + $limitQuery->getSql() + ); + + $this->entityManager->getConnection()->setDatabasePlatform($odp); + } + + public function testLimitSubqueryWithMixedSortOracle() + { + $odp = $this->entityManager->getConnection()->getDatabasePlatform(); + $this->entityManager->getConnection()->setDatabasePlatform(new \Doctrine\DBAL\Platforms\OraclePlatform); + + $query = $this->entityManager->createQuery( + 'SELECT u, g, COUNT(g.id) AS g_quantity FROM Doctrine\Tests\ORM\Tools\Pagination\User u JOIN u.groups g ORDER BY g_quantity, u.id DESC' + ); + $query->expireQueryCache(true); + $limitQuery = clone $query; + $limitQuery->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, 'Doctrine\ORM\Tools\Pagination\LimitSubqueryOutputWalker'); + + $this->assertEquals( + "SELECT DISTINCT ID1, SCLR0 FROM (SELECT COUNT(g0_.id) AS SCLR0, u1_.id AS ID1, g0_.id AS ID2 FROM User u1_ INNER JOIN user_group u2_ ON u1_.id = u2_.user_id INNER JOIN groups g0_ ON g0_.id = u2_.group_id ORDER BY SCLR0 ASC, u1_.id DESC) dctrn_result ORDER BY SCLR0 ASC, ID1 DESC", + $limitQuery->getSql() + ); + + $this->entityManager->getConnection()->setDatabasePlatform($odp); + } + + public function testLimitSubqueryOracle() + { + $odp = $this->entityManager->getConnection()->getDatabasePlatform(); + $this->entityManager->getConnection()->setDatabasePlatform(new \Doctrine\DBAL\Platforms\OraclePlatform); + + $query = $this->entityManager->createQuery( + 'SELECT p, c, a FROM Doctrine\Tests\ORM\Tools\Pagination\MyBlogPost p JOIN p.category c JOIN p.author a'); + $query->expireQueryCache(true); + $limitQuery = clone $query; + $limitQuery->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, 'Doctrine\ORM\Tools\Pagination\LimitSubqueryOutputWalker'); + + $this->assertEquals( + "SELECT DISTINCT ID0 FROM (SELECT m0_.id AS ID0, m0_.title AS TITLE1, c1_.id AS ID2, a2_.id AS ID3, a2_.name AS NAME4, m0_.author_id AS AUTHOR_ID5, m0_.category_id AS CATEGORY_ID6 FROM MyBlogPost m0_ INNER JOIN Category c1_ ON m0_.category_id = c1_.id INNER JOIN Author a2_ ON m0_.author_id = a2_.id) dctrn_result", $limitQuery->getSql() + ); + + $this->entityManager->getConnection()->setDatabasePlatform($odp); + } public function testCountQuery_MixedResultsWithName() { From bf92a40171e98c47a9c607bcded37daa0cb89a93 Mon Sep 17 00:00:00 2001 From: Adam Prager Date: Fri, 19 Apr 2013 14:49:32 +0200 Subject: [PATCH 092/332] skip test if php 5.3 --- .../Tests/ORM/Tools/EntityGeneratorTest.php | 42 ++++++++++--------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/tests/Doctrine/Tests/ORM/Tools/EntityGeneratorTest.php b/tests/Doctrine/Tests/ORM/Tools/EntityGeneratorTest.php index bdf2090ef..f687d06ed 100644 --- a/tests/Doctrine/Tests/ORM/Tools/EntityGeneratorTest.php +++ b/tests/Doctrine/Tests/ORM/Tools/EntityGeneratorTest.php @@ -458,27 +458,29 @@ class EntityGeneratorTest extends \Doctrine\Tests\OrmTestCase */ public function testTraitPropertiesAndMethodsAreNotDuplicated() { - if (PHP_VERSION_ID >= 50400) { - $cmf = new ClassMetadataFactory(); - $em = $this->_getTestEntityManager(); - $cmf->setEntityManager($em); - - $user = new DDC2372User(); - $metadata = $cmf->getMetadataFor(get_class($user)); - $metadata->name = $this->_namespace . "\DDC2372User"; - $metadata->namespace = $this->_namespace; - - $this->_generator->writeEntityClass($metadata, $this->_tmpDir); - - $this->assertFileExists($this->_tmpDir . "/" . $this->_namespace . "/DDC2372User.php"); - require $this->_tmpDir . "/" . $this->_namespace . "/DDC2372User.php"; - - $reflClass = new \ReflectionClass($metadata->name); - - $this->assertSame($reflClass->hasProperty('address'), false); - $this->assertSame($reflClass->hasMethod('setAddress'), false); - $this->assertSame($reflClass->hasMethod('getAddress'), false); + if (PHP_VERSION_ID < 50400) { + $this->markTestSkipped('Traits are not available before php 5.4.'); } + + $cmf = new ClassMetadataFactory(); + $em = $this->_getTestEntityManager(); + $cmf->setEntityManager($em); + + $user = new DDC2372User(); + $metadata = $cmf->getMetadataFor(get_class($user)); + $metadata->name = $this->_namespace . "\DDC2372User"; + $metadata->namespace = $this->_namespace; + + $this->_generator->writeEntityClass($metadata, $this->_tmpDir); + + $this->assertFileExists($this->_tmpDir . "/" . $this->_namespace . "/DDC2372User.php"); + require $this->_tmpDir . "/" . $this->_namespace . "/DDC2372User.php"; + + $reflClass = new \ReflectionClass($metadata->name); + + $this->assertSame($reflClass->hasProperty('address'), false); + $this->assertSame($reflClass->hasMethod('setAddress'), false); + $this->assertSame($reflClass->hasMethod('getAddress'), false); } /** From 92b41e017a1d67b355de6d0213e283728756024f Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sat, 20 Apr 2013 10:25:36 +0200 Subject: [PATCH 093/332] [DDC-2407] Fix missing support for UUID and CUSTOM id generators in Exporter --- lib/Doctrine/ORM/Tools/Export/Driver/AbstractExporter.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/Doctrine/ORM/Tools/Export/Driver/AbstractExporter.php b/lib/Doctrine/ORM/Tools/Export/Driver/AbstractExporter.php index 65169124c..d40d0786e 100644 --- a/lib/Doctrine/ORM/Tools/Export/Driver/AbstractExporter.php +++ b/lib/Doctrine/ORM/Tools/Export/Driver/AbstractExporter.php @@ -238,6 +238,12 @@ abstract class AbstractExporter case ClassMetadataInfo::GENERATOR_TYPE_IDENTITY: return 'IDENTITY'; + + case ClassMetadataInfo::GENERATOR_TYPE_UUID: + return 'UUID'; + + case ClassMetadataInfo::GENERATOR_TYPE_CUSTOM: + return 'CUSTOM'; } } } From c2967b35ff81617ad283aca1770c34879be656b3 Mon Sep 17 00:00:00 2001 From: Calum Brodie Date: Sun, 21 Apr 2013 20:43:48 +0200 Subject: [PATCH 094/332] Update coding standards Removing underscores from property/method names and change use statements to PSR-2 --- ...enting-the-notify-changetracking-policy.rst | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/en/cookbook/implementing-the-notify-changetracking-policy.rst b/docs/en/cookbook/implementing-the-notify-changetracking-policy.rst index a3cc74b8d..b8fd6ff7d 100644 --- a/docs/en/cookbook/implementing-the-notify-changetracking-policy.rst +++ b/docs/en/cookbook/implementing-the-notify-changetracking-policy.rst @@ -22,21 +22,21 @@ implement the ``NotifyPropertyChanged`` interface from the .. code-block:: php _listeners[] = $listener; + $this->listeners[] = $listener; } /** Notifies listeners of a change. */ - protected function _onPropertyChanged($propName, $oldValue, $newValue) { - if ($this->_listeners) { - foreach ($this->_listeners as $listener) { + protected function onPropertyChanged($propName, $oldValue, $newValue) { + if ($this->listeners) { + foreach ($this->listeners as $listener) { $listener->propertyChanged($this, $propName, $oldValue, $newValue); } } @@ -44,7 +44,7 @@ implement the ``NotifyPropertyChanged`` interface from the } Then, in each property setter of concrete, derived domain classes, -you need to invoke \_onPropertyChanged as follows to notify +you need to invoke \onPropertyChanged as follows to notify listeners: .. code-block:: php @@ -58,7 +58,7 @@ listeners: public function setData($data) { if ($data != $this->data) { // check: is it actually modified? - $this->_onPropertyChanged('data', $this->data, $data); + $this->onPropertyChanged('data', $this->data, $data); $this->data = $data; } } From a0a133b02c872981e76b02b6dc1c4839f105e757 Mon Sep 17 00:00:00 2001 From: Calum Brodie Date: Sun, 21 Apr 2013 20:55:54 +0200 Subject: [PATCH 095/332] Removed unneeded escape character Removed backslash --- .../cookbook/implementing-the-notify-changetracking-policy.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/cookbook/implementing-the-notify-changetracking-policy.rst b/docs/en/cookbook/implementing-the-notify-changetracking-policy.rst index b8fd6ff7d..44035ccf7 100644 --- a/docs/en/cookbook/implementing-the-notify-changetracking-policy.rst +++ b/docs/en/cookbook/implementing-the-notify-changetracking-policy.rst @@ -44,7 +44,7 @@ implement the ``NotifyPropertyChanged`` interface from the } Then, in each property setter of concrete, derived domain classes, -you need to invoke \onPropertyChanged as follows to notify +you need to invoke onPropertyChanged as follows to notify listeners: .. code-block:: php From 99ec4dc72cb172dd486efdc3227a231f43477f34 Mon Sep 17 00:00:00 2001 From: EuKov Date: Tue, 23 Apr 2013 20:46:19 +0300 Subject: [PATCH 096/332] Fixed typo in SQLFilter (use statement ClassMetadata) --- lib/Doctrine/ORM/Query/Filter/SQLFilter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Query/Filter/SQLFilter.php b/lib/Doctrine/ORM/Query/Filter/SQLFilter.php index 61bc1192d..fc749f151 100644 --- a/lib/Doctrine/ORM/Query/Filter/SQLFilter.php +++ b/lib/Doctrine/ORM/Query/Filter/SQLFilter.php @@ -20,7 +20,7 @@ namespace Doctrine\ORM\Query\Filter; use Doctrine\ORM\EntityManager; -use Doctrine\ORM\Mapping\ClassMetaData; +use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Query\ParameterTypeInferer; /** From 408eef1356da8c15fdc0e2d34de4d265db57d743 Mon Sep 17 00:00:00 2001 From: Danny Kopping Date: Fri, 26 Apr 2013 12:05:50 +0300 Subject: [PATCH 097/332] Fixed entities path In the tutorial, the user is told to create a new file in the '/src' folder, and the 'entities' folder is never referenced. Updating the SQLite schema according to the tutorial fails with the 'No Metadata Classes to process.' message. Changing the folder to '/src' fixes this, ostensibly. --- docs/en/tutorials/getting-started.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/tutorials/getting-started.rst b/docs/en/tutorials/getting-started.rst index 2faea8218..1ff524529 100644 --- a/docs/en/tutorials/getting-started.rst +++ b/docs/en/tutorials/getting-started.rst @@ -137,7 +137,7 @@ step: // Create a simple "default" Doctrine ORM configuration for Annotations $isDevMode = true; - $config = Setup::createAnnotationMetadataConfiguration(array(__DIR__."/entities"), $isDevMode); + $config = Setup::createAnnotationMetadataConfiguration(array(__DIR__."/src"), $isDevMode); // or if you prefer yaml or XML //$config = Setup::createXMLMetadataConfiguration(array(__DIR__."/config/xml"), $isDevMode); //$config = Setup::createYAMLMetadataConfiguration(array(__DIR__."/config/yaml"), $isDevMode); From 7c2da2d5b84b655b78e521b3f1be94e8c48968c7 Mon Sep 17 00:00:00 2001 From: "Fabio B. Silva" Date: Fri, 26 Apr 2013 16:11:04 -0300 Subject: [PATCH 098/332] Fix DDC-2415 --- .../ORM/Mapping/ClassMetadataFactory.php | 4 + .../ORM/Functional/Ticket/DDC2415Test.php | 106 ++++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2415Test.php diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php index a1da3ffcf..9320d1b6d 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php @@ -103,6 +103,10 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory $class->setLifecycleCallbacks($parent->lifecycleCallbacks); $class->setChangeTrackingPolicy($parent->changeTrackingPolicy); + if ( ! empty($parent->customGeneratorDefinition)) { + $class->setCustomGeneratorDefinition($parent->customGeneratorDefinition); + } + if ($parent->isMappedSuperclass) { $class->setCustomRepositoryClass($parent->customRepositoryClassName); } diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2415Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2415Test.php new file mode 100644 index 000000000..a440007a0 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2415Test.php @@ -0,0 +1,106 @@ +_em->getConfiguration()->setMetadataDriverImpl(new StaticPHPDriver(array())); + + $this->_schemaTool->createSchema(array( + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2415ParentEntity'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2415ChildEntity'), + )); + } + + public function testTicket() + { + $parentMetadata = $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2415ParentEntity'); + $childMetadata = $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2415ChildEntity'); + + $this->assertEquals($parentMetadata->generatorType, $childMetadata->generatorType); + $this->assertEquals($parentMetadata->customGeneratorDefinition, $childMetadata->customGeneratorDefinition); + $this->assertEquals('Doctrine\Tests\ORM\Functional\Ticket\DDC2415Generator', $parentMetadata->customGeneratorDefinition['class']); + + $e1 = new DDC2415ChildEntity("ChildEntity 1"); + $e2 = new DDC2415ChildEntity("ChildEntity 2"); + + $this->_em->persist($e1); + $this->_em->persist($e2); + $this->_em->flush(); + $this->_em->clear(); + + $this->assertEquals(md5($e1->getName()), $e1->getId()); + $this->assertEquals(md5($e2->getName()), $e2->getId()); + } +} + +class DDC2415ParentEntity +{ + protected $id; + + public function getId() + { + return $this->id; + } + + public static function loadMetadata(ClassMetadataInfo $metadata) + { + $metadata->mapField(array ( + 'id' => true, + 'fieldName' => 'id', + 'type' => 'string', + )); + + $metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_CUSTOM); + $metadata->setCustomGeneratorDefinition(array( + 'class' => 'Doctrine\Tests\ORM\Functional\Ticket\DDC2415Generator' + )); + + $metadata->isMappedSuperclass = true; + } +} + +class DDC2415ChildEntity extends DDC2415ParentEntity +{ + protected $name; + + public function __construct($name) + { + $this->name = $name; + } + + public function getName() + { + return $this->name; + } + + public static function loadMetadata(ClassMetadataInfo $metadata) + { + $metadata->mapField(array ( + 'fieldName' => 'name', + 'type' => 'string', + )); + } +} + +class DDC2415Generator extends AbstractIdGenerator +{ + public function generate(EntityManager $em, $entity) + { + return md5($entity->getName()); + } +} \ No newline at end of file From 5cdc73e13bf03db1435088359df8925d7b348743 Mon Sep 17 00:00:00 2001 From: "Fabio B. Silva" Date: Sun, 28 Apr 2013 16:35:50 -0300 Subject: [PATCH 099/332] Fix DDC-2409 --- lib/Doctrine/ORM/UnitOfWork.php | 2 +- .../ORM/Functional/Ticket/DDC2409Test.php | 72 +++++++++++++++++++ 2 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2409Test.php diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index 1d36718b9..4744a005d 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -1851,7 +1851,7 @@ class UnitOfWork implements PropertyChangedListener // do not merge fields marked lazy that have not been fetched. continue; } else if ( ! $assoc2['isCascadeMerge']) { - if ($this->getEntityState($other, self::STATE_DETACHED) !== self::STATE_MANAGED) { + if (($otherState = $this->getEntityState($other)) !== self::STATE_MANAGED && $otherState !== self::STATE_NEW) { $targetClass = $this->em->getClassMetadata($assoc2['targetEntity']); $relatedId = $targetClass->getIdentifierValues($other); diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2409Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2409Test.php new file mode 100644 index 000000000..c8b85f24f --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2409Test.php @@ -0,0 +1,72 @@ +useModelSet('cms'); + parent::setUp(); + } + + public function testIssue() + { + $em = $this->_em; + $uow = $em->getUnitOfWork(); + + $originalArticle = new CmsArticle(); + $originalUser = new CmsUser(); + + $originalArticle->topic = 'Unit Test'; + $originalArticle->text = 'How to write a test'; + + $originalUser->name = 'Doctrine Bot'; + $originalUser->username = 'DoctrineBot'; + $originalUser->status = 'active'; + + $originalUser->addArticle($originalArticle); + + $em->persist($originalUser); + $em->persist($originalArticle); + $em->flush(); + $em->clear(); + + $article = $em->find('Doctrine\Tests\Models\CMS\CmsArticle', $originalArticle->id); + $user = new CmsUser(); + + $user->name = 'Doctrine Bot 2.0'; + $user->username = 'BotDoctrine2'; + $user->status = 'new'; + + $article->setAuthor($user); + + $this->assertEquals(UnitOfWork::STATE_DETACHED, $uow->getEntityState($originalArticle)); + $this->assertEquals(UnitOfWork::STATE_DETACHED, $uow->getEntityState($originalUser)); + $this->assertEquals(UnitOfWork::STATE_MANAGED, $uow->getEntityState($article)); + $this->assertEquals(UnitOfWork::STATE_NEW, $uow->getEntityState($user)); + + $em->detach($user); + $em->detach($article); + + $userMerged = $em->merge($user); + $articleMerged = $em->merge($article); + + $this->assertEquals(UnitOfWork::STATE_NEW, $uow->getEntityState($user)); + $this->assertEquals(UnitOfWork::STATE_DETACHED, $uow->getEntityState($article)); + $this->assertEquals(UnitOfWork::STATE_MANAGED, $uow->getEntityState($userMerged)); + $this->assertEquals(UnitOfWork::STATE_MANAGED, $uow->getEntityState($articleMerged)); + + $this->assertNotSame($user, $userMerged); + $this->assertNotSame($article, $articleMerged); + $this->assertNotSame($userMerged, $articleMerged->user); + $this->assertSame($user, $articleMerged->user); + } +} \ No newline at end of file From 34adb16ee85deb0e26caab0eeeecbcf7ae7752de Mon Sep 17 00:00:00 2001 From: Asmir Mustafic Date: Mon, 29 Apr 2013 10:29:58 +0200 Subject: [PATCH 100/332] indexby ddc117 tests --- .../Tests/Models/DDC117/DDC117Article.php | 10 +++++++--- .../ORM/Functional/Ticket/DDC117Test.php | 15 +++++++++++++++ .../ORM/Hydration/ResultSetMappingTest.php | 19 +++++++++---------- 3 files changed, 31 insertions(+), 13 deletions(-) diff --git a/tests/Doctrine/Tests/Models/DDC117/DDC117Article.php b/tests/Doctrine/Tests/Models/DDC117/DDC117Article.php index d77bb942b..655ac3611 100644 --- a/tests/Doctrine/Tests/Models/DDC117/DDC117Article.php +++ b/tests/Doctrine/Tests/Models/DDC117/DDC117Article.php @@ -24,12 +24,12 @@ class DDC117Article private $details; /** - * @OneToMany(targetEntity="DDC117Translation", mappedBy="article", cascade={"persist", "remove"}) + * @OneToMany(targetEntity="DDC117Translation", mappedBy="article", indexBy="language", cascade={"persist", "remove"}) */ private $translations; /** - * @OneToMany(targetEntity="DDC117Link", mappedBy="source") + * @OneToMany(targetEntity="DDC117Link", mappedBy="source", indexBy="target_id") */ private $links; @@ -75,6 +75,10 @@ class DDC117Article return $this->details; } + public function getLinks() + { + return $this->links; + } public function resetText() { $this->details = null; @@ -84,4 +88,4 @@ class DDC117Article { return $this->translations; } -} \ No newline at end of file +} diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC117Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC117Test.php index 34e329465..6673afd1a 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC117Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC117Test.php @@ -8,6 +8,7 @@ use Doctrine\Tests\Models\DDC117\DDC117Reference; use Doctrine\Tests\Models\DDC117\DDC117Translation; use Doctrine\Tests\Models\DDC117\DDC117ApproveChanges; use Doctrine\Tests\Models\DDC117\DDC117Editor; +use Doctrine\Tests\Models\DDC117\DDC117Link; require_once __DIR__ . '/../../../TestInit.php'; @@ -30,6 +31,9 @@ class DDC117Test extends \Doctrine\Tests\OrmFunctionalTestCase $this->_em->persist($this->article2); $this->_em->flush(); + $link = new DDC117Link($this->article1, $this->article2, "Link-Description"); + $this->_em->persist($link); + $this->reference = new DDC117Reference($this->article1, $this->article2, "Test-Description"); $this->_em->persist($this->reference); @@ -479,4 +483,15 @@ class DDC117Test extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertEquals(\Doctrine\ORM\UnitOfWork::STATE_MANAGED, $this->_em->getUnitOfWork()->getEntityState($reference)); } + /** + * @group DDC-117 + */ + public function testIndexByOnCompositeKeyField() + { + $article = $this->_em->find("Doctrine\Tests\Models\DDC117\DDC117Article", $this->article1->id()); + + $this->assertInstanceOf('Doctrine\Tests\Models\DDC117\DDC117Article', $article); + $this->assertEquals(1, count($article->getLinks())); + $this->assertTrue($article->getLinks()->offsetExists($this->article2->id())); + } } diff --git a/tests/Doctrine/Tests/ORM/Hydration/ResultSetMappingTest.php b/tests/Doctrine/Tests/ORM/Hydration/ResultSetMappingTest.php index fac93b6e6..700e3fe05 100644 --- a/tests/Doctrine/Tests/ORM/Hydration/ResultSetMappingTest.php +++ b/tests/Doctrine/Tests/ORM/Hydration/ResultSetMappingTest.php @@ -255,19 +255,18 @@ class ResultSetMappingTest extends \Doctrine\Tests\OrmTestCase $this->assertEquals('Doctrine\Tests\Models\CMS\CmsUser', $rsm->getDeclaringClass('status')); $this->assertEquals('Doctrine\Tests\Models\CMS\CmsUser', $rsm->getDeclaringClass('username')); } - + /** + * @group DDC-117 + */ public function testIndexByMetadataColumn() { - $rms = $this->_rsm; - $rms->addEntityResult('Doctrine\Tests\Models\Legacy\LegacyUser', 'u') - ->addJoinedEntityResult('Doctrine\Tests\Models\Legacy', 'lu', 'u', '_references') - ->addMetaResult('lu', '_source', '_source', true) - ->addMetaResult('lu', '_target', '_target', true) - ->addIndexBy('lu', '_source'); - - - $this->assertTrue($rms->hasIndexBy('lu')); + $this->_rsm->addEntityResult('Doctrine\Tests\Models\Legacy\LegacyUser', 'u'); + $this->_rsm->addJoinedEntityResult('Doctrine\Tests\Models\Legacy', 'lu', 'u', '_references'); + $this->_rsm->addMetaResult('lu', '_source', '_source', true); + $this->_rsm->addMetaResult('lu', '_target', '_target', true); + $this->_rsm->addIndexBy('lu', '_source'); + $this->assertTrue($this->_rsm->hasIndexBy('lu')); } } From 2ca24375e417855b8f4adbc4a885c2b3e8424116 Mon Sep 17 00:00:00 2001 From: Asmir Mustafic Date: Mon, 29 Apr 2013 10:32:40 +0200 Subject: [PATCH 101/332] no lang --- tests/Doctrine/Tests/Models/DDC117/DDC117Article.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Doctrine/Tests/Models/DDC117/DDC117Article.php b/tests/Doctrine/Tests/Models/DDC117/DDC117Article.php index 655ac3611..e1df91c9a 100644 --- a/tests/Doctrine/Tests/Models/DDC117/DDC117Article.php +++ b/tests/Doctrine/Tests/Models/DDC117/DDC117Article.php @@ -24,7 +24,7 @@ class DDC117Article private $details; /** - * @OneToMany(targetEntity="DDC117Translation", mappedBy="article", indexBy="language", cascade={"persist", "remove"}) + * @OneToMany(targetEntity="DDC117Translation", mappedBy="article", cascade={"persist", "remove"}) */ private $translations; From 7abf46af7038559b83d0d3f963685ca04a2fbe17 Mon Sep 17 00:00:00 2001 From: Asmir Mustafic Date: Mon, 29 Apr 2013 11:03:55 +0200 Subject: [PATCH 102/332] cascade remove persist on links --- tests/Doctrine/Tests/Models/DDC117/DDC117Article.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Doctrine/Tests/Models/DDC117/DDC117Article.php b/tests/Doctrine/Tests/Models/DDC117/DDC117Article.php index e1df91c9a..9d57b5652 100644 --- a/tests/Doctrine/Tests/Models/DDC117/DDC117Article.php +++ b/tests/Doctrine/Tests/Models/DDC117/DDC117Article.php @@ -29,7 +29,7 @@ class DDC117Article private $translations; /** - * @OneToMany(targetEntity="DDC117Link", mappedBy="source", indexBy="target_id") + * @OneToMany(targetEntity="DDC117Link", mappedBy="source", indexBy="target_id", cascade={"persist", "remove"}) */ private $links; From 1f08acb5760135f51cb341779df2326e6f25a8a4 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 1 May 2013 10:42:28 +0200 Subject: [PATCH 103/332] [DBAL-483] Pass default values to DBAL mapping layer correctly to fix default comparision bug. --- lib/Doctrine/ORM/Tools/SchemaTool.php | 20 ++----- .../ORM/Functional/SchemaTool/DBAL483Test.php | 56 +++++++++++++++++++ .../ORM/Functional/SchemaTool/DDC214Test.php | 8 +-- 3 files changed, 65 insertions(+), 19 deletions(-) create mode 100644 tests/Doctrine/Tests/ORM/Functional/SchemaTool/DBAL483Test.php diff --git a/lib/Doctrine/ORM/Tools/SchemaTool.php b/lib/Doctrine/ORM/Tools/SchemaTool.php index f66a49d15..d6f6fc960 100644 --- a/lib/Doctrine/ORM/Tools/SchemaTool.php +++ b/lib/Doctrine/ORM/Tools/SchemaTool.php @@ -398,22 +398,14 @@ class SchemaTool } if (isset($mapping['options'])) { - if (isset($mapping['options']['comment'])) { - $options['comment'] = $mapping['options']['comment']; + $knownOptions = array('comment', 'unsigned', 'fixed', 'default'); - unset($mapping['options']['comment']); - } + foreach ($knownOptions as $knownOption) { + if ( isset($mapping['options'][$knownOption])) { + $options[$knownOption] = $mapping['options'][$knownOption]; - if (isset($mapping['options']['unsigned'])) { - $options['unsigned'] = $mapping['options']['unsigned']; - - unset($mapping['options']['unsigned']); - } - - if (isset($mapping['options']['fixed'])) { - $options['fixed'] = $mapping['options']['fixed']; - - unset($mapping['options']['fixed']); + unset($mapping['options'][$knownOption]); + } } $options['customSchemaOptions'] = $mapping['options']; diff --git a/tests/Doctrine/Tests/ORM/Functional/SchemaTool/DBAL483Test.php b/tests/Doctrine/Tests/ORM/Functional/SchemaTool/DBAL483Test.php new file mode 100644 index 000000000..0c76edf73 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/SchemaTool/DBAL483Test.php @@ -0,0 +1,56 @@ +_em->getConnection(); + + $this->schemaTool = new Tools\SchemaTool($this->_em); + } + + /** + * @group DBAL-483 + */ + public function testDefaultValueIsComparedCorrectly() + { + $class = $this->_em->getClassMetadata(__NAMESPACE__ . '\\DBAL483Default'); + + $this->schemaTool->createSchema(array($class)); + + $updateSql = $this->schemaTool->getUpdateSchemaSql(array($class)); + + $updateSql = array_filter($updateSql, function ($sql) { + return strpos($sql, 'DBAL483') !== false; + }); + + $this->assertEquals(0, count($updateSql)); + } +} + +/** + * @Entity + */ +class DBAL483Default +{ + /** + * @Id @Column(type="integer") @GeneratedValue + */ + public $id; + + /** + * @Column(type="integer", options={"default": 0}) + */ + public $num; + + /** + * @Column(type="string", options={"default": "foo"}) + */ + public $str = "foo"; +} diff --git a/tests/Doctrine/Tests/ORM/Functional/SchemaTool/DDC214Test.php b/tests/Doctrine/Tests/ORM/Functional/SchemaTool/DDC214Test.php index 9b428c71e..22d2b1f8e 100644 --- a/tests/Doctrine/Tests/ORM/Functional/SchemaTool/DDC214Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/SchemaTool/DDC214Test.php @@ -4,9 +4,6 @@ namespace Doctrine\Tests\ORM\Functional\SchemaTool; use Doctrine\ORM\Tools; - -require_once __DIR__ . '/../../../TestInit.php'; - /** * WARNING: This test should be run as last test! It can affect others very easily! */ @@ -15,7 +12,8 @@ class DDC214Test extends \Doctrine\Tests\OrmFunctionalTestCase private $classes = array(); private $schemaTool = null; - public function setUp() { + public function setUp() + { parent::setUp(); $conn = $this->_em->getConnection(); @@ -88,4 +86,4 @@ class DDC214Test extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertEquals(0, count($sql), "SQL: " . implode(PHP_EOL, $sql)); } -} \ No newline at end of file +} From 6505c96ec47538f24f460adf5af8298e7e0de112 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 1 May 2013 10:58:44 +0200 Subject: [PATCH 104/332] Simplify condition of previous commit (5cdc73e) --- lib/Doctrine/ORM/UnitOfWork.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index 4744a005d..c1065dba2 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -1851,7 +1851,7 @@ class UnitOfWork implements PropertyChangedListener // do not merge fields marked lazy that have not been fetched. continue; } else if ( ! $assoc2['isCascadeMerge']) { - if (($otherState = $this->getEntityState($other)) !== self::STATE_MANAGED && $otherState !== self::STATE_NEW) { + if ($this->getEntityState($other) === self::STATE_DETACHED) { $targetClass = $this->em->getClassMetadata($assoc2['targetEntity']); $relatedId = $targetClass->getIdentifierValues($other); @@ -1862,6 +1862,7 @@ class UnitOfWork implements PropertyChangedListener $this->registerManaged($other, $relatedId, array()); } } + $prop->setValue($managedCopy, $other); } } else { From 640a8e58c74bc0ed3b24b8c037f731f4dd2c4c14 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 1 May 2013 18:46:41 +0200 Subject: [PATCH 105/332] [DDC-2106] Fix entity as parameter value when its managed but not yet with identifier. --- .../ORM/Persisters/BasicEntityPersister.php | 11 +--- lib/Doctrine/ORM/UnitOfWork.php | 2 +- .../ORM/Functional/Ticket/DDC2106Test.php | 64 +++++++++++++++++++ 3 files changed, 66 insertions(+), 11 deletions(-) create mode 100644 tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2106Test.php diff --git a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php index 176eb38f8..8d2aa5236 100644 --- a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php @@ -1848,16 +1848,7 @@ class BasicEntityPersister return $value; } - if ($this->em->getUnitOfWork()->getEntityState($value) === UnitOfWork::STATE_MANAGED) { - $idValues = $this->em->getUnitOfWork()->getEntityIdentifier($value); - - return reset($idValues); - } - - $class = $this->em->getClassMetadata(get_class($value)); - $idValues = $class->getIdentifierValues($value); - - return reset($idValues); + return $this->em->getUnitOfWork()->getSingleIdentifierValue($value); } /** diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index c1065dba2..498c4a699 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -2860,7 +2860,7 @@ class UnitOfWork implements PropertyChangedListener throw ORMInvalidArgumentException::invalidCompositeIdentifier(); } - $values = ($this->getEntityState($entity) === UnitOfWork::STATE_MANAGED) + $values = $this->isInIdentityMap($entity) ? $this->getEntityIdentifier($entity) : $class->getIdentifierValues($entity); diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2106Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2106Test.php new file mode 100644 index 000000000..0de386f9a --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2106Test.php @@ -0,0 +1,64 @@ +_schemaTool->createSchema(array( + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2106Entity'), + )); + } + + public function testDetachedEntityAsId() + { + // We want an uninitialized PersistentCollection $entity->children + $entity = new DDC2106Entity(); + $this->_em->persist($entity); + $this->_em->flush(); + $this->_em->detach($entity); + $entity = $this->_em->getRepository(__NAMESPACE__ . '\DDC2106Entity')->findOneBy(array()); + + // ... and a managed entity without id + $entityWithoutId = new DDC2106Entity(); + $this->_em->persist($entityWithoutId); + + $criteria = Criteria::create()->where(Criteria::expr()->eq('parent', $entityWithoutId)); + $entity->children->matching($criteria)->count(); + } +} + +/** + * @Entity + */ +class DDC2106Entity +{ + /** + * @Id + * @GeneratedValue(strategy="IDENTITY") + * @Column(type="integer") + */ + public $id; + + /** @ManyToOne(targetEntity="DDC2106Entity", inversedBy="children") */ + public $parent; + + /** + * @OneToMany(targetEntity="DDC2106Entity", mappedBy="parent", cascade={"persist"}) + */ + public $children; + + public function __construct() + { + $this->children = new \Doctrine\Common\Collections\ArrayCollection; + } +} + From 131164b7f6082bb3f50176e3edb6be9966273271 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 1 May 2013 19:39:21 +0200 Subject: [PATCH 106/332] [DDC-1984] Throw exception if passing null into UnitOfWork#lock() - which can happen when EntityManager#find() tries to lock entity that was just deleted by another process. --- lib/Doctrine/ORM/UnitOfWork.php | 4 ++++ tests/Doctrine/Tests/ORM/UnitOfWorkTest.php | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index 498c4a699..b45e4921b 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -2270,6 +2270,10 @@ class UnitOfWork implements PropertyChangedListener */ public function lock($entity, $lockMode, $lockVersion = null) { + if ($entity === null) { + throw new \InvalidArgumentException("No entity passed to UnitOfWork#lock()."); + } + if ($this->getEntityState($entity, self::STATE_DETACHED) != self::STATE_MANAGED) { throw ORMInvalidArgumentException::entityNotManaged($entity); } diff --git a/tests/Doctrine/Tests/ORM/UnitOfWorkTest.php b/tests/Doctrine/Tests/ORM/UnitOfWorkTest.php index 9a645d52e..97f60f859 100644 --- a/tests/Doctrine/Tests/ORM/UnitOfWorkTest.php +++ b/tests/Doctrine/Tests/ORM/UnitOfWorkTest.php @@ -220,6 +220,15 @@ class UnitOfWorkTest extends \Doctrine\Tests\OrmTestCase // This commit should not raise an E_NOTICE $this->_unitOfWork->commit(); } + + /** + * @group DDC-1984 + */ + public function testLockWithoutEntityThrowsException() + { + $this->setExpectedException('InvalidArgumentException'); + $this->_unitOfWork->lock(null, null, null); + } } /** From 0864ab8adac5c31d5ba97f0eb6792e5431a75b70 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 1 May 2013 20:30:45 +0200 Subject: [PATCH 107/332] [DDC-1998] Pass types to Connection#delete() to allow custom conversions to happen. --- .../ORM/Persisters/BasicEntityPersister.php | 28 ++++- .../ORM/Functional/Ticket/DDC1998Test.php | 102 ++++++++++++++++++ 2 files changed, 127 insertions(+), 3 deletions(-) create mode 100644 tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1998Test.php diff --git a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php index 8d2aa5236..96f144934 100644 --- a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php @@ -560,13 +560,35 @@ class BasicEntityPersister */ public function delete($entity) { + $class = $this->class; + $em = $this->em; + $identifier = $this->em->getUnitOfWork()->getEntityIdentifier($entity); - $tableName = $this->quoteStrategy->getTableName($this->class, $this->platform); - $idColumns = $this->quoteStrategy->getIdentifierColumnNames($this->class, $this->platform); + $tableName = $this->quoteStrategy->getTableName($class, $this->platform); + $idColumns = $this->quoteStrategy->getIdentifierColumnNames($class, $this->platform); $id = array_combine($idColumns, $identifier); + $types = array_map(function ($identifier) use ($class, $em) { + if (isset($class->fieldMappings[$identifier])) { + return $class->fieldMappings[$identifier]['type']; + } + + $targetMapping = $em->getClassMetadata($class->associationMappings[$identifier]['targetEntity']); + + if (isset($targetMapping->fieldMappings[$targetMapping->identifier[0]])) { + return $targetMapping->fieldMappings[$targetMapping->identifier[0]]['type']; + } + + if (isset($targetMapping->associationMappings[$targetMapping->identifier[0]])) { + $types[] = $targetMapping->associationMappings[$targetMapping->identifier[0]]['type']; + } + + throw ORMException::unrecognizedField($targetMapping->identifier[0]); + + }, $class->identifier); + $this->deleteJoinTableRecords($identifier); - $this->conn->delete($tableName, $id); + $this->conn->delete($tableName, $id, $types); } /** diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1998Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1998Test.php new file mode 100644 index 000000000..95e0a76f5 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1998Test.php @@ -0,0 +1,102 @@ +_schemaTool->createSchema(array( + $this->_em->getClassMetadata(__NAMESPACE__ . '\\DDC1998Entity'), + )); + + $entity = new DDC1998Entity(); + $entity->id = new DDC1998Id("foo"); + + $this->_em->persist($entity); + $this->_em->flush(); + + $entity->num++; + + $this->_em->flush(); + + $this->_em->remove($entity); + $this->_em->flush(); + $this->_em->clear(); + + + $found = $this->_em->find(__NAMESPACE__ . '\\DDC1998Entity', $entity->id); + $this->assertNull($found); + + $found = $this->_em->find(__NAMESPACE__ . '\\DDC1998Entity', "foo"); + $this->assertNull($found); + + $this->assertEquals(0, count($this->_em->getRepository(__NAMESPACE__ . '\\DDC1998Entity')->findAll())); + } +} + +/** + * @Entity + */ +class DDC1998Entity +{ + /** + * @Id @Column(type="ddc1998") + */ + public $id; + + /** + * @Column(type="integer") + */ + public $num = 0; +} + +class DDC1998Type extends StringType +{ + const NAME = 'ddc1998'; + + /** + * {@inheritdoc} + */ + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + return (string)$value; + } + + public function convertToPhpValue($value, AbstractPlatform $platform) + { + return new DDC1998Id($value); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return self::NAME; + } +} + +class DDC1998Id +{ + private $val; + + public function __construct($val) + { + $this->val = $val; + } + + public function __toString() + { + return $this->val; + } +} From 6d02c7e1a59a6d19d695b3c564a452d138478e1a Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 1 May 2013 23:10:13 +0200 Subject: [PATCH 108/332] [DDC-2136] Fix exporting to YAML and XML with assocation keys. --- .travis.yml | 4 ++-- composer.json | 3 +++ lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php | 1 + .../ORM/Tools/Export/Driver/XmlExporter.php | 14 +++++++++++++- .../ORM/Tools/Export/Driver/YamlExporter.php | 8 +++++--- .../Export/AbstractClassMetadataExporterTest.php | 2 +- 6 files changed, 25 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 177b5ba6c..a97513344 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,6 +19,6 @@ before_script: - sh -c "if [ '$DB' = 'pgsql' ]; then psql -c 'create database doctrine_tests;' -U postgres; fi" - sh -c "if [ '$DB' = 'pgsql' ]; then psql -c 'create database doctrine_tests_tmp;' -U postgres; fi" - sh -c "if [ '$DB' = 'mysql' ]; then mysql -e 'create database IF NOT EXISTS doctrine_tests_tmp;create database IF NOT EXISTS doctrine_tests;'; fi" - - composer install --prefer-source + - composer install --prefer-source --dev -script: phpunit --configuration tests/travis/$DB.travis.xml \ No newline at end of file +script: phpunit --configuration tests/travis/$DB.travis.xml diff --git a/composer.json b/composer.json index 52960133d..f74058bac 100644 --- a/composer.json +++ b/composer.json @@ -19,6 +19,9 @@ "doctrine/dbal": ">=2.4-beta,<2.5-dev", "symfony/console": "2.*" }, + "require-dev": { + "symfony/yaml": "2.1" + }, "suggest": { "symfony/yaml": "If you want to use YAML Metadata Mapping Driver" }, diff --git a/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php b/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php index 5bd58ef79..9e8818a16 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php @@ -307,6 +307,7 @@ class YamlDriver extends FileDriver } } + // Evaluate oneToOne relationships if (isset($element['oneToOne'])) { foreach ($element['oneToOne'] as $name => $oneToOneElement) { diff --git a/lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php b/lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php index 2d9cb4d3c..ccbbfc3be 100644 --- a/lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php +++ b/lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php @@ -129,6 +129,15 @@ class XmlExporter extends AbstractExporter } } + foreach ($metadata->associationMappings as $name => $assoc) { + if (isset($assoc['id']) && $assoc['id']) { + $id[$name] = array( + 'fieldName' => $name, + 'associationKey' => true + ); + } + } + if ( ! $metadata->isIdentifierComposite && $idGeneratorType = $this->_getIdGeneratorTypeString($metadata->generatorType)) { $id[$metadata->getSingleIdentifierFieldName()]['generator']['strategy'] = $idGeneratorType; } @@ -137,7 +146,10 @@ class XmlExporter extends AbstractExporter foreach ($id as $field) { $idXml = $root->addChild('id'); $idXml->addAttribute('name', $field['fieldName']); - $idXml->addAttribute('type', $field['type']); + + if (isset($field['type'])) { + $idXml->addAttribute('type', $field['type']); + } if (isset($field['columnName'])) { $idXml->addAttribute('column', $field['columnName']); diff --git a/lib/Doctrine/ORM/Tools/Export/Driver/YamlExporter.php b/lib/Doctrine/ORM/Tools/Export/Driver/YamlExporter.php index ab5e5f968..45c431925 100644 --- a/lib/Doctrine/ORM/Tools/Export/Driver/YamlExporter.php +++ b/lib/Doctrine/ORM/Tools/Export/Driver/YamlExporter.php @@ -110,9 +110,7 @@ class YamlExporter extends AbstractExporter $ids[$metadata->getSingleIdentifierFieldName()]['generator']['strategy'] = $idGeneratorType; } - if ($ids) { - $array['fields'] = $ids; - } + $array['id'] = $ids; if ($fieldMappings) { if ( ! isset($array['fields'])) { @@ -152,6 +150,10 @@ class YamlExporter extends AbstractExporter 'cascade' => $cascade, ); + if (isset($mapping['id']) && $mapping['id'] === true) { + $array['id'][$name]['associationKey'] = true; + } + if ($associationMapping['type'] & ClassMetadataInfo::TO_ONE) { $joinColumns = $associationMapping['joinColumns']; $newJoinColumns = array(); diff --git a/tests/Doctrine/Tests/ORM/Tools/Export/AbstractClassMetadataExporterTest.php b/tests/Doctrine/Tests/ORM/Tools/Export/AbstractClassMetadataExporterTest.php index 37e0f0ca2..060345c83 100644 --- a/tests/Doctrine/Tests/ORM/Tools/Export/AbstractClassMetadataExporterTest.php +++ b/tests/Doctrine/Tests/ORM/Tools/Export/AbstractClassMetadataExporterTest.php @@ -178,7 +178,7 @@ abstract class AbstractClassMetadataExporterTest extends \Doctrine\Tests\OrmTest */ public function testIdentifierIsExported($class) { - $this->assertEquals(ClassMetadataInfo::GENERATOR_TYPE_IDENTITY, $class->generatorType); + $this->assertEquals(ClassMetadataInfo::GENERATOR_TYPE_IDENTITY, $class->generatorType, "Generator Type wrong"); $this->assertEquals(array('id'), $class->identifier); $this->assertTrue(isset($class->fieldMappings['id']['id']) && $class->fieldMappings['id']['id'] === true); From 59fff4ddef236d373ae4d08e34114c9757cdf3d2 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Fri, 3 May 2013 13:32:42 +0200 Subject: [PATCH 109/332] Fix Docs build --- docs/en/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/conf.py b/docs/en/conf.py index 4e0c87b50..5155ac9a6 100644 --- a/docs/en/conf.py +++ b/docs/en/conf.py @@ -22,7 +22,7 @@ sys.path.append(os.path.abspath('_exts')) # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['configurationblock', 'sphinx.ext.linkcode'] +extensions = ['configurationblock'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] From 9adc45767d31d90790ff25675faa2d6da86d67d8 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sat, 4 May 2013 12:58:06 +0200 Subject: [PATCH 110/332] [DDC-2426] Missing length attribute in doctrine-mapping.xsd for tag. --- doctrine-mapping.xsd | 1 + 1 file changed, 1 insertion(+) diff --git a/doctrine-mapping.xsd b/doctrine-mapping.xsd index 243bfcd5b..89c11a894 100644 --- a/doctrine-mapping.xsd +++ b/doctrine-mapping.xsd @@ -345,6 +345,7 @@ + From 5e19e1bed3ee593e38c7a7d882232d43df710d7c Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sat, 4 May 2013 13:36:37 +0200 Subject: [PATCH 111/332] [DDC-2267] Allow EntityManager#flush($entity) to be called on entities scheduled for removal. --- lib/Doctrine/ORM/UnitOfWork.php | 8 +++++--- .../Tests/ORM/Functional/BasicFunctionalTest.php | 13 +++++++++++-- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index b45e4921b..68fde5d2c 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -421,13 +421,15 @@ class UnitOfWork implements PropertyChangedListener */ private function computeSingleEntityChangeSet($entity) { - if ( $this->getEntityState($entity) !== self::STATE_MANAGED) { - throw new \InvalidArgumentException("Entity has to be managed for single computation " . self::objToStr($entity)); + $state = $this->getEntityState($entity); + + if ($state !== self::STATE_MANAGED && $state !== self::STATE_REMOVED) { + throw new \InvalidArgumentException("Entity has to be managed or scheduled for removal for single computation " . self::objToStr($entity)); } $class = $this->em->getClassMetadata(get_class($entity)); - if ($class->isChangeTrackingDeferredImplicit()) { + if ($state === self::STATE_MANAGED && $class->isChangeTrackingDeferredImplicit()) { $this->persist($entity); } diff --git a/tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php b/tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php index 221bc011f..2fb830a92 100644 --- a/tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php @@ -1118,7 +1118,7 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase $user->username = 'domnikl'; $user->status = 'developer'; - $this->setExpectedException('InvalidArgumentException', 'Entity has to be managed for single computation'); + $this->setExpectedException('InvalidArgumentException', 'Entity has to be managed or scheduled for removal for single computation'); $this->_em->flush($user); } @@ -1202,8 +1202,9 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase /** * @group DDC-720 * @group DDC-1612 + * @group DDC-2267 */ - public function testFlushSingleNewEntity() + public function testFlushSingleNewEntityThenRemove() { $user = new CmsUser; $user->name = 'Dominik'; @@ -1212,6 +1213,14 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->_em->persist($user); $this->_em->flush($user); + + $userId = $user->id; + + $this->_em->remove($user); + $this->_em->flush($user); + $this->_em->clear(); + + $this->assertNull($this->_em->find(get_class($user), $userId)); } /** From 33888f1b084c24d3c209888d5c70f5d2c9f4c043 Mon Sep 17 00:00:00 2001 From: Vladislav Vlastovskiy Date: Thu, 9 May 2013 03:30:48 +0400 Subject: [PATCH 112/332] Swapped places indexBy and condition in accordance with EBNF --- lib/Doctrine/ORM/Query/Expr/Join.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Doctrine/ORM/Query/Expr/Join.php b/lib/Doctrine/ORM/Query/Expr/Join.php index c7ca935eb..7a59e247a 100644 --- a/lib/Doctrine/ORM/Query/Expr/Join.php +++ b/lib/Doctrine/ORM/Query/Expr/Join.php @@ -139,7 +139,7 @@ class Join { return strtoupper($this->joinType) . ' JOIN ' . $this->join . ($this->alias ? ' ' . $this->alias : '') - . ($this->condition ? ' ' . strtoupper($this->conditionType) . ' ' . $this->condition : '') - . ($this->indexBy ? ' INDEX BY ' . $this->indexBy : ''); + . ($this->indexBy ? ' INDEX BY ' . $this->indexBy : '') + . ($this->condition ? ' ' . strtoupper($this->conditionType) . ' ' . $this->condition : ''); } } From 3997d0df8782927e38dd2bf8bf263c4832bb1d6f Mon Sep 17 00:00:00 2001 From: Vladislav Vlastovskiy Date: Thu, 9 May 2013 03:32:28 +0400 Subject: [PATCH 113/332] Added test complex inner join with indexBy --- tests/Doctrine/Tests/ORM/QueryBuilderTest.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/Doctrine/Tests/ORM/QueryBuilderTest.php b/tests/Doctrine/Tests/ORM/QueryBuilderTest.php index 19e0c0e95..6975c1510 100644 --- a/tests/Doctrine/Tests/ORM/QueryBuilderTest.php +++ b/tests/Doctrine/Tests/ORM/QueryBuilderTest.php @@ -141,6 +141,19 @@ class QueryBuilderTest extends \Doctrine\Tests\OrmTestCase 'SELECT u, a FROM Doctrine\Tests\Models\CMS\CmsUser u INNER JOIN u.articles a ON u.id = a.author_id' ); } + + public function testComplexInnerJoinWithIndexBy() + { + $qb = $this->_em->createQueryBuilder() + ->select('u', 'a') + ->from('Doctrine\Tests\Models\CMS\CmsUser', 'u') + ->innerJoin('u.articles', 'a', 'ON', 'u.id = a.author_id', 'a.name'); + + $this->assertValidQueryBuilder( + $qb, + 'SELECT u, a FROM Doctrine\Tests\Models\CMS\CmsUser u INNER JOIN u.articles a INDEX BY a.name ON u.id = a.author_id' + ); + } public function testLeftJoin() { From acbafd081b13885bb65f7738682317cc27056c78 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Thu, 9 May 2013 10:23:12 +0200 Subject: [PATCH 114/332] Add documentation to EntityManager about instantiation, decoration over inheritance, and some generic introduction. --- lib/Doctrine/ORM/EntityManager.php | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/lib/Doctrine/ORM/EntityManager.php b/lib/Doctrine/ORM/EntityManager.php index 53cbf1926..bc0d4ef18 100644 --- a/lib/Doctrine/ORM/EntityManager.php +++ b/lib/Doctrine/ORM/EntityManager.php @@ -34,6 +34,28 @@ use Doctrine\Common\Util\ClassUtils; /** * The EntityManager is the central access point to ORM functionality. * + * It is a facade to all different ORM subsystems such as UnitOfWork, + * Query Language and Repository API. Instantiation is done through + * the static create() method. The quickest way to obtain a fully + * configured EntityManager is: + * + * use Doctrine\ORM\Tools\Setup; + * use Doctrine\ORM\EntityManager; + * + * $paths = array('/path/to/entity/mapping/files'); + * + * $config = Setup::createAnnotationMetadataConfiguration($paths); + * $dbParams = array('driver' => 'pdo_sqlite', 'memory' => true); + * $entityManager = EntityManager::create($dbParams, $config); + * + * For more information see + * {@link http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/configuration.html} + * + * You should never attempt to inherit from the EntityManager: Inheritance + * is not a valid extension point for the EntityManager. Instead you + * should take a look at the {@see \Doctrine\ORM\Decorator\EntityManagerDecorator} + * and wrap your entity manager in a decorator. + * * @since 2.0 * @author Benjamin Eberlei * @author Guilherme Blanco From e3b8ce7737ecd18295db760c4dd1e55037580df4 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Thu, 9 May 2013 10:55:12 +0200 Subject: [PATCH 115/332] [DDC-2423] Fixed bug with EntityGenerator not generating fetch="" attribute in association annotations. --- lib/Doctrine/ORM/Tools/EntityGenerator.php | 9 +++++++++ tests/Doctrine/Tests/ORM/Tools/EntityGeneratorTest.php | 3 +++ 2 files changed, 12 insertions(+) diff --git a/lib/Doctrine/ORM/Tools/EntityGenerator.php b/lib/Doctrine/ORM/Tools/EntityGenerator.php index 98a65fb98..5a55f0fc9 100644 --- a/lib/Doctrine/ORM/Tools/EntityGenerator.php +++ b/lib/Doctrine/ORM/Tools/EntityGenerator.php @@ -1274,6 +1274,15 @@ public function __construct() $typeOptions[] = 'orphanRemoval=' . ($associationMapping['orphanRemoval'] ? 'true' : 'false'); } + if (isset($associationMapping['fetch']) && $associationMapping['fetch'] !== ClassMetadataInfo::FETCH_LAZY) { + $fetchMap = array( + ClassMetadataInfo::FETCH_EXTRA_LAZY => 'EXTRA_LAZY', + ClassMetadataInfo::FETCH_EAGER => 'EAGER', + ); + + $typeOptions[] = 'fetch="' . $fetchMap[$associationMapping['fetch']] . '"'; + } + $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . '' . $type . '(' . implode(', ', $typeOptions) . ')'; if (isset($associationMapping['joinColumns']) && $associationMapping['joinColumns']) { diff --git a/tests/Doctrine/Tests/ORM/Tools/EntityGeneratorTest.php b/tests/Doctrine/Tests/ORM/Tools/EntityGeneratorTest.php index a55b603d5..d8dfa1b56 100644 --- a/tests/Doctrine/Tests/ORM/Tools/EntityGeneratorTest.php +++ b/tests/Doctrine/Tests/ORM/Tools/EntityGeneratorTest.php @@ -64,6 +64,7 @@ class EntityGeneratorTest extends \Doctrine\Tests\OrmTestCase $metadata->mapManyToMany(array( 'fieldName' => 'comments', 'targetEntity' => 'Doctrine\Tests\ORM\Tools\EntityGeneratorComment', + 'fetch' => ClassMetadataInfo::FETCH_EXTRA_LAZY, 'joinTable' => array( 'name' => 'book_comment', 'joinColumns' => array(array('name' => 'book_id', 'referencedColumnName' => 'id')), @@ -223,6 +224,8 @@ class EntityGeneratorTest extends \Doctrine\Tests\OrmTestCase $this->assertEquals($cm->identifier, $metadata->identifier); $this->assertEquals($cm->idGenerator, $metadata->idGenerator); $this->assertEquals($cm->customRepositoryClassName, $metadata->customRepositoryClassName); + + $this->assertEquals(ClassMetadataInfo::FETCH_EXTRA_LAZY, $cm->associationMappings['comments']['fetch']); } public function testLoadPrefixedMetadata() From 7220c3c1251e149b268d73d7d09dc90b9f3656f9 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Thu, 9 May 2013 12:10:37 +0200 Subject: [PATCH 116/332] [DDC-2387] Fix DatabaseDriver not working with combinations of composite/association keys. --- .../ORM/Mapping/Driver/DatabaseDriver.php | 3 +- .../ORM/Functional/DatabaseDriverTest.php | 47 +--------------- .../ORM/Functional/DatabaseDriverTestCase.php | 55 +++++++++++++++++++ .../ORM/Functional/Ticket/DDC2387Test.php | 30 ++++++++++ 4 files changed, 89 insertions(+), 46 deletions(-) create mode 100644 tests/Doctrine/Tests/ORM/Functional/DatabaseDriverTestCase.php create mode 100644 tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2387Test.php diff --git a/lib/Doctrine/ORM/Mapping/Driver/DatabaseDriver.php b/lib/Doctrine/ORM/Mapping/Driver/DatabaseDriver.php index 15a4e0d2c..007be9639 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/DatabaseDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/DatabaseDriver.php @@ -224,7 +224,8 @@ class DatabaseDriver implements MappingDriver } if ($ids) { - if (count($ids) == 1) { + // We need to check for the columns here, because we might have associations as id as well. + if (count($primaryKeyColumns) == 1) { $metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_AUTO); } diff --git a/tests/Doctrine/Tests/ORM/Functional/DatabaseDriverTest.php b/tests/Doctrine/Tests/ORM/Functional/DatabaseDriverTest.php index 48140cc56..0c30a0bef 100644 --- a/tests/Doctrine/Tests/ORM/Functional/DatabaseDriverTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/DatabaseDriverTest.php @@ -2,12 +2,9 @@ namespace Doctrine\Tests\ORM\Functional; -require_once __DIR__ . '/../../TestInit.php'; +use Doctrine\ORM\Mapping\ClassMetadataInfo; -use Doctrine\ORM\Mapping\ClassMetadataInfo, - Doctrine\Common\Util\Inflector; - -class DatabaseDriverTest extends \Doctrine\Tests\OrmFunctionalTestCase +class DatabaseDriverTest extends DatabaseDriverTestCase { /** * @var \Doctrine\DBAL\Schema\AbstractSchemaManager @@ -148,44 +145,4 @@ class DatabaseDriverTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertEquals(0, count($metadatas['DbdriverBaz']->associationMappings), "no association mappings should be detected."); } - - protected function convertToClassMetadata(array $entityTables, array $manyTables = array()) - { - $driver = new \Doctrine\ORM\Mapping\Driver\DatabaseDriver($this->_sm); - $driver->setTables($entityTables, $manyTables); - - $metadatas = array(); - foreach ($driver->getAllClassNames() AS $className) { - $class = new ClassMetadataInfo($className); - $driver->loadMetadataForClass($className, $class); - $metadatas[$className] = $class; - } - - return $metadatas; - } - - /** - * @param string $className - * @return ClassMetadata - */ - protected function extractClassMetadata(array $classNames) - { - $classNames = array_map('strtolower', $classNames); - $metadatas = array(); - - $driver = new \Doctrine\ORM\Mapping\Driver\DatabaseDriver($this->_sm); - foreach ($driver->getAllClassNames() as $className) { - if (!in_array(strtolower($className), $classNames)) { - continue; - } - $class = new ClassMetadataInfo($className); - $driver->loadMetadataForClass($className, $class); - $metadatas[$className] = $class; - } - - if (count($metadatas) != count($classNames)) { - $this->fail("Have not found all classes matching the names '" . implode(", ", $classNames) . "' only tables " . implode(", ", array_keys($metadatas))); - } - return $metadatas; - } } diff --git a/tests/Doctrine/Tests/ORM/Functional/DatabaseDriverTestCase.php b/tests/Doctrine/Tests/ORM/Functional/DatabaseDriverTestCase.php new file mode 100644 index 000000000..35fd8b20c --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/DatabaseDriverTestCase.php @@ -0,0 +1,55 @@ +_em->getConnection()->getSchemaManager(); + $driver = new \Doctrine\ORM\Mapping\Driver\DatabaseDriver($sm); + $driver->setTables($entityTables, $manyTables); + + $metadatas = array(); + foreach ($driver->getAllClassNames() AS $className) { + $class = new ClassMetadataInfo($className); + $driver->loadMetadataForClass($className, $class); + $metadatas[$className] = $class; + } + + return $metadatas; + } + + /** + * @param string $className + * @return ClassMetadata + */ + protected function extractClassMetadata(array $classNames) + { + $classNames = array_map('strtolower', $classNames); + $metadatas = array(); + + $sm = $this->_em->getConnection()->getSchemaManager(); + $driver = new \Doctrine\ORM\Mapping\Driver\DatabaseDriver($sm); + + foreach ($driver->getAllClassNames() as $className) { + if (!in_array(strtolower($className), $classNames)) { + continue; + } + $class = new ClassMetadataInfo($className); + $driver->loadMetadataForClass($className, $class); + $metadatas[$className] = $class; + } + + if (count($metadatas) != count($classNames)) { + $this->fail("Have not found all classes matching the names '" . implode(", ", $classNames) . "' only tables " . implode(", ", array_keys($metadatas))); + } + return $metadatas; + } +} diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2387Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2387Test.php new file mode 100644 index 000000000..f74d4e15c --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2387Test.php @@ -0,0 +1,30 @@ +addColumn('id', 'integer'); + $product->setPrimaryKey(array('id')); + + $attributes = new \Doctrine\DBAL\Schema\Table('ddc2387_attributes'); + $attributes->addColumn('product_id', 'integer'); + $attributes->addColumn('attribute_name', 'string'); + $attributes->setPrimaryKey(array('product_id', 'attribute_name')); + $attributes->addForeignKeyConstraint('ddc2387_product', array('product_id'), array('product_id')); + + $metadata = $this->convertToClassMetadata(array($product, $attributes), array()); + + $this->assertEquals(ClassMetadataInfo::GENERATOR_TYPE_NONE, $metadata['Ddc2387Attributes']->generatorType); + $this->assertEquals(ClassMetadataInfo::GENERATOR_TYPE_AUTO, $metadata['Ddc2387Product']->generatorType); + } +} From 6d5afb18bc814bb578c2326b62ec6bcaa368b6c3 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Thu, 9 May 2013 13:24:36 +0200 Subject: [PATCH 117/332] [DDC-2430] Prevent Criteria queries using the ID of an assocation on PersistentCollections, as the in-memory ArrayCollection does not work with this kind of query. Attention this is a BC-BREAK, that is necessary to fix potentially very hard to debug bugs. Therefore it is not merged back into 2.3 --- UPGRADE.md | 14 ++++++++++ lib/Doctrine/ORM/PersistentCollection.php | 6 +---- .../ORM/Persisters/BasicEntityPersister.php | 2 +- .../ORM/Persisters/PersisterException.php | 20 ++++++++++++++ .../ORM/Persisters/SqlExpressionVisitor.php | 14 +++++++++- .../ORM/Functional/EntityRepositoryTest.php | 26 +++++++++++++++++-- .../Functional/SingleTableInheritanceTest.php | 19 ++++++++++++-- 7 files changed, 90 insertions(+), 11 deletions(-) create mode 100644 lib/Doctrine/ORM/Persisters/PersisterException.php diff --git a/UPGRADE.md b/UPGRADE.md index fe5f52528..0aacaa603 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -28,6 +28,20 @@ Now parenthesis are considered, the previous DQL will generate: SELECT 100 / (2 * 2) FROM my_entity +## Compatibility Bugfix in PersistentCollection#matching() breaks BC + +In Doctrine 2.3 it was possible to use the new ``matching($criteria)`` +functionality by adding constraints for assocations based on ID: + + Criteria::expr()->eq('association', $assocation->getId()); + +This functionality does not work on InMemory collections however, because +in memory criteria compares object values based on reference. +As of 2.4 the above code will throw an exception. You need to change +offending code to pass the ``$assocation`` reference directly: + + Criteria::expr()->eq('association', $assocation); + # Upgrade to 2.3 ## EntityManager#find() not calls EntityRepository#find() anymore diff --git a/lib/Doctrine/ORM/PersistentCollection.php b/lib/Doctrine/ORM/PersistentCollection.php index b644ec154..53ba1c1f2 100644 --- a/lib/Doctrine/ORM/PersistentCollection.php +++ b/lib/Doctrine/ORM/PersistentCollection.php @@ -850,12 +850,8 @@ final class PersistentCollection implements Collection, Selectable throw new \RuntimeException("Matching Criteria on PersistentCollection only works on OneToMany associations at the moment."); } - $id = $this->em - ->getClassMetadata(get_class($this->owner)) - ->getSingleIdReflectionProperty() - ->getValue($this->owner); $builder = Criteria::expr(); - $ownerExpression = $builder->eq($this->backRefFieldName, $id); + $ownerExpression = $builder->eq($this->backRefFieldName, $this->owner); $expression = $criteria->getWhereExpression(); $expression = $expression ? $builder->andX($expression, $ownerExpression) : $ownerExpression; diff --git a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php index 96f144934..8f921f2b8 100644 --- a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php @@ -1583,7 +1583,7 @@ class BasicEntityPersister return ''; } - $visitor = new SqlExpressionVisitor($this); + $visitor = new SqlExpressionVisitor($this, $this->class); return $visitor->dispatch($expression); } diff --git a/lib/Doctrine/ORM/Persisters/PersisterException.php b/lib/Doctrine/ORM/Persisters/PersisterException.php new file mode 100644 index 000000000..111455e5b --- /dev/null +++ b/lib/Doctrine/ORM/Persisters/PersisterException.php @@ -0,0 +1,20 @@ +persister = $persister; + $this->classMetadata = $classMetadata; } /** @@ -57,6 +65,10 @@ class SqlExpressionVisitor extends ExpressionVisitor $field = $comparison->getField(); $value = $comparison->getValue()->getValue(); // shortcut for walkValue() + if (isset($this->classMetadata->associationMappings[$field]) && ! is_object($value)) { + throw PersisterException::matchingAssocationFieldRequiresObject($this->classMetadata->name, $field); + } + return $this->persister->getSelectConditionStatementSQL($field, $value, null, $comparison->getOperator()); } diff --git a/tests/Doctrine/Tests/ORM/Functional/EntityRepositoryTest.php b/tests/Doctrine/Tests/ORM/Functional/EntityRepositoryTest.php index b45eee3d0..48e5f3389 100644 --- a/tests/Doctrine/Tests/ORM/Functional/EntityRepositoryTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/EntityRepositoryTest.php @@ -7,8 +7,7 @@ use Doctrine\Tests\Models\CMS\CmsEmail; use Doctrine\Tests\Models\CMS\CmsAddress; use Doctrine\Tests\Models\CMS\CmsPhonenumber; use Doctrine\Common\Collections\Criteria; - -require_once __DIR__ . '/../../TestInit.php'; +use Doctrine\Common\Collections\ArrayCollection; /** * @author robo @@ -781,6 +780,29 @@ class EntityRepositoryTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertEquals(4, count($users)); } + /** + * @group DDC-2430 + */ + public function testMatchingCriteriaAssocationByObjectInMemory() + { + list($userId, $addressId) = $this->loadAssociatedFixture(); + + $user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $userId); + + $criteria = new Criteria( + Criteria::expr()->gte('user', $user) + ); + + $repository = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsAddress'); + $addresses = $repository->matching($criteria); + + $this->assertEquals(1, count($addresses)); + + $addresses = new ArrayCollection($repository->findAll()); + + $this->assertEquals(1, count($addresses->matching($criteria))); + } + public function testMatchingCriteriaContainsComparison() { $this->loadFixture(); diff --git a/tests/Doctrine/Tests/ORM/Functional/SingleTableInheritanceTest.php b/tests/Doctrine/Tests/ORM/Functional/SingleTableInheritanceTest.php index 4254ce3d2..1704c8b4e 100644 --- a/tests/Doctrine/Tests/ORM/Functional/SingleTableInheritanceTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/SingleTableInheritanceTest.php @@ -343,17 +343,32 @@ class SingleTableInheritanceTest extends \Doctrine\Tests\OrmFunctionalTestCase $repository = $this->_em->getRepository("Doctrine\Tests\Models\Company\CompanyContract"); $contracts = $repository->matching(new Criteria( - Criteria::expr()->eq('salesPerson', $this->salesPerson->getId()) + Criteria::expr()->eq('salesPerson', $this->salesPerson) )); $this->assertEquals(3, count($contracts)); $repository = $this->_em->getRepository("Doctrine\Tests\Models\Company\CompanyFixContract"); $contracts = $repository->matching(new Criteria( - Criteria::expr()->eq('salesPerson', $this->salesPerson->getId()) + Criteria::expr()->eq('salesPerson', $this->salesPerson) )); $this->assertEquals(1, count($contracts)); } + /** + * @group DDC-2430 + */ + public function testMatchingNonObjectOnAssocationThrowsException() + { + $this->loadFullFixture(); + + $repository = $this->_em->getRepository("Doctrine\Tests\Models\Company\CompanyContract"); + + $this->setExpectedException('Doctrine\ORM\Persisters\PersisterException', 'annot match on Doctrine\Tests\Models\Company\CompanyContract::salesPerson with a non-object value.'); + $contracts = $repository->matching(new Criteria( + Criteria::expr()->eq('salesPerson', $this->salesPerson->getId()) + )); + } + /** * @group DDC-834 */ From dc674f809f3548226f867fb2eaf7c53149a0905a Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Thu, 9 May 2013 13:40:14 +0200 Subject: [PATCH 118/332] Mention BC BREAK in PersistentCollection#matching() more prominently. --- UPGRADE.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/UPGRADE.md b/UPGRADE.md index 0aacaa603..b864d8614 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -1,5 +1,19 @@ # Upgrade to 2.4 +## BC BREAK: Compatibility Bugfix in PersistentCollection#matching() + +In Doctrine 2.3 it was possible to use the new ``matching($criteria)`` +functionality by adding constraints for assocations based on ID: + + Criteria::expr()->eq('association', $assocation->getId()); + +This functionality does not work on InMemory collections however, because +in memory criteria compares object values based on reference. +As of 2.4 the above code will throw an exception. You need to change +offending code to pass the ``$assocation`` reference directly: + + Criteria::expr()->eq('association', $assocation); + ## Composer is now the default autoloader The test suite now runs with composer autoloading. Support for PEAR, and tarball autoloading is deprecated. @@ -28,20 +42,6 @@ Now parenthesis are considered, the previous DQL will generate: SELECT 100 / (2 * 2) FROM my_entity -## Compatibility Bugfix in PersistentCollection#matching() breaks BC - -In Doctrine 2.3 it was possible to use the new ``matching($criteria)`` -functionality by adding constraints for assocations based on ID: - - Criteria::expr()->eq('association', $assocation->getId()); - -This functionality does not work on InMemory collections however, because -in memory criteria compares object values based on reference. -As of 2.4 the above code will throw an exception. You need to change -offending code to pass the ``$assocation`` reference directly: - - Criteria::expr()->eq('association', $assocation); - # Upgrade to 2.3 ## EntityManager#find() not calls EntityRepository#find() anymore From f8efd85ae6d3f455bc4b89aad4d37c0988030b06 Mon Sep 17 00:00:00 2001 From: HarmenM Date: Thu, 9 May 2013 14:49:38 +0300 Subject: [PATCH 119/332] Update annotations-reference.rst Added missing @JoinColumns in the index --- docs/en/reference/annotations-reference.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/reference/annotations-reference.rst b/docs/en/reference/annotations-reference.rst index f705cb034..d42fda4e4 100644 --- a/docs/en/reference/annotations-reference.rst +++ b/docs/en/reference/annotations-reference.rst @@ -21,6 +21,7 @@ Index - :ref:`@Id ` - :ref:`@InheritanceType ` - :ref:`@JoinColumn ` +- :ref:`@JoinColumns ` - :ref:`@JoinTable ` - :ref:`@ManyToOne ` - :ref:`@ManyToMany ` From 97622b57bd9fadea77b50e2f5ced717efbac1357 Mon Sep 17 00:00:00 2001 From: Jakub Zalas Date: Thu, 9 May 2013 15:19:58 +0100 Subject: [PATCH 120/332] Fixed a code block. Sphinx does not like the way code was indented. Building the documentation raises the following error: en/cookbook/resolve-target-entity-listener.rst:121: ERROR: Unexpected indentation. --- docs/en/cookbook/resolve-target-entity-listener.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/en/cookbook/resolve-target-entity-listener.rst b/docs/en/cookbook/resolve-target-entity-listener.rst index 71b746602..4a1b3b941 100644 --- a/docs/en/cookbook/resolve-target-entity-listener.rst +++ b/docs/en/cookbook/resolve-target-entity-listener.rst @@ -119,8 +119,7 @@ the targetEntity resolution will occur reliably: $evm = new \Doctrine\Common\EventManager; $rtel = new \Doctrine\ORM\Tools\ResolveTargetEntityListener; - $rtel->addResolveTargetEntity('Acme\\InvoiceModule\\Model\\InvoiceSubjectInterface', - 'Acme\\CustomerModule\\Entity\\Customer', array()); + $rtel->addResolveTargetEntity('Acme\\InvoiceModule\\Model\\InvoiceSubjectInterface', 'Acme\\CustomerModule\\Entity\\Customer', array()); // Add the ResolveTargetEntityListener $evm->addEventSubscriber($rtel); From 1a0adecf291d4cdd7198d464b6c93698e52b90d2 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Thu, 9 May 2013 16:24:19 +0200 Subject: [PATCH 121/332] [DDC-2335] Add note about filtering schema by regexp expression to relevant commands help output. --- .../ORM/Tools/Console/Command/ConvertMappingCommand.php | 6 ++++++ .../ORM/Tools/Console/Command/SchemaTool/CreateCommand.php | 6 ++++++ .../ORM/Tools/Console/Command/SchemaTool/DropCommand.php | 6 ++++++ .../ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php | 6 ++++++ 4 files changed, 24 insertions(+) diff --git a/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php index cec5d26a3..020a4afaf 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php @@ -98,6 +98,12 @@ semantical operations on associations such as cascade. Hint: There is no need to convert YAML or XML mapping files to annotations every time you make changes. All mapping drivers are first class citizens in Doctrine 2 and can be used as runtime mapping for the ORM. + +Hint: If you have a database with tables that should not be managed +by the ORM, you can use a DBAL functionality to filter the tables and sequences down +on a global level: + + \$config->setFilterSchemaAssetsExpression(\$regexp); EOT ); } diff --git a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/CreateCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/CreateCommand.php index 7aaef2d55..610f25c95 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/CreateCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/CreateCommand.php @@ -55,6 +55,12 @@ class CreateCommand extends AbstractCommand )) ->setHelp(<<Hint: If you have a database with tables that should not be managed +by the ORM, you can use a DBAL functionality to filter the tables and sequences down +on a global level: + + \$config->setFilterSchemaAssetsExpression(\$regexp); EOT ); } diff --git a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php index 596f45adf..ac003b991 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php @@ -64,6 +64,12 @@ class DropCommand extends AbstractCommand ->setHelp(<<Hint: If you have a database with tables that should not be managed +by the ORM, you can use a DBAL functionality to filter the tables and sequences down +on a global level: + + \$config->setFilterSchemaAssetsExpression(\$regexp); EOT ); } diff --git a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php index 9acf8e4e3..577fb9e0f 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php @@ -93,6 +93,12 @@ task will drop all database assets (e.g. tables, etc) that are *not* described by the current metadata. In other words, without this option, this task leaves untouched any "extra" tables that exist in the database, but which aren't described by any metadata. + +Hint: If you have a database with tables that should not be managed +by the ORM, you can use a DBAL functionality to filter the tables and sequences down +on a global level: + + \$config->setFilterSchemaAssetsExpression(\$regexp); EOT ); } From b53f4fd4cc82e4d25f8325a750e66c293fa000ef Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Thu, 9 May 2013 18:15:41 +0200 Subject: [PATCH 122/332] [DDC-2280] length attribute in was not converted. --- lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php b/lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php index ccbbfc3be..1a8fde184 100644 --- a/lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php +++ b/lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php @@ -155,6 +155,10 @@ class XmlExporter extends AbstractExporter $idXml->addAttribute('column', $field['columnName']); } + if (isset($field['length'])) { + $idXml->addAttribute('length', $field['length']); + } + if (isset($field['associationKey']) && $field['associationKey']) { $idXml->addAttribute('association-key', 'true'); } From 07c207081e16e07b7bd574528427212194d871a3 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Thu, 9 May 2013 21:11:10 +0200 Subject: [PATCH 123/332] Adding failing test to demonstrate DDC-2432 Loading proxies with invalid identifiers will currently mark them as initialized regardless of the failure --- .../Tests/ORM/Proxy/ProxyFactoryTest.php | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/tests/Doctrine/Tests/ORM/Proxy/ProxyFactoryTest.php b/tests/Doctrine/Tests/ORM/Proxy/ProxyFactoryTest.php index 90f1d7ef7..d91a3f2b5 100644 --- a/tests/Doctrine/Tests/ORM/Proxy/ProxyFactoryTest.php +++ b/tests/Doctrine/Tests/ORM/Proxy/ProxyFactoryTest.php @@ -2,6 +2,7 @@ namespace Doctrine\Tests\ORM\Proxy; +use Doctrine\ORM\EntityNotFoundException; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Proxy\ProxyFactory; use Doctrine\Common\Proxy\ProxyGenerator; @@ -80,6 +81,60 @@ class ProxyFactoryTest extends \Doctrine\Tests\OrmTestCase $this->assertEquals(0, $num, "No proxies generated."); } + + /** + * @group DDC-2432 + */ + public function testFailedProxyLoadingDoesNotMarkTheProxyAsInitialized() + { + $persister = $this->getMock('Doctrine\ORM\Persisters\BasicEntityPersister', array('load'), array(), '', false); + $this->uowMock->setEntityPersister('Doctrine\Tests\Models\ECommerce\ECommerceFeature', $persister); + + /* @var $proxy \Doctrine\Common\Proxy\Proxy */ + $proxy = $this->proxyFactory->getProxy('Doctrine\Tests\Models\ECommerce\ECommerceFeature', array('id' => 42)); + + $persister + ->expects($this->atLeastOnce()) + ->method('load') + ->will($this->returnValue(null)); + + try { + $proxy->getDescription(); + $this->fail('An exception was expected to be raised'); + } catch (EntityNotFoundException $exception) { + } + + $this->assertFalse($proxy->__isInitialized()); + $this->assertInstanceOf('Closure', $proxy->__getInitializer(), 'The initializer wasn\'t removed'); + $this->assertInstanceOf('Closure', $proxy->__getCloner(), 'The cloner wasn\'t removed'); + } + + /** + * @group DDC-2432 + */ + public function testFailedProxyCloningDoesNotMarkTheProxyAsInitialized() + { + $persister = $this->getMock('Doctrine\ORM\Persisters\BasicEntityPersister', array('load'), array(), '', false); + $this->uowMock->setEntityPersister('Doctrine\Tests\Models\ECommerce\ECommerceFeature', $persister); + + /* @var $proxy \Doctrine\Common\Proxy\Proxy */ + $proxy = $this->proxyFactory->getProxy('Doctrine\Tests\Models\ECommerce\ECommerceFeature', array('id' => 42)); + + $persister + ->expects($this->atLeastOnce()) + ->method('load') + ->will($this->returnValue(null)); + + try { + $cloned = clone $proxy; + $this->fail('An exception was expected to be raised'); + } catch (EntityNotFoundException $exception) { + } + + $this->assertFalse($proxy->__isInitialized()); + $this->assertInstanceOf('Closure', $proxy->__getInitializer(), 'The initializer wasn\'t removed'); + $this->assertInstanceOf('Closure', $proxy->__getCloner(), 'The cloner wasn\'t removed'); + } } abstract class AbstractClass From 22c9f6ebec3dbfe7395760da824ab50cce0f2a7d Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Thu, 9 May 2013 21:14:58 +0200 Subject: [PATCH 124/332] applying required fixes for DDC-2432 --- lib/Doctrine/ORM/Proxy/ProxyFactory.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lib/Doctrine/ORM/Proxy/ProxyFactory.php b/lib/Doctrine/ORM/Proxy/ProxyFactory.php index c8964804e..dfac9a3e7 100644 --- a/lib/Doctrine/ORM/Proxy/ProxyFactory.php +++ b/lib/Doctrine/ORM/Proxy/ProxyFactory.php @@ -117,6 +117,9 @@ class ProxyFactory extends AbstractProxyFactory { if ($classMetadata->getReflectionClass()->hasMethod('__wakeup')) { return function (BaseProxy $proxy) use ($entityPersister, $classMetadata) { + $initializer = $proxy->__getInitializer(); + $cloner = $proxy->__getCloner(); + $proxy->__setInitializer(null); $proxy->__setCloner(null); @@ -136,12 +139,19 @@ class ProxyFactory extends AbstractProxyFactory $proxy->__wakeup(); if (null === $entityPersister->load($classMetadata->getIdentifierValues($proxy), $proxy)) { + $proxy->__setInitializer($initializer); + $proxy->__setCloner($cloner); + $proxy->__setInitialized(false); + throw new EntityNotFoundException(); } }; } return function (BaseProxy $proxy) use ($entityPersister, $classMetadata) { + $initializer = $proxy->__getInitializer(); + $cloner = $proxy->__getCloner(); + $proxy->__setInitializer(null); $proxy->__setCloner(null); @@ -160,6 +170,10 @@ class ProxyFactory extends AbstractProxyFactory $proxy->__setInitialized(true); if (null === $entityPersister->load($classMetadata->getIdentifierValues($proxy), $proxy)) { + $proxy->__setInitializer($initializer); + $proxy->__setCloner($cloner); + $proxy->__setInitialized(false); + throw new EntityNotFoundException(); } }; From eb1a162cbc003186c346f7c0b8b58093380c2fe9 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Fri, 10 May 2013 23:31:14 +0200 Subject: [PATCH 125/332] Fix regression in DDC-2430. --- .../ORM/Persisters/SqlExpressionVisitor.php | 5 +++- .../ORM/Functional/EntityRepositoryTest.php | 25 ++++++++++++++++++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/lib/Doctrine/ORM/Persisters/SqlExpressionVisitor.php b/lib/Doctrine/ORM/Persisters/SqlExpressionVisitor.php index 74b86208b..b06358a20 100644 --- a/lib/Doctrine/ORM/Persisters/SqlExpressionVisitor.php +++ b/lib/Doctrine/ORM/Persisters/SqlExpressionVisitor.php @@ -65,7 +65,10 @@ class SqlExpressionVisitor extends ExpressionVisitor $field = $comparison->getField(); $value = $comparison->getValue()->getValue(); // shortcut for walkValue() - if (isset($this->classMetadata->associationMappings[$field]) && ! is_object($value)) { + if (isset($this->classMetadata->associationMappings[$field]) && + ! is_object($value) && + ! in_array($comparison->getOperator(), array(Comparison::IN, Comparison::NIN))) { + throw PersisterException::matchingAssocationFieldRequiresObject($this->classMetadata->name, $field); } diff --git a/tests/Doctrine/Tests/ORM/Functional/EntityRepositoryTest.php b/tests/Doctrine/Tests/ORM/Functional/EntityRepositoryTest.php index 48e5f3389..517ff3d97 100644 --- a/tests/Doctrine/Tests/ORM/Functional/EntityRepositoryTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/EntityRepositoryTest.php @@ -790,7 +790,30 @@ class EntityRepositoryTest extends \Doctrine\Tests\OrmFunctionalTestCase $user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $userId); $criteria = new Criteria( - Criteria::expr()->gte('user', $user) + Criteria::expr()->eq('user', $user) + ); + + $repository = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsAddress'); + $addresses = $repository->matching($criteria); + + $this->assertEquals(1, count($addresses)); + + $addresses = new ArrayCollection($repository->findAll()); + + $this->assertEquals(1, count($addresses->matching($criteria))); + } + + /** + * @group DDC-2430 + */ + public function testMatchingCriteriaAssocationInWithArray() + { + list($userId, $addressId) = $this->loadAssociatedFixture(); + + $user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $userId); + + $criteria = new Criteria( + Criteria::expr()->in('user', array($user)) ); $repository = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsAddress'); From b147c472be73a6f0c565080f74852a28d7318a4e Mon Sep 17 00:00:00 2001 From: "Fabio B. Silva" Date: Mon, 13 May 2013 11:04:36 -0300 Subject: [PATCH 126/332] [DDC-2429] Fix xsd definition --- doctrine-mapping.xsd | 51 ++++++++++++++----- .../ORM/Mapping/XmlMappingDriverTest.php | 21 +++++--- ...ctrine.Tests.Models.CMS.CmsAddress.dcm.xml | 19 ++++--- .../Doctrine.Tests.Models.CMS.CmsUser.dcm.xml | 32 ++++++------ ...Tests.Models.Company.CompanyPerson.dcm.xml | 12 ++--- ...ts.Models.DDC117.DDC117Translation.dcm.xml | 15 +++--- .../Doctrine.Tests.ORM.Mapping.Animal.dcm.xml | 2 +- ...ine.Tests.ORM.Mapping.DDC807Entity.dcm.xml | 8 +-- 8 files changed, 98 insertions(+), 62 deletions(-) diff --git a/doctrine-mapping.xsd b/doctrine-mapping.xsd index 89c11a894..f9c774d57 100644 --- a/doctrine-mapping.xsd +++ b/doctrine-mapping.xsd @@ -37,6 +37,7 @@ + @@ -86,8 +87,11 @@ + + + + - @@ -95,14 +99,14 @@ - + - + @@ -127,20 +131,24 @@ + - - - + + + + + + + - @@ -155,6 +163,7 @@ + @@ -221,6 +230,7 @@ + @@ -380,7 +390,7 @@ - + @@ -505,7 +515,7 @@ - + @@ -521,16 +531,33 @@ - + - - + + + + + + + + + + + + + + + + + + + diff --git a/tests/Doctrine/Tests/ORM/Mapping/XmlMappingDriverTest.php b/tests/Doctrine/Tests/ORM/Mapping/XmlMappingDriverTest.php index e69f23a35..42b871f1e 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/XmlMappingDriverTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/XmlMappingDriverTest.php @@ -66,23 +66,32 @@ class XmlMappingDriverTest extends AbstractMappingDriverTest /** * @param string $xmlMappingFile * @dataProvider dataValidSchema + * @group DDC-2429 */ public function testValidateXmlSchema($xmlMappingFile) { - $xsdSchemaFile = __DIR__ . "/../../../../../doctrine-mapping.xsd"; + $xsdSchemaFile = __DIR__ . '/../../../../../doctrine-mapping.xsd'; + $dom = new \DOMDocument('UTF-8'); - $dom = new \DOMDocument('UTF-8'); $dom->load($xmlMappingFile); + $this->assertTrue($dom->schemaValidate($xsdSchemaFile)); } static public function dataValidSchema() { - return array( - array(__DIR__ . "/xml/Doctrine.Tests.ORM.Mapping.CTI.dcm.xml"), - array(__DIR__ . "/xml/Doctrine.Tests.ORM.Mapping.User.dcm.xml"), - array(__DIR__ . "/xml/CatNoId.dcm.xml"), + $list = glob(__DIR__ . '/xml/*.xml'); + $invalid = array( + 'Doctrine.Tests.Models.DDC889.DDC889Class.dcm' ); + + $list = array_filter($list, function($item) use ($invalid){ + return ! in_array(pathinfo($item, PATHINFO_FILENAME), $invalid); + }); + + return array_map(function($item){ + return array($item); + }, $list); } /** diff --git a/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.CMS.CmsAddress.dcm.xml b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.CMS.CmsAddress.dcm.xml index b35569ae4..7e7bd5aaa 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.CMS.CmsAddress.dcm.xml +++ b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.CMS.CmsAddress.dcm.xml @@ -1,12 +1,10 @@ - - - - + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping + http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd"> + + @@ -15,7 +13,7 @@ SELECT id, country, city FROM cms_addresses - + SELECT * FROM cms_addresses WHERE id = ? @@ -46,7 +44,7 @@ - + @@ -54,6 +52,7 @@ - + + \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.CMS.CmsUser.dcm.xml b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.CMS.CmsUser.dcm.xml index 64a545df6..122a45e6f 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.CMS.CmsUser.dcm.xml +++ b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.CMS.CmsUser.dcm.xml @@ -15,7 +15,7 @@ SELECT id, username FROM cms_users WHERE username = ? - + SELECT * FROM cms_users WHERE username = ? @@ -90,20 +90,7 @@ - - - - - - - - - - - - - @@ -117,12 +104,25 @@ + + + + + + + + + + + + + - + @@ -133,7 +133,7 @@ - + diff --git a/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.Company.CompanyPerson.dcm.xml b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.Company.CompanyPerson.dcm.xml index c573504e0..2ddd88a32 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.Company.CompanyPerson.dcm.xml +++ b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.Company.CompanyPerson.dcm.xml @@ -7,6 +7,12 @@ + + + + + + SELECT id, name, discr FROM company_persons ORDER BY name @@ -26,12 +32,6 @@ - - - - - - diff --git a/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.DDC117.DDC117Translation.dcm.xml b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.DDC117.DDC117Translation.dcm.xml index c0df0886a..ec3bc74fd 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.DDC117.DDC117Translation.dcm.xml +++ b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.DDC117.DDC117Translation.dcm.xml @@ -5,18 +5,19 @@ http://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd"> - - - - - - - + + + + + + + + \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.Animal.dcm.xml b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.Animal.dcm.xml index 3f67a6b82..a4c4e9bfd 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.Animal.dcm.xml +++ b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.Animal.dcm.xml @@ -4,11 +4,11 @@ xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping http://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd"> + - diff --git a/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.DDC807Entity.dcm.xml b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.DDC807Entity.dcm.xml index 3dc9135c5..82711dc2f 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.DDC807Entity.dcm.xml +++ b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.DDC807Entity.dcm.xml @@ -5,16 +5,16 @@ http://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd"> - - - - + + + + \ No newline at end of file From bef5b585cb30568fc04080058d0e5a523c98ceff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Haso=C5=88?= Date: Fri, 17 May 2013 12:08:14 +0200 Subject: [PATCH 127/332] Fixed generating join column names for self referencing entity. --- .../ORM/Mapping/ClassMetadataInfo.php | 11 ++++--- .../Tests/ORM/Mapping/ClassMetadataTest.php | 32 ++++++++++++++++--- 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php index 1e038d659..e4a0955df 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php @@ -1547,15 +1547,18 @@ class ClassMetadataInfo implements ClassMetadata $mapping['joinTable']['name'] = $this->namingStrategy->joinTableName($mapping['sourceEntity'], $mapping['targetEntity'], $mapping['fieldName']); } + $selfReferencingEntityWithoutJoinColumns = $mapping['sourceEntity'] == $mapping['targetEntity'] + && (! (isset($mapping['joinTable']['joinColumns']) || isset($mapping['joinTable']['inverseJoinColumns']))); + if ( ! isset($mapping['joinTable']['joinColumns'])) { $mapping['joinTable']['joinColumns'] = array(array( - 'name' => $this->namingStrategy->joinKeyColumnName($mapping['sourceEntity']), + 'name' => $this->namingStrategy->joinKeyColumnName($mapping['sourceEntity'], $selfReferencingEntityWithoutJoinColumns ? 'source' : null), 'referencedColumnName' => $this->namingStrategy->referenceColumnName(), 'onDelete' => 'CASCADE')); } if ( ! isset($mapping['joinTable']['inverseJoinColumns'])) { $mapping['joinTable']['inverseJoinColumns'] = array(array( - 'name' => $this->namingStrategy->joinKeyColumnName($mapping['targetEntity']), + 'name' => $this->namingStrategy->joinKeyColumnName($mapping['targetEntity'], $selfReferencingEntityWithoutJoinColumns ? 'target' : null), 'referencedColumnName' => $this->namingStrategy->referenceColumnName(), 'onDelete' => 'CASCADE')); } @@ -2309,7 +2312,7 @@ class ClassMetadataInfo implements ClassMetadata } $entityResult['entityClass'] = $this->fullyQualifiedClassName($entityResult['entityClass']); - + $resultMapping['entities'][$key]['entityClass'] = ltrim($entityResult['entityClass'], '\\'); $resultMapping['entities'][$key]['isSelfClass'] = $entityResult['isSelfClass']; @@ -2429,7 +2432,7 @@ class ClassMetadataInfo implements ClassMetadata * lifecycle callbacks and lifecycle listeners. * * @deprecated Deprecated since version 2.4 in favor of \Doctrine\ORM\Event\ListenersInvoker - * + * * @param string $lifecycleEvent The lifecycle event. * @param object $entity The Entity on which the event occurred. * diff --git a/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php b/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php index b8e0becc4..8792545ad 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php @@ -340,7 +340,7 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase 'fieldName' => 'user', 'targetEntity' => 'CmsUser' )); - + $this->assertEquals(array('USER_ID'=>'ID'), $oneToOneMetadata->associationMappings['user']['sourceToTargetKeyColumns']); $this->assertEquals(array('USER_ID'=>'USER_ID'), $oneToOneMetadata->associationMappings['user']['joinColumnFieldNames']); $this->assertEquals(array('ID'=>'USER_ID'), $oneToOneMetadata->associationMappings['user']['targetToSourceKeyColumns']); @@ -348,7 +348,7 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase $this->assertEquals('USER_ID', $oneToOneMetadata->associationMappings['user']['joinColumns'][0]['name']); $this->assertEquals('ID', $oneToOneMetadata->associationMappings['user']['joinColumns'][0]['referencedColumnName']); - + $this->assertEquals('CMS_ADDRESS_CMS_USER', $manyToManyMetadata->associationMappings['user']['joinTable']['name']); $this->assertEquals(array('CMS_ADDRESS_ID','CMS_USER_ID'), $manyToManyMetadata->associationMappings['user']['joinTableColumns']); @@ -770,7 +770,7 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase { $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'); $cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService); - + $cm->addSqlResultSetMapping(array( 'name' => 'find-all', 'entities' => array( @@ -1000,7 +1000,7 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase /** * @group DDC-1955 - * + * * @expectedException Doctrine\ORM\Mapping\MappingException * @expectedExceptionMessage Entity Listener "\InvalidClassName" declared on "Doctrine\Tests\Models\CMS\CmsUser" not found. */ @@ -1025,6 +1025,30 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase $cm->addEntityListener(Events::postLoad, '\Doctrine\Tests\Models\Company\CompanyContractListener', 'invalidMethod'); } + + public function testManyToManySelfReferencingNamingStrategyDefaults() + { + $cm = new ClassMetadata('Doctrine\Tests\Models\CustomType\CustomTypeParent'); + $cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService); + $cm->mapManyToMany( + array( + 'fieldName' => 'friendsWithMe', + 'targetEntity' => 'CustomTypeParent' + ) + ); + + $this->assertEquals( + array( + 'name' => 'customtypeparent_customtypeparent', + 'joinColumns' => array(array('name' => 'customtypeparent_source', 'referencedColumnName' => 'id', 'onDelete' => 'CASCADE')), + 'inverseJoinColumns' => array(array('name' => 'customtypeparent_target', 'referencedColumnName' => 'id', 'onDelete' => 'CASCADE')), + ), + $cm->associationMappings['friendsWithMe']['joinTable'] + ); + $this->assertEquals(array('customtypeparent_source', 'customtypeparent_target'), $cm->associationMappings['friendsWithMe']['joinTableColumns']); + $this->assertEquals(array('customtypeparent_source' => 'id'), $cm->associationMappings['friendsWithMe']['relationToSourceKeyColumns']); + $this->assertEquals(array('customtypeparent_target' => 'id'), $cm->associationMappings['friendsWithMe']['relationToTargetKeyColumns']); + } } class MyNamespacedNamingStrategy extends \Doctrine\ORM\Mapping\DefaultNamingStrategy From 4d6cef1ff6fb6fe06dfbedbce45f2afc0eb14695 Mon Sep 17 00:00:00 2001 From: "Fabio B. Silva" Date: Fri, 17 May 2013 11:42:11 -0300 Subject: [PATCH 128/332] [DDC-2451] Fix entity listeners serialization --- lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php | 4 ++++ .../Tests/ORM/Mapping/ClassMetadataTest.php | 17 +++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php index 1e038d659..1f0d4653f 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php @@ -831,6 +831,10 @@ class ClassMetadataInfo implements ClassMetadata $serialized[] = 'lifecycleCallbacks'; } + if ($this->entityListeners) { + $serialized[] = 'entityListeners'; + } + if ($this->namedQueries) { $serialized[] = 'namedQueries'; } diff --git a/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php b/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php index b8e0becc4..4904b43f8 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php @@ -714,6 +714,23 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase $this->assertEquals(1, count($cm->getNamedNativeQueries())); } + /** + * @group DDC-2451 + */ + public function testSerializeEntityListeners() + { + $metadata = new ClassMetadata('Doctrine\Tests\Models\Company\CompanyContract'); + + $metadata->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService); + $metadata->addEntityListener(\Doctrine\ORM\Events::prePersist, 'CompanyContractListener', 'prePersistHandler'); + $metadata->addEntityListener(\Doctrine\ORM\Events::postPersist, 'CompanyContractListener', 'postPersistHandler'); + + $serialize = serialize($metadata); + $unserialize = unserialize($serialize); + + $this->assertEquals($unserialize->entityListeners, $metadata->entityListeners); + } + /** * @expectedException \Doctrine\ORM\Mapping\MappingException * @expectedExceptionMessage Query named "userById" in "Doctrine\Tests\Models\CMS\CmsUser" was already declared, but it must be declared only once From 1d7c72cc06a6fadc3c1d8e59c66ca4b9d679a7fe Mon Sep 17 00:00:00 2001 From: "Fabio B. Silva" Date: Fri, 17 May 2013 11:55:36 -0300 Subject: [PATCH 129/332] [DDC-2451] Fix test assert --- tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php b/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php index 4904b43f8..330b54a3f 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php @@ -728,7 +728,7 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase $serialize = serialize($metadata); $unserialize = unserialize($serialize); - $this->assertEquals($unserialize->entityListeners, $metadata->entityListeners); + $this->assertEquals($metadata->entityListeners, $unserialize->entityListeners); } /** From 1e95110b08d528551e482fc7171d9ce10b5d6156 Mon Sep 17 00:00:00 2001 From: "Fabio B. Silva" Date: Fri, 17 May 2013 11:59:28 -0300 Subject: [PATCH 130/332] Add missing mapping files --- .../Tests/ORM/Mapping/xml/DDC2429Book.orm.xml | 40 ++++++++++++++++++ .../ORM/Mapping/xml/DDC2429Novel.orm.xml | 42 +++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 tests/Doctrine/Tests/ORM/Mapping/xml/DDC2429Book.orm.xml create mode 100644 tests/Doctrine/Tests/ORM/Mapping/xml/DDC2429Novel.orm.xml diff --git a/tests/Doctrine/Tests/ORM/Mapping/xml/DDC2429Book.orm.xml b/tests/Doctrine/Tests/ORM/Mapping/xml/DDC2429Book.orm.xml new file mode 100644 index 000000000..8640c6f51 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Mapping/xml/DDC2429Book.orm.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/Doctrine/Tests/ORM/Mapping/xml/DDC2429Novel.orm.xml b/tests/Doctrine/Tests/ORM/Mapping/xml/DDC2429Novel.orm.xml new file mode 100644 index 000000000..43ce94285 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Mapping/xml/DDC2429Novel.orm.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From f92214997f0046232d8245b32d0a78f9400adb9d Mon Sep 17 00:00:00 2001 From: "Fabio B. Silva" Date: Fri, 17 May 2013 12:44:48 -0300 Subject: [PATCH 131/332] [DDC-2435] Fix column name with numbers and non alphanumeric characters. --- .../ORM/Mapping/DefaultQuoteStrategy.php | 9 ++++-- .../Tests/Models/Quote/NumericEntity.php | 31 +++++++++++++++++++ .../ORM/Query/SelectSqlGenerationTest.php | 21 +++++++++++++ 3 files changed, 58 insertions(+), 3 deletions(-) create mode 100644 tests/Doctrine/Tests/Models/Quote/NumericEntity.php diff --git a/lib/Doctrine/ORM/Mapping/DefaultQuoteStrategy.php b/lib/Doctrine/ORM/Mapping/DefaultQuoteStrategy.php index 3f5b0c853..70359de33 100644 --- a/lib/Doctrine/ORM/Mapping/DefaultQuoteStrategy.php +++ b/lib/Doctrine/ORM/Mapping/DefaultQuoteStrategy.php @@ -127,12 +127,15 @@ class DefaultQuoteStrategy implements QuoteStrategy */ public function getColumnAlias($columnName, $counter, AbstractPlatform $platform, ClassMetadata $class = null) { - // Trim the column alias to the maximum identifier length of the platform. - // If the alias is to long, characters are cut off from the beginning. - // And strip non alphanumeric characters + // 1 ) Concatenate column name and counter + // 2 ) Trim the column alias to the maximum identifier length of the platform. + // If the alias is to long, characters are cut off from the beginning. + // 3 ) Strip non alphanumeric characters + // 4 ) Prefix with "_" if the result its numeric $columnName = $columnName . $counter; $columnName = substr($columnName, -$platform->getMaxIdentifierLength()); $columnName = preg_replace('/[^A-Za-z0-9_]/', '', $columnName); + $columnName = is_numeric($columnName) ? '_' . $columnName : $columnName; return $platform->getSQLResultCasing($columnName); } diff --git a/tests/Doctrine/Tests/Models/Quote/NumericEntity.php b/tests/Doctrine/Tests/Models/Quote/NumericEntity.php new file mode 100644 index 000000000..f1186cbcc --- /dev/null +++ b/tests/Doctrine/Tests/Models/Quote/NumericEntity.php @@ -0,0 +1,31 @@ +value = $value; + } + +} \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php index 747df24bb..d15f78d48 100644 --- a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php +++ b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php @@ -1810,6 +1810,27 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase ); } + /** + * @group DDC-2435 + */ + public function testColumnNameWithNumbersAndNonAlphanumericCharacters() + { + $this->assertSqlGeneration( + 'SELECT e FROM Doctrine\Tests\Models\Quote\NumericEntity e', + 'SELECT t0_."1:1" AS _110, t0_."2:2" AS _221 FROM table t0_' + ); + + $this->assertSqlGeneration( + 'SELECT e.value FROM Doctrine\Tests\Models\Quote\NumericEntity e', + 'SELECT t0_."2:2" AS _220 FROM table t0_' + ); + + $this->assertSqlGeneration( + 'SELECT TRIM(e.value) FROM Doctrine\Tests\Models\Quote\NumericEntity e', + 'SELECT TRIM(t0_."2:2") AS sclr0 FROM table t0_' + ); + } + /** * @group DDC-1845 */ From 7430320bac802095b773ab596c7c4215fc81f4e0 Mon Sep 17 00:00:00 2001 From: Gusakov Nikita Date: Sat, 18 May 2013 01:40:05 +0400 Subject: [PATCH 132/332] ready --- tools/sandbox/bootstrap.php | 0 tools/sandbox/cli-config.php | 36 ------------------------------------ 2 files changed, 36 deletions(-) create mode 100644 tools/sandbox/bootstrap.php delete mode 100644 tools/sandbox/cli-config.php diff --git a/tools/sandbox/bootstrap.php b/tools/sandbox/bootstrap.php new file mode 100644 index 000000000..e69de29bb diff --git a/tools/sandbox/cli-config.php b/tools/sandbox/cli-config.php deleted file mode 100644 index 2a34fd89b..000000000 --- a/tools/sandbox/cli-config.php +++ /dev/null @@ -1,36 +0,0 @@ -register(); -$classLoader = new \Doctrine\Common\ClassLoader('Doctrine\DBAL', realpath(__DIR__ . '/../../lib/vendor/doctrine-dbal/lib')); -$classLoader->register(); -$classLoader = new \Doctrine\Common\ClassLoader('Doctrine\Common', realpath(__DIR__ . '/../../lib/vendor/doctrine-common/lib')); -$classLoader->register(); -$classLoader = new \Doctrine\Common\ClassLoader('Symfony', realpath(__DIR__ . '/../../lib/vendor')); -$classLoader->register(); -$classLoader = new \Doctrine\Common\ClassLoader('Entities', __DIR__); -$classLoader->register(); -$classLoader = new \Doctrine\Common\ClassLoader('Proxies', __DIR__); -$classLoader->register(); - -$config = new \Doctrine\ORM\Configuration(); -$config->setMetadataCacheImpl(new \Doctrine\Common\Cache\ArrayCache); -$driverImpl = $config->newDefaultAnnotationDriver(array(__DIR__."/Entities")); -$config->setMetadataDriverImpl($driverImpl); - -$config->setProxyDir(__DIR__ . '/Proxies'); -$config->setProxyNamespace('Proxies'); - -$connectionOptions = array( - 'driver' => 'pdo_sqlite', - 'path' => 'database.sqlite' -); - -$em = \Doctrine\ORM\EntityManager::create($connectionOptions, $config); - -$helpers = new Symfony\Component\Console\Helper\HelperSet(array( - 'db' => new \Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper($em->getConnection()), - 'em' => new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($em) -)); \ No newline at end of file From dca0881d943b22d0d42bed5cd451611e6e9a0343 Mon Sep 17 00:00:00 2001 From: Gusakov Nikita Date: Sat, 18 May 2013 01:42:55 +0400 Subject: [PATCH 133/332] forget add changes --- tools/sandbox/bootstrap.php | 35 +++++++++++++++++++++++++++ tools/sandbox/doctrine.php | 22 ++++------------- tools/sandbox/index.php | 48 ++++--------------------------------- 3 files changed, 44 insertions(+), 61 deletions(-) diff --git a/tools/sandbox/bootstrap.php b/tools/sandbox/bootstrap.php index e69de29bb..0e7120544 100644 --- a/tools/sandbox/bootstrap.php +++ b/tools/sandbox/bootstrap.php @@ -0,0 +1,35 @@ +add('Entities', __DIR__); +$loader->add('Proxies', __DIR__); + +$config = new \Doctrine\ORM\Configuration(); + +// Set up Metadata Drivers +$driverImpl = $config->newDefaultAnnotationDriver(array(__DIR__ . "/Entities")); +$config->setMetadataDriverImpl($driverImpl); + +// Set up caches +$cache = new ApcCache; +$config->setMetadataCacheImpl($cache); +$config->setQueryCacheImpl($cache); +$config->setMetadataCacheImpl(new \Doctrine\Common\Cache\ArrayCache); + +// Proxy configuration +$config->setProxyDir(__DIR__ . '/Proxies'); +$config->setProxyNamespace('Proxies'); + +// Database connection information +$connectionOptions = array( + 'driver' => 'pdo_sqlite', + 'path' => 'database.sqlite' +); + +return EntityManager::create($connectionOptions, $config); \ No newline at end of file diff --git a/tools/sandbox/doctrine.php b/tools/sandbox/doctrine.php index 9060e6f7b..66ed4f3c1 100644 --- a/tools/sandbox/doctrine.php +++ b/tools/sandbox/doctrine.php @@ -1,27 +1,15 @@ register(); -$classLoader = new \Doctrine\Common\ClassLoader('Doctrine\DBAL', realpath(__DIR__ . '/../../lib/vendor/doctrine-dbal/lib')); -$classLoader->register(); -$classLoader = new \Doctrine\Common\ClassLoader('Doctrine\Common', realpath(__DIR__ . '/../../lib/vendor/doctrine-common/lib')); -$classLoader->register(); -$classLoader = new \Doctrine\Common\ClassLoader('Symfony', realpath(__DIR__ . '/../../lib/vendor')); -$classLoader->register(); -$classLoader = new \Doctrine\Common\ClassLoader('Entities', __DIR__); -$classLoader->register(); -$classLoader = new \Doctrine\Common\ClassLoader('Proxies', __DIR__); -$classLoader->register(); - -require __DIR__ . '/cli-config.php'; +$em = require_once __DIR__.'/bootstrap.php'; $cli = new \Symfony\Component\Console\Application('Doctrine Command Line Interface', Doctrine\Common\Version::VERSION); $cli->setCatchExceptions(true); // Variable $helpers is defined inside cli-config.php -$cli->setHelperSet($helpers); +$cli->setHelperSet(new Symfony\Component\Console\Helper\HelperSet(array( + 'db' => new \Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper($em->getConnection()), + 'em' => new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($em) +))); $cli->addCommands(array( // DBAL Commands diff --git a/tools/sandbox/index.php b/tools/sandbox/index.php index ad1cb9d6b..f866aeada 100644 --- a/tools/sandbox/index.php +++ b/tools/sandbox/index.php @@ -1,7 +1,7 @@ register(); -$classLoader = new ClassLoader('Doctrine\DBAL', realpath(__DIR__ . '/../../lib/vendor/doctrine-dbal/lib')); -$classLoader->register(); -$classLoader = new ClassLoader('Doctrine\Common', realpath(__DIR__ . '/../../lib/vendor/doctrine-common/lib')); -$classLoader->register(); -$classLoader = new ClassLoader('Symfony', realpath(__DIR__ . '/../../lib/vendor')); -$classLoader->register(); -$classLoader = new ClassLoader('Entities', __DIR__); -$classLoader->register(); -$classLoader = new ClassLoader('Proxies', __DIR__); -$classLoader->register(); - -// Set up caches -$config = new Configuration; -$cache = new ApcCache; -$config->setMetadataCacheImpl($cache); -$driverImpl = $config->newDefaultAnnotationDriver(array(__DIR__."/Entities")); -$config->setMetadataDriverImpl($driverImpl); -$config->setQueryCacheImpl($cache); - -// Proxy configuration -$config->setProxyDir(__DIR__ . '/Proxies'); -$config->setProxyNamespace('Proxies'); -$config->setMetadataCacheImpl(new \Doctrine\Common\Cache\ArrayCache); - -// Database connection information -$connectionOptions = array( - 'driver' => 'pdo_sqlite', - 'path' => 'database.sqlite' -); - -// Create EntityManager -$em = EntityManager::create($connectionOptions, $config); +$em = require_once __DIR__ . '/bootstrap.php'; ## PUT YOUR TEST CODE BELOW From ec57306efe40770d8a77bb92117e21252fa401c7 Mon Sep 17 00:00:00 2001 From: Gusakov Nikita Date: Sat, 18 May 2013 01:44:47 +0400 Subject: [PATCH 134/332] remove comment about cli-config --- tools/sandbox/doctrine.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/sandbox/doctrine.php b/tools/sandbox/doctrine.php index 66ed4f3c1..6cadae0d4 100644 --- a/tools/sandbox/doctrine.php +++ b/tools/sandbox/doctrine.php @@ -5,7 +5,6 @@ $em = require_once __DIR__.'/bootstrap.php'; $cli = new \Symfony\Component\Console\Application('Doctrine Command Line Interface', Doctrine\Common\Version::VERSION); $cli->setCatchExceptions(true); -// Variable $helpers is defined inside cli-config.php $cli->setHelperSet(new Symfony\Component\Console\Helper\HelperSet(array( 'db' => new \Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper($em->getConnection()), 'em' => new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($em) From 4c1869dca0b1cba45e64103b9df7daa0165f6cf5 Mon Sep 17 00:00:00 2001 From: Gusakov Nikita Date: Sat, 18 May 2013 02:09:07 +0400 Subject: [PATCH 135/332] ready --- .../ORM/Tools/Console/Command/ConvertDoctrine1SchemaCommand.php | 2 +- .../ORM/Tools/Console/Command/ConvertMappingCommand.php | 2 +- .../ORM/Tools/Console/Command/GenerateEntitiesCommand.php | 2 +- .../ORM/Tools/Console/Command/GenerateProxiesCommand.php | 2 +- .../ORM/Tools/Console/Command/GenerateRepositoriesCommand.php | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/Doctrine/ORM/Tools/Console/Command/ConvertDoctrine1SchemaCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/ConvertDoctrine1SchemaCommand.php index 725d2bf83..0e6936ac2 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/ConvertDoctrine1SchemaCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/ConvertDoctrine1SchemaCommand.php @@ -101,7 +101,7 @@ class ConvertDoctrine1SchemaCommand extends Command protected function configure() { $this - ->setName('orm:convert-d1-schema') + ->setName('orm:convert:d1-schema') ->setDescription('Converts Doctrine 1.X schema into a Doctrine 2.X schema.') ->setDefinition(array( new InputArgument( diff --git a/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php index 020a4afaf..039843614 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php @@ -48,7 +48,7 @@ class ConvertMappingCommand extends Command protected function configure() { $this - ->setName('orm:convert-mapping') + ->setName('orm:convert:mapping') ->setDescription('Convert mapping information between supported formats.') ->setDefinition(array( new InputOption( diff --git a/lib/Doctrine/ORM/Tools/Console/Command/GenerateEntitiesCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/GenerateEntitiesCommand.php index 764e89500..7cdce30e0 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/GenerateEntitiesCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/GenerateEntitiesCommand.php @@ -46,7 +46,7 @@ class GenerateEntitiesCommand extends Command protected function configure() { $this - ->setName('orm:generate-entities') + ->setName('orm:generate:entities') ->setDescription('Generate entity classes and method stubs from your mapping information.') ->setDefinition(array( new InputOption( diff --git a/lib/Doctrine/ORM/Tools/Console/Command/GenerateProxiesCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/GenerateProxiesCommand.php index 1de9dd7dc..77e414d3d 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/GenerateProxiesCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/GenerateProxiesCommand.php @@ -44,7 +44,7 @@ class GenerateProxiesCommand extends Command protected function configure() { $this - ->setName('orm:generate-proxies') + ->setName('orm:generate:proxies') ->setDescription('Generates proxy classes for entity classes.') ->setDefinition(array( new InputOption( diff --git a/lib/Doctrine/ORM/Tools/Console/Command/GenerateRepositoriesCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/GenerateRepositoriesCommand.php index 7d1bd4c04..fdb88d2d1 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/GenerateRepositoriesCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/GenerateRepositoriesCommand.php @@ -45,7 +45,7 @@ class GenerateRepositoriesCommand extends Command protected function configure() { $this - ->setName('orm:generate-repositories') + ->setName('orm:generate:repositories') ->setDescription('Generate repository classes from your mapping information.') ->setDefinition(array( new InputOption( From 6bb3184dbfd3679c0aaf2323cb1ca711451c9321 Mon Sep 17 00:00:00 2001 From: Gusakov Nikita Date: Sat, 18 May 2013 02:40:18 +0400 Subject: [PATCH 136/332] fix bc --- .../Tools/Console/Command/ConvertDoctrine1SchemaCommand.php | 3 ++- .../ORM/Tools/Console/Command/ConvertMappingCommand.php | 3 ++- .../ORM/Tools/Console/Command/GenerateEntitiesCommand.php | 3 ++- .../ORM/Tools/Console/Command/GenerateProxiesCommand.php | 3 ++- .../ORM/Tools/Console/Command/GenerateRepositoriesCommand.php | 3 ++- 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/Doctrine/ORM/Tools/Console/Command/ConvertDoctrine1SchemaCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/ConvertDoctrine1SchemaCommand.php index 0e6936ac2..71d72deb8 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/ConvertDoctrine1SchemaCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/ConvertDoctrine1SchemaCommand.php @@ -101,7 +101,8 @@ class ConvertDoctrine1SchemaCommand extends Command protected function configure() { $this - ->setName('orm:convert:d1-schema') + ->setName('orm:convert-d1-schema') + ->setAliases(array('orm:convert:d1-schema')) ->setDescription('Converts Doctrine 1.X schema into a Doctrine 2.X schema.') ->setDefinition(array( new InputArgument( diff --git a/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php index 039843614..5300783af 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php @@ -48,7 +48,8 @@ class ConvertMappingCommand extends Command protected function configure() { $this - ->setName('orm:convert:mapping') + ->setName('orm:convert-mapping') + ->setAliases(array('orm:convert:mapping')) ->setDescription('Convert mapping information between supported formats.') ->setDefinition(array( new InputOption( diff --git a/lib/Doctrine/ORM/Tools/Console/Command/GenerateEntitiesCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/GenerateEntitiesCommand.php index 7cdce30e0..a8fcac3c8 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/GenerateEntitiesCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/GenerateEntitiesCommand.php @@ -46,7 +46,8 @@ class GenerateEntitiesCommand extends Command protected function configure() { $this - ->setName('orm:generate:entities') + ->setName('orm:generate-entities') + ->setAliases(array('orm:generate:entities')) ->setDescription('Generate entity classes and method stubs from your mapping information.') ->setDefinition(array( new InputOption( diff --git a/lib/Doctrine/ORM/Tools/Console/Command/GenerateProxiesCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/GenerateProxiesCommand.php index 77e414d3d..522118794 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/GenerateProxiesCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/GenerateProxiesCommand.php @@ -44,7 +44,8 @@ class GenerateProxiesCommand extends Command protected function configure() { $this - ->setName('orm:generate:proxies') + ->setName('orm:generate-proxies') + ->setAliases(array('orm:generate:proxies')) ->setDescription('Generates proxy classes for entity classes.') ->setDefinition(array( new InputOption( diff --git a/lib/Doctrine/ORM/Tools/Console/Command/GenerateRepositoriesCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/GenerateRepositoriesCommand.php index fdb88d2d1..975bc6938 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/GenerateRepositoriesCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/GenerateRepositoriesCommand.php @@ -45,7 +45,8 @@ class GenerateRepositoriesCommand extends Command protected function configure() { $this - ->setName('orm:generate:repositories') + ->setName('orm:generate-repositories') + ->setAliases(array('orm:generate:repositories')) ->setDescription('Generate repository classes from your mapping information.') ->setDefinition(array( new InputOption( From 384dfa87ab4dd1483c331cbad8d6096a066318b6 Mon Sep 17 00:00:00 2001 From: Gusakov Nikita Date: Sat, 18 May 2013 04:00:28 +0400 Subject: [PATCH 137/332] add docs --- docs/en/reference/tools.rst | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/en/reference/tools.rst b/docs/en/reference/tools.rst index b65863ed3..a5f99d141 100644 --- a/docs/en/reference/tools.rst +++ b/docs/en/reference/tools.rst @@ -131,6 +131,20 @@ The following Commands are currently available: update the database schema of EntityManager Storage Connection or generate the SQL output. +For these commands are also available aliases: + + +- ``orm:convert:d1-schema`` is alias for ``orm:convert-d1-schema``. +- ``orm:convert:mapping`` is alias for ``orm:convert-mapping``. +- ``orm:generate:entities`` is alias for ``orm:generate-entities``. +- ``orm:generate:proxies`` is alias for ``orm:generate-proxies``. +- ``orm:generate:repositories`` is alias for ``orm:generate-repositories``. + +.. note:: + + Console also supports auto completion, for example, instead of + ``orm:clear-cache:query`` you can use just ``o:c:q``. + Database Schema Generation -------------------------- From 1f4a65f3e05689b8725851d8bb88a0fc7f77b150 Mon Sep 17 00:00:00 2001 From: Gusakov Nikita Date: Mon, 20 May 2013 17:08:20 +0400 Subject: [PATCH 138/332] add debug variable and check for loader --- tools/sandbox/bootstrap.php | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/tools/sandbox/bootstrap.php b/tools/sandbox/bootstrap.php index 0e7120544..5b97b9412 100644 --- a/tools/sandbox/bootstrap.php +++ b/tools/sandbox/bootstrap.php @@ -1,26 +1,32 @@ add('Entities', __DIR__); $loader->add('Proxies', __DIR__); +$debug = true; $config = new \Doctrine\ORM\Configuration(); // Set up Metadata Drivers $driverImpl = $config->newDefaultAnnotationDriver(array(__DIR__ . "/Entities")); $config->setMetadataDriverImpl($driverImpl); -// Set up caches -$cache = new ApcCache; +// Set up caches, depending on $debug variable. +// You can use another variable to define which one of the cache systems you gonna use. +$cache = $debug ? new Cache\ArrayCache : new Cache\ApcCache; $config->setMetadataCacheImpl($cache); $config->setQueryCacheImpl($cache); -$config->setMetadataCacheImpl(new \Doctrine\Common\Cache\ArrayCache); // Proxy configuration $config->setProxyDir(__DIR__ . '/Proxies'); From 66a842c143d669707de5eb3149c2ac3143dd6977 Mon Sep 17 00:00:00 2001 From: Alexander Date: Sun, 26 May 2013 07:41:01 +0200 Subject: [PATCH 139/332] [DDC-2471] Fix EQ/NEQ null handling of criteria --- .../ORM/Persisters/BasicEntityPersister.php | 10 +++- .../ORM/Persisters/SqlValueVisitor.php | 9 ++- .../EntityRepositoryCriteriaTest.php | 56 +++++++++++++++++++ .../BasicEntityPersisterTypeValueSqlTest.php | 14 ++++- 4 files changed, 86 insertions(+), 3 deletions(-) diff --git a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php index 8f921f2b8..f101fda13 100644 --- a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php @@ -85,7 +85,7 @@ class BasicEntityPersister */ static private $comparisonMap = array( Comparison::EQ => '= %s', - Comparison::IS => 'IS %s', + Comparison::IS => '= %s', Comparison::NEQ => '!= %s', Comparison::GT => '> %s', Comparison::GTE => '>= %s', @@ -1608,6 +1608,14 @@ class BasicEntityPersister } if ($comparison !== null) { + + // special case null value handling + if (($comparison === Comparison::EQ || $comparison === Comparison::IS) && $value === null) { + return $condition . ' IS NULL'; + } else if ($comparison === Comparison::NEQ && $value === null) { + return $condition . ' IS NOT NULL'; + } + return $condition . ' ' . sprintf(self::$comparisonMap[$comparison], $placeholder); } diff --git a/lib/Doctrine/ORM/Persisters/SqlValueVisitor.php b/lib/Doctrine/ORM/Persisters/SqlValueVisitor.php index 79e5150e5..0e680ad07 100644 --- a/lib/Doctrine/ORM/Persisters/SqlValueVisitor.php +++ b/lib/Doctrine/ORM/Persisters/SqlValueVisitor.php @@ -52,7 +52,14 @@ class SqlValueVisitor extends ExpressionVisitor { $value = $this->getValueFromComparison($comparison); $field = $comparison->getField(); - + $operator = $comparison->getOperator(); + + if (($operator === Comparison::EQ || $operator === Comparison::IS) && $value === null) { + return; + } else if ($operator === Comparison::NEQ && $value === null) { + return; + } + $this->values[] = $value; $this->types[] = array($field, $value); } diff --git a/tests/Doctrine/Tests/ORM/Functional/EntityRepositoryCriteriaTest.php b/tests/Doctrine/Tests/ORM/Functional/EntityRepositoryCriteriaTest.php index 8631e529d..83c26b432 100644 --- a/tests/Doctrine/Tests/ORM/Functional/EntityRepositoryCriteriaTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/EntityRepositoryCriteriaTest.php @@ -84,4 +84,60 @@ class EntityRepositoryCriteriaTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertEquals(2, count($dates)); } + + private function loadNullFieldFixtures() + { + $today = new DateTimeModel(); + $today->datetime = + $today->date = + new \DateTime('today'); + + $this->_em->persist($today); + + $tomorrow = new DateTimeModel(); + $tomorrow->datetime = + $tomorrow->date = + $tomorrow->time = + new \DateTime('tomorrow'); + $this->_em->persist($tomorrow); + + $this->_em->flush(); + $this->_em->clear(); + } + + public function testIsNullComparison() + { + $this->loadNullFieldFixtures(); + $repository = $this->_em->getRepository('Doctrine\Tests\Models\Generic\DateTimeModel'); + + $dates = $repository->matching(new Criteria( + Criteria::expr()->isNull('time') + )); + + $this->assertEquals(1, count($dates)); + } + + public function testEqNullComparison() + { + $this->loadNullFieldFixtures(); + $repository = $this->_em->getRepository('Doctrine\Tests\Models\Generic\DateTimeModel'); + + $dates = $repository->matching(new Criteria( + Criteria::expr()->eq('time', null) + )); + + $this->assertEquals(1, count($dates)); + } + + public function testNotEqNullComparison() + { + $this->loadNullFieldFixtures(); + $repository = $this->_em->getRepository('Doctrine\Tests\Models\Generic\DateTimeModel'); + + $dates = $repository->matching(new Criteria( + Criteria::expr()->neq('time', null) + )); + + $this->assertEquals(1, count($dates)); + } } diff --git a/tests/Doctrine/Tests/ORM/Persisters/BasicEntityPersisterTypeValueSqlTest.php b/tests/Doctrine/Tests/ORM/Persisters/BasicEntityPersisterTypeValueSqlTest.php index 018044c03..668ff13a0 100644 --- a/tests/Doctrine/Tests/ORM/Persisters/BasicEntityPersisterTypeValueSqlTest.php +++ b/tests/Doctrine/Tests/ORM/Persisters/BasicEntityPersisterTypeValueSqlTest.php @@ -96,6 +96,18 @@ class BasicEntityPersisterTypeValueSqlTest extends \Doctrine\Tests\OrmTestCase public function testSelectConditionStatementIsNull() { $statement = $this->_persister->getSelectConditionStatementSQL('test', null, array(), Comparison::IS); - $this->assertEquals('test IS ?', $statement); + $this->assertEquals('test IS NULL', $statement); + } + + public function testSelectConditionStatementEqNull() + { + $statement = $this->_persister->getSelectConditionStatementSQL('test', null, array(), Comparison::EQ); + $this->assertEquals('test IS NULL', $statement); + } + + public function testSelectConditionStatementNeqNull() + { + $statement = $this->_persister->getSelectConditionStatementSQL('test', null, array(), Comparison::NEQ); + $this->assertEquals('test IS NOT NULL', $statement); } } From 0e29fe871a854279113f080845a009d724ef4787 Mon Sep 17 00:00:00 2001 From: John Bafford Date: Mon, 27 May 2013 11:20:26 -0400 Subject: [PATCH 140/332] Fix an instance of doubled words --- docs/en/reference/native-sql.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/reference/native-sql.rst b/docs/en/reference/native-sql.rst index cc59f1630..89eb2850b 100644 --- a/docs/en/reference/native-sql.rst +++ b/docs/en/reference/native-sql.rst @@ -277,7 +277,7 @@ detail: The first parameter is the alias of the entity result to which the meta column belongs. A meta result column (foreign key or -discriminator column) always belongs to to an entity result. The +discriminator column) always belongs to an entity result. The second parameter is the column alias/name of the column in the SQL result set and the third parameter is the column name used in the mapping. From 6bc18402e2f2acb7735b770cbd42e183a57b7400 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Mon, 27 May 2013 21:47:16 +0200 Subject: [PATCH 141/332] Release 2.4.0-RC1 --- composer.json | 2 +- lib/Doctrine/ORM/Version.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index f74058bac..88236f2fd 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "doctrine/orm", - "type": "library", + "type": "library","version":"2.4.0-RC1", "description": "Object-Relational-Mapper for PHP", "keywords": ["orm", "database"], "homepage": "http://www.doctrine-project.org", diff --git a/lib/Doctrine/ORM/Version.php b/lib/Doctrine/ORM/Version.php index 4dba7902f..f8347d998 100644 --- a/lib/Doctrine/ORM/Version.php +++ b/lib/Doctrine/ORM/Version.php @@ -36,7 +36,7 @@ class Version /** * Current Doctrine Version */ - const VERSION = '2.4.0-DEV'; + const VERSION = '2.4.0-RC1'; /** * Compares a Doctrine version with the current one. From f269ecc3ac1bcae20d1e0e5f3c8c0670b8669dd1 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Mon, 27 May 2013 21:47:16 +0200 Subject: [PATCH 142/332] Bump dev version to 2.4.0 --- composer.json | 2 +- lib/Doctrine/ORM/Version.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 88236f2fd..f74058bac 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "doctrine/orm", - "type": "library","version":"2.4.0-RC1", + "type": "library", "description": "Object-Relational-Mapper for PHP", "keywords": ["orm", "database"], "homepage": "http://www.doctrine-project.org", diff --git a/lib/Doctrine/ORM/Version.php b/lib/Doctrine/ORM/Version.php index f8347d998..4dba7902f 100644 --- a/lib/Doctrine/ORM/Version.php +++ b/lib/Doctrine/ORM/Version.php @@ -36,7 +36,7 @@ class Version /** * Current Doctrine Version */ - const VERSION = '2.4.0-RC1'; + const VERSION = '2.4.0-DEV'; /** * Compares a Doctrine version with the current one. From c64c149ebf862d9560cfff86f079a001c84c79f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonio=20J=2E=20Garc=C3=ADa=20Lagar?= Date: Tue, 28 May 2013 08:57:17 +0200 Subject: [PATCH 143/332] Fix getting started doc to work with current version --- docs/en/tutorials/getting-started.rst | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/en/tutorials/getting-started.rst b/docs/en/tutorials/getting-started.rst index 1ff524529..187029dfa 100644 --- a/docs/en/tutorials/getting-started.rst +++ b/docs/en/tutorials/getting-started.rst @@ -187,9 +187,7 @@ doctrine command. Its a fairly simple file: // cli-config.php require_once "bootstrap.php"; - $helperSet = new \Symfony\Component\Console\Helper\HelperSet(array( - 'em' => new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($entityManager) - )); + return \Doctrine\ORM\Tools\Console\ConsoleRunner::createHelperSet($entityManager); You can then change into your project directory and call the Doctrine command-line tool: From a986fe013e250b1b7af4040274bc36d2df58d3cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Mon, 3 Jun 2013 12:07:08 +0200 Subject: [PATCH 144/332] Explicitely state what the problem is People like me think the problem is that there is no association mapping, when the problem in fact could be that there also is a field mapping on the property. This message makes it clearer why we are getting an error message. --- lib/Doctrine/ORM/Tools/SchemaValidator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Tools/SchemaValidator.php b/lib/Doctrine/ORM/Tools/SchemaValidator.php index 328aeb231..958e411b6 100644 --- a/lib/Doctrine/ORM/Tools/SchemaValidator.php +++ b/lib/Doctrine/ORM/Tools/SchemaValidator.php @@ -115,7 +115,7 @@ class SchemaValidator if ($assoc['mappedBy']) { if ($targetMetadata->hasField($assoc['mappedBy'])) { $ce[] = "The association " . $class->name . "#" . $fieldName . " refers to the owning side ". - "field " . $assoc['targetEntity'] . "#" . $assoc['mappedBy'] . " which is not defined as association."; + "field " . $assoc['targetEntity'] . "#" . $assoc['mappedBy'] . " which is not defined as association, but as field."; } if (!$targetMetadata->hasAssociation($assoc['mappedBy'])) { $ce[] = "The association " . $class->name . "#" . $fieldName . " refers to the owning side ". From 27511374ec9cf04bf92995b7544255dd63856b62 Mon Sep 17 00:00:00 2001 From: "Fabio B. Silva" Date: Tue, 4 Jun 2013 23:50:43 -0400 Subject: [PATCH 145/332] Fix DDC-2475 --- lib/Doctrine/ORM/Query/SqlWalker.php | 106 +++++++++++------- .../ORM/Query/SelectSqlGenerationTest.php | 15 +++ 2 files changed, 83 insertions(+), 38 deletions(-) diff --git a/lib/Doctrine/ORM/Query/SqlWalker.php b/lib/Doctrine/ORM/Query/SqlWalker.php index 77b8ec1d4..b971bcfe5 100644 --- a/lib/Doctrine/ORM/Query/SqlWalker.php +++ b/lib/Doctrine/ORM/Query/SqlWalker.php @@ -24,6 +24,7 @@ use Doctrine\DBAL\Types\Type; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Query; use Doctrine\ORM\Query\QueryException; +use Doctrine\ORM\OptimisticLockException; use Doctrine\ORM\Mapping\ClassMetadataInfo; /** @@ -117,6 +118,13 @@ class SqlWalker implements TreeWalker */ private $scalarResultAliasMap = array(); + /** + * Map from Table-Alias + Column-Name to OrderBy-Direction. + * + * @var array + */ + private $orderedColumnsMap = array(); + /** * Map from DQL-Alias + Field-Name to SQL Column Alias. * @@ -388,25 +396,36 @@ class SqlWalker implements TreeWalker */ private function _generateOrderedCollectionOrderByItems() { - $sqlParts = array(); + $orderedColumns = array(); foreach ($this->selectedClasses as $selectedClass) { - $dqlAlias = $selectedClass['dqlAlias']; - $qComp = $this->queryComponents[$dqlAlias]; + $dqlAlias = $selectedClass['dqlAlias']; + $qComp = $this->queryComponents[$dqlAlias]; + $persister = $this->em->getUnitOfWork()->getEntityPersister($qComp['metadata']->name); - if ( ! isset($qComp['relation']['orderBy'])) continue; + if ( ! isset($qComp['relation']['orderBy'])) { + continue; + } foreach ($qComp['relation']['orderBy'] as $fieldName => $orientation) { $columnName = $this->quoteStrategy->getColumnName($fieldName, $qComp['metadata'], $this->platform); $tableName = ($qComp['metadata']->isInheritanceTypeJoined()) - ? $this->em->getUnitOfWork()->getEntityPersister($qComp['metadata']->name)->getOwningTable($fieldName) + ? $persister->getOwningTable($fieldName) : $qComp['metadata']->getTableName(); - $sqlParts[] = $this->getSQLTableAlias($tableName, $dqlAlias) . '.' . $columnName . ' ' . $orientation; + $orderedColumn = $this->getSQLTableAlias($tableName, $dqlAlias) . '.' . $columnName; + + // OrderByClause should replace an ordered relation. see - DDC-2475 + if (isset($this->orderedColumnsMap[$orderedColumn])) { + continue; + } + + $this->orderedColumnsMap[$orderedColumn] = $orientation; + $orderedColumns[] = $orderedColumn . ' ' . $orientation; } } - return implode(', ', $sqlParts); + return implode(', ', $orderedColumns); } /** @@ -495,44 +514,52 @@ class SqlWalker implements TreeWalker */ public function walkSelectStatement(AST\SelectStatement $AST) { - $sql = $this->walkSelectClause($AST->selectClause); - $sql .= $this->walkFromClause($AST->fromClause); - $sql .= $this->walkWhereClause($AST->whereClause); - $sql .= $AST->groupByClause ? $this->walkGroupByClause($AST->groupByClause) : ''; - $sql .= $AST->havingClause ? $this->walkHavingClause($AST->havingClause) : ''; + $limit = $this->query->getMaxResults(); + $offset = $this->query->getFirstResult(); + $lockMode = $this->query->getHint(Query::HINT_LOCK_MODE); + $sql = $this->walkSelectClause($AST->selectClause) + . $this->walkFromClause($AST->fromClause) + . $this->walkWhereClause($AST->whereClause); - if (($orderByClause = $AST->orderByClause) !== null) { - $sql .= $AST->orderByClause ? $this->walkOrderByClause($AST->orderByClause) : ''; - } else if (($orderBySql = $this->_generateOrderedCollectionOrderByItems()) !== '') { + if ($AST->groupByClause) { + $sql .= $this->walkGroupByClause($AST->groupByClause); + } + + if ($AST->havingClause) { + $sql .= $this->walkHavingClause($AST->havingClause); + } + + if ($AST->orderByClause) { + $sql .= $this->walkOrderByClause($AST->orderByClause); + } + + if ( ! $AST->orderByClause && ($orderBySql = $this->_generateOrderedCollectionOrderByItems())) { $sql .= ' ORDER BY ' . $orderBySql; } - $sql = $this->platform->modifyLimitQuery( - $sql, $this->query->getMaxResults(), $this->query->getFirstResult() - ); + if ($limit !== null || $offset !== null) { + $sql = $this->platform->modifyLimitQuery($sql, $limit, $offset); + } - if (($lockMode = $this->query->getHint(Query::HINT_LOCK_MODE)) !== false) { - switch ($lockMode) { - case LockMode::PESSIMISTIC_READ: - $sql .= ' ' . $this->platform->getReadLockSQL(); - break; + if ($lockMode === false || $lockMode === LockMode::NONE) { + return $sql; + } - case LockMode::PESSIMISTIC_WRITE: - $sql .= ' ' . $this->platform->getWriteLockSQL(); - break; + if ($lockMode === LockMode::PESSIMISTIC_READ) { + return $sql . ' ' . $this->platform->getReadLockSQL(); + } - case LockMode::OPTIMISTIC: - foreach ($this->selectedClasses as $selectedClass) { - if ( ! $selectedClass['class']->isVersioned) { - throw \Doctrine\ORM\OptimisticLockException::lockFailed($selectedClass['class']->name); - } - } - break; - case LockMode::NONE: - break; + if ($lockMode === LockMode::PESSIMISTIC_WRITE) { + return $sql . ' ' . $this->platform->getWriteLockSQL(); + } - default: - throw \Doctrine\ORM\Query\QueryException::invalidLockMode(); + if ($lockMode !== LockMode::OPTIMISTIC) { + throw QueryException::invalidLockMode(); + } + + foreach ($this->selectedClasses as $selectedClass) { + if ( ! $selectedClass['class']->isVersioned) { + throw OptimisticLockException::lockFailed($selectedClass['class']->name); } } @@ -998,12 +1025,15 @@ class SqlWalker implements TreeWalker */ public function walkOrderByItem($orderByItem) { + $type = strtoupper($orderByItem->type); $expr = $orderByItem->expression; $sql = ($expr instanceof AST\Node) ? $expr->dispatch($this) : $this->walkResultVariable($this->queryComponents[$expr]['token']['value']); - return $sql . ' ' . strtoupper($orderByItem->type); + $this->orderedColumnsMap[$sql] = $type; + + return $sql . ' ' . $type; } /** diff --git a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php index d15f78d48..26a6a1ec1 100644 --- a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php +++ b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php @@ -1971,6 +1971,21 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase ); } + /** + * @group DDC-2475 + */ + public function testOrderByClauseShouldReplaceOrderByRelationMapping() + { + $this->assertSqlGeneration( + 'SELECT r, b FROM Doctrine\Tests\Models\Routing\RoutingRoute r JOIN r.bookings b', + 'SELECT r0_.id AS id0, r1_.id AS id1, r1_.passengerName AS passengerName2 FROM RoutingRoute r0_ INNER JOIN RoutingRouteBooking r1_ ON r0_.id = r1_.route_id ORDER BY r1_.passengerName ASC' + ); + + $this->assertSqlGeneration( + 'SELECT r, b FROM Doctrine\Tests\Models\Routing\RoutingRoute r JOIN r.bookings b ORDER BY b.passengerName DESC', + 'SELECT r0_.id AS id0, r1_.id AS id1, r1_.passengerName AS passengerName2 FROM RoutingRoute r0_ INNER JOIN RoutingRouteBooking r1_ ON r0_.id = r1_.route_id ORDER BY r1_.passengerName DESC' + ); + } } class MyAbsFunction extends \Doctrine\ORM\Query\AST\Functions\FunctionNode From 424793c2635b91932a7cad11f118c6bb13536066 Mon Sep 17 00:00:00 2001 From: Matthieu Napoli Date: Thu, 6 Jun 2013 11:36:02 +0300 Subject: [PATCH 146/332] Fixed rendering Fixed some broken rendering on http://docs.doctrine-project.org/en/latest/reference/yaml-mapping.html --- docs/en/reference/yaml-mapping.rst | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/docs/en/reference/yaml-mapping.rst b/docs/en/reference/yaml-mapping.rst index 9a00d08b3..bc6c64c8f 100644 --- a/docs/en/reference/yaml-mapping.rst +++ b/docs/en/reference/yaml-mapping.rst @@ -19,8 +19,6 @@ In order to work, this requires certain conventions: convention and you are not forced to do this. You can change the file extension easily enough. -- - .. code-block:: php Date: Thu, 6 Jun 2013 15:08:22 -0700 Subject: [PATCH 147/332] Implement QuoteStrategy on SqlWalker walkRangeVariableDeclaration Based on: http://www.doctrine-project.org/jira/browse/DDC-1845 https://github.com/doctrine/doctrine2/commit/cb72219b118c158c9b5344c4b81ff2b1a9149ab0 --- lib/Doctrine/ORM/Query/SqlWalker.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Doctrine/ORM/Query/SqlWalker.php b/lib/Doctrine/ORM/Query/SqlWalker.php index b971bcfe5..9a9ecf58c 100644 --- a/lib/Doctrine/ORM/Query/SqlWalker.php +++ b/lib/Doctrine/ORM/Query/SqlWalker.php @@ -844,8 +844,8 @@ class SqlWalker implements TreeWalker $this->rootAliases[] = $dqlAlias; - $sql = $class->getQuotedTableName($this->platform) . ' ' - . $this->getSQLTableAlias($class->getTableName(), $dqlAlias); + $sql = $this->quoteStrategy->getTableName($class,$this->platform) . ' ' + . $this->getSQLTableAlias($class->getTableName(), $dqlAlias); if ($class->isInheritanceTypeJoined()) { $sql .= $this->_generateClassTableInheritanceJoins($class, $dqlAlias); From 9bea612d7405c90ac979f41171b0257d2c49d31b Mon Sep 17 00:00:00 2001 From: John Brown Date: Thu, 6 Jun 2013 15:51:23 -0700 Subject: [PATCH 148/332] Adding simple test to ensure quoting of table names still functions. Note: the funtionality of this change won't be noticiable unless a custom quote strategy is implemented --- .../Tests/ORM/Query/SelectSqlGenerationTest.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php index 26a6a1ec1..1b2c58b8c 100644 --- a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php +++ b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php @@ -1831,6 +1831,17 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase ); } + /** + * @group DDC-1845 + */ + public function testQuotedTableDeclaration() + { + $this->assertSqlGeneration( + 'SELECT u FROM Doctrine\Tests\Models\Quote\User u', + 'SELECT q0_."user-id" AS userid0, q0_."user-name" AS username1 FROM "quote-user" q0_' + ); + } + /** * @group DDC-1845 */ From afb9c829e2fb2395924d1064a47a2ac2f64883e0 Mon Sep 17 00:00:00 2001 From: John Brown Date: Fri, 7 Jun 2013 08:49:49 -0700 Subject: [PATCH 149/332] updating sql walker to use quote strategy in joins --- lib/Doctrine/ORM/Query/SqlWalker.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Query/SqlWalker.php b/lib/Doctrine/ORM/Query/SqlWalker.php index 9a9ecf58c..820438a59 100644 --- a/lib/Doctrine/ORM/Query/SqlWalker.php +++ b/lib/Doctrine/ORM/Query/SqlWalker.php @@ -875,7 +875,7 @@ class SqlWalker implements TreeWalker $relation = $this->queryComponents[$joinedDqlAlias]['relation']; $targetClass = $this->em->getClassMetadata($relation['targetEntity']); $sourceClass = $this->em->getClassMetadata($relation['sourceEntity']); - $targetTableName = $targetClass->getQuotedTableName($this->platform); + $targetTableName = $this->quoteStrategy->getTableName($targetClass,$this->platform); $targetTableAlias = $this->getSQLTableAlias($targetClass->getTableName(), $joinedDqlAlias); $sourceTableAlias = $this->getSQLTableAlias($sourceClass->getTableName(), $associationPathExpression->identificationVariable); From 4ef043fc3b05f8f610a211d597e802c9c8131c16 Mon Sep 17 00:00:00 2001 From: John Brown Date: Fri, 7 Jun 2013 08:56:58 -0700 Subject: [PATCH 150/332] updating sql walker to use quote strategy in joins --- lib/Doctrine/ORM/Query/SqlWalker.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Query/SqlWalker.php b/lib/Doctrine/ORM/Query/SqlWalker.php index 820438a59..abbb97374 100644 --- a/lib/Doctrine/ORM/Query/SqlWalker.php +++ b/lib/Doctrine/ORM/Query/SqlWalker.php @@ -930,7 +930,7 @@ class SqlWalker implements TreeWalker // Join relation table $joinTable = $assoc['joinTable']; $joinTableAlias = $this->getSQLTableAlias($joinTable['name'], $joinedDqlAlias); - $joinTableName = $sourceClass->getQuotedJoinTableName($assoc, $this->platform); + $joinTableName = $this->quoteStrategy->getJoinTableName($assoc, $sourceClass, $this->platform); $conditions = array(); $relationColumns = ($relation['isOwningSide']) From 710d0d1109720810cb3d4a226cbb2d9db8ba668f Mon Sep 17 00:00:00 2001 From: "Fabio B. Silva" Date: Fri, 7 Jun 2013 17:01:52 -0400 Subject: [PATCH 151/332] Fix DDC-1995 --- .../ORM/Mapping/ClassMetadataInfo.php | 14 ++++ lib/Doctrine/ORM/Query.php | 21 +++-- lib/Doctrine/ORM/Query/ResultSetMapping.php | 18 +++++ lib/Doctrine/ORM/Query/SqlWalker.php | 35 ++++---- .../ORM/Functional/Ticket/DDC1995Test.php | 81 +++++++++++++++++++ 5 files changed, 148 insertions(+), 21 deletions(-) create mode 100644 tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1995Test.php diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php index cda11d44b..da2b1caa6 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php @@ -3026,4 +3026,18 @@ class ClassMetadataInfo implements ClassMetadata return $className; } + + /** + * @param string $name + * + * @return mixed + */ + public function getMetadataValue($name) { + + if (isset($this->$name)) { + return $this->$name; + } + + return null; + } } diff --git a/lib/Doctrine/ORM/Query.php b/lib/Doctrine/ORM/Query.php index 107055ed3..71f5f5550 100644 --- a/lib/Doctrine/ORM/Query.php +++ b/lib/Doctrine/ORM/Query.php @@ -26,6 +26,8 @@ use Doctrine\DBAL\LockMode; use Doctrine\ORM\Query\Parser; use Doctrine\ORM\Query\ParserResult; use Doctrine\ORM\Query\QueryException; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Query\ParameterTypeInferer; /** * A Query object represents a DQL query. @@ -268,6 +270,10 @@ final class Query extends AbstractQuery $executor->setQueryCacheProfile($this->_queryCacheProfile); } + if ($this->_resultSetMapping === null) { + $this->_resultSetMapping = $this->_parserResult->getResultSetMapping(); + } + // Prepare parameters $paramMappings = $this->_parserResult->getParameterMappings(); @@ -277,10 +283,6 @@ final class Query extends AbstractQuery list($sqlParams, $types) = $this->processParameterMappings($paramMappings); - if ($this->_resultSetMapping === null) { - $this->_resultSetMapping = $this->_parserResult->getResultSetMapping(); - } - return $executor->execute($this->_em->getConnection(), $sqlParams, $types); } @@ -299,16 +301,21 @@ final class Query extends AbstractQuery $types = array(); foreach ($this->parameters as $parameter) { - $key = $parameter->getName(); + $key = $parameter->getName(); + $value = $parameter->getValue(); if ( ! isset($paramMappings[$key])) { throw QueryException::unknownParameter($key); } - $value = $this->processParameterValue($parameter->getValue()); + if (isset($this->_resultSetMapping->metadataParameterMapping[$key]) && $value instanceof ClassMetadata) { + $value = $value->getMetadataValue($this->_resultSetMapping->metadataParameterMapping[$key]); + } + + $value = $this->processParameterValue($value); $type = ($parameter->getValue() === $value) ? $parameter->getType() - : Query\ParameterTypeInferer::inferType($value); + : ParameterTypeInferer::inferType($value); foreach ($paramMappings[$key] as $position) { $types[$position] = $type; diff --git a/lib/Doctrine/ORM/Query/ResultSetMapping.php b/lib/Doctrine/ORM/Query/ResultSetMapping.php index b06b68385..2f1727383 100644 --- a/lib/Doctrine/ORM/Query/ResultSetMapping.php +++ b/lib/Doctrine/ORM/Query/ResultSetMapping.php @@ -153,6 +153,13 @@ class ResultSetMapping */ public $newObjectMappings = array(); + /** + * Maps metadata parameter names to the metadata attribute. + * + * @var array + */ + public $metadataParameterMapping = array(); + /** * Adds an entity result to this ResultSetMapping. * @@ -371,6 +378,17 @@ class ResultSetMapping return $this; } + /** + * Adds a metadata parameter mappings. + * + * @param mixed $parameter The parameter name in the SQL result set. + * @param string $attribute The metadata attribute. + */ + public function addMetadataParameterMapping($parameter, $attribute) + { + $this->metadataParameterMapping[$parameter] = $attribute; + } + /** * Checks whether a column with a given name is mapped as a scalar result. * diff --git a/lib/Doctrine/ORM/Query/SqlWalker.php b/lib/Doctrine/ORM/Query/SqlWalker.php index b971bcfe5..c048ba2c9 100644 --- a/lib/Doctrine/ORM/Query/SqlWalker.php +++ b/lib/Doctrine/ORM/Query/SqlWalker.php @@ -1972,24 +1972,31 @@ class SqlWalker implements TreeWalker $sqlParameterList = array(); foreach ($instanceOfExpr->value as $parameter) { + if ($parameter instanceof AST\InputParameter) { + + $this->rsm->addMetadataParameterMapping($parameter->name, 'discriminatorValue'); + $sqlParameterList[] = $this->walkInputParameter($parameter); - } else { - // Get name from ClassMetadata to resolve aliases. - $entityClassName = $this->em->getClassMetadata($parameter)->name; - if ($entityClassName == $class->name) { - $sqlParameterList[] = $this->conn->quote($class->discriminatorValue); - } else { - $discrMap = array_flip($class->discriminatorMap); - - if (!isset($discrMap[$entityClassName])) { - throw QueryException::instanceOfUnrelatedClass($entityClassName, $class->rootEntityName); - } - - $sqlParameterList[] = $this->conn->quote($discrMap[$entityClassName]); - } + continue; } + + // Get name from ClassMetadata to resolve aliases. + $entityClassName = $this->em->getClassMetadata($parameter)->name; + $discriminatorValue = $class->discriminatorValue; + + if ($entityClassName !== $class->name) { + $discrMap = array_flip($class->discriminatorMap); + + if ( ! isset($discrMap[$entityClassName])) { + throw QueryException::instanceOfUnrelatedClass($entityClassName, $class->rootEntityName); + } + + $discriminatorValue = $discrMap[$entityClassName]; + } + + $sqlParameterList[] = $this->conn->quote($discriminatorValue); } $sql .= '(' . implode(', ', $sqlParameterList) . ')'; diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1995Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1995Test.php new file mode 100644 index 000000000..0bfd73960 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1995Test.php @@ -0,0 +1,81 @@ +useModelSet('company'); + parent::setUp(); + } + + public function testIssue() + { + $person = new CompanyPerson; + $person->setName('p1'); + + $employee = new CompanyEmployee; + $employee->setName('Foo'); + $employee->setDepartment('bar'); + $employee->setSalary(1000); + + $this->_em->persist($person); + $this->_em->persist($employee); + $this->_em->flush(); + $this->_em->clear(); + + $dql = 'SELECT u FROM Doctrine\Tests\Models\Company\CompanyPerson u WHERE u INSTANCE OF ?1'; + $class = $this->_em->getClassMetadata('Doctrine\Tests\Models\Company\CompanyEmployee'); + + $result = $this->_em->createQuery($dql) + ->setParameter(1, $class) + ->getResult(); + + $this->assertCount(1, $result); + $this->assertInstanceOf('Doctrine\Tests\Models\Company\CompanyEmployee', $result[0]); + } + + public function testQueryCache() + { + $person = new CompanyPerson; + $person->setName('p1'); + + $employee = new CompanyEmployee; + $employee->setName('Foo'); + $employee->setDepartment('bar'); + $employee->setSalary(1000); + + $this->_em->persist($person); + $this->_em->persist($employee); + $this->_em->flush(); + $this->_em->clear(); + + $dql = 'SELECT u FROM Doctrine\Tests\Models\Company\CompanyPerson u WHERE u INSTANCE OF :type'; + $class1 = $this->_em->getClassMetadata('Doctrine\Tests\Models\Company\CompanyEmployee'); + $class2 = $this->_em->getClassMetadata('Doctrine\Tests\Models\Company\CompanyPerson'); + + $result1 = $this->_em->createQuery($dql) + ->setParameter('type', $class1) + ->useQueryCache(true) + ->getResult(); + + $result2 = $this->_em->createQuery($dql) + ->setParameter('type', $class2) + ->useQueryCache(true) + ->getResult(); + + $this->assertCount(1, $result1); + $this->assertCount(1, $result2); + + $this->assertInstanceOf('Doctrine\Tests\Models\Company\CompanyEmployee', $result1[0]); + $this->assertInstanceOf('Doctrine\Tests\Models\Company\CompanyPerson', $result2[0]); + $this->assertNotInstanceOf('Doctrine\Tests\Models\Company\CompanyEmployee', $result2[0]); + } +} \ No newline at end of file From 4e99c5c127c810bf63c9a371f87f0ff5c82e3e79 Mon Sep 17 00:00:00 2001 From: Guilherme Blanco Date: Tue, 11 Jun 2013 01:21:47 -0400 Subject: [PATCH 152/332] DDC-1858 Added coverage to ticket. Functionality already implemented. --- .../ORM/Query/LanguageRecognitionTest.php | 8 +++++ .../ORM/Query/SelectSqlGenerationTest.php | 30 ++++++++++++++++--- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php b/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php index f71c5c7f1..ca9085457 100644 --- a/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php +++ b/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php @@ -590,6 +590,14 @@ class LanguageRecognitionTest extends \Doctrine\Tests\OrmTestCase { $this->assertValidDQL("select COALESCE(NULLIF(u.name, ''), u.username) as Display FROM Doctrine\Tests\Models\CMS\CmsUser u"); } + + /** + * @gorup DDC-1858 + */ + public function testHavingSupportIsNullExpression() + { + $this->assertValidDQL("SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u HAVING u.username IS NULL"); + } } /** @Entity */ diff --git a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php index 26a6a1ec1..e311214b1 100644 --- a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php +++ b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php @@ -1916,7 +1916,7 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase { $connMock = $this->_em->getConnection(); $orgPlatform = $connMock->getDatabasePlatform(); - + $connMock->setDatabasePlatform(new \Doctrine\DBAL\Platforms\MySqlPlatform); $this->assertSqlGeneration( "SELECT u.id FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE CONCAT(u.name, u.status, 's') = ?1", @@ -1926,7 +1926,7 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase "SELECT CONCAT(u.id, u.name, u.status) FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = ?1", "SELECT CONCAT(c0_.id, c0_.name, c0_.status) AS sclr0 FROM cms_users c0_ WHERE c0_.id = ?" ); - + $connMock->setDatabasePlatform(new \Doctrine\DBAL\Platforms\PostgreSqlPlatform); $this->assertSqlGeneration( "SELECT u.id FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE CONCAT(u.name, u.status, 's') = ?1", @@ -1936,7 +1936,7 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase "SELECT CONCAT(u.id, u.name, u.status) FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = ?1", "SELECT c0_.id || c0_.name || c0_.status AS sclr0 FROM cms_users c0_ WHERE c0_.id = ?" ); - + $connMock->setDatabasePlatform(new \Doctrine\DBAL\Platforms\SQLServerPlatform()); $this->assertSqlGeneration( "SELECT u.id FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE CONCAT(u.name, u.status, 's') = ?1", @@ -1946,7 +1946,7 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase "SELECT CONCAT(u.id, u.name, u.status) FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = ?1", "SELECT (c0_.id + c0_.name + c0_.status) AS sclr0 FROM cms_users c0_ WITH (NOLOCK) WHERE c0_.id = ?" ); - + $connMock->setDatabasePlatform($orgPlatform); } @@ -1986,6 +1986,28 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase 'SELECT r0_.id AS id0, r1_.id AS id1, r1_.passengerName AS passengerName2 FROM RoutingRoute r0_ INNER JOIN RoutingRouteBooking r1_ ON r0_.id = r1_.route_id ORDER BY r1_.passengerName DESC' ); } + + /** + * @group DDC-1858 + */ + public function testHavingSupportIsNullExpression() + { + $this->assertSqlGeneration( + 'SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u HAVING u.username IS NULL', + 'SELECT c0_.name AS name0 FROM cms_users c0_ HAVING c0_.username IS NULL' + ); + } + + /** + * @group DDC-1858 + */ + public function testHavingSupportResultVariableInExpression() + { + $this->assertSqlGeneration( + 'SELECT u.name AS foo FROM Doctrine\Tests\Models\CMS\CmsUser u HAVING foo IN (?1)', + 'SELECT c0_.name AS name0 FROM cms_users c0_ HAVING name0 IN (?)' + ); + } } class MyAbsFunction extends \Doctrine\ORM\Query\AST\Functions\FunctionNode From 529064aff2cffb71580ee64f6fb4bb4b7f145b7e Mon Sep 17 00:00:00 2001 From: Dustin Thomson Date: Tue, 11 Jun 2013 18:09:49 -0600 Subject: [PATCH 153/332] Modified identity function to work with joined inheritance tables. Added regression tests --- .../ORM/Query/AST/Functions/IdentityFunction.php | 5 ++++- .../Tests/ORM/Query/SelectSqlGenerationTest.php | 15 +++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Query/AST/Functions/IdentityFunction.php b/lib/Doctrine/ORM/Query/AST/Functions/IdentityFunction.php index 5f51a9044..58f0dde13 100644 --- a/lib/Doctrine/ORM/Query/AST/Functions/IdentityFunction.php +++ b/lib/Doctrine/ORM/Query/AST/Functions/IdentityFunction.php @@ -82,7 +82,10 @@ class IdentityFunction extends FunctionNode } } - $tableAlias = $sqlWalker->getSQLTableAlias($class->getTableName(), $dqlAlias); + //The table with the relation may be a subclass, so get the table name from the association definition + $tableName = $sqlWalker->getEntityManager()->getClassMetadata($assoc['sourceEntity'])->getTableName(); + + $tableAlias = $sqlWalker->getSQLTableAlias($tableName, $dqlAlias); $columnName = $quoteStrategy->getJoinColumnName($joinColumn, $targetEntity, $platform); return $tableAlias . '.' . $columnName; diff --git a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php index e311214b1..cb610a859 100644 --- a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php +++ b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php @@ -1303,6 +1303,21 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase ); } + public function testIdenityFunctionInJoinedSubclass() + { + //relation is in the subclass (CompanyManager) we are querying + $this->assertSqlGeneration( + 'SELECT m, IDENTITY(m.car) as car_id FROM Doctrine\Tests\Models\Company\CompanyManager m', + 'SELECT c0_.id AS id0, c0_.name AS name1, c1_.salary AS salary2, c1_.department AS department3, c1_.startDate AS startDate4, c2_.title AS title5, c2_.car_id AS sclr6, c0_.discr AS discr7 FROM company_managers c2_ INNER JOIN company_employees c1_ ON c2_.id = c1_.id INNER JOIN company_persons c0_ ON c2_.id = c0_.id' + ); + + //relation is in the base class (CompanyPerson). + $this->assertSqlGeneration( + 'SELECT m, IDENTITY(m.spouse) as spouse_id FROM Doctrine\Tests\Models\Company\CompanyManager m', + 'SELECT c0_.id AS id0, c0_.name AS name1, c1_.salary AS salary2, c1_.department AS department3, c1_.startDate AS startDate4, c2_.title AS title5, c0_.spouse_id AS sclr6, c0_.discr AS discr7 FROM company_managers c2_ INNER JOIN company_employees c1_ ON c2_.id = c1_.id INNER JOIN company_persons c0_ ON c2_.id = c0_.id' + ); + } + /** * @group DDC-1339 */ From ed7a4bdcf3897b91b39b1b3b281cea2e4a406fbc Mon Sep 17 00:00:00 2001 From: Dustin Thomson Date: Tue, 11 Jun 2013 18:38:05 -0600 Subject: [PATCH 154/332] Fix typo in test name --- tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php index cb610a859..a924ed741 100644 --- a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php +++ b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php @@ -1303,7 +1303,7 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase ); } - public function testIdenityFunctionInJoinedSubclass() + public function testIdentityFunctionInJoinedSubclass() { //relation is in the subclass (CompanyManager) we are querying $this->assertSqlGeneration( From 0d834d0bd4015de2c103a03592c1543399f1b363 Mon Sep 17 00:00:00 2001 From: Guilherme Blanco Date: Wed, 12 Jun 2013 00:31:25 -0400 Subject: [PATCH 155/332] DDC-2489 Added missing semicolon when dump-sql on schema update. --- .../ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php index 577fb9e0f..0c2665eda 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php @@ -123,7 +123,7 @@ EOT $force = true === $input->getOption('force'); if ($dumpSql) { - $output->writeln(implode(';' . PHP_EOL, $sqls)); + $output->writeln(implode(';' . PHP_EOL, $sqls) . ';'); } if ($force) { @@ -138,7 +138,7 @@ EOT if ($dumpSql || $force) { return 0; } - + $output->writeln('ATTENTION: This operation should not be executed in a production environment.'); $output->writeln(' Use the incremental update to detect changes during development and use'); $output->writeln(' the SQL DDL provided to manually update your database in production.'); From 3d86c82a7fb51875d1b6c66b865134c9ccf1a878 Mon Sep 17 00:00:00 2001 From: Guilherme Blanco Date: Wed, 12 Jun 2013 02:00:36 -0400 Subject: [PATCH 156/332] DDC-2476 Better approach for reverse engineer. Some refactoring done to driver. --- .../ORM/Mapping/Driver/DatabaseDriver.php | 633 +++++++++++------- .../ORM/Functional/DatabaseDriverTest.php | 53 ++ 2 files changed, 433 insertions(+), 253 deletions(-) diff --git a/lib/Doctrine/ORM/Mapping/Driver/DatabaseDriver.php b/lib/Doctrine/ORM/Mapping/Driver/DatabaseDriver.php index 007be9639..9a12bbc72 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/DatabaseDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/DatabaseDriver.php @@ -19,12 +19,15 @@ namespace Doctrine\ORM\Mapping\Driver; -use Doctrine\DBAL\Schema\AbstractSchemaManager; -use Doctrine\DBAL\Schema\SchemaException; use Doctrine\Common\Persistence\Mapping\Driver\MappingDriver; use Doctrine\Common\Persistence\Mapping\ClassMetadata; -use Doctrine\ORM\Mapping\ClassMetadataInfo; use Doctrine\Common\Util\Inflector; +use Doctrine\DBAL\Schema\AbstractSchemaManager; +use Doctrine\DBAL\Schema\SchemaException; +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Types\Type; +use Doctrine\ORM\Mapping\ClassMetadataInfo; use Doctrine\ORM\Mapping\MappingException; /** @@ -84,249 +87,15 @@ class DatabaseDriver implements MappingDriver } /** - * Sets tables manually instead of relying on the reverse engineering capabilities of SchemaManager. + * Set the namespace for the generated entities. * - * @param array $entityTables - * @param array $manyToManyTables + * @param string $namespace * * @return void */ - public function setTables($entityTables, $manyToManyTables) + public function setNamespace($namespace) { - $this->tables = $this->manyToManyTables = $this->classToTableNames = array(); - foreach ($entityTables as $table) { - $className = $this->getClassNameForTable($table->getName()); - $this->classToTableNames[$className] = $table->getName(); - $this->tables[$table->getName()] = $table; - } - foreach ($manyToManyTables as $table) { - $this->manyToManyTables[$table->getName()] = $table; - } - } - - /** - * @return void - * - * @throws \Doctrine\ORM\Mapping\MappingException - */ - private function reverseEngineerMappingFromDatabase() - { - if ($this->tables !== null) { - return; - } - - $tables = array(); - - foreach ($this->_sm->listTableNames() as $tableName) { - $tables[$tableName] = $this->_sm->listTableDetails($tableName); - } - - $this->tables = $this->manyToManyTables = $this->classToTableNames = array(); - foreach ($tables as $tableName => $table) { - /* @var $table \Doctrine\DBAL\Schema\Table */ - if ($this->_sm->getDatabasePlatform()->supportsForeignKeyConstraints()) { - $foreignKeys = $table->getForeignKeys(); - } else { - $foreignKeys = array(); - } - - $allForeignKeyColumns = array(); - foreach ($foreignKeys as $foreignKey) { - $allForeignKeyColumns = array_merge($allForeignKeyColumns, $foreignKey->getLocalColumns()); - } - - if ( ! $table->hasPrimaryKey()) { - throw new MappingException( - "Table " . $table->getName() . " has no primary key. Doctrine does not ". - "support reverse engineering from tables that don't have a primary key." - ); - } - - $pkColumns = $table->getPrimaryKey()->getColumns(); - sort($pkColumns); - sort($allForeignKeyColumns); - - if ($pkColumns == $allForeignKeyColumns && count($foreignKeys) == 2) { - $this->manyToManyTables[$tableName] = $table; - } else { - // lower-casing is necessary because of Oracle Uppercase Tablenames, - // assumption is lower-case + underscore separated. - $className = $this->getClassNameForTable($tableName); - $this->tables[$tableName] = $table; - $this->classToTableNames[$className] = $tableName; - } - } - } - - /** - * {@inheritDoc} - */ - public function loadMetadataForClass($className, ClassMetadata $metadata) - { - $this->reverseEngineerMappingFromDatabase(); - - if (!isset($this->classToTableNames[$className])) { - throw new \InvalidArgumentException("Unknown class " . $className); - } - - $tableName = $this->classToTableNames[$className]; - - $metadata->name = $className; - $metadata->table['name'] = $tableName; - - $columns = $this->tables[$tableName]->getColumns(); - $indexes = $this->tables[$tableName]->getIndexes(); - try { - $primaryKeyColumns = $this->tables[$tableName]->getPrimaryKey()->getColumns(); - } catch(SchemaException $e) { - $primaryKeyColumns = array(); - } - - if ($this->_sm->getDatabasePlatform()->supportsForeignKeyConstraints()) { - $foreignKeys = $this->tables[$tableName]->getForeignKeys(); - } else { - $foreignKeys = array(); - } - - $allForeignKeyColumns = array(); - foreach ($foreignKeys as $foreignKey) { - $allForeignKeyColumns = array_merge($allForeignKeyColumns, $foreignKey->getLocalColumns()); - } - - $ids = array(); - $fieldMappings = array(); - foreach ($columns as $column) { - $fieldMapping = array(); - - if (in_array($column->getName(), $allForeignKeyColumns)) { - continue; - } else if ($primaryKeyColumns && in_array($column->getName(), $primaryKeyColumns)) { - $fieldMapping['id'] = true; - } - - $fieldMapping['fieldName'] = $this->getFieldNameForColumn($tableName, $column->getName(), false); - $fieldMapping['columnName'] = $column->getName(); - $fieldMapping['type'] = strtolower((string) $column->getType()); - - if ($column->getType() instanceof \Doctrine\DBAL\Types\StringType) { - $fieldMapping['length'] = $column->getLength(); - $fieldMapping['fixed'] = $column->getFixed(); - } else if ($column->getType() instanceof \Doctrine\DBAL\Types\IntegerType) { - $fieldMapping['unsigned'] = $column->getUnsigned(); - } - $fieldMapping['nullable'] = $column->getNotNull() ? false : true; - - if (isset($fieldMapping['id'])) { - $ids[] = $fieldMapping; - } else { - $fieldMappings[] = $fieldMapping; - } - } - - if ($ids) { - // We need to check for the columns here, because we might have associations as id as well. - if (count($primaryKeyColumns) == 1) { - $metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_AUTO); - } - - foreach ($ids as $id) { - $metadata->mapField($id); - } - } - - foreach ($fieldMappings as $fieldMapping) { - $metadata->mapField($fieldMapping); - } - - foreach ($this->manyToManyTables as $manyTable) { - foreach ($manyTable->getForeignKeys() as $foreignKey) { - // foreign key maps to the table of the current entity, many to many association probably exists - if (strtolower($tableName) == strtolower($foreignKey->getForeignTableName())) { - $myFk = $foreignKey; - $otherFk = null; - foreach ($manyTable->getForeignKeys() as $foreignKey) { - if ($foreignKey != $myFk) { - $otherFk = $foreignKey; - break; - } - } - - if (!$otherFk) { - // the definition of this many to many table does not contain - // enough foreign key information to continue reverse engineering. - continue; - } - - $localColumn = current($myFk->getColumns()); - $associationMapping = array(); - $associationMapping['fieldName'] = $this->getFieldNameForColumn($manyTable->getName(), current($otherFk->getColumns()), true); - $associationMapping['targetEntity'] = $this->getClassNameForTable($otherFk->getForeignTableName()); - if (current($manyTable->getColumns())->getName() == $localColumn) { - $associationMapping['inversedBy'] = $this->getFieldNameForColumn($manyTable->getName(), current($myFk->getColumns()), true); - $associationMapping['joinTable'] = array( - 'name' => strtolower($manyTable->getName()), - 'joinColumns' => array(), - 'inverseJoinColumns' => array(), - ); - - $fkCols = $myFk->getForeignColumns(); - $cols = $myFk->getColumns(); - for ($i = 0; $i < count($cols); $i++) { - $associationMapping['joinTable']['joinColumns'][] = array( - 'name' => $cols[$i], - 'referencedColumnName' => $fkCols[$i], - ); - } - - $fkCols = $otherFk->getForeignColumns(); - $cols = $otherFk->getColumns(); - for ($i = 0; $i < count($cols); $i++) { - $associationMapping['joinTable']['inverseJoinColumns'][] = array( - 'name' => $cols[$i], - 'referencedColumnName' => $fkCols[$i], - ); - } - } else { - $associationMapping['mappedBy'] = $this->getFieldNameForColumn($manyTable->getName(), current($myFk->getColumns()), true); - } - $metadata->mapManyToMany($associationMapping); - break; - } - } - } - - foreach ($foreignKeys as $foreignKey) { - $foreignTable = $foreignKey->getForeignTableName(); - $cols = $foreignKey->getColumns(); - $fkCols = $foreignKey->getForeignColumns(); - - $localColumn = current($cols); - $associationMapping = array(); - $associationMapping['fieldName'] = $this->getFieldNameForColumn($tableName, $localColumn, true); - $associationMapping['targetEntity'] = $this->getClassNameForTable($foreignTable); - - if (isset($metadata->fieldMappings[$associationMapping['fieldName']])) { - $associationMapping['fieldName'] = $associationMapping['fieldName'] . "2"; - } - - if ($primaryKeyColumns && in_array($localColumn, $primaryKeyColumns)) { - $associationMapping['id'] = true; - } - - for ($i = 0; $i < count($cols); $i++) { - $associationMapping['joinColumns'][] = array( - 'name' => $cols[$i], - 'referencedColumnName' => $fkCols[$i], - ); - } - - //Here we need to check if $cols are the same as $primaryKeyColumns - if (!array_diff($cols,$primaryKeyColumns)) { - $metadata->mapOneToOne($associationMapping); - } else { - $metadata->mapManyToOne($associationMapping); - } - } + $this->namespace = $namespace; } /** @@ -374,6 +143,376 @@ class DatabaseDriver implements MappingDriver $this->fieldNamesForColumns[$tableName][$columnName] = $fieldName; } + /** + * Sets tables manually instead of relying on the reverse engineering capabilities of SchemaManager. + * + * @param array $entityTables + * @param array $manyToManyTables + * + * @return void + */ + public function setTables($entityTables, $manyToManyTables) + { + $this->tables = $this->manyToManyTables = $this->classToTableNames = array(); + + foreach ($entityTables as $table) { + $className = $this->getClassNameForTable($table->getName()); + + $this->classToTableNames[$className] = $table->getName(); + $this->tables[$table->getName()] = $table; + } + + foreach ($manyToManyTables as $table) { + $this->manyToManyTables[$table->getName()] = $table; + } + } + + /** + * {@inheritDoc} + */ + public function loadMetadataForClass($className, ClassMetadata $metadata) + { + $this->reverseEngineerMappingFromDatabase(); + + if ( ! isset($this->classToTableNames[$className])) { + throw new \InvalidArgumentException("Unknown class " . $className); + } + + $tableName = $this->classToTableNames[$className]; + + $metadata->name = $className; + $metadata->table['name'] = $tableName; + + $this->buildIndexes($metadata); + $this->buildFieldMappings($metadata); + $this->buildToOneAssociationMappings($metadata); + + foreach ($this->manyToManyTables as $manyTable) { + foreach ($manyTable->getForeignKeys() as $foreignKey) { + // foreign key maps to the table of the current entity, many to many association probably exists + if ( ! (strtolower($tableName) === strtolower($foreignKey->getForeignTableName()))) { + continue; + } + + $myFk = $foreignKey; + $otherFk = null; + + foreach ($manyTable->getForeignKeys() as $foreignKey) { + if ($foreignKey != $myFk) { + $otherFk = $foreignKey; + break; + } + } + + if ( ! $otherFk) { + // the definition of this many to many table does not contain + // enough foreign key information to continue reverse engineering. + continue; + } + + $localColumn = current($myFk->getColumns()); + + $associationMapping = array(); + $associationMapping['fieldName'] = $this->getFieldNameForColumn($manyTable->getName(), current($otherFk->getColumns()), true); + $associationMapping['targetEntity'] = $this->getClassNameForTable($otherFk->getForeignTableName()); + + if (current($manyTable->getColumns())->getName() == $localColumn) { + $associationMapping['inversedBy'] = $this->getFieldNameForColumn($manyTable->getName(), current($myFk->getColumns()), true); + $associationMapping['joinTable'] = array( + 'name' => strtolower($manyTable->getName()), + 'joinColumns' => array(), + 'inverseJoinColumns' => array(), + ); + + $fkCols = $myFk->getForeignColumns(); + $cols = $myFk->getColumns(); + + for ($i = 0; $i < count($cols); $i++) { + $associationMapping['joinTable']['joinColumns'][] = array( + 'name' => $cols[$i], + 'referencedColumnName' => $fkCols[$i], + ); + } + + $fkCols = $otherFk->getForeignColumns(); + $cols = $otherFk->getColumns(); + + for ($i = 0; $i < count($cols); $i++) { + $associationMapping['joinTable']['inverseJoinColumns'][] = array( + 'name' => $cols[$i], + 'referencedColumnName' => $fkCols[$i], + ); + } + } else { + $associationMapping['mappedBy'] = $this->getFieldNameForColumn($manyTable->getName(), current($myFk->getColumns()), true); + } + + $metadata->mapManyToMany($associationMapping); + + break; + } + } + } + + /** + * @return void + * + * @throws \Doctrine\ORM\Mapping\MappingException + */ + private function reverseEngineerMappingFromDatabase() + { + if ($this->tables !== null) { + return; + } + + $tables = array(); + + foreach ($this->_sm->listTableNames() as $tableName) { + $tables[$tableName] = $this->_sm->listTableDetails($tableName); + } + + $this->tables = $this->manyToManyTables = $this->classToTableNames = array(); + + foreach ($tables as $tableName => $table) { + $foreignKeys = ($this->_sm->getDatabasePlatform()->supportsForeignKeyConstraints()) + ? $table->getForeignKeys() + : array(); + + $allForeignKeyColumns = array(); + + foreach ($foreignKeys as $foreignKey) { + $allForeignKeyColumns = array_merge($allForeignKeyColumns, $foreignKey->getLocalColumns()); + } + + if ( ! $table->hasPrimaryKey()) { + throw new MappingException( + "Table " . $table->getName() . " has no primary key. Doctrine does not ". + "support reverse engineering from tables that don't have a primary key." + ); + } + + $pkColumns = $table->getPrimaryKey()->getColumns(); + + sort($pkColumns); + sort($allForeignKeyColumns); + + if ($pkColumns == $allForeignKeyColumns && count($foreignKeys) == 2) { + $this->manyToManyTables[$tableName] = $table; + } else { + // lower-casing is necessary because of Oracle Uppercase Tablenames, + // assumption is lower-case + underscore separated. + $className = $this->getClassNameForTable($tableName); + + $this->tables[$tableName] = $table; + $this->classToTableNames[$className] = $tableName; + } + } + } + + /** + * Build indexes from a class metadata. + * + * @param \Doctrine\ORM\Mapping\ClassMetadataInfo $metadata + */ + private function buildIndexes(ClassMetadataInfo $metadata) + { + $tableName = $metadata->table['name']; + $indexes = $this->tables[$tableName]->getIndexes(); + + foreach($indexes as $index){ + if ($index->isPrimary()) { + continue; + } + + $indexName = $index->getName(); + $indexColumns = $index->getColumns(); + $constraintType = $index->isUnique() + ? 'uniqueConstraints' + : 'indexes'; + + $metadata->table[$constraintType][$indexName]['columns'] = $indexColumns; + } + } + + /** + * Build field mapping from class metadata. + * + * @param \Doctrine\ORM\Mapping\ClassMetadataInfo $metadata + */ + private function buildFieldMappings(ClassMetadataInfo $metadata) + { + $tableName = $metadata->table['name']; + $columns = $this->tables[$tableName]->getColumns(); + $primaryKeys = $this->getTablePrimaryKeys($this->tables[$tableName]); + $foreignKeys = $this->getTableForeignKeys($this->tables[$tableName]); + $allForeignKeys = array(); + + foreach ($foreignKeys as $foreignKey) { + $allForeignKeys = array_merge($allForeignKeys, $foreignKey->getLocalColumns()); + } + + $ids = array(); + $fieldMappings = array(); + + foreach ($columns as $column) { + if (in_array($column->getName(), $allForeignKeys)) { + continue; + } + + $fieldMapping = $this->buildFieldMapping($tableName, $column); + + if ($primaryKeys && in_array($column->getName(), $primaryKeys)) { + $fieldMapping['id'] = true; + $ids[] = $fieldMapping; + } + + $fieldMappings[] = $fieldMapping; + } + + // We need to check for the columns here, because we might have associations as id as well. + if ($ids && count($primaryKeys) == 1) { + $metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_AUTO); + } + + foreach ($fieldMappings as $fieldMapping) { + $metadata->mapField($fieldMapping); + } + } + + /** + * Build field mapping from a schema column definition + * + * @param string $tableName + * @param \Doctrine\DBAL\Schema\Column $column + * + * @return array + */ + private function buildFieldMapping($tableName, Column $column) + { + $fieldMapping = array( + 'fieldName' => $this->getFieldNameForColumn($tableName, $column->getName(), false), + 'columnName' => $column->getName(), + 'type' => strtolower((string) $column->getType()), + 'nullable' => ( ! $column->getNotNull()), + ); + + // Type specific elements + switch ($fieldMapping['type']) { + case Type::TARRAY: + case Type::BLOB: + case Type::GUID: + case Type::JSON_ARRAY: + case Type::OBJECT: + case Type::SIMPLE_ARRAY: + case Type::STRING: + case Type::TEXT: + $fieldMapping['length'] = $column->getLength(); + $fieldMapping['fixed'] = $column->getFixed(); + break; + + case Type::DECIMAL: + case Type::FLOAT: + $fieldMapping['precision'] = $column->getPrecision(); + $fieldMapping['scale'] = $column->getScale(); + break; + + case Type::INTEGER: + case Type::BIGINT: + case Type::SMALLINT: + $fieldMapping['unsigned'] = $column->getUnsigned(); + break; + } + + // Comment + if (($comment = $column->getComment()) !== null) { + $fieldMapping['comment'] = $comment; + } + + // Default + if (($default = $column->getDefault()) !== null) { + $fieldMapping['default'] = $default; + } + + return $fieldMapping; + } + + /** + * Build to one (one to one, many to one) association mapping from class metadata. + * + * @param \Doctrine\ORM\Mapping\ClassMetadataInfo $metadata + */ + private function buildToOneAssociationMappings(ClassMetadataInfo $metadata) + { + $tableName = $metadata->table['name']; + $primaryKeys = $this->getTablePrimaryKeys($this->tables[$tableName]); + $foreignKeys = $this->getTableForeignKeys($this->tables[$tableName]); + + foreach ($foreignKeys as $foreignKey) { + $foreignTableName = $foreignKey->getForeignTableName(); + $fkColumns = $foreignKey->getColumns(); + $fkForeignColumns = $foreignKey->getForeignColumns(); + $localColumn = current($fkColumns); + $associationMapping = array( + 'fieldName' => $this->getFieldNameForColumn($tableName, $localColumn, true), + 'targetEntity' => $this->getClassNameForTable($foreignTableName), + ); + + if (isset($metadata->fieldMappings[$associationMapping['fieldName']])) { + $associationMapping['fieldName'] .= '2'; // "foo" => "foo2" + } + + if ($primaryKeys && in_array($localColumn, $primaryKeys)) { + $associationMapping['id'] = true; + } + + for ($i = 0; $i < count($fkColumns); $i++) { + $associationMapping['joinColumns'][] = array( + 'name' => $fkColumns[$i], + 'referencedColumnName' => $fkForeignColumns[$i], + ); + } + + // Here we need to check if $fkColumns are the same as $primaryKeys + if ( ! array_diff($fkColumns, $primaryKeys)) { + $metadata->mapOneToOne($associationMapping); + } else { + $metadata->mapManyToOne($associationMapping); + } + } + } + + /** + * Retreive schema table definition foreign keys. + * + * @param \Doctrine\DBAL\Schema\Table $table + * + * @return array + */ + private function getTableForeignKeys(Table $table) + { + return ($this->_sm->getDatabasePlatform()->supportsForeignKeyConstraints()) + ? $table->getForeignKeys() + : array(); + } + + /** + * Retreive schema table definition primary keys. + * + * @param \Doctrine\DBAL\Schema\Table $table + * + * @return array + */ + private function getTablePrimaryKeys(Table $table) + { + try { + return $table->getPrimaryKey()->getColumns(); + } catch(SchemaException $e) { + // Do nothing + } + + return array(); + } + /** * Returns the mapped class name for a table if it exists. Otherwise return "classified" version. * @@ -413,16 +552,4 @@ class DatabaseDriver implements MappingDriver } return Inflector::camelize($columnName); } - - /** - * Set the namespace for the generated entities. - * - * @param string $namespace - * - * @return void - */ - public function setNamespace($namespace) - { - $this->namespace = $namespace; - } } diff --git a/tests/Doctrine/Tests/ORM/Functional/DatabaseDriverTest.php b/tests/Doctrine/Tests/ORM/Functional/DatabaseDriverTest.php index 0c30a0bef..b9039e714 100644 --- a/tests/Doctrine/Tests/ORM/Functional/DatabaseDriverTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/DatabaseDriverTest.php @@ -145,4 +145,57 @@ class DatabaseDriverTest extends DatabaseDriverTestCase $this->assertEquals(0, count($metadatas['DbdriverBaz']->associationMappings), "no association mappings should be detected."); } + + public function testLoadMetadataFromDatabaseDetail() + { + if (!$this->_em->getConnection()->getDatabasePlatform()->supportsForeignKeyConstraints()) { + $this->markTestSkipped('Platform does not support foreign keys.'); + } + + $table = new \Doctrine\DBAL\Schema\Table("dbdriver_foo"); + $table->addColumn('id', 'integer',array('unsigned'=>true)); + $table->setPrimaryKey(array('id')); + $table->addColumn('column_unsigned','integer',array('unsigned'=>true)); + $table->addColumn('column_comment','string',array('comment'=>'test_comment')); + $table->addColumn('column_default','string',array('default'=>'test_default')); + $table->addColumn('column_decimal','decimal',array('precision'=>4,'scale'=>3)); + + $table->addColumn('column_index1','string'); + $table->addColumn('column_index2','string'); + $table->addIndex(array('column_index1','column_index2'),'index1'); + + $table->addColumn('column_unique_index1','string'); + $table->addColumn('column_unique_index2','string'); + $table->addUniqueIndex(array('column_unique_index1','column_unique_index2'),'unique_index1'); + $this->_sm->dropAndCreateTable($table); + + $metadatas = $this->extractClassMetadata(array("DbdriverFoo")); + + $this->assertArrayHasKey('DbdriverFoo', $metadatas); + $metadata = $metadatas['DbdriverFoo']; + + $this->assertArrayHasKey('id', $metadata->fieldMappings); + $this->assertEquals('id', $metadata->fieldMappings['id']['fieldName']); + $this->assertEquals('id', strtolower($metadata->fieldMappings['id']['columnName'])); + $this->assertEquals('integer', (string)$metadata->fieldMappings['id']['type']); + + $this->assertArrayHasKey('columnUnsigned', $metadata->fieldMappings); + $this->assertTrue($metadata->fieldMappings['columnUnsigned']['unsigned']); + + $this->assertArrayHasKey('columnComment', $metadata->fieldMappings); + $this->assertEquals('test_comment', $metadata->fieldMappings['columnComment']['comment']); + + $this->assertArrayHasKey('columnDefault', $metadata->fieldMappings); + $this->assertEquals('test_default', $metadata->fieldMappings['columnDefault']['default']); + + $this->assertArrayHasKey('columnDecimal', $metadata->fieldMappings); + $this->assertEquals(4, $metadata->fieldMappings['columnDecimal']['precision']); + $this->assertEquals(3, $metadata->fieldMappings['columnDecimal']['scale']); + + $this->assertTrue(!empty($metadata->table['indexes']['index1']['columns'])); + $this->assertEquals(array('column_index1','column_index2'),$metadata->table['indexes']['index1']['columns']); + + $this->assertTrue(!empty($metadata->table['uniqueConstraints']['unique_index1']['columns'])); + $this->assertEquals(array('column_unique_index1','column_unique_index2'),$metadata->table['uniqueConstraints']['unique_index1']['columns']); + } } From b15758bb425578159ebe386c3d8fe2f1cc04bccb Mon Sep 17 00:00:00 2001 From: Guilherme Blanco Date: Wed, 12 Jun 2013 02:29:08 -0400 Subject: [PATCH 157/332] DDC-2476 Commented check under PostgreSQL. --- .../ORM/Functional/DatabaseDriverTest.php | 72 +++++++++++-------- 1 file changed, 43 insertions(+), 29 deletions(-) diff --git a/tests/Doctrine/Tests/ORM/Functional/DatabaseDriverTest.php b/tests/Doctrine/Tests/ORM/Functional/DatabaseDriverTest.php index b9039e714..fce29d93c 100644 --- a/tests/Doctrine/Tests/ORM/Functional/DatabaseDriverTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/DatabaseDriverTest.php @@ -3,6 +3,7 @@ namespace Doctrine\Tests\ORM\Functional; use Doctrine\ORM\Mapping\ClassMetadataInfo; +use Doctrine\DBAL\Platforms\PostgreSqlPlatform; class DatabaseDriverTest extends DatabaseDriverTestCase { @@ -148,54 +149,67 @@ class DatabaseDriverTest extends DatabaseDriverTestCase public function testLoadMetadataFromDatabaseDetail() { - if (!$this->_em->getConnection()->getDatabasePlatform()->supportsForeignKeyConstraints()) { + if ( ! $this->_em->getConnection()->getDatabasePlatform()->supportsForeignKeyConstraints()) { $this->markTestSkipped('Platform does not support foreign keys.'); } $table = new \Doctrine\DBAL\Schema\Table("dbdriver_foo"); - $table->addColumn('id', 'integer',array('unsigned'=>true)); + + $table->addColumn('id', 'integer', array('unsigned' => true)); $table->setPrimaryKey(array('id')); - $table->addColumn('column_unsigned','integer',array('unsigned'=>true)); - $table->addColumn('column_comment','string',array('comment'=>'test_comment')); - $table->addColumn('column_default','string',array('default'=>'test_default')); - $table->addColumn('column_decimal','decimal',array('precision'=>4,'scale'=>3)); + $table->addColumn('column_unsigned', 'integer', array('unsigned' => true)); + $table->addColumn('column_comment', 'string', array('comment' => 'test_comment')); + $table->addColumn('column_default', 'string', array('default' => 'test_default')); + $table->addColumn('column_decimal', 'decimal', array('precision' => 4, 'scale' => 3)); - $table->addColumn('column_index1','string'); - $table->addColumn('column_index2','string'); - $table->addIndex(array('column_index1','column_index2'),'index1'); + $table->addColumn('column_index1', 'string'); + $table->addColumn('column_index2', 'string'); + $table->addIndex(array('column_index1','column_index2'), 'index1'); + + $table->addColumn('column_unique_index1', 'string'); + $table->addColumn('column_unique_index2', 'string'); + $table->addUniqueIndex(array('column_unique_index1', 'column_unique_index2'), 'unique_index1'); - $table->addColumn('column_unique_index1','string'); - $table->addColumn('column_unique_index2','string'); - $table->addUniqueIndex(array('column_unique_index1','column_unique_index2'),'unique_index1'); $this->_sm->dropAndCreateTable($table); $metadatas = $this->extractClassMetadata(array("DbdriverFoo")); $this->assertArrayHasKey('DbdriverFoo', $metadatas); + $metadata = $metadatas['DbdriverFoo']; - $this->assertArrayHasKey('id', $metadata->fieldMappings); - $this->assertEquals('id', $metadata->fieldMappings['id']['fieldName']); - $this->assertEquals('id', strtolower($metadata->fieldMappings['id']['columnName'])); - $this->assertEquals('integer', (string)$metadata->fieldMappings['id']['type']); + $this->assertArrayHasKey('id', $metadata->fieldMappings); + $this->assertEquals('id', $metadata->fieldMappings['id']['fieldName']); + $this->assertEquals('id', strtolower($metadata->fieldMappings['id']['columnName'])); + $this->assertEquals('integer', (string) $metadata->fieldMappings['id']['type']); - $this->assertArrayHasKey('columnUnsigned', $metadata->fieldMappings); - $this->assertTrue($metadata->fieldMappings['columnUnsigned']['unsigned']); + // FIXME: Condition here is fugly. + // NOTE: PostgreSQL does not support UNSIGNED + if ( ! $this->_em->getConnection()->getDatabasePlatform() instanceof PostgreSqlPlatform) { + $this->assertArrayHasKey('columnUnsigned', $metadata->fieldMappings); + $this->assertTrue($metadata->fieldMappings['columnUnsigned']['unsigned']); + } - $this->assertArrayHasKey('columnComment', $metadata->fieldMappings); - $this->assertEquals('test_comment', $metadata->fieldMappings['columnComment']['comment']); + $this->assertArrayHasKey('columnComment', $metadata->fieldMappings); + $this->assertEquals('test_comment', $metadata->fieldMappings['columnComment']['comment']); - $this->assertArrayHasKey('columnDefault', $metadata->fieldMappings); - $this->assertEquals('test_default', $metadata->fieldMappings['columnDefault']['default']); + $this->assertArrayHasKey('columnDefault', $metadata->fieldMappings); + $this->assertEquals('test_default', $metadata->fieldMappings['columnDefault']['default']); - $this->assertArrayHasKey('columnDecimal', $metadata->fieldMappings); - $this->assertEquals(4, $metadata->fieldMappings['columnDecimal']['precision']); - $this->assertEquals(3, $metadata->fieldMappings['columnDecimal']['scale']); + $this->assertArrayHasKey('columnDecimal', $metadata->fieldMappings); + $this->assertEquals(4, $metadata->fieldMappings['columnDecimal']['precision']); + $this->assertEquals(3, $metadata->fieldMappings['columnDecimal']['scale']); - $this->assertTrue(!empty($metadata->table['indexes']['index1']['columns'])); - $this->assertEquals(array('column_index1','column_index2'),$metadata->table['indexes']['index1']['columns']); + $this->assertTrue( ! empty($metadata->table['indexes']['index1']['columns'])); + $this->assertEquals( + array('column_index1','column_index2'), + $metadata->table['indexes']['index1']['columns'] + ); - $this->assertTrue(!empty($metadata->table['uniqueConstraints']['unique_index1']['columns'])); - $this->assertEquals(array('column_unique_index1','column_unique_index2'),$metadata->table['uniqueConstraints']['unique_index1']['columns']); + $this->assertTrue( ! empty($metadata->table['uniqueConstraints']['unique_index1']['columns'])); + $this->assertEquals( + array('column_unique_index1', 'column_unique_index2'), + $metadata->table['uniqueConstraints']['unique_index1']['columns'] + ); } } From d685f592fe7afacd99840680a6f12404d5e2ac59 Mon Sep 17 00:00:00 2001 From: "Fabio B. Silva" Date: Tue, 11 Jun 2013 13:44:08 -0400 Subject: [PATCH 158/332] Fix DDC-2494 --- .../Hydration/SimpleObjectHydrator.php | 82 +++---- .../ORM/Persisters/BasicEntityPersister.php | 12 +- lib/Doctrine/ORM/Query/ResultSetMapping.php | 13 +- .../ORM/Functional/Ticket/DDC2494Test.php | 211 ++++++++++++++++++ 4 files changed, 273 insertions(+), 45 deletions(-) create mode 100644 tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2494Test.php diff --git a/lib/Doctrine/ORM/Internal/Hydration/SimpleObjectHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/SimpleObjectHydrator.php index 249b60462..85f3a0fad 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/SimpleObjectHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/SimpleObjectHydrator.php @@ -114,9 +114,8 @@ class SimpleObjectHydrator extends AbstractHydrator } // Convert field to a valid PHP value - if (isset($cache[$column]['field'])) { - $type = Type::getType($cache[$column]['class']->fieldMappings[$cache[$column]['name']]['type']); - $value = $type->convertToPHPValue($value, $this->_platform); + if (isset($cache[$column]['type'])) { + $value = Type::getType($cache[$column]['type'])->convertToPHPValue($value, $this->_platform); } // Prevent overwrite in case of inherit classes using same property name (See AbstractHydrator) @@ -145,44 +144,51 @@ class SimpleObjectHydrator extends AbstractHydrator */ protected function hydrateColumnInfo($entityName, $column) { - switch (true) { - case (isset($this->_rsm->fieldMappings[$column])): - $class = isset($this->declaringClasses[$column]) - ? $this->declaringClasses[$column] - : $this->class; - // If class is not part of the inheritance, ignore - if ( ! ($class->name === $entityName || is_subclass_of($entityName, $class->name))) { - return null; - } + if (isset($this->_rsm->fieldMappings[$column])) { + $name = $this->_rsm->fieldMappings[$column]; + $class = isset($this->declaringClasses[$column]) + ? $this->declaringClasses[$column] + : $this->class; - return array( - 'class' => $class, - 'name' => $this->_rsm->fieldMappings[$column], - 'field' => true, - ); - - case (isset($this->_rsm->relationMap[$column])): - $class = isset($this->_rsm->relationMap[$column]) - ? $this->_rsm->relationMap[$column] - : $this->class; - - // If class is not self referencing, ignore - if ( ! ($class === $entityName || is_subclass_of($entityName, $class))) { - return null; - } - - // TODO: Decide what to do with associations. It seems original code is incomplete. - // One solution is to load the association, but it might require extra efforts. - return array('name' => $column); - - case (isset($this->_rsm->metaMappings[$column])): - return array( - 'name' => $this->_rsm->metaMappings[$column] - ); - - default: + // If class is not part of the inheritance, ignore + if ( ! ($class->name === $entityName || is_subclass_of($entityName, $class->name))) { return null; + } + + return array( + 'name' => $name, + 'type' => $class->fieldMappings[$name]['type'] + ); } + + if (isset($this->_rsm->relationMap[$column])) { + $class = isset($this->_rsm->relationMap[$column]) + ? $this->_rsm->relationMap[$column] + : $this->class; + + // If class is not self referencing, ignore + if ( ! ($class === $entityName || is_subclass_of($entityName, $class))) { + return null; + } + + // TODO: Decide what to do with associations. It seems original code is incomplete. + // One solution is to load the association, but it might require extra efforts. + return array('name' => $column); + } + + if (isset($this->_rsm->metaMappings[$column])) { + $name = $this->_rsm->metaMappings[$column]; + $type = isset($this->_rsm->typeMappings[$column]) + ? $this->_rsm->typeMappings[$column] + : null; + + return array( + 'name' => $name, + 'type' => $type + ); + } + + return null; } } diff --git a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php index f101fda13..66e949ca8 100644 --- a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php @@ -1334,16 +1334,22 @@ class BasicEntityPersister return ''; } - $columnList = array(); + $columnList = array(); + $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); foreach ($assoc['joinColumns'] as $joinColumn) { - + $type = null; + $isIdentifier = isset($assoc['id']) && $assoc['id'] === true; $quotedColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform); $resultColumnName = $this->getSQLColumnAlias($joinColumn['name']); $columnList[] = $this->getSQLTableAlias($class->name, ($alias == 'r' ? '' : $alias) ) . '.' . $quotedColumn . ' AS ' . $resultColumnName; - $this->rsm->addMetaResult($alias, $resultColumnName, $quotedColumn, isset($assoc['id']) && $assoc['id'] === true); + if (isset($targetClass->fieldNames[$joinColumn['referencedColumnName']])) { + $type = $targetClass->fieldMappings[$targetClass->fieldNames[$joinColumn['referencedColumnName']]]['type']; + } + + $this->rsm->addMetaResult($alias, $resultColumnName, $quotedColumn, $isIdentifier, $type); } return implode(', ', $columnList); diff --git a/lib/Doctrine/ORM/Query/ResultSetMapping.php b/lib/Doctrine/ORM/Query/ResultSetMapping.php index 2f1727383..f4d11cf44 100644 --- a/lib/Doctrine/ORM/Query/ResultSetMapping.php +++ b/lib/Doctrine/ORM/Query/ResultSetMapping.php @@ -543,14 +543,15 @@ class ResultSetMapping /** * Adds a meta column (foreign key or discriminator column) to the result set. * - * @param string $alias - * @param string $columnName - * @param string $fieldName + * @param string $alias The result alias with which the meta result should be placed in the result structure. + * @param string $columnName The name of the column in the SQL result set. + * @param string $fieldName The name of the field on the declaring class. * @param bool $isIdentifierColumn + * @param string $type The column type * * @return ResultSetMapping This ResultSetMapping instance. */ - public function addMetaResult($alias, $columnName, $fieldName, $isIdentifierColumn = false) + public function addMetaResult($alias, $columnName, $fieldName, $isIdentifierColumn = false, $type = null) { $this->metaMappings[$columnName] = $fieldName; $this->columnOwnerMap[$columnName] = $alias; @@ -559,6 +560,10 @@ class ResultSetMapping $this->isIdentifierColumn[$alias][$columnName] = true; } + if ($type) { + $this->typeMappings[$columnName] = $type; + } + return $this; } } diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2494Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2494Test.php new file mode 100644 index 000000000..4c27fb777 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2494Test.php @@ -0,0 +1,211 @@ +_schemaTool->createSchema(array( + $this->_em->getClassMetadata(DDC2494Currency::CLASSNAME), + $this->_em->getClassMetadata(DDC2494Campaign::CLASSNAME), + )); + } + + public function testIssue() + { + $currency = new DDC2494Currency(1, 2); + + $this->_em->persist($currency); + $this->_em->flush(); + + $campaign = new DDC2494Campaign($currency); + + $this->_em->persist($campaign); + $this->_em->flush(); + $this->_em->close(); + + $this->assertArrayHasKey('convertToDatabaseValue', DDC2494TinyIntType::$calls); + $this->assertCount(3, DDC2494TinyIntType::$calls['convertToDatabaseValue']); + + $item = $this->_em->find(DDC2494Campaign::CLASSNAME, $campaign->getId()); + + $this->assertInstanceOf(DDC2494Campaign::CLASSNAME, $item); + $this->assertInstanceOf(DDC2494Currency::CLASSNAME, $item->getCurrency()); + + $queryCount = $this->getCurrentQueryCount(); + + $this->assertInstanceOf('\Doctrine\Common\Proxy\Proxy', $item->getCurrency()); + $this->assertFalse($item->getCurrency()->__isInitialized()); + + $this->assertArrayHasKey('convertToPHPValue', DDC2494TinyIntType::$calls); + $this->assertCount(1, DDC2494TinyIntType::$calls['convertToPHPValue']); + + $this->assertInternalType('integer', $item->getCurrency()->getId()); + $this->assertCount(1, DDC2494TinyIntType::$calls['convertToPHPValue']); + $this->assertFalse($item->getCurrency()->__isInitialized()); + + $this->assertEquals($queryCount, $this->getCurrentQueryCount()); + + $this->assertInternalType('integer', $item->getCurrency()->getTemp()); + $this->assertCount(3, DDC2494TinyIntType::$calls['convertToPHPValue']); + $this->assertTrue($item->getCurrency()->__isInitialized()); + + $this->assertEquals($queryCount + 1, $this->getCurrentQueryCount()); + } +} + +/** + * @Table(name="ddc2494_currency") + * @Entity + */ +class DDC2494Currency +{ + const CLASSNAME = __CLASS__; + + /** + * @Id + * @Column(type="integer", type="ddc2494_tinyint") + */ + protected $id; + + /** + * @Column(name="temp", type="ddc2494_tinyint", nullable=false) + */ + protected $temp; + + /** + * @var \Doctrine\Common\Collections\Collection + * + * @OneToMany(targetEntity="DDC2494Campaign", mappedBy="currency") + */ + protected $campaigns; + + public function __construct($id, $temp) + { + $this->id = $id; + $this->temp = $temp; + } + + public function getId() + { + return $this->id; + } + + public function getTemp() + { + return $this->temp; + } + + public function getCampaigns() + { + return $this->campaigns; + } +} + +/** + * @Table(name="ddc2494_campaign") + * @Entity + */ +class DDC2494Campaign +{ + const CLASSNAME = __CLASS__; + + /** + * @Id + * @GeneratedValue + * @Column(type="integer") + */ + protected $id; + + /** + * @var \Doctrine\Tests\ORM\Functional\Ticket\DDC2494Currency + * + * @ManyToOne(targetEntity="DDC2494Currency", inversedBy="campaigns") + * @JoinColumn(name="currency_id", referencedColumnName="id", nullable=false) + */ + protected $currency; + + public function __construct(DDC2494Currency $currency) + { + $this->currency = $currency; + } + + public function getId() + { + return $this->id; + } + + /** + * @return \Doctrine\Tests\ORM\Functional\Ticket\DDC2494Currency + */ + public function getCurrency() + { + return $this->currency; + } +} + +class DDC2494TinyIntType extends Type +{ + public static $calls = array(); + + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getSmallIntTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + $return = (string) $value; + + self::$calls[__FUNCTION__][] = array( + 'value' => $value, + 'return' => $return, + 'platform' => $platform, + ); + + return $return; + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + $return = (integer) $value; + + self::$calls[__FUNCTION__][] = array( + 'value' => $value, + 'return' => $return, + 'platform' => $platform, + ); + + return $return; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'ddc2494_tinyint'; + } +} \ No newline at end of file From d961028b1443799a580bdc60437445a49c31b863 Mon Sep 17 00:00:00 2001 From: "Fabio B. Silva" Date: Tue, 11 Jun 2013 14:23:50 -0400 Subject: [PATCH 159/332] small optimization --- .../ORM/Internal/Hydration/SimpleObjectHydrator.php | 9 ++------- .../Doctrine/Tests/ORM/Functional/Ticket/DDC2494Test.php | 2 +- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/lib/Doctrine/ORM/Internal/Hydration/SimpleObjectHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/SimpleObjectHydrator.php index 85f3a0fad..412ad41e2 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/SimpleObjectHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/SimpleObjectHydrator.php @@ -178,14 +178,9 @@ class SimpleObjectHydrator extends AbstractHydrator } if (isset($this->_rsm->metaMappings[$column])) { - $name = $this->_rsm->metaMappings[$column]; - $type = isset($this->_rsm->typeMappings[$column]) - ? $this->_rsm->typeMappings[$column] - : null; - return array( - 'name' => $name, - 'type' => $type + 'name' => $this->_rsm->metaMappings[$column], + 'type' => (isset($this->_rsm->typeMappings[$column]) ? $this->_rsm->typeMappings[$column] : null) ); } diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2494Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2494Test.php index 4c27fb777..1b2ca881e 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2494Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2494Test.php @@ -208,4 +208,4 @@ class DDC2494TinyIntType extends Type { return 'ddc2494_tinyint'; } -} \ No newline at end of file +} From c1e688fc812f68d0f68c2135d46303fdb22ea23a Mon Sep 17 00:00:00 2001 From: "Fabio B. Silva" Date: Wed, 12 Jun 2013 10:29:17 -0400 Subject: [PATCH 160/332] drop useless support for associations --- .../Hydration/SimpleObjectHydrator.php | 24 ++++++------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/lib/Doctrine/ORM/Internal/Hydration/SimpleObjectHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/SimpleObjectHydrator.php index 412ad41e2..eb0982c7d 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/SimpleObjectHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/SimpleObjectHydrator.php @@ -146,8 +146,8 @@ class SimpleObjectHydrator extends AbstractHydrator { if (isset($this->_rsm->fieldMappings[$column])) { - $name = $this->_rsm->fieldMappings[$column]; - $class = isset($this->declaringClasses[$column]) + $name = $this->_rsm->fieldMappings[$column]; + $class = isset($this->declaringClasses[$column]) ? $this->declaringClasses[$column] : $this->class; @@ -162,21 +162,6 @@ class SimpleObjectHydrator extends AbstractHydrator ); } - if (isset($this->_rsm->relationMap[$column])) { - $class = isset($this->_rsm->relationMap[$column]) - ? $this->_rsm->relationMap[$column] - : $this->class; - - // If class is not self referencing, ignore - if ( ! ($class === $entityName || is_subclass_of($entityName, $class))) { - return null; - } - - // TODO: Decide what to do with associations. It seems original code is incomplete. - // One solution is to load the association, but it might require extra efforts. - return array('name' => $column); - } - if (isset($this->_rsm->metaMappings[$column])) { return array( 'name' => $this->_rsm->metaMappings[$column], @@ -184,6 +169,11 @@ class SimpleObjectHydrator extends AbstractHydrator ); } + // An ObjectHydrator should be used instead of SimpleObjectHydrator + if (isset($this->_rsm->relationMap[$column])) { + throw new \Exception(sprintf('Unable to retrieve association information for column "%s"', $column)); + } + return null; } } From a1355d0bb9ed502d10ed8d7d333544e7089ce34c Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Wed, 12 Jun 2013 19:53:43 +0200 Subject: [PATCH 161/332] Adding failing test for DDC-2214 Parameters being bound to an SQL query should have the same type as the identifier of the objects being bound to the placeholders of a DQL query - this is currently broken with proxies, as this test demonstrates. --- .../ORM/Functional/Ticket/DDC2214Test.php | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2214Test.php diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2214Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2214Test.php new file mode 100644 index 000000000..060b6ba33 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2214Test.php @@ -0,0 +1,71 @@ + + * + * @group DDC-2214 + */ +class DDC2214Test extends \Doctrine\Tests\OrmFunctionalTestCase +{ + protected function setUp() + { + parent::setUp(); + + $this->_schemaTool->createSchema(array( + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2214Foo'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2214Bar'), + )); + } + + public function testIssue() + { + $foo = new DDC2214Foo(); + $bar = new DDC2214Bar(); + + $foo->bar = $bar; + + $this->_em->persist($foo); + $this->_em->persist($bar); + $this->_em->flush(); + $this->_em->clear(); + + /* @var $foo \Doctrine\Tests\ORM\Functional\Ticket\DDC2214Foo */ + $foo = $this->_em->find(__NAMESPACE__ . '\\DDC2214Foo', $foo->id); + $bar = $foo->bar; + + $logger = $this->_em->getConnection()->getConfiguration()->getSQLLogger(); + + $related = $this + ->_em + ->createQuery('SELECT b FROM '.__NAMESPACE__ . '\DDC2214Bar b WHERE b.id IN(:ids)') + ->setParameter('ids', array($bar)) + ->getResult(); + + $query = end($logger->queries); + + $this->assertEquals(\Doctrine\DBAL\Connection::PARAM_INT_ARRAY, $query['types'][0]); + } +} + +/** @Entity */ +class DDC2214Foo +{ + /** @Id @Column(type="integer") @GeneratedValue(strategy="AUTO") */ + public $id; + + /** @ManyToOne(targetEntity="DDC2214Bar") */ + public $bar; +} + +/** @Entity */ +class DDC2214Bar +{ + /** @Id @Column(type="integer") @GeneratedValue(strategy="AUTO") */ + public $id; +} From f16c8e3efe0cf4d75f2b6009ecec0a7d5743118f Mon Sep 17 00:00:00 2001 From: "Fabio B. Silva" Date: Thu, 13 Jun 2013 16:21:26 -0400 Subject: [PATCH 162/332] Fix DDC-2478 --- .../ORM/Persisters/SqlExpressionVisitor.php | 1 + .../ORM/Functional/EntityRepositoryTest.php | 29 +++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/lib/Doctrine/ORM/Persisters/SqlExpressionVisitor.php b/lib/Doctrine/ORM/Persisters/SqlExpressionVisitor.php index b06358a20..7736943c6 100644 --- a/lib/Doctrine/ORM/Persisters/SqlExpressionVisitor.php +++ b/lib/Doctrine/ORM/Persisters/SqlExpressionVisitor.php @@ -66,6 +66,7 @@ class SqlExpressionVisitor extends ExpressionVisitor $value = $comparison->getValue()->getValue(); // shortcut for walkValue() if (isset($this->classMetadata->associationMappings[$field]) && + $value !== null && ! is_object($value) && ! in_array($comparison->getOperator(), array(Comparison::IN, Comparison::NIN))) { diff --git a/tests/Doctrine/Tests/ORM/Functional/EntityRepositoryTest.php b/tests/Doctrine/Tests/ORM/Functional/EntityRepositoryTest.php index 517ff3d97..6432c3072 100644 --- a/tests/Doctrine/Tests/ORM/Functional/EntityRepositoryTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/EntityRepositoryTest.php @@ -842,6 +842,35 @@ class EntityRepositoryTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertEquals(2, count($users)); } + /** + * @group DDC-2478 + */ + public function testMatchingCriteriaNullAssocComparison() + { + $fixtures = $this->loadFixtureUserEmail(); + $user = $this->_em->merge($fixtures[0]); + $repository = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsUser'); + $criteriaIsNull = Criteria::create()->where(Criteria::expr()->isNull('email')); + $criteriaEqNull = Criteria::create()->where(Criteria::expr()->eq('email', null)); + + $user->setEmail(null); + $this->_em->persist($user); + $this->_em->flush(); + $this->_em->clear(); + + $usersIsNull = $repository->matching($criteriaIsNull); + $usersEqNull = $repository->matching($criteriaEqNull); + + $this->assertCount(1, $usersIsNull); + $this->assertCount(1, $usersEqNull); + + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $usersIsNull[0]); + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $usersEqNull[0]); + + $this->assertNull($usersIsNull[0]->getEmail()); + $this->assertNull($usersEqNull[0]->getEmail()); + } + /** * @group DDC-2055 */ From 7eb744126ba5081ea51441a893dd950046777383 Mon Sep 17 00:00:00 2001 From: Guilherme Blanco Date: Thu, 13 Jun 2013 21:47:40 -0400 Subject: [PATCH 163/332] Implemented support for RepositoryFactory. --- lib/Doctrine/ORM/Configuration.php | 50 ++++++++--- lib/Doctrine/ORM/EntityManager.php | 46 ++++------ .../Repository/DefaultRepositoryFactory.php | 89 +++++++++++++++++++ .../ORM/Repository/RepositoryFactory.php | 47 ++++++++++ .../ORM/Functional/Ticket/DDC2359Test.php | 5 +- 5 files changed, 195 insertions(+), 42 deletions(-) create mode 100644 lib/Doctrine/ORM/Repository/DefaultRepositoryFactory.php create mode 100644 lib/Doctrine/ORM/Repository/RepositoryFactory.php diff --git a/lib/Doctrine/ORM/Configuration.php b/lib/Doctrine/ORM/Configuration.php index 959a206dd..a1535de85 100644 --- a/lib/Doctrine/ORM/Configuration.php +++ b/lib/Doctrine/ORM/Configuration.php @@ -19,20 +19,22 @@ namespace Doctrine\ORM; -use Doctrine\Common\Cache\Cache; -use Doctrine\Common\Cache\ArrayCache; -use Doctrine\Common\Annotations\AnnotationRegistry; use Doctrine\Common\Annotations\AnnotationReader; -use Doctrine\Common\Persistence\Mapping\Driver\MappingDriver; -use Doctrine\ORM\Mapping\Driver\AnnotationDriver; -use Doctrine\ORM\Mapping\QuoteStrategy; -use Doctrine\ORM\Mapping\DefaultQuoteStrategy; -use Doctrine\ORM\Mapping\NamingStrategy; -use Doctrine\ORM\Mapping\DefaultNamingStrategy; -use Doctrine\ORM\Mapping\EntityListenerResolver; -use Doctrine\ORM\Mapping\DefaultEntityListenerResolver; -use Doctrine\Common\Annotations\SimpleAnnotationReader; +use Doctrine\Common\Annotations\AnnotationRegistry; use Doctrine\Common\Annotations\CachedReader; +use Doctrine\Common\Annotations\SimpleAnnotationReader; +use Doctrine\Common\Cache\ArrayCache; +use Doctrine\Common\Cache\Cache; +use Doctrine\Common\Persistence\Mapping\Driver\MappingDriver; +use Doctrine\ORM\Mapping\DefaultEntityListenerResolver; +use Doctrine\ORM\Mapping\DefaultNamingStrategy; +use Doctrine\ORM\Mapping\DefaultQuoteStrategy; +use Doctrine\ORM\Mapping\Driver\AnnotationDriver; +use Doctrine\ORM\Mapping\EntityListenerResolver; +use Doctrine\ORM\Mapping\NamingStrategy; +use Doctrine\ORM\Mapping\QuoteStrategy; +use Doctrine\ORM\Repository\DefaultRepositoryFactory; +use Doctrine\ORM\Repository\RepositoryFactoryInterface; /** * Configuration container for all configuration options of Doctrine. @@ -779,4 +781,28 @@ class Configuration extends \Doctrine\DBAL\Configuration return $this->_attributes['entityListenerResolver']; } + + /** + * Set the entity repository factory. + * + * @since 2.5 + * @param \Doctrine\ORM\Repository\RepositoryFactoryInterface $repositoryFactory + */ + public function setRepositoryFactory(RepositoryFactoryInterface $repositoryFactory) + { + $this->_attributes['repositoryFactory'] = $repositoryFactory; + } + + /** + * Get the entity repository factory. + * + * @since 2.5 + * @return \Doctrine\ORM\Repository\RepositoryFactoryInterface + */ + public function getRepositoryFactory() + { + return isset($this->_attributes['repositoryFactory']) + ? $this->_attributes['repositoryFactory'] + : new DefaultRepositoryFactory(); + } } diff --git a/lib/Doctrine/ORM/EntityManager.php b/lib/Doctrine/ORM/EntityManager.php index bc0d4ef18..8ee39cf06 100644 --- a/lib/Doctrine/ORM/EntityManager.php +++ b/lib/Doctrine/ORM/EntityManager.php @@ -85,13 +85,6 @@ use Doctrine\Common\Util\ClassUtils; */ private $metadataFactory; - /** - * The EntityRepository instances. - * - * @var array - */ - private $repositories = array(); - /** * The UnitOfWork used to coordinate object-level transactions. * @@ -120,6 +113,13 @@ use Doctrine\Common\Util\ClassUtils; */ private $proxyFactory; + /** + * The repository factory used to create dynamic repositories. + * + * @var \Doctrine\ORM\Repository\RepositoryFactory + */ + private $repositoryFactory; + /** * The expression builder instance used to generate query expressions. * @@ -151,9 +151,9 @@ use Doctrine\Common\Util\ClassUtils; */ protected function __construct(Connection $conn, Configuration $config, EventManager $eventManager) { - $this->conn = $conn; - $this->config = $config; - $this->eventManager = $eventManager; + $this->conn = $conn; + $this->config = $config; + $this->eventManager = $eventManager; $metadataFactoryClassName = $config->getClassMetadataFactoryName(); @@ -161,6 +161,11 @@ use Doctrine\Common\Util\ClassUtils; $this->metadataFactory->setEntityManager($this); $this->metadataFactory->setCacheDriver($this->config->getMetadataCacheImpl()); + $repositoryFactory = $config->getRepositoryFactory(); + + $this->repositoryFactory = $repositoryFactory; + $this->repositoryFactory->setEntityManager($this); + $this->unitOfWork = new UnitOfWork($this); $this->proxyFactory = new ProxyFactory( $this, @@ -758,28 +763,11 @@ use Doctrine\Common\Util\ClassUtils; * * @param string $entityName The name of the entity. * - * @return EntityRepository The repository class. + * @return \Doctrine\ORM\EntityRepository The repository class. */ public function getRepository($entityName) { - $entityName = ltrim($entityName, '\\'); - - if (isset($this->repositories[$entityName])) { - return $this->repositories[$entityName]; - } - - $metadata = $this->getClassMetadata($entityName); - $repositoryClassName = $metadata->customRepositoryClassName; - - if ($repositoryClassName === null) { - $repositoryClassName = $this->config->getDefaultRepositoryClassName(); - } - - $repository = new $repositoryClassName($this, $metadata); - - $this->repositories[$entityName] = $repository; - - return $repository; + return $this->repositoryFactory->getRepository($entityName); } /** diff --git a/lib/Doctrine/ORM/Repository/DefaultRepositoryFactory.php b/lib/Doctrine/ORM/Repository/DefaultRepositoryFactory.php new file mode 100644 index 000000000..b97424810 --- /dev/null +++ b/lib/Doctrine/ORM/Repository/DefaultRepositoryFactory.php @@ -0,0 +1,89 @@ +. + */ + +namespace Doctrine\ORM\Repository; + +use Doctrine\ORM\EntityManagerInterface; + +/** + * This factory is used to create default repository objects for entities at runtime. + * + * @author Guilherme Blanco + * @since 2.5 + */ +class DefaultRepositoryFactory implements RepositoryFactory +{ + /** + * The list of EntityRepository instances. + * + * @var array<\Doctrine\ORM\EntityRepository> + */ + private $repositoryList = array(); + + /** + * @var \Doctrine\ORM\EntityManagerInterface + */ + protected $entityManager; + + /** + * {@inheritdoc} + */ + public function setEntityManager(EntityManagerInterface $entityManager) + { + $this->entityManager = $entityManager; + } + + /** + * {@inheritdoc} + */ + public function getRepository($entityName) + { + $entityName = ltrim($entityName, '\\'); + + if (isset($this->repositoryList[$entityName])) { + return $this->repositoryList[$entityName]; + } + + $repository = $this->createRepository($entityName); + + $this->repositoryList[$entityName] = $repository; + + return $repository; + } + + /** + * Create a new repository instance for an entity class. + * + * @param string $entityName The name of the entity. + * + * @return \Doctrine\ORM\EntityRepository + */ + protected function createRepository($entityName) + { + $metadata = $this->entityManager->getClassMetadata($entityName); + $repositoryClassName = $metadata->customRepositoryClassName; + + if ($repositoryClassName === null) { + $configuration = $this->entityManager->getConfiguration(); + $repositoryClassName = $configuration->getDefaultRepositoryClassName(); + } + + return new $repositoryClassName($this->entityManager, $metadata); + } +} \ No newline at end of file diff --git a/lib/Doctrine/ORM/Repository/RepositoryFactory.php b/lib/Doctrine/ORM/Repository/RepositoryFactory.php new file mode 100644 index 000000000..cd3e20a26 --- /dev/null +++ b/lib/Doctrine/ORM/Repository/RepositoryFactory.php @@ -0,0 +1,47 @@ +. + */ + +namespace Doctrine\ORM\Repository; + +use Doctrine\ORM\EntityManagerInterface; + +/** + * Interface for entity repository factory. + * + * @author Guilherme Blanco + * @since 2.5 + */ +interface RepositoryFactory +{ + /** + * Set the entity manager. + * + * @param \Doctrine\ORM\EntityManagerInterface $entityManager + */ + public function setEntityManager(EntityManagerInterface $entityManager); + + /** + * Gets the repository for an entity class. + * + * @param string $entityName The name of the entity. + * + * @return \Doctrine\ORM\EntityRepository + */ + public function getRepository($entityName); +} \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2359Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2359Test.php index b9670d27a..c1189d797 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2359Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2359Test.php @@ -19,18 +19,21 @@ class DDC2359Test extends \PHPUnit_Framework_TestCase $mockDriver = $this->getMock('Doctrine\\Common\\Persistence\\Mapping\\Driver\\MappingDriver'); $mockMetadata = $this->getMock('Doctrine\\ORM\\Mapping\\ClassMetadata', array(), array(), '', false); $entityManager = $this->getMock('Doctrine\\ORM\\EntityManager', array(), array(), '', false); + /* @var $metadataFactory \Doctrine\ORM\Mapping\ClassMetadataFactory|\PHPUnit_Framework_MockObject_MockObject */ $metadataFactory = $this->getMock( 'Doctrine\\ORM\\Mapping\\ClassMetadataFactory', array('newClassMetadataInstance', 'wakeupReflection') ); - $configuration = $this->getMock('Doctrine\\ORM\\Configuration'); + + $configuration = $this->getMock('Doctrine\\ORM\\Configuration', array('getMetadataDriverImpl')); $connection = $this->getMock('Doctrine\\DBAL\\Connection', array(), array(), '', false); $configuration ->expects($this->any()) ->method('getMetadataDriverImpl') ->will($this->returnValue($mockDriver)); + $entityManager->expects($this->any())->method('getConfiguration')->will($this->returnValue($configuration)); $entityManager->expects($this->any())->method('getConnection')->will($this->returnValue($connection)); $entityManager From 37e7e841c350ac6920ef5dc80b242659963b0868 Mon Sep 17 00:00:00 2001 From: Guilherme Blanco Date: Thu, 13 Jun 2013 23:31:18 -0400 Subject: [PATCH 164/332] Fixed wrong interface. --- lib/Doctrine/ORM/Configuration.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/Doctrine/ORM/Configuration.php b/lib/Doctrine/ORM/Configuration.php index a1535de85..073543ed9 100644 --- a/lib/Doctrine/ORM/Configuration.php +++ b/lib/Doctrine/ORM/Configuration.php @@ -34,7 +34,7 @@ use Doctrine\ORM\Mapping\EntityListenerResolver; use Doctrine\ORM\Mapping\NamingStrategy; use Doctrine\ORM\Mapping\QuoteStrategy; use Doctrine\ORM\Repository\DefaultRepositoryFactory; -use Doctrine\ORM\Repository\RepositoryFactoryInterface; +use Doctrine\ORM\Repository\RepositoryFactory; /** * Configuration container for all configuration options of Doctrine. @@ -786,9 +786,9 @@ class Configuration extends \Doctrine\DBAL\Configuration * Set the entity repository factory. * * @since 2.5 - * @param \Doctrine\ORM\Repository\RepositoryFactoryInterface $repositoryFactory + * @param \Doctrine\ORM\Repository\RepositoryFactory $repositoryFactory */ - public function setRepositoryFactory(RepositoryFactoryInterface $repositoryFactory) + public function setRepositoryFactory(RepositoryFactory $repositoryFactory) { $this->_attributes['repositoryFactory'] = $repositoryFactory; } @@ -797,7 +797,7 @@ class Configuration extends \Doctrine\DBAL\Configuration * Get the entity repository factory. * * @since 2.5 - * @return \Doctrine\ORM\Repository\RepositoryFactoryInterface + * @return \Doctrine\ORM\Repository\RepositoryFactory */ public function getRepositoryFactory() { From a66fc03441cff3f9ad2047852d1dcbfed9f48683 Mon Sep 17 00:00:00 2001 From: Guilherme Blanco Date: Thu, 13 Jun 2013 23:53:53 -0400 Subject: [PATCH 165/332] Reducing dependency on RepositoryFactory by providing EntityManager as a getRepository argument. --- lib/Doctrine/ORM/EntityManager.php | 12 +++----- .../Repository/DefaultRepositoryFactory.php | 28 ++++++------------- .../ORM/Repository/RepositoryFactory.php | 12 ++------ 3 files changed, 15 insertions(+), 37 deletions(-) diff --git a/lib/Doctrine/ORM/EntityManager.php b/lib/Doctrine/ORM/EntityManager.php index 8ee39cf06..acaec984d 100644 --- a/lib/Doctrine/ORM/EntityManager.php +++ b/lib/Doctrine/ORM/EntityManager.php @@ -161,13 +161,9 @@ use Doctrine\Common\Util\ClassUtils; $this->metadataFactory->setEntityManager($this); $this->metadataFactory->setCacheDriver($this->config->getMetadataCacheImpl()); - $repositoryFactory = $config->getRepositoryFactory(); - - $this->repositoryFactory = $repositoryFactory; - $this->repositoryFactory->setEntityManager($this); - - $this->unitOfWork = new UnitOfWork($this); - $this->proxyFactory = new ProxyFactory( + $this->repositoryFactory = $config->getRepositoryFactory(); + $this->unitOfWork = new UnitOfWork($this); + $this->proxyFactory = new ProxyFactory( $this, $config->getProxyDir(), $config->getProxyNamespace(), @@ -767,7 +763,7 @@ use Doctrine\Common\Util\ClassUtils; */ public function getRepository($entityName) { - return $this->repositoryFactory->getRepository($entityName); + return $this->repositoryFactory->getRepository($this, $entityName); } /** diff --git a/lib/Doctrine/ORM/Repository/DefaultRepositoryFactory.php b/lib/Doctrine/ORM/Repository/DefaultRepositoryFactory.php index b97424810..0caab1b63 100644 --- a/lib/Doctrine/ORM/Repository/DefaultRepositoryFactory.php +++ b/lib/Doctrine/ORM/Repository/DefaultRepositoryFactory.php @@ -36,23 +36,10 @@ class DefaultRepositoryFactory implements RepositoryFactory */ private $repositoryList = array(); - /** - * @var \Doctrine\ORM\EntityManagerInterface - */ - protected $entityManager; - /** * {@inheritdoc} */ - public function setEntityManager(EntityManagerInterface $entityManager) - { - $this->entityManager = $entityManager; - } - - /** - * {@inheritdoc} - */ - public function getRepository($entityName) + public function getRepository(EntityManagerInterface $entityManager, $entityName) { $entityName = ltrim($entityName, '\\'); @@ -60,7 +47,7 @@ class DefaultRepositoryFactory implements RepositoryFactory return $this->repositoryList[$entityName]; } - $repository = $this->createRepository($entityName); + $repository = $this->createRepository($entityManager, $entityName); $this->repositoryList[$entityName] = $repository; @@ -70,20 +57,21 @@ class DefaultRepositoryFactory implements RepositoryFactory /** * Create a new repository instance for an entity class. * - * @param string $entityName The name of the entity. + * @param \Doctrine\ORM\EntityManagerInterface $entityManager The EntityManager instance. + * @param string $entityName The name of the entity. * * @return \Doctrine\ORM\EntityRepository */ - protected function createRepository($entityName) + protected function createRepository(EntityManagerInterface $entityManager, $entityName) { - $metadata = $this->entityManager->getClassMetadata($entityName); + $metadata = $entityManager->getClassMetadata($entityName); $repositoryClassName = $metadata->customRepositoryClassName; if ($repositoryClassName === null) { - $configuration = $this->entityManager->getConfiguration(); + $configuration = $entityManager->getConfiguration(); $repositoryClassName = $configuration->getDefaultRepositoryClassName(); } - return new $repositoryClassName($this->entityManager, $metadata); + return new $repositoryClassName($entityManager, $metadata); } } \ No newline at end of file diff --git a/lib/Doctrine/ORM/Repository/RepositoryFactory.php b/lib/Doctrine/ORM/Repository/RepositoryFactory.php index cd3e20a26..faf7d6c81 100644 --- a/lib/Doctrine/ORM/Repository/RepositoryFactory.php +++ b/lib/Doctrine/ORM/Repository/RepositoryFactory.php @@ -29,19 +29,13 @@ use Doctrine\ORM\EntityManagerInterface; */ interface RepositoryFactory { - /** - * Set the entity manager. - * - * @param \Doctrine\ORM\EntityManagerInterface $entityManager - */ - public function setEntityManager(EntityManagerInterface $entityManager); - /** * Gets the repository for an entity class. * - * @param string $entityName The name of the entity. + * @param \Doctrine\ORM\EntityManagerInterface $entityManager The EntityManager instance. + * @param string $entityName The name of the entity. * * @return \Doctrine\ORM\EntityRepository */ - public function getRepository($entityName); + public function getRepository(EntityManagerInterface $entityManager, $entityName); } \ No newline at end of file From 3488049c18849a0cb942f4fea00501fa11727a83 Mon Sep 17 00:00:00 2001 From: Guilherme Blanco Date: Thu, 13 Jun 2013 23:59:08 -0400 Subject: [PATCH 166/332] Reduced granularity of DefaultRepositoryFactory reference to ObjectRepository instances, in cases where consumers are completely rewrote EntityRepository. --- lib/Doctrine/ORM/Repository/DefaultRepositoryFactory.php | 4 ++-- lib/Doctrine/ORM/Repository/RepositoryFactory.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Doctrine/ORM/Repository/DefaultRepositoryFactory.php b/lib/Doctrine/ORM/Repository/DefaultRepositoryFactory.php index 0caab1b63..932a1108c 100644 --- a/lib/Doctrine/ORM/Repository/DefaultRepositoryFactory.php +++ b/lib/Doctrine/ORM/Repository/DefaultRepositoryFactory.php @@ -32,7 +32,7 @@ class DefaultRepositoryFactory implements RepositoryFactory /** * The list of EntityRepository instances. * - * @var array<\Doctrine\ORM\EntityRepository> + * @var array<\Doctrine\Common\Persistence\ObjectRepository> */ private $repositoryList = array(); @@ -60,7 +60,7 @@ class DefaultRepositoryFactory implements RepositoryFactory * @param \Doctrine\ORM\EntityManagerInterface $entityManager The EntityManager instance. * @param string $entityName The name of the entity. * - * @return \Doctrine\ORM\EntityRepository + * @return \Doctrine\Common\Persistence\ObjectRepository */ protected function createRepository(EntityManagerInterface $entityManager, $entityName) { diff --git a/lib/Doctrine/ORM/Repository/RepositoryFactory.php b/lib/Doctrine/ORM/Repository/RepositoryFactory.php index faf7d6c81..00efca2c7 100644 --- a/lib/Doctrine/ORM/Repository/RepositoryFactory.php +++ b/lib/Doctrine/ORM/Repository/RepositoryFactory.php @@ -35,7 +35,7 @@ interface RepositoryFactory * @param \Doctrine\ORM\EntityManagerInterface $entityManager The EntityManager instance. * @param string $entityName The name of the entity. * - * @return \Doctrine\ORM\EntityRepository + * @return \Doctrine\Common\Persistence\ObjectRepository */ public function getRepository(EntityManagerInterface $entityManager, $entityName); } \ No newline at end of file From 37d7df6ac4d4501370487705bd02906983470158 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Perrin?= Date: Fri, 14 Jun 2013 11:00:17 +0300 Subject: [PATCH 167/332] Fix phpDoc syntax in ClassMetadataInfo.php --- lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php index da2b1caa6..3d42b24e6 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php @@ -358,7 +358,7 @@ class ClassMetadataInfo implements ClassMetadata * - scale (integer, optional, schema-only) * The scale of a decimal column. Only valid if the column type is decimal. * - [* - 'unique'] (string, optional, schema-only) + * - 'unique' (string, optional, schema-only) * Whether a unique constraint should be generated for the column. * * @var array From 73e2aa54ef72d8278e3a9f0a968b18b39d685a46 Mon Sep 17 00:00:00 2001 From: Adam Prager Date: Fri, 14 Jun 2013 10:07:05 +0200 Subject: [PATCH 168/332] moved php version check --- lib/Doctrine/ORM/Tools/EntityGenerator.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/Doctrine/ORM/Tools/EntityGenerator.php b/lib/Doctrine/ORM/Tools/EntityGenerator.php index 1c55eb1bc..42a6e85c9 100644 --- a/lib/Doctrine/ORM/Tools/EntityGenerator.php +++ b/lib/Doctrine/ORM/Tools/EntityGenerator.php @@ -759,14 +759,12 @@ public function __construct() */ protected function getTraits(ClassMetadataInfo $metadata) { - if ($metadata->reflClass !== null || class_exists($metadata->name)) { + if (PHP_VERSION_ID >= 50400 && ($metadata->reflClass !== null || class_exists($metadata->name))) { $reflClass = $metadata->reflClass === null ? new \ReflectionClass($metadata->name) : $metadata->reflClass; - if (PHP_VERSION_ID >= 50400) { - return $reflClass->getTraits(); - } + return $reflClass->getTraits(); } return array(); From 52b3fc1fc3b1adfe17290126712916196f5b5345 Mon Sep 17 00:00:00 2001 From: Guilherme Blanco Date: Fri, 14 Jun 2013 12:07:28 -0400 Subject: [PATCH 169/332] Updated since php doc tag. --- lib/Doctrine/ORM/Configuration.php | 4 ++-- lib/Doctrine/ORM/Repository/DefaultRepositoryFactory.php | 2 +- lib/Doctrine/ORM/Repository/RepositoryFactory.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/Doctrine/ORM/Configuration.php b/lib/Doctrine/ORM/Configuration.php index 073543ed9..3a096ba21 100644 --- a/lib/Doctrine/ORM/Configuration.php +++ b/lib/Doctrine/ORM/Configuration.php @@ -785,7 +785,7 @@ class Configuration extends \Doctrine\DBAL\Configuration /** * Set the entity repository factory. * - * @since 2.5 + * @since 2.4 * @param \Doctrine\ORM\Repository\RepositoryFactory $repositoryFactory */ public function setRepositoryFactory(RepositoryFactory $repositoryFactory) @@ -796,7 +796,7 @@ class Configuration extends \Doctrine\DBAL\Configuration /** * Get the entity repository factory. * - * @since 2.5 + * @since 2.4 * @return \Doctrine\ORM\Repository\RepositoryFactory */ public function getRepositoryFactory() diff --git a/lib/Doctrine/ORM/Repository/DefaultRepositoryFactory.php b/lib/Doctrine/ORM/Repository/DefaultRepositoryFactory.php index 932a1108c..2774dea68 100644 --- a/lib/Doctrine/ORM/Repository/DefaultRepositoryFactory.php +++ b/lib/Doctrine/ORM/Repository/DefaultRepositoryFactory.php @@ -25,7 +25,7 @@ use Doctrine\ORM\EntityManagerInterface; * This factory is used to create default repository objects for entities at runtime. * * @author Guilherme Blanco - * @since 2.5 + * @since 2.4 */ class DefaultRepositoryFactory implements RepositoryFactory { diff --git a/lib/Doctrine/ORM/Repository/RepositoryFactory.php b/lib/Doctrine/ORM/Repository/RepositoryFactory.php index 00efca2c7..f3af43ebe 100644 --- a/lib/Doctrine/ORM/Repository/RepositoryFactory.php +++ b/lib/Doctrine/ORM/Repository/RepositoryFactory.php @@ -25,7 +25,7 @@ use Doctrine\ORM\EntityManagerInterface; * Interface for entity repository factory. * * @author Guilherme Blanco - * @since 2.5 + * @since 2.4 */ interface RepositoryFactory { From 184e8eb26cc8b449e8bfdb9b365bbc5ae3e2d3fb Mon Sep 17 00:00:00 2001 From: Robert Broen Date: Mon, 17 Jun 2013 10:31:06 +0200 Subject: [PATCH 170/332] Update getting-started.rst The tutorial assumes Doctrine 2.4 --- docs/en/tutorials/getting-started.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/tutorials/getting-started.rst b/docs/en/tutorials/getting-started.rst index 187029dfa..8c51b6480 100644 --- a/docs/en/tutorials/getting-started.rst +++ b/docs/en/tutorials/getting-started.rst @@ -25,7 +25,7 @@ The code of this tutorial is `available on Github Date: Mon, 17 Jun 2013 11:36:09 +0200 Subject: [PATCH 171/332] list_bugs.php needs to call to getters for protected vars list_bugs.php needs to call to getters for protected vars. This was changed in the "getting-started" code repository, but not in the "getting-started" tutorial. --- docs/en/tutorials/getting-started.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/en/tutorials/getting-started.rst b/docs/en/tutorials/getting-started.rst index 8c51b6480..4f169cd94 100644 --- a/docs/en/tutorials/getting-started.rst +++ b/docs/en/tutorials/getting-started.rst @@ -1108,10 +1108,10 @@ the first read-only use-case: foreach($bugs AS $bug) { echo $bug->getDescription()." - ".$bug->getCreated()->format('d.m.Y')."\n"; - echo " Reported by: ".$bug->getReporter()->name."\n"; - echo " Assigned to: ".$bug->getEngineer()->name."\n"; + echo " Reported by: ".$bug->getReporter()->getName()."\n"; + echo " Assigned to: ".$bug->getEngineer()->getName()."\n"; foreach($bug->getProducts() AS $product) { - echo " Platform: ".$product->name."\n"; + echo " Platform: ".$product->getName()."\n"; } echo "\n"; } From 3340234785351ef4680421f5c07f5706bd1524e0 Mon Sep 17 00:00:00 2001 From: shulcsm Date: Wed, 19 Jun 2013 16:34:44 +0300 Subject: [PATCH 172/332] Clear visitedCollections Visited collections are cleared only in commit(). Commit clears up only if it actually has something to do. Processing large amounts of records without changing them cause visitedCollections to grow without any way of clearing. --- lib/Doctrine/ORM/UnitOfWork.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index 68fde5d2c..9a8363863 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -2356,6 +2356,7 @@ class UnitOfWork implements PropertyChangedListener $this->collectionUpdates = $this->extraUpdates = $this->readOnlyObjects = + $this->visitedCollections = $this->orphanRemovals = array(); if ($this->commitOrderCalculator !== null) { From c743bb938b30efafefe4932b01b6f63f95fe2c87 Mon Sep 17 00:00:00 2001 From: Giulio De Donato Date: Wed, 19 Jun 2013 16:40:57 +0200 Subject: [PATCH 173/332] added badges --- README.markdown | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.markdown b/README.markdown index 64f7189a3..39bde0285 100644 --- a/README.markdown +++ b/README.markdown @@ -5,6 +5,9 @@ Master: [![Build Status](https://secure.travis-ci.org/doctrine/doctrine2.png?bra 2.2: [![Build Status](https://secure.travis-ci.org/doctrine/doctrine2.png?branch=2.2)](http://travis-ci.org/doctrine/doctrine2) 2.1: [![Build Status](https://secure.travis-ci.org/doctrine/doctrine2.png?branch=2.1.x)](http://travis-ci.org/doctrine/doctrine2) +[![Latest Stable Version](https://poser.pugx.org/doctrine/orm/v/stable.png)](https://packagist.org/packages/doctrine/orm) [![Total Downloads](https://poser.pugx.org/doctrine/orm/downloads.png)](https://packagist.org/packages/doctrine/orm) + + Doctrine 2 is an object-relational mapper (ORM) for PHP 5.3.2+ that provides transparent persistence for PHP objects. It sits on top of a powerful database abstraction layer (DBAL). One of its key features is the option to write database queries in a proprietary object oriented SQL dialect called Doctrine Query Language (DQL), From 523697d0b663221bc91d39c93b4317d164726816 Mon Sep 17 00:00:00 2001 From: Sander Marechal Date: Thu, 20 Jun 2013 09:29:56 +0200 Subject: [PATCH 174/332] [DDC-1398] Extra-lazy get for indexed associations If an association is EXTRA_LAZY and has an indexBy, then you can call get() without loading the entire collection. --- lib/Doctrine/ORM/PersistentCollection.php | 8 ++++ .../ORM/Persisters/ManyToManyPersister.php | 18 ++++++++ .../ORM/Persisters/OneToManyPersister.php | 18 ++++++++ .../Functional/ExtraLazyCollectionTest.php | 41 ++++++++++++++++++- 4 files changed, 83 insertions(+), 2 deletions(-) diff --git a/lib/Doctrine/ORM/PersistentCollection.php b/lib/Doctrine/ORM/PersistentCollection.php index 53ba1c1f2..d5a922aaf 100644 --- a/lib/Doctrine/ORM/PersistentCollection.php +++ b/lib/Doctrine/ORM/PersistentCollection.php @@ -517,6 +517,14 @@ final class PersistentCollection implements Collection, Selectable */ public function get($key) { + if ( ! $this->initialized + && $this->association['fetch'] === Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY + && isset($this->association['indexBy']) + ) { + $persister = $this->em->getUnitOfWork()->getCollectionPersister($this->association); + return $persister->get($this, $key); + } + $this->initialize(); return $this->coll->get($key); diff --git a/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php b/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php index d9f7e30ca..4ad32e8d8 100644 --- a/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php +++ b/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php @@ -33,6 +33,24 @@ use Doctrine\ORM\UnitOfWork; */ class ManyToManyPersister extends AbstractCollectionPersister { + /** + * {@inheritdoc} + * + * @override + */ + public function get(PersistentCollection $coll, $index) + { + $mapping = $coll->getMapping(); + $uow = $this->em->getUnitOfWork(); + $persister = $uow->getEntityPersister($mapping['targetEntity']); + + if (!isset($mapping['indexBy'])) { + throw new \BadMethodCallException("Selecting a collection by index is only supported on indexed collections."); + } + + return current($persister->load(array($mapping['indexBy'] => $index), null, null, array(), 0, 1)); + } + /** * {@inheritdoc} * diff --git a/lib/Doctrine/ORM/Persisters/OneToManyPersister.php b/lib/Doctrine/ORM/Persisters/OneToManyPersister.php index 911d699f6..041dfe5e2 100644 --- a/lib/Doctrine/ORM/Persisters/OneToManyPersister.php +++ b/lib/Doctrine/ORM/Persisters/OneToManyPersister.php @@ -32,6 +32,24 @@ use Doctrine\ORM\UnitOfWork; */ class OneToManyPersister extends AbstractCollectionPersister { + /** + * {@inheritdoc} + * + * @override + */ + public function get(PersistentCollection $coll, $index) + { + $mapping = $coll->getMapping(); + $uow = $this->em->getUnitOfWork(); + $persister = $uow->getEntityPersister($mapping['targetEntity']); + + if (!isset($mapping['indexBy'])) { + throw new \BadMethodCallException("Selecting a collection by index is only supported on indexed collections."); + } + + return current($persister->load(array($mapping['indexBy'] => $index), null, null, array(), 0, 1)); + } + /** * Generates the SQL UPDATE that updates a particular row's foreign * key to null. diff --git a/tests/Doctrine/Tests/ORM/Functional/ExtraLazyCollectionTest.php b/tests/Doctrine/Tests/ORM/Functional/ExtraLazyCollectionTest.php index b6c7a0f1e..aa6adb13a 100644 --- a/tests/Doctrine/Tests/ORM/Functional/ExtraLazyCollectionTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/ExtraLazyCollectionTest.php @@ -24,7 +24,9 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase $class = $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'); $class->associationMappings['groups']['fetch'] = ClassMetadataInfo::FETCH_EXTRA_LAZY; + $class->associationMappings['groups']['indexBy'] = 'id'; $class->associationMappings['articles']['fetch'] = ClassMetadataInfo::FETCH_EXTRA_LAZY; + $class->associationMappings['articles']['indexBy'] = 'id'; $class = $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsGroup'); $class->associationMappings['users']['fetch'] = ClassMetadataInfo::FETCH_EXTRA_LAZY; @@ -40,6 +42,9 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase $class->associationMappings['groups']['fetch'] = ClassMetadataInfo::FETCH_LAZY; $class->associationMappings['articles']['fetch'] = ClassMetadataInfo::FETCH_LAZY; + unset($class->associationMappings['groups']['indexBy']); + unset($class->associationMappings['articles']['indexBy']); + $class = $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsGroup'); $class->associationMappings['users']['fetch'] = ClassMetadataInfo::FETCH_LAZY; } @@ -174,8 +179,8 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertEquals($queryCount + 1, $this->getCurrentQueryCount()); $this->assertEquals(2, count($someGroups)); - $this->assertTrue($user->groups->contains($someGroups[0])); - $this->assertTrue($user->groups->contains($someGroups[1])); + $this->assertTrue($user->groups->contains(array_shift($someGroups))); + $this->assertTrue($user->groups->contains(array_shift($someGroups))); } /** @@ -512,6 +517,38 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertEquals($qc + 1, $this->getCurrentQueryCount()); } + /** + * @group DDC-1398 + */ + public function testGetIndexByOneToMany() + { + $user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId); + /* @var $user CmsUser */ + + $queryCount = $this->getCurrentQueryCount(); + + $user->articles->get($this->articleId); + + $this->assertFalse($user->articles->isInitialized()); + $this->assertEquals($queryCount + 1, $this->getCurrentQueryCount()); + } + + /** + * @group DDC-1398 + */ + public function testGetIndexByManyToMany() + { + $user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId); + /* @var $user CmsUser */ + + $queryCount = $this->getCurrentQueryCount(); + + $user->groups->get($this->groupId); + + $this->assertFalse($user->groups->isInitialized()); + $this->assertEquals($queryCount + 1, $this->getCurrentQueryCount()); + } + private function loadFixture() { $user1 = new \Doctrine\Tests\Models\CMS\CmsUser(); From 3555007f08c94435921d3c1ca2ad46e38a82ba2b Mon Sep 17 00:00:00 2001 From: Sander Marechal Date: Thu, 20 Jun 2013 10:09:52 +0200 Subject: [PATCH 175/332] Return NULL for non-existent keys The load() function already returns just one entity or NULL, so the current() is not needed and the result can be returned directly. --- lib/Doctrine/ORM/Persisters/ManyToManyPersister.php | 2 +- lib/Doctrine/ORM/Persisters/OneToManyPersister.php | 2 +- .../Tests/ORM/Functional/ExtraLazyCollectionTest.php | 12 ++++++++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php b/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php index 4ad32e8d8..df46aa30a 100644 --- a/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php +++ b/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php @@ -48,7 +48,7 @@ class ManyToManyPersister extends AbstractCollectionPersister throw new \BadMethodCallException("Selecting a collection by index is only supported on indexed collections."); } - return current($persister->load(array($mapping['indexBy'] => $index), null, null, array(), 0, 1)); + return $persister->load(array($mapping['indexBy'] => $index), null, null, array(), 0, 1); } /** diff --git a/lib/Doctrine/ORM/Persisters/OneToManyPersister.php b/lib/Doctrine/ORM/Persisters/OneToManyPersister.php index 041dfe5e2..120ad548d 100644 --- a/lib/Doctrine/ORM/Persisters/OneToManyPersister.php +++ b/lib/Doctrine/ORM/Persisters/OneToManyPersister.php @@ -47,7 +47,7 @@ class OneToManyPersister extends AbstractCollectionPersister throw new \BadMethodCallException("Selecting a collection by index is only supported on indexed collections."); } - return current($persister->load(array($mapping['indexBy'] => $index), null, null, array(), 0, 1)); + return $persister->load(array($mapping['indexBy'] => $index), null, null, array(), 0, 1); } /** diff --git a/tests/Doctrine/Tests/ORM/Functional/ExtraLazyCollectionTest.php b/tests/Doctrine/Tests/ORM/Functional/ExtraLazyCollectionTest.php index aa6adb13a..fe642ac1b 100644 --- a/tests/Doctrine/Tests/ORM/Functional/ExtraLazyCollectionTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/ExtraLazyCollectionTest.php @@ -549,6 +549,18 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertEquals($queryCount + 1, $this->getCurrentQueryCount()); } + /** + * @group DDC-1398 + */ + public function testGetNonExistentIndexBy() + { + $user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId); + /* @var $user CmsUser */ + + $this->assertNull($user->articles->get(-1)); + $this->assertNull($user->groups->get(-1)); + } + private function loadFixture() { $user1 = new \Doctrine\Tests\Models\CMS\CmsUser(); From 647c5e2cad135abb9158a538b4e3f7758f8b71ed Mon Sep 17 00:00:00 2001 From: Sander Marechal Date: Thu, 20 Jun 2013 10:18:08 +0200 Subject: [PATCH 176/332] Test actual data returned by get() --- .../Tests/ORM/Functional/ExtraLazyCollectionTest.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/Doctrine/Tests/ORM/Functional/ExtraLazyCollectionTest.php b/tests/Doctrine/Tests/ORM/Functional/ExtraLazyCollectionTest.php index fe642ac1b..b1a2f7fd4 100644 --- a/tests/Doctrine/Tests/ORM/Functional/ExtraLazyCollectionTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/ExtraLazyCollectionTest.php @@ -527,10 +527,11 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase $queryCount = $this->getCurrentQueryCount(); - $user->articles->get($this->articleId); + $article = $user->articles->get($this->articleId); $this->assertFalse($user->articles->isInitialized()); $this->assertEquals($queryCount + 1, $this->getCurrentQueryCount()); + $this->assertSame($article, $this->_em->find('Doctrine\Tests\Models\CMS\CmsArticle', $this->articleId)); } /** @@ -543,10 +544,11 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase $queryCount = $this->getCurrentQueryCount(); - $user->groups->get($this->groupId); + $group = $user->groups->get($this->groupId); $this->assertFalse($user->groups->isInitialized()); $this->assertEquals($queryCount + 1, $this->getCurrentQueryCount()); + $this->assertSame($group, $this->_em->find('Doctrine\Tests\Models\CMS\CmsGroup', $this->groupId)); } /** From 53c9ffda302af24e137607b244591b79419e6116 Mon Sep 17 00:00:00 2001 From: Sander Marechal Date: Thu, 20 Jun 2013 10:20:16 +0200 Subject: [PATCH 177/332] Get rid of variable --- lib/Doctrine/ORM/PersistentCollection.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/Doctrine/ORM/PersistentCollection.php b/lib/Doctrine/ORM/PersistentCollection.php index d5a922aaf..dd5a90d18 100644 --- a/lib/Doctrine/ORM/PersistentCollection.php +++ b/lib/Doctrine/ORM/PersistentCollection.php @@ -521,8 +521,7 @@ final class PersistentCollection implements Collection, Selectable && $this->association['fetch'] === Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY && isset($this->association['indexBy']) ) { - $persister = $this->em->getUnitOfWork()->getCollectionPersister($this->association); - return $persister->get($this, $key); + return $this->em->getUnitOfWork()->getCollectionPersister($this->association)->get($this, $key); } $this->initialize(); From 3b92cfac5a7ae7fdc853b819f3d12505f2fe1c2f Mon Sep 17 00:00:00 2001 From: Sander Marechal Date: Thu, 20 Jun 2013 13:45:38 +0200 Subject: [PATCH 178/332] Use find() if the indexBy field is the identifier --- lib/Doctrine/ORM/PersistentCollection.php | 6 ++++++ .../Tests/ORM/Functional/ExtraLazyCollectionTest.php | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/lib/Doctrine/ORM/PersistentCollection.php b/lib/Doctrine/ORM/PersistentCollection.php index dd5a90d18..4e64acb26 100644 --- a/lib/Doctrine/ORM/PersistentCollection.php +++ b/lib/Doctrine/ORM/PersistentCollection.php @@ -521,6 +521,12 @@ final class PersistentCollection implements Collection, Selectable && $this->association['fetch'] === Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY && isset($this->association['indexBy']) ) { + $class = $this->em->getClassMetadata($this->association['targetEntity']); + + if (!$class->isIdentifierComposite && $class->isIdentifier($this->association['indexBy'])) { + return $this->em->find($class->name, $key); + } + return $this->em->getUnitOfWork()->getCollectionPersister($this->association)->get($this, $key); } diff --git a/tests/Doctrine/Tests/ORM/Functional/ExtraLazyCollectionTest.php b/tests/Doctrine/Tests/ORM/Functional/ExtraLazyCollectionTest.php index b1a2f7fd4..cbafd1132 100644 --- a/tests/Doctrine/Tests/ORM/Functional/ExtraLazyCollectionTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/ExtraLazyCollectionTest.php @@ -532,6 +532,9 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertFalse($user->articles->isInitialized()); $this->assertEquals($queryCount + 1, $this->getCurrentQueryCount()); $this->assertSame($article, $this->_em->find('Doctrine\Tests\Models\CMS\CmsArticle', $this->articleId)); + + $article = $user->articles->get($this->articleId); + $this->assertEquals($queryCount + 1, $this->getCurrentQueryCount(), "Getting the same entity should not cause an extra query to be executed"); } /** @@ -549,6 +552,9 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertFalse($user->groups->isInitialized()); $this->assertEquals($queryCount + 1, $this->getCurrentQueryCount()); $this->assertSame($group, $this->_em->find('Doctrine\Tests\Models\CMS\CmsGroup', $this->groupId)); + + $group = $user->groups->get($this->groupId); + $this->assertEquals($queryCount + 1, $this->getCurrentQueryCount(), "Getting the same entity should not cause an extra query to be executed"); } /** From 28791620157c413e9dce23663a064eeb012061e3 Mon Sep 17 00:00:00 2001 From: Sander Marechal Date: Thu, 20 Jun 2013 14:00:58 +0200 Subject: [PATCH 179/332] No need to lookup metadata --- lib/Doctrine/ORM/PersistentCollection.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/Doctrine/ORM/PersistentCollection.php b/lib/Doctrine/ORM/PersistentCollection.php index 4e64acb26..a50cd8343 100644 --- a/lib/Doctrine/ORM/PersistentCollection.php +++ b/lib/Doctrine/ORM/PersistentCollection.php @@ -521,10 +521,8 @@ final class PersistentCollection implements Collection, Selectable && $this->association['fetch'] === Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY && isset($this->association['indexBy']) ) { - $class = $this->em->getClassMetadata($this->association['targetEntity']); - - if (!$class->isIdentifierComposite && $class->isIdentifier($this->association['indexBy'])) { - return $this->em->find($class->name, $key); + if (!$this->typeClass->isIdentifierComposite && $this->typeClass->isIdentifier($this->association['indexBy'])) { + return $this->em->find($this->typeClass->name, $key); } return $this->em->getUnitOfWork()->getCollectionPersister($this->association)->get($this, $key); From 70427871ce73090441ea8c0ed635efe6782a48c3 Mon Sep 17 00:00:00 2001 From: Sander Marechal Date: Thu, 20 Jun 2013 14:20:00 +0200 Subject: [PATCH 180/332] Extra test for indexBy identifier versus indexBy other fields --- .../Functional/ExtraLazyCollectionTest.php | 63 ++++++++++++++----- 1 file changed, 49 insertions(+), 14 deletions(-) diff --git a/tests/Doctrine/Tests/ORM/Functional/ExtraLazyCollectionTest.php b/tests/Doctrine/Tests/ORM/Functional/ExtraLazyCollectionTest.php index cbafd1132..5d8fa0f2a 100644 --- a/tests/Doctrine/Tests/ORM/Functional/ExtraLazyCollectionTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/ExtraLazyCollectionTest.php @@ -17,6 +17,10 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase private $groupId; private $articleId; + private $groupname; + private $topic; + private $phonenumber; + public function setUp() { $this->useModelSet('cms'); @@ -24,9 +28,11 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase $class = $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'); $class->associationMappings['groups']['fetch'] = ClassMetadataInfo::FETCH_EXTRA_LAZY; - $class->associationMappings['groups']['indexBy'] = 'id'; + $class->associationMappings['groups']['indexBy'] = 'name'; $class->associationMappings['articles']['fetch'] = ClassMetadataInfo::FETCH_EXTRA_LAZY; - $class->associationMappings['articles']['indexBy'] = 'id'; + $class->associationMappings['articles']['indexBy'] = 'topic'; + $class->associationMappings['phonenumbers']['fetch'] = ClassMetadataInfo::FETCH_EXTRA_LAZY; + $class->associationMappings['phonenumbers']['indexBy'] = 'phonenumber'; $class = $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsGroup'); $class->associationMappings['users']['fetch'] = ClassMetadataInfo::FETCH_EXTRA_LAZY; @@ -41,9 +47,11 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase $class = $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'); $class->associationMappings['groups']['fetch'] = ClassMetadataInfo::FETCH_LAZY; $class->associationMappings['articles']['fetch'] = ClassMetadataInfo::FETCH_LAZY; + $class->associationMappings['phonenumbers']['fetch'] = ClassMetadataInfo::FETCH_LAZY; unset($class->associationMappings['groups']['indexBy']); unset($class->associationMappings['articles']['indexBy']); + unset($class->associationMappings['phonenumbers']['indexBy']); $class = $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsGroup'); $class->associationMappings['users']['fetch'] = ClassMetadataInfo::FETCH_LAZY; @@ -517,6 +525,26 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertEquals($qc + 1, $this->getCurrentQueryCount()); } + /** + * @group DDC-1398 + */ + public function testGetIndexByIdentifier() + { + $user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId); + /* @var $user CmsUser */ + + $queryCount = $this->getCurrentQueryCount(); + + $phonenumber = $user->phonenumbers->get($this->phonenumber); + + $this->assertFalse($user->phonenumbers->isInitialized()); + $this->assertEquals($queryCount + 1, $this->getCurrentQueryCount()); + $this->assertSame($phonenumber, $this->_em->find('Doctrine\Tests\Models\CMS\CmsPhonenumber', $this->phonenumber)); + + $article = $user->phonenumbers->get($this->phonenumber); + $this->assertEquals($queryCount + 1, $this->getCurrentQueryCount(), "Getting the same entity should not cause an extra query to be executed"); + } + /** * @group DDC-1398 */ @@ -527,14 +555,11 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase $queryCount = $this->getCurrentQueryCount(); - $article = $user->articles->get($this->articleId); + $article = $user->articles->get($this->topic); $this->assertFalse($user->articles->isInitialized()); $this->assertEquals($queryCount + 1, $this->getCurrentQueryCount()); $this->assertSame($article, $this->_em->find('Doctrine\Tests\Models\CMS\CmsArticle', $this->articleId)); - - $article = $user->articles->get($this->articleId); - $this->assertEquals($queryCount + 1, $this->getCurrentQueryCount(), "Getting the same entity should not cause an extra query to be executed"); } /** @@ -547,14 +572,11 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase $queryCount = $this->getCurrentQueryCount(); - $group = $user->groups->get($this->groupId); + $group = $user->groups->get($this->groupname); $this->assertFalse($user->groups->isInitialized()); $this->assertEquals($queryCount + 1, $this->getCurrentQueryCount()); $this->assertSame($group, $this->_em->find('Doctrine\Tests\Models\CMS\CmsGroup', $this->groupId)); - - $group = $user->groups->get($this->groupId); - $this->assertEquals($queryCount + 1, $this->getCurrentQueryCount(), "Getting the same entity should not cause an extra query to be executed"); } /** @@ -618,23 +640,36 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->_em->persist($group3); $article1 = new \Doctrine\Tests\Models\CMS\CmsArticle(); - $article1->topic = "Test"; - $article1->text = "Test"; + $article1->topic = "Test1"; + $article1->text = "Test1"; $article1->setAuthor($user1); $article2 = new \Doctrine\Tests\Models\CMS\CmsArticle(); - $article2->topic = "Test"; - $article2->text = "Test"; + $article2->topic = "Test2"; + $article2->text = "Test2"; $article2->setAuthor($user1); $this->_em->persist($article1); $this->_em->persist($article2); + $phonenumber1 = new \Doctrine\Tests\Models\CMS\CmsPhonenumber(); + $phonenumber1->phonenumber = '12345'; + + $phonenumber2 = new \Doctrine\Tests\Models\CMS\CmsPhonenumber(); + $phonenumber2->phonenumber = '67890'; + + $this->_em->persist($phonenumber1); + $this->_em->persist($phonenumber2); + $this->_em->flush(); $this->_em->clear(); $this->articleId = $article1->id; $this->userId = $user1->getId(); $this->groupId = $group1->id; + + $this->groupname = $group1->name; + $this->topic = $article1->topic; + $this->phonenumber = $phonenumber1->phonenumber; } } \ No newline at end of file From a165f63c8c44bbf1375f9b2aa7ae8a0551ba3622 Mon Sep 17 00:00:00 2001 From: "Fabio B. Silva" Date: Sun, 16 Jun 2013 14:55:01 -0400 Subject: [PATCH 181/332] ANSI compliant quote strategy --- .../ORM/Mapping/AnsiQuoteStrategy.php | 97 ++++++++++++ .../ORM/Mapping/AnsiQuoteStrategyTest.php | 148 ++++++++++++++++++ 2 files changed, 245 insertions(+) create mode 100644 lib/Doctrine/ORM/Mapping/AnsiQuoteStrategy.php create mode 100644 tests/Doctrine/Tests/ORM/Mapping/AnsiQuoteStrategyTest.php diff --git a/lib/Doctrine/ORM/Mapping/AnsiQuoteStrategy.php b/lib/Doctrine/ORM/Mapping/AnsiQuoteStrategy.php new file mode 100644 index 000000000..440c3909f --- /dev/null +++ b/lib/Doctrine/ORM/Mapping/AnsiQuoteStrategy.php @@ -0,0 +1,97 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * ANSI compliant quote strategy, this strategy does not apply any quote. + * To use this strategy all mapped tables and columns should be ANSI compliant. + * + * @since 2.5 + * @author Fabio B. Silva + */ +class AnsiQuoteStrategy implements QuoteStrategy +{ + /** + * {@inheritdoc} + */ + public function getColumnName($fieldName, ClassMetadata $class, AbstractPlatform $platform) + { + return $class->fieldMappings[$fieldName]['columnName']; + } + + /** + * {@inheritdoc} + */ + public function getTableName(ClassMetadata $class, AbstractPlatform $platform) + { + return $class->table['name']; + } + + /** + * {@inheritdoc} + */ + public function getSequenceName(array $definition, ClassMetadata $class, AbstractPlatform $platform) + { + return $definition['sequenceName']; + } + + /** + * {@inheritdoc} + */ + public function getJoinColumnName(array $joinColumn, ClassMetadata $class, AbstractPlatform $platform) + { + return $joinColumn['name']; + } + + /** + * {@inheritdoc} + */ + public function getReferencedJoinColumnName(array $joinColumn, ClassMetadata $class, AbstractPlatform $platform) + { + return $joinColumn['referencedColumnName']; + } + + /** + * {@inheritdoc} + */ + public function getJoinTableName(array $association, ClassMetadata $class, AbstractPlatform $platform) + { + return $association['joinTable']['name']; + } + + /** + * {@inheritdoc} + */ + public function getIdentifierColumnNames(ClassMetadata $class, AbstractPlatform $platform) + { + return $class->identifier; + } + + /** + * {@inheritdoc} + */ + public function getColumnAlias($columnName, $counter, AbstractPlatform $platform, ClassMetadata $class = null) + { + return $platform->getSQLResultCasing($columnName . $counter); + } +} diff --git a/tests/Doctrine/Tests/ORM/Mapping/AnsiQuoteStrategyTest.php b/tests/Doctrine/Tests/ORM/Mapping/AnsiQuoteStrategyTest.php new file mode 100644 index 000000000..5c51eb034 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Mapping/AnsiQuoteStrategyTest.php @@ -0,0 +1,148 @@ +_getTestEntityManager(); + $this->platform = $em->getConnection()->getDatabasePlatform(); + $this->strategy = new AnsiQuoteStrategy(); + } + + /** + * @param string $className + * @return \Doctrine\ORM\Mapping\ClassMetadata + */ + private function createClassMetadata($className) + { + $class = new ClassMetadata($className); + $class->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService); + + return $class; + } + + public function testGetColumnName() + { + $class = $this->createClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'); + $class->mapField(array('fieldName' => 'name', 'columnName' => 'name')); + $class->mapField(array('fieldName' => 'id', 'columnName' => 'id', 'id' => true)); + + $this->assertEquals('id' ,$this->strategy->getColumnName('id', $class, $this->platform)); + $this->assertEquals('name' ,$this->strategy->getColumnName('name', $class, $this->platform)); + } + + public function testGetTableName() + { + $class = $this->createClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'); + + $class->setPrimaryTable(array('name'=>'cms_user')); + $this->assertEquals('cms_user' ,$this->strategy->getTableName($class, $this->platform)); + } + + public function testJoinTableName() + { + $class = $this->createClassMetadata('Doctrine\Tests\Models\CMS\CmsAddress'); + + $class->mapManyToMany(array( + 'fieldName' => 'user', + 'targetEntity' => 'CmsUser', + 'inversedBy' => 'users', + 'joinTable' => array( + 'name' => 'cmsaddress_cmsuser' + ) + )); + + $this->assertEquals('cmsaddress_cmsuser', $this->strategy->getJoinTableName($class->associationMappings['user'], $class, $this->platform)); + + } + + public function testIdentifierColumnNames() + { + $class = $this->createClassMetadata('Doctrine\Tests\Models\CMS\CmsAddress'); + + $class->mapField(array( + 'id' => true, + 'fieldName' => 'id', + 'columnName' => 'id', + )); + + $this->assertEquals(array('id'), $this->strategy->getIdentifierColumnNames($class, $this->platform)); + } + + + public function testColumnAlias() + { + $this->assertEquals('columnName1', $this->strategy->getColumnAlias('columnName', 1, $this->platform)); + } + + public function testJoinColumnName() + { + $class = $this->createClassMetadata('Doctrine\Tests\Models\DDC117\DDC117ArticleDetails'); + + $class->mapOneToOne(array( + 'id' => true, + 'fieldName' => 'article', + 'targetEntity' => 'Doctrine\Tests\Models\DDC117\DDC117Article', + 'joinColumns' => array(array( + 'name' => 'article' + )), + )); + + $joinColumn = $class->associationMappings['article']['joinColumns'][0]; + $this->assertEquals('article',$this->strategy->getJoinColumnName($joinColumn, $class, $this->platform)); + } + + public function testReferencedJoinColumnName() + { + $cm = $this->createClassMetadata('Doctrine\Tests\Models\DDC117\DDC117ArticleDetails'); + + $cm->mapOneToOne(array( + 'id' => true, + 'fieldName' => 'article', + 'targetEntity' => 'Doctrine\Tests\Models\DDC117\DDC117Article', + 'joinColumns' => array(array( + 'name' => 'article' + )), + )); + + $joinColumn = $cm->associationMappings['article']['joinColumns'][0]; + $this->assertEquals('id',$this->strategy->getReferencedJoinColumnName($joinColumn, $cm, $this->platform)); + } + + public function testGetSequenceName() + { + $class = $this->createClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'); + $definition = array( + 'sequenceName' => 'user_id_seq', + 'allocationSize' => 1, + 'initialValue' => 2 + ); + + $class->setSequenceGeneratorDefinition($definition); + + $this->assertEquals('user_id_seq',$this->strategy->getSequenceName($definition, $class, $this->platform)); + } +} \ No newline at end of file From 1cff8b4d98bc97c40b059342370f31613032b266 Mon Sep 17 00:00:00 2001 From: "Fabio B. Silva" Date: Thu, 20 Jun 2013 00:05:49 -0400 Subject: [PATCH 182/332] Fix DDC-2519 --- lib/Doctrine/ORM/Query/Parser.php | 6 ++ .../ORM/Functional/Ticket/DDC2519Test.php | 85 +++++++++++++++++++ .../ORM/Query/SelectSqlGenerationTest.php | 16 ++++ 3 files changed, 107 insertions(+) create mode 100644 tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2519Test.php diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index 67ee0729d..a82d9d82f 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -670,6 +670,12 @@ class Parser continue; } + if (isset($class->associationMappings[$field]) && + $class->associationMappings[$field]['isOwningSide'] && + $class->associationMappings[$field]['type'] & ClassMetadata::TO_ONE) { + continue; + } + $this->semanticalError( "There is no mapped field named '$field' on class " . $class->name . ".", $deferredItem['token'] ); diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2519Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2519Test.php new file mode 100644 index 000000000..d6ea115b5 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2519Test.php @@ -0,0 +1,85 @@ +useModelSet('legacy'); + parent::setUp(); + + $this->loadFixture(); + } + + /** + * @group DDC-2519 + */ + public function testIssue() + { + $dql = 'SELECT PARTIAL l.{_source, _target} FROM Doctrine\Tests\Models\Legacy\LegacyUserReference l'; + $result = $this->_em->createQuery($dql)->getResult(); + + $this->assertCount(2, $result); + $this->assertInstanceOf('Doctrine\Tests\Models\Legacy\LegacyUserReference', $result[0]); + $this->assertInstanceOf('Doctrine\Tests\Models\Legacy\LegacyUserReference', $result[1]); + + $this->assertInstanceOf('Doctrine\Tests\Models\Legacy\LegacyUser', $result[0]->source()); + $this->assertInstanceOf('Doctrine\Tests\Models\Legacy\LegacyUser', $result[0]->target()); + $this->assertInstanceOf('Doctrine\Tests\Models\Legacy\LegacyUser', $result[1]->source()); + $this->assertInstanceOf('Doctrine\Tests\Models\Legacy\LegacyUser', $result[1]->target()); + + $this->assertInstanceOf('Doctrine\ORM\Proxy\Proxy', $result[0]->source()); + $this->assertInstanceOf('Doctrine\ORM\Proxy\Proxy', $result[0]->target()); + $this->assertInstanceOf('Doctrine\ORM\Proxy\Proxy', $result[1]->source()); + $this->assertInstanceOf('Doctrine\ORM\Proxy\Proxy', $result[1]->target()); + + $this->assertFalse($result[0]->target()->__isInitialized()); + $this->assertFalse($result[0]->source()->__isInitialized()); + $this->assertFalse($result[1]->target()->__isInitialized()); + $this->assertFalse($result[1]->source()->__isInitialized()); + + $this->assertNotNull($result[0]->source()->getId()); + $this->assertNotNull($result[0]->target()->getId()); + $this->assertNotNull($result[1]->source()->getId()); + $this->assertNotNull($result[1]->target()->getId()); + } + + public function loadFixture() + { + $user1 = new LegacyUser(); + $user1->_username = 'FabioBatSilva'; + $user1->_name = 'Fabio B. Silva'; + $user1->_status = 'active'; + + $user2 = new LegacyUser(); + $user2->_username = 'doctrinebot'; + $user2->_name = 'Doctrine Bot'; + $user2->_status = 'active'; + + $user3 = new LegacyUser(); + $user3->_username = 'test'; + $user3->_name = 'Tester'; + $user3->_status = 'active'; + + $this->_em->persist($user1); + $this->_em->persist($user2); + $this->_em->persist($user3); + + $this->_em->flush(); + + $this->_em->persist(new LegacyUserReference($user1, $user2, 'foo')); + $this->_em->persist(new LegacyUserReference($user1, $user3, 'bar')); + + $this->_em->flush(); + $this->_em->clear(); + } +} diff --git a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php index 785e25850..dbb303323 100644 --- a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php +++ b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php @@ -1292,6 +1292,22 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase ); } + /** + * @group DDC-2519 + */ + public function testPartialWithAssociationIdentifier() + { + $this->assertSqlGeneration( + "SELECT PARTIAL l.{_source, _target} FROM Doctrine\Tests\Models\Legacy\LegacyUserReference l", + 'SELECT l0_.iUserIdSource AS iUserIdSource0, l0_.iUserIdTarget AS iUserIdTarget1 FROM legacy_users_reference l0_' + ); + + $this->assertSqlGeneration( + "SELECT PARTIAL l.{_description, _source, _target} FROM Doctrine\Tests\Models\Legacy\LegacyUserReference l", + 'SELECT l0_.description AS description0, l0_.iUserIdSource AS iUserIdSource1, l0_.iUserIdTarget AS iUserIdTarget2 FROM legacy_users_reference l0_' + ); + } + /** * @group DDC-1339 */ From 2f6e914d642e91a70053aa66e02f0a214130194d Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Mon, 24 Jun 2013 08:42:05 +0200 Subject: [PATCH 183/332] Dont allow failures in 5.5 anymore --- .travis.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index a97513344..d6c3f027a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,9 +5,6 @@ php: - 5.4 - 5.5 -matrix: -  allow_failures: -    - php: 5.5 env: - DB=mysql - DB=pgsql From a91050e7f4eb40b35fd8f90b16359c754e05a8ee Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Tue, 25 Jun 2013 19:34:12 +0200 Subject: [PATCH 184/332] [DDC-2350] Eager Collections are not marked as initialized, leading to multiple queries being executed. --- lib/Doctrine/ORM/UnitOfWork.php | 2 + .../ORM/Functional/Ticket/DDC2350Test.php | 68 +++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2350Test.php diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index 68fde5d2c..62bce139d 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -2773,6 +2773,8 @@ class UnitOfWork implements PropertyChangedListener $persister->loadManyToManyCollection($assoc, $collection->getOwner(), $collection); break; } + + $collection->setInitialized(true); } /** diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2350Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2350Test.php new file mode 100644 index 000000000..229bbff52 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2350Test.php @@ -0,0 +1,68 @@ +_schemaTool->createSchema(array( + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2350User'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2350Bug'), + )); + } + + public function testEagerCollectionsAreOnlyRetrievedOnce() + { + $user = new DDC2350User(); + $bug1 = new DDC2350Bug(); + $bug1->user = $user; + $bug2 = new DDC2350Bug(); + $bug2->user = $user; + + $this->_em->persist($user); + $this->_em->persist($bug1); + $this->_em->persist($bug2); + $this->_em->flush(); + + $this->_em->clear(); + + $cnt = $this->getCurrentQueryCount(); + $user = $this->_em->find(__NAMESPACE__ . '\DDC2350User', $user->id); + + $this->assertEquals($cnt + 2, $this->getCurrentQueryCount()); + + $this->assertEquals(2, count($user->reportedBugs)); + + $this->assertEquals($cnt + 2, $this->getCurrentQueryCount()); + } +} + +/** + * @Entity + */ +class DDC2350User +{ + /** @Id @Column(type="integer") @GeneratedValue */ + public $id; + /** @OneToMany(targetEntity="DDC2350Bug", mappedBy="user", fetch="EAGER") */ + public $reportedBugs; +} + +/** + * @Entity + */ +class DDC2350Bug +{ + /** @Id @Column(type="integer") @GeneratedValue */ + public $id; + /** @ManyToOne(targetEntity="DDC2350User", inversedBy="reportedBugs") */ + public $user; +} From 5635fa60a4bea0580b969a5cd9235e2ae6e18a78 Mon Sep 17 00:00:00 2001 From: Sander Marechal Date: Thu, 27 Jun 2013 14:17:41 +0200 Subject: [PATCH 185/332] Check owning entitiy on extra lazy get with OneToMany relation --- lib/Doctrine/ORM/Persisters/OneToManyPersister.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Persisters/OneToManyPersister.php b/lib/Doctrine/ORM/Persisters/OneToManyPersister.php index 120ad548d..2915fd2ed 100644 --- a/lib/Doctrine/ORM/Persisters/OneToManyPersister.php +++ b/lib/Doctrine/ORM/Persisters/OneToManyPersister.php @@ -47,7 +47,7 @@ class OneToManyPersister extends AbstractCollectionPersister throw new \BadMethodCallException("Selecting a collection by index is only supported on indexed collections."); } - return $persister->load(array($mapping['indexBy'] => $index), null, null, array(), 0, 1); + return $persister->load(array($mapping['mappedBy'] => $coll->getOwner(), $mapping['indexBy'] => $index), null, null, array(), 0, 1); } /** From 06ed21e88303b41381d7b7488033bcef983277dd Mon Sep 17 00:00:00 2001 From: Sander Marechal Date: Thu, 27 Jun 2013 14:19:39 +0200 Subject: [PATCH 186/332] Remove extra-lazy-get for ManyToMany relation --- lib/Doctrine/ORM/PersistentCollection.php | 1 + .../ORM/Persisters/ManyToManyPersister.php | 18 -------------- .../Functional/ExtraLazyCollectionTest.php | 24 +------------------ 3 files changed, 2 insertions(+), 41 deletions(-) diff --git a/lib/Doctrine/ORM/PersistentCollection.php b/lib/Doctrine/ORM/PersistentCollection.php index a50cd8343..947774542 100644 --- a/lib/Doctrine/ORM/PersistentCollection.php +++ b/lib/Doctrine/ORM/PersistentCollection.php @@ -518,6 +518,7 @@ final class PersistentCollection implements Collection, Selectable public function get($key) { if ( ! $this->initialized + && $this->association['type'] === Mapping\ClassMetadataInfo::ONE_TO_MANY && $this->association['fetch'] === Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY && isset($this->association['indexBy']) ) { diff --git a/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php b/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php index df46aa30a..d9f7e30ca 100644 --- a/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php +++ b/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php @@ -33,24 +33,6 @@ use Doctrine\ORM\UnitOfWork; */ class ManyToManyPersister extends AbstractCollectionPersister { - /** - * {@inheritdoc} - * - * @override - */ - public function get(PersistentCollection $coll, $index) - { - $mapping = $coll->getMapping(); - $uow = $this->em->getUnitOfWork(); - $persister = $uow->getEntityPersister($mapping['targetEntity']); - - if (!isset($mapping['indexBy'])) { - throw new \BadMethodCallException("Selecting a collection by index is only supported on indexed collections."); - } - - return $persister->load(array($mapping['indexBy'] => $index), null, null, array(), 0, 1); - } - /** * {@inheritdoc} * diff --git a/tests/Doctrine/Tests/ORM/Functional/ExtraLazyCollectionTest.php b/tests/Doctrine/Tests/ORM/Functional/ExtraLazyCollectionTest.php index 5d8fa0f2a..eb4dbe4d8 100644 --- a/tests/Doctrine/Tests/ORM/Functional/ExtraLazyCollectionTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/ExtraLazyCollectionTest.php @@ -17,7 +17,6 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase private $groupId; private $articleId; - private $groupname; private $topic; private $phonenumber; @@ -562,33 +561,13 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertSame($article, $this->_em->find('Doctrine\Tests\Models\CMS\CmsArticle', $this->articleId)); } - /** - * @group DDC-1398 - */ - public function testGetIndexByManyToMany() - { - $user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId); - /* @var $user CmsUser */ - - $queryCount = $this->getCurrentQueryCount(); - - $group = $user->groups->get($this->groupname); - - $this->assertFalse($user->groups->isInitialized()); - $this->assertEquals($queryCount + 1, $this->getCurrentQueryCount()); - $this->assertSame($group, $this->_em->find('Doctrine\Tests\Models\CMS\CmsGroup', $this->groupId)); - } - /** * @group DDC-1398 */ public function testGetNonExistentIndexBy() { $user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId); - /* @var $user CmsUser */ - $this->assertNull($user->articles->get(-1)); - $this->assertNull($user->groups->get(-1)); } private function loadFixture() @@ -668,8 +647,7 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->userId = $user1->getId(); $this->groupId = $group1->id; - $this->groupname = $group1->name; $this->topic = $article1->topic; $this->phonenumber = $phonenumber1->phonenumber; } -} \ No newline at end of file +} From e4bccdc7b3e40b5f017675e289272b952ae721bf Mon Sep 17 00:00:00 2001 From: "Fabio B. Silva" Date: Thu, 27 Jun 2013 20:18:21 -0400 Subject: [PATCH 187/332] coveralls code coverage --- .coveralls.yml | 4 ++++ .travis.yml | 3 +++ README.markdown | 2 ++ composer.json | 3 ++- tests/travis/mysql.travis.xml | 4 ++++ tests/travis/pgsql.travis.xml | 5 +++++ tests/travis/sqlite.travis.xml | 6 ++++++ 7 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 .coveralls.yml diff --git a/.coveralls.yml b/.coveralls.yml new file mode 100644 index 000000000..0c0823360 --- /dev/null +++ b/.coveralls.yml @@ -0,0 +1,4 @@ +# for php-coveralls +service_name: travis-ci +src_dir: lib +coverage_clover: build/logs/clover.xml diff --git a/.travis.yml b/.travis.yml index d6c3f027a..92b97851f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,3 +19,6 @@ before_script: - composer install --prefer-source --dev script: phpunit --configuration tests/travis/$DB.travis.xml + +after_script: + - php vendor/bin/coveralls -v diff --git a/README.markdown b/README.markdown index 39bde0285..b6b60ac16 100644 --- a/README.markdown +++ b/README.markdown @@ -5,6 +5,8 @@ Master: [![Build Status](https://secure.travis-ci.org/doctrine/doctrine2.png?bra 2.2: [![Build Status](https://secure.travis-ci.org/doctrine/doctrine2.png?branch=2.2)](http://travis-ci.org/doctrine/doctrine2) 2.1: [![Build Status](https://secure.travis-ci.org/doctrine/doctrine2.png?branch=2.1.x)](http://travis-ci.org/doctrine/doctrine2) +Master: [![Coverage Status](https://coveralls.io/repos/doctrine/doctrine2/badge.png?branch=master)](https://coveralls.io/r/doctrine/doctrine2?branch=master) + [![Latest Stable Version](https://poser.pugx.org/doctrine/orm/v/stable.png)](https://packagist.org/packages/doctrine/orm) [![Total Downloads](https://poser.pugx.org/doctrine/orm/downloads.png)](https://packagist.org/packages/doctrine/orm) diff --git a/composer.json b/composer.json index f74058bac..8805d54a3 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,8 @@ "symfony/console": "2.*" }, "require-dev": { - "symfony/yaml": "2.1" + "symfony/yaml": "2.1", + "satooshi/php-coveralls": "dev-master" }, "suggest": { "symfony/yaml": "If you want to use YAML Metadata Mapping Driver" diff --git a/tests/travis/mysql.travis.xml b/tests/travis/mysql.travis.xml index f17a4b87d..82559afdf 100644 --- a/tests/travis/mysql.travis.xml +++ b/tests/travis/mysql.travis.xml @@ -16,6 +16,10 @@ + + + + ./../Doctrine/Tests/ORM diff --git a/tests/travis/pgsql.travis.xml b/tests/travis/pgsql.travis.xml index fa0581acb..b92f775aa 100644 --- a/tests/travis/pgsql.travis.xml +++ b/tests/travis/pgsql.travis.xml @@ -18,6 +18,11 @@ + + + + + ./../Doctrine/Tests/ORM diff --git a/tests/travis/sqlite.travis.xml b/tests/travis/sqlite.travis.xml index 5d310c327..a4c400caa 100644 --- a/tests/travis/sqlite.travis.xml +++ b/tests/travis/sqlite.travis.xml @@ -1,10 +1,16 @@ + + + + + ./../Doctrine/Tests/ORM + performance From c57f2c39f622b7fc4bfd705d1561706039bf89f1 Mon Sep 17 00:00:00 2001 From: Dave Hulbert Date: Tue, 2 Jul 2013 16:28:58 +0100 Subject: [PATCH 188/332] Fix grammar in DropCommand --- .../ORM/Tools/Console/Command/SchemaTool/DropCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php index ac003b991..247bca1e4 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php @@ -50,7 +50,7 @@ class DropCommand extends AbstractCommand ->setDefinition(array( new InputOption( 'dump-sql', null, InputOption::VALUE_NONE, - 'Instead of try to apply generated SQLs into EntityManager Storage Connection, output them.' + 'Instead of trying to apply generated SQLs into EntityManager Storage Connection, output them.' ), new InputOption( 'force', null, InputOption::VALUE_NONE, From 5e700db6d38aded206f19586789e9d68981fc9bf Mon Sep 17 00:00:00 2001 From: Dave Hulbert Date: Tue, 2 Jul 2013 16:29:36 +0100 Subject: [PATCH 189/332] Fix grammar in CreateCommand --- .../ORM/Tools/Console/Command/SchemaTool/CreateCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/CreateCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/CreateCommand.php index 610f25c95..9a49c698d 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/CreateCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/CreateCommand.php @@ -50,7 +50,7 @@ class CreateCommand extends AbstractCommand ->setDefinition(array( new InputOption( 'dump-sql', null, InputOption::VALUE_NONE, - 'Instead of try to apply generated SQLs into EntityManager Storage Connection, output them.' + 'Instead of trying to apply generated SQLs into EntityManager Storage Connection, output them.' ) )) ->setHelp(<< Date: Wed, 3 Jul 2013 12:14:16 +0200 Subject: [PATCH 190/332] Appending the Paginator tree walker hint, instead of removing all the other hints. --- lib/Doctrine/ORM/Tools/Pagination/Paginator.php | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) mode change 100644 => 100755 lib/Doctrine/ORM/Tools/Pagination/Paginator.php diff --git a/lib/Doctrine/ORM/Tools/Pagination/Paginator.php b/lib/Doctrine/ORM/Tools/Pagination/Paginator.php old mode 100644 new mode 100755 index 115eb590e..d5eda5107 --- a/lib/Doctrine/ORM/Tools/Pagination/Paginator.php +++ b/lib/Doctrine/ORM/Tools/Pagination/Paginator.php @@ -134,7 +134,7 @@ class Paginator implements \Countable, \IteratorAggregate $countQuery->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, 'Doctrine\ORM\Tools\Pagination\CountOutputWalker'); $countQuery->setResultSetMapping($rsm); } else { - $countQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\CountWalker')); + $this->appendTreeWalker($countQuery, 'Doctrine\ORM\Tools\Pagination\CountWalker'); } $countQuery->setFirstResult(null)->setMaxResults(null); @@ -165,7 +165,7 @@ class Paginator implements \Countable, \IteratorAggregate if ($this->useOutputWalker($subQuery)) { $subQuery->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, 'Doctrine\ORM\Tools\Pagination\LimitSubqueryOutputWalker'); } else { - $subQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\LimitSubqueryWalker')); + $this->appendTreeWalker($subQuery, 'Doctrine\ORM\Tools\Pagination\LimitSubqueryWalker'); } $subQuery->setFirstResult($offset)->setMaxResults($length); @@ -231,4 +231,16 @@ class Paginator implements \Countable, \IteratorAggregate return $this->useOutputWalkers; } + + private function appendTreeWalker(Query $query, $walkerClass) + { + $hints = $query->getHint(Query::HINT_CUSTOM_TREE_WALKERS); + + if ($hints === false) { + $hints = array(); + } + + $hints[] = $walkerClass; + $query->setHint(Query::HINT_CUSTOM_TREE_WALKERS, $hints); + } } From 981fcb2c21a0c3a8f624681283c4bec1263d53ca Mon Sep 17 00:00:00 2001 From: Roger Llopart Pla Date: Wed, 3 Jul 2013 12:24:57 +0200 Subject: [PATCH 191/332] Added docblock. --- lib/Doctrine/ORM/Tools/Pagination/Paginator.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/Doctrine/ORM/Tools/Pagination/Paginator.php b/lib/Doctrine/ORM/Tools/Pagination/Paginator.php index d5eda5107..80438e03c 100755 --- a/lib/Doctrine/ORM/Tools/Pagination/Paginator.php +++ b/lib/Doctrine/ORM/Tools/Pagination/Paginator.php @@ -232,6 +232,12 @@ class Paginator implements \Countable, \IteratorAggregate return $this->useOutputWalkers; } + /** + * Appends a custom tree walker to the tree walkers hint. + * + * @param Query $query + * @param string $walkerClass + */ private function appendTreeWalker(Query $query, $walkerClass) { $hints = $query->getHint(Query::HINT_CUSTOM_TREE_WALKERS); From 91638aadcfe9675e8c3d3f85dbfd5ba3cad56b53 Mon Sep 17 00:00:00 2001 From: Roger Llopart Pla Date: Wed, 3 Jul 2013 11:18:19 +0000 Subject: [PATCH 192/332] Added a test which verifies that the tree walkers are kept. --- .../Tests/ORM/Functional/PaginationTest.php | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tests/Doctrine/Tests/ORM/Functional/PaginationTest.php b/tests/Doctrine/Tests/ORM/Functional/PaginationTest.php index 0a2845582..15c043d84 100644 --- a/tests/Doctrine/Tests/ORM/Functional/PaginationTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/PaginationTest.php @@ -139,6 +139,18 @@ class PaginationTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertTrue($query->getParameters()->isEmpty()); } + public function testQueryWalkerIsKept() + { + $dql = "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u"; + $query = $this->_em->createQuery($dql); + $query->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\Tests\ORM\Functional\CustomTreeWalker')); + + $paginator = new Paginator($query, true); + $paginator->setUseOutputWalkers(false); + $this->assertCount(1, $paginator->getIterator()); + $this->assertEquals(1, $paginator->count()); + } + public function populate() { for ($i = 0; $i < 3; $i++) { @@ -166,3 +178,22 @@ class PaginationTest extends \Doctrine\Tests\OrmFunctionalTestCase ); } } + +class CustomTreeWalker extends Query\TreeWalkerAdapter +{ + public function walkSelectStatement(Query\AST\SelectStatement $selectStatement) + { + $condition = new Query\AST\ConditionalPrimary(); + + $path = new Query\AST\PathExpression(Query\AST\PathExpression::TYPE_STATE_FIELD, 'u', 'name'); + $path->type = Query\AST\PathExpression::TYPE_STATE_FIELD; + + $condition->simpleConditionalExpression = new Query\AST\ComparisonExpression( + $path, + '=', + new Query\AST\Literal(Query\AST\Literal::STRING, 'Name1') + ); + + $selectStatement->whereClause = new Query\AST\WhereClause($condition); + } +} From 6aa58d9939018f1960106f03b5d440e916e01867 Mon Sep 17 00:00:00 2001 From: Paul Date: Thu, 4 Jul 2013 10:12:36 +0200 Subject: [PATCH 193/332] Allow query parameters starting with an underscore --- lib/Doctrine/ORM/Query/Lexer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Query/Lexer.php b/lib/Doctrine/ORM/Query/Lexer.php index bf0b5f6db..d5721a735 100644 --- a/lib/Doctrine/ORM/Query/Lexer.php +++ b/lib/Doctrine/ORM/Query/Lexer.php @@ -129,7 +129,7 @@ class Lexer extends \Doctrine\Common\Lexer '[a-z_\\\][a-z0-9_\:\\\]*[a-z0-9_]{1}', '(?:[0-9]+(?:[\.][0-9]+)*)(?:e[+-]?[0-9]+)?', "'(?:[^']|'')*'", - '\?[0-9]*|:[a-z]{1}[a-z0-9_]{0,}' + '\?[0-9]*|:[a-z_][a-z0-9_]*' ); } From 9f2994f4629d0a6a1b666b56ec8941e39067f177 Mon Sep 17 00:00:00 2001 From: Roger Llopart Pla Date: Thu, 4 Jul 2013 08:29:46 +0000 Subject: [PATCH 194/332] Fixed name colision. --- tests/Doctrine/Tests/ORM/Functional/PaginationTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Doctrine/Tests/ORM/Functional/PaginationTest.php b/tests/Doctrine/Tests/ORM/Functional/PaginationTest.php index 15c043d84..bf9e4a685 100644 --- a/tests/Doctrine/Tests/ORM/Functional/PaginationTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/PaginationTest.php @@ -143,7 +143,7 @@ class PaginationTest extends \Doctrine\Tests\OrmFunctionalTestCase { $dql = "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u"; $query = $this->_em->createQuery($dql); - $query->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\Tests\ORM\Functional\CustomTreeWalker')); + $query->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\Tests\ORM\Functional\CustomPaginationTestTreeWalker')); $paginator = new Paginator($query, true); $paginator->setUseOutputWalkers(false); @@ -179,7 +179,7 @@ class PaginationTest extends \Doctrine\Tests\OrmFunctionalTestCase } } -class CustomTreeWalker extends Query\TreeWalkerAdapter +class CustomPaginationTestTreeWalker extends Query\TreeWalkerAdapter { public function walkSelectStatement(Query\AST\SelectStatement $selectStatement) { From b5394fc5a0303401a69fdfc5ae020f3ff1a2a792 Mon Sep 17 00:00:00 2001 From: Paul Hooijenga Date: Fri, 5 Jul 2013 09:21:24 +0200 Subject: [PATCH 195/332] Add test for query paremeters starting with underscore --- tests/Doctrine/Tests/ORM/Query/LexerTest.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/Doctrine/Tests/ORM/Query/LexerTest.php b/tests/Doctrine/Tests/ORM/Query/LexerTest.php index 29bba4bae..1798cc1ed 100644 --- a/tests/Doctrine/Tests/ORM/Query/LexerTest.php +++ b/tests/Doctrine/Tests/ORM/Query/LexerTest.php @@ -158,6 +158,15 @@ class LexerTest extends \Doctrine\Tests\OrmTestCase $this->assertEquals(':name', $token['value']); } + public function testScannerRecognizesNamedInputParameterStartingWithUnderscore() + { + $lexer = new Lexer(':_name'); + $lexer->moveNext(); + $token = $lexer->lookahead; + $this->assertEquals(Lexer::T_INPUT_PARAMETER, $token['type']); + $this->assertEquals(':_name', $token['value']); + } + public function testScannerTokenizesASimpleQueryCorrectly() { $dql = "SELECT u FROM My\Namespace\User u WHERE u.name = 'Jack O''Neil'"; From 641774630bcb2398aa2e2a89f1babf31690a7240 Mon Sep 17 00:00:00 2001 From: Tristan Lins Date: Fri, 5 Jul 2013 13:40:57 +0200 Subject: [PATCH 196/332] Access properties via static:: instead of self::. The properties of EntityGenerator are now protected instead of private. But this does not make sense until they are accessed with static::. Otherwise the templates cannot be overwritten within a sub class. --- lib/Doctrine/ORM/Tools/EntityGenerator.php | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/Doctrine/ORM/Tools/EntityGenerator.php b/lib/Doctrine/ORM/Tools/EntityGenerator.php index 5a55f0fc9..d9a5a2858 100644 --- a/lib/Doctrine/ORM/Tools/EntityGenerator.php +++ b/lib/Doctrine/ORM/Tools/EntityGenerator.php @@ -390,7 +390,7 @@ public function __construct() $this->generateEntityBody($metadata) ); - $code = str_replace($placeHolders, $replacements, self::$classTemplate); + $code = str_replace($placeHolders, $replacements, static::$classTemplate); return str_replace('', $this->spaces, $code); } @@ -474,7 +474,7 @@ public function __construct() */ public function setFieldVisibility($visibility) { - if ($visibility !== self::FIELD_VISIBLE_PRIVATE && $visibility !== self::FIELD_VISIBLE_PROTECTED) { + if ($visibility !== static::FIELD_VISIBLE_PRIVATE && $visibility !== static::FIELD_VISIBLE_PROTECTED) { throw new \InvalidArgumentException('Invalid provided visibility (only private and protected are allowed): ' . $visibility); } @@ -633,7 +633,7 @@ public function __construct() } if ($collections) { - return $this->prefixCodeWithSpaces(str_replace("", implode("\n".$this->spaces, $collections), self::$constructorMethodTemplate)); + return $this->prefixCodeWithSpaces(str_replace("", implode("\n".$this->spaces, $collections), static::$constructorMethodTemplate)); } return ''; @@ -1102,7 +1102,7 @@ public function __construct() $this->staticReflection[$metadata->name]['methods'][] = $methodName; $var = sprintf('%sMethodTemplate', $type); - $template = self::$$var; + $template = static::$$var; $methodTypeHint = null; $types = Type::getTypesMap(); @@ -1155,7 +1155,7 @@ public function __construct() $method = str_replace( array_keys($replacements), array_values($replacements), - self::$lifecycleCallbackMethodTemplate + static::$lifecycleCallbackMethodTemplate ); return $this->prefixCodeWithSpaces($method); @@ -1463,11 +1463,11 @@ public function __construct() */ protected function getInheritanceTypeString($type) { - if ( ! isset(self::$inheritanceTypeMap[$type])) { + if ( ! isset(static::$inheritanceTypeMap[$type])) { throw new \InvalidArgumentException(sprintf('Invalid provided InheritanceType: %s', $type)); } - return self::$inheritanceTypeMap[$type]; + return static::$inheritanceTypeMap[$type]; } /** @@ -1479,11 +1479,11 @@ public function __construct() */ protected function getChangeTrackingPolicyString($type) { - if ( ! isset(self::$changeTrackingPolicyMap[$type])) { + if ( ! isset(static::$changeTrackingPolicyMap[$type])) { throw new \InvalidArgumentException(sprintf('Invalid provided ChangeTrackingPolicy: %s', $type)); } - return self::$changeTrackingPolicyMap[$type]; + return static::$changeTrackingPolicyMap[$type]; } /** @@ -1495,10 +1495,10 @@ public function __construct() */ protected function getIdGeneratorTypeString($type) { - if ( ! isset(self::$generatorStrategyMap[$type])) { + if ( ! isset(static::$generatorStrategyMap[$type])) { throw new \InvalidArgumentException(sprintf('Invalid provided IdGeneratorType: %s', $type)); } - return self::$generatorStrategyMap[$type]; + return static::$generatorStrategyMap[$type]; } } From 3f112db7256994fc2284f011b4063d36b8614016 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Gallego?= Date: Tue, 9 Jul 2013 17:31:30 +0200 Subject: [PATCH 197/332] Allow to have non-distinct queries --- lib/Doctrine/ORM/AbstractQuery.php | 12 ++++++++++++ lib/Doctrine/ORM/Tools/Pagination/Paginator.php | 2 +- tests/Doctrine/Tests/ORM/Query/QueryTest.php | 2 ++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/AbstractQuery.php b/lib/Doctrine/ORM/AbstractQuery.php index b32310512..583969944 100644 --- a/lib/Doctrine/ORM/AbstractQuery.php +++ b/lib/Doctrine/ORM/AbstractQuery.php @@ -701,6 +701,18 @@ abstract class AbstractQuery return isset($this->_hints[$name]) ? $this->_hints[$name] : false; } + /** + * Check if the query has a hint + * + * @param string $name The name of the hint + * + * @return bool False if the query does not have any hint + */ + public function hasHint($name) + { + return isset($this->_hints[$name]); + } + /** * Return the key value map of query hints that are currently set. * diff --git a/lib/Doctrine/ORM/Tools/Pagination/Paginator.php b/lib/Doctrine/ORM/Tools/Pagination/Paginator.php index 115eb590e..2d45df95a 100644 --- a/lib/Doctrine/ORM/Tools/Pagination/Paginator.php +++ b/lib/Doctrine/ORM/Tools/Pagination/Paginator.php @@ -121,7 +121,7 @@ class Paginator implements \Countable, \IteratorAggregate /* @var $countQuery Query */ $countQuery = $this->cloneQuery($this->query); - if ( ! $countQuery->getHint(CountWalker::HINT_DISTINCT)) { + if ( ! $countQuery->hasHint(CountWalker::HINT_DISTINCT)) { $countQuery->setHint(CountWalker::HINT_DISTINCT, true); } diff --git a/tests/Doctrine/Tests/ORM/Query/QueryTest.php b/tests/Doctrine/Tests/ORM/Query/QueryTest.php index 9617fa873..adef91d4a 100644 --- a/tests/Doctrine/Tests/ORM/Query/QueryTest.php +++ b/tests/Doctrine/Tests/ORM/Query/QueryTest.php @@ -105,6 +105,8 @@ class QueryTest extends \Doctrine\Tests\OrmTestCase $this->assertEquals('bar', $q->getHint('foo')); $this->assertEquals('baz', $q->getHint('bar')); $this->assertEquals(array('foo' => 'bar', 'bar' => 'baz'), $q->getHints()); + $this->assertTrue($q->hasHint('foo')); + $this->assertFalse($q->hasHint('barFooBaz')); } /** From d249a22f74c105925fe2de80d4c29d7c965e5fd6 Mon Sep 17 00:00:00 2001 From: Alessandro Tagliapietra Date: Thu, 11 Jul 2013 11:52:26 +0200 Subject: [PATCH 198/332] Updated batch-processing link extension I've changed the batch processing link adding .html else the link is broken --- docs/en/reference/dql-doctrine-query-language.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/reference/dql-doctrine-query-language.rst b/docs/en/reference/dql-doctrine-query-language.rst index 856a4b077..f9ac299e3 100644 --- a/docs/en/reference/dql-doctrine-query-language.rst +++ b/docs/en/reference/dql-doctrine-query-language.rst @@ -1189,7 +1189,7 @@ There are situations when a query you want to execute returns a very large result-set that needs to be processed. All the previously described hydration modes completely load a result-set into memory which might not be feasible with large result sets. See -the `Batch Processing `_ section on details how +the `Batch Processing `_ section on details how to iterate large result sets. Functions From 0a151f247410287d81ff893f1d9a362a73beef1b Mon Sep 17 00:00:00 2001 From: Eugene Morgan Date: Mon, 15 Jul 2013 15:01:33 -0500 Subject: [PATCH 199/332] Remove extra semicolon before ->setParameter() calls --- docs/en/reference/query-builder.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/en/reference/query-builder.rst b/docs/en/reference/query-builder.rst index 8029822b5..b96e2dfe2 100644 --- a/docs/en/reference/query-builder.rst +++ b/docs/en/reference/query-builder.rst @@ -208,7 +208,7 @@ allowed. Binding parameters can simply be achieved as follows: $qb->select('u') ->from('User u') ->where('u.id = ?1') - ->orderBy('u.name', 'ASC'); + ->orderBy('u.name', 'ASC') ->setParameter(1, 100); // Sets ?1 to 100, and thus we will fetch a user with u.id = 100 You are not forced to enumerate your placeholders as the @@ -222,7 +222,7 @@ alternative syntax is available: $qb->select('u') ->from('User u') ->where('u.id = :identifier') - ->orderBy('u.name', 'ASC'); + ->orderBy('u.name', 'ASC') ->setParameter('identifier', 100); // Sets :identifier to 100, and thus we will fetch a user with u.id = 100 Note that numeric placeholders start with a ? followed by a number From 0c3581a1f8343bb4ebeb683c018a2c1d175e01a8 Mon Sep 17 00:00:00 2001 From: Thomas Tourlourat Date: Fri, 19 Jul 2013 11:33:35 +0200 Subject: [PATCH 200/332] Color message like the update tools --- .../ORM/Tools/Console/Command/SchemaTool/DropCommand.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php index 247bca1e4..a852194bc 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php @@ -115,8 +115,11 @@ EOT } if (count($sqls)) { - $output->writeln('Schema-Tool would execute ' . count($sqls) . ' queries to drop the database.'); - $output->writeln('Please run the operation with --force to execute these queries or use --dump-sql to see them.'); + $output->writeln(sprintf('The Schema-Tool would execute "%s" queries to update the database.', count($sqls))); + $output->writeln('Please run the operation by passing one - or both - of the following options:'); + + $output->writeln(sprintf(' %s --force to execute the command', $this->getName())); + $output->writeln(sprintf(' %s --dump-sql to dump the SQL statements to the screen', $this->getName())); return 1; } From b4e9dafd10f2f962cf9000f6fe9e167804aa4fc9 Mon Sep 17 00:00:00 2001 From: David Buchmann Date: Mon, 22 Jul 2013 10:23:47 +0200 Subject: [PATCH 201/332] add missing hint about lifecycle callback --- docs/en/reference/events.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/en/reference/events.rst b/docs/en/reference/events.rst index 517f2ec5b..1d5351563 100644 --- a/docs/en/reference/events.rst +++ b/docs/en/reference/events.rst @@ -173,7 +173,7 @@ the life-time of their registered entities. database or after the refresh operation has been applied to it. - loadClassMetadata - The loadClassMetadata event occurs after the mapping metadata for a class has been loaded from a mapping source - (annotations/xml/yaml). + (annotations/xml/yaml). This event is not a lifecycle callback. - preFlush - The preFlush event occurs at the very beginning of a flush operation. This event is not a lifecycle callback. - onFlush - The onFlush event occurs after the change-sets of all @@ -183,7 +183,7 @@ the life-time of their registered entities. event is not a lifecycle callback. - onClear - The onClear event occurs when the EntityManager#clear() operation is invoked, after all references to entities have been removed from the unit of - work. + work. This event is not a lifecycle callback. .. warning:: From eba933bb478a4a1d2d8bd5bd1ef48e6efad87d0c Mon Sep 17 00:00:00 2001 From: Nicolas Nutten Date: Mon, 22 Jul 2013 10:56:18 +0200 Subject: [PATCH 202/332] To avoid "SpacingAfterParams" error with PHPCS Symfony2 coding standard Hello, I added two blank lines in comments two avoid the following error with PHPCS Symfony2 coding standard : Error Code: SpacingAfterParams Error Description: Last parameter comment requires a blank new line after it. --- lib/Doctrine/ORM/Tools/EntityGenerator.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/Doctrine/ORM/Tools/EntityGenerator.php b/lib/Doctrine/ORM/Tools/EntityGenerator.php index 94c1a2eac..c1e1c771e 100644 --- a/lib/Doctrine/ORM/Tools/EntityGenerator.php +++ b/lib/Doctrine/ORM/Tools/EntityGenerator.php @@ -234,6 +234,7 @@ public function () * * * @param $ + * * @return */ public function ($) @@ -251,6 +252,7 @@ public function ($) * * * @param $ + * * @return */ public function ($) From 4882ff1ef5463c7b66deac9bfc8b464fb1fa6599 Mon Sep 17 00:00:00 2001 From: Thomas Tourlourat Date: Mon, 22 Jul 2013 17:11:53 +0200 Subject: [PATCH 203/332] Add info tag around ATTENTION --- .../ORM/Tools/Console/Command/SchemaTool/DropCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php index a852194bc..f7ba0687b 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php @@ -106,7 +106,7 @@ EOT return 0; } - $output->writeln('ATTENTION: This operation should not be executed in a production environment.' . PHP_EOL); + $output->writeln('ATTENTION: This operation should not be executed in a production environment.' . PHP_EOL); if ($isFullDatabaseDrop) { $sqls = $schemaTool->getDropDatabaseSQL(); From 5fd844d73e18a3a0afff1c0874fb35661b14be8d Mon Sep 17 00:00:00 2001 From: Austin Morris Date: Mon, 22 Jul 2013 14:39:00 -0400 Subject: [PATCH 204/332] PersistentCollection - initialize coll - create failing tests --- .../PersistentCollectionFunctionalTest.php | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 tests/Doctrine/Tests/ORM/PersistentCollectionFunctionalTest.php diff --git a/tests/Doctrine/Tests/ORM/PersistentCollectionFunctionalTest.php b/tests/Doctrine/Tests/ORM/PersistentCollectionFunctionalTest.php new file mode 100644 index 000000000..5206a70d7 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/PersistentCollectionFunctionalTest.php @@ -0,0 +1,73 @@ + + */ +class PersistentCollectionFunctionalTest extends OrmFunctionalTestCase +{ + /** + * @var PersistentCollection + */ + protected $collection; + + /** + * Setup tests. + */ + public function setUp() + { + $this->useModelSet('ecommerce'); + parent::setUp(); + + $classMetaData = $this->_em->getClassMetadata('Doctrine\Tests\Models\ECommerce\ECommerceCart'); + $this->collection = new PersistentCollection($this->_em, $classMetaData, new ArrayCollection); + $this->collection->setInitialized(false); + $this->collection->setOwner(new ECommerceCart(), $classMetaData->getAssociationMapping('products')); + } + + /** + * Test that PersistentCollection::add() initializes the collection. + */ + public function testAddInitializesCollection() + { + $this->collection->add(new ECommerceProduct); + $this->assertTrue($this->collection->isInitialized()); + } + + /** + * Test that PersistentCollection::current() initializes the collection. + */ + public function testCurrentInitializesCollection() + { + $this->collection->current(); + $this->assertTrue($this->collection->isInitialized()); + } + + /** + * Test that PersistentCollection::key() initializes the collection. + */ + public function testKeyInitializesCollection() + { + $this->collection->key(); + $this->assertTrue($this->collection->isInitialized()); + } + + /** + * Test that PersistentCollection::next() initializes the collection. + */ + public function testNextInitializesCollection() + { + $this->collection->next(); + $this->assertTrue($this->collection->isInitialized()); + } +} From 6bae2eac290f2e30b3716ee9d735b6a2f8897cd2 Mon Sep 17 00:00:00 2001 From: Austin Morris Date: Mon, 22 Jul 2013 14:42:03 -0400 Subject: [PATCH 205/332] Initialize coll when using Collection methods inside PersistentCollection --- lib/Doctrine/ORM/PersistentCollection.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/Doctrine/ORM/PersistentCollection.php b/lib/Doctrine/ORM/PersistentCollection.php index 947774542..462c23ec7 100644 --- a/lib/Doctrine/ORM/PersistentCollection.php +++ b/lib/Doctrine/ORM/PersistentCollection.php @@ -587,6 +587,8 @@ final class PersistentCollection implements Collection, Selectable */ public function add($value) { + $this->initialize(); + $this->coll->add($value); $this->changed(); @@ -757,6 +759,8 @@ final class PersistentCollection implements Collection, Selectable */ public function key() { + $this->initialize(); + return $this->coll->key(); } @@ -765,6 +769,8 @@ final class PersistentCollection implements Collection, Selectable */ public function current() { + $this->initialize(); + return $this->coll->current(); } @@ -773,6 +779,8 @@ final class PersistentCollection implements Collection, Selectable */ public function next() { + $this->initialize(); + return $this->coll->next(); } From 1c8ae505570c603868b2cb1b53e9ddbcca4cbb41 Mon Sep 17 00:00:00 2001 From: Austin Morris Date: Mon, 22 Jul 2013 18:54:02 -0400 Subject: [PATCH 206/332] do not initialize coll on add() --- lib/Doctrine/ORM/PersistentCollection.php | 2 -- .../Tests/ORM/PersistentCollectionFunctionalTest.php | 9 --------- 2 files changed, 11 deletions(-) diff --git a/lib/Doctrine/ORM/PersistentCollection.php b/lib/Doctrine/ORM/PersistentCollection.php index 462c23ec7..8d0fef757 100644 --- a/lib/Doctrine/ORM/PersistentCollection.php +++ b/lib/Doctrine/ORM/PersistentCollection.php @@ -587,8 +587,6 @@ final class PersistentCollection implements Collection, Selectable */ public function add($value) { - $this->initialize(); - $this->coll->add($value); $this->changed(); diff --git a/tests/Doctrine/Tests/ORM/PersistentCollectionFunctionalTest.php b/tests/Doctrine/Tests/ORM/PersistentCollectionFunctionalTest.php index 5206a70d7..fbce752f7 100644 --- a/tests/Doctrine/Tests/ORM/PersistentCollectionFunctionalTest.php +++ b/tests/Doctrine/Tests/ORM/PersistentCollectionFunctionalTest.php @@ -35,15 +35,6 @@ class PersistentCollectionFunctionalTest extends OrmFunctionalTestCase $this->collection->setOwner(new ECommerceCart(), $classMetaData->getAssociationMapping('products')); } - /** - * Test that PersistentCollection::add() initializes the collection. - */ - public function testAddInitializesCollection() - { - $this->collection->add(new ECommerceProduct); - $this->assertTrue($this->collection->isInitialized()); - } - /** * Test that PersistentCollection::current() initializes the collection. */ From 23cc3ea4bc07bbc2ace284e68d91b7715d65e46c Mon Sep 17 00:00:00 2001 From: trsteel88 Date: Tue, 23 Jul 2013 12:30:33 +1000 Subject: [PATCH 207/332] Update working-with-associations.rst Always use the constant for sorting. If you use 'asc' it will not work. --- docs/en/reference/working-with-associations.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/reference/working-with-associations.rst b/docs/en/reference/working-with-associations.rst index 0efba84f2..b65773391 100644 --- a/docs/en/reference/working-with-associations.rst +++ b/docs/en/reference/working-with-associations.rst @@ -630,7 +630,7 @@ large collections. $criteria = Criteria::create() ->where(Criteria::expr()->eq("birthday", "1982-02-17")) - ->orderBy(array("username" => "ASC")) + ->orderBy(array("username" => Criteria::ASC)) ->setFirstResult(0) ->setMaxResults(20) ; From 0083cb8ca68ce97b6447ff0c8246f5fa1e74babe Mon Sep 17 00:00:00 2001 From: Austin Morris Date: Tue, 23 Jul 2013 09:01:01 -0400 Subject: [PATCH 208/332] remove redundant require_once for TestInit.php --- .../Doctrine/Tests/ORM/PersistentCollectionFunctionalTest.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/Doctrine/Tests/ORM/PersistentCollectionFunctionalTest.php b/tests/Doctrine/Tests/ORM/PersistentCollectionFunctionalTest.php index fbce752f7..d168c0502 100644 --- a/tests/Doctrine/Tests/ORM/PersistentCollectionFunctionalTest.php +++ b/tests/Doctrine/Tests/ORM/PersistentCollectionFunctionalTest.php @@ -5,11 +5,8 @@ namespace Doctrine\Tests\ORM; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\ORM\PersistentCollection; use Doctrine\Tests\Models\ECommerce\ECommerceCart; -use Doctrine\Tests\Models\ECommerce\ECommerceProduct; use Doctrine\Tests\OrmFunctionalTestCase; -require_once __DIR__ . '/../TestInit.php'; - /** * Tests the initialization of persistent collections. * @author Austin Morris From 5fc1184a49f539581db92332e476954377a6e5ed Mon Sep 17 00:00:00 2001 From: Austin Morris Date: Tue, 23 Jul 2013 09:40:46 -0400 Subject: [PATCH 209/332] convert PersistentCollection functional tests to unit tests --- .../PersistentCollectionFunctionalTest.php | 61 ------------------- .../Tests/ORM/PersistentCollectionTest.php | 56 +++++++++++++++-- 2 files changed, 51 insertions(+), 66 deletions(-) delete mode 100644 tests/Doctrine/Tests/ORM/PersistentCollectionFunctionalTest.php diff --git a/tests/Doctrine/Tests/ORM/PersistentCollectionFunctionalTest.php b/tests/Doctrine/Tests/ORM/PersistentCollectionFunctionalTest.php deleted file mode 100644 index d168c0502..000000000 --- a/tests/Doctrine/Tests/ORM/PersistentCollectionFunctionalTest.php +++ /dev/null @@ -1,61 +0,0 @@ - - */ -class PersistentCollectionFunctionalTest extends OrmFunctionalTestCase -{ - /** - * @var PersistentCollection - */ - protected $collection; - - /** - * Setup tests. - */ - public function setUp() - { - $this->useModelSet('ecommerce'); - parent::setUp(); - - $classMetaData = $this->_em->getClassMetadata('Doctrine\Tests\Models\ECommerce\ECommerceCart'); - $this->collection = new PersistentCollection($this->_em, $classMetaData, new ArrayCollection); - $this->collection->setInitialized(false); - $this->collection->setOwner(new ECommerceCart(), $classMetaData->getAssociationMapping('products')); - } - - /** - * Test that PersistentCollection::current() initializes the collection. - */ - public function testCurrentInitializesCollection() - { - $this->collection->current(); - $this->assertTrue($this->collection->isInitialized()); - } - - /** - * Test that PersistentCollection::key() initializes the collection. - */ - public function testKeyInitializesCollection() - { - $this->collection->key(); - $this->assertTrue($this->collection->isInitialized()); - } - - /** - * Test that PersistentCollection::next() initializes the collection. - */ - public function testNextInitializesCollection() - { - $this->collection->next(); - $this->assertTrue($this->collection->isInitialized()); - } -} diff --git a/tests/Doctrine/Tests/ORM/PersistentCollectionTest.php b/tests/Doctrine/Tests/ORM/PersistentCollectionTest.php index fd0dffbf2..9b9a067fc 100644 --- a/tests/Doctrine/Tests/ORM/PersistentCollectionTest.php +++ b/tests/Doctrine/Tests/ORM/PersistentCollectionTest.php @@ -6,16 +6,21 @@ use Doctrine\Common\Collections\ArrayCollection; use Doctrine\ORM\PersistentCollection; use Doctrine\Tests\Mocks\ConnectionMock; use Doctrine\Tests\Mocks\EntityManagerMock; -use Doctrine\Tests\Models\ECommerce\ECommerceProduct; - -require_once __DIR__ . '/../TestInit.php'; +use Doctrine\Tests\Models\ECommerce\ECommerceCart; +use Doctrine\Tests\OrmTestCase; /** - * Tests the lazy-loading capabilities of the PersistentCollection. + * Tests the lazy-loading capabilities of the PersistentCollection and the initialization of collections. * @author Giorgio Sironi + * @author Austin Morris */ -class PersistentCollectionTest extends \Doctrine\Tests\OrmTestCase +class PersistentCollectionTest extends OrmTestCase { + /** + * @var PersistentCollection + */ + protected $collection; + private $_connectionMock; private $_emMock; @@ -27,6 +32,17 @@ class PersistentCollectionTest extends \Doctrine\Tests\OrmTestCase $this->_emMock = EntityManagerMock::create($this->_connectionMock); } + /** + * Set up the PersistentCollection used for collection initialization tests. + */ + public function setUpPersistentCollection() + { + $classMetaData = $this->_emMock->getClassMetadata('Doctrine\Tests\Models\ECommerce\ECommerceCart'); + $this->collection = new PersistentCollection($this->_emMock, $classMetaData, new ArrayCollection); + $this->collection->setInitialized(false); + $this->collection->setOwner(new ECommerceCart(), $classMetaData->getAssociationMapping('products')); + } + public function testCanBePutInLazyLoadingMode() { $class = $this->_emMock->getClassMetadata('Doctrine\Tests\Models\ECommerce\ECommerceProduct'); @@ -34,4 +50,34 @@ class PersistentCollectionTest extends \Doctrine\Tests\OrmTestCase $collection->setInitialized(false); $this->assertFalse($collection->isInitialized()); } + + /** + * Test that PersistentCollection::current() initializes the collection. + */ + public function testCurrentInitializesCollection() + { + $this->setUpPersistentCollection(); + $this->collection->current(); + $this->assertTrue($this->collection->isInitialized()); + } + + /** + * Test that PersistentCollection::key() initializes the collection. + */ + public function testKeyInitializesCollection() + { + $this->setUpPersistentCollection(); + $this->collection->key(); + $this->assertTrue($this->collection->isInitialized()); + } + + /** + * Test that PersistentCollection::next() initializes the collection. + */ + public function testNextInitializesCollection() + { + $this->setUpPersistentCollection(); + $this->collection->next(); + $this->assertTrue($this->collection->isInitialized()); + } } From 9b574ad53b592225fe08393ac07aa1a3c127d20b Mon Sep 17 00:00:00 2001 From: Roger Llopart Pla Date: Wed, 24 Jul 2013 13:07:06 +0200 Subject: [PATCH 210/332] Update Parser.php Fix the docummentation for Parser::Literal() --- lib/Doctrine/ORM/Query/Parser.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index a82d9d82f..b64785dfd 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -2552,7 +2552,7 @@ class Parser /** * Literal ::= string | char | integer | float | boolean * - * @return string + * @return \Doctrine\ORM\Query\AST\Literal */ public function Literal() { From 69d4fdda1bcaffaf01b06de6f47885500e1080fd Mon Sep 17 00:00:00 2001 From: fabios Date: Fri, 26 Jul 2013 17:59:50 -0400 Subject: [PATCH 211/332] Fix proxy performance test --- .../Doctrine/Tests/ORM/Performance/ProxyPerformanceTest.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/Doctrine/Tests/ORM/Performance/ProxyPerformanceTest.php b/tests/Doctrine/Tests/ORM/Performance/ProxyPerformanceTest.php index b94fd812e..f06e273ab 100644 --- a/tests/Doctrine/Tests/ORM/Performance/ProxyPerformanceTest.php +++ b/tests/Doctrine/Tests/ORM/Performance/ProxyPerformanceTest.php @@ -113,6 +113,12 @@ class MockEntityManager extends EntityManager ); } + /** {@inheritDoc} */ + public function getMetadataFactory() + { + return $this->em->getMetadataFactory(); + } + /** {@inheritDoc} */ public function getClassMetadata($className) { From c28b457221c3b5a2e976f5145bf1ca5515ad87c1 Mon Sep 17 00:00:00 2001 From: Guilherme Blanco Date: Sun, 28 Jul 2013 20:30:42 -0400 Subject: [PATCH 212/332] Modified Hydrators to be per-query instances instead of a singleton-like approach. --- lib/Doctrine/ORM/AbstractQuery.php | 2 +- .../ORM/Decorator/EntityManagerDecorator.php | 8 ------ lib/Doctrine/ORM/EntityManager.php | 26 ------------------- lib/Doctrine/ORM/EntityManagerInterface.php | 1 - 4 files changed, 1 insertion(+), 36 deletions(-) diff --git a/lib/Doctrine/ORM/AbstractQuery.php b/lib/Doctrine/ORM/AbstractQuery.php index b32310512..8c41a3b21 100644 --- a/lib/Doctrine/ORM/AbstractQuery.php +++ b/lib/Doctrine/ORM/AbstractQuery.php @@ -787,7 +787,7 @@ abstract class AbstractQuery return $stmt; } - $data = $this->_em->getHydrator($this->_hydrationMode)->hydrateAll( + $data = $this->_em->newHydrator($this->_hydrationMode)->hydrateAll( $stmt, $this->_resultSetMapping, $this->_hints ); diff --git a/lib/Doctrine/ORM/Decorator/EntityManagerDecorator.php b/lib/Doctrine/ORM/Decorator/EntityManagerDecorator.php index dc123118f..23097edac 100644 --- a/lib/Doctrine/ORM/Decorator/EntityManagerDecorator.php +++ b/lib/Doctrine/ORM/Decorator/EntityManagerDecorator.php @@ -221,14 +221,6 @@ abstract class EntityManagerDecorator extends ObjectManagerDecorator implements return $this->wrapped->getUnitOfWork(); } - /** - * {@inheritdoc} - */ - public function getHydrator($hydrationMode) - { - return $this->wrapped->getHydrator($hydrationMode); - } - /** * {@inheritdoc} */ diff --git a/lib/Doctrine/ORM/EntityManager.php b/lib/Doctrine/ORM/EntityManager.php index acaec984d..220b9d313 100644 --- a/lib/Doctrine/ORM/EntityManager.php +++ b/lib/Doctrine/ORM/EntityManager.php @@ -99,13 +99,6 @@ use Doctrine\Common\Util\ClassUtils; */ private $eventManager; - /** - * The maintained (cached) hydrators. One instance per type. - * - * @var array - */ - private $hydrators = array(); - /** * The proxy factory used to create dynamic proxies. * @@ -834,25 +827,6 @@ use Doctrine\Common\Util\ClassUtils; return $this->unitOfWork; } - /** - * Gets a hydrator for the given hydration mode. - * - * This method caches the hydrator instances which is used for all queries that don't - * selectively iterate over the result. - * - * @param int $hydrationMode - * - * @return \Doctrine\ORM\Internal\Hydration\AbstractHydrator - */ - public function getHydrator($hydrationMode) - { - if ( ! isset($this->hydrators[$hydrationMode])) { - $this->hydrators[$hydrationMode] = $this->newHydrator($hydrationMode); - } - - return $this->hydrators[$hydrationMode]; - } - /** * Create a new instance for the given hydration mode. * diff --git a/lib/Doctrine/ORM/EntityManagerInterface.php b/lib/Doctrine/ORM/EntityManagerInterface.php index d72f7cd0c..aa26e36ab 100644 --- a/lib/Doctrine/ORM/EntityManagerInterface.php +++ b/lib/Doctrine/ORM/EntityManagerInterface.php @@ -51,7 +51,6 @@ interface EntityManagerInterface extends ObjectManager public function getConfiguration(); public function isOpen(); public function getUnitOfWork(); - public function getHydrator($hydrationMode); public function newHydrator($hydrationMode); public function getProxyFactory(); public function getFilters(); From 2dd73d4def895e7d8ff9db60180176ae92da0e82 Mon Sep 17 00:00:00 2001 From: Guilherme Blanco Date: Mon, 29 Jul 2013 10:46:47 -0400 Subject: [PATCH 213/332] Kept BC. --- .../ORM/Decorator/EntityManagerDecorator.php | 8 ++++++++ lib/Doctrine/ORM/EntityManager.php | 17 +++++++++++++++++ lib/Doctrine/ORM/EntityManagerInterface.php | 1 + 3 files changed, 26 insertions(+) diff --git a/lib/Doctrine/ORM/Decorator/EntityManagerDecorator.php b/lib/Doctrine/ORM/Decorator/EntityManagerDecorator.php index 23097edac..dc123118f 100644 --- a/lib/Doctrine/ORM/Decorator/EntityManagerDecorator.php +++ b/lib/Doctrine/ORM/Decorator/EntityManagerDecorator.php @@ -221,6 +221,14 @@ abstract class EntityManagerDecorator extends ObjectManagerDecorator implements return $this->wrapped->getUnitOfWork(); } + /** + * {@inheritdoc} + */ + public function getHydrator($hydrationMode) + { + return $this->wrapped->getHydrator($hydrationMode); + } + /** * {@inheritdoc} */ diff --git a/lib/Doctrine/ORM/EntityManager.php b/lib/Doctrine/ORM/EntityManager.php index 220b9d313..d21d3fb86 100644 --- a/lib/Doctrine/ORM/EntityManager.php +++ b/lib/Doctrine/ORM/EntityManager.php @@ -827,6 +827,23 @@ use Doctrine\Common\Util\ClassUtils; return $this->unitOfWork; } + /** + * Gets a hydrator for the given hydration mode. + * + * This method caches the hydrator instances which is used for all queries that don't + * selectively iterate over the result. + * + * @deprecated + * + * @param int $hydrationMode + * + * @return \Doctrine\ORM\Internal\Hydration\AbstractHydrator + */ + public function getHydrator($hydrationMode) + { + return $this->newHydrator($hydrationMode); + } + /** * Create a new instance for the given hydration mode. * diff --git a/lib/Doctrine/ORM/EntityManagerInterface.php b/lib/Doctrine/ORM/EntityManagerInterface.php index aa26e36ab..d72f7cd0c 100644 --- a/lib/Doctrine/ORM/EntityManagerInterface.php +++ b/lib/Doctrine/ORM/EntityManagerInterface.php @@ -51,6 +51,7 @@ interface EntityManagerInterface extends ObjectManager public function getConfiguration(); public function isOpen(); public function getUnitOfWork(); + public function getHydrator($hydrationMode); public function newHydrator($hydrationMode); public function getProxyFactory(); public function getFilters(); From 1fbe1ffc5a5ddb00d074d7090c533a9758d3260e Mon Sep 17 00:00:00 2001 From: "Fabio B. Silva" Date: Mon, 29 Jul 2013 13:10:09 -0400 Subject: [PATCH 214/332] fix DDC-2579 --- .../ORM/Persisters/BasicEntityPersister.php | 4 +- .../ORM/Functional/Ticket/DDC2579Test.php | 180 ++++++++++++++++++ 2 files changed, 182 insertions(+), 2 deletions(-) create mode 100644 tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2579Test.php diff --git a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php index 66e949ca8..00b00cfa2 100644 --- a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php @@ -567,8 +567,8 @@ class BasicEntityPersister $tableName = $this->quoteStrategy->getTableName($class, $this->platform); $idColumns = $this->quoteStrategy->getIdentifierColumnNames($class, $this->platform); $id = array_combine($idColumns, $identifier); + $types = array_map(function ($identifier) use ($class, $em) { - $types = array_map(function ($identifier) use ($class, $em) { if (isset($class->fieldMappings[$identifier])) { return $class->fieldMappings[$identifier]['type']; } @@ -580,7 +580,7 @@ class BasicEntityPersister } if (isset($targetMapping->associationMappings[$targetMapping->identifier[0]])) { - $types[] = $targetMapping->associationMappings[$targetMapping->identifier[0]]['type']; + return $targetMapping->associationMappings[$targetMapping->identifier[0]]['type']; } throw ORMException::unrecognizedField($targetMapping->identifier[0]); diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2579Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2579Test.php new file mode 100644 index 000000000..fe68ff564 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2579Test.php @@ -0,0 +1,180 @@ +_schemaTool->createSchema(array( + $this->_em->getClassMetadata(DDC2579Entity::CLASSNAME), + $this->_em->getClassMetadata(DDC2579EntityAssoc::CLASSNAME), + $this->_em->getClassMetadata(DDC2579AssocAssoc::CLASSNAME), + )); + } + + public function testIssue() + { + $id = new DDC2579Id("foo"); + $assoc = new DDC2579AssocAssoc($id); + $assocAssoc = new DDC2579EntityAssoc($assoc); + $entity = new DDC2579Entity($assocAssoc); + $repository = $this->_em->getRepository(DDC2579Entity::CLASSNAME); + + $this->_em->persist($assoc); + $this->_em->persist($assocAssoc); + $this->_em->persist($entity); + $this->_em->flush(); + + $entity->value++; + + $this->_em->persist($entity); + $this->_em->flush(); + $this->_em->clear(); + + $id = $entity->id; + $value = $entity->value; + $criteria = array('assoc' => $assoc, 'id' => $id); + $entity = $repository->findOneBy($criteria); + + $this->assertInstanceOf(DDC2579Entity::CLASSNAME, $entity); + $this->assertEquals($value, $entity->value); + + $this->_em->remove($entity); + $this->_em->flush(); + $this->_em->clear(); + + $this->assertNull($repository->findOneBy($criteria)); + $this->assertCount(0, $repository->findAll()); + } +} + +/** + * @Entity + */ +class DDC2579Entity +{ + + const CLASSNAME = __CLASS__; + + /** + * @Id + * @Column(type="ddc2579") + */ + public $id; + + /** + * @Id + * @ManyToOne(targetEntity="DDC2579EntityAssoc") + * @JoinColumn(name="relation_id", referencedColumnName="association_id") + */ + public $assoc; + + /** + * @Column(type="integer") + */ + public $value; + + public function __construct(DDC2579EntityAssoc $assoc, $value = 0) + { + $this->id = $assoc->assocAssoc->associationId; + $this->assoc = $assoc; + $this->value = $value; + } + +} + +/** + * @Entity + */ +class DDC2579EntityAssoc +{ + const CLASSNAME = __CLASS__; + + /** + * @Id + * @ManyToOne(targetEntity="DDC2579AssocAssoc") + * @JoinColumn(name="association_id", referencedColumnName="associationId") + */ + public $assocAssoc; + + public function __construct(DDC2579AssocAssoc $assocAssoc) + { + $this->assocAssoc = $assocAssoc; + } +} + +/** + * @Entity + */ +class DDC2579AssocAssoc +{ + const CLASSNAME = __CLASS__; + + /** + * @Id + * @Column(type="ddc2579") + */ + public $associationId; + + public function __construct(DDC2579Id $id) + { + $this->associationId = $id; + } +} + + +class DDC2579Type extends StringType +{ + const NAME = 'ddc2579'; + const CLASSNAME = __CLASS__; + + /** + * {@inheritdoc} + */ + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + return (string)$value; + } + + public function convertToPhpValue($value, AbstractPlatform $platform) + { + return new DDC2579Id($value); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return self::NAME; + } +} + +class DDC2579Id +{ + const CLASSNAME = __CLASS__; + + private $val; + + public function __construct($val) + { + $this->val = $val; + } + + public function __toString() + { + return $this->val; + } +} From d4814dec4251a7d14335050ad091513c167c4405 Mon Sep 17 00:00:00 2001 From: Guilherme Blanco Date: Mon, 29 Jul 2013 21:24:08 -0400 Subject: [PATCH 215/332] Synchronized support of FilterCollection with ODM by adding missing method. --- lib/Doctrine/ORM/Query/FilterCollection.php | 21 +++- .../Tests/ORM/Query/FilterCollectionTest.php | 95 +++++++++++++++++++ 2 files changed, 113 insertions(+), 3 deletions(-) create mode 100644 tests/Doctrine/Tests/ORM/Query/FilterCollectionTest.php diff --git a/lib/Doctrine/ORM/Query/FilterCollection.php b/lib/Doctrine/ORM/Query/FilterCollection.php index e26d093f8..62fb32813 100644 --- a/lib/Doctrine/ORM/Query/FilterCollection.php +++ b/lib/Doctrine/ORM/Query/FilterCollection.php @@ -104,11 +104,13 @@ class FilterCollection */ public function enable($name) { - if (null === $filterClass = $this->config->getFilterClassName($name)) { + if ( ! $this->has($name)) { throw new \InvalidArgumentException("Filter '" . $name . "' does not exist."); } - if (!isset($this->enabledFilters[$name])) { + if ( ! $this->isEnabled($name)) { + $filterClass = $this->config->getFilterClassName($name); + $this->enabledFilters[$name] = new $filterClass($this->em); // Keep the enabled filters sorted for the hash @@ -154,13 +156,25 @@ class FilterCollection */ public function getFilter($name) { - if (!isset($this->enabledFilters[$name])) { + if ( ! $this->isEnabled($name)) { throw new \InvalidArgumentException("Filter '" . $name . "' is not enabled."); } return $this->enabledFilters[$name]; } + /** + * Checks whether filter with given name is defined. + * + * @param string $name Name of the filter. + * + * @return bool true if the filter exists, false if not. + */ + public function has($name) + { + return null !== $this->config->getFilterClassName($name); + } + /** * Checks if a filter is enabled. * @@ -194,6 +208,7 @@ class FilterCollection } $filterHash = ''; + foreach ($this->enabledFilters as $name => $filter) { $filterHash .= $name . $filter; } diff --git a/tests/Doctrine/Tests/ORM/Query/FilterCollectionTest.php b/tests/Doctrine/Tests/ORM/Query/FilterCollectionTest.php new file mode 100644 index 000000000..42266bf50 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Query/FilterCollectionTest.php @@ -0,0 +1,95 @@ + + */ +class FilterCollectionTest extends \Doctrine\Tests\OrmTestCase +{ + /** + * @var \Doctrine\ORM\EntityManager + */ + private $em; + + protected function setUp() + { + $this->em = $this->_getTestEntityManager(); + + $this->em->getConfiguration()->addFilter('testFilter', 'Doctrine\Tests\ORM\Query\MyFilter'); + } + + public function testEnable() + { + $filterCollection = $this->em->getFilters(); + + $this->assertCount(0, $filterCollection->getEnabledFilters()); + + $filterCollection->enable('testFilter'); + + $enabledFilters = $filterCollection->getEnabledFilters(); + + $this->assertCount(1, $enabledFilters); + $this->assertContainsOnly('Doctrine\Tests\ORM\Query\MyFilter', $enabledFilters); + + $filterCollection->disable('testFilter'); + $this->assertCount(0, $filterCollection->getEnabledFilters()); + } + + public function testHasFilter() + { + $filterCollection = $this->em->getFilters(); + + $this->assertTrue($filterCollection->has('testFilter')); + $this->assertFalse($filterCollection->has('fakeFilter')); + } + + /** + * @depends testEnable + */ + public function testIsEnabled() + { + $filterCollection = $this->em->getFilters(); + + $this->assertFalse($filterCollection->isEnabled('testFilter')); + + $filterCollection->enable('testFilter'); + + $this->assertTrue($filterCollection->isEnabled('testFilter')); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testGetFilterInvalidArgument() + { + $filterCollection = $this->em->getFilters(); + + $filterCollection->getFilter('testFilter'); + } + + public function testGetFilter() + { + $filterCollection = $this->em->getFilters(); + + $filterCollection->enable('testFilter'); + + $this->assertInstanceOf('Doctrine\Tests\ORM\Query\MyFilter', $filterCollection->getFilter('testFilter')); + } +} + +class MyFilter extends SQLFilter +{ + public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias) + { + // getParameter applies quoting automatically + return $targetTableAlias . '.id = ' . $this->getParameter('id'); + } +} \ No newline at end of file From c7b4c9bf0fbc5dca830e0a797cea470510def994 Mon Sep 17 00:00:00 2001 From: Guilherme Blanco Date: Tue, 30 Jul 2013 01:29:34 -0400 Subject: [PATCH 216/332] Fixed DDC-1884. --- .../ORM/Internal/Hydration/ArrayHydrator.php | 26 +-- tests/Doctrine/Tests/Models/Taxi/Car.php | 42 +++++ tests/Doctrine/Tests/Models/Taxi/Driver.php | 37 ++++ tests/Doctrine/Tests/Models/Taxi/PaidRide.php | 42 +++++ tests/Doctrine/Tests/Models/Taxi/Ride.php | 32 ++++ .../ORM/Functional/Ticket/DDC1884Test.php | 158 ++++++++++++++++++ .../Doctrine/Tests/OrmFunctionalTestCase.php | 14 +- 7 files changed, 338 insertions(+), 13 deletions(-) create mode 100644 tests/Doctrine/Tests/Models/Taxi/Car.php create mode 100644 tests/Doctrine/Tests/Models/Taxi/Driver.php create mode 100644 tests/Doctrine/Tests/Models/Taxi/PaidRide.php create mode 100644 tests/Doctrine/Tests/Models/Taxi/Ride.php create mode 100644 tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1884Test.php diff --git a/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php index bb18f32c6..98cb937d2 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php @@ -185,7 +185,7 @@ class ArrayHydrator extends AbstractHydrator if ( ! isset($nonemptyComponents[$dqlAlias]) && ! isset($baseElement[$relationAlias])) { $baseElement[$relationAlias] = null; - } else if ( ! isset($baseElement[$relationAlias])) { + } else if ( ! isset($baseElement[$relationAlias]) || ! isset($this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]])) { $baseElement[$relationAlias] = $data; } } @@ -204,19 +204,20 @@ class ArrayHydrator extends AbstractHydrator // if this row has a NULL value for the root result id then make it a null result. if ( ! isset($nonemptyComponents[$dqlAlias]) ) { - if ($this->_rsm->isMixed) { - $result[] = array($entityKey => null); - } else { - $result[] = null; - } + $result[] = ($this->_rsm->isMixed) + ? array($entityKey => null) + : null; + $resultKey = $this->_resultCounter; ++$this->_resultCounter; + continue; } // Check for an existing element if ($this->_isSimpleQuery || ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) { $element = $rowData[$dqlAlias]; + if ($this->_rsm->isMixed) { $element = array($entityKey => $element); } @@ -239,6 +240,7 @@ class ArrayHydrator extends AbstractHydrator ++$this->_resultCounter; }*/ } + $this->updateResultPointer($result, $index, $dqlAlias, false); } } @@ -279,6 +281,12 @@ class ArrayHydrator extends AbstractHydrator return; } + if ($oneToOne) { + $this->_resultPointers[$dqlAlias] =& $coll; + + return; + } + if ($index !== false) { $this->_resultPointers[$dqlAlias] =& $coll[$index]; @@ -289,12 +297,6 @@ class ArrayHydrator extends AbstractHydrator return; } - if ($oneToOne) { - $this->_resultPointers[$dqlAlias] =& $coll; - - return; - } - end($coll); $this->_resultPointers[$dqlAlias] =& $coll[key($coll)]; diff --git a/tests/Doctrine/Tests/Models/Taxi/Car.php b/tests/Doctrine/Tests/Models/Taxi/Car.php new file mode 100644 index 000000000..2db5ecd36 --- /dev/null +++ b/tests/Doctrine/Tests/Models/Taxi/Car.php @@ -0,0 +1,42 @@ +brand = $brand; + } + + public function setModel($model) + { + $this->model = $model; + } +} diff --git a/tests/Doctrine/Tests/Models/Taxi/Driver.php b/tests/Doctrine/Tests/Models/Taxi/Driver.php new file mode 100644 index 000000000..ccb493f1d --- /dev/null +++ b/tests/Doctrine/Tests/Models/Taxi/Driver.php @@ -0,0 +1,37 @@ +name = $name; + } +} diff --git a/tests/Doctrine/Tests/Models/Taxi/PaidRide.php b/tests/Doctrine/Tests/Models/Taxi/PaidRide.php new file mode 100644 index 000000000..040e3917e --- /dev/null +++ b/tests/Doctrine/Tests/Models/Taxi/PaidRide.php @@ -0,0 +1,42 @@ +driver = $driver; + $this->car = $car; + } + + public function setFare($fare) + { + $this->fare = $fare; + } +} diff --git a/tests/Doctrine/Tests/Models/Taxi/Ride.php b/tests/Doctrine/Tests/Models/Taxi/Ride.php new file mode 100644 index 000000000..e90a38217 --- /dev/null +++ b/tests/Doctrine/Tests/Models/Taxi/Ride.php @@ -0,0 +1,32 @@ +driver = $driver; + $this->car = $car; + } +} diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1884Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1884Test.php new file mode 100644 index 000000000..de02e3067 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1884Test.php @@ -0,0 +1,158 @@ + + */ +class DDC1884Test extends \Doctrine\Tests\OrmFunctionalTestCase +{ + protected function setUp() + { + $this->useModelSet('taxi'); + parent::setUp(); + + list($bimmer, $crysler, $merc, $volvo) = $this->createCars('Doctrine\Tests\Models\Taxi\Car'); + list($john, $foo) = $this->createDrivers('Doctrine\Tests\Models\Taxi\Driver'); + $this->_em->flush(); + + $ride1 = new Ride($john, $bimmer); + $ride2 = new Ride($john, $merc); + $ride3 = new Ride($john, $volvo); + $ride4 = new Ride($foo, $merc); + + $this->_em->persist($ride1); + $this->_em->persist($ride2); + $this->_em->persist($ride3); + $this->_em->persist($ride4); + + $ride5 = new PaidRide($john, $bimmer); + $ride5->setFare(10.50); + + $ride6 = new PaidRide($john, $merc); + $ride6->setFare(16.00); + + $ride7 = new PaidRide($john, $volvo); + $ride7->setFare(20.70); + + $ride8 = new PaidRide($foo, $merc); + $ride8->setFare(32.15); + + $this->_em->persist($ride5); + $this->_em->persist($ride6); + $this->_em->persist($ride7); + $this->_em->persist($ride8); + + $this->_em->flush(); + } + + private function createCars($class) + { + $bimmer = new $class; + $bimmer->setBrand('BMW'); + $bimmer->setModel('7-Series'); + + $crysler = new $class; + $crysler->setBrand('Crysler'); + $crysler->setModel('300'); + + $merc = new $class; + $merc->setBrand('Mercedes'); + $merc->setModel('C-Class'); + + $volvo = new $class; + $volvo->setBrand('Volvo'); + $volvo->setModel('XC90'); + + $this->_em->persist($bimmer); + $this->_em->persist($crysler); + $this->_em->persist($merc); + $this->_em->persist($volvo); + + return array($bimmer, $crysler, $merc, $volvo); + } + + private function createDrivers($class) + { + $john = new $class; + $john->setName('John Doe'); + + $foo = new $class; + $foo->setName('Foo Bar'); + + $this->_em->persist($foo); + $this->_em->persist($john); + + return array($john, $foo); + } + + /** + * 1) Ride contains only columns that are part of its composite primary key + * 2) We use fetch joins here + */ + public function testSelectFromInverseSideWithCompositePkAndSolelyIdentifierColumnsUsingFetchJoins() + { + $qb = $this->_em->createQueryBuilder(); + + $result = $qb->select('d, dr, c') + ->from('Doctrine\Tests\Models\Taxi\Driver', 'd') + ->leftJoin('d.freeDriverRides', 'dr') + ->leftJoin('dr.car', 'c') + ->where('d.name = ?1') + ->setParameter(1, 'John Doe') + ->getQuery() + ->getArrayResult(); + + $this->assertCount(1, $result); + $this->assertArrayHasKey('freeDriverRides', $result[0]); + $this->assertCount(3, $result[0]['freeDriverRides']); + } + + /** + * 1) PaidRide contains an extra column that is not part of the composite primary key + * 2) Again we will use fetch joins + */ + public function testSelectFromInverseSideWithCompositePkUsingFetchJoins() + { + $qb = $this->_em->createQueryBuilder(); + + $result = $qb->select('d, dr, c') + ->from('Doctrine\Tests\Models\Taxi\Driver', 'd') + ->leftJoin('d.driverRides', 'dr') + ->leftJoin('dr.car', 'c') + ->where('d.name = ?1') + ->setParameter(1, 'John Doe') + ->getQuery()->getArrayResult(); + + $this->assertCount(1, $result); + $this->assertArrayHasKey('driverRides', $result[0]); + $this->assertCount(3, $result[0]['driverRides']); + } + + /** + * The other way around will fail too + */ + public function testSelectFromOwningSideUsingFetchJoins() + { + $qb = $this->_em->createQueryBuilder(); + + $result = $qb->select('r, d, c') + ->from('Doctrine\Tests\Models\Taxi\PaidRide', 'r') + ->leftJoin('r.driver', 'd') + ->leftJoin('r.car', 'c') + ->where('d.name = ?1') + ->setParameter(1, 'John Doe') + ->getQuery()->getArrayResult(); + + $this->assertCount(3, $result); + $this->assertArrayHasKey('driver', $result[0]); + $this->assertArrayHasKey('car', $result[0]); + } +} diff --git a/tests/Doctrine/Tests/OrmFunctionalTestCase.php b/tests/Doctrine/Tests/OrmFunctionalTestCase.php index 9c922802c..1b70be721 100644 --- a/tests/Doctrine/Tests/OrmFunctionalTestCase.php +++ b/tests/Doctrine/Tests/OrmFunctionalTestCase.php @@ -155,7 +155,13 @@ abstract class OrmFunctionalTestCase extends OrmTestCase 'Doctrine\Tests\Models\CompositeKeyInheritance\JoinedChildClass', 'Doctrine\Tests\Models\CompositeKeyInheritance\SingleRootClass', 'Doctrine\Tests\Models\CompositeKeyInheritance\SingleChildClass', - ) + ), + 'taxi' => array( + 'Doctrine\Tests\Models\Taxi\PaidRide', + 'Doctrine\Tests\Models\Taxi\Ride', + 'Doctrine\Tests\Models\Taxi\Car', + 'Doctrine\Tests\Models\Taxi\Driver', + ), ); /** @@ -284,6 +290,12 @@ abstract class OrmFunctionalTestCase extends OrmTestCase $conn->executeUpdate('DELETE FROM SingleRootClass'); } + if (isset($this->_usedModelSets['taxi'])) { + $conn->executeUpdate('DELETE FROM taxi_paid_ride'); + $conn->executeUpdate('DELETE FROM taxi_ride'); + $conn->executeUpdate('DELETE FROM taxi_car'); + $conn->executeUpdate('DELETE FROM taxi_driver'); + } $this->_em->clear(); } From d5bc48623aa3c3aa0d1f90796ca4597f6cd7b1e5 Mon Sep 17 00:00:00 2001 From: Christian Morgan Date: Tue, 30 Jul 2013 14:00:17 +0100 Subject: [PATCH 217/332] Cleaned up events.rst Was a mix-up between TestEventSubscriber and EventTest (e.g. the definition of TestEventSubscriber referenced TestEvent::preFoo, which did not exist). To clarify this I've renamed EventTest to TestEvent. Tried to clarify the text in the Naming Convention section. Added note that onClear is not a lifecycle callback. Tried to clarify the definition of Lifecycle Callbacks. Separated key/value descriptions into XML and YAML parts since the details are different Added note in Implementing Event Listeners section that since 2.4 you do have access to EntityManager and UnitOfWork from lifecycle callbacks. Added example about how to use the computed changeset to modify a primitive value in preUpdate section Added naming convention example to Entity listeners class section The other changes are typos and small fixes. --- docs/en/reference/events.rst | 110 ++++++++++++++++++++--------------- 1 file changed, 63 insertions(+), 47 deletions(-) diff --git a/docs/en/reference/events.rst b/docs/en/reference/events.rst index 517f2ec5b..a3e2e50e4 100644 --- a/docs/en/reference/events.rst +++ b/docs/en/reference/events.rst @@ -20,12 +20,12 @@ manager. $evm = new EventManager(); Now we can add some event listeners to the ``$evm``. Let's create a -``EventTest`` class to play around with. +``TestEvent`` class to play around with. .. code-block:: php dispatchEvent(EventTest::preFoo); - $evm->dispatchEvent(EventTest::postFoo); + $evm->dispatchEvent(TestEvent::preFoo); + $evm->dispatchEvent(TestEvent::postFoo); You can easily remove a listener with the ``removeEventListener()`` method. @@ -133,13 +133,12 @@ several reasons: - It is easy to read. - Simplicity. - Each method within an EventSubscriber is named after the - corresponding constant. If constant name and constant value differ, - you MUST use the new value and thus, your code might be subject to - codechanges when the value changes. This contradicts the intention - of a constant. + corresponding constant's value. If the constant's name and value differ + it contradicts the intention of using the constant and makes your code + harder to maintain. An example for a correct notation can be found in the example -``EventTest`` above. +``TestEvent`` above. .. _reference-events-lifecycle-events: @@ -159,7 +158,7 @@ the life-time of their registered entities. - prePersist - The prePersist event occurs for a given entity before the respective EntityManager persist operation for that entity is executed. It should be noted that this event is only triggered on - *initial* persist of an entity + *initial* persist of an entity (i.e. it does not trigger on future updates). - postPersist - The postPersist event occurs for an entity after the entity has been made persistent. It will be invoked after the database insert operations. Generated primary key values are @@ -183,7 +182,7 @@ the life-time of their registered entities. event is not a lifecycle callback. - onClear - The onClear event occurs when the EntityManager#clear() operation is invoked, after all references to entities have been removed from the unit of - work. + work. This event is not a lifecycle callback. .. warning:: @@ -205,12 +204,14 @@ ORM package. These can be hooked into by two different types of event listeners: - - Lifecycle Callbacks are methods on the entity classes that are - called when the event is triggered. They receives some kind of ``EventArgs``. + called when the event is triggered. As of v2.4 they receive some kind + of ``EventArgs`` instance. - Lifecycle Event Listeners and Subscribers are classes with specific callback - methods that receives some kind of ``EventArgs`` instance which - give access to the entity, EntityManager or other relevant data. + methods that receives some kind of ``EventArgs`` instance. + +The EventArgs instance received by the listener gives access to the entity, +EntityManager and other relevant data. .. note:: @@ -224,10 +225,10 @@ listeners: Lifecycle Callbacks ------------------- -A lifecycle event is a regular event with the additional feature of -providing a mechanism to register direct callbacks inside the -corresponding entity classes that are executed when the lifecycle -event occurs. +Lifecycle Callbacks are defined on an entity class. They allow you to +trigger callbacks whenever an instance of that entity class experiences +a relevant lifecycle event. More than one callback can be defined for each +lifecycle event. .. code-block:: php @@ -292,9 +293,13 @@ can do it with the following. name: type: string(50) lifecycleCallbacks: - prePersist: [ doStuffOnPrePersist, doOtherStuffOnPrePersistToo ] + prePersist: [ doStuffOnPrePersist, doOtherStuffOnPrePersist ] postPersist: [ doStuffOnPostPersist ] +In YAML the ``key`` of the lifecycleCallbacks entry is the event that you +are triggering on and the values are the methods to call. The allowed event +types are the ones listed in the previous Lifecycle Events section. + XML would look something like this: .. code-block:: xml @@ -317,9 +322,14 @@ XML would look something like this: -You just need to make sure a public ``doStuffOnPrePersist()`` and -``doStuffOnPostPersist()`` method is defined on your ``User`` -model. +In XML the ``type`` of the lifecycle-callback entry is the event that you +are triggering on and the ``method`` is the method to call. The allowed event +types are the ones listed in the previous Lifecycle Events section. + +When using YAML or XML you need to remember to create public methods to match the +callback names you defined. E.g. in these examples ``doStuffOnPrePersist()``, +``doOtherStuffOnPrePersist()`` and ``doStuffOnPostPersist()`` methods need to be +defined on your ``User`` model. .. code-block:: php @@ -335,15 +345,17 @@ model. // ... } + public function doOtherStuffOnPrePersist() + { + // ... + } + public function doStuffOnPostPersist() { // ... } } -The ``key`` of the lifecycleCallbacks is the name of the method and -the value is the event type. The allowed event types are the ones -listed in the previous Lifecycle Events section. Lifecycle Callbacks Event Argument ----------------------------------- @@ -375,8 +387,10 @@ Listening and subscribing to Lifecycle Events Lifecycle event listeners are much more powerful than the simple lifecycle callbacks that are defined on the entity classes. They -allow to implement re-usable behaviors between different entity -classes, yet require much more detailed knowledge about the inner +sit at a level above the entities and allow you to implement re-usable +behaviors across different entity classes. + +Note that they require much more detailed knowledge about the inner workings of the EntityManager and UnitOfWork. Please read the *Implementing Event Listeners* section carefully if you are trying to write your own listener. @@ -476,8 +490,8 @@ data and lost updates/persists/removes. For the described events that are also lifecycle callback events the restrictions apply as well, with the additional restriction -that you do not have access to the EntityManager or UnitOfWork APIs -inside these events. +that (prior to version 2.4) you do not have access to the +EntityManager or UnitOfWork APIs inside these events. prePersist ~~~~~~~~~~ @@ -501,10 +515,10 @@ The following restrictions apply to ``prePersist``: - If you are using a PrePersist Identity Generator such as sequences the ID value will *NOT* be available within any PrePersist events. -- Doctrine will not recognize changes made to relations in a pre - persist event called by "reachability" through a cascade persist - unless you use the internal ``UnitOfWork`` API. We do not recommend - such operations in the persistence by reachability context, so do +- Doctrine will not recognize changes made to relations in a prePersist + event called by "reachability" through a cascade persist unless you + use the internal ``UnitOfWork`` API. We do not recommend such + operations in the persistence by reachability context, so do this at your own risk and possibly supported by unit-tests. preRemove @@ -593,13 +607,13 @@ mentioned sets. See this example: The following restrictions apply to the onFlush event: -- If you create and persist a new entity in "onFlush", then +- If you create and persist a new entity in ``onFlush``, then calling ``EntityManager#persist()`` is not enough. You have to execute an additional call to ``$unitOfWork->computeChangeSet($classMetadata, $entity)``. - Changing primitive fields or associations requires you to explicitly trigger a re-computation of the changeset of the - affected entity. This can be done by either calling + affected entity. This can be done by calling ``$unitOfWork->recomputeSingleEntityChangeSet($classMetadata, $entity)``. postFlush @@ -699,7 +713,8 @@ Restrictions for this event: recognized by the flush operation anymore. - Changes to fields of the passed entities are not recognized by the flush operation anymore, use the computed change-set passed to - the event to modify primitive field values. + the event to modify primitive field values, e.g. use + ``$eventArgs->setNewValue($field, $value);`` as in the Alice to Bob example above. - Any calls to ``EntityManager#persist()`` or ``EntityManager#remove()``, even in combination with the UnitOfWork API are strongly discouraged and don't work as expected outside the @@ -769,9 +784,10 @@ An ``Entity Listener`` could be any class, by default it should be a class with - Different from :ref:`reference-events-implementing-listeners` an ``Entity Listener`` is invoked just to the specified entity - An entity listener method receives two arguments, the entity instance and the lifecycle event. -- A callback method could be defined by naming convention or specifying a method mapping. -- When the listener mapping is not given the parser will lookup for methods that match with the naming convention. -- When the listener mapping is given the parser won't lookup for any naming convention. +- The callback method can be defined by naming convention or specifying a method mapping. +- When a listener mapping is not given the parser will use the naming convention to look for a matching method, + e.g. it will look for a public ``preUpdate()`` method if you are listening to the ``preUpdate`` event. +- When a listener mapping is given the parser will not look for any methods using the naming convention. .. code-block:: php @@ -784,8 +800,8 @@ An ``Entity Listener`` could be any class, by default it should be a class with } } -To define a specific event listener method -you should map the listener method using the event type mapping. +To define a specific event listener method (one that does not follow the naming convention) +you need to map the listener method using the event type mapping: .. configuration-block:: @@ -863,9 +879,9 @@ you should map the listener method using the event type mapping. Entity listeners resolver ~~~~~~~~~~~~~~~~~~~~~~~~~~ -Doctrine invoke the listener resolver to get the listener instance. +Doctrine invokes the listener resolver to get the listener instance. -- An resolver allows you register a specific ``Entity Listener`` instance. +- A resolver allows you register a specific entity listener instance. - You can also implement your own resolver by extending ``Doctrine\ORM\Mapping\DefaultEntityListenerResolver`` or implementing ``Doctrine\ORM\Mapping\EntityListenerResolver`` Specifying an entity listener instance : @@ -933,12 +949,12 @@ process and manipulate the instance. .. code-block:: php getMetadataFactory(); $evm = $em->getEventManager(); $evm->addEventListener(Events::loadClassMetadata, $test); - class EventTest + class TestEvent { public function loadClassMetadata(\Doctrine\ORM\Event\LoadClassMetadataEventArgs $eventArgs) { From 157588f6dca20ed8447d1f6d61a96c1be63e3f85 Mon Sep 17 00:00:00 2001 From: Guilherme Blanco Date: Tue, 30 Jul 2013 12:11:08 -0400 Subject: [PATCH 218/332] CS fixes. --- .../ORM/Internal/Hydration/ArrayHydrator.php | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php index 98cb937d2..5688d8652 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php @@ -146,6 +146,7 @@ class ArrayHydrator extends AbstractHydrator $baseElement =& $this->_resultPointers[$parent]; } else { unset($this->_resultPointers[$dqlAlias]); // Ticket #1228 + continue; } @@ -167,6 +168,7 @@ class ArrayHydrator extends AbstractHydrator if ( ! $indexExists || ! $indexIsValid) { $element = $data; + if (isset($this->_rsm->indexByMap[$dqlAlias])) { $baseElement[$relationAlias][$row[$this->_rsm->indexByMap[$dqlAlias]]] = $element; } else { @@ -183,9 +185,15 @@ class ArrayHydrator extends AbstractHydrator } else { $oneToOne = true; - if ( ! isset($nonemptyComponents[$dqlAlias]) && ! isset($baseElement[$relationAlias])) { + if ( + ( ! isset($nonemptyComponents[$dqlAlias])) && + ( ! isset($baseElement[$relationAlias])) + ) { $baseElement[$relationAlias] = null; - } else if ( ! isset($baseElement[$relationAlias]) || ! isset($this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]])) { + } else if ( + ( ! isset($baseElement[$relationAlias])) || + ( ! isset($this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]])) + ) { $baseElement[$relationAlias] = $data; } } @@ -195,7 +203,6 @@ class ArrayHydrator extends AbstractHydrator if ($coll !== null) { $this->updateResultPointer($coll, $index, $dqlAlias, $oneToOne); } - } else { // It's a root result element @@ -204,7 +211,7 @@ class ArrayHydrator extends AbstractHydrator // if this row has a NULL value for the root result id then make it a null result. if ( ! isset($nonemptyComponents[$dqlAlias]) ) { - $result[] = ($this->_rsm->isMixed) + $result[] = $this->_rsm->isMixed ? array($entityKey => null) : null; @@ -216,11 +223,9 @@ class ArrayHydrator extends AbstractHydrator // Check for an existing element if ($this->_isSimpleQuery || ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) { - $element = $rowData[$dqlAlias]; - - if ($this->_rsm->isMixed) { - $element = array($entityKey => $element); - } + $element = $this->_rsm->isMixed + ? array($entityKey => $rowData[$dqlAlias]) + : $rowData[$dqlAlias]; if (isset($this->_rsm->indexByMap[$dqlAlias])) { $resultKey = $row[$this->_rsm->indexByMap[$dqlAlias]]; @@ -228,6 +233,7 @@ class ArrayHydrator extends AbstractHydrator } else { $resultKey = $this->_resultCounter; $result[] = $element; + ++$this->_resultCounter; } @@ -235,6 +241,7 @@ class ArrayHydrator extends AbstractHydrator } else { $index = $this->_identifierMap[$dqlAlias][$id[$dqlAlias]]; $resultKey = $index; + /*if ($this->_rsm->isMixed) { $result[] =& $result[$index]; ++$this->_resultCounter; @@ -249,11 +256,9 @@ class ArrayHydrator extends AbstractHydrator if (isset($scalars)) { if ( ! isset($resultKey) ) { // this only ever happens when no object is fetched (scalar result only) - if (isset($this->_rsm->indexByMap['scalars'])) { - $resultKey = $row[$this->_rsm->indexByMap['scalars']]; - } else { - $resultKey = $this->_resultCounter - 1; - } + $resultKey = isset($this->_rsm->indexByMap['scalars']) + ? $row[$this->_rsm->indexByMap['scalars']] + : $this->_resultCounter - 1; } foreach ($scalars as $name => $value) { From b3d0ad7a87026445525b3dd6722abfc03852ac82 Mon Sep 17 00:00:00 2001 From: Guilherme Blanco Date: Wed, 31 Jul 2013 01:24:02 -0400 Subject: [PATCH 219/332] Added coverage to DDC-2524. Updated DDC-1719 to fix related DBAL bug. --- .../ORM/Functional/Ticket/DDC1719Test.php | 22 ++- .../ORM/Functional/Ticket/DDC2524Test.php | 148 ++++++++++++++++++ 2 files changed, 162 insertions(+), 8 deletions(-) create mode 100644 tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2524Test.php diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1719Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1719Test.php index 8ea93a9ec..93130f56b 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1719Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1719Test.php @@ -11,18 +11,24 @@ require_once __DIR__ . '/../../../TestInit.php'; */ class DDC1719Test extends \Doctrine\Tests\OrmFunctionalTestCase { - - const CLASS_NAME = '\Doctrine\Tests\Models\Quote\SimpleEntity'; + const CLASS_NAME = 'Doctrine\Tests\Models\Quote\SimpleEntity'; protected function setUp() { parent::setUp(); - try { - $this->_schemaTool->createSchema(array( - $this->_em->getClassMetadata(self::CLASS_NAME), - )); - } catch(\Exception $e) { - } + + $this->_schemaTool->createSchema(array( + $this->_em->getClassMetadata(self::CLASS_NAME), + )); + } + + protected function tearDown() + { + parent::tearDown(); + + $this->_schemaTool->dropSchema(array( + $this->_em->getClassMetadata(self::CLASS_NAME), + )); } public function testCreateRetrieveUpdateDelete() diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2524Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2524Test.php new file mode 100644 index 000000000..c5a7cad70 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2524Test.php @@ -0,0 +1,148 @@ +_schemaTool->createSchema(array( + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2524EntityO'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2524EntityG'), + )); + } + + protected function tearDown() + { + parent::tearDown(); + + $this->_schemaTool->dropSchema(array( + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2524EntityO'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2524EntityG'), + )); + } + + public function testSingle() + { + $eO = new DDC2524EntityO(); + $eG = new DDC2524EntityG($eO); + + $this->_em->persist($eO); + $this->_em->flush(); + $this->_em->clear(); + + $eOloaded = $this->_em->find(__NAMESPACE__ . '\DDC2524EntityO', $eO->getId()); + + $this->_em->remove($eOloaded); + $this->_em->flush(); + } + + public function testMany() + { + $eO = new DDC2524EntityO(); + $eG1 = new DDC2524EntityG($eO); + $eG2 = new DDC2524EntityG($eO); + $eG3 = new DDC2524EntityG($eO); + + $eO->setOneToOneG($eG2); + + $this->_em->persist($eO); + $this->_em->flush(); + $this->_em->clear(); + + $eOloaded = $this->_em->find(__NAMESPACE__ . '\DDC2524EntityO', $eO->getId()); + + $this->_em->remove($eOloaded); + $this->_em->flush(); + } +} + +/** + * @Entity + */ +class DDC2524EntityO +{ + /** + * @Id @Column(type="integer") + * @GeneratedValue + */ + private $id; + + /** + * @OneToOne(targetEntity="Doctrine\Tests\ORM\Functional\Ticket\DDC2524EntityG") + * @JoinColmun(nullable=true) + **/ + private $oneToOneG; + + /** + * @OneToMany(targetEntity="Doctrine\Tests\ORM\Functional\Ticket\DDC2524EntityG", mappedBy="ownerO", cascade={"persist", "remove"}) + **/ + private $oneToManyG; + + public function __construct() + { + $this->oneToManyG = new ArrayCollection(); + } + + public function getId() + { + return $this->id; + } + + public function setOneToOneG(DDC2524EntityG $eG) + { + $this->oneToOneG = $eG; + } + + public function getOneToOneG() + { + return $this->oneToOneG; + } + + public function addOneToManyG(DDC2524EntityG $eG) + { + $this->oneToManyG->add($eG); + } + + public function getOneToManyGs() + { + return $this->oneToManyG->toArray(); + } +} + +/** + * @Entity + */ +class DDC2524EntityG +{ + /** + * @Id @Column(type="integer") + * @GeneratedValue + */ + private $id; + + /** + * @ManyToOne(targetEntity="Doctrine\Tests\ORM\Functional\Ticket\DDC2524EntityO", inversedBy="oneToManyG") + **/ + private $ownerO; + + public function __construct(DDC2524EntityO $eO, $position = 1) + { + $this->position = $position; + $this->ownerO = $eO; + + $this->ownerO->addOneToManyG($this); + } + + public function getId() + { + return $this->id; + } +} From c715d91dc73c8aebf68db3ea09d01d828f5cffbb Mon Sep 17 00:00:00 2001 From: Christian Morgan Date: Thu, 1 Aug 2013 17:56:45 +0100 Subject: [PATCH 220/332] Updates based on feedback from Ocramius Addresses all comments made so far, except the one about persists/updates --- docs/en/reference/events.rst | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/en/reference/events.rst b/docs/en/reference/events.rst index a3e2e50e4..7eccd3674 100644 --- a/docs/en/reference/events.rst +++ b/docs/en/reference/events.rst @@ -228,7 +228,8 @@ Lifecycle Callbacks Lifecycle Callbacks are defined on an entity class. They allow you to trigger callbacks whenever an instance of that entity class experiences a relevant lifecycle event. More than one callback can be defined for each -lifecycle event. +lifecycle event. Lifecycle Callbacks are best used for simple operations +specific to a particular entity class's lifecycle. .. code-block:: php @@ -278,8 +279,9 @@ lifecycle event. } } -Note that when using annotations you have to apply the -@HasLifecycleCallbacks marker annotation on the entity class. +Note that the methods set as lifecycle callbacks need to be public and, +when using these annotations, you have to apply the +``@HasLifecycleCallbacks`` marker annotation on the entity class. If you want to register lifecycle callbacks from YAML or XML you can do it with the following. @@ -297,8 +299,8 @@ can do it with the following. postPersist: [ doStuffOnPostPersist ] In YAML the ``key`` of the lifecycleCallbacks entry is the event that you -are triggering on and the values are the methods to call. The allowed event -types are the ones listed in the previous Lifecycle Events section. +are triggering on and the value is the method (or methods) to call. The allowed +event types are the ones listed in the previous Lifecycle Events section. XML would look something like this: From 3c613b9c023e64fb398e335071d2ecfead08b8dd Mon Sep 17 00:00:00 2001 From: J Bruni Date: Sat, 3 Aug 2013 05:44:15 -0300 Subject: [PATCH 221/332] Corrected PHP type for "decimal" mapping type "Basic Mapping" documentation says: "decimal: Type that maps a SQL DECIMAL to a PHP string." --- 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 c1e1c771e..a9e34d727 100644 --- a/lib/Doctrine/ORM/Tools/EntityGenerator.php +++ b/lib/Doctrine/ORM/Tools/EntityGenerator.php @@ -152,7 +152,7 @@ class EntityGenerator Type::SMALLINT => 'integer', Type::TEXT => 'string', Type::BLOB => 'string', - Type::DECIMAL => 'float', + Type::DECIMAL => 'string', Type::JSON_ARRAY => 'array', Type::SIMPLE_ARRAY => 'array', ); From 15c2c4dd23f4140bc579d660b9396722cbc4694a Mon Sep 17 00:00:00 2001 From: "J. Bruni" Date: Sat, 3 Aug 2013 10:44:02 -0300 Subject: [PATCH 222/332] Updated EntityGeneratorTest::testEntityTypeAlias --- tests/Doctrine/Tests/ORM/Tools/EntityGeneratorTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Doctrine/Tests/ORM/Tools/EntityGeneratorTest.php b/tests/Doctrine/Tests/ORM/Tools/EntityGeneratorTest.php index b49125cdc..831379ad5 100644 --- a/tests/Doctrine/Tests/ORM/Tools/EntityGeneratorTest.php +++ b/tests/Doctrine/Tests/ORM/Tools/EntityGeneratorTest.php @@ -548,9 +548,9 @@ class EntityGeneratorTest extends \Doctrine\Tests\OrmTestCase )), array(array( 'fieldName' => 'decimal', - 'phpType' => 'float', + 'phpType' => 'string', 'dbType' => 'decimal', - 'value' => 33.33 + 'value' => '33.33' ), )); } From 13c1efb240dd0af25ad0abe230df98ec895892c7 Mon Sep 17 00:00:00 2001 From: J Bruni Date: Sat, 3 Aug 2013 11:26:37 -0300 Subject: [PATCH 223/332] Update basic-mapping.rst The attribute name is "name", not "column", isn't it? --- docs/en/reference/basic-mapping.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/reference/basic-mapping.rst b/docs/en/reference/basic-mapping.rst index 2909048dd..6537b0b12 100644 --- a/docs/en/reference/basic-mapping.rst +++ b/docs/en/reference/basic-mapping.rst @@ -297,7 +297,7 @@ list: - ``type``: (optional, defaults to 'string') The mapping type to use for the column. -- ``column``: (optional, defaults to field name) The name of the +- ``name``: (optional, defaults to field name) The name of the column in the database. - ``length``: (optional, default 255) The length of the column in the database. (Applies only if a string-valued column is used). From d9c1782a4f6d46f66e9deb2c375830f9192d4482 Mon Sep 17 00:00:00 2001 From: Guilherme Blanco Date: Sat, 3 Aug 2013 17:38:55 -0400 Subject: [PATCH 224/332] Properly fixed DDC-1858. Added support for ResultVariable in NullComparisons while using HavingClause. --- lib/Doctrine/ORM/Query/Parser.php | 26 +++++++++++++++++-- lib/Doctrine/ORM/Query/SqlWalker.php | 10 +++++-- .../ORM/Query/SelectSqlGenerationTest.php | 11 ++++++++ 3 files changed, 43 insertions(+), 4 deletions(-) diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index b64785dfd..f873f4d01 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -3108,7 +3108,7 @@ class Parser } /** - * NullComparisonExpression ::= (InputParameter | NullIfExpression | CoalesceExpression | SingleValuedPathExpression) "IS" ["NOT"] "NULL" + * NullComparisonExpression ::= (InputParameter | NullIfExpression | CoalesceExpression | SingleValuedPathExpression | ResultVariable) "IS" ["NOT"] "NULL" * * @return \Doctrine\ORM\Query\AST\NullComparisonExpression */ @@ -3134,7 +3134,29 @@ class Parser break; default: - $expr = $this->SingleValuedPathExpression(); + // We need to check if we are in a IdentificationVariable or SingleValuedPathExpression + $glimpse = $this->lexer->glimpse(); + + if ($glimpse['type'] === Lexer::T_DOT) { + $expr = $this->SingleValuedPathExpression(); + + // Leave switch statement + break; + } + + $lookaheadValue = $this->lexer->lookahead['value']; + + // Validate existing component + if ( ! isset($this->queryComponents[$lookaheadValue])) { + $this->semanticalError('Cannot add having condition on undefined result variable.'); + } + + // Validating ResultVariable + if ( ! isset($this->queryComponents[$lookaheadValue]['resultVariable'])) { + $this->semanticalError('Cannot add having condition on a non result variable.'); + } + + $expr = $this->ResultVariable(); break; } diff --git a/lib/Doctrine/ORM/Query/SqlWalker.php b/lib/Doctrine/ORM/Query/SqlWalker.php index 5eae0bf9a..fe923c953 100644 --- a/lib/Doctrine/ORM/Query/SqlWalker.php +++ b/lib/Doctrine/ORM/Query/SqlWalker.php @@ -1921,9 +1921,15 @@ class SqlWalker implements TreeWalker */ public function walkNullComparisonExpression($nullCompExpr) { - $expression = $nullCompExpr->expression; - $comparison = ' IS' . ($nullCompExpr->not ? ' NOT' : '') . ' NULL'; + $expression = $nullCompExpr->expression; + $comparison = ' IS' . ($nullCompExpr->not ? ' NOT' : '') . ' NULL'; + // Handle ResultVariable + if (is_string($expression) && isset($this->queryComponents[$expression]['resultVariable'])) { + return $this->walkResultVariable($expression) . $comparison; + } + + // Handle InputParameter mapping inclusion to ParserResult if ($expression instanceof AST\InputParameter) { $this->parserResult->addParameterMapping($expression->name, $this->sqlParamIndex++); diff --git a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php index dbb303323..a444a2e87 100644 --- a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php +++ b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php @@ -2050,6 +2050,17 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase 'SELECT c0_.name AS name0 FROM cms_users c0_ HAVING name0 IN (?)' ); } + + /** + * @group DDC-1858 + */ + public function testHavingSupportResultVariableInAggregateFunction() + { + $this->assertSqlGeneration( + 'SELECT COUNT(u.name) AS countName FROM Doctrine\Tests\Models\CMS\CmsUser u HAVING countName IS NULL', + 'SELECT COUNT(c0_.name) AS sclr0 FROM cms_users c0_ HAVING sclr0 IS NULL' + ); + } } class MyAbsFunction extends \Doctrine\ORM\Query\AST\Functions\FunctionNode From 36e7e3ccde85a17de7039beade4fc07afe610c95 Mon Sep 17 00:00:00 2001 From: Guilherme Blanco Date: Sat, 3 Aug 2013 17:40:48 -0400 Subject: [PATCH 225/332] Updated documentation. --- docs/en/reference/dql-doctrine-query-language.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/reference/dql-doctrine-query-language.rst b/docs/en/reference/dql-doctrine-query-language.rst index f9ac299e3..a7fb149bf 100644 --- a/docs/en/reference/dql-doctrine-query-language.rst +++ b/docs/en/reference/dql-doctrine-query-language.rst @@ -1632,7 +1632,7 @@ QUANTIFIED/BETWEEN/COMPARISON/LIKE/NULL/EXISTS InstanceOfExpression ::= IdentificationVariable ["NOT"] "INSTANCE" ["OF"] (InstanceOfParameter | "(" InstanceOfParameter {"," InstanceOfParameter}* ")") InstanceOfParameter ::= AbstractSchemaName | InputParameter LikeExpression ::= StringExpression ["NOT"] "LIKE" StringPrimary ["ESCAPE" char] - NullComparisonExpression ::= (SingleValuedPathExpression | InputParameter) "IS" ["NOT"] "NULL" + NullComparisonExpression ::= (InputParameter | NullIfExpression | CoalesceExpression | SingleValuedPathExpression | ResultVariable) "IS" ["NOT"] "NULL" ExistsExpression ::= ["NOT"] "EXISTS" "(" Subselect ")" ComparisonOperator ::= "=" | "<" | "<=" | "<>" | ">" | ">=" | "!=" From e0fd3778282c91a869d7de2555e08589570b9974 Mon Sep 17 00:00:00 2001 From: Guilherme Blanco Date: Sat, 3 Aug 2013 19:03:10 -0400 Subject: [PATCH 226/332] Removed ticket that was breaking the build. DDC-2524 is a circular dependency that is impossible to be fixed with our current codebase. --- lib/Doctrine/ORM/UnitOfWork.php | 7 +- .../ORM/Functional/Ticket/DDC2524Test.php | 148 ------------------ 2 files changed, 3 insertions(+), 152 deletions(-) delete mode 100644 tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2524Test.php diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index 0eb7c8fd8..7e983d801 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -1071,13 +1071,12 @@ class UnitOfWork implements PropertyChangedListener $newNodes = array(); foreach ($entityChangeSet as $entity) { - $className = $this->em->getClassMetadata(get_class($entity))->name; + $class = $this->em->getClassMetadata(get_class($entity)); - if ($calc->hasClass($className)) { + if ($calc->hasClass($class->name)) { continue; } - $class = $this->em->getClassMetadata($className); $calc->addClass($class); $newNodes[] = $class; @@ -2875,7 +2874,7 @@ class UnitOfWork implements PropertyChangedListener return isset($values[$class->identifier[0]]) ? $values[$class->identifier[0]] : null; } - + /** * Tries to find an entity with the given identifier in the identity map of * this UnitOfWork. diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2524Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2524Test.php deleted file mode 100644 index c5a7cad70..000000000 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2524Test.php +++ /dev/null @@ -1,148 +0,0 @@ -_schemaTool->createSchema(array( - $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2524EntityO'), - $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2524EntityG'), - )); - } - - protected function tearDown() - { - parent::tearDown(); - - $this->_schemaTool->dropSchema(array( - $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2524EntityO'), - $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2524EntityG'), - )); - } - - public function testSingle() - { - $eO = new DDC2524EntityO(); - $eG = new DDC2524EntityG($eO); - - $this->_em->persist($eO); - $this->_em->flush(); - $this->_em->clear(); - - $eOloaded = $this->_em->find(__NAMESPACE__ . '\DDC2524EntityO', $eO->getId()); - - $this->_em->remove($eOloaded); - $this->_em->flush(); - } - - public function testMany() - { - $eO = new DDC2524EntityO(); - $eG1 = new DDC2524EntityG($eO); - $eG2 = new DDC2524EntityG($eO); - $eG3 = new DDC2524EntityG($eO); - - $eO->setOneToOneG($eG2); - - $this->_em->persist($eO); - $this->_em->flush(); - $this->_em->clear(); - - $eOloaded = $this->_em->find(__NAMESPACE__ . '\DDC2524EntityO', $eO->getId()); - - $this->_em->remove($eOloaded); - $this->_em->flush(); - } -} - -/** - * @Entity - */ -class DDC2524EntityO -{ - /** - * @Id @Column(type="integer") - * @GeneratedValue - */ - private $id; - - /** - * @OneToOne(targetEntity="Doctrine\Tests\ORM\Functional\Ticket\DDC2524EntityG") - * @JoinColmun(nullable=true) - **/ - private $oneToOneG; - - /** - * @OneToMany(targetEntity="Doctrine\Tests\ORM\Functional\Ticket\DDC2524EntityG", mappedBy="ownerO", cascade={"persist", "remove"}) - **/ - private $oneToManyG; - - public function __construct() - { - $this->oneToManyG = new ArrayCollection(); - } - - public function getId() - { - return $this->id; - } - - public function setOneToOneG(DDC2524EntityG $eG) - { - $this->oneToOneG = $eG; - } - - public function getOneToOneG() - { - return $this->oneToOneG; - } - - public function addOneToManyG(DDC2524EntityG $eG) - { - $this->oneToManyG->add($eG); - } - - public function getOneToManyGs() - { - return $this->oneToManyG->toArray(); - } -} - -/** - * @Entity - */ -class DDC2524EntityG -{ - /** - * @Id @Column(type="integer") - * @GeneratedValue - */ - private $id; - - /** - * @ManyToOne(targetEntity="Doctrine\Tests\ORM\Functional\Ticket\DDC2524EntityO", inversedBy="oneToManyG") - **/ - private $ownerO; - - public function __construct(DDC2524EntityO $eO, $position = 1) - { - $this->position = $position; - $this->ownerO = $eO; - - $this->ownerO->addOneToManyG($this); - } - - public function getId() - { - return $this->id; - } -} From 7ec59878a17d97fefd617ba3c45dcae503158d81 Mon Sep 17 00:00:00 2001 From: Guilherme Blanco Date: Tue, 6 Aug 2013 01:47:42 -0400 Subject: [PATCH 227/332] Added postLoad behavioral event coverage as a set of functional test. --- .../ORM/Functional/PostLoadEventTest.php | 231 ++++++++++++++++++ 1 file changed, 231 insertions(+) create mode 100644 tests/Doctrine/Tests/ORM/Functional/PostLoadEventTest.php diff --git a/tests/Doctrine/Tests/ORM/Functional/PostLoadEventTest.php b/tests/Doctrine/Tests/ORM/Functional/PostLoadEventTest.php new file mode 100644 index 000000000..bd965eff2 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/PostLoadEventTest.php @@ -0,0 +1,231 @@ + + */ +class PostLoadEventTest extends \Doctrine\Tests\OrmFunctionalTestCase +{ + /** + * @var integer + */ + private $userId; + + /** + * {@inheritdoc} + */ + protected function setUp() + { + $this->useModelSet('cms'); + + parent::setUp(); + + $this->loadFixture(); + } + + public function testLoadedEntityUsingFindShouldTriggerEvent() + { + $mockListener = $this->getMock('Doctrine\Tests\ORM\Functional\PostLoadListener'); + + // CmsUser and CmsAddres, because it's a ToOne inverse side on CmsUser + $mockListener + ->expects($this->exactly(2)) + ->method('postLoad') + ->will($this->returnValue(true)); + + $eventManager = $this->_em->getEventManager(); + + $eventManager->addEventListener(array(Events::postLoad), $mockListener); + + $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId); + } + + public function testLoadedEntityUsingQueryShouldTriggerEvent() + { + $mockListener = $this->getMock('Doctrine\Tests\ORM\Functional\PostLoadListener'); + + // CmsUser and CmsAddres, because it's a ToOne inverse side on CmsUser + $mockListener + ->expects($this->exactly(2)) + ->method('postLoad') + ->will($this->returnValue(true)); + + $eventManager = $this->_em->getEventManager(); + + $eventManager->addEventListener(array(Events::postLoad), $mockListener); + + $query = $this->_em->createQuery('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = :id'); + + $query->setParameter('id', $this->userId); + $query->getResult(); + } + + public function testLoadedAssociationToOneShouldTriggerEvent() + { + $mockListener = $this->getMock('Doctrine\Tests\ORM\Functional\PostLoadListener'); + + // CmsUser (root), CmsAddress (ToOne inverse side), CmsEmail (joined association) + $mockListener + ->expects($this->exactly(3)) + ->method('postLoad') + ->will($this->returnValue(true)); + + $eventManager = $this->_em->getEventManager(); + + $eventManager->addEventListener(array(Events::postLoad), $mockListener); + + $query = $this->_em->createQuery('SELECT u, e FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.email e WHERE u.id = :id'); + + $query->setParameter('id', $this->userId); + $query->getResult(); + } + + public function testLoadedAssociationToManyShouldTriggerEvent() + { + $mockListener = $this->getMock('Doctrine\Tests\ORM\Functional\PostLoadListener'); + + // CmsUser (root), CmsAddress (ToOne inverse side), 2 CmsPhonenumber (joined association) + $mockListener + ->expects($this->exactly(4)) + ->method('postLoad') + ->will($this->returnValue(true)); + + $eventManager = $this->_em->getEventManager(); + + $eventManager->addEventListener(array(Events::postLoad), $mockListener); + + $query = $this->_em->createQuery('SELECT u, p FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.phonenumbers p WHERE u.id = :id'); + + $query->setParameter('id', $this->userId); + $query->getResult(); + } + + public function testLoadedProxyEntityShouldTriggerEvent() + { + $eventManager = $this->_em->getEventManager(); + + // Should not be invoked during getReference call + $mockListener = $this->getMock('Doctrine\Tests\ORM\Functional\PostLoadListener'); + + $mockListener + ->expects($this->never()) + ->method('postLoad') + ->will($this->returnValue(true)); + + $eventManager->addEventListener(array(Events::postLoad), $mockListener); + + $userProxy = $this->_em->getReference('Doctrine\Tests\Models\CMS\CmsUser', $this->userId); + + // Now deactivate original listener and attach new one + $eventManager->removeEventListener(array(Events::postLoad), $mockListener); + + $mockListener2 = $this->getMock('Doctrine\Tests\ORM\Functional\PostLoadListener'); + + $mockListener2 + ->expects($this->exactly(2)) + ->method('postLoad') + ->will($this->returnValue(true)); + + $eventManager->addEventListener(array(Events::postLoad), $mockListener2); + + $userProxy->getName(); + } + + public function testLoadedProxyAssociationToOneShouldTriggerEvent() + { + $user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId); + + $mockListener = $this->getMock('Doctrine\Tests\ORM\Functional\PostLoadListener'); + + // CmsEmail (proxy) + $mockListener + ->expects($this->exactly(1)) + ->method('postLoad') + ->will($this->returnValue(true)); + + $eventManager = $this->_em->getEventManager(); + + $eventManager->addEventListener(array(Events::postLoad), $mockListener); + + $emailProxy = $user->getEmail(); + + $emailProxy->getEmail(); + } + + public function testLoadedProxyAssociationToManyShouldTriggerEvent() + { + $user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId); + + $mockListener = $this->getMock('Doctrine\Tests\ORM\Functional\PostLoadListener'); + + // 2 CmsPhonenumber (proxy) + $mockListener + ->expects($this->exactly(2)) + ->method('postLoad') + ->will($this->returnValue(true)); + + $eventManager = $this->_em->getEventManager(); + + $eventManager->addEventListener(array(Events::postLoad), $mockListener); + + $phonenumbersCol = $user->getPhonenumbers(); + + $phonenumbersCol->first(); + } + + private function loadFixture() + { + $user = new CmsUser; + $user->name = 'Roman'; + $user->username = 'romanb'; + $user->status = 'developer'; + + $address = new CmsAddress; + $address->country = 'Germany'; + $address->city = 'Berlin'; + $address->zip = '12345'; + + $user->setAddress($address); + + $email = new CmsEmail; + $email->setEmail('roman@domain.com'); + + $user->setEmail($email); + + $ph1 = new CmsPhonenumber; + $ph1->phonenumber = "0301234"; + + $ph2 = new CmsPhonenumber; + $ph2->phonenumber = "987654321"; + + $user->addPhonenumber($ph1); + $user->addPhonenumber($ph2); + + $this->_em->persist($user); + $this->_em->flush(); + + $this->userId = $user->getId(); + + $this->_em->clear(); + } +} + +class PostLoadListener +{ + public function postLoad(LifecycleEventArgs $event) + { + // Expected to be mocked out + echo 'Should never be called!'; + } +} \ No newline at end of file From 97d9ba62bbf7baade3fd9f286e7e7836b41d08e0 Mon Sep 17 00:00:00 2001 From: bronze1man Date: Tue, 6 Aug 2013 14:12:05 +0800 Subject: [PATCH 228/332] fix some file mode 755->644 --- tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2012Test.php | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2012Test.php diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2012Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2012Test.php old mode 100755 new mode 100644 From dd975fe53d7b46959abbc3951a1e84e0e55485a7 Mon Sep 17 00:00:00 2001 From: amakhov Date: Tue, 6 Aug 2013 12:20:22 +0400 Subject: [PATCH 229/332] Add hour to DATE_ADD and DATE_SUB --- lib/Doctrine/ORM/Query/AST/Functions/DateAddFunction.php | 7 ++++++- lib/Doctrine/ORM/Query/AST/Functions/DateSubFunction.php | 5 +++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Query/AST/Functions/DateAddFunction.php b/lib/Doctrine/ORM/Query/AST/Functions/DateAddFunction.php index 1c3817fe2..d78cd976a 100644 --- a/lib/Doctrine/ORM/Query/AST/Functions/DateAddFunction.php +++ b/lib/Doctrine/ORM/Query/AST/Functions/DateAddFunction.php @@ -45,6 +45,11 @@ class DateAddFunction extends FunctionNode public function getSql(SqlWalker $sqlWalker) { switch (strtolower($this->unit->value)) { + case 'hour': + return $sqlWalker->getConnection()->getDatabasePlatform()->getDateAddHourExpression( + $this->firstDateExpression->dispatch($sqlWalker), + $this->intervalExpression->dispatch($sqlWalker) + ); case 'day': return $sqlWalker->getConnection()->getDatabasePlatform()->getDateAddDaysExpression( $this->firstDateExpression->dispatch($sqlWalker), @@ -59,7 +64,7 @@ class DateAddFunction extends FunctionNode default: throw QueryException::semanticalError( - 'DATE_ADD() only supports units of type day and month.' + 'DATE_ADD() only supports units of type hour, day and month.' ); } } diff --git a/lib/Doctrine/ORM/Query/AST/Functions/DateSubFunction.php b/lib/Doctrine/ORM/Query/AST/Functions/DateSubFunction.php index 35add0ed6..bcd994ba9 100644 --- a/lib/Doctrine/ORM/Query/AST/Functions/DateSubFunction.php +++ b/lib/Doctrine/ORM/Query/AST/Functions/DateSubFunction.php @@ -39,6 +39,11 @@ class DateSubFunction extends DateAddFunction public function getSql(SqlWalker $sqlWalker) { switch (strtolower($this->unit->value)) { + case 'hour': + return $sqlWalker->getConnection()->getDatabasePlatform()->getDateSubHourExpression( + $this->firstDateExpression->dispatch($sqlWalker), + $this->intervalExpression->dispatch($sqlWalker) + ); case 'day': return $sqlWalker->getConnection()->getDatabasePlatform()->getDateSubDaysExpression( $this->firstDateExpression->dispatch($sqlWalker), From d8fcdc0c547df9c5ab091f21aade3cdc1ae3a963 Mon Sep 17 00:00:00 2001 From: amakhov Date: Tue, 6 Aug 2013 12:40:30 +0400 Subject: [PATCH 230/332] Add hour to DATE_ADD and DATE_SUB. Excepcion message fix --- lib/Doctrine/ORM/Query/AST/Functions/DateSubFunction.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Query/AST/Functions/DateSubFunction.php b/lib/Doctrine/ORM/Query/AST/Functions/DateSubFunction.php index bcd994ba9..d0e890fa7 100644 --- a/lib/Doctrine/ORM/Query/AST/Functions/DateSubFunction.php +++ b/lib/Doctrine/ORM/Query/AST/Functions/DateSubFunction.php @@ -58,7 +58,7 @@ class DateSubFunction extends DateAddFunction default: throw QueryException::semanticalError( - 'DATE_SUB() only supports units of type day and month.' + 'DATE_SUB() only supports units of type hour, day and month.' ); } } From 14bc65bae7b41302d7bf94a5ff8197c61abd79f8 Mon Sep 17 00:00:00 2001 From: "J. Bruni" Date: Tue, 6 Aug 2013 11:23:15 -0300 Subject: [PATCH 231/332] Updated EntityGeneratorTest::testEntityTypeAlias --- tests/Doctrine/Tests/ORM/Tools/EntityGeneratorTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Doctrine/Tests/ORM/Tools/EntityGeneratorTest.php b/tests/Doctrine/Tests/ORM/Tools/EntityGeneratorTest.php index 831379ad5..338ace221 100644 --- a/tests/Doctrine/Tests/ORM/Tools/EntityGeneratorTest.php +++ b/tests/Doctrine/Tests/ORM/Tools/EntityGeneratorTest.php @@ -550,7 +550,7 @@ class EntityGeneratorTest extends \Doctrine\Tests\OrmTestCase 'fieldName' => 'decimal', 'phpType' => 'string', 'dbType' => 'decimal', - 'value' => '33.33' + 'value' => '12.34' ), )); } From 3c1fd9a3a95bfa245650bd33e77c4e12b2a5cc86 Mon Sep 17 00:00:00 2001 From: "Konstantin.Myakshin" Date: Mon, 29 Jul 2013 00:35:56 +0300 Subject: [PATCH 232/332] Skip not mapped public properties in SchemaValidator --- lib/Doctrine/ORM/Tools/SchemaValidator.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/Doctrine/ORM/Tools/SchemaValidator.php b/lib/Doctrine/ORM/Tools/SchemaValidator.php index 958e411b6..889297ce8 100644 --- a/lib/Doctrine/ORM/Tools/SchemaValidator.php +++ b/lib/Doctrine/ORM/Tools/SchemaValidator.php @@ -241,6 +241,11 @@ class SchemaValidator continue; } + if ( ! isset($class->fieldMappings[$publicAttr->getName()]) && + ! isset($class->associationMappings[$publicAttr->getName()])) { + continue; + } + $ce[] = "Field '".$publicAttr->getName()."' in class '".$class->name."' must be private ". "or protected. Public fields may break lazy-loading."; } From 0a42401a43dfe1e5c24018bb0fb65deab338d4b5 Mon Sep 17 00:00:00 2001 From: "Fabio B. Silva" Date: Thu, 8 Aug 2013 19:13:13 -0400 Subject: [PATCH 233/332] [DDC-2572] Fix ResolveTargetEntityListener subscription example --- docs/en/cookbook/resolve-target-entity-listener.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/en/cookbook/resolve-target-entity-listener.rst b/docs/en/cookbook/resolve-target-entity-listener.rst index 4a1b3b941..c2d4a0faf 100644 --- a/docs/en/cookbook/resolve-target-entity-listener.rst +++ b/docs/en/cookbook/resolve-target-entity-listener.rst @@ -116,13 +116,14 @@ the targetEntity resolution will occur reliably: .. code-block:: php - $evm = new \Doctrine\Common\EventManager; - + $evm = new \Doctrine\Common\EventManager; $rtel = new \Doctrine\ORM\Tools\ResolveTargetEntityListener; + + // Adds a target-entity class $rtel->addResolveTargetEntity('Acme\\InvoiceModule\\Model\\InvoiceSubjectInterface', 'Acme\\CustomerModule\\Entity\\Customer', array()); // Add the ResolveTargetEntityListener - $evm->addEventSubscriber($rtel); + $evm->addEventListener(Doctrine\ORM\Events::loadClassMetadata, $rtel); $em = \Doctrine\ORM\EntityManager::create($connectionOptions, $config, $evm); From 3ec267e8a64d75c74016676c614e517350605c5b Mon Sep 17 00:00:00 2001 From: Guilherme Blanco Date: Thu, 8 Aug 2013 20:53:11 -0400 Subject: [PATCH 234/332] Added coverage for querying support during postLoad. --- .../ORM/Functional/PostLoadEventTest.php | 21 ++ .../Tests/ORM/Performance/DDC2602Test.php | 333 ++++++++++++++++++ 2 files changed, 354 insertions(+) create mode 100644 tests/Doctrine/Tests/ORM/Performance/DDC2602Test.php diff --git a/tests/Doctrine/Tests/ORM/Functional/PostLoadEventTest.php b/tests/Doctrine/Tests/ORM/Functional/PostLoadEventTest.php index bd965eff2..65e438cc1 100644 --- a/tests/Doctrine/Tests/ORM/Functional/PostLoadEventTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/PostLoadEventTest.php @@ -142,6 +142,27 @@ class PostLoadEventTest extends \Doctrine\Tests\OrmFunctionalTestCase $userProxy->getName(); } + public function testLoadedProxyPartialShouldTriggerEvent() + { + $eventManager = $this->_em->getEventManager(); + + // Should not be invoked during getReference call + $mockListener = $this->getMock('Doctrine\Tests\ORM\Functional\PostLoadListener'); + + // CmsUser (partially loaded), CmsAddress (inverse ToOne), 2 CmsPhonenumber + $mockListener + ->expects($this->exactly(4)) + ->method('postLoad') + ->will($this->returnValue(true)); + + $eventManager->addEventListener(array(Events::postLoad), $mockListener); + + $query = $this->_em->createQuery('SELECT PARTIAL u.{id, name}, p FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.phonenumbers p WHERE u.id = :id'); + + $query->setParameter('id', $this->userId); + $query->getResult(); + } + public function testLoadedProxyAssociationToOneShouldTriggerEvent() { $user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId); diff --git a/tests/Doctrine/Tests/ORM/Performance/DDC2602Test.php b/tests/Doctrine/Tests/ORM/Performance/DDC2602Test.php new file mode 100644 index 000000000..e5537000a --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Performance/DDC2602Test.php @@ -0,0 +1,333 @@ +_schemaTool->createSchema(array( + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2602User'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2602Biography'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2602BiographyField'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2602BiographyFieldChoice'), + )); + + $this->loadFixture(); + } + + protected function tearDown() + { + parent::tearDown(); + + $this->_schemaTool->dropSchema(array( + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2602User'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2602Biography'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2602BiographyField'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2602BiographyFieldChoice'), + )); + } + + public function testIssue() + { + $eventManager = $this->_em->getEventManager(); + $eventManager->addEventListener(array(Events::postLoad), new DDC2602PostLoadListener()); + + // Set maximum seconds this can run + $this->setMaxRunningTime(1); + + $s = microtime(true); + + $query = $this->_em->createQuery('SELECT u, b FROM Doctrine\Tests\ORM\Performance\DDC2602User u JOIN u.biography b'); + $query->getResult(); + + $e = microtime(true); + + echo __FUNCTION__ . " - " . ($e - $s) . " seconds" . PHP_EOL; + } + + private function loadFixture() + { + $user1 = new DDC2602User(); + $user2 = new DDC2602User(); + $biography1 = new DDC2602Biography(); + $biography2 = new DDC2602Biography(); + $biographyField1 = new DDC2602BiographyField(); + $biographyField2 = new DDC2602BiographyField(); + $biographyFieldChoice1 = new DDC2602BiographyFieldChoice(); + $biographyFieldChoice2 = new DDC2602BiographyFieldChoice(); + $biographyFieldChoice3 = new DDC2602BiographyFieldChoice(); + $biographyFieldChoice4 = new DDC2602BiographyFieldChoice(); + $biographyFieldChoice5 = new DDC2602BiographyFieldChoice(); + $biographyFieldChoice6 = new DDC2602BiographyFieldChoice(); + + $user1->name = 'Gblanco'; + $user1->biography = $biography1; + + $user2->name = 'Beberlei'; + $user2->biography = $biography2; + + $biography1->user = $user1; + $biography1->content = '[{"field": 1, "choiceList": [1,3]}, {"field": 2, "choiceList": [5]}]'; + + $biography2->user = $user2; + $biography2->content = '[{"field": 1, "choiceList": [1,2,3,4]}]'; + + $biographyField1->alias = 'question_1'; + $biographyField1->label = 'Question 1'; + $biographyField1->choiceList->add($biographyFieldChoice1); + $biographyField1->choiceList->add($biographyFieldChoice2); + $biographyField1->choiceList->add($biographyFieldChoice3); + $biographyField1->choiceList->add($biographyFieldChoice4); + + $biographyField2->alias = 'question_2'; + $biographyField2->label = 'Question 2'; + $biographyField2->choiceList->add($biographyFieldChoice5); + $biographyField2->choiceList->add($biographyFieldChoice6); + + $biographyFieldChoice1->field = $biographyField1; + $biographyFieldChoice1->label = 'Answer 1.1'; + + $biographyFieldChoice2->field = $biographyField1; + $biographyFieldChoice2->label = 'Answer 1.2'; + + $biographyFieldChoice3->field = $biographyField1; + $biographyFieldChoice3->label = 'Answer 1.3'; + + $biographyFieldChoice4->field = $biographyField1; + $biographyFieldChoice4->label = 'Answer 1.4'; + + $biographyFieldChoice5->field = $biographyField2; + $biographyFieldChoice5->label = 'Answer 2.1'; + + $biographyFieldChoice6->field = $biographyField2; + $biographyFieldChoice6->label = 'Answer 2.2'; + + $this->_em->persist($user1); + $this->_em->persist($user2); + + $this->_em->persist($biographyField1); + $this->_em->persist($biographyField2); + + $this->_em->flush(); + $this->_em->clear(); + } +} + + +class DDC2602PostLoadListener +{ + public function postLoad(LifecycleEventArgs $event) + { + $entity = $event->getEntity(); + + if ( ! ($entity instanceof DDC2602Biography)) { + return; + } + + $entityManager = $event->getEntityManager(); + $query = $entityManager->createQuery(' + SELECT f, fc + FROM Doctrine\Tests\ORM\Performance\DDC2602BiographyField f INDEX BY f.id + JOIN f.choiceList fc INDEX BY fc.id + '); + + $result = $query->getResult(); + $content = json_decode($entity->content); + $fieldList = new ArrayCollection(); + + foreach ($content as $selection) { + $field = $result[$selection->field]; + $choiceList = $selection->choiceList; + $fieldSelection = new DDC2602FieldSelection(); + + $fieldSelection->field = $field; + $fieldSelection->choiceList = $field->choiceList->filter(function ($choice) use ($choiceList) { + return in_array($choice->id, $choiceList); + }); + + $fieldList->add($fieldSelection); + } + + $entity->fieldList = $fieldList; + } +} + + +/** + * @Entity + */ +class DDC2602User +{ + /** + * @Id @GeneratedValue + * @Column(type="integer") + * + * @var integer + */ + public $id; + + /** + * @Column(type="string", length=15) + * + * @var string + */ + public $name; + + /** + * @OneToOne( + * targetEntity="DDC2602Biography", + * inversedBy="user", + * cascade={"persist", "merge", "refresh", "remove"} + * ) + * @JoinColumn(nullable=false) + * + * @var DDC2602Biography + */ + public $biography; +} + +/** + * @Entity + */ +class DDC2602Biography +{ + /** + * @Id @GeneratedValue + * @Column(type="integer") + * + * @var integer + */ + public $id; + + /** + * @OneToOne( + * targetEntity="DDC2602User", + * mappedBy="biography", + * cascade={"persist", "merge", "refresh"} + * ) + * + * @var DDC2602User + */ + public $user; + + /** + * @Column(type="text", nullable=true) + * + * @var string + */ + public $content; + + /** + * @var array + */ + public $fieldList = array(); +} + +/** + * @Entity + */ +class DDC2602BiographyField +{ + /** + * @Id @GeneratedValue + * @Column(type="integer") + * + * @var integer + */ + public $id; + + /** + * @Column(type="string", unique=true, length=100) + */ + public $alias; + + /** + * @Column(type="string", length=100) + */ + public $label; + + /** + * @OneToMany( + * targetEntity="DDC2602BiographyFieldChoice", + * mappedBy="field", + * cascade={"persist", "merge", "refresh"} + * ) + * + * @var \Doctrine\Common\Collections\ArrayCollection + */ + public $choiceList; + + /** + * Constructor. + * + */ + public function __construct() + { + $this->choiceList = new ArrayCollection(); + } +} + +/** + * @Entity + */ +class DDC2602BiographyFieldChoice +{ + /** + * @Id @GeneratedValue + * @Column(type="integer") + * + * @var integer + */ + public $id; + + /** + * @Column(type="string", unique=true, length=100) + */ + public $label; + + /** + * @ManyToOne( + * targetEntity="DDC2602BiographyField", + * inversedBy="choiceList" + * ) + * @JoinColumn(onDelete="CASCADE") + * + * @var DDC2602BiographyField + */ + public $field; +} + +class DDC2602FieldSelection +{ + /** + * @var DDC2602BiographyField + */ + public $field; + + /** + * @var \Doctrine\Common\Collections\ArrayCollection + */ + public $choiceList; + + /** + * Constructor. + * + */ + public function __construct() + { + $this->choiceList = new ArrayCollection(); + } +} From b314476599954086f1eaa43800901b9d817df257 Mon Sep 17 00:00:00 2001 From: Guilherme Blanco Date: Thu, 8 Aug 2013 22:01:26 -0400 Subject: [PATCH 235/332] ORM side fixes. --- .../SchemaTool/PostgreSqlSchemaToolTest.php | 11 ++++++----- .../Tests/ORM/Functional/Ticket/DDC1360Test.php | 1 + 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/tests/Doctrine/Tests/ORM/Functional/SchemaTool/PostgreSqlSchemaToolTest.php b/tests/Doctrine/Tests/ORM/Functional/SchemaTool/PostgreSqlSchemaToolTest.php index 6326fc384..cc4b231d6 100644 --- a/tests/Doctrine/Tests/ORM/Functional/SchemaTool/PostgreSqlSchemaToolTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/SchemaTool/PostgreSqlSchemaToolTest.php @@ -9,8 +9,10 @@ require_once __DIR__ . '/../../../TestInit.php'; class PostgreSqlSchemaToolTest extends \Doctrine\Tests\OrmFunctionalTestCase { - protected function setUp() { + protected function setUp() + { parent::setUp(); + if ($this->_em->getConnection()->getDatabasePlatform()->getName() !== 'postgresql') { $this->markTestSkipped('The ' . __CLASS__ .' requires the use of postgresql.'); } @@ -19,6 +21,7 @@ class PostgreSqlSchemaToolTest extends \Doctrine\Tests\OrmFunctionalTestCase public function testPostgresMetadataSequenceIncrementedBy10() { $address = $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsAddress'); + $this->assertEquals(1, $address->sequenceGeneratorDefinition['allocationSize']); } @@ -97,7 +100,9 @@ class PostgreSqlSchemaToolTest extends \Doctrine\Tests\OrmFunctionalTestCase $sql = $tool->getDropSchemaSQL($classes); $this->assertEquals(14, count($sql)); + $dropSequenceSQLs = 0; + foreach ($sql AS $stmt) { if (strpos($stmt, "DROP SEQUENCE") === 0) { $dropSequenceSQLs++; @@ -115,10 +120,6 @@ class PostgreSqlSchemaToolTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->_em->getClassMetadata(__NAMESPACE__ . '\\DDC1657Screen'), $this->_em->getClassMetadata(__NAMESPACE__ . '\\DDC1657Avatar'), ); - try { - $this->_em->getConnection()->exec("CREATE SCHEMA stonewood"); - } catch(\Exception $e) { - } $tool = new SchemaTool($this->_em); $tool->createSchema($classes); diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1360Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1360Test.php index 38f5837e5..3f45256d7 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1360Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1360Test.php @@ -20,6 +20,7 @@ class DDC1360Test extends OrmFunctionalTestCase )); $this->assertEquals(array( + 'CREATE SCHEMA "user"', 'CREATE TABLE "user"."user" (id INT NOT NULL, PRIMARY KEY(id))', 'CREATE SEQUENCE "user"."user_id_seq" INCREMENT BY 1 MINVALUE 1 START 1', ), $sql); From 5f5c0ffc32634155ae7b8498036845e33ebb07aa Mon Sep 17 00:00:00 2001 From: Guilherme Blanco Date: Thu, 8 Aug 2013 23:57:41 -0400 Subject: [PATCH 236/332] Fixing pgsql test. --- tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1360Test.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1360Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1360Test.php index 3f45256d7..f85a9aa49 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1360Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1360Test.php @@ -20,7 +20,7 @@ class DDC1360Test extends OrmFunctionalTestCase )); $this->assertEquals(array( - 'CREATE SCHEMA "user"', + 'CREATE SCHEMA user', 'CREATE TABLE "user"."user" (id INT NOT NULL, PRIMARY KEY(id))', 'CREATE SEQUENCE "user"."user_id_seq" INCREMENT BY 1 MINVALUE 1 START 1', ), $sql); From 7f1becf2832b49d842078b30f4a5c9153ee946b0 Mon Sep 17 00:00:00 2001 From: Roger Llopart Pla Date: Wed, 3 Jul 2013 12:14:16 +0200 Subject: [PATCH 237/332] Appending the Paginator tree walker hint, instead of removing all the other hints. --- lib/Doctrine/ORM/Tools/Pagination/Paginator.php | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) mode change 100644 => 100755 lib/Doctrine/ORM/Tools/Pagination/Paginator.php diff --git a/lib/Doctrine/ORM/Tools/Pagination/Paginator.php b/lib/Doctrine/ORM/Tools/Pagination/Paginator.php old mode 100644 new mode 100755 index 2d45df95a..6e2accc7c --- a/lib/Doctrine/ORM/Tools/Pagination/Paginator.php +++ b/lib/Doctrine/ORM/Tools/Pagination/Paginator.php @@ -134,7 +134,7 @@ class Paginator implements \Countable, \IteratorAggregate $countQuery->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, 'Doctrine\ORM\Tools\Pagination\CountOutputWalker'); $countQuery->setResultSetMapping($rsm); } else { - $countQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\CountWalker')); + $this->appendTreeWalker($countQuery, 'Doctrine\ORM\Tools\Pagination\CountWalker'); } $countQuery->setFirstResult(null)->setMaxResults(null); @@ -165,7 +165,7 @@ class Paginator implements \Countable, \IteratorAggregate if ($this->useOutputWalker($subQuery)) { $subQuery->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, 'Doctrine\ORM\Tools\Pagination\LimitSubqueryOutputWalker'); } else { - $subQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\LimitSubqueryWalker')); + $this->appendTreeWalker($subQuery, 'Doctrine\ORM\Tools\Pagination\LimitSubqueryWalker'); } $subQuery->setFirstResult($offset)->setMaxResults($length); @@ -231,4 +231,16 @@ class Paginator implements \Countable, \IteratorAggregate return $this->useOutputWalkers; } + + private function appendTreeWalker(Query $query, $walkerClass) + { + $hints = $query->getHint(Query::HINT_CUSTOM_TREE_WALKERS); + + if ($hints === false) { + $hints = array(); + } + + $hints[] = $walkerClass; + $query->setHint(Query::HINT_CUSTOM_TREE_WALKERS, $hints); + } } From 1bc614039412de8e8aaaaf5adc0b0bc17be01c7b Mon Sep 17 00:00:00 2001 From: Roger Llopart Pla Date: Wed, 3 Jul 2013 12:24:57 +0200 Subject: [PATCH 238/332] Added docblock. --- lib/Doctrine/ORM/Tools/Pagination/Paginator.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/Doctrine/ORM/Tools/Pagination/Paginator.php b/lib/Doctrine/ORM/Tools/Pagination/Paginator.php index 6e2accc7c..b9e8c5298 100755 --- a/lib/Doctrine/ORM/Tools/Pagination/Paginator.php +++ b/lib/Doctrine/ORM/Tools/Pagination/Paginator.php @@ -232,6 +232,12 @@ class Paginator implements \Countable, \IteratorAggregate return $this->useOutputWalkers; } + /** + * Appends a custom tree walker to the tree walkers hint. + * + * @param Query $query + * @param string $walkerClass + */ private function appendTreeWalker(Query $query, $walkerClass) { $hints = $query->getHint(Query::HINT_CUSTOM_TREE_WALKERS); From e755fe7842e33d24900530c13821545a59eaefae Mon Sep 17 00:00:00 2001 From: Roger Llopart Pla Date: Wed, 3 Jul 2013 11:18:19 +0000 Subject: [PATCH 239/332] Added a test which verifies that the tree walkers are kept. --- .../Tests/ORM/Functional/PaginationTest.php | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tests/Doctrine/Tests/ORM/Functional/PaginationTest.php b/tests/Doctrine/Tests/ORM/Functional/PaginationTest.php index 0a2845582..15c043d84 100644 --- a/tests/Doctrine/Tests/ORM/Functional/PaginationTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/PaginationTest.php @@ -139,6 +139,18 @@ class PaginationTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertTrue($query->getParameters()->isEmpty()); } + public function testQueryWalkerIsKept() + { + $dql = "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u"; + $query = $this->_em->createQuery($dql); + $query->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\Tests\ORM\Functional\CustomTreeWalker')); + + $paginator = new Paginator($query, true); + $paginator->setUseOutputWalkers(false); + $this->assertCount(1, $paginator->getIterator()); + $this->assertEquals(1, $paginator->count()); + } + public function populate() { for ($i = 0; $i < 3; $i++) { @@ -166,3 +178,22 @@ class PaginationTest extends \Doctrine\Tests\OrmFunctionalTestCase ); } } + +class CustomTreeWalker extends Query\TreeWalkerAdapter +{ + public function walkSelectStatement(Query\AST\SelectStatement $selectStatement) + { + $condition = new Query\AST\ConditionalPrimary(); + + $path = new Query\AST\PathExpression(Query\AST\PathExpression::TYPE_STATE_FIELD, 'u', 'name'); + $path->type = Query\AST\PathExpression::TYPE_STATE_FIELD; + + $condition->simpleConditionalExpression = new Query\AST\ComparisonExpression( + $path, + '=', + new Query\AST\Literal(Query\AST\Literal::STRING, 'Name1') + ); + + $selectStatement->whereClause = new Query\AST\WhereClause($condition); + } +} From 5a7efa289516781888df68f07f74c0c9a208cbdb Mon Sep 17 00:00:00 2001 From: Roger Llopart Pla Date: Thu, 4 Jul 2013 08:29:46 +0000 Subject: [PATCH 240/332] Fixed name colision. --- tests/Doctrine/Tests/ORM/Functional/PaginationTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Doctrine/Tests/ORM/Functional/PaginationTest.php b/tests/Doctrine/Tests/ORM/Functional/PaginationTest.php index 15c043d84..bf9e4a685 100644 --- a/tests/Doctrine/Tests/ORM/Functional/PaginationTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/PaginationTest.php @@ -143,7 +143,7 @@ class PaginationTest extends \Doctrine\Tests\OrmFunctionalTestCase { $dql = "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u"; $query = $this->_em->createQuery($dql); - $query->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\Tests\ORM\Functional\CustomTreeWalker')); + $query->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\Tests\ORM\Functional\CustomPaginationTestTreeWalker')); $paginator = new Paginator($query, true); $paginator->setUseOutputWalkers(false); @@ -179,7 +179,7 @@ class PaginationTest extends \Doctrine\Tests\OrmFunctionalTestCase } } -class CustomTreeWalker extends Query\TreeWalkerAdapter +class CustomPaginationTestTreeWalker extends Query\TreeWalkerAdapter { public function walkSelectStatement(Query\AST\SelectStatement $selectStatement) { From 27bae51fa03628d6634925265ff33531573cd518 Mon Sep 17 00:00:00 2001 From: Dustin Thomson Date: Sun, 11 Aug 2013 19:41:10 -0600 Subject: [PATCH 241/332] Modified executeInserts method in JoinedSubclassPersister to only check for the presence of columns in a composite primary key --- lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php b/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php index 9300c36c3..684d30571 100644 --- a/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php +++ b/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php @@ -197,7 +197,7 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister } foreach ($data as $columnName => $value) { - if (!isset($id[$columnName])) { + if (!is_array($id) || !isset($id[$columnName])) { $stmt->bindValue($paramIndex++, $value, $this->columnTypes[$columnName]); } } From 7f1c1d2b4acca7596d82ece885fd491537cdf230 Mon Sep 17 00:00:00 2001 From: Guilherme Blanco Date: Tue, 13 Aug 2013 01:07:34 -0400 Subject: [PATCH 242/332] Fixed DDC-2506 by manually updating code. Closes PR #708. --- lib/Doctrine/ORM/Query/SqlWalker.php | 21 ++++++++++--------- .../ORM/Query/SelectSqlGenerationTest.php | 12 +++++++++++ 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/lib/Doctrine/ORM/Query/SqlWalker.php b/lib/Doctrine/ORM/Query/SqlWalker.php index fe923c953..cf66f66a7 100644 --- a/lib/Doctrine/ORM/Query/SqlWalker.php +++ b/lib/Doctrine/ORM/Query/SqlWalker.php @@ -858,13 +858,14 @@ class SqlWalker implements TreeWalker * Walks down a JoinAssociationDeclaration AST node, thereby generating the appropriate SQL. * * @param AST\JoinAssociationDeclaration $joinAssociationDeclaration - * @param int $joinType + * @param int $joinType + * @param AST\ConditionalExpression $condExpr * * @return string * * @throws QueryException */ - public function walkJoinAssociationDeclaration($joinAssociationDeclaration, $joinType = AST\Join::JOIN_TYPE_INNER) + public function walkJoinAssociationDeclaration($joinAssociationDeclaration, $joinType = AST\Join::JOIN_TYPE_INNER, $condExpr = null) { $sql = ''; @@ -979,6 +980,13 @@ class SqlWalker implements TreeWalker break; } + // Handle WITH clause + if ($condExpr !== null) { + // Phase 2 AST optimization: Skip processing of ConditionalExpression + // if only one ConditionalTerm is defined + $sql .= ' AND (' . $this->walkConditionalExpression($condExpr) . ')'; + } + // FIXME: these should either be nested or all forced to be left joins (DDC-XXX) if ($targetClass->isInheritanceTypeJoined()) { $sql .= $this->_generateClassTableInheritanceJoins($targetClass, $joinedDqlAlias); @@ -1068,14 +1076,7 @@ class SqlWalker implements TreeWalker break; case ($joinDeclaration instanceof \Doctrine\ORM\Query\AST\JoinAssociationDeclaration): - $sql .= $this->walkJoinAssociationDeclaration($joinDeclaration, $joinType); - - // Handle WITH clause - if (($condExpr = $join->conditionalExpression) !== null) { - // Phase 2 AST optimization: Skip processing of ConditionalExpression - // if only one ConditionalTerm is defined - $sql .= ' AND (' . $this->walkConditionalExpression($condExpr) . ')'; - } + $sql .= $this->walkJoinAssociationDeclaration($joinDeclaration, $joinType, $join->conditionalExpression); break; } diff --git a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php index a444a2e87..0ec3afe17 100644 --- a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php +++ b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php @@ -2040,6 +2040,18 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase ); } + /** + * @group DDC-2506 + */ + public function testClassTableInheritanceJoinWithConditionAppliesToBaseTable() + { + $this->assertSqlGeneration( + 'SELECT e.id FROM Doctrine\Tests\Models\Company\CompanyOrganization o JOIN o.events e WITH e.id = ?1', + 'SELECT c0_.id AS id0 FROM company_organizations c1_ INNER JOIN company_events c0_ ON c1_.id = c0_.org_id AND (c0_.id = ?) LEFT JOIN company_auctions c2_ ON c0_.id = c2_.id LEFT JOIN company_raffles c3_ ON c0_.id = c3_.id', + array(Query::HINT_FORCE_PARTIAL_LOAD => false) + ); + } + /** * @group DDC-1858 */ From 036547e95662337ccf2c70fdb2c1bf2836e2f98f Mon Sep 17 00:00:00 2001 From: Matthieu Napoli Date: Tue, 13 Aug 2013 14:39:17 +0200 Subject: [PATCH 243/332] Update working-with-associations.rst Fixed syntax highlighting --- docs/en/reference/working-with-associations.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/reference/working-with-associations.rst b/docs/en/reference/working-with-associations.rst index b65773391..91f04966d 100644 --- a/docs/en/reference/working-with-associations.rst +++ b/docs/en/reference/working-with-associations.rst @@ -474,6 +474,7 @@ removed from the system: .. code-block:: php + find('User', $deleteUserId); foreach ($user->getAuthoredComments() AS $comment) { From 494ab1fc2b6bc5349d84ad92fa279731596a5b7c Mon Sep 17 00:00:00 2001 From: Douglas Greenshields Date: Tue, 13 Aug 2013 20:45:04 +0100 Subject: [PATCH 244/332] corrected English grammar in docblocks --- lib/Doctrine/ORM/Tools/EntityGenerator.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Doctrine/ORM/Tools/EntityGenerator.php b/lib/Doctrine/ORM/Tools/EntityGenerator.php index a9e34d727..6d497d7f4 100644 --- a/lib/Doctrine/ORM/Tools/EntityGenerator.php +++ b/lib/Doctrine/ORM/Tools/EntityGenerator.php @@ -1493,7 +1493,7 @@ public function __construct() * * @return string The literal string for the inheritance type. * - * @throws \InvalidArgumentException When the inheritance type does not exists. + * @throws \InvalidArgumentException When the inheritance type does not exist. */ protected function getInheritanceTypeString($type) { @@ -1509,7 +1509,7 @@ public function __construct() * * @return string The literal string for the change-tracking type. * - * @throws \InvalidArgumentException When the change-tracking type does not exists. + * @throws \InvalidArgumentException When the change-tracking type does not exist. */ protected function getChangeTrackingPolicyString($type) { @@ -1525,7 +1525,7 @@ public function __construct() * * @return string The literal string for the generator type. * - * @throws \InvalidArgumentException When the generator type does not exists. + * @throws \InvalidArgumentException When the generator type does not exist. */ protected function getIdGeneratorTypeString($type) { From c933ac1a4a11676352be85ffb5cbbfe39fb8cbd2 Mon Sep 17 00:00:00 2001 From: Peter Mitchell Date: Tue, 13 Aug 2013 16:08:42 -0400 Subject: [PATCH 245/332] Update override field mapping tut * Added reference to override section of inheritance-mapping.rst * Added link to yaml/xml examples in override-field-association-mappings-in-subclasses.rst --- docs/en/reference/inheritance-mapping.rst | 1 + .../override-field-association-mappings-in-subclasses.rst | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/en/reference/inheritance-mapping.rst b/docs/en/reference/inheritance-mapping.rst index 1bc92f3f3..9ea2edccb 100644 --- a/docs/en/reference/inheritance-mapping.rst +++ b/docs/en/reference/inheritance-mapping.rst @@ -260,6 +260,7 @@ or auto-increment details). Furthermore each child table has to have a foreign key pointing from the id column to the root table id column and cascading on delete. +.. _inheritence_mapping_overrides: Overrides --------- diff --git a/docs/en/tutorials/override-field-association-mappings-in-subclasses.rst b/docs/en/tutorials/override-field-association-mappings-in-subclasses.rst index 792d97887..aef663f2a 100644 --- a/docs/en/tutorials/override-field-association-mappings-in-subclasses.rst +++ b/docs/en/tutorials/override-field-association-mappings-in-subclasses.rst @@ -87,4 +87,4 @@ The case for just extending a class would be just the same but: // ... } -Overriding is also supported via XML and YAML. \ No newline at end of file +Overriding is also supported via XML and YAML (:ref:`examples `). From ad2ae4c4b4ead54b853c4cbc94c1acef4f344d8c Mon Sep 17 00:00:00 2001 From: Matthieu Napoli Date: Wed, 14 Aug 2013 12:14:02 +0200 Subject: [PATCH 246/332] Update resolve-target-entity-listener.rst Another syntax highlighting fix --- docs/en/cookbook/resolve-target-entity-listener.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/en/cookbook/resolve-target-entity-listener.rst b/docs/en/cookbook/resolve-target-entity-listener.rst index c2d4a0faf..21c726bc3 100644 --- a/docs/en/cookbook/resolve-target-entity-listener.rst +++ b/docs/en/cookbook/resolve-target-entity-listener.rst @@ -40,6 +40,7 @@ A Customer entity .. code-block:: php + Date: Fri, 16 Aug 2013 00:07:06 -0400 Subject: [PATCH 247/332] Fixed DDC-2235. --- .../Query/AST/RangeVariableDeclaration.php | 15 +++- lib/Doctrine/ORM/Query/Parser.php | 19 +++-- lib/Doctrine/ORM/Query/SqlWalker.php | 32 +++++-- .../ORM/Query/SelectSqlGenerationTest.php | 83 ++++++++++++++++++- 4 files changed, 132 insertions(+), 17 deletions(-) diff --git a/lib/Doctrine/ORM/Query/AST/RangeVariableDeclaration.php b/lib/Doctrine/ORM/Query/AST/RangeVariableDeclaration.php index 72a2ea9aa..0ca5274d1 100644 --- a/lib/Doctrine/ORM/Query/AST/RangeVariableDeclaration.php +++ b/lib/Doctrine/ORM/Query/AST/RangeVariableDeclaration.php @@ -41,13 +41,20 @@ class RangeVariableDeclaration extends Node public $aliasIdentificationVariable; /** - * @param string $abstractSchemaName - * @param string $aliasIdentificationVar + * @var boolean */ - public function __construct($abstractSchemaName, $aliasIdentificationVar) + public $isRoot; + + /** + * @param string $abstractSchemaName + * @param string $aliasIdentificationVar + * @param boolean $isRoot + */ + public function __construct($abstractSchemaName, $aliasIdentificationVar, $isRoot = true) { - $this->abstractSchemaName = $abstractSchemaName; + $this->abstractSchemaName = $abstractSchemaName; $this->aliasIdentificationVariable = $aliasIdentificationVar; + $this->isRoot = $isRoot; } /** diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index f873f4d01..d2d116c03 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -1544,6 +1544,9 @@ class Parser public function IdentificationVariableDeclaration() { $rangeVariableDeclaration = $this->RangeVariableDeclaration(); + + $rangeVariableDeclaration->isRoot = true; + $indexBy = $this->lexer->isNextToken(Lexer::T_INDEX) ? $this->IndexBy() : null; $joins = array(); @@ -1622,15 +1625,19 @@ class Parser $this->match(Lexer::T_JOIN); $next = $this->lexer->glimpse(); - $joinDeclaration = ($next['type'] === Lexer::T_DOT) - ? $this->JoinAssociationDeclaration() - : $this->RangeVariableDeclaration(); + $joinDeclaration = ($next['type'] === Lexer::T_DOT) ? $this->JoinAssociationDeclaration() : $this->RangeVariableDeclaration(); + $adhocConditions = $this->lexer->isNextToken(Lexer::T_WITH); + $join = new AST\Join($joinType, $joinDeclaration); - // Create AST node - $join = new AST\Join($joinType, $joinDeclaration); + // Describe non-root join declaration + if ($joinDeclaration instanceof AST\RangeVariableDeclaration) { + $joinDeclaration->isRoot = false; + + $adhocConditions = true; + } // Check for ad-hoc Join conditions - if ($this->lexer->isNextToken(Lexer::T_WITH) || $joinDeclaration instanceof AST\RangeVariableDeclaration) { + if ($adhocConditions) { $this->match(Lexer::T_WITH); $join->conditionalExpression = $this->ConditionalExpression(); diff --git a/lib/Doctrine/ORM/Query/SqlWalker.php b/lib/Doctrine/ORM/Query/SqlWalker.php index cf66f66a7..c92577f6f 100644 --- a/lib/Doctrine/ORM/Query/SqlWalker.php +++ b/lib/Doctrine/ORM/Query/SqlWalker.php @@ -842,7 +842,9 @@ class SqlWalker implements TreeWalker $class = $this->em->getClassMetadata($rangeVariableDeclaration->abstractSchemaName); $dqlAlias = $rangeVariableDeclaration->aliasIdentificationVariable; - $this->rootAliases[] = $dqlAlias; + if ($rangeVariableDeclaration->isRoot) { + $this->rootAliases[] = $dqlAlias; + } $sql = $this->quoteStrategy->getTableName($class,$this->platform) . ' ' . $this->getSQLTableAlias($class->getTableName(), $dqlAlias); @@ -1066,13 +1068,33 @@ class SqlWalker implements TreeWalker switch (true) { case ($joinDeclaration instanceof \Doctrine\ORM\Query\AST\RangeVariableDeclaration): - $class = $this->em->getClassMetadata($joinDeclaration->abstractSchemaName); - $condExprConjunction = $class->isInheritanceTypeJoined() && $joinType != AST\Join::JOIN_TYPE_LEFT && $joinType != AST\Join::JOIN_TYPE_LEFTOUTER + $class = $this->em->getClassMetadata($joinDeclaration->abstractSchemaName); + $dqlAlias = $joinDeclaration->aliasIdentificationVariable; + $tableAlias = $this->getSQLTableAlias($class->table['name'], $dqlAlias); + $condition = '(' . $this->walkConditionalExpression($join->conditionalExpression) . ')'; + $condExprConjunction = ($class->isInheritanceTypeJoined() && $joinType != AST\Join::JOIN_TYPE_LEFT && $joinType != AST\Join::JOIN_TYPE_LEFTOUTER) ? ' AND ' : ' ON '; - $sql .= $this->walkRangeVariableDeclaration($joinDeclaration) - . $condExprConjunction . '(' . $this->walkConditionalExpression($join->conditionalExpression) . ')'; + $sql .= $this->walkRangeVariableDeclaration($joinDeclaration); + + $conditions = array($condition); + + // Apply remaining inheritance restrictions + $discrSql = $this->_generateDiscriminatorColumnConditionSQL(array($dqlAlias)); + + if ($discrSql) { + $conditions[] = $discrSql; + } + + // Apply the filters + $filterExpr = $this->generateFilterConditionSQL($class, $tableAlias); + + if ($filterExpr) { + $conditions[] = $filterExpr; + } + + $sql .= $condExprConjunction . implode(' AND ', $conditions); break; case ($joinDeclaration instanceof \Doctrine\ORM\Query\AST\JoinAssociationDeclaration): diff --git a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php index 0ec3afe17..6aa366b67 100644 --- a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php +++ b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php @@ -155,12 +155,12 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase { $this->assertSqlGeneration( 'SELECT e FROM Doctrine\Tests\Models\Company\CompanyEmployee e JOIN Doctrine\Tests\Models\Company\CompanyManager m WITH e.id = m.id', - 'SELECT c0_.id AS id0, c0_.name AS name1, c1_.salary AS salary2, c1_.department AS department3, c1_.startDate AS startDate4, c0_.discr AS discr5 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id INNER JOIN company_managers c2_ INNER JOIN company_employees c3_ ON c2_.id = c3_.id INNER JOIN company_persons c4_ ON c2_.id = c4_.id AND (c0_.id = c4_.id)' + 'SELECT c0_.id AS id0, c0_.name AS name1, c1_.salary AS salary2, c1_.department AS department3, c1_.startDate AS startDate4, c0_.discr AS discr5 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id INNER JOIN company_managers c2_ INNER JOIN company_employees c4_ ON c2_.id = c4_.id INNER JOIN company_persons c3_ ON c2_.id = c3_.id AND (c0_.id = c3_.id)' ); $this->assertSqlGeneration( 'SELECT e FROM Doctrine\Tests\Models\Company\CompanyEmployee e LEFT JOIN Doctrine\Tests\Models\Company\CompanyManager m WITH e.id = m.id', - 'SELECT c0_.id AS id0, c0_.name AS name1, c1_.salary AS salary2, c1_.department AS department3, c1_.startDate AS startDate4, c0_.discr AS discr5 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id LEFT JOIN company_managers c2_ INNER JOIN company_employees c3_ ON c2_.id = c3_.id INNER JOIN company_persons c4_ ON c2_.id = c4_.id ON (c0_.id = c4_.id)' + 'SELECT c0_.id AS id0, c0_.name AS name1, c1_.salary AS salary2, c1_.department AS department3, c1_.startDate AS startDate4, c0_.discr AS discr5 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id LEFT JOIN company_managers c2_ INNER JOIN company_employees c4_ ON c2_.id = c4_.id INNER JOIN company_persons c3_ ON c2_.id = c3_.id ON (c0_.id = c3_.id)' ); } @@ -2052,6 +2052,85 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase ); } + /** + * @group DDC-2235 + */ + public function testSingleTableInheritanceLeftJoinWithCondition() + { + // Regression test for the bug + $this->assertSqlGeneration( + 'SELECT c FROM Doctrine\Tests\Models\Company\CompanyEmployee e LEFT JOIN Doctrine\Tests\Models\Company\CompanyContract c WITH c.salesPerson = e.id', + "SELECT c0_.id AS id0, c0_.completed AS completed1, c0_.fixPrice AS fixPrice2, c0_.hoursWorked AS hoursWorked3, c0_.pricePerHour AS pricePerHour4, c0_.maxPrice AS maxPrice5, c0_.discr AS discr6 FROM company_employees c1_ INNER JOIN company_persons c2_ ON c1_.id = c2_.id LEFT JOIN company_contracts c0_ ON (c0_.salesPerson_id = c2_.id) AND c0_.discr IN ('fix', 'flexible', 'flexultra')" + ); + } + + /** + * @group DDC-2235 + */ + public function testSingleTableInheritanceLeftJoinWithConditionAndWhere() + { + // Ensure other WHERE predicates are passed through to the main WHERE clause + $this->assertSqlGeneration( + 'SELECT c FROM Doctrine\Tests\Models\Company\CompanyEmployee e LEFT JOIN Doctrine\Tests\Models\Company\CompanyContract c WITH c.salesPerson = e.id WHERE e.salary > 1000', + "SELECT c0_.id AS id0, c0_.completed AS completed1, c0_.fixPrice AS fixPrice2, c0_.hoursWorked AS hoursWorked3, c0_.pricePerHour AS pricePerHour4, c0_.maxPrice AS maxPrice5, c0_.discr AS discr6 FROM company_employees c1_ INNER JOIN company_persons c2_ ON c1_.id = c2_.id LEFT JOIN company_contracts c0_ ON (c0_.salesPerson_id = c2_.id) AND c0_.discr IN ('fix', 'flexible', 'flexultra') WHERE c1_.salary > 1000" + ); + } + + /** + * @group DDC-2235 + */ + public function testSingleTableInheritanceInnerJoinWithCondition() + { + // Test inner joins too + $this->assertSqlGeneration( + 'SELECT c FROM Doctrine\Tests\Models\Company\CompanyEmployee e INNER JOIN Doctrine\Tests\Models\Company\CompanyContract c WITH c.salesPerson = e.id', + "SELECT c0_.id AS id0, c0_.completed AS completed1, c0_.fixPrice AS fixPrice2, c0_.hoursWorked AS hoursWorked3, c0_.pricePerHour AS pricePerHour4, c0_.maxPrice AS maxPrice5, c0_.discr AS discr6 FROM company_employees c1_ INNER JOIN company_persons c2_ ON c1_.id = c2_.id INNER JOIN company_contracts c0_ ON (c0_.salesPerson_id = c2_.id) AND c0_.discr IN ('fix', 'flexible', 'flexultra')" + ); + } + + /** + * @group DDC-2235 + */ + public function testSingleTableInheritanceLeftJoinNonAssociationWithConditionAndWhere() + { + // Test that the discriminator IN() predicate is still added into + // the where clause when not joining onto that table + $this->assertSqlGeneration( + 'SELECT c FROM Doctrine\Tests\Models\Company\CompanyContract c LEFT JOIN Doctrine\Tests\Models\Company\CompanyEmployee e WITH e.id = c.salesPerson WHERE c.completed = true', + "SELECT c0_.id AS id0, c0_.completed AS completed1, c0_.fixPrice AS fixPrice2, c0_.hoursWorked AS hoursWorked3, c0_.pricePerHour AS pricePerHour4, c0_.maxPrice AS maxPrice5, c0_.discr AS discr6 FROM company_contracts c0_ LEFT JOIN company_employees c1_ INNER JOIN company_persons c2_ ON c1_.id = c2_.id ON (c2_.id = c0_.salesPerson_id) WHERE (c0_.completed = 1) AND c0_.discr IN ('fix', 'flexible', 'flexultra')" + ); + } + + /** + * @group DDC-2235 + */ + public function testSingleTableInheritanceJoinCreatesOnCondition() + { + // Test that the discriminator IN() predicate is still added + // into the where clause when not joining onto a single table inheritance entity + // via a join association + $this->assertSqlGeneration( + 'SELECT c FROM Doctrine\Tests\Models\Company\CompanyContract c JOIN c.salesPerson s WHERE c.completed = true', + "SELECT c0_.id AS id0, c0_.completed AS completed1, c0_.fixPrice AS fixPrice2, c0_.hoursWorked AS hoursWorked3, c0_.pricePerHour AS pricePerHour4, c0_.maxPrice AS maxPrice5, c0_.discr AS discr6 FROM company_contracts c0_ INNER JOIN company_employees c1_ ON c0_.salesPerson_id = c1_.id LEFT JOIN company_persons c2_ ON c1_.id = c2_.id WHERE (c0_.completed = 1) AND c0_.discr IN ('fix', 'flexible', 'flexultra')" + ); + } + + /** + * @group DDC-2235 + */ + public function testSingleTableInheritanceCreatesOnConditionAndWhere() + { + // Test that when joining onto an entity using single table inheritance via + // a join association that the discriminator IN() predicate is placed + // into the ON clause of the join + $this->assertSqlGeneration( + 'SELECT e, COUNT(c) FROM Doctrine\Tests\Models\Company\CompanyEmployee e JOIN e.contracts c WHERE e.department = :department', + "SELECT c0_.id AS id0, c0_.name AS name1, c1_.salary AS salary2, c1_.department AS department3, c1_.startDate AS startDate4, COUNT(c2_.id) AS sclr5, c0_.discr AS discr6 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id INNER JOIN company_contract_employees c3_ ON c1_.id = c3_.employee_id INNER JOIN company_contracts c2_ ON c2_.id = c3_.contract_id AND c2_.discr IN ('fix', 'flexible', 'flexultra') WHERE c1_.department = ?", + array(), + array('department' => 'foobar') + ); + } + /** * @group DDC-1858 */ From 3405659ebac55e57957a084d62e6efc07c5d70fe Mon Sep 17 00:00:00 2001 From: Mark Fox Date: Sat, 17 Aug 2013 14:07:29 -0700 Subject: [PATCH 248/332] Update getting-started.rst MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The inline cli-config.php, bootstrap.php examples were out of sync with Doctrine 2.4.x — I referenced https://github.com/doctrine/doctrine2-orm-tutorial/ for correct examples --- docs/en/tutorials/getting-started.rst | 47 ++++++++++++++++++--------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/docs/en/tutorials/getting-started.rst b/docs/en/tutorials/getting-started.rst index 4f169cd94..5342ff12e 100644 --- a/docs/en/tutorials/getting-started.rst +++ b/docs/en/tutorials/getting-started.rst @@ -25,7 +25,7 @@ The code of this tutorial is `available on Github 'pdo_sqlite', 'path' => __DIR__ . '/db.sqlite', ); - + // obtaining the entity manager - $entityManager = EntityManager::create($conn, $config); + $entityManager = \Doctrine\ORM\EntityManager::create($conn, $config); The first require statement sets up the autoloading capabilities of Doctrine using the Composer autoload. @@ -186,8 +199,10 @@ doctrine command. Its a fairly simple file: new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($entityManager) + )); You can then change into your project directory and call the Doctrine command-line tool: From c89258a0eaa07c75c50e5daa81bf58712e075a6b Mon Sep 17 00:00:00 2001 From: Mark Fox Date: Sat, 17 Aug 2013 14:25:37 -0700 Subject: [PATCH 249/332] fixed spelling mistake "bootsrapped" --- docs/en/tutorials/getting-started.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/tutorials/getting-started.rst b/docs/en/tutorials/getting-started.rst index 5342ff12e..1e88f8538 100644 --- a/docs/en/tutorials/getting-started.rst +++ b/docs/en/tutorials/getting-started.rst @@ -113,7 +113,7 @@ This is the current directory structure: |-- composer.json `-- vendor -Next we'll create a basic bootsrapped application that uses the Symfony +Next we'll create a basic bootstrapped application that uses the Symfony Console to interact with Doctrine via the command line. Here's a preview of what we'll add: From 29366bb9c77aaaa8ecd668e419cc90067c6f1837 Mon Sep 17 00:00:00 2001 From: Mark Fox Date: Sat, 17 Aug 2013 21:15:03 -0700 Subject: [PATCH 250/332] Updated require version and my earlier errors dissolved As stated, the tutorial was written for 2.4.x, but the composer.json settings proffered would install 2.3.4, which has a different enough API that basic parts of the tutorial code would fail. --- docs/en/tutorials/getting-started.rst | 37 ++++++++------------------- 1 file changed, 11 insertions(+), 26 deletions(-) diff --git a/docs/en/tutorials/getting-started.rst b/docs/en/tutorials/getting-started.rst index 1e88f8538..4038947c7 100644 --- a/docs/en/tutorials/getting-started.rst +++ b/docs/en/tutorials/getting-started.rst @@ -87,14 +87,16 @@ the following contents: { "require": { - "doctrine/orm": "2.*", + "doctrine/orm": "2.4.*", "symfony/yaml": "2.*" }, "autoload": { "psr-0": {"": "src/"} - } + }, + "minimum-stability" : "dev" } + Install Doctrine using the Composer Dependency Management tool, by calling: :: @@ -105,30 +107,14 @@ This will install the packages Doctrine Common, Doctrine DBAL, Doctrine ORM, Symfony YAML and Symfony Console into the `vendor` directory. The Symfony dependencies are not required by Doctrine but will be used in this tutorial. -This is the current directory structure: - +Add the following directories: :: doctrine2-tutorial - |-- composer.json - `-- vendor - -Next we'll create a basic bootstrapped application that uses the Symfony -Console to interact with Doctrine via the command line. - -Here's a preview of what we'll add: - -:: - - doctrine2-tutorial - |-- bootstrap.php - |-- cli-config.php - |-- composer.json |-- config | |-- xml | `-- yaml - |-- src - `-- vendor + `-- src Obtaining the EntityManager --------------------------- @@ -145,13 +131,14 @@ step: new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($entityManager) - )); + return \Doctrine\ORM\Tools\Console\ConsoleRunner::createHelperSet($entityManager); You can then change into your project directory and call the Doctrine command-line tool: From 43fc8bafa766b4e924b05c74825dd30393a17f06 Mon Sep 17 00:00:00 2001 From: Guilherme Blanco Date: Sun, 18 Aug 2013 23:52:32 -0400 Subject: [PATCH 251/332] DDC-1858 Added missing support to ResultVariable in LikeExpression. --- .../reference/dql-doctrine-query-language.rst | 2 +- lib/Doctrine/ORM/Query/Parser.php | 26 ++++++++++++------- lib/Doctrine/ORM/Query/SqlWalker.php | 18 ++++++++----- .../ORM/Query/LanguageRecognitionTest.php | 8 ++++++ .../ORM/Query/SelectSqlGenerationTest.php | 11 ++++++++ 5 files changed, 47 insertions(+), 18 deletions(-) diff --git a/docs/en/reference/dql-doctrine-query-language.rst b/docs/en/reference/dql-doctrine-query-language.rst index a7fb149bf..ec4907ec6 100644 --- a/docs/en/reference/dql-doctrine-query-language.rst +++ b/docs/en/reference/dql-doctrine-query-language.rst @@ -1583,7 +1583,7 @@ Scalar and Type Expressions .. code-block:: php ScalarExpression ::= SimpleArithmeticExpression | StringPrimary | DateTimePrimary | StateFieldPathExpression | BooleanPrimary | CaseExpression | InstanceOfExpression - StringExpression ::= StringPrimary | "(" Subselect ")" + StringExpression ::= StringPrimary | ResultVariable | "(" Subselect ")" StringPrimary ::= StateFieldPathExpression | string | InputParameter | FunctionsReturningStrings | AggregateExpression | CaseExpression BooleanExpression ::= BooleanPrimary | "(" Subselect ")" BooleanPrimary ::= StateFieldPathExpression | boolean | InputParameter diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index d2d116c03..5c721a1d5 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -2776,23 +2776,29 @@ class Parser } /** - * StringExpression ::= StringPrimary | "(" Subselect ")" + * StringExpression ::= StringPrimary | ResultVariable | "(" Subselect ")" * * @return \Doctrine\ORM\Query\AST\StringPrimary | - * \Doctrine\ORM\Query\AST\Subselect + * \Doctrine\ORM\Query\AST\Subselect | + * string */ public function StringExpression() { - if ($this->lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) { - $peek = $this->lexer->glimpse(); + $peek = $this->lexer->glimpse(); - if ($peek['type'] === Lexer::T_SELECT) { - $this->match(Lexer::T_OPEN_PARENTHESIS); - $expr = $this->Subselect(); - $this->match(Lexer::T_CLOSE_PARENTHESIS); + // Subselect + if ($this->lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS) && $peek['type'] === Lexer::T_SELECT) { + $this->match(Lexer::T_OPEN_PARENTHESIS); + $expr = $this->Subselect(); + $this->match(Lexer::T_CLOSE_PARENTHESIS); - return $expr; - } + return $expr; + } + + // ResultVariable (string) + if ($this->lexer->isNextToken(Lexer::T_IDENTIFIER) && + isset($this->queryComponents[$this->lexer->lookahead['value']]['resultVariable'])) { + return $this->ResultVariable(); } return $this->StringPrimary(); diff --git a/lib/Doctrine/ORM/Query/SqlWalker.php b/lib/Doctrine/ORM/Query/SqlWalker.php index c92577f6f..31cb7a0d0 100644 --- a/lib/Doctrine/ORM/Query/SqlWalker.php +++ b/lib/Doctrine/ORM/Query/SqlWalker.php @@ -1944,8 +1944,8 @@ class SqlWalker implements TreeWalker */ public function walkNullComparisonExpression($nullCompExpr) { - $expression = $nullCompExpr->expression; - $comparison = ' IS' . ($nullCompExpr->not ? ' NOT' : '') . ' NULL'; + $expression = $nullCompExpr->expression; + $comparison = ' IS' . ($nullCompExpr->not ? ' NOT' : '') . ' NULL'; // Handle ResultVariable if (is_string($expression) && isset($this->queryComponents[$expression]['resultVariable'])) { @@ -2001,9 +2001,7 @@ class SqlWalker implements TreeWalker $sqlParameterList = array(); foreach ($instanceOfExpr->value as $parameter) { - if ($parameter instanceof AST\InputParameter) { - $this->rsm->addMetadataParameterMapping($parameter->name, 'discriminatorValue'); $sqlParameterList[] = $this->walkInputParameter($parameter); @@ -2073,7 +2071,9 @@ class SqlWalker implements TreeWalker { $sql = $this->walkArithmeticExpression($betweenExpr->expression); - if ($betweenExpr->not) $sql .= ' NOT'; + if ($betweenExpr->not) { + $sql .= ' NOT'; + } $sql .= ' BETWEEN ' . $this->walkArithmeticExpression($betweenExpr->leftBetweenExpression) . ' AND ' . $this->walkArithmeticExpression($betweenExpr->rightBetweenExpression); @@ -2087,14 +2087,18 @@ class SqlWalker implements TreeWalker public function walkLikeExpression($likeExpr) { $stringExpr = $likeExpr->stringExpression; - $sql = $stringExpr->dispatch($this) . ($likeExpr->not ? ' NOT' : '') . ' LIKE '; + $leftExpr = (is_string($stringExpr) && isset($this->queryComponents[$stringExpr]['resultVariable'])) + ? $this->walkResultVariable($stringExpr) + : $stringExpr->dispatch($this); + + $sql = $leftExpr . ($likeExpr->not ? ' NOT' : '') . ' LIKE '; if ($likeExpr->stringPattern instanceof AST\InputParameter) { $inputParam = $likeExpr->stringPattern; $dqlParamKey = $inputParam->name; $this->parserResult->addParameterMapping($dqlParamKey, $this->sqlParamIndex++); $sql .= '?'; - } elseif ($likeExpr->stringPattern instanceof AST\Functions\FunctionNode ) { + } elseif ($likeExpr->stringPattern instanceof AST\Functions\FunctionNode) { $sql .= $this->walkFunction($likeExpr->stringPattern); } elseif ($likeExpr->stringPattern instanceof AST\PathExpression) { $sql .= $this->walkPathExpression($likeExpr->stringPattern); diff --git a/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php b/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php index ca9085457..97ad6ac8e 100644 --- a/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php +++ b/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php @@ -598,6 +598,14 @@ class LanguageRecognitionTest extends \Doctrine\Tests\OrmTestCase { $this->assertValidDQL("SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u HAVING u.username IS NULL"); } + + /** + * @gorup DDC-1858 + */ + public function testHavingSupportLikeExpression() + { + $this->assertValidDQL("SELECT _u.id, count(_articles) as uuuu FROM Doctrine\Tests\Models\CMS\CmsUser _u LEFT JOIN _u.articles _articles GROUP BY _u HAVING uuuu LIKE '3'"); + } } /** @Entity */ diff --git a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php index 6aa366b67..2587905ed 100644 --- a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php +++ b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php @@ -2142,6 +2142,17 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase ); } + /** + * @group DDC-1858 + */ + public function testHavingSupportResultVariableLikeExpression() + { + $this->assertSqlGeneration( + "SELECT u.name AS foo FROM Doctrine\Tests\Models\CMS\CmsUser u HAVING foo LIKE '3'", + "SELECT c0_.name AS name0 FROM cms_users c0_ HAVING name0 LIKE '3'" + ); + } + /** * @group DDC-1858 */ From cc83ac6ce8d11f0c6c421cd4afd3503c8a69c35f Mon Sep 17 00:00:00 2001 From: Guilherme Blanco Date: Mon, 19 Aug 2013 00:33:45 -0400 Subject: [PATCH 252/332] Centralize InputParameter SQL generation. --- lib/Doctrine/ORM/Query/SqlWalker.php | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/lib/Doctrine/ORM/Query/SqlWalker.php b/lib/Doctrine/ORM/Query/SqlWalker.php index 31cb7a0d0..082f95f50 100644 --- a/lib/Doctrine/ORM/Query/SqlWalker.php +++ b/lib/Doctrine/ORM/Query/SqlWalker.php @@ -1954,9 +1954,7 @@ class SqlWalker implements TreeWalker // Handle InputParameter mapping inclusion to ParserResult if ($expression instanceof AST\InputParameter) { - $this->parserResult->addParameterMapping($expression->name, $this->sqlParamIndex++); - - return '?' . $comparison; + return $this->walkInputParameter($expression) . $comparison; } return $expression->dispatch($this) . $comparison; @@ -2094,10 +2092,7 @@ class SqlWalker implements TreeWalker $sql = $leftExpr . ($likeExpr->not ? ' NOT' : '') . ' LIKE '; if ($likeExpr->stringPattern instanceof AST\InputParameter) { - $inputParam = $likeExpr->stringPattern; - $dqlParamKey = $inputParam->name; - $this->parserResult->addParameterMapping($dqlParamKey, $this->sqlParamIndex++); - $sql .= '?'; + $sql .= $this->walkInputParameter($likeExpr->stringPattern); } elseif ($likeExpr->stringPattern instanceof AST\Functions\FunctionNode) { $sql .= $this->walkFunction($likeExpr->stringPattern); } elseif ($likeExpr->stringPattern instanceof AST\PathExpression) { From a765f2e3b65d65473bdf94724b885f16574ff5f0 Mon Sep 17 00:00:00 2001 From: Christian Stoller Date: Tue, 20 Aug 2013 11:17:04 +0200 Subject: [PATCH 253/332] added yaml mapping documentation of uniqueConstraint --- docs/en/reference/yaml-mapping.rst | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/docs/en/reference/yaml-mapping.rst b/docs/en/reference/yaml-mapping.rst index bc6c64c8f..1d7f8b17a 100644 --- a/docs/en/reference/yaml-mapping.rst +++ b/docs/en/reference/yaml-mapping.rst @@ -114,4 +114,22 @@ of several common elements: Be aware that class-names specified in the YAML files should be fully qualified. +Reference +~~~~~~~~~~~~~~~~~~~~~~ + +Unique Constraints +------------------ + +It is possible to define unique constraints by the following declaration: + +.. code-block:: yaml + + # ECommerceProduct.orm.yml + ECommerceProduct: + type: entity + fields: + # definition of some fields + uniqueConstraints: + search_idx: + columns: [ name, email ] From b041c2281434392dbf4b123aeaec17e4172d991a Mon Sep 17 00:00:00 2001 From: Adam Prager Date: Tue, 20 Aug 2013 12:15:17 +0200 Subject: [PATCH 254/332] Entity generator - trait in parent class --- lib/Doctrine/ORM/Tools/EntityGenerator.php | 10 +++++- .../Tests/Models/DDC2372/DDC2372Admin.php | 8 +++++ .../Tests/ORM/Tools/EntityGeneratorTest.php | 31 +++++++++++++++++++ 3 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 tests/Doctrine/Tests/Models/DDC2372/DDC2372Admin.php diff --git a/lib/Doctrine/ORM/Tools/EntityGenerator.php b/lib/Doctrine/ORM/Tools/EntityGenerator.php index 6d497d7f4..8a8880606 100644 --- a/lib/Doctrine/ORM/Tools/EntityGenerator.php +++ b/lib/Doctrine/ORM/Tools/EntityGenerator.php @@ -766,7 +766,15 @@ public function __construct() ? new \ReflectionClass($metadata->name) : $metadata->reflClass; - return $reflClass->getTraits(); + $traits = array(); + + while ($reflClass !== false) { + $traits = array_merge($traits, $reflClass->getTraits()); + + $reflClass = $reflClass->getParentClass(); + } + + return $traits; } return array(); diff --git a/tests/Doctrine/Tests/Models/DDC2372/DDC2372Admin.php b/tests/Doctrine/Tests/Models/DDC2372/DDC2372Admin.php new file mode 100644 index 000000000..604e919f7 --- /dev/null +++ b/tests/Doctrine/Tests/Models/DDC2372/DDC2372Admin.php @@ -0,0 +1,8 @@ +assertSame($reflClass->hasMethod('getAddress'), false); } + /** + * @group DDC-2372 + */ + public function testTraitPropertiesAndMethodsAreNotDuplicatedInChildClasses() + { + if (PHP_VERSION_ID < 50400) { + $this->markTestSkipped('Traits are not available before php 5.4.'); + } + + $cmf = new ClassMetadataFactory(); + $em = $this->_getTestEntityManager(); + $cmf->setEntityManager($em); + + $user = new DDC2372Admin(); + $metadata = $cmf->getMetadataFor(get_class($user)); + $metadata->name = $this->_namespace . "\DDC2372Admin"; + $metadata->namespace = $this->_namespace; + + $this->_generator->writeEntityClass($metadata, $this->_tmpDir); + + $this->assertFileExists($this->_tmpDir . "/" . $this->_namespace . "/DDC2372Admin.php"); + require $this->_tmpDir . "/" . $this->_namespace . "/DDC2372Admin.php"; + + $reflClass = new \ReflectionClass($metadata->name); + + $this->assertSame($reflClass->hasProperty('address'), false); + $this->assertSame($reflClass->hasMethod('setAddress'), false); + $this->assertSame($reflClass->hasMethod('getAddress'), false); + } + /** * @return array */ From 09d51f9df5bbd33c9b00a8941814dd5a4b818441 Mon Sep 17 00:00:00 2001 From: Guilherme Blanco Date: Wed, 21 Aug 2013 23:39:40 -0400 Subject: [PATCH 255/332] Fixing missing table aliases when using Many2Many persister. --- lib/Doctrine/ORM/Persisters/ManyToManyPersister.php | 10 +++++++--- .../Tests/ORM/Functional/Ticket/DDC1595Test.php | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php b/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php index d9f7e30ca..1ec6e5e7c 100644 --- a/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php +++ b/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php @@ -235,7 +235,7 @@ class ManyToManyPersister extends AbstractCollectionPersister foreach ($joinColumns as $joinColumn) { $columnName = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); $referencedName = $joinColumn['referencedColumnName']; - $conditions[] = $columnName . ' = ?'; + $conditions[] = 't.' . $columnName . ' = ?'; $params[] = ($class->containsForeignIdentifier) ? $id[$class->getFieldForColumn($referencedName)] : $id[$class->fieldNames[$referencedName]]; @@ -361,12 +361,13 @@ class ManyToManyPersister extends AbstractCollectionPersister $params = array(); foreach ($mapping['joinTableColumns'] as $joinTableColumn) { - $whereClauses[] = $joinTableColumn . ' = ?'; + $whereClauses[] = ($addFilters ? 't.' : '') . $joinTableColumn . ' = ?'; if (isset($mapping['relationToTargetKeyColumns'][$joinTableColumn])) { $params[] = ($targetClass->containsForeignIdentifier) ? $targetId[$targetClass->getFieldForColumn($mapping['relationToTargetKeyColumns'][$joinTableColumn])] : $targetId[$targetClass->fieldNames[$mapping['relationToTargetKeyColumns'][$joinTableColumn]]]; + continue; } @@ -377,9 +378,12 @@ class ManyToManyPersister extends AbstractCollectionPersister } if ($addFilters) { + $quotedJoinTable .= ' t'; + list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($filterMapping); + if ($filterSql) { - $quotedJoinTable .= ' t ' . $joinTargetEntitySQL; + $quotedJoinTable .= ' ' . $joinTargetEntitySQL; $whereClauses[] = $filterSql; } } diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1595Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1595Test.php index a45c9f1f2..4ccb4c5f4 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1595Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1595Test.php @@ -53,7 +53,7 @@ class DDC1595Test extends \Doctrine\Tests\OrmFunctionalTestCase $entities = $entity1->getEntities()->count(); $this->assertEquals( - "SELECT COUNT(*) FROM entity1_entity2 t WHERE parent = ?", + "SELECT COUNT(*) FROM entity1_entity2 t WHERE t.parent = ?", $sqlLogger->queries[count($sqlLogger->queries)]['sql'] ); } From 5669aaf4a315a17e1a4c39900e1145326fd972f4 Mon Sep 17 00:00:00 2001 From: Attila Fulop Date: Wed, 28 Aug 2013 18:26:16 +0300 Subject: [PATCH 256/332] Fix for entity generator discriminator column --- 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 8a8880606..5bbaf280c 100644 --- a/lib/Doctrine/ORM/Tools/EntityGenerator.php +++ b/lib/Doctrine/ORM/Tools/EntityGenerator.php @@ -953,7 +953,7 @@ public function __construct() protected function generateDiscriminatorColumnAnnotation($metadata) { if ($metadata->inheritanceType != ClassMetadataInfo::INHERITANCE_TYPE_NONE) { - $discrColumn = $metadata->discriminatorValue; + $discrColumn = $metadata->discriminatorColumn; $columnDefinition = 'name="' . $discrColumn['name'] . '", type="' . $discrColumn['type'] . '", length=' . $discrColumn['length']; From b0513a75173326016e39800c0f513f3f5e9e908f Mon Sep 17 00:00:00 2001 From: Christian Morgan Date: Wed, 28 Aug 2013 22:35:29 +0100 Subject: [PATCH 257/332] Added indexBy option to createQueryBuilder Added way to access the underlying QueryBuilder#from() method's 'indexBy' parameter when using EntityRepository#createQueryBuilder() --- lib/Doctrine/ORM/EntityRepository.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/Doctrine/ORM/EntityRepository.php b/lib/Doctrine/ORM/EntityRepository.php index 9a968d1ba..cb6dd6ffd 100644 --- a/lib/Doctrine/ORM/EntityRepository.php +++ b/lib/Doctrine/ORM/EntityRepository.php @@ -75,14 +75,15 @@ class EntityRepository implements ObjectRepository, Selectable * Creates a new QueryBuilder instance that is prepopulated for this entity name. * * @param string $alias + * @param string $indexBy The index for the from. * * @return QueryBuilder */ - public function createQueryBuilder($alias) + public function createQueryBuilder($alias, $indexBy = null) { return $this->_em->createQueryBuilder() ->select($alias) - ->from($this->_entityName, $alias); + ->from($this->_entityName, $alias, $indexBy); } /** From f98dfc475866a57f1c7596cc586e6f96ae0228bc Mon Sep 17 00:00:00 2001 From: Maks Feltrin Date: Thu, 29 Aug 2013 08:36:39 +0200 Subject: [PATCH 258/332] DO NOT OVERRIDE CUSTOM TREE WALKERS IN getIterator() --- lib/Doctrine/ORM/Tools/Pagination/Paginator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Tools/Pagination/Paginator.php b/lib/Doctrine/ORM/Tools/Pagination/Paginator.php index b9e8c5298..ecace32f6 100755 --- a/lib/Doctrine/ORM/Tools/Pagination/Paginator.php +++ b/lib/Doctrine/ORM/Tools/Pagination/Paginator.php @@ -178,7 +178,7 @@ class Paginator implements \Countable, \IteratorAggregate return new \ArrayIterator(array()); } - $whereInQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\WhereInWalker')); + $this->appendTreeWalker($whereInQuery, 'Doctrine\ORM\Tools\Pagination\WhereInWalker'); $whereInQuery->setHint(WhereInWalker::HINT_PAGINATOR_ID_COUNT, count($ids)); $whereInQuery->setFirstResult(null)->setMaxResults(null); $whereInQuery->setParameter(WhereInWalker::PAGINATOR_ID_ALIAS, $ids); From cf2580d2844d13d6450b696592dd52165f9f7e5a Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sat, 31 Aug 2013 15:46:10 +0200 Subject: [PATCH 259/332] Cleanup of Configuration and Mapping reference chapters. --- docs/en/reference/basic-mapping.rst | 361 ++++++++++++---------------- docs/en/reference/configuration.rst | 11 +- 2 files changed, 164 insertions(+), 208 deletions(-) diff --git a/docs/en/reference/basic-mapping.rst b/docs/en/reference/basic-mapping.rst index 6537b0b12..379167ce9 100644 --- a/docs/en/reference/basic-mapping.rst +++ b/docs/en/reference/basic-mapping.rst @@ -2,8 +2,8 @@ Basic Mapping ============= This chapter explains the basic mapping of objects and properties. -Mapping of associations will be covered in the next chapter -"Association Mapping". +Mapping of associations will be covered in the next chapter on +:doc:`Association Mapping `. Mapping Drivers --------------- @@ -11,30 +11,27 @@ Mapping Drivers Doctrine provides several different ways for specifying object-relational mapping metadata: - - Docblock Annotations - XML - YAML +- PHP code -This manual usually mentions docblock annotations in all the examples -that are spread throughout all chapters, however for many examples -alternative YAML and XML examples are given as well. There are dedicated -reference chapters for XML and YAML mapping, respectively that explain them -in more detail. There is also an Annotation reference chapter. +This manual usually mentions docblock annotations in all the examples that are +spread throughout all chapters, however for many examples alternative YAML and +XML examples are given as well. There are dedicated reference chapters for +:doc:`XML ` and :doc:`YAML ` mapping, respectively +that explain them in more detail. There is also a reference chapter for +:doc:`Annotations `. .. note:: - If you're wondering which mapping driver gives the best - performance, the answer is: They all give exactly the same performance. - Once the metadata of a class has - been read from the source (annotations, xml or yaml) it is stored - in an instance of the ``Doctrine\ORM\Mapping\ClassMetadata`` class - and these instances are stored in the metadata cache. Therefore at - the end of the day all drivers perform equally well. If you're not - using a metadata cache (not recommended!) then the XML driver might - have a slight edge in performance due to the powerful native XML - support in PHP. - + All metadata drivers give exactly the same performance. Once the metadata + of a class has been read from the source (annotations, xml or yaml) it is + stored in an instance of the ``Doctrine\ORM\Mapping\ClassMetadata`` class + and these instances are stored in the metadata cache. Therefore at the end + of the day all drivers perform equally well. If you're not using a metadata + cache (not recommended!) then the XML driver is the fastest by using PHP's + native XML support. Introduction to Docblock Annotations ------------------------------------ @@ -69,9 +66,8 @@ annotations for supplying object-relational mapping metadata. Persistent classes ------------------ -In order to mark a class for object-relational persistence it needs -to be designated as an entity. This can be done through the -``@Entity`` marker annotation. +Every PHP Classthat you want to save in the database using Doctrine +need to be configured as "Entity". .. configuration-block:: @@ -98,9 +94,9 @@ to be designated as an entity. This can be done through the type: entity # ... -By default, the entity will be persisted to a table with the same -name as the class name. In order to change that, you can use the -``@Table`` annotation as follows: +With no additional information given Doctrine expects the entity to be saved +into a table with the same name as the class. You can change this assumption +by adding more information about the used table: .. configuration-block:: @@ -131,24 +127,22 @@ name as the class name. In order to change that, you can use the table: my_persistent_class # ... -Now instances of MyPersistentClass will be persisted into a table -named ``my_persistent_class``. +In this example the class ``MyPersistentClass`` will be saved and fetched from +the table ``my_persistent_class``. Doctrine Mapping Types ---------------------- -A Doctrine Mapping Type defines the mapping between a PHP type and -a SQL type. All Doctrine Mapping Types that ship with Doctrine are -fully portable between different RDBMS. You can even write your own -custom mapping types that might or might not be portable, which is -explained later in this chapter. +A Doctrine Mapping Type defines the conversion the type of a PHP variable and +an SQL type. All Mapping Types that ship with Doctrine are fully portable +between the supported database systems. You can add your own custom mapping +types to add more conversions. -For example, the Doctrine Mapping Type ``string`` defines the +As an example the Doctrine Mapping Type ``string`` defines the mapping from a PHP string to a SQL VARCHAR (or VARCHAR2 etc. depending on the RDBMS brand). Here is a quick overview of the built-in mapping types: - - ``string``: Type that maps a SQL VARCHAR to a PHP string. - ``integer``: Type that maps a SQL INT to a PHP integer. - ``smallint``: Type that maps a database SMALLINT to a PHP @@ -180,14 +174,6 @@ built-in mapping types: varchar but uses a specific type if the platform supports it. - ``blob``: Type that maps a SQL BLOB to a PHP resource stream -.. note:: - - Doctrine Mapping Types are NOT SQL types and NOT PHP - types! They are mapping types between 2 types. - Additionally Mapping types are *case-sensitive*. For example, using - a DateTime column will NOT match the datetime type that ships with - Doctrine 2. - .. note:: DateTime and Object types are compared by reference, not by value. Doctrine updates this values @@ -206,21 +192,17 @@ built-in mapping types: on working with datetimes that gives hints for implementing multi timezone applications. - Property Mapping ---------------- -After a class has been marked as an entity it can specify mappings -for its instance fields. Here we will only look at simple fields -that hold scalar values like strings, numbers, etc. Associations to -other objects are covered in the chapter "Association Mapping". +Properties of an entity class can be mapped to columns of the +SQL table of that entity. + +To configure a property use the ``@Column`` docblock annotation. This +annotation usually requires at least 1 attribute to be set, the ``type``. The +``type`` attribute specifies the Doctrine Mapping Type to use for the field. If +the type is not specified, ``string`` is used as the default mapping type. -To mark a property for relational persistence the ``@Column`` -docblock annotation is used. This annotation usually requires at -least 1 attribute to be set, the ``type``. The ``type`` attribute -specifies the Doctrine Mapping Type to use for the field. If the -type is not specified, 'string' is used as the default mapping type -since it is the most flexible. Example: @@ -258,13 +240,11 @@ Example: name: length: 50 -In that example we mapped the field ``id`` to the column ``id`` -using the mapping type ``integer`` and the field ``name`` is mapped -to the column ``name`` with the default mapping type ``string``. As -you can see, by default the column names are assumed to be the same -as the field names. To specify a different name for the column, you -can use the ``name`` attribute of the Column annotation as -follows: +In that example we configured the property ``id`` to map to the column ``id`` +using the mapping type ``integer``. The field ``name`` is mapped to the column +``name`` with the default mapping type ``string``. Column names are assumed to +be the same as the field names unless you pecify a different name for the +column using the ``name`` attribute of the Column annotation: .. configuration-block:: @@ -294,7 +274,6 @@ follows: The Column annotation has some more attributes. Here is a complete list: - - ``type``: (optional, defaults to 'string') The mapping type to use for the column. - ``name``: (optional, defaults to field name) The name of the @@ -310,129 +289,12 @@ list: - ``scale``: (optional, default 0) The scale for a decimal (exact numeric) column. (Applies only if a decimal column is used.) -.. _reference-basic-mapping-custom-mapping-types: - -Custom Mapping Types --------------------- - -Doctrine allows you to create new mapping types. This can come in -handy when you're missing a specific mapping type or when you want -to replace the existing implementation of a mapping type. - -In order to create a new mapping type you need to subclass -``Doctrine\DBAL\Types\Type`` and implement/override the methods as -you wish. Here is an example skeleton of such a custom type class: - -.. code-block:: php - - getConnection(); - $conn->getDatabasePlatform()->registerDoctrineTypeMapping('db_mytype', 'mytype'); - -Now using Schema-Tool, whenever it detects a column having the -``db_mytype`` it will convert it into a ``mytype`` Doctrine Type -instance for Schema representation. Keep in mind that you can -easily produce clashes this way, each database type can only map to -exactly one Doctrine mapping type. - -Custom ColumnDefinition ------------------------ - -You can define a custom definition for each column using the "columnDefinition" -attribute of ``@Column``. You have to define all the definitions that follow -the name of a column here. - -.. note:: - - Using columnDefinition will break change-detection in SchemaTool. - Identifiers / Primary Keys -------------------------- Every entity class needs an identifier/primary key. You designate -the field that serves as the identifier with the ``@Id`` marker -annotation. Here is an example: +the field that serves as the identifier with the ``@Id`` +annotation: .. configuration-block:: @@ -466,14 +328,12 @@ annotation. Here is an example: name: length: 50 -Without doing anything else, the identifier is assumed to be -manually assigned. That means your code would need to properly set -the identifier property before passing a new entity to +This definition is missing an ID generation strategy, which means that your code needs to assign +the identifier manually before passing a new entity to ``EntityManager#persist($entity)``. -A common alternative strategy is to use a generated value as the -identifier. To do this, you use the ``@GeneratedValue`` annotation -like this: +Doctrine can alternatively generate identifiers for entities using generation strategies, +using database sequences or auto incrementing numbers. .. configuration-block:: @@ -531,7 +391,6 @@ make use of some additional features. Here is the list of possible generation strategies: - - ``AUTO`` (default): Tells Doctrine to pick the strategy that is preferred by the used database platform. The preferred strategies are IDENTITY for MySQL, SQLite and MsSQL and SEQUENCE for Oracle @@ -648,12 +507,10 @@ To designate a composite primary key / identifier, simply put the Quoting Reserved Words ---------------------- -It may sometimes be necessary to quote a column or table name -because it conflicts with a reserved word of the particular RDBMS -in use. This is often referred to as "Identifier Quoting". To let -Doctrine know that you would like a table or column name to be -quoted in all SQL statements, enclose the table or column name in -backticks. Here is an example: +Sometimes it is necessary to quote a column or table name because of reserved +word conflicts. Doctrine does not quote identifiers automatically, because it +leads to more problems then it would solve. Quoting tables and column names +needs to be done explicitly using ticks in the definition. .. code-block:: php @@ -666,18 +523,116 @@ according to the used database platform. .. warning:: - Identifier Quoting is not supported for join column - names or discriminator column names. + Identifier Quoting does not work for join column names or discriminator + column names. -.. warning:: +.. _reference-basic-mapping-custom-mapping-types: - Identifier Quoting is a feature that is mainly intended - to support legacy database schemas. The use of reserved words and - identifier quoting is generally discouraged. Identifier quoting - should not be used to enable the use non-standard-characters such - as a dash in a hypothetical column ``test-name``. Also Schema-Tool - will likely have troubles when quoting is used for case-sensitivity - reasons (in Oracle for example). +Custom Mapping Types +-------------------- +Doctrine allows you to create new mapping types. This can come in +handy when you're missing a specific mapping type or when you want +to replace the existing implementation of a mapping type. +In order to create a new mapping type you need to subclass +``Doctrine\DBAL\Types\Type`` and implement/override the methods as +you wish. Here is an example skeleton of such a custom type class: + +.. code-block:: php + + getConnection(); + $conn->getDatabasePlatform()->registerDoctrineTypeMapping('db_mytype', 'mytype'); + +When registering the custom types in the configuration you specify a unique +name for the mapping type and map that to the corresponding fully qualified +class name. Now the new type can be used when mapping columns: + +.. code-block:: php + + Date: Sat, 31 Aug 2013 16:25:53 +0200 Subject: [PATCH 260/332] Cleanup Association Mapping chapter --- docs/en/reference/association-mapping.rst | 451 ++++++++++------------ 1 file changed, 200 insertions(+), 251 deletions(-) diff --git a/docs/en/reference/association-mapping.rst b/docs/en/reference/association-mapping.rst index f41bb610f..c212e57a5 100644 --- a/docs/en/reference/association-mapping.rst +++ b/docs/en/reference/association-mapping.rst @@ -1,12 +1,11 @@ Association Mapping =================== -This chapter introduces association mappings which are used to explain -references between objects and are mapped to a relational database using -foreign keys. +This chapter explains mapping associations between objects. -Instead of working with the foreign keys directly you will always work with -references to objects: +Instead of working with foreign keys in your code, you will always work with +references to objects instead and Doctrine will convert those references +to foreign keys internally. - A reference to a single object is represented by a foreign key. - A collection of objects is represented by many foreign keys pointing to the object holding the collection @@ -17,15 +16,89 @@ This chapter is split into three different sections. - :ref:`association_mapping_defaults` are explained that simplify the use-case examples. - :ref:`collections` are introduced that contain entities in associations. -To master associations you should also learn about :doc:`owning and inverse sides of associations ` +To gain a full understanding of associations you should also read about :doc:`owning and +inverse sides of associations ` + +Many-To-One, Unidirectional +--------------------------- + +A many-to-one association is the most common association between objects. + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + + .. code-block:: yaml + + User: + type: entity + manyToOne: + address: + targetEntity: Address + joinColumn: + name: address_id + referencedColumnName: id + + +.. note:: + + The above ``@JoinColumn`` is optional as it would default + to ``address_id`` and ``id`` anyways. You can omit it and let it + use the defaults. + +Generated MySQL Schema: + +.. code-block:: sql + + CREATE TABLE User ( + id INT AUTO_INCREMENT NOT NULL, + address_id INT DEFAULT NULL, + PRIMARY KEY(id) + ) ENGINE = InnoDB; + + CREATE TABLE Address ( + id INT AUTO_INCREMENT NOT NULL, + PRIMARY KEY(id) + ) ENGINE = InnoDB; + + ALTER TABLE User ADD FOREIGN KEY (address_id) REFERENCES Address(id); One-To-One, Unidirectional -------------------------- -A unidirectional one-to-one association is very common. Here is an -example of a ``Product`` that has one ``Shipping`` object -associated to it. The ``Shipping`` side does not reference back to -the ``Product`` so it is unidirectional. +Here is an example of a one-to-one association with a ``Product`` entity that +references one ``Shipping`` entity. The ``Shipping`` does not reference back to +the ``Product`` so that the reference is said to be unidirectional, in one +direction only. .. configuration-block:: @@ -184,7 +257,7 @@ relation, the table ``Cart``. One-To-One, Self-referencing ---------------------------- -You can easily have self referencing one-to-one relationships like +You can define a self-referencing one-to-one relationships like below. .. code-block:: php @@ -218,6 +291,102 @@ With the generated MySQL Schema: ) ENGINE = InnoDB; ALTER TABLE Student ADD FOREIGN KEY (mentor_id) REFERENCES Student(id); +One-To-Many, Bidirectional +-------------------------- + +A one-to-many association has to be bidirectional, unless you are using an +additional join-table. This is necessary, because of the foreign key +in a one-to-many association being defined on the "many" side. Doctrine +needs a many-to-one association that defines the mapping of this +foreign key. + +This bidirectional mapping requires the ``mappedBy`` attribute on the +``OneToMany`` association and the ``inversedBy`` attribute on the ``ManyToOne`` +association. + +.. configuration-block:: + + .. code-block:: php + + features = new ArrayCollection(); + } + } + + /** @Entity **/ + class Feature + { + // ... + /** + * @ManyToOne(targetEntity="Product", inversedBy="features") + * @JoinColumn(name="product_id", referencedColumnName="id") + **/ + private $product; + // ... + } + + .. code-block:: xml + + + + + + + + + + + + + .. code-block:: yaml + + Product: + type: entity + oneToMany: + features: + targetEntity: Feature + mappedBy: product + Feature: + type: entity + manyToOne: + product: + targetEntity: Product + inversedBy: features + joinColumn: + name: product_id + referencedColumnName: id + +Note that the @JoinColumn is not really necessary in this example, +as the defaults would be the same. + +Generated MySQL Schema: + +.. code-block:: sql + + CREATE TABLE Product ( + id INT AUTO_INCREMENT NOT NULL, + PRIMARY KEY(id) + ) ENGINE = InnoDB; + CREATE TABLE Feature ( + id INT AUTO_INCREMENT NOT NULL, + product_id INT DEFAULT NULL, + PRIMARY KEY(id) + ) ENGINE = InnoDB; + ALTER TABLE Feature ADD FOREIGN KEY (product_id) REFERENCES Product(id); + One-To-Many, Unidirectional with Join Table ------------------------------------------- @@ -226,12 +395,6 @@ join table. From Doctrine's point of view, it is simply mapped as a unidirectional many-to-many whereby a unique constraint on one of the join columns enforces the one-to-many cardinality. -.. note:: - - One-To-Many uni-directional relations with join-table only - work using the @ManyToMany annotation and a unique-constraint. - - The following example sets up such a unidirectional one-to-many association: .. configuration-block:: @@ -326,171 +489,6 @@ Generates the following MySQL Schema: ALTER TABLE users_phonenumbers ADD FOREIGN KEY (user_id) REFERENCES User(id); ALTER TABLE users_phonenumbers ADD FOREIGN KEY (phonenumber_id) REFERENCES Phonenumber(id); - -Many-To-One, Unidirectional ---------------------------- - -You can easily implement a many-to-one unidirectional association -with the following: - -.. configuration-block:: - - .. code-block:: php - - - - - - - - - - .. code-block:: yaml - - User: - type: entity - manyToOne: - address: - targetEntity: Address - joinColumn: - name: address_id - referencedColumnName: id - - -.. note:: - - The above ``@JoinColumn`` is optional as it would default - to ``address_id`` and ``id`` anyways. You can omit it and let it - use the defaults. - - -Generated MySQL Schema: - -.. code-block:: sql - - CREATE TABLE User ( - id INT AUTO_INCREMENT NOT NULL, - address_id INT DEFAULT NULL, - PRIMARY KEY(id) - ) ENGINE = InnoDB; - - CREATE TABLE Address ( - id INT AUTO_INCREMENT NOT NULL, - PRIMARY KEY(id) - ) ENGINE = InnoDB; - - ALTER TABLE User ADD FOREIGN KEY (address_id) REFERENCES Address(id); - -One-To-Many, Bidirectional --------------------------- - -Bidirectional one-to-many associations are very common. The -following code shows an example with a Product and a Feature -class: - -.. configuration-block:: - - .. code-block:: php - - features = new \Doctrine\Common\Collections\ArrayCollection(); - } - } - - /** @Entity **/ - class Feature - { - // ... - /** - * @ManyToOne(targetEntity="Product", inversedBy="features") - * @JoinColumn(name="product_id", referencedColumnName="id") - **/ - private $product; - // ... - } - - .. code-block:: xml - - - - - - - - - - - - - .. code-block:: yaml - - Product: - type: entity - oneToMany: - features: - targetEntity: Feature - mappedBy: product - Feature: - type: entity - manyToOne: - product: - targetEntity: Product - inversedBy: features - joinColumn: - name: product_id - referencedColumnName: id - - -Note that the @JoinColumn is not really necessary in this example, -as the defaults would be the same. - -Generated MySQL Schema: - -.. code-block:: sql - - CREATE TABLE Product ( - id INT AUTO_INCREMENT NOT NULL, - PRIMARY KEY(id) - ) ENGINE = InnoDB; - CREATE TABLE Feature ( - id INT AUTO_INCREMENT NOT NULL, - product_id INT DEFAULT NULL, - PRIMARY KEY(id) - ) ENGINE = InnoDB; - ALTER TABLE Feature ADD FOREIGN KEY (product_id) REFERENCES Product(id); - One-To-Many, Self-referencing ----------------------------- @@ -756,8 +754,8 @@ one is bidirectional. The MySQL schema is exactly the same as for the Many-To-Many uni-directional case above. -Picking Owning and Inverse Side -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Owning and Inverse Side on a ManyToMany association +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ For Many-To-Many associations you can chose which entity is the owning and which the inverse side. There is a very simple semantic @@ -869,11 +867,9 @@ Generated MySQL Schema: Mapping Defaults ---------------- -Before we introduce all the association mappings in detail, you -should note that the @JoinColumn and @JoinTable definitions are -usually optional and have sensible default values. The defaults for -a join column in a one-to-one/many-to-one association is as -follows: +The ``@JoinColumn`` and ``@JoinTable`` definitions are usually optional and have +sensible default values. The defaults for a join column in a +one-to-one/many-to-one association is as follows: :: @@ -973,8 +969,7 @@ similar defaults. As an example, consider this mapping: groups: targetEntity: Group -This is essentially the same as the following, more verbose, -mapping: +This is essentially the same as the following, more verbose, mapping: .. configuration-block:: @@ -1043,73 +1038,28 @@ minimum. Collections ----------- -In all the examples of many-valued associations in this manual we -will make use of a ``Collection`` interface and a corresponding -default implementation ``ArrayCollection`` that are defined in the -``Doctrine\Common\Collections`` namespace. Why do we need that? -Doesn't that couple my domain model to Doctrine? Unfortunately, PHP -arrays, while being great for many things, do not make up for good -collections of business objects, especially not in the context of -an ORM. The reason is that plain PHP arrays can not be -transparently extended / instrumented in PHP code, which is -necessary for a lot of advanced ORM features. The classes / -interfaces that come closest to an OO collection are ArrayAccess -and ArrayObject but until instances of these types can be used in -all places where a plain array can be used (something that may -happen in PHP6) their usability is fairly limited. You "can" -type-hint on ``ArrayAccess`` instead of ``Collection``, since the -Collection interface extends ``ArrayAccess``, but this will -severely limit you in the way you can work with the collection, -because the ``ArrayAccess`` API is (intentionally) very primitive -and more importantly because you can not pass this collection to -all the useful PHP array functions, which makes it very hard to -work with. +Unfortunately, PHP arrays, while being great for many things, are missing +features that make them suitable for lazy loading in the context of an ORM. +This is why in all the examples of many-valued associations in this manual we +will make use of a ``Collection`` interface and its +default implementation ``ArrayCollection`` that are both defined in the +``Doctrine\Common\Collections`` namespace. A collection implements +the PHP interfaces ``ArrayAccess``, ``Traversable`` and ``Countable``. -.. warning:: +.. note:: The Collection interface and ArrayCollection class, like everything else in the Doctrine namespace, are neither part of the ORM, nor the DBAL, it is a plain PHP class that has no outside dependencies apart from dependencies on PHP itself (and the SPL). - Therefore using this class in your domain classes and elsewhere - does not introduce a coupling to the persistence layer. The - Collection class, like everything else in the Common namespace, is - not part of the persistence layer. You could even copy that class - over to your project if you want to remove Doctrine from your - project and all your domain classes will work the same as before. - - + Therefore using this class in your model and elsewhere + does not introduce a coupling to the ORM. Initializing Collections ------------------------ -You have to be careful when using entity fields that contain a -collection of related entities. Say we have a User entity that -contains a collection of groups: - -.. code-block:: php - - groups; - } - } - -With this code alone the ``$groups`` field only contains an -instance of ``Doctrine\Common\Collections\Collection`` if the user -is retrieved from Doctrine, however not after you instantiated a -fresh instance of the User. When your user entity is still new -``$groups`` will obviously be null. - -This is why we recommend to initialize all collection fields to an -empty ``ArrayCollection`` in your entities constructor: +You should always initialize the collections of your ``@OneToMany`` +and ``@ManyToMany`` associations in the constructor of your entities: .. code-block:: php @@ -1133,13 +1083,12 @@ empty ``ArrayCollection`` in your entities constructor: } } -Now the following code will work even if the Entity hasn't +The following code will then work even if the Entity hasn't been associated with an EntityManager yet: .. code-block:: php find('Group', $groupId); + $group = new Group(); $user = new User(); $user->getGroups()->add($group); - From c603fe7ab97c37f095d6d10cf3677b81c7e526fe Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sat, 31 Aug 2013 16:38:00 +0200 Subject: [PATCH 261/332] Simplify Working with Associations a little --- .../reference/working-with-associations.rst | 32 +++++++------------ 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/docs/en/reference/working-with-associations.rst b/docs/en/reference/working-with-associations.rst index 91f04966d..791425ee9 100644 --- a/docs/en/reference/working-with-associations.rst +++ b/docs/en/reference/working-with-associations.rst @@ -2,27 +2,25 @@ Working with Associations ========================= Associations between entities are represented just like in regular -object-oriented PHP, with references to other objects or -collections of objects. When it comes to persistence, it is -important to understand three main things: +object-oriented PHP code using references to other objects or +collections of objects. +Changes to associations in your code are not synchronized to the +database directly, only when calling ``EntityManager#flush()``. + +There are other concepts you should know about when working +with associations in Doctrine: -- The :doc:`concept of owning and inverse sides ` - in bidirectional associations. - If an entity is removed from a collection, the association is removed, not the entity itself. A collection of entities always only represents the association to the containing entities, not the entity itself. -- Collection-valued :ref:`persistent fields ` have to be instances of the +- When a bidirectional assocation is updated, Doctrine only checks + on one of both sides for these changes. This is called the :doc:`owning side ` + of the association. +- A property with a reference to many entities has to be instances of the ``Doctrine\Common\Collections\Collection`` interface. -Changes to associations in your code are not synchronized to the -database directly, but upon calling ``EntityManager#flush()``. - -To describe all the concepts of working with associations we -introduce a specific set of example entities that show all the -different flavors of association management in Doctrine. - Association Example Entities ---------------------------- @@ -44,10 +42,6 @@ information about its type and if it's the owning or inverse side. * Bidirectional - Many users have Many favorite comments (OWNING SIDE) * * @ManyToMany(targetEntity="Comment", inversedBy="userFavorites") - * @JoinTable(name="user_favorite_comments", - * joinColumns={@JoinColumn(name="user_id", referencedColumnName="id")}, - * inverseJoinColumns={@JoinColumn(name="favorite_comment_id", referencedColumnName="id")} - * ) */ private $favorites; @@ -55,10 +49,6 @@ information about its type and if it's the owning or inverse side. * Unidirectional - Many users have marked many comments as read * * @ManyToMany(targetEntity="Comment") - * @JoinTable(name="user_read_comments", - * joinColumns={@JoinColumn(name="user_id", referencedColumnName="id")}, - * inverseJoinColumns={@JoinColumn(name="comment_id", referencedColumnName="id")} - * ) */ private $commentsRead; From 528fe408396be75c83ce8fe03ea8b108cd5c817e Mon Sep 17 00:00:00 2001 From: Nick Hazen Date: Wed, 4 Sep 2013 16:55:06 -0400 Subject: [PATCH 262/332] Update create bug script This script is using the first argument twice and ignoring the second argument completely. --- docs/en/tutorials/getting-started.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/tutorials/getting-started.rst b/docs/en/tutorials/getting-started.rst index 4038947c7..02a07bc47 100644 --- a/docs/en/tutorials/getting-started.rst +++ b/docs/en/tutorials/getting-started.rst @@ -1037,7 +1037,7 @@ like this: require_once "bootstrap.php"; $theReporterId = $argv[1]; - $theDefaultEngineerId = $argv[1]; + $theDefaultEngineerId = $argv[2]; $productIds = explode(",", $argv[3]); $reporter = $entityManager->find("User", $theReporterId); From 982840ac3cbf899136cfba58a46813a3f6e26131 Mon Sep 17 00:00:00 2001 From: Nick Hazen Date: Thu, 5 Sep 2013 09:32:36 -0400 Subject: [PATCH 263/332] Fixed typo in mapping documentation "Classthat" to "Class that" line #69 --- docs/en/reference/basic-mapping.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/reference/basic-mapping.rst b/docs/en/reference/basic-mapping.rst index 379167ce9..8c525eab5 100644 --- a/docs/en/reference/basic-mapping.rst +++ b/docs/en/reference/basic-mapping.rst @@ -66,7 +66,7 @@ annotations for supplying object-relational mapping metadata. Persistent classes ------------------ -Every PHP Classthat you want to save in the database using Doctrine +Every PHP Class that you want to save in the database using Doctrine need to be configured as "Entity". .. configuration-block:: From 1d247fbeaa0fc90a0d38fd404c1951fd8953d24f Mon Sep 17 00:00:00 2001 From: Nick Hazen Date: Thu, 5 Sep 2013 09:48:00 -0400 Subject: [PATCH 264/332] Fixed typo in property mapping Changed 'pecify' to 'specify' line # 246 --- docs/en/reference/basic-mapping.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/reference/basic-mapping.rst b/docs/en/reference/basic-mapping.rst index 8c525eab5..e7b18b834 100644 --- a/docs/en/reference/basic-mapping.rst +++ b/docs/en/reference/basic-mapping.rst @@ -243,7 +243,7 @@ Example: In that example we configured the property ``id`` to map to the column ``id`` using the mapping type ``integer``. The field ``name`` is mapped to the column ``name`` with the default mapping type ``string``. Column names are assumed to -be the same as the field names unless you pecify a different name for the +be the same as the field names unless you specify a different name for the column using the ``name`` attribute of the Column annotation: .. configuration-block:: From e808865e6fd421178aec555d395878c34cbbe0d7 Mon Sep 17 00:00:00 2001 From: Nick Hazen Date: Thu, 5 Sep 2013 10:09:53 -0400 Subject: [PATCH 265/332] Fixed grammar in custom data types Changed 'are apply' to 'are applied' --- docs/en/reference/basic-mapping.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/reference/basic-mapping.rst b/docs/en/reference/basic-mapping.rst index 8c525eab5..6c28aa137 100644 --- a/docs/en/reference/basic-mapping.rst +++ b/docs/en/reference/basic-mapping.rst @@ -575,7 +575,7 @@ you wish. Here is an example skeleton of such a custom type class: } } -The following assumptions are apply to mapping types by the ORM: +The following assumptions are applied to mapping types by the ORM: - If the value of the field is *NULL* the method ``convertToDatabaseValue()`` is not called. From 490b501679073c5a1c980b9d56baed0e97b7f245 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sat, 7 Sep 2013 08:52:50 +0200 Subject: [PATCH 266/332] Fix tests running against Oracle not comparing SQL case-insenstive --- .../Tests/ORM/Functional/NativeQueryTest.php | 10 +++++----- .../ORM/Functional/OneToOneEagerLoadingTest.php | 10 +++++----- .../Tests/ORM/Functional/Ticket/DDC1430Test.php | 14 +++++++------- .../Tests/ORM/Functional/Ticket/DDC1595Test.php | 6 +++--- tests/Doctrine/Tests/OrmFunctionalTestCase.php | 5 +++++ 5 files changed, 25 insertions(+), 20 deletions(-) diff --git a/tests/Doctrine/Tests/ORM/Functional/NativeQueryTest.php b/tests/Doctrine/Tests/ORM/Functional/NativeQueryTest.php index d1167b3e7..bbdefb07d 100644 --- a/tests/Doctrine/Tests/ORM/Functional/NativeQueryTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/NativeQueryTest.php @@ -719,7 +719,7 @@ class NativeQueryTest extends \Doctrine\Tests\OrmFunctionalTestCase $selectClause = $rsm->generateSelectClause(); - $this->assertEquals('u.id AS id, u.status AS status, u.username AS username, u.name AS name, u.email_id AS email_id', $selectClause); + $this->assertSQLEquals('u.id AS id, u.status AS status, u.username AS username, u.name AS name, u.email_id AS email_id', $selectClause); } /** @@ -735,7 +735,7 @@ class NativeQueryTest extends \Doctrine\Tests\OrmFunctionalTestCase $selectClause = $rsm->generateSelectClause(); - $this->assertEquals('u.id AS id1, u.status AS status, u.username AS username2, u.name AS name, u.email_id AS email_id', $selectClause); + $this->assertSQLEquals('u.id AS id1, u.status AS status, u.username AS username2, u.name AS name, u.email_id AS email_id', $selectClause); } /** @@ -748,7 +748,7 @@ class NativeQueryTest extends \Doctrine\Tests\OrmFunctionalTestCase $selectClause = $rsm->generateSelectClause(array('u' => 'u1')); - $this->assertEquals('u1.id AS id, u1.status AS status, u1.username AS username, u1.name AS name, u1.email_id AS email_id', $selectClause); + $this->assertSQLEquals('u1.id AS id, u1.status AS status, u1.username AS username, u1.name AS name, u1.email_id AS email_id', $selectClause); } /** @@ -761,7 +761,7 @@ class NativeQueryTest extends \Doctrine\Tests\OrmFunctionalTestCase $selectClause = $rsm->generateSelectClause(); - $this->assertEquals('u.id AS id0, u.status AS status1, u.username AS username2, u.name AS name3, u.email_id AS email_id4', $selectClause); + $this->assertSQLEquals('u.id AS id0, u.status AS status1, u.username AS username2, u.name AS name3, u.email_id AS email_id4', $selectClause); } /** @@ -772,6 +772,6 @@ class NativeQueryTest extends \Doctrine\Tests\OrmFunctionalTestCase $rsm = new ResultSetMappingBuilder($this->_em, ResultSetMappingBuilder::COLUMN_RENAMING_INCREMENT); $rsm->addRootEntityFromClassMetadata('Doctrine\Tests\Models\CMS\CmsUser', 'u'); - $this->assertEquals('u.id AS id0, u.status AS status1, u.username AS username2, u.name AS name3, u.email_id AS email_id4', (string)$rsm); + $this->assertSQLEquals('u.id AS id0, u.status AS status1, u.username AS username2, u.name AS name3, u.email_id AS email_id4', (string)$rsm); } } diff --git a/tests/Doctrine/Tests/ORM/Functional/OneToOneEagerLoadingTest.php b/tests/Doctrine/Tests/ORM/Functional/OneToOneEagerLoadingTest.php index 7ad30ab7a..db62fa1ba 100644 --- a/tests/Doctrine/Tests/ORM/Functional/OneToOneEagerLoadingTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/OneToOneEagerLoadingTest.php @@ -128,14 +128,14 @@ class OneToOneEagerLoadingTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->_em->clear(); $train = $this->_em->find(get_class($train), $train->id); - $this->assertEquals( + $this->assertSQLEquals( "SELECT t0.id AS id1, t0.driver_id AS driver_id2, t3.id AS id4, t3.name AS name5, t0.owner_id AS owner_id6, t7.id AS id8, t7.name AS name9 FROM Train t0 LEFT JOIN TrainDriver t3 ON t0.driver_id = t3.id INNER JOIN TrainOwner t7 ON t0.owner_id = t7.id WHERE t0.id = ?", $this->_sqlLoggerStack->queries[$this->_sqlLoggerStack->currentQuery]['sql'] ); $this->_em->clear(); $driver = $this->_em->find(get_class($driver), $driver->id); - $this->assertEquals( + $this->assertSQLEquals( "SELECT t0.id AS id1, t0.name AS name2, t3.id AS id4, t3.driver_id AS driver_id5, t3.owner_id AS owner_id6 FROM TrainOwner t0 LEFT JOIN Train t3 ON t3.owner_id = t0.id WHERE t0.id IN (?)", $this->_sqlLoggerStack->queries[$this->_sqlLoggerStack->currentQuery]['sql'] ); @@ -156,13 +156,13 @@ class OneToOneEagerLoadingTest extends \Doctrine\Tests\OrmFunctionalTestCase $waggon = $this->_em->find(get_class($waggon), $waggon->id); // The last query is the eager loading of the owner of the train - $this->assertEquals( + $this->assertSQLEquals( "SELECT t0.id AS id1, t0.name AS name2, t3.id AS id4, t3.driver_id AS driver_id5, t3.owner_id AS owner_id6 FROM TrainOwner t0 LEFT JOIN Train t3 ON t3.owner_id = t0.id WHERE t0.id IN (?)", $this->_sqlLoggerStack->queries[$this->_sqlLoggerStack->currentQuery]['sql'] ); // The one before is the fetching of the waggon and train - $this->assertEquals( + $this->assertSQLEquals( "SELECT t0.id AS id1, t0.train_id AS train_id2, t3.id AS id4, t3.driver_id AS driver_id5, t3.owner_id AS owner_id6 FROM Waggon t0 INNER JOIN Train t3 ON t0.train_id = t3.id WHERE t0.id = ?", $this->_sqlLoggerStack->queries[$this->_sqlLoggerStack->currentQuery - 1]['sql'] ); @@ -177,7 +177,7 @@ class OneToOneEagerLoadingTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->_em->clear(); $waggon = $this->_em->find(get_class($owner), $owner->id); - $this->assertEquals( + $this->assertSQLEquals( "SELECT t0.id AS id1, t0.name AS name2, t3.id AS id4, t3.driver_id AS driver_id5, t3.owner_id AS owner_id6 FROM TrainOwner t0 LEFT JOIN Train t3 ON t3.owner_id = t0.id WHERE t0.id = ?", $this->_sqlLoggerStack->queries[$this->_sqlLoggerStack->currentQuery]['sql'] ); diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1430Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1430Test.php index a8102239d..41018e6d7 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1430Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1430Test.php @@ -35,8 +35,8 @@ class DDC1430Test extends \Doctrine\Tests\OrmFunctionalTestCase ->orderBy('o.id') ->getQuery(); - $this->assertEquals('SELECT o.id, o.date, COUNT(p.id) AS p_count FROM Doctrine\Tests\ORM\Functional\Ticket\DDC1430Order o LEFT JOIN o.products p GROUP BY o.id, o.date ORDER BY o.id ASC', $query->getDQL()); - $this->assertEquals('SELECT d0_.order_id AS order_id0, d0_.created_at AS created_at1, COUNT(d1_.id) AS sclr2 FROM DDC1430Order d0_ LEFT JOIN DDC1430OrderProduct d1_ ON d0_.order_id = d1_.order_id GROUP BY d0_.order_id, d0_.created_at ORDER BY d0_.order_id ASC', $query->getSQL()); + $this->assertSQLEquals('SELECT o.id, o.date, COUNT(p.id) AS p_count FROM Doctrine\Tests\ORM\Functional\Ticket\DDC1430Order o LEFT JOIN o.products p GROUP BY o.id, o.date ORDER BY o.id ASC', $query->getDQL()); + $this->assertSQLEquals('SELECT d0_.order_id AS order_id0, d0_.created_at AS created_at1, COUNT(d1_.id) AS sclr2 FROM DDC1430Order d0_ LEFT JOIN DDC1430OrderProduct d1_ ON d0_.order_id = d1_.order_id GROUP BY d0_.order_id, d0_.created_at ORDER BY d0_.order_id ASC', $query->getSQL()); $result = $query->getResult(); @@ -67,8 +67,8 @@ class DDC1430Test extends \Doctrine\Tests\OrmFunctionalTestCase ->getQuery(); - $this->assertEquals('SELECT o, COUNT(p.id) AS p_count FROM Doctrine\Tests\ORM\Functional\Ticket\DDC1430Order o LEFT JOIN o.products p GROUP BY o.id, o.date, o.status ORDER BY o.id ASC', $query->getDQL()); - $this->assertEquals('SELECT d0_.order_id AS order_id0, d0_.created_at AS created_at1, d0_.order_status AS order_status2, COUNT(d1_.id) AS sclr3 FROM DDC1430Order d0_ LEFT JOIN DDC1430OrderProduct d1_ ON d0_.order_id = d1_.order_id GROUP BY d0_.order_id, d0_.created_at, d0_.order_status ORDER BY d0_.order_id ASC', $query->getSQL()); + $this->assertSQLEquals('SELECT o, COUNT(p.id) AS p_count FROM Doctrine\Tests\ORM\Functional\Ticket\DDC1430Order o LEFT JOIN o.products p GROUP BY o.id, o.date, o.status ORDER BY o.id ASC', $query->getDQL()); + $this->assertSQLEquals('SELECT d0_.order_id AS order_id0, d0_.created_at AS created_at1, d0_.order_status AS order_status2, COUNT(d1_.id) AS sclr3 FROM DDC1430Order d0_ LEFT JOIN DDC1430OrderProduct d1_ ON d0_.order_id = d1_.order_id GROUP BY d0_.order_id, d0_.created_at, d0_.order_status ORDER BY d0_.order_id ASC', $query->getSQL()); $result = $query->getResult(); @@ -96,8 +96,8 @@ class DDC1430Test extends \Doctrine\Tests\OrmFunctionalTestCase ->getQuery(); - $this->assertEquals('SELECT o, COUNT(p.id) AS p_count FROM Doctrine\Tests\ORM\Functional\Ticket\DDC1430Order o LEFT JOIN o.products p GROUP BY o ORDER BY o.id ASC', $query->getDQL()); - $this->assertEquals('SELECT d0_.order_id AS order_id0, d0_.created_at AS created_at1, d0_.order_status AS order_status2, COUNT(d1_.id) AS sclr3 FROM DDC1430Order d0_ LEFT JOIN DDC1430OrderProduct d1_ ON d0_.order_id = d1_.order_id GROUP BY d0_.order_id, d0_.created_at, d0_.order_status ORDER BY d0_.order_id ASC', $query->getSQL()); + $this->assertSQLEquals('SELECT o, COUNT(p.id) AS p_count FROM Doctrine\Tests\ORM\Functional\Ticket\DDC1430Order o LEFT JOIN o.products p GROUP BY o ORDER BY o.id ASC', $query->getDQL()); + $this->assertSQLEquals('SELECT d0_.order_id AS order_id0, d0_.created_at AS created_at1, d0_.order_status AS order_status2, COUNT(d1_.id) AS sclr3 FROM DDC1430Order d0_ LEFT JOIN DDC1430OrderProduct d1_ ON d0_.order_id = d1_.order_id GROUP BY d0_.order_id, d0_.created_at, d0_.order_status ORDER BY d0_.order_id ASC', $query->getSQL()); $result = $query->getResult(); @@ -294,4 +294,4 @@ class DDC1430OrderProduct $this->value = $value; } -} \ No newline at end of file +} diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1595Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1595Test.php index 4ccb4c5f4..e6bc58942 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1595Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1595Test.php @@ -35,7 +35,7 @@ class DDC1595Test extends \Doctrine\Tests\OrmFunctionalTestCase $entity1 = $repository->find($e1->id); // DDC-1596 - $this->assertEquals( + $this->assertSQLEquals( "SELECT t0.id AS id1, t0.type FROM base t0 WHERE t0.id = ? AND t0.type IN ('Entity1')", $sqlLogger->queries[count($sqlLogger->queries)]['sql'] ); @@ -52,7 +52,7 @@ class DDC1595Test extends \Doctrine\Tests\OrmFunctionalTestCase $entity1 = $repository->find($e1->id); $entities = $entity1->getEntities()->count(); - $this->assertEquals( + $this->assertSQLEquals( "SELECT COUNT(*) FROM entity1_entity2 t WHERE t.parent = ?", $sqlLogger->queries[count($sqlLogger->queries)]['sql'] ); @@ -108,4 +108,4 @@ class DDC1595InheritedEntity1 extends DDC1595BaseInheritance */ class DDC1595InheritedEntity2 extends DDC1595BaseInheritance { -} \ No newline at end of file +} diff --git a/tests/Doctrine/Tests/OrmFunctionalTestCase.php b/tests/Doctrine/Tests/OrmFunctionalTestCase.php index 1b70be721..d2a41cfb9 100644 --- a/tests/Doctrine/Tests/OrmFunctionalTestCase.php +++ b/tests/Doctrine/Tests/OrmFunctionalTestCase.php @@ -479,6 +479,11 @@ abstract class OrmFunctionalTestCase extends OrmTestCase throw $e; } + public function assertSQLEquals($expectedSql, $actualSql) + { + return $this->assertEquals(strtolower($expectedSql), strtolower($actualSql), "Lowercase comparison of SQL statements failed."); + } + /** * Using the SQL Logger Stack this method retrieves the current query count executed in this test. * From 6139a61ff0c27e828d88c2c2c84392efd77f5153 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sat, 7 Sep 2013 12:56:49 +0200 Subject: [PATCH 267/332] New Build process - Switch from Phing to Ant - Remove PEAR packaging - Add Composer archiving --- build.properties | 10 +-- build.xml | 199 ++++++++++++++++++++++------------------------- composer.json | 5 +- 3 files changed, 98 insertions(+), 116 deletions(-) diff --git a/build.properties b/build.properties index 37c834c0e..4b4c921a5 100644 --- a/build.properties +++ b/build.properties @@ -1,11 +1,3 @@ -# Project Name -project.name=DoctrineORM - -# Dependency minimum versions -dependencies.common=2.2.0beta1 -dependencies.dbal=2.2.0beta1 -dependencies.sfconsole=2.0.0 - # Version class and file -project.version_class = Doctrine\ORM\Version +project.version_class = Doctrine\\ORM\\Version project.version_file = lib/Doctrine/ORM/Version.php diff --git a/build.xml b/build.xml index 890ed05bc..26132d19c 100644 --- a/build.xml +++ b/build.xml @@ -1,114 +1,101 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + - - - - DoctrineORM - Doctrine Object Relational Mapper - pear.doctrine-project.org - The Doctrine ORM package is the primary package containing the object relational mapper. - - - - - LGPL - - - - - - - - - - - - - script - Doctrine/Common/ - Doctrine/DBAL/ - Symfony/Component/Yaml/ - Symfony/Component/Console/ - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/composer.json b/composer.json index 8805d54a3..cce0c15b0 100644 --- a/composer.json +++ b/composer.json @@ -32,7 +32,10 @@ "bin": ["bin/doctrine", "bin/doctrine.php"], "extra": { "branch-alias": { - "dev-master": "2.4.x-dev" + "dev-master": "2.5.x-dev" } + }, + "archive": { + "exclude": ["!vendor", "tests", "*phpunit.xml", ".travis.yml", "build.xml", "build.properties", "composer.phar", "vendor/satooshi", "lib/vendor"] } } From 90c89b388103a74cfc597a527c2c53513a8a02c9 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sat, 7 Sep 2013 13:21:02 +0200 Subject: [PATCH 268/332] Travis should prefer dist. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 92b97851f..adbf643c2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,7 +16,7 @@ before_script: - sh -c "if [ '$DB' = 'pgsql' ]; then psql -c 'create database doctrine_tests;' -U postgres; fi" - sh -c "if [ '$DB' = 'pgsql' ]; then psql -c 'create database doctrine_tests_tmp;' -U postgres; fi" - sh -c "if [ '$DB' = 'mysql' ]; then mysql -e 'create database IF NOT EXISTS doctrine_tests_tmp;create database IF NOT EXISTS doctrine_tests;'; fi" - - composer install --prefer-source --dev + - composer install --prefer-dist --dev script: phpunit --configuration tests/travis/$DB.travis.xml From fab3f5e146f64173bb63b7c323f26bb6031d27ba Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sat, 7 Sep 2013 18:27:20 +0200 Subject: [PATCH 269/332] More excludes --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index cce0c15b0..66616dbf4 100644 --- a/composer.json +++ b/composer.json @@ -36,6 +36,6 @@ } }, "archive": { - "exclude": ["!vendor", "tests", "*phpunit.xml", ".travis.yml", "build.xml", "build.properties", "composer.phar", "vendor/satooshi", "lib/vendor"] + "exclude": ["!vendor", "tests", "*phpunit.xml", ".travis.yml", "build.xml", "build.properties", "composer.phar", "vendor/satooshi", "lib/vendor", "*.swp", "*coveralls.yml"] } } From 3719a6f2f2677e9c5f2450c4dab309de4b65be9a Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 8 Sep 2013 10:37:13 +0200 Subject: [PATCH 270/332] [DDC-2661] Fix bug in YamlDriver not passing options from id to mapField() --- lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php b/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php index 9e8818a16..1e7aa3356 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php @@ -264,6 +264,10 @@ class YamlDriver extends FileDriver $mapping['columnDefinition'] = $idElement['columnDefinition']; } + if (isset($idElement['options'])) { + $mapping['options'] = $idElement['options']; + } + $metadata->mapField($mapping); if (isset($idElement['generator'])) { From 275724fb467bfa09ddc12b04b73a41f5b2840303 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 8 Sep 2013 12:13:47 +0200 Subject: [PATCH 271/332] Add missing documentation for 2.4 features --- docs/en/reference/basic-mapping.rst | 21 ++++++++++++++++++- .../reference/dql-doctrine-query-language.rst | 9 ++++++++ docs/en/reference/query-builder.rst | 3 +++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/docs/en/reference/basic-mapping.rst b/docs/en/reference/basic-mapping.rst index 1cf1b16e3..cfd06cd24 100644 --- a/docs/en/reference/basic-mapping.rst +++ b/docs/en/reference/basic-mapping.rst @@ -524,10 +524,29 @@ according to the used database platform. .. warning:: Identifier Quoting does not work for join column names or discriminator - column names. + column names unless you are using a custom ``QuoteStrategy``. .. _reference-basic-mapping-custom-mapping-types: +.. versionadded: 2.3 + +For more control over column quoting the ``Doctrine\ORM\Mapping\QuoteStrategy`` interface +was introduced in 2.3. It is invoked for every column, table, alias and other +SQL names. You can implement the QuoteStrategy and set it by calling +``Doctrine\ORM\Configuration#setQuoteStrategy()``. + +.. versionadded: 2.4 + +The ANSI Quote Strategy was added, which assumes quoting is not necessary for any SQL name. +You can use it with the following code: + +.. code-block:: php + + setQuoteStrategy(new AnsiQuoteStrategy()); + Custom Mapping Types -------------------- diff --git a/docs/en/reference/dql-doctrine-query-language.rst b/docs/en/reference/dql-doctrine-query-language.rst index ec4907ec6..fd8fca430 100644 --- a/docs/en/reference/dql-doctrine-query-language.rst +++ b/docs/en/reference/dql-doctrine-query-language.rst @@ -427,6 +427,8 @@ Get all users visible on a given website that have chosen certain gender: createQuery('SELECT u FROM User u WHERE u.gender IN (SELECT IDENTITY(agl.gender) FROM Site s JOIN s.activeGenderList agl WHERE s.id = ?1)'); +.. versionadded:: 2.4 + Starting with 2.4, the IDENTITY() DQL function also works for composite primary keys: .. code-block:: php @@ -434,6 +436,13 @@ Starting with 2.4, the IDENTITY() DQL function also works for composite primary createQuery('SELECT IDENTITY(c.location, 'latitude') AS latitude, IDENTITY(c.location, 'longitude') AS longitude FROM Checkpoint c WHERE c.user = ?1'); +Joins between entities without associations were not possible until version +2.4, where you can generate an arbitrary join with the following syntax: + +.. code-block:: php + + createQuery('SELECT u FROM User u JOIN Blacklist b WITH u.email = b.email'); Partial Object Syntax ^^^^^^^^^^^^^^^^^^^^^ diff --git a/docs/en/reference/query-builder.rst b/docs/en/reference/query-builder.rst index b96e2dfe2..63f1a872a 100644 --- a/docs/en/reference/query-builder.rst +++ b/docs/en/reference/query-builder.rst @@ -433,6 +433,9 @@ complete list of supported helper methods available: // Example - $qb->expr()->like('u.firstname', $qb->expr()->literal('Gui%')) public function like($x, $y); // Returns Expr\Comparison instance + // Example - $qb->expr()->notLike('u.firstname', $qb->expr()->literal('Gui%')) + public function notLike($x, $y); // Returns Expr\Comparison instance + // Example - $qb->expr()->between('u.id', '1', '10') public function between($val, $x, $y); // Returns Expr\Func From f2fa47dedbf774c672f5c2e660ab563edec8e209 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 8 Sep 2013 13:04:08 +0200 Subject: [PATCH 272/332] Fix RST bug --- docs/en/reference/native-sql.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/reference/native-sql.rst b/docs/en/reference/native-sql.rst index 89eb2850b..75231b7a9 100644 --- a/docs/en/reference/native-sql.rst +++ b/docs/en/reference/native-sql.rst @@ -80,7 +80,7 @@ with inheritance hierachies. The builder extends the ``ResultSetMapping`` class and as such has all the functionality of it as well. -..versionadded:: 2.4 +.. versionadded:: 2.4 Starting with Doctrine ORM 2.4 you can generate the ``SELECT`` clause from a ``ResultSetMappingBuilder``. You can either cast the builder From 5c2157219d81ed475a467a89504ddff77fd6a720 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 8 Sep 2013 14:38:59 +0200 Subject: [PATCH 273/332] [DDC-2660] Fix error with NativeSQL, ResultSetMappingBuilder and Associations as Primary Key. --- .../ORM/Query/ResultSetMappingBuilder.php | 7 +- .../ORM/Functional/Ticket/DDC2660Test.php | 122 ++++++++++++++++++ 2 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2660Test.php diff --git a/lib/Doctrine/ORM/Query/ResultSetMappingBuilder.php b/lib/Doctrine/ORM/Query/ResultSetMappingBuilder.php index da7ce2565..ad159e0a5 100644 --- a/lib/Doctrine/ORM/Query/ResultSetMappingBuilder.php +++ b/lib/Doctrine/ORM/Query/ResultSetMappingBuilder.php @@ -168,7 +168,12 @@ class ResultSetMappingBuilder extends ResultSetMapping throw new \InvalidArgumentException("The column '$columnAlias' conflicts with another column in the mapper."); } - $this->addMetaResult($alias, $columnAlias, $columnName); + $this->addMetaResult( + $alias, + $columnAlias, + $columnName, + (isset($associationMapping['id']) && $associationMapping['id'] === true) + ); } } } diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2660Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2660Test.php new file mode 100644 index 000000000..78ffdeb4f --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2660Test.php @@ -0,0 +1,122 @@ +_schemaTool->createSchema(array( + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2660Product'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2660Customer'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2660CustomerOrder') + )); + } catch(\Exception $e) { + return; + } + + for ($i = 0; $i < 5; $i++) { + $product = new DDC2660Product(); + $customer = new DDC2660Customer(); + $order = new DDC2660CustomerOrder($product, $customer, 'name' . $i); + + $this->_em->persist($product); + $this->_em->persist($customer); + $this->_em->flush(); + + $this->_em->persist($order); + $this->_em->flush(); + } + + $this->_em->clear(); + } + + public function testIssueWithExtraColumn() + { + $sql = "SELECT o.product_id, o.customer_id, o.name FROM ddc_2660_customer_order o"; + + $rsm = new ResultSetMappingBuilder($this->_getEntityManager()); + $rsm->addRootEntityFromClassMetadata(__NAMESPACE__ . '\DDC2660CustomerOrder', 'c'); + + $query = $this->_em->createNativeQuery($sql, $rsm); + $result = $query->getResult(); + + $this->assertCount(5, $result); + + foreach ($result as $order) { + $this->assertNotNull($order); + $this->assertInstanceOf(__NAMESPACE__ . '\\DDC2660CustomerOrder', $order); + } + } + + public function testIssueWithoutExtraColumn() + { + $sql = "SELECT o.product_id, o.customer_id FROM ddc_2660_customer_order o"; + + $rsm = new ResultSetMappingBuilder($this->_getEntityManager()); + $rsm->addRootEntityFromClassMetadata(__NAMESPACE__ . '\DDC2660CustomerOrder', 'c'); + + $query = $this->_em->createNativeQuery($sql, $rsm); + $result = $query->getResult(); + + $this->assertCount(5, $result); + + foreach ($result as $order) { + $this->assertNotNull($order); + $this->assertInstanceOf(__NAMESPACE__ . '\\DDC2660CustomerOrder', $order); + } + } +} +/** + * @Entity @Table(name="ddc_2660_product") + */ +class DDC2660Product +{ + /** @Id @Column(type="integer") @GeneratedValue */ + public $id; +} + +/** @Entity @Table(name="ddc_2660_customer") */ +class DDC2660Customer +{ + /** @Id @Column(type="integer") @GeneratedValue */ + public $id; +} + +/** @Entity @Table(name="ddc_2660_customer_order") */ +class DDC2660CustomerOrder +{ + /** + * @Id @ManyToOne(targetEntity="DDC2660Product") + */ + public $product; + + /** + * @Id @ManyToOne(targetEntity="DDC2660Customer") + */ + public $customer; + + /** + * @Column(type="string") + */ + public $name; + + public function __construct(DDC2660Product $product, DDC2660Customer $customer, $name) + { + $this->product = $product; + $this->customer = $customer; + $this->name = $name; + } +} From 4a50493ab7211dfb277da7752a9ae7ef5510ee47 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 8 Sep 2013 15:59:39 +0200 Subject: [PATCH 274/332] [DDC-2608][DDC-2662] Fix SequenceGenerator requiring "sequenceName" and now throw exception. Fix a bug in quoting the sequenceName. --- .../ORM/Mapping/ClassMetadataInfo.php | 8 ++++-- lib/Doctrine/ORM/Mapping/MappingException.php | 12 +++++++++ .../Tests/ORM/Mapping/ClassMetadataTest.php | 27 ++++++++++++++++++- 3 files changed, 44 insertions(+), 3 deletions(-) diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php index 3d42b24e6..9b3b29492 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php @@ -2790,8 +2790,12 @@ class ClassMetadataInfo implements ClassMetadata */ public function setSequenceGeneratorDefinition(array $definition) { - if (isset($definition['name']) && $definition['name'] == '`') { - $definition['name'] = trim($definition['name'], '`'); + if ( ! isset($definition['sequenceName'])) { + throw MappingException::missingSequenceName($this->name); + } + + if ($definition['sequenceName'][0] == '`') { + $definition['sequenceName'] = trim($definition['sequenceName'], '`'); $definition['quoted'] = true; } diff --git a/lib/Doctrine/ORM/Mapping/MappingException.php b/lib/Doctrine/ORM/Mapping/MappingException.php index a9d5d1214..987e60917 100644 --- a/lib/Doctrine/ORM/Mapping/MappingException.php +++ b/lib/Doctrine/ORM/Mapping/MappingException.php @@ -757,4 +757,16 @@ class MappingException extends \Doctrine\ORM\ORMException $cascades )); } + + /** + * @param string $className + * + * @return MappingException + */ + public static function missingSequenceName($className) + { + return new self( + sprintf('Missing "sequenceName" attribute for sequence id generator definition on class "%s".', $className) + ); + } } diff --git a/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php b/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php index 4aaa2b9e4..67bf4c905 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php @@ -1066,6 +1066,31 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase $this->assertEquals(array('customtypeparent_source' => 'id'), $cm->associationMappings['friendsWithMe']['relationToSourceKeyColumns']); $this->assertEquals(array('customtypeparent_target' => 'id'), $cm->associationMappings['friendsWithMe']['relationToTargetKeyColumns']); } + + /** + * @group DDC-2608 + */ + public function testSetSequenceGeneratorThrowsExceptionWhenSequenceNameIsMissing() + { + $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'); + $cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService); + + $this->setExpectedException('Doctrine\ORM\Mapping\MappingException'); + $cm->setSequenceGeneratorDefinition(array()); + } + + /** + * @group DDC-2662 + */ + public function testQuotedSequenceName() + { + $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'); + $cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService); + + $cm->setSequenceGeneratorDefinition(array('sequenceName' => '`foo`')); + + $this->assertEquals(array('sequenceName' => 'foo', 'quoted' => true), $cm->sequenceGeneratorDefinition); + } } class MyNamespacedNamingStrategy extends \Doctrine\ORM\Mapping\DefaultNamingStrategy @@ -1092,4 +1117,4 @@ class MyPrefixNamingStrategy extends \Doctrine\ORM\Mapping\DefaultNamingStrategy { return strtolower($this->classToTableName($className)) . '_' . $propertyName; } -} \ No newline at end of file +} From 85f227372d02da2f21917c633ae1d61394d2b03b Mon Sep 17 00:00:00 2001 From: Douglas Greenshields Date: Sun, 8 Sep 2013 21:40:35 +0100 Subject: [PATCH 275/332] fixed English grammar --- docs/en/reference/events.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/en/reference/events.rst b/docs/en/reference/events.rst index 72e396100..f4cf9776d 100644 --- a/docs/en/reference/events.rst +++ b/docs/en/reference/events.rst @@ -742,9 +742,9 @@ Entity listeners .. versionadded:: 2.4 -An entity listeners is a lifecycle listener classes used for an entity. +An entity listener is a lifecycle listener class used for an entity. -- The entity listeners mapping may be applied to an entity class or mapped superclass. +- The entity listener's mapping may be applied to an entity class or mapped superclass. - An entity listener is defined by mapping the entity class with the corresponding mapping. .. configuration-block:: From 4a0a4094dafa74957a3426aae9671f8a96216d9c Mon Sep 17 00:00:00 2001 From: Matthieu Napoli Date: Mon, 9 Sep 2013 10:49:37 +0200 Subject: [PATCH 276/332] #DDC-2664 Document new Proxy factory flags usage as of DCOM-210 [Document new Proxy factory flags usage as of DCOM-210](http://www.doctrine-project.org/jira/browse/DDC-2664) --- docs/en/reference/advanced-configuration.rst | 51 +++++++++++++++----- 1 file changed, 39 insertions(+), 12 deletions(-) diff --git a/docs/en/reference/advanced-configuration.rst b/docs/en/reference/advanced-configuration.rst index 5c8ad01d1..c1b96294b 100644 --- a/docs/en/reference/advanced-configuration.rst +++ b/docs/en/reference/advanced-configuration.rst @@ -329,24 +329,51 @@ controls this behavior is: .. code-block:: php setAutoGenerateProxyClasses($bool); - $config->getAutoGenerateProxyClasses(); + $config->setAutoGenerateProxyClasses($mode); -The default value is ``TRUE`` for convenient development. However, -this setting is not optimal for performance and therefore not -recommended for a production environment. To eliminate the overhead -of proxy class generation during runtime, set this configuration -option to ``FALSE``. When you do this in a development environment, -note that you may get class/file not found errors if certain proxy -classes are not available or failing lazy-loads if new methods were -added to the entity class that are not yet in the proxy class. In -such a case, simply use the Doctrine Console to (re)generate the -proxy classes like so: +Possible values are: + +- ``Doctrine\Common\Proxy\AbstractProxyFactory::AUTOGENERATE_NEVER`` + +Never autogenerate a proxy and rely that it was generated by some +process before deployment. + +To generate the proxies, use: .. code-block:: php +When you do this in a development environment, +note that you may get class/file not found errors if certain proxy +classes are not available or failing lazy-loads if new methods were +added to the entity class that are not yet in the proxy class. +In such a case, simply use the Doctrine Console to (re)generate the +proxy classes. + $ ./doctrine orm:generate-proxies +- ``Doctrine\Common\Proxy\AbstractProxyFactory::AUTOGENERATE_ALWAYS`` + +Always generates a new proxy in every request. + +- ``Doctrine\Common\Proxy\AbstractProxyFactory::AUTOGENERATE_FILE_NOT_EXISTS`` + +Autogenerate the proxy class when the proxy file does not exist. +This strategy causes a file exists call whenever any proxy is +used the first time in a request. + +- ``Doctrine\Common\Proxy\AbstractProxyFactory::AUTOGENERATE_EVAL`` + +Generate the proxy classes using eval(), which avoids writing the +proxies to disk. This strategy is only sane for development. + +In a production environment, it is highly recommended to use +AUTOGENERATE_NEVER to allow for optimal performances. The other +options are interesting in development environment. + +Before v2.4, ``setAutoGenerateProxyClasses`` would accept a boolean +value. This is still possible, ``FALSE`` being equivalent to +AUTOGENERATE_NEVER and ``TRUE`` to AUTOGENERATE_ALWAYS. + Autoloading Proxies ------------------- From 26e1ac6afd6f0d3ac9c2ade644a015d04afd94bf Mon Sep 17 00:00:00 2001 From: Matthieu Napoli Date: Mon, 9 Sep 2013 11:20:12 +0200 Subject: [PATCH 277/332] #DDC-2664 Improving and fixing documentation for new Proxy factory flags usage as of DCOM-210 --- docs/en/reference/advanced-configuration.rst | 109 ++++++++++--------- 1 file changed, 59 insertions(+), 50 deletions(-) diff --git a/docs/en/reference/advanced-configuration.rst b/docs/en/reference/advanced-configuration.rst index c1b96294b..be127ce0a 100644 --- a/docs/en/reference/advanced-configuration.rst +++ b/docs/en/reference/advanced-configuration.rst @@ -210,17 +210,56 @@ implementation that logs to the standard output using ``echo`` and Auto-generating Proxy Classes (***OPTIONAL***) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Proxy classes can either be generated manually through the Doctrine +Console or automatically at runtime by Doctrine. The configuration +option that controls this behavior is: + .. code-block:: php setAutoGenerateProxyClasses($bool); - $config->getAutoGenerateProxyClasses(); + $config->setAutoGenerateProxyClasses($mode); -Gets or sets whether proxy classes should be generated -automatically at runtime by Doctrine. If set to ``FALSE``, proxy -classes must be generated manually through the doctrine command -line task ``generate-proxies``. The strongly recommended value for -a production environment is ``FALSE``. +Possible values for ``$mode`` are: + +- ``Doctrine\Common\Proxy\AbstractProxyFactory::AUTOGENERATE_NEVER`` + +Never autogenerate a proxy. You will need to generate the proxies +manually, for this use the Doctrine Console like so: + +.. code-block:: php + + $ ./doctrine orm:generate-proxies + +When you do this in a development environment, +be aware that you may get class/file not found errors if certain proxies +are not yet generated. You may also get failing lazy-loads if new +methods were added to the entity class that are not yet in the proxy class. +In such a case, simply use the Doctrine Console to (re)generate the +proxy classes. + +- ``Doctrine\Common\Proxy\AbstractProxyFactory::AUTOGENERATE_ALWAYS`` + +Always generates a new proxy in every request and writes it to disk. + +- ``Doctrine\Common\Proxy\AbstractProxyFactory::AUTOGENERATE_FILE_NOT_EXISTS`` + +Generate the proxy class when the proxy file does not exist. +This strategy causes a file exists call whenever any proxy is +used the first time in a request. + +- ``Doctrine\Common\Proxy\AbstractProxyFactory::AUTOGENERATE_EVAL`` + +Generate the proxy classes and evaluate them on the fly via eval(), +avoiding writing the proxies to disk. +This strategy is only sane for development. + +In a production environment, it is highly recommended to use +AUTOGENERATE_NEVER to allow for optimal performances. The other +options are interesting in development environment. + +Before v2.4, ``setAutoGenerateProxyClasses`` would accept a boolean +value. This is still possible, ``FALSE`` being equivalent to +AUTOGENERATE_NEVER and ``TRUE`` to AUTOGENERATE_ALWAYS. Development vs Production Configuration --------------------------------------- @@ -322,57 +361,27 @@ transparently initialize itself on first access. Generating Proxy classes ~~~~~~~~~~~~~~~~~~~~~~~~ -Proxy classes can either be generated manually through the Doctrine -Console or automatically by Doctrine. The configuration option that -controls this behavior is: +In a production environment, it is highly recommended to use +``AUTOGENERATE_NEVER`` to allow for optimal performances. +However you will be required to generate the proxies manually +using the Doctrine Console: .. code-block:: php - setAutoGenerateProxyClasses($mode); - -Possible values are: - -- ``Doctrine\Common\Proxy\AbstractProxyFactory::AUTOGENERATE_NEVER`` - -Never autogenerate a proxy and rely that it was generated by some -process before deployment. - -To generate the proxies, use: - -.. code-block:: php - -When you do this in a development environment, -note that you may get class/file not found errors if certain proxy -classes are not available or failing lazy-loads if new methods were -added to the entity class that are not yet in the proxy class. -In such a case, simply use the Doctrine Console to (re)generate the -proxy classes. - $ ./doctrine orm:generate-proxies -- ``Doctrine\Common\Proxy\AbstractProxyFactory::AUTOGENERATE_ALWAYS`` +The other options are interesting in development environment: -Always generates a new proxy in every request. +- ``AUTOGENERATE_ALWAYS`` will require you to create and configure +a proxy directory. Proxies will be generated and written to file +on each request, so any modification to your code will be acknowledged. -- ``Doctrine\Common\Proxy\AbstractProxyFactory::AUTOGENERATE_FILE_NOT_EXISTS`` +- ``AUTOGENERATE_FILE_NOT_EXISTS`` will not overwrite an existing +proxy file. If your code changes, you will need to regenerate the +proxies manually. -Autogenerate the proxy class when the proxy file does not exist. -This strategy causes a file exists call whenever any proxy is -used the first time in a request. - -- ``Doctrine\Common\Proxy\AbstractProxyFactory::AUTOGENERATE_EVAL`` - -Generate the proxy classes using eval(), which avoids writing the -proxies to disk. This strategy is only sane for development. - -In a production environment, it is highly recommended to use -AUTOGENERATE_NEVER to allow for optimal performances. The other -options are interesting in development environment. - -Before v2.4, ``setAutoGenerateProxyClasses`` would accept a boolean -value. This is still possible, ``FALSE`` being equivalent to -AUTOGENERATE_NEVER and ``TRUE`` to AUTOGENERATE_ALWAYS. +- ``AUTOGENERATE_EVAL`` will regenerate each proxy on each request, +but without writing them to disk. Autoloading Proxies ------------------- From 88754622419524bf92cfc18f9ab7fac148c35924 Mon Sep 17 00:00:00 2001 From: Matthieu Napoli Date: Mon, 9 Sep 2013 11:30:18 +0200 Subject: [PATCH 278/332] #DDC-2664 Improving and fixing documentation for new Proxy factory flags usage as of DCOM-210 --- lib/Doctrine/ORM/Configuration.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Configuration.php b/lib/Doctrine/ORM/Configuration.php index 3a096ba21..7ab147f98 100644 --- a/lib/Doctrine/ORM/Configuration.php +++ b/lib/Doctrine/ORM/Configuration.php @@ -90,7 +90,7 @@ class Configuration extends \Doctrine\DBAL\Configuration * Sets a boolean flag that indicates whether proxy classes should always be regenerated * during each script execution. * - * @param boolean $bool + * @param boolean|int $bool Possible values are constants of Doctrine\Common\Proxy\AbstractProxyFactory * * @return void */ From f7108b40c35c75028855e326ae55a7d6ffb5d97f Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Tue, 10 Sep 2013 20:55:06 +0200 Subject: [PATCH 279/332] Move Annotations introduction to the reference. --- docs/en/reference/annotations-reference.rst | 26 ++++++++++++ docs/en/reference/basic-mapping.rst | 46 +++++++-------------- 2 files changed, 41 insertions(+), 31 deletions(-) diff --git a/docs/en/reference/annotations-reference.rst b/docs/en/reference/annotations-reference.rst index d42fda4e4..80ed01e7a 100644 --- a/docs/en/reference/annotations-reference.rst +++ b/docs/en/reference/annotations-reference.rst @@ -1,6 +1,32 @@ Annotations Reference ===================== +You've probably used docblock annotations in some form already, +most likely to provide documentation metadata for a tool like +``PHPDocumentor`` (@author, @link, ...). Docblock annotations are a +tool to embed metadata inside the documentation section which can +then be processed by some tool. Doctrine 2 generalizes the concept +of docblock annotations so that they can be used for any kind of +metadata and so that it is easy to define new docblock annotations. +In order to allow more involved annotation values and to reduce the +chances of clashes with other docblock annotations, the Doctrine 2 +docblock annotations feature an alternative syntax that is heavily +inspired by the Annotation syntax introduced in Java 5. + +The implementation of these enhanced docblock annotations is +located in the ``Doctrine\Common\Annotations`` namespace and +therefore part of the Common package. Doctrine 2 docblock +annotations support namespaces and nested annotations among other +things. The Doctrine 2 ORM defines its own set of docblock +annotations for supplying object-relational mapping metadata. + +.. note:: + + If you're not comfortable with the concept of docblock + annotations, don't worry, as mentioned earlier Doctrine 2 provides + XML and YAML alternatives and you could easily implement your own + favourite mechanism for defining ORM metadata. + In this chapter a reference of every Doctrine 2 Annotation is given with short explanations on their context and usage. diff --git a/docs/en/reference/basic-mapping.rst b/docs/en/reference/basic-mapping.rst index cfd06cd24..d21a4d007 100644 --- a/docs/en/reference/basic-mapping.rst +++ b/docs/en/reference/basic-mapping.rst @@ -1,10 +1,24 @@ Basic Mapping ============= -This chapter explains the basic mapping of objects and properties. +This guide explains the basic mapping of entities and properties. +After working through this guide you should know: + +- How to create PHP classes that can be saved in the database with Doctrine +- How to configure the mapping between columns on tables and properties on + entities. +- Defining primary keys and how identifiers are generated by Doctrine +- What Doctrine types are + Mapping of associations will be covered in the next chapter on :doc:`Association Mapping `. +Guide Assumptions +----------------- + +You should have already :doc:`installed and configure ` +Doctrine. + Mapping Drivers --------------- @@ -33,36 +47,6 @@ that explain them in more detail. There is also a reference chapter for cache (not recommended!) then the XML driver is the fastest by using PHP's native XML support. -Introduction to Docblock Annotations ------------------------------------- - -You've probably used docblock annotations in some form already, -most likely to provide documentation metadata for a tool like -``PHPDocumentor`` (@author, @link, ...). Docblock annotations are a -tool to embed metadata inside the documentation section which can -then be processed by some tool. Doctrine 2 generalizes the concept -of docblock annotations so that they can be used for any kind of -metadata and so that it is easy to define new docblock annotations. -In order to allow more involved annotation values and to reduce the -chances of clashes with other docblock annotations, the Doctrine 2 -docblock annotations feature an alternative syntax that is heavily -inspired by the Annotation syntax introduced in Java 5. - -The implementation of these enhanced docblock annotations is -located in the ``Doctrine\Common\Annotations`` namespace and -therefore part of the Common package. Doctrine 2 docblock -annotations support namespaces and nested annotations among other -things. The Doctrine 2 ORM defines its own set of docblock -annotations for supplying object-relational mapping metadata. - -.. note:: - - If you're not comfortable with the concept of docblock - annotations, don't worry, as mentioned earlier Doctrine 2 provides - XML and YAML alternatives and you could easily implement your own - favourite mechanism for defining ORM metadata. - - Persistent classes ------------------ From caf6ba65e8223a9e72514bc609bcbac0f2623185 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Tue, 10 Sep 2013 21:39:40 +0200 Subject: [PATCH 280/332] Refactor Basic Mapping chapter to focus on the actual topic more. Rearrange apperance of topics. Introduce a sane example entity as example. --- docs/en/reference/basic-mapping.rst | 256 ++++++++++++++-------------- 1 file changed, 125 insertions(+), 131 deletions(-) diff --git a/docs/en/reference/basic-mapping.rst b/docs/en/reference/basic-mapping.rst index d21a4d007..22ecc4d72 100644 --- a/docs/en/reference/basic-mapping.rst +++ b/docs/en/reference/basic-mapping.rst @@ -19,39 +19,52 @@ Guide Assumptions You should have already :doc:`installed and configure ` Doctrine. -Mapping Drivers ---------------- +Creating Classes for the Database +--------------------------------- -Doctrine provides several different ways for specifying -object-relational mapping metadata: +Every PHP Class that you want to save in the database using Doctrine +need to be marked as "Entity". The term "Entity" describes objects +that have identity over many independent requests. This identity is +usually achieved by assigning a unique identifier to an entity. +In this tutorial the following ``Message`` PHP class will serve as +example Entity: -- Docblock Annotations -- XML -- YAML +.. code-block:: php + + ` +- :doc:`XML ` +- :doc:`YAML ` - PHP code This manual usually mentions docblock annotations in all the examples that are spread throughout all chapters, however for many examples alternative YAML and -XML examples are given as well. There are dedicated reference chapters for -:doc:`XML ` and :doc:`YAML ` mapping, respectively -that explain them in more detail. There is also a reference chapter for -:doc:`Annotations `. +XML examples are given as well. .. note:: - All metadata drivers give exactly the same performance. Once the metadata - of a class has been read from the source (annotations, xml or yaml) it is - stored in an instance of the ``Doctrine\ORM\Mapping\ClassMetadata`` class - and these instances are stored in the metadata cache. Therefore at the end - of the day all drivers perform equally well. If you're not using a metadata - cache (not recommended!) then the XML driver is the fastest by using PHP's - native XML support. + All metadata drivers perform equally. Once the metadata of a class has been + read from the source (annotations, xml or yaml) it is stored in an instance + of the ``Doctrine\ORM\Mapping\ClassMetadata`` class and these instances are + stored in the metadata cache. If you're not using a metadata cache (not + recommended!) then the XML driver is the fastest by using PHP's native XML + support. -Persistent classes ------------------- - -Every PHP Class that you want to save in the database using Doctrine -need to be configured as "Entity". +Marking our ``Message`` entity for Doctrine is straightforward: .. configuration-block:: @@ -59,7 +72,7 @@ need to be configured as "Entity". - + .. code-block:: yaml - MyPersistentClass: + Message: type: entity # ... With no additional information given Doctrine expects the entity to be saved -into a table with the same name as the class. You can change this assumption -by adding more information about the used table: +into a table with the same name as the class in our case ``Message``. +You can change this by configuring information about the table: .. configuration-block:: @@ -89,9 +102,9 @@ by adding more information about the used table: - + .. code-block:: yaml - MyPersistentClass: + Message: type: entity - table: my_persistent_class + table: message # ... -In this example the class ``MyPersistentClass`` will be saved and fetched from -the table ``my_persistent_class``. +Now the class ``Message`` will be saved and fetched from the table ``message``. + +Property Mapping +---------------- + +The next step after defining a PHP class as entity is mapping of properties +to columns in a table. + +To configure a property use the ``@Column`` docblock annotation. The ``type`` +attribute specifies the Doctrine Mapping Type to use for the field. If the type +is not specified, ``string`` is used as the default mapping type. + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + + .. code-block:: yaml + + Message: + type: entity + fields: + id: + type: integer + text: + length: 140 + postedAt: + type: datetime + name: posted_at + +Because we don't explicitly specify a column name, Doctrine assumes the field +name is also the column name. In the example we configured the property ``id`` to map to the column ``id`` +using the mapping type ``integer``. The field ``text`` is mapped to the column +``text`` with the default mapping type ``string`` and ``postedAt`` is of the +datetime type, but mapped to a column called ``posted_at``. + +The Column annotation has some more attributes. Here is a complete +list: + +- ``type``: (optional, defaults to 'string') The mapping type to + use for the column. +- ``name``: (optional, defaults to field name) The name of the + column in the database. +- ``length``: (optional, default 255) The length of the column in + the database. (Applies only if a string-valued column is used). +- ``unique``: (optional, default FALSE) Whether the column is a + unique key. +- ``nullable``: (optional, default FALSE) Whether the database + column is nullable. +- ``precision``: (optional, default 0) The precision for a decimal + (exact numeric) column. (Applies only if a decimal column is used.) +- ``scale``: (optional, default 0) The scale for a decimal (exact + numeric) column. (Applies only if a decimal column is used.) +- ``columnDefinition``: (optional) Allows to define a custom + DDL snippet that is used to create the column. Warning: This normally + confuses the SchemaTool to always detect the column as changed. +- ``options``: (optional) Key-value pairs of options that get passed + to the underlying database platform when generating DDL statements. Doctrine Mapping Types ---------------------- @@ -176,102 +266,6 @@ built-in mapping types: on working with datetimes that gives hints for implementing multi timezone applications. -Property Mapping ----------------- - -Properties of an entity class can be mapped to columns of the -SQL table of that entity. - -To configure a property use the ``@Column`` docblock annotation. This -annotation usually requires at least 1 attribute to be set, the ``type``. The -``type`` attribute specifies the Doctrine Mapping Type to use for the field. If -the type is not specified, ``string`` is used as the default mapping type. - - -Example: - -.. configuration-block:: - - .. code-block:: php - - - - - - - - - .. code-block:: yaml - - MyPersistentClass: - type: entity - fields: - id: - type: integer - name: - length: 50 - -In that example we configured the property ``id`` to map to the column ``id`` -using the mapping type ``integer``. The field ``name`` is mapped to the column -``name`` with the default mapping type ``string``. Column names are assumed to -be the same as the field names unless you specify a different name for the -column using the ``name`` attribute of the Column annotation: - -.. configuration-block:: - - .. code-block:: php - - - - - - - - .. code-block:: yaml - - MyPersistentClass: - type: entity - fields: - name: - length: 50 - column: db_name - -The Column annotation has some more attributes. Here is a complete -list: - -- ``type``: (optional, defaults to 'string') The mapping type to - use for the column. -- ``name``: (optional, defaults to field name) The name of the - column in the database. -- ``length``: (optional, default 255) The length of the column in - the database. (Applies only if a string-valued column is used). -- ``unique``: (optional, default FALSE) Whether the column is a - unique key. -- ``nullable``: (optional, default FALSE) Whether the database - column is nullable. -- ``precision``: (optional, default 0) The precision for a decimal - (exact numeric) column. (Applies only if a decimal column is used.) -- ``scale``: (optional, default 0) The scale for a decimal (exact - numeric) column. (Applies only if a decimal column is used.) Identifiers / Primary Keys -------------------------- From 546c817f646bc767fdcf397da244721ceecb67df Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Tue, 10 Sep 2013 22:08:54 +0200 Subject: [PATCH 281/332] Move Custom Mapping Types into cookbook, restructure the chapter more. --- docs/en/cookbook/custom-mapping-types.rst | 97 ++++++++++ docs/en/reference/basic-mapping.rst | 210 ++++------------------ 2 files changed, 131 insertions(+), 176 deletions(-) create mode 100644 docs/en/cookbook/custom-mapping-types.rst diff --git a/docs/en/cookbook/custom-mapping-types.rst b/docs/en/cookbook/custom-mapping-types.rst new file mode 100644 index 000000000..282a846b0 --- /dev/null +++ b/docs/en/cookbook/custom-mapping-types.rst @@ -0,0 +1,97 @@ +Custom Mapping Types +==================== + +Doctrine allows you to create new mapping types. This can come in +handy when you're missing a specific mapping type or when you want +to replace the existing implementation of a mapping type. + +In order to create a new mapping type you need to subclass +``Doctrine\DBAL\Types\Type`` and implement/override the methods as +you wish. Here is an example skeleton of such a custom type class: + +.. code-block:: php + + getConnection(); + $conn->getDatabasePlatform()->registerDoctrineTypeMapping('db_mytype', 'mytype'); + +When registering the custom types in the configuration you specify a unique +name for the mapping type and map that to the corresponding fully qualified +class name. Now the new type can be used when mapping columns: + +.. code-block:: php + + `. @@ -41,7 +42,10 @@ example Entity: Because Doctrine is a generic library, it can only know about your entities when you are describing their existance and structure using -Metadata Configuration. +Metadata Mapping, a pattern `described by Martin Fowler in PoEAA +`_. Because +of this pattern the documentation will often speak of "mapping something", +which means that we use the metadata mapping pattern to use this feature. Doctrine provides several different ways for specifying object-relational mapping metadata: @@ -207,10 +211,11 @@ list: Doctrine Mapping Types ---------------------- -A Doctrine Mapping Type defines the conversion the type of a PHP variable and -an SQL type. All Mapping Types that ship with Doctrine are fully portable -between the supported database systems. You can add your own custom mapping -types to add more conversions. +The ``type`` option used in the ``@Column`` accepts any of the existing +Doctrine types or even your own custom types. A Doctrine type defines +the conversion between PHP and SQL types, indepedant from the database vendor +you are using. All Mapping Types that ship with Doctrine are fully portable +between the supported database systems. As an example the Doctrine Mapping Type ``string`` defines the mapping from a PHP string to a SQL VARCHAR (or VARCHAR2 etc. @@ -222,7 +227,7 @@ built-in mapping types: - ``smallint``: Type that maps a database SMALLINT to a PHP integer. - ``bigint``: Type that maps a database BIGINT to a PHP string. -- ``boolean``: Type that maps a SQL boolean to a PHP boolean. +- ``boolean``: Type that maps a SQL boolean or equivalent (TINYINT) to a PHP boolean. - ``decimal``: Type that maps a SQL DECIMAL to a PHP string. - ``date``: Type that maps a SQL DATETIME to a PHP DateTime object. @@ -248,10 +253,14 @@ built-in mapping types: varchar but uses a specific type if the platform supports it. - ``blob``: Type that maps a SQL BLOB to a PHP resource stream +A cookbook article shows how to define :doc:`your own custom mapping types +<../cookbook/custom-mapping-types>`. + .. note:: - DateTime and Object types are compared by reference, not by value. Doctrine updates this values - if the reference changes and therefore behaves as if these objects are immutable value objects. + DateTime and Object types are compared by reference, not by value. Doctrine + updates this values if the reference changes and therefore behaves as if + these objects are immutable value objects. .. warning:: @@ -266,22 +275,24 @@ built-in mapping types: on working with datetimes that gives hints for implementing multi timezone applications. - Identifiers / Primary Keys -------------------------- -Every entity class needs an identifier/primary key. You designate +Every entity class requires an identifier/primary key. You can select the field that serves as the identifier with the ``@Id`` -annotation: +annotation. .. configuration-block:: .. code-block:: php - - - - - - - .. code-block:: yaml - - MyPersistentClass: - type: entity - id: - id: - type: integer - fields: - name: - length: 50 - -This definition is missing an ID generation strategy, which means that your code needs to assign -the identifier manually before passing a new entity to -``EntityManager#persist($entity)``. - -Doctrine can alternatively generate identifiers for entities using generation strategies, -using database sequences or auto incrementing numbers. - -.. configuration-block:: - - .. code-block:: php - - - + - + .. code-block:: yaml - MyPersistentClass: + Message: type: entity id: id: @@ -348,15 +318,12 @@ using database sequences or auto incrementing numbers. generator: strategy: AUTO fields: - name: - length: 50 + # fields here -This tells Doctrine to automatically generate a value for the -identifier. How this value is generated is specified by the -``strategy`` attribute, which is optional and defaults to 'AUTO'. A -value of ``AUTO`` tells Doctrine to use the generation strategy -that is preferred by the currently used database platform. See -below for details. +In most cases using the automatic generator strategy (``@GeneratedValue``) is +what you want. It defaults to the identifier generation mechanism your current +database vendor prefers: AUTO_INCREMENT with MySQL, SERIAL with PostgreSQL, +Sequences with Oracle and so on. Identifier Generation Strategies ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -524,112 +491,3 @@ You can use it with the following code: use Doctrine\ORM\Mapping\AnsiQuoteStrategy; $configuration->setQuoteStrategy(new AnsiQuoteStrategy()); - -Custom Mapping Types --------------------- - -Doctrine allows you to create new mapping types. This can come in -handy when you're missing a specific mapping type or when you want -to replace the existing implementation of a mapping type. - -In order to create a new mapping type you need to subclass -``Doctrine\DBAL\Types\Type`` and implement/override the methods as -you wish. Here is an example skeleton of such a custom type class: - -.. code-block:: php - - getConnection(); - $conn->getDatabasePlatform()->registerDoctrineTypeMapping('db_mytype', 'mytype'); - -When registering the custom types in the configuration you specify a unique -name for the mapping type and map that to the corresponding fully qualified -class name. Now the new type can be used when mapping columns: - -.. code-block:: php - - Date: Tue, 10 Sep 2013 22:36:03 +0200 Subject: [PATCH 282/332] Small fixes in basic-mapping.rst --- docs/en/reference/basic-mapping.rst | 40 ++++++++++++++--------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/docs/en/reference/basic-mapping.rst b/docs/en/reference/basic-mapping.rst index a95766afc..b8ded9338 100644 --- a/docs/en/reference/basic-mapping.rst +++ b/docs/en/reference/basic-mapping.rst @@ -52,8 +52,8 @@ mapping metadata: - :doc:`Docblock Annotations ` - :doc:`XML ` -- :doc:`YAML ` -- PHP code +- :doc:`YAML ` +- :doc:`PHP code ` This manual usually mentions docblock annotations in all the examples that are spread throughout all chapters, however for many examples alternative YAML and @@ -188,25 +188,25 @@ datetime type, but mapped to a column called ``posted_at``. The Column annotation has some more attributes. Here is a complete list: -- ``type``: (optional, defaults to 'string') The mapping type to - use for the column. -- ``name``: (optional, defaults to field name) The name of the - column in the database. -- ``length``: (optional, default 255) The length of the column in - the database. (Applies only if a string-valued column is used). -- ``unique``: (optional, default FALSE) Whether the column is a - unique key. -- ``nullable``: (optional, default FALSE) Whether the database - column is nullable. -- ``precision``: (optional, default 0) The precision for a decimal - (exact numeric) column. (Applies only if a decimal column is used.) -- ``scale``: (optional, default 0) The scale for a decimal (exact - numeric) column. (Applies only if a decimal column is used.) -- ``columnDefinition``: (optional) Allows to define a custom - DDL snippet that is used to create the column. Warning: This normally - confuses the SchemaTool to always detect the column as changed. +- ``type``: (optional, defaults to 'string') The mapping type to + use for the column. +- ``name``: (optional, defaults to field name) The name of the + column in the database. +- ``length``: (optional, default 255) The length of the column in + the database. (Applies only if a string-valued column is used). +- ``unique``: (optional, default FALSE) Whether the column is a + unique key. +- ``nullable``: (optional, default FALSE) Whether the database + column is nullable. +- ``precision``: (optional, default 0) The precision for a decimal + (exact numeric) column. (Applies only if a decimal column is used.) +- ``scale``: (optional, default 0) The scale for a decimal (exact + numeric) column. (Applies only if a decimal column is used.) +- ``columnDefinition``: (optional) Allows to define a custom + DDL snippet that is used to create the column. Warning: This normally + confuses the SchemaTool to always detect the column as changed. - ``options``: (optional) Key-value pairs of options that get passed - to the underlying database platform when generating DDL statements. + to the underlying database platform when generating DDL statements. Doctrine Mapping Types ---------------------- From 9983fcbac39960117773376fe94c85a328351479 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Tue, 10 Sep 2013 23:15:43 +0200 Subject: [PATCH 283/332] Fix some more details in basic-mapping.rst --- docs/en/reference/basic-mapping.rst | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/en/reference/basic-mapping.rst b/docs/en/reference/basic-mapping.rst index b8ded9338..c2b3aaa61 100644 --- a/docs/en/reference/basic-mapping.rst +++ b/docs/en/reference/basic-mapping.rst @@ -368,30 +368,31 @@ besides specifying the sequence's name: .. code-block:: php - + - + .. code-block:: yaml - MyPersistentClass: + Message: type: entity id: id: @@ -399,7 +400,7 @@ besides specifying the sequence's name: generator: strategy: SEQUENCE sequenceGenerator: - sequenceName: tablename_seq + sequenceName: message_seq allocationSize: 100 initialValue: 1 @@ -439,15 +440,14 @@ need to access the sequence once to generate the identifiers for Composite Keys ~~~~~~~~~~~~~~ -Doctrine 2 allows to use composite primary keys. There are however -some restrictions opposed to using a single identifier. The use of -the ``@GeneratedValue`` annotation is only supported for simple -(not composite) primary keys, which means you can only use -composite keys if you generate the primary key values yourself -before calling ``EntityManager#persist()`` on the entity. +with Doctrine 2 you can use composite primary keys, using ``@Id`` on more then +one column. Some restrictions exist opposed to using a single identifier in +this case: The use of the ``@GeneratedValue`` annotation is not supported, +which means you can only use composite keys if you generate the primary key +values yourself before calling ``EntityManager#persist()`` on the entity. -To designate a composite primary key / identifier, simply put the -@Id marker annotation on all fields that make up the primary key. +More details on composite primary keys are discussed in a :doc:`dedicated tutorial +<../tutorials/composite-primary-keys>`. Quoting Reserved Words ---------------------- From bc37ceb58b7a9b9404202e12feb0c1f72d690582 Mon Sep 17 00:00:00 2001 From: Michal Piotrowski Date: Tue, 10 Sep 2013 23:33:06 +0200 Subject: [PATCH 284/332] fix warnings p1 fix warnings p2 fix warnings p3 fix warnings p4 --- docs/en/index.rst | 3 ++- docs/en/reference/advanced-configuration.rst | 10 +++++----- docs/en/reference/caching.rst | 2 +- docs/en/reference/configuration.rst | 2 +- docs/en/reference/faq.rst | 2 +- docs/en/toc.rst | 12 ++++++++---- 6 files changed, 18 insertions(+), 13 deletions(-) diff --git a/docs/en/index.rst b/docs/en/index.rst index e71ea7a43..72cac5727 100644 --- a/docs/en/index.rst +++ b/docs/en/index.rst @@ -14,7 +14,7 @@ Doctrine ORM don't panic. You can get help from different sources: - There is a :doc:`FAQ ` with answers to frequent questions. - The `Doctrine Mailing List `_ -- Internet Relay Chat (IRC) in `#doctrine on Freenode `_ +- Internet Relay Chat (IRC) in #doctrine on Freenode - Report a bug on `JIRA `_. - On `Twitter `_ with ``#doctrine2`` - On `StackOverflow `_ @@ -120,3 +120,4 @@ Cookbook :doc:`MySQL Enums ` :doc:`Advanced Field Value Conversion ` +.. include:: toc.rst diff --git a/docs/en/reference/advanced-configuration.rst b/docs/en/reference/advanced-configuration.rst index be127ce0a..e9213a239 100644 --- a/docs/en/reference/advanced-configuration.rst +++ b/docs/en/reference/advanced-configuration.rst @@ -373,15 +373,15 @@ using the Doctrine Console: The other options are interesting in development environment: - ``AUTOGENERATE_ALWAYS`` will require you to create and configure -a proxy directory. Proxies will be generated and written to file -on each request, so any modification to your code will be acknowledged. + a proxy directory. Proxies will be generated and written to file + on each request, so any modification to your code will be acknowledged. - ``AUTOGENERATE_FILE_NOT_EXISTS`` will not overwrite an existing -proxy file. If your code changes, you will need to regenerate the -proxies manually. + proxy file. If your code changes, you will need to regenerate the + proxies manually. - ``AUTOGENERATE_EVAL`` will regenerate each proxy on each request, -but without writing them to disk. + but without writing them to disk. Autoloading Proxies ------------------- diff --git a/docs/en/reference/caching.rst b/docs/en/reference/caching.rst index 1e87efaff..d45090cbc 100644 --- a/docs/en/reference/caching.rst +++ b/docs/en/reference/caching.rst @@ -106,7 +106,7 @@ Redis In order to use the Redis cache driver you must have it compiled and enabled in your php.ini. You can read about what is Redis `from here `_. Also check -`here `_ for how you can use +`A PHP extension for Redis `_ for how you can use and install Redis PHP extension. Below is a simple example of how you could use the Redis cache diff --git a/docs/en/reference/configuration.rst b/docs/en/reference/configuration.rst index 91bd9caf0..5837f0f95 100644 --- a/docs/en/reference/configuration.rst +++ b/docs/en/reference/configuration.rst @@ -101,7 +101,7 @@ Doctrine ships with a number of command line tools that are very helpful during development. You can call this command from the Composer binary directory: -.. code-block:: +.. code-block:: sh $ php vendor/bin/doctrine diff --git a/docs/en/reference/faq.rst b/docs/en/reference/faq.rst index 28e3a42c4..45fde18d7 100644 --- a/docs/en/reference/faq.rst +++ b/docs/en/reference/faq.rst @@ -143,7 +143,7 @@ See the documentation chapter on :doc:`inheritance mapping the details. Why does Doctrine not create proxy objects for my inheritance hierarchy? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If you set a many-to-one or one-to-one association target-entity to any parent class of an inheritance hierarchy Doctrine does not know what PHP class the foreign is actually of. diff --git a/docs/en/toc.rst b/docs/en/toc.rst index 73f1f25b9..1a331fa23 100644 --- a/docs/en/toc.rst +++ b/docs/en/toc.rst @@ -8,12 +8,14 @@ Tutorials :maxdepth: 1 tutorials/getting-started + tutorials/getting-started-database + tutorials/getting-started-models tutorials/working-with-indexed-associations tutorials/extra-lazy-associations tutorials/composite-primary-keys tutorials/ordered-associations - tutorials/in-ten-quick-steps tutorials/override-field-association-mappings-in-subclasses + tutorials/pagination.rst Reference Guide --------------- @@ -22,9 +24,9 @@ Reference Guide :maxdepth: 1 :numbered: - reference/introduction reference/architecture - reference/configuration + reference/installation + reference/configuration.rst reference/faq reference/basic-mapping reference/association-mapping @@ -51,9 +53,9 @@ Reference Guide reference/metadata-drivers reference/best-practices reference/limitations-and-known-issues - tutorials/pagination.rst reference/filters.rst reference/namingstrategy.rst + reference/advanced-configuration.rst Cookbook @@ -63,6 +65,7 @@ Cookbook :maxdepth: 1 cookbook/aggregate-fields + cookbook/custom-mapping-types cookbook/decorator-pattern cookbook/dql-custom-walkers cookbook/dql-user-defined-functions @@ -70,6 +73,7 @@ Cookbook cookbook/implementing-the-notify-changetracking-policy cookbook/implementing-wakeup-or-clone cookbook/integrating-with-codeigniter + cookbook/resolve-target-entity-listener cookbook/sql-table-prefixes cookbook/strategy-cookbook-introduction cookbook/validation-of-entities From ef722066e3b69132214bce26e8a77f10b9c93de9 Mon Sep 17 00:00:00 2001 From: Daniel VanMullen Date: Wed, 11 Sep 2013 09:25:52 -0400 Subject: [PATCH 285/332] Update dql-custom-walkers.rst Fixed typo of "customer" to "custom" --- docs/en/cookbook/dql-custom-walkers.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/cookbook/dql-custom-walkers.rst b/docs/en/cookbook/dql-custom-walkers.rst index 2887a5d68..bcead51f4 100644 --- a/docs/en/cookbook/dql-custom-walkers.rst +++ b/docs/en/cookbook/dql-custom-walkers.rst @@ -112,7 +112,7 @@ The ``Paginate::count(Query $query)`` looks like: } It clones the query, resets the limit clause first and max results -and registers the ``CountSqlWalker`` customer tree walker which +and registers the ``CountSqlWalker`` custom tree walker which will modify the AST to execute a count query. The walkers implementation is: From b6e624b6bf08b74e43fb7db79abf61762c712152 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 11 Sep 2013 22:43:25 +0200 Subject: [PATCH 286/332] Delete some very old, leftover code. --- .../ORM/Persisters/UnionSubclassPersister.php | 24 ------------------- lib/Doctrine/ORM/UnitOfWork.php | 2 +- 2 files changed, 1 insertion(+), 25 deletions(-) delete mode 100644 lib/Doctrine/ORM/Persisters/UnionSubclassPersister.php diff --git a/lib/Doctrine/ORM/Persisters/UnionSubclassPersister.php b/lib/Doctrine/ORM/Persisters/UnionSubclassPersister.php deleted file mode 100644 index d27dcc9fd..000000000 --- a/lib/Doctrine/ORM/Persisters/UnionSubclassPersister.php +++ /dev/null @@ -1,24 +0,0 @@ -. - */ - -namespace Doctrine\ORM\Persisters; - -class UnionSubclassPersister extends BasicEntityPersister -{ -} diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index 7e983d801..5de769f63 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -2964,7 +2964,7 @@ class UnitOfWork implements PropertyChangedListener break; default: - $persister = new Persisters\UnionSubclassPersister($this->em, $class); + throw new \RuntimeException('No persister found for entity.'); } $this->persisters[$entityName] = $persister; From 067ed7b1c6b5752bf31753c8e8960228c5856070 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Thu, 12 Sep 2013 08:00:08 +0200 Subject: [PATCH 287/332] Fix master dependening on DBAL 2.5-dev --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 66616dbf4..4b0d449f0 100644 --- a/composer.json +++ b/composer.json @@ -16,7 +16,7 @@ "php": ">=5.3.2", "ext-pdo": "*", "doctrine/collections": "~1.1", - "doctrine/dbal": ">=2.4-beta,<2.5-dev", + "doctrine/dbal": ">=2.5-dev,<2.6-dev", "symfony/console": "2.*" }, "require-dev": { From 36d7d60c3b30055dfbab001896c6cb9b1565e915 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Thu, 12 Sep 2013 17:08:48 -0500 Subject: [PATCH 288/332] Minor updates while reading the basic-mapping page The only arguable changes involve a few spots where I either removed something I thought was too technical and not valuable (e.g. talking about the metadata mapping pattern). --- docs/en/reference/basic-mapping.rst | 72 +++++++++++++++-------------- 1 file changed, 37 insertions(+), 35 deletions(-) diff --git a/docs/en/reference/basic-mapping.rst b/docs/en/reference/basic-mapping.rst index c2b3aaa61..622af453b 100644 --- a/docs/en/reference/basic-mapping.rst +++ b/docs/en/reference/basic-mapping.rst @@ -4,12 +4,12 @@ Basic Mapping This guide explains the basic mapping of entities and properties. After working through this guide you should know: -- How to create PHP classes that can be saved in the database with Doctrine +- How to create PHP classes that can be saved to the database with Doctrine; - How to configure the mapping between columns on tables and properties on - entities. -- What Doctrine Mapping types are -- Defining primary keys and how identifiers are generated by Doctrine -- How quoting of reserved symbols works in Doctrine + entities; +- What Doctrine mapping types are; +- Defining primary keys and how identifiers are generated by Doctrine; +- How quoting of reserved symbols works in Doctrine. Mapping of associations will be covered in the next chapter on :doc:`Association Mapping `. @@ -23,11 +23,11 @@ Doctrine. Creating Classes for the Database --------------------------------- -Every PHP Class that you want to save in the database using Doctrine -need to be marked as "Entity". The term "Entity" describes objects -that have identity over many independent requests. This identity is +Every PHP class that you want to save in the database using Doctrine +is called an "Entity". The term "Entity" describes objects +that have an identity over many independent requests. This identity is usually achieved by assigning a unique identifier to an entity. -In this tutorial the following ``Message`` PHP class will serve as +In this tutorial the following ``Message`` PHP class will serve as the example Entity: .. code-block:: php @@ -40,14 +40,14 @@ example Entity: private $postedAt; } -Because Doctrine is a generic library, it can only know about your -entities when you are describing their existance and structure using -Metadata Mapping, a pattern `described by Martin Fowler in PoEAA -`_. Because -of this pattern the documentation will often speak of "mapping something", -which means that we use the metadata mapping pattern to use this feature. +Because Doctrine is a generic library, it only knows about your +entities because you will describe their existence and structure using +mapping metadata, which is configuration that tells Doctrine how your +entity should be stored in the database. The documentation will often +speak of "mapping something", which means writing the mapping metadata +that describes your entity. -Doctrine provides several different ways for specifying object-relational +Doctrine provides several different ways to specifying object-relational mapping metadata: - :doc:`Docblock Annotations ` @@ -55,9 +55,8 @@ mapping metadata: - :doc:`YAML ` - :doc:`PHP code ` -This manual usually mentions docblock annotations in all the examples that are -spread throughout all chapters, however for many examples alternative YAML and -XML examples are given as well. +This manual will usually show mapping metadata via docblock annotations, though +many examples also show the equivalent configuration in YAML and XML. .. note:: @@ -65,10 +64,9 @@ XML examples are given as well. read from the source (annotations, xml or yaml) it is stored in an instance of the ``Doctrine\ORM\Mapping\ClassMetadata`` class and these instances are stored in the metadata cache. If you're not using a metadata cache (not - recommended!) then the XML driver is the fastest by using PHP's native XML - support. + recommended!) then the XML driver is the fastest. -Marking our ``Message`` entity for Doctrine is straightforward: +Marking our ``Message`` class as an entity for Doctrine is straightforward: .. configuration-block:: @@ -95,7 +93,7 @@ Marking our ``Message`` entity for Doctrine is straightforward: type: entity # ... -With no additional information given Doctrine expects the entity to be saved +With no additional information, Doctrine expects the entity to be saved into a table with the same name as the class in our case ``Message``. You can change this by configuring information about the table: @@ -133,12 +131,13 @@ Now the class ``Message`` will be saved and fetched from the table ``message``. Property Mapping ---------------- -The next step after defining a PHP class as entity is mapping of properties +The next step after marking a PHP class as an entity is mapping its properties to columns in a table. To configure a property use the ``@Column`` docblock annotation. The ``type`` -attribute specifies the Doctrine Mapping Type to use for the field. If the type -is not specified, ``string`` is used as the default mapping type. +attribute specifies the :ref:`Doctrine Mapping Type ` +to use for the field. If the type is not specified, ``string`` is used as the +default. .. configuration-block:: @@ -179,11 +178,12 @@ is not specified, ``string`` is used as the default mapping type. type: datetime name: posted_at -Because we don't explicitly specify a column name, Doctrine assumes the field -name is also the column name. In the example we configured the property ``id`` to map to the column ``id`` -using the mapping type ``integer``. The field ``text`` is mapped to the column -``text`` with the default mapping type ``string`` and ``postedAt`` is of the -datetime type, but mapped to a column called ``posted_at``. +When we don't explicitly specify a column name via the ``name`` option, Doctrine +assumes the field name is also the column name. This means that: + +* the ``id`` property will map to the column ``id`` using the type ``integer``; +* the ``text`` property will map to the column ``text`` with the default mapping type ``string``; +* the ``postedAt`` property will map to the ``posted_at`` column with the ``datetime`` type. The Column annotation has some more attributes. Here is a complete list: @@ -208,16 +208,18 @@ list: - ``options``: (optional) Key-value pairs of options that get passed to the underlying database platform when generating DDL statements. +.. _reference-mapping-types: + Doctrine Mapping Types ---------------------- The ``type`` option used in the ``@Column`` accepts any of the existing Doctrine types or even your own custom types. A Doctrine type defines -the conversion between PHP and SQL types, indepedant from the database vendor +the conversion between PHP and SQL types, independent from the database vendor you are using. All Mapping Types that ship with Doctrine are fully portable between the supported database systems. -As an example the Doctrine Mapping Type ``string`` defines the +As an example, the Doctrine Mapping Type ``string`` defines the mapping from a PHP string to a SQL VARCHAR (or VARCHAR2 etc. depending on the RDBMS brand). Here is a quick overview of the built-in mapping types: @@ -278,7 +280,7 @@ A cookbook article shows how to define :doc:`your own custom mapping types Identifiers / Primary Keys -------------------------- -Every entity class requires an identifier/primary key. You can select +Every entity class must have an identifier/primary key. You can select the field that serves as the identifier with the ``@Id`` annotation. @@ -331,7 +333,7 @@ Identifier Generation Strategies The previous example showed how to use the default identifier generation strategy without knowing the underlying database with the AUTO-detection strategy. It is also possible to specify the -identifier generation strategy more explicitly, which allows to +identifier generation strategy more explicitly, which allows you to make use of some additional features. Here is the list of possible generation strategies: From 1fcef3321e4bbe266a01481a10fc334a8d5d98c6 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Thu, 12 Sep 2013 20:52:12 -0500 Subject: [PATCH 289/332] Changes thanks to @Ocramius --- docs/en/reference/basic-mapping.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/en/reference/basic-mapping.rst b/docs/en/reference/basic-mapping.rst index 622af453b..699df1d09 100644 --- a/docs/en/reference/basic-mapping.rst +++ b/docs/en/reference/basic-mapping.rst @@ -4,7 +4,7 @@ Basic Mapping This guide explains the basic mapping of entities and properties. After working through this guide you should know: -- How to create PHP classes that can be saved to the database with Doctrine; +- How to create PHP objects that can be saved to the database with Doctrine; - How to configure the mapping between columns on tables and properties on entities; - What Doctrine mapping types are; @@ -23,7 +23,7 @@ Doctrine. Creating Classes for the Database --------------------------------- -Every PHP class that you want to save in the database using Doctrine +Every PHP object that you want to save in the database using Doctrine is called an "Entity". The term "Entity" describes objects that have an identity over many independent requests. This identity is usually achieved by assigning a unique identifier to an entity. @@ -47,7 +47,7 @@ entity should be stored in the database. The documentation will often speak of "mapping something", which means writing the mapping metadata that describes your entity. -Doctrine provides several different ways to specifying object-relational +Doctrine provides several different ways to specify object-relational mapping metadata: - :doc:`Docblock Annotations ` From a190dad0b136bf2efc7ba3f860014731e6416845 Mon Sep 17 00:00:00 2001 From: flip111 Date: Fri, 13 Sep 2013 15:11:35 +0200 Subject: [PATCH 290/332] Update DDC719Test.php to be compatible with MsSQL Apparently Doctrine adds "with (nolock)" now for the mssql platform, but the test has not been updated yet. --- .../Tests/ORM/Functional/Ticket/DDC719Test.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC719Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC719Test.php index 6bd18ef98..a728fa089 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC719Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC719Test.php @@ -19,8 +19,14 @@ class DDC719Test extends \Doctrine\Tests\OrmFunctionalTestCase { $q = $this->_em->createQuery('SELECT g, c FROM Doctrine\Tests\ORM\Functional\Ticket\DDC719Group g LEFT JOIN g.children c WHERE g.parents IS EMPTY'); + if ($this->_em->getConnection()->getDatabasePlatform()->getName() === 'mssql') { + $referenceSQL = 'SELECT g0_.name AS name0, g0_.description AS description1, g0_.id AS id2, g1_.name AS name3, g1_.description AS description4, g1_.id AS id5 FROM groups g0_ with (nolock) LEFT JOIN groups_groups g2_ ON g0_.id = g2_.parent_id LEFT JOIN groups g1_ ON g1_.id = g2_.child_id WHERE (SELECT COUNT(*) FROM groups_groups g3_ WHERE g3_.child_id = g0_.id) = 0'; + } else { + $referenceSQL = 'SELECT g0_.name AS name0, g0_.description AS description1, g0_.id AS id2, g1_.name AS name3, g1_.description AS description4, g1_.id AS id5 FROM groups g0_ LEFT JOIN groups_groups g2_ ON g0_.id = g2_.parent_id LEFT JOIN groups g1_ ON g1_.id = g2_.child_id WHERE (SELECT COUNT(*) FROM groups_groups g3_ WHERE g3_.child_id = g0_.id) = 0'; + } + $this->assertEquals( - strtolower('SELECT g0_.name AS name0, g0_.description AS description1, g0_.id AS id2, g1_.name AS name3, g1_.description AS description4, g1_.id AS id5 FROM groups g0_ LEFT JOIN groups_groups g2_ ON g0_.id = g2_.parent_id LEFT JOIN groups g1_ ON g1_.id = g2_.child_id WHERE (SELECT COUNT(*) FROM groups_groups g3_ WHERE g3_.child_id = g0_.id) = 0'), + strtolower($referenceSQL), strtolower($q->getSQL()) ); } @@ -109,4 +115,4 @@ class DDC719Group extends Entity { public function getChildren() { return $this->children; } public function getParents() { return $this->parents; } public function getChannels() { return $this->channels; } -} \ No newline at end of file +} From e49a673a01d3756612fef14eef13450d6dbb36c1 Mon Sep 17 00:00:00 2001 From: flip111 Date: Fri, 13 Sep 2013 16:29:31 +0200 Subject: [PATCH 291/332] Update DatabaseDriverTest.php SQL Server does not support unsigned integers --- tests/Doctrine/Tests/ORM/Functional/DatabaseDriverTest.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/Doctrine/Tests/ORM/Functional/DatabaseDriverTest.php b/tests/Doctrine/Tests/ORM/Functional/DatabaseDriverTest.php index fce29d93c..1b94ed1d1 100644 --- a/tests/Doctrine/Tests/ORM/Functional/DatabaseDriverTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/DatabaseDriverTest.php @@ -184,8 +184,9 @@ class DatabaseDriverTest extends DatabaseDriverTestCase $this->assertEquals('integer', (string) $metadata->fieldMappings['id']['type']); // FIXME: Condition here is fugly. - // NOTE: PostgreSQL does not support UNSIGNED - if ( ! $this->_em->getConnection()->getDatabasePlatform() instanceof PostgreSqlPlatform) { + // NOTE: PostgreSQL and SQL SERVER do not support UNSIGNED integer + if ( ! $this->_em->getConnection()->getDatabasePlatform() instanceof PostgreSqlPlatform AND + ! $this->_em->getConnection()->getDatabasePlatform() instanceof SQLServerPlatform) { $this->assertArrayHasKey('columnUnsigned', $metadata->fieldMappings); $this->assertTrue($metadata->fieldMappings['columnUnsigned']['unsigned']); } From 67dd32d9fb9a7e1176a3add2719dd3040df97cf8 Mon Sep 17 00:00:00 2001 From: flip111 Date: Fri, 13 Sep 2013 16:35:34 +0200 Subject: [PATCH 292/332] A nicer way of detecting the platform Inspired by https://github.com/doctrine/doctrine2/blob/master/tests/Doctrine/Tests/ORM/Functional/DatabaseDriverTest.php#L188 --- tests/Doctrine/Tests/ORM/Functional/Ticket/DDC719Test.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC719Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC719Test.php index a728fa089..9a0dc285b 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC719Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC719Test.php @@ -19,7 +19,7 @@ class DDC719Test extends \Doctrine\Tests\OrmFunctionalTestCase { $q = $this->_em->createQuery('SELECT g, c FROM Doctrine\Tests\ORM\Functional\Ticket\DDC719Group g LEFT JOIN g.children c WHERE g.parents IS EMPTY'); - if ($this->_em->getConnection()->getDatabasePlatform()->getName() === 'mssql') { + if ($this->_em->getConnection()->getDatabasePlatform() instanceof SQLServerPlatform) { $referenceSQL = 'SELECT g0_.name AS name0, g0_.description AS description1, g0_.id AS id2, g1_.name AS name3, g1_.description AS description4, g1_.id AS id5 FROM groups g0_ with (nolock) LEFT JOIN groups_groups g2_ ON g0_.id = g2_.parent_id LEFT JOIN groups g1_ ON g1_.id = g2_.child_id WHERE (SELECT COUNT(*) FROM groups_groups g3_ WHERE g3_.child_id = g0_.id) = 0'; } else { $referenceSQL = 'SELECT g0_.name AS name0, g0_.description AS description1, g0_.id AS id2, g1_.name AS name3, g1_.description AS description4, g1_.id AS id5 FROM groups g0_ LEFT JOIN groups_groups g2_ ON g0_.id = g2_.parent_id LEFT JOIN groups g1_ ON g1_.id = g2_.child_id WHERE (SELECT COUNT(*) FROM groups_groups g3_ WHERE g3_.child_id = g0_.id) = 0'; From 64a016193524322a58289b8aebf0cd9925007cd5 Mon Sep 17 00:00:00 2001 From: javer Date: Sun, 15 Sep 2013 20:57:30 +0300 Subject: [PATCH 293/332] HHVM compatibility: func_get_args All func_get_args() calls have been moved to the top of the methods because HHVM doesn't keep a copy of the original args for performance reasons. See facebook/hiphop-php#1027 for details. --- lib/Doctrine/ORM/QueryBuilder.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/Doctrine/ORM/QueryBuilder.php b/lib/Doctrine/ORM/QueryBuilder.php index 76d2e506c..294c56c63 100644 --- a/lib/Doctrine/ORM/QueryBuilder.php +++ b/lib/Doctrine/ORM/QueryBuilder.php @@ -894,8 +894,8 @@ class QueryBuilder */ public function andWhere($where) { - $where = $this->getDQLPart('where'); $args = func_get_args(); + $where = $this->getDQLPart('where'); if ($where instanceof Expr\Andx) { $where->addMultiple($args); @@ -927,8 +927,8 @@ class QueryBuilder */ public function orWhere($where) { - $where = $this->getDqlPart('where'); $args = func_get_args(); + $where = $this->getDqlPart('where'); if ($where instanceof Expr\Orx) { $where->addMultiple($args); @@ -1007,8 +1007,8 @@ class QueryBuilder */ public function andHaving($having) { - $having = $this->getDqlPart('having'); $args = func_get_args(); + $having = $this->getDqlPart('having'); if ($having instanceof Expr\Andx) { $having->addMultiple($args); @@ -1030,8 +1030,8 @@ class QueryBuilder */ public function orHaving($having) { - $having = $this->getDqlPart('having'); $args = func_get_args(); + $having = $this->getDqlPart('having'); if ($having instanceof Expr\Orx) { $having->addMultiple($args); From 6f538c509ced72dfe538f4ab2200f67121067d55 Mon Sep 17 00:00:00 2001 From: Matthieu Napoli Date: Mon, 16 Sep 2013 14:56:04 +0200 Subject: [PATCH 294/332] Implemented "contains" operator for Criteria expressions --- lib/Doctrine/ORM/Query/QueryExpressionVisitor.php | 5 +++++ .../Doctrine/Tests/ORM/Query/QueryExpressionVisitorTest.php | 2 ++ 2 files changed, 7 insertions(+) diff --git a/lib/Doctrine/ORM/Query/QueryExpressionVisitor.php b/lib/Doctrine/ORM/Query/QueryExpressionVisitor.php index 775d70019..2da6fb412 100644 --- a/lib/Doctrine/ORM/Query/QueryExpressionVisitor.php +++ b/lib/Doctrine/ORM/Query/QueryExpressionVisitor.php @@ -154,6 +154,11 @@ class QueryExpressionVisitor extends ExpressionVisitor $this->parameters[] = $parameter; return $this->expr->neq($comparison->getField(), $placeholder); + case Comparison::CONTAINS: + $parameter->setValue('%' . $parameter->getValue() . '%', $parameter->getType()); + $this->parameters[] = $parameter; + return $this->expr->like($comparison->getField(), $placeholder); + default: $operator = self::convertComparisonOperator($comparison->getOperator()); if ($operator) { diff --git a/tests/Doctrine/Tests/ORM/Query/QueryExpressionVisitorTest.php b/tests/Doctrine/Tests/ORM/Query/QueryExpressionVisitorTest.php index 5eaeb7646..5c7a5b776 100644 --- a/tests/Doctrine/Tests/ORM/Query/QueryExpressionVisitorTest.php +++ b/tests/Doctrine/Tests/ORM/Query/QueryExpressionVisitorTest.php @@ -85,6 +85,8 @@ class QueryExpressionVisitorTest extends \PHPUnit_Framework_TestCase array($cb->in('field', array('value')), $qb->in('field', ':field'), new Parameter('field', array('value'))), array($cb->notIn('field', array('value')), $qb->notIn('field', ':field'), new Parameter('field', array('value'))), + array($cb->contains('field', 'value'), $qb->like('field', ':field'), new Parameter('field', '%value%')), + // Test parameter conversion array($cb->eq('object.field', 'value'), $qb->eq('object.field', ':object_field'), new Parameter('object_field', 'value')), ); From 689da2f36b761b5992d2282160fe78fe23b58a3a Mon Sep 17 00:00:00 2001 From: "Fabio B. Silva" Date: Mon, 16 Sep 2013 21:36:19 -0400 Subject: [PATCH 295/332] [DDC-2668] Fix trim leading zero string --- .../ORM/Query/AST/Functions/TrimFunction.php | 110 +++++++++++++----- .../ORM/Query/SelectSqlGenerationTest.php | 11 ++ 2 files changed, 90 insertions(+), 31 deletions(-) diff --git a/lib/Doctrine/ORM/Query/AST/Functions/TrimFunction.php b/lib/Doctrine/ORM/Query/AST/Functions/TrimFunction.php index 69a45d2ee..0c1f5d86f 100644 --- a/lib/Doctrine/ORM/Query/AST/Functions/TrimFunction.php +++ b/lib/Doctrine/ORM/Query/AST/Functions/TrimFunction.php @@ -20,6 +20,8 @@ namespace Doctrine\ORM\Query\AST\Functions; use Doctrine\ORM\Query\Lexer; +use Doctrine\ORM\Query\Parser; +use Doctrine\ORM\Query\SqlWalker; use Doctrine\DBAL\Platforms\AbstractPlatform; /** @@ -36,71 +38,60 @@ use Doctrine\DBAL\Platforms\AbstractPlatform; class TrimFunction extends FunctionNode { /** - * @var bool + * @var boolean */ public $leading; /** - * @var bool + * @var boolean */ public $trailing; /** - * @var bool + * @var boolean */ public $both; /** - * @var bool + * @var boolean */ public $trimChar = false; + /** + * @var \Doctrine\ORM\Query\AST\Node + */ public $stringPrimary; /** - * @override + * {@inheritdoc} */ - public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + public function getSql(SqlWalker $sqlWalker) { - $pos = AbstractPlatform::TRIM_UNSPECIFIED; - if ($this->leading) { - $pos = AbstractPlatform::TRIM_LEADING; - } else if ($this->trailing) { - $pos = AbstractPlatform::TRIM_TRAILING; - } else if ($this->both) { - $pos = AbstractPlatform::TRIM_BOTH; - } + $stringPrimary = $sqlWalker->walkStringPrimary($this->stringPrimary); + $platform = $sqlWalker->getConnection()->getDatabasePlatform(); + $trimMode = $this->getTrimMode(); + $trimChar = ($this->trimChar !== false) + ? $sqlWalker->getConnection()->quote($this->trimChar) + : false; - return $sqlWalker->getConnection()->getDatabasePlatform()->getTrimExpression( - $sqlWalker->walkStringPrimary($this->stringPrimary), - $pos, - ($this->trimChar != false) ? $sqlWalker->getConnection()->quote($this->trimChar) : false - ); + return $platform->getTrimExpression($stringPrimary, $trimMode, $trimChar); } /** - * @override + * {@inheritdoc} */ - public function parse(\Doctrine\ORM\Query\Parser $parser) + public function parse(Parser $parser) { $lexer = $parser->getLexer(); $parser->match(Lexer::T_IDENTIFIER); $parser->match(Lexer::T_OPEN_PARENTHESIS); - if (strcasecmp('leading', $lexer->lookahead['value']) === 0) { - $parser->match(Lexer::T_LEADING); - $this->leading = true; - } else if (strcasecmp('trailing', $lexer->lookahead['value']) === 0) { - $parser->match(Lexer::T_TRAILING); - $this->trailing = true; - } else if (strcasecmp('both', $lexer->lookahead['value']) === 0) { - $parser->match(Lexer::T_BOTH); - $this->both = true; - } + $this->parseTrimMode($parser); if ($lexer->isNextToken(Lexer::T_STRING)) { $parser->match(Lexer::T_STRING); + $this->trimChar = $lexer->token['value']; } @@ -112,4 +103,61 @@ class TrimFunction extends FunctionNode $parser->match(Lexer::T_CLOSE_PARENTHESIS); } + + /** + * @param \Doctrine\ORM\Query\Parser $parser + * + * @return integer + */ + private function getTrimMode() + { + if ($this->leading) { + return AbstractPlatform::TRIM_LEADING; + } + + if ($this->trailing) { + return AbstractPlatform::TRIM_TRAILING; + } + + if ($this->both) { + return AbstractPlatform::TRIM_BOTH; + } + + return AbstractPlatform::TRIM_UNSPECIFIED; + } + + /** + * @param \Doctrine\ORM\Query\Parser $parser + * + * @return void + */ + private function parseTrimMode(Parser $parser) + { + $lexer = $parser->getLexer(); + $value = $lexer->lookahead['value']; + + if (strcasecmp('leading', $value) === 0) { + $parser->match(Lexer::T_LEADING); + + $this->leading = true; + + return; + } + + if (strcasecmp('trailing', $value) === 0) { + $parser->match(Lexer::T_TRAILING); + + $this->trailing = true; + + return; + } + + if (strcasecmp('both', $value) === 0) { + $parser->match(Lexer::T_BOTH); + + $this->both = true; + + return; + } + } } diff --git a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php index 2587905ed..cf0c95b0b 100644 --- a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php +++ b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php @@ -394,6 +394,17 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase ); } + /** + * @group DDC-2668 + */ + public function testSupportsTrimLeadingZeroString() + { + $this->assertSqlGeneration( + "SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE TRIM(TRAILING '0' FROM u.name) != ''", + "SELECT c0_.name AS name0 FROM cms_users c0_ WHERE TRIM(TRAILING '0' FROM c0_.name) <> ''" + ); + } + // Ticket 894 public function testSupportsBetweenClauseWithPositionalParameters() { From b803f06c8f3c518b1a6fef19aba1ec61c69722c8 Mon Sep 17 00:00:00 2001 From: flip111 Date: Tue, 17 Sep 2013 15:07:08 +0200 Subject: [PATCH 296/332] Improved error messages in Paginator: LimitSubqueryOutputWalker The already existing follow-up check throwing "Not all identifier properties can be found in the ResultSetMapping: %s" is a much more technical description. If $sqlIdentifier is empty (count 0) it can be concluded that there are no mapped results. An alternative error message could thus be "The Paginator does not support queries without mapped results." (line 153). Possibly '!=' on line 154 could be replaced by '>' because i think this is the only situation that can occur. But since i don't know this for sure, i left it like i found it. --- .../ORM/Tools/Pagination/LimitSubqueryOutputWalker.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php b/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php index 2ee5f9056..3b7f0a85a 100644 --- a/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php +++ b/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php @@ -149,7 +149,9 @@ class LimitSubqueryOutputWalker extends SqlWalker } } - if (count($rootIdentifier) != count($sqlIdentifier)) { + if (count($sqlIdentifier) === 0) { + throw new \RuntimeException('The Paginator does not support Queries which only yield ScalarResults.'); + } elseif (count($rootIdentifier) != count($sqlIdentifier)) { throw new \RuntimeException(sprintf( 'Not all identifier properties can be found in the ResultSetMapping: %s', implode(', ', array_diff($rootIdentifier, array_keys($sqlIdentifier))) From a18aba1bb694d418f6b13e8deeef7f5e31777c9f Mon Sep 17 00:00:00 2001 From: flip111 Date: Tue, 17 Sep 2013 21:01:09 +0200 Subject: [PATCH 297/332] Improved error messages in Paginator: LimitSubqueryOutputWalker The already existing follow-up check throwing "Not all identifier properties can be found in the ResultSetMapping: %s" is a much more technical description. If $sqlIdentifier is empty (count 0) it can be concluded that there are no mapped results. An alternative error message could thus be "The Paginator does not support queries without mapped results." (line 153). Possibly '!=' on line 154 could be replaced by '>' because i think this is the only situation that can occur. But since i don't know this for sure, i left it like i found it. Didn't use elseif in this commit as per Stof his suggestion. --- .../ORM/Tools/Pagination/LimitSubqueryOutputWalker.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php b/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php index 3b7f0a85a..e211bb4e0 100644 --- a/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php +++ b/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php @@ -151,7 +151,9 @@ class LimitSubqueryOutputWalker extends SqlWalker if (count($sqlIdentifier) === 0) { throw new \RuntimeException('The Paginator does not support Queries which only yield ScalarResults.'); - } elseif (count($rootIdentifier) != count($sqlIdentifier)) { + } + + if (count($rootIdentifier) != count($sqlIdentifier)) { throw new \RuntimeException(sprintf( 'Not all identifier properties can be found in the ResultSetMapping: %s', implode(', ', array_diff($rootIdentifier, array_keys($sqlIdentifier))) From a419c7c93b2ce5ad68e447516191983065aa5d2f Mon Sep 17 00:00:00 2001 From: sabzeta Date: Fri, 20 Sep 2013 15:27:05 +0300 Subject: [PATCH 298/332] Update query-builder.rst expresion substr changed to substring as of http://www.doctrine-project.org/jira/browse/DDC-382 --- docs/en/reference/query-builder.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/en/reference/query-builder.rst b/docs/en/reference/query-builder.rst index 63f1a872a..9a93190a5 100644 --- a/docs/en/reference/query-builder.rst +++ b/docs/en/reference/query-builder.rst @@ -448,8 +448,8 @@ complete list of supported helper methods available: // Example - $qb->expr()->concat('u.firstname', $qb->expr()->concat($qb->expr()->literal(' '), 'u.lastname')) public function concat($x, $y); // Returns Expr\Func - // Example - $qb->expr()->substr('u.firstname', 0, 1) - public function substr($x, $from, $len); // Returns Expr\Func + // Example - $qb->expr()->substring('u.firstname', 0, 1) + public function substring($x, $from, $len); // Returns Expr\Func // Example - $qb->expr()->lower('u.firstname') public function lower($x); // Returns Expr\Func From 614eed7f86715847a52e2d9918889961f95b0ac1 Mon Sep 17 00:00:00 2001 From: "Fabio B. Silva" Date: Mon, 23 Sep 2013 19:29:50 -0400 Subject: [PATCH 299/332] remove unused test case --- .../Tests/ORM/Criteria/DqlGenerationTest.php | 252 ------------------ 1 file changed, 252 deletions(-) delete mode 100644 tests/Doctrine/Tests/ORM/Criteria/DqlGenerationTest.php diff --git a/tests/Doctrine/Tests/ORM/Criteria/DqlGenerationTest.php b/tests/Doctrine/Tests/ORM/Criteria/DqlGenerationTest.php deleted file mode 100644 index b3aa60792..000000000 --- a/tests/Doctrine/Tests/ORM/Criteria/DqlGenerationTest.php +++ /dev/null @@ -1,252 +0,0 @@ -. - */ - -namespace Doctrine\Tests\ORM\Query; - -require_once __DIR__ . '/../../TestInit.php'; - -/** - * Test case for testing the saving and referencing of query identifiers. - * - * @author Guilherme Blanco - * @author Janne Vanhala - * @author Konsta Vesterinen - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL - * @link http://www.doctrine-project.org - * @since 2.0 - * @version $Revision$ - */ -class DqlGenerationTest extends \Doctrine\Tests\OrmTestCase -{ - protected function setUp() { - $this->markTestSkipped('Not yet implemented.'); - } - - protected function createQuery() - { - return $this->_em->createQuery(); - } - - public function testSelect() - { - $query = $this->createQuery(); - - // select and from - $query->setDql('FROM User u'); - $this->assertEquals('FROM User u', $query->getDql()); // Internally we use SELECT * FROM User u to process the DQL - $query->free(); - - $query->select()->from('User u'); - $this->assertEquals('SELECT * FROM User u', $query->getDql()); - $query->free(); - - $query->select('u.*')->from('User u'); - $this->assertEquals('SELECT u.* FROM User u', $query->getDql()); - $query->free(); - - $query->select('u.id')->from('User u'); - $this->assertEquals('SELECT u.id FROM User u', $query->getDql()); - $query->free(); - - $query->select('u.id, u.name')->from('User u'); - $this->assertEquals('SELECT u.id, u.name FROM User u', $query->getDql()); - $query->free(); - - $query->select('u.name AS myCustomName')->from('User u'); - $this->assertEquals('SELECT u.name AS myCustomName FROM User u', $query->getDql()); - $query->free(); - - $query->select('u.id')->select('u.name')->from('User u'); - $this->assertEquals('SELECT u.id, u.name FROM User u', $query->getDql()); - $query->free(); - } - - - public function testSelectDistinct() - { - $query = $this->createQuery(); - - $query->select()->distinct()->from('User u'); - $this->assertEquals('SELECT DISTINCT * FROM User u', $query->getDql()); - $query->free(); - - $query->select('u.name')->distinct(false)->from('User u'); - $this->assertEquals('SELECT u.name FROM User u', $query->getDql()); - $query->free(); - - $query->select()->distinct(false)->from('User u'); - $this->assertEquals('SELECT * FROM User u', $query->getDql()); - $query->free(); - - $query->select('u.name')->distinct()->from('User u'); - $this->assertEquals('SELECT DISTINCT u.name FROM User u', $query->getDql()); - $query->free(); - - $query->select('u.name, u.email')->distinct()->from('User u'); - $this->assertEquals('SELECT DISTINCT u.name, u.email FROM User u', $query->getDql()); - $query->free(); - - $query->select('u.name')->select('u.email')->distinct()->from('User u'); - $this->assertEquals('SELECT DISTINCT u.name, u.email FROM User u', $query->getDql()); - $query->free(); - - $query->select('DISTINCT u.name')->from('User u'); - $this->assertEquals('SELECT DISTINCT u.name FROM User u', $query->getDql()); - $query->free(); - - $query->select('DISTINCT u.name, u.email')->from('User u'); - $this->assertEquals('SELECT DISTINCT u.name, u.email FROM User u', $query->getDql()); - $query->free(); - - $query->select('DISTINCT u.name')->select('u.email')->from('User u'); - $this->assertEquals('SELECT DISTINCT u.name, u.email FROM User u', $query->getDql()); - $query->free(); - } - - - public function testSelectJoin() - { - $query = $this->createQuery(); - - $query->select('u.*')->from('User u')->join('u.Group g')->where('g.id = ?', 1); - $this->assertEquals('SELECT u.* FROM User u INNER JOIN u.Group g WHERE g.id = ?', $query->getDql()); - $this->assertEquals(array(1), $query->getParams()); - $query->free(); - - $query->select('u.*')->from('User u')->innerJoin('u.Group g')->where('g.id = ?', 1); - $this->assertEquals('SELECT u.* FROM User u INNER JOIN u.Group g WHERE g.id = ?', $query->getDql()); - $this->assertEquals(array(1), $query->getParams()); - $query->free(); - - $query->select('u.*')->from('User u')->leftJoin('u.Group g')->where('g.id IS NULL'); - $this->assertEquals('SELECT u.* FROM User u LEFT JOIN u.Group g WHERE g.id IS NULL', $query->getDql()); - $query->free(); - - $query->select('u.*')->from('User u')->leftJoin('u.UserGroup ug')->leftJoin('ug.Group g')->where('g.name = ?', 'admin'); - $this->assertEquals('SELECT u.* FROM User u LEFT JOIN u.UserGroup ug LEFT JOIN ug.Group g WHERE g.name = ?', $query->getDql()); - $query->free(); - } - - - public function testSelectWhere() - { - $query = $this->createQuery(); - - $query->select('u.name')->from('User u')->where('u.id = ?', 1); - $this->assertEquals('SELECT u.name FROM User u WHERE u.id = ?', $query->getDql()); - $this->assertEquals(array(1), $query->getParams()); - $query->free(); - - $query->select('u.name')->from('User u')->where('u.id = ? AND u.type != ?', array(1, 'admin')); - $this->assertEquals('SELECT u.name FROM User u WHERE u.id = ? AND u.type != ?', $query->getDql()); - $this->assertEquals(array(1, 'admin'), $query->getParams()); - $query->free(); - - $query->select('u.name')->from('User u')->where('u.id = ?', 1)->andWhere('u.type != ?', 'admin'); - $this->assertEquals('SELECT u.name FROM User u WHERE u.id = ? AND u.type != ?', $query->getDql()); - $this->assertEquals(array(1, 'admin'), $query->getParams()); - $query->free(); - - $query->select('u.name')->from('User u')->where('( u.id = ?', 1)->andWhere('u.type != ? )', 'admin'); - $this->assertEquals('SELECT u.name FROM User u WHERE ( u.id = ? AND u.type != ? )', $query->getDql()); - $this->assertEquals(array(1, 'admin'), $query->getParams()); - $query->free(); - - $query->select('u.name')->from('User u')->where('u.id = ? OR u.type != ?', array(1, 'admin')); - $this->assertEquals('SELECT u.name FROM User u WHERE u.id = ? OR u.type != ?', $query->getDql()); - $this->assertEquals(array(1, 'admin'), $query->getParams()); - $query->free(); - - $query->select('u.name')->from('User u')->where('u.id = ?', 1)->orWhere('u.type != ?', 'admin'); - $this->assertEquals('SELECT u.name FROM User u WHERE u.id = ? OR u.type != ?', $query->getDql()); - $this->assertEquals(array(1, 'admin'), $query->getParams()); - $query->free(); - - $query->select('u.name')->from('User u')->andwhere('u.id = ?', 1)->andWhere('u.type != ?', 'admin')->orWhere('u.email = ?', 'admin@localhost'); - $this->assertEquals('SELECT u.name FROM User u WHERE u.id = ? AND u.type != ? OR u.email = ?', $query->getDql()); - $this->assertEquals(array(1, 'admin', 'admin@localhost'), $query->getParams()); - $query->free(); - } - - - public function testSelectWhereIn() - { - $query = $this->createQuery(); - - $query->select('u.name')->from('User u')->whereIn('u.id', array(1, 2, 3, 4, 5)); - $this->assertEquals('SELECT u.name FROM User u WHERE u.id IN (?, ?, ?, ?, ?)', $query->getDql()); - $this->assertEquals(array(1, 2, 3, 4, 5), $query->getParams()); - $query->free(); - - $query->select('u.name')->from('User u')->whereNotIn('u.id', array(1, 2, 3)); - $this->assertEquals('SELECT u.name FROM User u WHERE u.id NOT IN (?, ?, ?)', $query->getDql()); - $this->assertEquals(array(1, 2, 3), $query->getParams()); - $query->free(); - - $query->select('u.name')->from('User u')->where('u.type = ?', 'admin')->andWhereIn('u.id', array(1, 2)); - $this->assertEquals('SELECT u.name FROM User u WHERE u.type = ? AND u.id IN (?, ?)', $query->getDql()); - $this->assertEquals(array('admin', 1, 2), $query->getParams()); - $query->free(); - - $query->select('u.name')->from('User u')->where('u.type = ?', 'admin')->andWhereNotIn('u.id', array(1, 2)); - $this->assertEquals('SELECT u.name FROM User u WHERE u.type = ? AND u.id NOT IN (?, ?)', $query->getDql()); - $this->assertEquals(array('admin', 1, 2), $query->getParams()); - $query->free(); - - $query->select('u.name')->from('User u')->whereIn('u.type', array('admin', 'moderator'))->andWhereNotIn('u.id', array(1, 2, 3, 4)); - $this->assertEquals('SELECT u.name FROM User u WHERE u.type IN (?, ?) AND u.id NOT IN (?, ?, ?, ?)', $query->getDql()); - $this->assertEquals(array('admin', 'moderator', 1, 2, 3, 4), $query->getParams()); - $query->free(); - - $query->select('u.name')->from('User u')->whereIn('u.type', array('admin', 'moderator'))->orWhereIn('u.id', array(1, 2, 3, 4)); - $this->assertEquals('SELECT u.name FROM User u WHERE u.type IN (?, ?) OR u.id IN (?, ?, ?, ?)', $query->getDql()); - $this->assertEquals(array('admin', 'moderator', 1, 2, 3, 4), $query->getParams()); - $query->free(); - - $query->select('u.name')->from('User u')->whereIn('u.type', array('admin', 'moderator'))->andWhereNotIn('u.id', array(1, 2))->orWhereNotIn('u.type', array('admin', 'moderator'))->andWhereNotIn('u.email', array('user@localhost', 'guest@localhost')); - $this->assertEquals('SELECT u.name FROM User u WHERE u.type IN (?, ?) AND u.id NOT IN (?, ?) OR u.type NOT IN (?, ?) AND u.email NOT IN (?, ?)', $query->getDql()); - $this->assertEquals(array('admin', 'moderator', 1, 2, 'admin', 'moderator', 'user@localhost', 'guest@localhost'), $query->getParams()); - $query->free(); - } - - - public function testDelete() - { - $query = $this->createQuery(); - - $query->setDql('DELETE CmsUser u'); - $this->assertEquals('DELETE CmsUser u', $query->getDql()); - $query->free(); - - $query->delete()->from('CmsUser u'); - $this->assertEquals('DELETE FROM CmsUser u', $query->getDql()); - $query->free(); - - $query->delete()->from('CmsUser u')->where('u.id = ?', 1); - $this->assertEquals('DELETE FROM CmsUser u WHERE u.id = ?', $query->getDql()); - $query->free(); - - $query->delete()->from('CmsUser u')->where('u.username = ?', 'gblanco')->orWhere('u.name = ?', 'Guilherme'); - $this->assertEquals('DELETE FROM CmsUser u WHERE u.username = ? OR u.name = ?', $query->getDql()); - $query->free(); - } - -} From 76fda9562c8726d229ad90f24f159fee38309750 Mon Sep 17 00:00:00 2001 From: flip111 Date: Thu, 26 Sep 2013 14:11:56 +0200 Subject: [PATCH 300/332] Update SqlWalker.php fixed wrong GROUP BY clause on SQL Server platform Without this patch a query would like like: ``` SELECT c0_.Country AS sclr0 FROM Continent c0_ WITH (NOLOCK) WHERE c0_.Country = 38 GROUP BY sclr0 ``` Using the column alias in the GROUP BY clause. However this is not allowed on SQL Server. References: 1. http://stackoverflow.com/a/3841804 2. http://technet.microsoft.com/en-us/library/ms189499.aspx (Logical Processing Order of the SELECT statement) The correct query should be: ``` SELECT c0_.Country AS sclr0 FROM Continent c0_ WITH (NOLOCK) WHERE c0_.Country = 38 GROUP BY c0_.Country ``` --- lib/Doctrine/ORM/Query/SqlWalker.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Query/SqlWalker.php b/lib/Doctrine/ORM/Query/SqlWalker.php index 082f95f50..c3fb467de 100644 --- a/lib/Doctrine/ORM/Query/SqlWalker.php +++ b/lib/Doctrine/ORM/Query/SqlWalker.php @@ -21,6 +21,7 @@ namespace Doctrine\ORM\Query; use Doctrine\DBAL\LockMode; use Doctrine\DBAL\Types\Type; +use Doctrine\DBAL\Platforms\SQLServerPlatform; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Query; use Doctrine\ORM\Query\QueryException; @@ -1624,7 +1625,11 @@ class SqlWalker implements TreeWalker // ResultVariable if (isset($this->queryComponents[$groupByItem]['resultVariable'])) { - return $this->walkResultVariable($groupByItem); + if ($this->platform instanceof SQLServerPlatform) { + return $this->walkPathExpression($this->queryComponents[$groupByItem]['resultVariable']->pathExpression); + } else { + return $this->walkResultVariable($groupByItem); + } } // IdentificationVariable From 032673134849d1833e22f16859100f4e6eaa07f7 Mon Sep 17 00:00:00 2001 From: Kristian Mide Date: Thu, 26 Sep 2013 14:41:06 +0200 Subject: [PATCH 301/332] Respect unsigned fields when tables get converted to entities. This is working on our mysql setup, however i have no idea of the consequences of this change when using other RDBMSes. --- lib/Doctrine/ORM/Tools/EntityGenerator.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Tools/EntityGenerator.php b/lib/Doctrine/ORM/Tools/EntityGenerator.php index 5bbaf280c..c497f5761 100644 --- a/lib/Doctrine/ORM/Tools/EntityGenerator.php +++ b/lib/Doctrine/ORM/Tools/EntityGenerator.php @@ -1430,7 +1430,11 @@ public function __construct() if (isset($fieldMapping['nullable'])) { $column[] = 'nullable=' . var_export($fieldMapping['nullable'], true); } - + + if (isset($fieldMapping['unsigned']) && $fieldMapping['unsigned']) { + $column[] = 'options={"unsigned"=true}'; + } + if (isset($fieldMapping['columnDefinition'])) { $column[] = 'columnDefinition="' . $fieldMapping['columnDefinition'] . '"'; } From 1fda797c8f69915a34257e19ae3095ea7adca450 Mon Sep 17 00:00:00 2001 From: Cas Date: Sat, 28 Sep 2013 17:47:35 +0200 Subject: [PATCH 302/332] Appended newline to (newly) generated files for PSR2 compatibility Appended newline to (newly) generated files for PSR2 compatibility Additionally, slightly improved styling of concatenating return value --- lib/Doctrine/ORM/Tools/EntityGenerator.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Doctrine/ORM/Tools/EntityGenerator.php b/lib/Doctrine/ORM/Tools/EntityGenerator.php index c497f5761..8e93a2817 100644 --- a/lib/Doctrine/ORM/Tools/EntityGenerator.php +++ b/lib/Doctrine/ORM/Tools/EntityGenerator.php @@ -392,7 +392,7 @@ public function __construct() $this->generateEntityBody($metadata) ); - $code = str_replace($placeHolders, $replacements, static::$classTemplate); + $code = str_replace($placeHolders, $replacements, static::$classTemplate) . "\n"; return str_replace('', $this->spaces, $code); } @@ -413,7 +413,7 @@ public function __construct() $body = str_replace('', $this->spaces, $body); $last = strrpos($currentCode, '}'); - return substr($currentCode, 0, $last) . $body . (strlen($body) > 0 ? "\n" : ''). "}\n"; + return substr($currentCode, 0, $last) . $body . (strlen($body) > 0 ? "\n" : '') . "}\n"; } /** From bd0e0c3fcf2e33d9332c858c37ac178bbf8a22bc Mon Sep 17 00:00:00 2001 From: Diego Mazzaro Date: Sun, 29 Sep 2013 16:17:10 +0200 Subject: [PATCH 303/332] [bugfix] if BIGINT or SMALLINT version field was not incremented. Made version field accepted type coherent between ClassMetadataInfo and BasicEntityPersister --- lib/Doctrine/ORM/Persisters/BasicEntityPersister.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php index 00b00cfa2..c594f3bbf 100644 --- a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php @@ -464,7 +464,9 @@ class BasicEntityPersister $params[] = $this->class->reflFields[$versionField]->getValue($entity); switch ($versionFieldType) { + case Type::SMALLINT: case Type::INTEGER: + case Type::BIGINT: $set[] = $versionColumn . ' = ' . $versionColumn . ' + 1'; break; @@ -1974,4 +1976,4 @@ class BasicEntityPersister $sql = implode(' AND ', $filterClauses); return $sql ? "(" . $sql . ")" : ""; // Wrap again to avoid "X or Y and FilterConditionSQL" } -} +} \ No newline at end of file From 72ae7f5497adf0afdcf532c82fc8fc94e495226f Mon Sep 17 00:00:00 2001 From: flip111 Date: Mon, 30 Sep 2013 11:08:42 +0200 Subject: [PATCH 304/332] Changed GroupBy alias to real column name for all platforms and adjusted failing test accordingly. Has fallback in cases where real column name is not possible (example: Doctrine\Tests\ORM\Query\SelectSqlGenerationTest::testGroupBySupportsIdentificationVariable) --- lib/Doctrine/ORM/Query/SqlWalker.php | 5 +++-- tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php | 3 +-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/Doctrine/ORM/Query/SqlWalker.php b/lib/Doctrine/ORM/Query/SqlWalker.php index c3fb467de..946579b8a 100644 --- a/lib/Doctrine/ORM/Query/SqlWalker.php +++ b/lib/Doctrine/ORM/Query/SqlWalker.php @@ -21,7 +21,6 @@ namespace Doctrine\ORM\Query; use Doctrine\DBAL\LockMode; use Doctrine\DBAL\Types\Type; -use Doctrine\DBAL\Platforms\SQLServerPlatform; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Query; use Doctrine\ORM\Query\QueryException; @@ -1625,7 +1624,9 @@ class SqlWalker implements TreeWalker // ResultVariable if (isset($this->queryComponents[$groupByItem]['resultVariable'])) { - if ($this->platform instanceof SQLServerPlatform) { + if ($this->queryComponents[$groupByItem]['resultVariable'] instanceof AST\PathExpression) { + return $this->walkPathExpression($this->queryComponents[$groupByItem]['resultVariable']); + } elseif (isset($this->queryComponents[$groupByItem]['resultVariable']->pathExpression)) { return $this->walkPathExpression($this->queryComponents[$groupByItem]['resultVariable']->pathExpression); } else { return $this->walkResultVariable($groupByItem); diff --git a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php index 2587905ed..208b57b6b 100644 --- a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php +++ b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php @@ -1573,7 +1573,7 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase { $this->assertSqlGeneration( 'SELECT u, u.status AS st FROM Doctrine\Tests\Models\CMS\CmsUser u GROUP BY st', - 'SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3, c0_.status AS status4 FROM cms_users c0_ GROUP BY status4' + 'SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3, c0_.status AS status4 FROM cms_users c0_ GROUP BY c0_.status' ); } @@ -2257,4 +2257,3 @@ class DDC1474Entity } } - From a4e39210903efe3919194c8ac7819068867cb9cb Mon Sep 17 00:00:00 2001 From: jan brunnert Date: Mon, 30 Sep 2013 11:32:46 +0200 Subject: [PATCH 305/332] When the OptimisticLockingException is generated with the static function lockFailedVersionMismatch and the passed parameters are DateTime instances, the exception could not be thrown because the DateTime object is not implicitly converted to a string. --- lib/Doctrine/ORM/OptimisticLockException.php | 2 ++ .../ORM/Functional/Locking/OptimisticTest.php | 35 ++++++++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/OptimisticLockException.php b/lib/Doctrine/ORM/OptimisticLockException.php index 57ea66bde..cadf4ce28 100644 --- a/lib/Doctrine/ORM/OptimisticLockException.php +++ b/lib/Doctrine/ORM/OptimisticLockException.php @@ -73,6 +73,8 @@ class OptimisticLockException extends ORMException */ public static function lockFailedVersionMismatch($entity, $expectedLockVersion, $actualLockVersion) { + $expectedLockVersion = (is_object($expectedLockVersion) && $expectedLockVersion instanceof \DateTime) ? $expectedLockVersion->getTimestamp() : $expectedLockVersion; + $actualLockVersion = (is_object($actualLockVersion) && $actualLockVersion instanceof \DateTime) ? $actualLockVersion->getTimestamp() : $actualLockVersion; return new self("The optimistic lock failed, version " . $expectedLockVersion . " was expected, but is actually ".$actualLockVersion, $entity); } diff --git a/tests/Doctrine/Tests/ORM/Functional/Locking/OptimisticTest.php b/tests/Doctrine/Tests/ORM/Functional/Locking/OptimisticTest.php index e2341a19e..cd0e04b02 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Locking/OptimisticTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/Locking/OptimisticTest.php @@ -7,6 +7,8 @@ use Doctrine\ORM\OptimisticLockException; use Doctrine\Common\EventManager; use Doctrine\ORM\Mapping\ClassMetadataFactory; use Doctrine\Tests\TestUtil; +use Doctrine\DBAL\LockMode; +use DateTime; require_once __DIR__ . '/../../../TestInit.php'; @@ -181,13 +183,44 @@ class OptimisticTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->_conn->executeQuery('UPDATE optimistic_timestamp SET version = ? WHERE id = ?', array(date($format, strtotime($test->version->format($format)) + 3600), $test->id)); // Try and update the record and it should throw an exception + $caughtException = null; $test->name = 'Testing again'; try { $this->_em->flush(); } catch (OptimisticLockException $e) { - $this->assertSame($test, $e->getEntity()); + $caughtException = $e; } + + $this->assertNotNull($caughtException, "No OptimisticLockingException was thrown"); + $this->assertSame($test, $caughtException->getEntity()); + } + + /** + * @depends testOptimisticTimestampSetsDefaultValue + */ + public function testOptimisticTimestampLockFailureThrowsException(OptimisticTimestamp $entity) + { + $q = $this->_em->createQuery('SELECT t FROM Doctrine\Tests\ORM\Functional\Locking\OptimisticTimestamp t WHERE t.id = :id'); + $q->setParameter('id', $entity->id); + $test = $q->getSingleResult(); + + $this->assertInstanceOf('DateTime', $test->version); + + // Try to lock the record with an older timestamp and it should throw an exception + $caughtException = null; + try { + $expectedVersionExpired = DateTime::createFromFormat('U', $test->version->getTimestamp()-3600); + $this->_em->lock($test, LockMode::OPTIMISTIC, $expectedVersionExpired); + } catch (OptimisticLockException $e) { + $caughtException = $e; + } + + $this->assertNotNull($caughtException, "No OptimisticLockingException was thrown"); + $this->assertSame($test, $caughtException->getEntity()); + + } + } /** From f64d6695ce267e0b77efe73723783103f6553a9b Mon Sep 17 00:00:00 2001 From: jan brunnert Date: Tue, 1 Oct 2013 13:00:37 +0200 Subject: [PATCH 306/332] Removed unnecessary is_object() check --- lib/Doctrine/ORM/OptimisticLockException.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Doctrine/ORM/OptimisticLockException.php b/lib/Doctrine/ORM/OptimisticLockException.php index cadf4ce28..ecd5445b7 100644 --- a/lib/Doctrine/ORM/OptimisticLockException.php +++ b/lib/Doctrine/ORM/OptimisticLockException.php @@ -73,8 +73,8 @@ class OptimisticLockException extends ORMException */ public static function lockFailedVersionMismatch($entity, $expectedLockVersion, $actualLockVersion) { - $expectedLockVersion = (is_object($expectedLockVersion) && $expectedLockVersion instanceof \DateTime) ? $expectedLockVersion->getTimestamp() : $expectedLockVersion; - $actualLockVersion = (is_object($actualLockVersion) && $actualLockVersion instanceof \DateTime) ? $actualLockVersion->getTimestamp() : $actualLockVersion; + $expectedLockVersion = ($expectedLockVersion instanceof \DateTime) ? $expectedLockVersion->getTimestamp() : $expectedLockVersion; + $actualLockVersion = ($actualLockVersion instanceof \DateTime) ? $actualLockVersion->getTimestamp() : $actualLockVersion; return new self("The optimistic lock failed, version " . $expectedLockVersion . " was expected, but is actually ".$actualLockVersion, $entity); } From 228a501014fa0e0de30019154bf755faf5786fc5 Mon Sep 17 00:00:00 2001 From: flip111 Date: Tue, 1 Oct 2013 16:53:53 +0200 Subject: [PATCH 307/332] Made the code prettier :) --- lib/Doctrine/ORM/Query/SqlWalker.php | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/Doctrine/ORM/Query/SqlWalker.php b/lib/Doctrine/ORM/Query/SqlWalker.php index 946579b8a..fb6177919 100644 --- a/lib/Doctrine/ORM/Query/SqlWalker.php +++ b/lib/Doctrine/ORM/Query/SqlWalker.php @@ -1624,13 +1624,17 @@ class SqlWalker implements TreeWalker // ResultVariable if (isset($this->queryComponents[$groupByItem]['resultVariable'])) { - if ($this->queryComponents[$groupByItem]['resultVariable'] instanceof AST\PathExpression) { - return $this->walkPathExpression($this->queryComponents[$groupByItem]['resultVariable']); - } elseif (isset($this->queryComponents[$groupByItem]['resultVariable']->pathExpression)) { - return $this->walkPathExpression($this->queryComponents[$groupByItem]['resultVariable']->pathExpression); - } else { - return $this->walkResultVariable($groupByItem); + $resultVariable = $this->queryComponents[$groupByItem]['resultVariable']; + + if ($resultVariable instanceof AST\PathExpression) { + return $this->walkPathExpression($resultVariable); } + + if (isset($resultVariable->pathExpression)) { + return $this->walkPathExpression($resultVariable->pathExpression); + } + + return $this->walkResultVariable($groupByItem); } // IdentificationVariable From 4282fa478744e346f50c734a76b58f037c30d79c Mon Sep 17 00:00:00 2001 From: flip111 Date: Tue, 1 Oct 2013 17:25:49 +0200 Subject: [PATCH 308/332] Used ternary operator to reduce LOC --- tests/Doctrine/Tests/ORM/Functional/Ticket/DDC719Test.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC719Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC719Test.php index 9a0dc285b..87be4f644 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC719Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC719Test.php @@ -19,11 +19,7 @@ class DDC719Test extends \Doctrine\Tests\OrmFunctionalTestCase { $q = $this->_em->createQuery('SELECT g, c FROM Doctrine\Tests\ORM\Functional\Ticket\DDC719Group g LEFT JOIN g.children c WHERE g.parents IS EMPTY'); - if ($this->_em->getConnection()->getDatabasePlatform() instanceof SQLServerPlatform) { - $referenceSQL = 'SELECT g0_.name AS name0, g0_.description AS description1, g0_.id AS id2, g1_.name AS name3, g1_.description AS description4, g1_.id AS id5 FROM groups g0_ with (nolock) LEFT JOIN groups_groups g2_ ON g0_.id = g2_.parent_id LEFT JOIN groups g1_ ON g1_.id = g2_.child_id WHERE (SELECT COUNT(*) FROM groups_groups g3_ WHERE g3_.child_id = g0_.id) = 0'; - } else { - $referenceSQL = 'SELECT g0_.name AS name0, g0_.description AS description1, g0_.id AS id2, g1_.name AS name3, g1_.description AS description4, g1_.id AS id5 FROM groups g0_ LEFT JOIN groups_groups g2_ ON g0_.id = g2_.parent_id LEFT JOIN groups g1_ ON g1_.id = g2_.child_id WHERE (SELECT COUNT(*) FROM groups_groups g3_ WHERE g3_.child_id = g0_.id) = 0'; - } + $referenceSQL = ($this->_em->getConnection()->getDatabasePlatform() instanceof SQLServerPlatform) ? 'SELECT g0_.name AS name0, g0_.description AS description1, g0_.id AS id2, g1_.name AS name3, g1_.description AS description4, g1_.id AS id5 FROM groups g0_ with (nolock) LEFT JOIN groups_groups g2_ ON g0_.id = g2_.parent_id LEFT JOIN groups g1_ ON g1_.id = g2_.child_id WHERE (SELECT COUNT(*) FROM groups_groups g3_ WHERE g3_.child_id = g0_.id) = 0' : 'SELECT g0_.name AS name0, g0_.description AS description1, g0_.id AS id2, g1_.name AS name3, g1_.description AS description4, g1_.id AS id5 FROM groups g0_ LEFT JOIN groups_groups g2_ ON g0_.id = g2_.parent_id LEFT JOIN groups g1_ ON g1_.id = g2_.child_id WHERE (SELECT COUNT(*) FROM groups_groups g3_ WHERE g3_.child_id = g0_.id) = 0'; $this->assertEquals( strtolower($referenceSQL), From dd4bdd776adef88ed1f4c59878eb714d28e4d7ff Mon Sep 17 00:00:00 2001 From: fabios Date: Tue, 1 Oct 2013 12:00:52 -0400 Subject: [PATCH 309/332] Fix DDC-1514 test --- .../Tests/ORM/Functional/Ticket/DDC1514Test.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1514Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1514Test.php index cc0f9728d..2e3b2fbc5 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1514Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1514Test.php @@ -29,10 +29,10 @@ class DDC1514Test extends \Doctrine\Tests\OrmFunctionalTestCase public function testIssue() { $a1 = new DDC1514EntityA(); - $a1->title = "foo"; + $a1->title = "1foo"; $a2 = new DDC1514EntityA(); - $a2->title = "bar"; + $a2->title = "2bar"; $b1 = new DDC1514EntityB(); $b1->entityAFrom = $a1; @@ -54,9 +54,13 @@ class DDC1514Test extends \Doctrine\Tests\OrmFunctionalTestCase $this->_em->flush(); $this->_em->clear(); - $dql = "SELECT a, b, ba, c FROM " . __NAMESPACE__ . "\DDC1514EntityA AS a LEFT JOIN a.entitiesB AS b LEFT JOIN b.entityATo AS ba LEFT JOIN a.entityC AS c"; + $dql = "SELECT a, b, ba, c FROM " . __NAMESPACE__ . "\DDC1514EntityA AS a LEFT JOIN a.entitiesB AS b LEFT JOIN b.entityATo AS ba LEFT JOIN a.entityC AS c ORDER BY a.title"; $results = $this->_em->createQuery($dql)->getResult(); + $this->assertEquals($a1->id, $results[0]->id); + $this->assertNull($results[0]->entityC); + + $this->assertEquals($a2->id, $results[1]->id); $this->assertEquals($c->title, $results[1]->entityC->title); } } From fc53bc890981739a92b75c3188e29ab417a43026 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 2 Oct 2013 12:07:28 +0200 Subject: [PATCH 310/332] Fix some LGPL occurances to MIT --- lib/Doctrine/ORM/Event/OnClearEventArgs.php | 2 +- lib/Doctrine/ORM/Event/OnFlushEventArgs.php | 2 +- lib/Doctrine/ORM/Event/PostFlushEventArgs.php | 2 +- lib/Doctrine/ORM/Event/PreFlushEventArgs.php | 2 +- lib/Doctrine/ORM/Mapping/Builder/ClassMetadataBuilder.php | 2 +- lib/Doctrine/ORM/Mapping/Builder/FieldBuilder.php | 2 +- .../ORM/Mapping/Builder/ManyToManyAssociationBuilder.php | 2 +- .../ORM/Mapping/Builder/OneToManyAssociationBuilder.php | 2 +- lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php | 2 +- lib/Doctrine/ORM/PessimisticLockException.php | 2 +- lib/Doctrine/ORM/Query/AST/JoinClassPathExpression.php | 2 +- lib/Doctrine/ORM/Query/Exec/AbstractSqlExecutor.php | 2 +- lib/Doctrine/ORM/Query/Exec/MultiTableDeleteExecutor.php | 2 +- lib/Doctrine/ORM/Query/Exec/SingleSelectExecutor.php | 2 +- lib/Doctrine/ORM/Query/Exec/SingleTableDeleteUpdateExecutor.php | 2 +- lib/Doctrine/ORM/Query/ParserResult.php | 2 +- lib/Doctrine/ORM/Query/Printer.php | 2 +- .../ORM/Tools/Console/Command/ValidateSchemaCommand.php | 2 +- lib/Doctrine/ORM/Tools/Console/MetadataFilter.php | 2 +- lib/Doctrine/ORM/Tools/Event/GenerateSchemaEventArgs.php | 2 +- lib/Doctrine/ORM/Tools/Event/GenerateSchemaTableEventArgs.php | 2 +- lib/Doctrine/ORM/Tools/SchemaValidator.php | 2 +- lib/Doctrine/ORM/TransactionRequiredException.php | 2 +- 23 files changed, 23 insertions(+), 23 deletions(-) diff --git a/lib/Doctrine/ORM/Event/OnClearEventArgs.php b/lib/Doctrine/ORM/Event/OnClearEventArgs.php index 3a29680f7..f37120573 100644 --- a/lib/Doctrine/ORM/Event/OnClearEventArgs.php +++ b/lib/Doctrine/ORM/Event/OnClearEventArgs.php @@ -24,7 +24,7 @@ use Doctrine\ORM\EntityManager; /** * Provides event arguments for the onClear event. * - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @license http://www.opensource.org/licenses/mit-license.php MIT * @link www.doctrine-project.org * @since 2.0 * @author Roman Borschel diff --git a/lib/Doctrine/ORM/Event/OnFlushEventArgs.php b/lib/Doctrine/ORM/Event/OnFlushEventArgs.php index 2dd05a5fa..c4f82a971 100644 --- a/lib/Doctrine/ORM/Event/OnFlushEventArgs.php +++ b/lib/Doctrine/ORM/Event/OnFlushEventArgs.php @@ -25,7 +25,7 @@ use Doctrine\ORM\EntityManager; /** * Provides event arguments for the preFlush event. * - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @license http://www.opensource.org/licenses/mit-license.php MIT * @link www.doctrine-project.org * @since 2.0 * @author Roman Borschel diff --git a/lib/Doctrine/ORM/Event/PostFlushEventArgs.php b/lib/Doctrine/ORM/Event/PostFlushEventArgs.php index d12060082..f860b7fa9 100644 --- a/lib/Doctrine/ORM/Event/PostFlushEventArgs.php +++ b/lib/Doctrine/ORM/Event/PostFlushEventArgs.php @@ -24,7 +24,7 @@ use Doctrine\Common\EventArgs; /** * Provides event arguments for the postFlush event. * - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @license http://www.opensource.org/licenses/mit-license.php MIT * @link www.doctrine-project.org * @since 2.0 * @author Daniel Freudenberger diff --git a/lib/Doctrine/ORM/Event/PreFlushEventArgs.php b/lib/Doctrine/ORM/Event/PreFlushEventArgs.php index bfee5c76e..4f49162b8 100644 --- a/lib/Doctrine/ORM/Event/PreFlushEventArgs.php +++ b/lib/Doctrine/ORM/Event/PreFlushEventArgs.php @@ -25,7 +25,7 @@ use Doctrine\ORM\EntityManager; /** * Provides event arguments for the preFlush event. * - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @license http://www.opensource.org/licenses/mit-license.php MIT * @link www.doctrine-project.com * @since 2.0 * @author Roman Borschel diff --git a/lib/Doctrine/ORM/Mapping/Builder/ClassMetadataBuilder.php b/lib/Doctrine/ORM/Mapping/Builder/ClassMetadataBuilder.php index 774c9e06e..2b0d74b7d 100644 --- a/lib/Doctrine/ORM/Mapping/Builder/ClassMetadataBuilder.php +++ b/lib/Doctrine/ORM/Mapping/Builder/ClassMetadataBuilder.php @@ -25,7 +25,7 @@ use Doctrine\ORM\Mapping\ClassMetadataInfo; /** * Builder Object for ClassMetadata * - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @license http://www.opensource.org/licenses/mit-license.php MIT * @link www.doctrine-project.com * @since 2.2 * @author Benjamin Eberlei diff --git a/lib/Doctrine/ORM/Mapping/Builder/FieldBuilder.php b/lib/Doctrine/ORM/Mapping/Builder/FieldBuilder.php index a4eb8cced..f517dd343 100644 --- a/lib/Doctrine/ORM/Mapping/Builder/FieldBuilder.php +++ b/lib/Doctrine/ORM/Mapping/Builder/FieldBuilder.php @@ -22,7 +22,7 @@ namespace Doctrine\ORM\Mapping\Builder; /** * Field Builder * - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @license http://www.opensource.org/licenses/mit-license.php MIT * @link www.doctrine-project.com * @since 2.2 * @author Benjamin Eberlei diff --git a/lib/Doctrine/ORM/Mapping/Builder/ManyToManyAssociationBuilder.php b/lib/Doctrine/ORM/Mapping/Builder/ManyToManyAssociationBuilder.php index 0dc0af487..a182e1e86 100644 --- a/lib/Doctrine/ORM/Mapping/Builder/ManyToManyAssociationBuilder.php +++ b/lib/Doctrine/ORM/Mapping/Builder/ManyToManyAssociationBuilder.php @@ -22,7 +22,7 @@ namespace Doctrine\ORM\Mapping\Builder; /** * ManyToMany Association Builder * - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @license http://www.opensource.org/licenses/mit-license.php MIT * @link www.doctrine-project.com * @since 2.0 * @author Benjamin Eberlei diff --git a/lib/Doctrine/ORM/Mapping/Builder/OneToManyAssociationBuilder.php b/lib/Doctrine/ORM/Mapping/Builder/OneToManyAssociationBuilder.php index d8f4c3953..4ca60f0ce 100644 --- a/lib/Doctrine/ORM/Mapping/Builder/OneToManyAssociationBuilder.php +++ b/lib/Doctrine/ORM/Mapping/Builder/OneToManyAssociationBuilder.php @@ -22,7 +22,7 @@ namespace Doctrine\ORM\Mapping\Builder; /** * OneToMany Association Builder * - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @license http://www.opensource.org/licenses/mit-license.php MIT * @link www.doctrine-project.com * @since 2.0 * @author Benjamin Eberlei diff --git a/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php b/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php index ed4386ac4..6e024d041 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php @@ -28,7 +28,7 @@ use Doctrine\ORM\Mapping\MappingException; /** * XmlDriver is a metadata driver that enables mapping through XML files. * - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @license http://www.opensource.org/licenses/mit-license.php MIT * @link www.doctrine-project.org * @since 2.0 * @author Benjamin Eberlei diff --git a/lib/Doctrine/ORM/PessimisticLockException.php b/lib/Doctrine/ORM/PessimisticLockException.php index 2bc93bffe..d60f7a821 100644 --- a/lib/Doctrine/ORM/PessimisticLockException.php +++ b/lib/Doctrine/ORM/PessimisticLockException.php @@ -22,7 +22,7 @@ namespace Doctrine\ORM; /** * Pessimistic Lock Exception * - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @license http://www.opensource.org/licenses/mit-license.php MIT * @link www.doctrine-project.com * @since 1.0 * @author Benjamin Eberlei diff --git a/lib/Doctrine/ORM/Query/AST/JoinClassPathExpression.php b/lib/Doctrine/ORM/Query/AST/JoinClassPathExpression.php index 1c67cb9c4..7e374149f 100644 --- a/lib/Doctrine/ORM/Query/AST/JoinClassPathExpression.php +++ b/lib/Doctrine/ORM/Query/AST/JoinClassPathExpression.php @@ -22,7 +22,7 @@ namespace Doctrine\ORM\Query\AST; /** * JoinClassPathExpression ::= AbstractSchemaName ["AS"] AliasIdentificationVariable * - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @license http://www.opensource.org/licenses/mit-license.php MIT * @link www.doctrine-project.org * @since 2.3 * @author Alexander diff --git a/lib/Doctrine/ORM/Query/Exec/AbstractSqlExecutor.php b/lib/Doctrine/ORM/Query/Exec/AbstractSqlExecutor.php index 3d6ed4353..9be35df18 100644 --- a/lib/Doctrine/ORM/Query/Exec/AbstractSqlExecutor.php +++ b/lib/Doctrine/ORM/Query/Exec/AbstractSqlExecutor.php @@ -26,7 +26,7 @@ use Doctrine\DBAL\Cache\QueryCacheProfile; * Base class for SQL statement executors. * * @author Roman Borschel - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://www.doctrine-project.org * @since 2.0 * @todo Rename: AbstractSQLExecutor diff --git a/lib/Doctrine/ORM/Query/Exec/MultiTableDeleteExecutor.php b/lib/Doctrine/ORM/Query/Exec/MultiTableDeleteExecutor.php index 13af55905..e110d13e1 100644 --- a/lib/Doctrine/ORM/Query/Exec/MultiTableDeleteExecutor.php +++ b/lib/Doctrine/ORM/Query/Exec/MultiTableDeleteExecutor.php @@ -27,7 +27,7 @@ use Doctrine\ORM\Query\AST; * Class Table Inheritance (JOINED). * * @author Roman Borschel - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://www.doctrine-project.org * @since 2.0 */ diff --git a/lib/Doctrine/ORM/Query/Exec/SingleSelectExecutor.php b/lib/Doctrine/ORM/Query/Exec/SingleSelectExecutor.php index f652fbe8c..91827ab1a 100644 --- a/lib/Doctrine/ORM/Query/Exec/SingleSelectExecutor.php +++ b/lib/Doctrine/ORM/Query/Exec/SingleSelectExecutor.php @@ -26,7 +26,7 @@ use Doctrine\ORM\Query\SqlWalker; /** * Executor that executes the SQL statement for simple DQL SELECT statements. * - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @license http://www.opensource.org/licenses/mit-license.php MIT * @author Roman Borschel * @link www.doctrine-project.org * @since 2.0 diff --git a/lib/Doctrine/ORM/Query/Exec/SingleTableDeleteUpdateExecutor.php b/lib/Doctrine/ORM/Query/Exec/SingleTableDeleteUpdateExecutor.php index 695176d5b..e0183dd68 100644 --- a/lib/Doctrine/ORM/Query/Exec/SingleTableDeleteUpdateExecutor.php +++ b/lib/Doctrine/ORM/Query/Exec/SingleTableDeleteUpdateExecutor.php @@ -26,7 +26,7 @@ use Doctrine\ORM\Query\AST; * Executor that executes the SQL statements for DQL DELETE/UPDATE statements on classes * that are mapped to a single table. * - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @license http://www.opensource.org/licenses/mit-license.php MIT * @author Roman Borschel * @link www.doctrine-project.org * @since 2.0 diff --git a/lib/Doctrine/ORM/Query/ParserResult.php b/lib/Doctrine/ORM/Query/ParserResult.php index dfd7dd2c9..dfc7361f8 100644 --- a/lib/Doctrine/ORM/Query/ParserResult.php +++ b/lib/Doctrine/ORM/Query/ParserResult.php @@ -26,7 +26,7 @@ namespace Doctrine\ORM\Query; * @author Guilherme Blanco * @author Janne Vanhala * @author Roman Borschel - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://www.doctrine-project.org * @since 2.0 */ diff --git a/lib/Doctrine/ORM/Query/Printer.php b/lib/Doctrine/ORM/Query/Printer.php index ffb457538..d92ad850f 100644 --- a/lib/Doctrine/ORM/Query/Printer.php +++ b/lib/Doctrine/ORM/Query/Printer.php @@ -23,7 +23,7 @@ namespace Doctrine\ORM\Query; * A parse tree printer for Doctrine Query Language parser. * * @author Janne Vanhala - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @license http://www.opensource.org/licenses/mit-license.php MIT * @link http://www.phpdoctrine.org * @since 2.0 */ diff --git a/lib/Doctrine/ORM/Tools/Console/Command/ValidateSchemaCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/ValidateSchemaCommand.php index 4bffbf76e..37a8e3f76 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/ValidateSchemaCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/ValidateSchemaCommand.php @@ -27,7 +27,7 @@ use Doctrine\ORM\Tools\SchemaValidator; /** * Command to validate that the current mapping is valid. * - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @license http://www.opensource.org/licenses/mit-license.php MIT * @link www.doctrine-project.com * @since 1.0 * @author Benjamin Eberlei diff --git a/lib/Doctrine/ORM/Tools/Console/MetadataFilter.php b/lib/Doctrine/ORM/Tools/Console/MetadataFilter.php index 4f7813c1f..6d691dc06 100644 --- a/lib/Doctrine/ORM/Tools/Console/MetadataFilter.php +++ b/lib/Doctrine/ORM/Tools/Console/MetadataFilter.php @@ -22,7 +22,7 @@ namespace Doctrine\ORM\Tools\Console; /** * Used by CLI Tools to restrict entity-based commands to given patterns. * - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @license http://www.opensource.org/licenses/mit-license.php MIT * @link www.doctrine-project.com * @since 1.0 * @author Benjamin Eberlei diff --git a/lib/Doctrine/ORM/Tools/Event/GenerateSchemaEventArgs.php b/lib/Doctrine/ORM/Tools/Event/GenerateSchemaEventArgs.php index 36455621c..b8a7b20af 100644 --- a/lib/Doctrine/ORM/Tools/Event/GenerateSchemaEventArgs.php +++ b/lib/Doctrine/ORM/Tools/Event/GenerateSchemaEventArgs.php @@ -26,7 +26,7 @@ use Doctrine\ORM\EntityManager; /** * Event Args used for the Events::postGenerateSchema event. * - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @license http://www.opensource.org/licenses/mit-license.php MIT * @link www.doctrine-project.com * @since 1.0 * @author Benjamin Eberlei diff --git a/lib/Doctrine/ORM/Tools/Event/GenerateSchemaTableEventArgs.php b/lib/Doctrine/ORM/Tools/Event/GenerateSchemaTableEventArgs.php index 9e68ae6e9..e2c38f9c5 100644 --- a/lib/Doctrine/ORM/Tools/Event/GenerateSchemaTableEventArgs.php +++ b/lib/Doctrine/ORM/Tools/Event/GenerateSchemaTableEventArgs.php @@ -26,7 +26,7 @@ use Doctrine\DBAL\Schema\Table; /** * Event Args used for the Events::postGenerateSchemaTable event. * - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @license http://www.opensource.org/licenses/mit-license.php MIT * @link www.doctrine-project.com * @since 1.0 * @author Benjamin Eberlei diff --git a/lib/Doctrine/ORM/Tools/SchemaValidator.php b/lib/Doctrine/ORM/Tools/SchemaValidator.php index 889297ce8..11910138e 100644 --- a/lib/Doctrine/ORM/Tools/SchemaValidator.php +++ b/lib/Doctrine/ORM/Tools/SchemaValidator.php @@ -26,7 +26,7 @@ use Doctrine\DBAL\Types\Type; /** * Performs strict validation of the mapping schema * - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @license http://www.opensource.org/licenses/mit-license.php MIT * @link www.doctrine-project.com * @since 1.0 * @author Benjamin Eberlei diff --git a/lib/Doctrine/ORM/TransactionRequiredException.php b/lib/Doctrine/ORM/TransactionRequiredException.php index 2242e60fb..c3417b674 100644 --- a/lib/Doctrine/ORM/TransactionRequiredException.php +++ b/lib/Doctrine/ORM/TransactionRequiredException.php @@ -22,7 +22,7 @@ namespace Doctrine\ORM; /** * Is thrown when a transaction is required for the current operation, but there is none open. * - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @license http://www.opensource.org/licenses/mit-license.php MIT * @link www.doctrine-project.com * @since 1.0 * @author Benjamin Eberlei From 62ae8dc81e0ed488adb55b20431a94918a23ad3e Mon Sep 17 00:00:00 2001 From: flip111 Date: Wed, 2 Oct 2013 14:12:35 +0200 Subject: [PATCH 311/332] Update SingleScalarHydrator error message bummed into this one. Now more developer friendly :) --- lib/Doctrine/ORM/Internal/Hydration/SingleScalarHydrator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Internal/Hydration/SingleScalarHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/SingleScalarHydrator.php index b94724176..297aa98a0 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/SingleScalarHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/SingleScalarHydrator.php @@ -44,7 +44,7 @@ class SingleScalarHydrator extends AbstractHydrator } if ($numRows > 1 || count($data[key($data)]) > 1) { - throw new NonUniqueResultException(); + throw new NonUniqueResultException('The query returned multiple rows. Change the query or use a different result function like getScalarResult().'); } $cache = array(); From 33ddb9c0ca7dec99d33d67208a6f256f3488cbb1 Mon Sep 17 00:00:00 2001 From: Nicolas Bastien Date: Mon, 7 Oct 2013 15:58:41 +0200 Subject: [PATCH 312/332] Remove unused use statement --- lib/Doctrine/ORM/EntityManager.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/Doctrine/ORM/EntityManager.php b/lib/Doctrine/ORM/EntityManager.php index d21d3fb86..65ec9336d 100644 --- a/lib/Doctrine/ORM/EntityManager.php +++ b/lib/Doctrine/ORM/EntityManager.php @@ -21,11 +21,8 @@ namespace Doctrine\ORM; use Exception; use Doctrine\Common\EventManager; -use Doctrine\Common\Persistence\ObjectManager; use Doctrine\DBAL\Connection; use Doctrine\DBAL\LockMode; -use Doctrine\ORM\Mapping\ClassMetadata; -use Doctrine\ORM\Mapping\ClassMetadataFactory; use Doctrine\ORM\Query\ResultSetMapping; use Doctrine\ORM\Proxy\ProxyFactory; use Doctrine\ORM\Query\FilterCollection; From 089006927e1039bafdcc03f5d30cbfd47e7b8dd5 Mon Sep 17 00:00:00 2001 From: David Stensland Date: Thu, 5 Sep 2013 17:10:15 -0400 Subject: [PATCH 313/332] [DDC-2655] Don't let getOneOrNullResult throw NoResultException --- lib/Doctrine/ORM/AbstractQuery.php | 7 +++++- .../ORM/Functional/Ticket/DDC2655Test.php | 23 +++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2655Test.php diff --git a/lib/Doctrine/ORM/AbstractQuery.php b/lib/Doctrine/ORM/AbstractQuery.php index 07b6b9ea9..4c1f9ac64 100644 --- a/lib/Doctrine/ORM/AbstractQuery.php +++ b/lib/Doctrine/ORM/AbstractQuery.php @@ -609,7 +609,12 @@ abstract class AbstractQuery */ public function getOneOrNullResult($hydrationMode = null) { - $result = $this->execute(null, $hydrationMode); + try { + $result = $this->execute(null, $hydrationMode); + } catch (NoResultException $e) { + return null; + } + if ($this->_hydrationMode !== self::HYDRATE_SINGLE_SCALAR && ! $result) { return null; diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2655Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2655Test.php new file mode 100644 index 000000000..8a2dc166a --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2655Test.php @@ -0,0 +1,23 @@ +useModelSet('cms'); + parent::setUp(); + } + + public function testSingleScalarOneOrNullResult() + { + $query = $this->_em->createQuery("SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.username = 'happy_doctrine_user'"); + $this->assertNull($query->getOneOrNullResult(Query::HYDRATE_SINGLE_SCALAR)); + } +} From 006dac13a9084be230e97e71ea423945f10e2f38 Mon Sep 17 00:00:00 2001 From: Mark Fox Date: Fri, 11 Oct 2013 14:34:49 -0700 Subject: [PATCH 314/332] Removed "minimum-stability" : "dev" from composer.json --- docs/en/tutorials/getting-started.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/en/tutorials/getting-started.rst b/docs/en/tutorials/getting-started.rst index 02a07bc47..414e2ec3b 100644 --- a/docs/en/tutorials/getting-started.rst +++ b/docs/en/tutorials/getting-started.rst @@ -92,8 +92,7 @@ the following contents: }, "autoload": { "psr-0": {"": "src/"} - }, - "minimum-stability" : "dev" + } } From c5adfca0ed7dc5f84573d42b0d2129b0a4c1863e Mon Sep 17 00:00:00 2001 From: Mark Fox Date: Fri, 11 Oct 2013 15:16:46 -0700 Subject: [PATCH 315/332] Clarified tutorial context around introduction of `orm:schema-tool:*` commands See: http://stackoverflow.com/questions/17473225/doctrine2-no-metadata-classes-to-process for evidence of this confusion --- docs/en/tutorials/getting-started.rst | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/en/tutorials/getting-started.rst b/docs/en/tutorials/getting-started.rst index 4f169cd94..e756240d3 100644 --- a/docs/en/tutorials/getting-started.rst +++ b/docs/en/tutorials/getting-started.rst @@ -197,9 +197,12 @@ Doctrine command-line tool: $ cd project/ $ php vendor/bin/doctrine orm:schema-tool:create -During the development you probably need to re-create the database -several times when changing the Entity metadata. You can then -either re-create the database: +At this point no entitiy metadata exists in `src` so you will see a message like +"No Metadata Classes to process." — we'll add a Product entity and metadata soon. + +You should be aware that during the development process you'll need to keep +the database schema in sync with changes to your Entities metadata. +You can easily recreate the database: :: From 126cd0bac2f6b72302757412a68b4845060d643a Mon Sep 17 00:00:00 2001 From: Mark Fox Date: Fri, 11 Oct 2013 15:20:53 -0700 Subject: [PATCH 316/332] further tweaking of last commit --- docs/en/tutorials/getting-started.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/en/tutorials/getting-started.rst b/docs/en/tutorials/getting-started.rst index e756240d3..174528197 100644 --- a/docs/en/tutorials/getting-started.rst +++ b/docs/en/tutorials/getting-started.rst @@ -198,10 +198,12 @@ Doctrine command-line tool: $ php vendor/bin/doctrine orm:schema-tool:create At this point no entitiy metadata exists in `src` so you will see a message like -"No Metadata Classes to process." — we'll add a Product entity and metadata soon. +"No Metadata Classes to process." Don't worry, we'll create a Product entity and +corresponding metadata in the next section. + +You should be aware that during the development process you'll periodically want +to synchronize your database schema with your Entities metadata. -You should be aware that during the development process you'll need to keep -the database schema in sync with changes to your Entities metadata. You can easily recreate the database: :: From 2f8b0801cc2746fc59b058661b44bb0f48a511b8 Mon Sep 17 00:00:00 2001 From: Mark Fox Date: Fri, 11 Oct 2013 15:22:39 -0700 Subject: [PATCH 317/332] further tweaking of last commit pt.2 --- docs/en/tutorials/getting-started.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/en/tutorials/getting-started.rst b/docs/en/tutorials/getting-started.rst index 174528197..5b57a477d 100644 --- a/docs/en/tutorials/getting-started.rst +++ b/docs/en/tutorials/getting-started.rst @@ -201,8 +201,8 @@ At this point no entitiy metadata exists in `src` so you will see a message like "No Metadata Classes to process." Don't worry, we'll create a Product entity and corresponding metadata in the next section. -You should be aware that during the development process you'll periodically want -to synchronize your database schema with your Entities metadata. +You should be aware that during the development process you'll periodically need +to update your database schema to be in sync with your Entities metadata. You can easily recreate the database: From 5506d7adce0cc75c38b42280b2bf524046d5e56e Mon Sep 17 00:00:00 2001 From: Vincent BOURDEIX Date: Mon, 14 Oct 2013 15:04:52 +0200 Subject: [PATCH 318/332] Fixes a Fatal Error when using a subexpression in parenthesis When some dql contains a subselect with expression in parenthesis, the expression of the $simpleSelectExpression parameter given to walkSimpleSelectExpression is an instance of AST\ParenthesisExpression. Before this commit, this case defaulted to $this->walkEntityIdentificationVariable($expr) where $expr is supposed to be a string. A fatal error was then yielded. --- lib/Doctrine/ORM/Query/SqlWalker.php | 4 ++++ tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/lib/Doctrine/ORM/Query/SqlWalker.php b/lib/Doctrine/ORM/Query/SqlWalker.php index fb6177919..37f970843 100644 --- a/lib/Doctrine/ORM/Query/SqlWalker.php +++ b/lib/Doctrine/ORM/Query/SqlWalker.php @@ -1581,6 +1581,10 @@ class SqlWalker implements TreeWalker $sql .= $expr->dispatch($this) . ' AS ' . $columnAlias; break; + case ($expr instanceof AST\ParenthesisExpression): + $sql .= $this->walkParenthesisExpression($expr); + break; + default: // IdentificationVariable $sql .= $this->walkEntityIdentificationVariable($expr); break; diff --git a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php index 19624e1b8..f0afcc798 100644 --- a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php +++ b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php @@ -1641,6 +1641,13 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase ); } + public function testSupportsParenthesisExpressionInSubSelect() { + $this->assertSqlGeneration( + 'SELECT u.id, (SELECT (1000*SUM(subU.id)/SUM(subU.id)) FROM Doctrine\Tests\Models\CMS\CmsUser subU where subU.id = u.id) AS subSelect FROM Doctrine\Tests\Models\CMS\CmsUser u', + 'SELECT c0_.id AS id0, (SELECT (1000 * SUM(c1_.id) / SUM(c1_.id)) FROM cms_users c1_ WHERE c1_.id = c0_.id) AS sclr1 FROM cms_users c0_' + ); + } + /** * @group DDC-1557 */ From a90311cb446a0c517c17cf150196d2ba3230808b Mon Sep 17 00:00:00 2001 From: Matthieu Napoli Date: Thu, 17 Oct 2013 15:53:45 +0200 Subject: [PATCH 319/332] Fixed documentation rendering The 2 lists I've fixed render as citation in the docs: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/index.html If I didn't make any mistake, now they should render as simple lists. --- docs/en/index.rst | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/docs/en/index.rst b/docs/en/index.rst index 72cac5727..58753eb25 100644 --- a/docs/en/index.rst +++ b/docs/en/index.rst @@ -65,29 +65,29 @@ Working with Objects Advanced Topics --------------- - * :doc:`Architecture ` - * :doc:`Advanced Configuration ` - * :doc:`Limitations and knowns issues ` - * :doc:`Commandline Tools ` - * :doc:`Transactions and Concurrency ` - * :doc:`Filters ` - * :doc:`NamingStrategy ` - * :doc:`Improving Performance ` - * :doc:`Caching ` - * :doc:`Partial Objects ` - * :doc:`Change Tracking Policies ` - * :doc:`Best Practices ` - * :doc:`Metadata Drivers ` +* :doc:`Architecture ` +* :doc:`Advanced Configuration ` +* :doc:`Limitations and knowns issues ` +* :doc:`Commandline Tools ` +* :doc:`Transactions and Concurrency ` +* :doc:`Filters ` +* :doc:`NamingStrategy ` +* :doc:`Improving Performance ` +* :doc:`Caching ` +* :doc:`Partial Objects ` +* :doc:`Change Tracking Policies ` +* :doc:`Best Practices ` +* :doc:`Metadata Drivers ` Tutorials --------- - * :doc:`Indexed associations ` - * :doc:`Extra Lazy Associations ` - * :doc:`Composite Primary Keys ` - * :doc:`Ordered associations ` - * :doc:`Pagination ` - * :doc:`Override Field/Association Mappings In Subclasses ` +* :doc:`Indexed associations ` +* :doc:`Extra Lazy Associations ` +* :doc:`Composite Primary Keys ` +* :doc:`Ordered associations ` +* :doc:`Pagination ` +* :doc:`Override Field/Association Mappings In Subclasses ` Cookbook -------- From 84b980227f8c01b0af1303424d9157d4ea606b52 Mon Sep 17 00:00:00 2001 From: Brikou CARRE Date: Mon, 21 Oct 2013 09:44:48 +0200 Subject: [PATCH 320/332] s/PostgreSQLPlatform/PostgreSqlPlatform/ --- lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php index 9320d1b6d..63c36474a 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php @@ -450,7 +450,7 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory $sequenceName = null; $fieldName = $class->identifier ? $class->getSingleIdentifierFieldName() : null; - if ($this->targetPlatform instanceof Platforms\PostgreSQLPlatform) { + if ($this->targetPlatform instanceof Platforms\PostgreSqlPlatform) { $columnName = $class->getSingleIdentifierColumnName(); $quoted = isset($class->fieldMappings[$fieldName]['quoted']) || isset($class->table['quoted']); $sequenceName = $class->getTableName() . '_' . $columnName . '_seq'; From 195b1169aeb6ddf8f9e6ea0fcbd4662b005561ea Mon Sep 17 00:00:00 2001 From: flack Date: Wed, 23 Oct 2013 11:32:04 +0200 Subject: [PATCH 321/332] Spelling fix --- docs/en/reference/unitofwork.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/reference/unitofwork.rst b/docs/en/reference/unitofwork.rst index f01c3f91d..cdfb6e0be 100644 --- a/docs/en/reference/unitofwork.rst +++ b/docs/en/reference/unitofwork.rst @@ -157,7 +157,7 @@ wishes to be hydrated. Default result-types include: - SQL to a single result variable Hydration to entities and arrays is one of most complex parts of Doctrine -algorithm-wise. It can built results with for example: +algorithm-wise. It can build results with for example: - Single table selects - Joins with n:1 or 1:n cardinality, grouping belonging to the same parent. From 7249804e3fbdac1d8e57851600d3f34d43a5828e Mon Sep 17 00:00:00 2001 From: Chris Collins Date: Thu, 24 Oct 2013 13:02:42 +0100 Subject: [PATCH 322/332] Added a failing test case for DDC-2759. --- .../ORM/Functional/Ticket/DDC2759Test.php | 121 ++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2759Test.php diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2759Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2759Test.php new file mode 100644 index 000000000..a071d4c0c --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2759Test.php @@ -0,0 +1,121 @@ +_schemaTool->createSchema(array( + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2759Qualification'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2759Category'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2759QualificationMetadata'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2759MetadataCategory'), + )); + } catch(\Exception $e) { + return; + } + + $qualification = new DDC2759Qualification(); + $qualificationMetadata = new DDC2759QualificationMetadata($qualification); + + $category1 = new DDC2759Category(); + $category2 = new DDC2759Category(); + + $metadataCategory1 = new DDC2759MetadataCategory($qualificationMetadata, $category1); + $metadataCategory2 = new DDC2759MetadataCategory($qualificationMetadata, $category2); + + $this->_em->persist($qualification); + $this->_em->persist($qualificationMetadata); + + $this->_em->persist($category1); + $this->_em->persist($category2); + + $this->_em->persist($metadataCategory1); + $this->_em->persist($metadataCategory2); + + $this->_em->flush(); + $this->_em->clear(); + } + + public function testCorrectNumberOfAssociationsIsReturned() + { + $repository = $this->_em->getRepository(__NAMESPACE__ . '\DDC2759Qualification'); + + $builder = $repository->createQueryBuilder('q') + ->select('q, qm, qmc') + ->innerJoin('q.metadata', 'qm') + ->innerJoin('qm.metadataCategories', 'qmc'); + + $result = $builder->getQuery() + ->getArrayResult(); + + $this->assertCount(2, $result[0]['metadata']['metadataCategories']); + } +} + +/** @Entity @Table(name="ddc_2759_qualification") */ +class DDC2759Qualification +{ + /** @Id @Column(type="integer") @GeneratedValue */ + public $id; + + /** @OneToOne(targetEntity="DDC2759QualificationMetadata", mappedBy="content") */ + public $metadata; +} + +/** @Entity @Table(name="ddc_2759_category") */ +class DDC2759Category +{ + /** @Id @Column(type="integer") @GeneratedValue */ + public $id; + + /** @OneToMany(targetEntity="DDC2759MetadataCategory", mappedBy="category") */ + public $metadataCategories; +} + +/** @Entity @Table(name="ddc_2759_qualification_metadata") */ +class DDC2759QualificationMetadata +{ + /** @Id @Column(type="integer") @GeneratedValue */ + public $id; + + /** @OneToOne(targetEntity="DDC2759Qualification", inversedBy="metadata") */ + public $content; + + /** @OneToMany(targetEntity="DDC2759MetadataCategory", mappedBy="metadata") */ + protected $metadataCategories; + + public function __construct(DDC2759Qualification $content) + { + $this->content = $content; + } +} + +/** @Entity @Table(name="ddc_2759_metadata_category") */ +class DDC2759MetadataCategory +{ + /** @Id @Column(type="integer") @GeneratedValue */ + public $id; + + /** @ManyToOne(targetEntity="DDC2759QualificationMetadata", inversedBy="metadataCategories") */ + public $metadata; + + /** @ManyToOne(targetEntity="DDC2759Category", inversedBy="metadataCategories") */ + public $category; + + public function __construct(DDC2759QualificationMetadata $metadata, DDC2759Category $category) + { + $this->metadata = $metadata; + $this->category = $category; + } +} From 9a3298347c5319eed73805d13ca4f75d87736b9c Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sat, 26 Oct 2013 11:16:07 +0200 Subject: [PATCH 323/332] [DDC-2759] Fix regression in ArrayHydrator introduced in DDC-1884 at SHA c7b4c9bf0fbc5dca830e0a797cea470510def994 --- lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php index 5688d8652..c336e7a5d 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php @@ -190,17 +190,14 @@ class ArrayHydrator extends AbstractHydrator ( ! isset($baseElement[$relationAlias])) ) { $baseElement[$relationAlias] = null; - } else if ( - ( ! isset($baseElement[$relationAlias])) || - ( ! isset($this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]])) - ) { + } else if ( ! isset($baseElement[$relationAlias])) { $baseElement[$relationAlias] = $data; } } $coll =& $baseElement[$relationAlias]; - if ($coll !== null) { + if (is_array($coll)) { $this->updateResultPointer($coll, $index, $dqlAlias, $oneToOne); } } else { From 514dd4e8528c47c47d97765ac041ce91d8147a36 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sat, 26 Oct 2013 12:32:53 +0200 Subject: [PATCH 324/332] [DDC-2757] Update documentation with regard to manually closing the EntityManager when an Exception occurs. --- .../transactions-and-concurrency.rst | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/docs/en/reference/transactions-and-concurrency.rst b/docs/en/reference/transactions-and-concurrency.rst index 1b06156e9..4dc18318e 100644 --- a/docs/en/reference/transactions-and-concurrency.rst +++ b/docs/en/reference/transactions-and-concurrency.rst @@ -70,7 +70,6 @@ looks like this: $em->getConnection()->commit(); } catch (Exception $e) { $em->getConnection()->rollback(); - $em->close(); throw $e; } @@ -81,14 +80,12 @@ require an active transaction. Such methods will throw a ``TransactionRequiredException`` to inform you of that requirement. -A more convenient alternative for explicit transaction demarcation -is the use of provided control abstractions in the form of -``Connection#transactional($func)`` and -``EntityManager#transactional($func)``. When used, these control -abstractions ensure that you never forget to rollback the -transaction or close the ``EntityManager``, apart from the obvious -code reduction. An example that is functionally equivalent to the -previously shown code looks as follows: +A more convenient alternative for explicit transaction demarcation is the use +of provided control abstractions in the form of +``Connection#transactional($func)`` and ``EntityManager#transactional($func)``. +When used, these control abstractions ensure that you never forget to rollback +the transaction, in addition to the obvious code reduction. An example that is +functionally equivalent to the previously shown code looks as follows: .. code-block:: php @@ -104,8 +101,8 @@ previously shown code looks as follows: The difference between ``Connection#transactional($func)`` and ``EntityManager#transactional($func)`` is that the latter abstraction flushes the ``EntityManager`` prior to transaction -commit and also closes the ``EntityManager`` properly when an -exception occurs (in addition to rolling back the transaction). +commit and rolls back the transaction when an +exception occurs. Exception Handling ~~~~~~~~~~~~~~~~~~ From c67ac8a11b56fbc37eee7a475c011548c6c63487 Mon Sep 17 00:00:00 2001 From: "Johannes M. Schmitt" Date: Fri, 1 Nov 2013 20:38:19 +0100 Subject: [PATCH 325/332] adds support for selecting based on embedded fields --- lib/Doctrine/ORM/Query/Parser.php | 8 ++++- .../Tests/ORM/Functional/ValueObjectsTest.php | 31 +++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index 5c721a1d5..912138208 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -1049,7 +1049,7 @@ class Parser * Parses an arbitrary path expression and defers semantical validation * based on expected types. * - * PathExpression ::= IdentificationVariable "." identifier + * PathExpression ::= IdentificationVariable "." identifier [ ("." identifier)* ] * * @param integer $expectedTypes * @@ -1065,6 +1065,12 @@ class Parser $this->match(Lexer::T_IDENTIFIER); $field = $this->lexer->token['value']; + + while ($this->lexer->isNextToken(Lexer::T_DOT)) { + $this->match(Lexer::T_DOT); + $this->match(Lexer::T_IDENTIFIER); + $field .= '.'.$this->lexer->token['value']; + } } // Creating AST node diff --git a/tests/Doctrine/Tests/ORM/Functional/ValueObjectsTest.php b/tests/Doctrine/Tests/ORM/Functional/ValueObjectsTest.php index 89a51a406..a309acdb1 100644 --- a/tests/Doctrine/Tests/ORM/Functional/ValueObjectsTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/ValueObjectsTest.php @@ -101,6 +101,24 @@ class ValueObjectsTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertEquals('funkytown', $person['address.city']); } } + + /** + * @group dql + */ + public function testDqlOnEmbeddedObjectsField() + { + $person = new DDC93Person('Johannes', new DDC93Address('Moo', '12345', 'Karlsruhe')); + $this->_em->persist($person); + $this->_em->flush($person); + + $dql = "SELECT p FROM " . __NAMESPACE__ ."\\DDC93Person p WHERE p.address.city = :city"; + $loadedPerson = $this->_em->createQuery($dql) + ->setParameter('city', 'Karlsruhe') + ->getSingleResult(); + $this->assertEquals($person, $loadedPerson); + + $this->assertNull($this->_em->createQuery($dql)->setParameter('city', 'asdf')->getOneOrNullResult()); + } } /** @@ -118,6 +136,12 @@ class DDC93Person /** @Embedded(class="DDC93Address") */ public $address; + + public function __construct($name = null, DDC93Address $address = null) + { + $this->name = $name; + $this->address = $address; + } } /** @@ -139,5 +163,12 @@ class DDC93Address * @Column(type="string") */ public $city; + + public function __construct($street = null, $zip = null, $city = null) + { + $this->street = $street; + $this->zip = $zip; + $this->city = $city; + } } From 30897c311593a9e51200d7d5bffc8aa6bdcb81b0 Mon Sep 17 00:00:00 2001 From: "Johannes M. Schmitt" Date: Fri, 1 Nov 2013 20:46:08 +0100 Subject: [PATCH 326/332] adds tests for update/delete DQL queries --- .../Tests/ORM/Functional/ValueObjectsTest.php | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/tests/Doctrine/Tests/ORM/Functional/ValueObjectsTest.php b/tests/Doctrine/Tests/ORM/Functional/ValueObjectsTest.php index a309acdb1..9386d1885 100644 --- a/tests/Doctrine/Tests/ORM/Functional/ValueObjectsTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/ValueObjectsTest.php @@ -111,13 +111,32 @@ class ValueObjectsTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->_em->persist($person); $this->_em->flush($person); - $dql = "SELECT p FROM " . __NAMESPACE__ ."\\DDC93Person p WHERE p.address.city = :city"; - $loadedPerson = $this->_em->createQuery($dql) + // SELECT + $selectDql = "SELECT p FROM " . __NAMESPACE__ ."\\DDC93Person p WHERE p.address.city = :city"; + $loadedPerson = $this->_em->createQuery($selectDql) ->setParameter('city', 'Karlsruhe') ->getSingleResult(); $this->assertEquals($person, $loadedPerson); - $this->assertNull($this->_em->createQuery($dql)->setParameter('city', 'asdf')->getOneOrNullResult()); + $this->assertNull($this->_em->createQuery($selectDql)->setParameter('city', 'asdf')->getOneOrNullResult()); + + // UPDATE + $updateDql = "UPDATE " . __NAMESPACE__ . "\\DDC93Person p SET p.address.street = :street WHERE p.address.city = :city"; + $this->_em->createQuery($updateDql) + ->setParameter('street', 'Boo') + ->setParameter('city', 'Karlsruhe') + ->execute(); + + $this->_em->refresh($person); + $this->assertEquals('Boo', $person->address->street); + + // DELETE + $this->_em->createQuery("DELETE " . __NAMESPACE__ . "\\DDC93Person p WHERE p.address.city = :city") + ->setParameter('city', 'Karlsruhe') + ->execute(); + + $this->_em->clear(); + $this->assertNull($this->_em->find(__NAMESPACE__.'\\DDC93Person', $person->id)); } } From 41c937b983027329a55bcfbeb6ce1fbc0a8e6edf Mon Sep 17 00:00:00 2001 From: "Johannes M. Schmitt" Date: Fri, 1 Nov 2013 20:54:45 +0100 Subject: [PATCH 327/332] adds test for non-existent field --- .../Doctrine/Tests/ORM/Functional/ValueObjectsTest.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/Doctrine/Tests/ORM/Functional/ValueObjectsTest.php b/tests/Doctrine/Tests/ORM/Functional/ValueObjectsTest.php index 9386d1885..3ccfd05bb 100644 --- a/tests/Doctrine/Tests/ORM/Functional/ValueObjectsTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/ValueObjectsTest.php @@ -138,6 +138,16 @@ class ValueObjectsTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->_em->clear(); $this->assertNull($this->_em->find(__NAMESPACE__.'\\DDC93Person', $person->id)); } + + /** + * @expectedException Doctrine\ORM\Query\QueryException + * @expectedExceptionMessage no field or association named address.asdfasdf + */ + public function testDqlWithNonExistentEmbeddableField() + { + $this->_em->createQuery("SELECT p FROM " . __NAMESPACE__ . "\\DDC93Person p WHERE p.address.asdfasdf IS NULL") + ->execute(); + } } /** From fd8b5bd045d3c8d8669893f1a652ddab2130db41 Mon Sep 17 00:00:00 2001 From: "Johannes M. Schmitt" Date: Fri, 1 Nov 2013 21:16:02 +0100 Subject: [PATCH 328/332] removes outdated todos --- lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php index 510d46672..03dc3cb15 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php @@ -3093,8 +3093,8 @@ class ClassMetadataInfo implements ClassMetadata foreach ($embeddable->fieldMappings as $fieldMapping) { $fieldMapping['declaredField'] = $property; $fieldMapping['originalField'] = $fieldMapping['fieldName']; - $fieldMapping['fieldName'] = $property . "." . $fieldMapping['fieldName']; // TODO: Change DQL parser to accept this dot notation - $fieldMapping['columnName'] = $property . "_" . $fieldMapping['columnName']; // TODO: Use naming strategy + $fieldMapping['fieldName'] = $property . "." . $fieldMapping['fieldName']; + $fieldMapping['columnName'] = $property . "_" . $fieldMapping['columnName']; $this->mapField($fieldMapping); } From 20fb8270dc3c84eceaf90979644fdfbe0b2e615e Mon Sep 17 00:00:00 2001 From: "Johannes M. Schmitt" Date: Fri, 1 Nov 2013 21:44:57 +0100 Subject: [PATCH 329/332] make use of NamingStrategy for columns of embedded fields --- UPGRADE.md | 8 ++++++++ lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php | 2 +- lib/Doctrine/ORM/Mapping/DefaultNamingStrategy.php | 8 ++++++++ lib/Doctrine/ORM/Mapping/NamingStrategy.php | 10 ++++++++++ lib/Doctrine/ORM/Mapping/UnderscoreNamingStrategy.php | 8 ++++++++ 5 files changed, 35 insertions(+), 1 deletion(-) diff --git a/UPGRADE.md b/UPGRADE.md index b864d8614..224967b87 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -1,3 +1,11 @@ +# Upgrade to 2.5 + +## BC BREAK: NamingStrategy has a new method ``embeddedFieldToColumnName($propertyName, $embeddedColumnName)`` + +This method generates the column name for fields of embedded objects. If you implement your custom NamingStrategy, you +now also need to implement this new method. + + # Upgrade to 2.4 ## BC BREAK: Compatibility Bugfix in PersistentCollection#matching() diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php index 03dc3cb15..c72f884e9 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php @@ -3094,7 +3094,7 @@ class ClassMetadataInfo implements ClassMetadata $fieldMapping['declaredField'] = $property; $fieldMapping['originalField'] = $fieldMapping['fieldName']; $fieldMapping['fieldName'] = $property . "." . $fieldMapping['fieldName']; - $fieldMapping['columnName'] = $property . "_" . $fieldMapping['columnName']; + $fieldMapping['columnName'] = $this->namingStrategy->embeddedFieldToColumnName($property, $fieldMapping['columnName']); $this->mapField($fieldMapping); } diff --git a/lib/Doctrine/ORM/Mapping/DefaultNamingStrategy.php b/lib/Doctrine/ORM/Mapping/DefaultNamingStrategy.php index 2433b4afa..22e51674e 100644 --- a/lib/Doctrine/ORM/Mapping/DefaultNamingStrategy.php +++ b/lib/Doctrine/ORM/Mapping/DefaultNamingStrategy.php @@ -50,6 +50,14 @@ class DefaultNamingStrategy implements NamingStrategy return $propertyName; } + /** + * {@inheritdoc} + */ + public function embeddedFieldToColumnName($propertyName, $embeddedColumnName) + { + return $propertyName.ucfirst($embeddedColumnName); + } + /** * {@inheritdoc} */ diff --git a/lib/Doctrine/ORM/Mapping/NamingStrategy.php b/lib/Doctrine/ORM/Mapping/NamingStrategy.php index fc66905c5..b48e95069 100644 --- a/lib/Doctrine/ORM/Mapping/NamingStrategy.php +++ b/lib/Doctrine/ORM/Mapping/NamingStrategy.php @@ -49,6 +49,16 @@ interface NamingStrategy */ function propertyToColumnName($propertyName, $className = null); + /** + * Returns a column name for an embedded property. + * + * @param string $propertyName + * @param string $embeddedColumnName + * + * @return string + */ + function embeddedFieldToColumnName($propertyName, $embeddedColumnName); + /** * Returns the default reference column name. * diff --git a/lib/Doctrine/ORM/Mapping/UnderscoreNamingStrategy.php b/lib/Doctrine/ORM/Mapping/UnderscoreNamingStrategy.php index 5231aaafc..e301d958b 100644 --- a/lib/Doctrine/ORM/Mapping/UnderscoreNamingStrategy.php +++ b/lib/Doctrine/ORM/Mapping/UnderscoreNamingStrategy.php @@ -87,6 +87,14 @@ class UnderscoreNamingStrategy implements NamingStrategy return $this->underscore($propertyName); } + /** + * {@inheritdoc} + */ + public function embeddedFieldToColumnName($propertyName, $embeddedColumnName) + { + return $this->underscore($propertyName).'_'.$embeddedColumnName; + } + /** * {@inheritdoc} */ From 4f6c15099a6e47b275d409c4ce17f5a35e73dd4f Mon Sep 17 00:00:00 2001 From: "Johannes M. Schmitt" Date: Fri, 1 Nov 2013 21:47:56 +0100 Subject: [PATCH 330/332] fixes coding style --- tests/Doctrine/Tests/ORM/Functional/ValueObjectsTest.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/Doctrine/Tests/ORM/Functional/ValueObjectsTest.php b/tests/Doctrine/Tests/ORM/Functional/ValueObjectsTest.php index 3ccfd05bb..bc02268c0 100644 --- a/tests/Doctrine/Tests/ORM/Functional/ValueObjectsTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/ValueObjectsTest.php @@ -139,12 +139,10 @@ class ValueObjectsTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertNull($this->_em->find(__NAMESPACE__.'\\DDC93Person', $person->id)); } - /** - * @expectedException Doctrine\ORM\Query\QueryException - * @expectedExceptionMessage no field or association named address.asdfasdf - */ public function testDqlWithNonExistentEmbeddableField() { + $this->setExpectedException('Doctrine\ORM\Query\QueryException', 'no field or association named address.asdfasdf'); + $this->_em->createQuery("SELECT p FROM " . __NAMESPACE__ . "\\DDC93Person p WHERE p.address.asdfasdf IS NULL") ->execute(); } From f86abd81dd6d4eef31b685c9ffa9942831359c92 Mon Sep 17 00:00:00 2001 From: "Johannes M. Schmitt" Date: Fri, 1 Nov 2013 22:33:59 +0100 Subject: [PATCH 331/332] fixes annotation context --- lib/Doctrine/ORM/Mapping/Embeddable.php | 2 +- lib/Doctrine/ORM/Mapping/Embedded.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Doctrine/ORM/Mapping/Embeddable.php b/lib/Doctrine/ORM/Mapping/Embeddable.php index 34cdcd1f1..f14bfac82 100644 --- a/lib/Doctrine/ORM/Mapping/Embeddable.php +++ b/lib/Doctrine/ORM/Mapping/Embeddable.php @@ -21,7 +21,7 @@ namespace Doctrine\ORM\Mapping; /** * @Annotation - * @Target("PROPERTY") + * @Target("CLASS") */ final class Embeddable implements Annotation { diff --git a/lib/Doctrine/ORM/Mapping/Embedded.php b/lib/Doctrine/ORM/Mapping/Embedded.php index c3dcb0837..aa4d89356 100644 --- a/lib/Doctrine/ORM/Mapping/Embedded.php +++ b/lib/Doctrine/ORM/Mapping/Embedded.php @@ -21,7 +21,7 @@ namespace Doctrine\ORM\Mapping; /** * @Annotation - * @Target("CLASS") + * @Target("PROPERTY") */ final class Embedded implements Annotation { From 97836ef8c6e8ef9c906699d2b20b603b6a4ddd77 Mon Sep 17 00:00:00 2001 From: "Johannes M. Schmitt" Date: Fri, 1 Nov 2013 22:37:59 +0100 Subject: [PATCH 332/332] some consistency fixes --- lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php | 2 +- lib/Doctrine/ORM/Mapping/DefaultNamingStrategy.php | 4 ++-- lib/Doctrine/ORM/Mapping/NamingStrategy.php | 2 +- lib/Doctrine/ORM/Mapping/UnderscoreNamingStrategy.php | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php index c72f884e9..612574e90 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php @@ -3094,7 +3094,7 @@ class ClassMetadataInfo implements ClassMetadata $fieldMapping['declaredField'] = $property; $fieldMapping['originalField'] = $fieldMapping['fieldName']; $fieldMapping['fieldName'] = $property . "." . $fieldMapping['fieldName']; - $fieldMapping['columnName'] = $this->namingStrategy->embeddedFieldToColumnName($property, $fieldMapping['columnName']); + $fieldMapping['columnName'] = $this->namingStrategy->embeddedFieldToColumnName($property, $fieldMapping['columnName'], $this->reflClass->name, $embeddable->reflClass->name); $this->mapField($fieldMapping); } diff --git a/lib/Doctrine/ORM/Mapping/DefaultNamingStrategy.php b/lib/Doctrine/ORM/Mapping/DefaultNamingStrategy.php index 22e51674e..06bc593be 100644 --- a/lib/Doctrine/ORM/Mapping/DefaultNamingStrategy.php +++ b/lib/Doctrine/ORM/Mapping/DefaultNamingStrategy.php @@ -53,9 +53,9 @@ class DefaultNamingStrategy implements NamingStrategy /** * {@inheritdoc} */ - public function embeddedFieldToColumnName($propertyName, $embeddedColumnName) + public function embeddedFieldToColumnName($propertyName, $embeddedColumnName, $className = null, $embeddedClassName = null) { - return $propertyName.ucfirst($embeddedColumnName); + return $propertyName.'_'.$embeddedColumnName; } /** diff --git a/lib/Doctrine/ORM/Mapping/NamingStrategy.php b/lib/Doctrine/ORM/Mapping/NamingStrategy.php index b48e95069..94938ccf2 100644 --- a/lib/Doctrine/ORM/Mapping/NamingStrategy.php +++ b/lib/Doctrine/ORM/Mapping/NamingStrategy.php @@ -57,7 +57,7 @@ interface NamingStrategy * * @return string */ - function embeddedFieldToColumnName($propertyName, $embeddedColumnName); + function embeddedFieldToColumnName($propertyName, $embeddedColumnName, $className = null, $embeddedClassName = null); /** * Returns the default reference column name. diff --git a/lib/Doctrine/ORM/Mapping/UnderscoreNamingStrategy.php b/lib/Doctrine/ORM/Mapping/UnderscoreNamingStrategy.php index e301d958b..ec74373a8 100644 --- a/lib/Doctrine/ORM/Mapping/UnderscoreNamingStrategy.php +++ b/lib/Doctrine/ORM/Mapping/UnderscoreNamingStrategy.php @@ -90,7 +90,7 @@ class UnderscoreNamingStrategy implements NamingStrategy /** * {@inheritdoc} */ - public function embeddedFieldToColumnName($propertyName, $embeddedColumnName) + public function embeddedFieldToColumnName($propertyName, $embeddedColumnName, $className = null, $embeddedClassName = null) { return $this->underscore($propertyName).'_'.$embeddedColumnName; }