From f8b1915efd604bb9c2177a8fccb2d3248be796ec Mon Sep 17 00:00:00 2001 From: "Fabio B. Silva" Date: Sun, 11 Mar 2012 21:46:31 -0300 Subject: [PATCH] named native query inheritance --- .../ORM/Mapping/ClassMetadataFactory.php | 60 +++++++++++++++++++ .../ORM/Mapping/ClassMetadataInfo.php | 9 +++ .../Tests/Models/Company/CompanyContract.php | 42 +++++++++++++ .../Models/Company/CompanyFlexContract.php | 42 +++++++++++++ .../Tests/ORM/Functional/NativeQueryTest.php | 53 ++++++++++++++++ 5 files changed, 206 insertions(+) diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php index 2aaa30a44..63ac01c25 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php @@ -326,6 +326,14 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface $this->addInheritedNamedQueries($class, $parent); } + if ($parent && !empty ($parent->namedNativeQueries)) { + $this->addInheritedNamedNativeQueries($class, $parent); + } + + if ($parent && !empty ($parent->sqlResultSetMappings)) { + $this->addInheritedSqlResultSetMappings($class, $parent); + } + $class->setParentClasses($visited); if ($this->evm->hasListeners(Events::loadClassMetadata)) { @@ -466,6 +474,58 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface } } + /** + * Adds inherited named native queries to the subclass mapping. + * + * @since 2.3 + * @param \Doctrine\ORM\Mapping\ClassMetadata $subClass + * @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass + */ + private function addInheritedNamedNativeQueries(ClassMetadata $subClass, ClassMetadata $parentClass) + { + foreach ($parentClass->namedNativeQueries as $name => $query) { + if (!isset ($subClass->namedNativeQueries[$name])) { + $subClass->addNamedNativeQuery(array( + 'name' => $query['name'], + 'query' => $query['query'], + 'isSelfClass' => $query['isSelfClass'], + 'resultSetMapping' => $query['resultSetMapping'], + 'resultClass' => $query['isSelfClass'] ? $subClass->name : $query['resultClass'], + )); + } + } + } + + /** + * Adds inherited sql result set mappings to the subclass mapping. + * + * @since 2.3 + * @param \Doctrine\ORM\Mapping\ClassMetadata $subClass + * @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass + */ + private function addInheritedSqlResultSetMappings(ClassMetadata $subClass, ClassMetadata $parentClass) + { + foreach ($parentClass->sqlResultSetMappings as $name => $mapping) { + if (!isset ($subClass->sqlResultSetMappings[$name])) { + $entities = array(); + foreach ($mapping['entities'] as $entity) { + $entities[] = array( + 'fields' => $entity['fields'], + 'isSelfClass' => $entity['isSelfClass'], + 'discriminatorColumn' => $entity['discriminatorColumn'], + 'entityClass' => $entity['isSelfClass'] ? $subClass->name : $entity['entityClass'], + ); + } + + $subClass->addSqlResultSetMapping(array( + 'name' => $mapping['name'], + 'columns' => $mapping['columns'], + 'entities' => $entities, + )); + } + } + } + /** * Completes the ID generator mapping. If "auto" is specified we choose the generator * most appropriate for the targeted database platform. diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php index b518f0394..1f3b00dbf 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php @@ -1983,10 +1983,14 @@ class ClassMetadataInfo implements ClassMetadata throw MappingException::missingQueryMapping($this->name, $queryMapping['name']); } + $queryMapping['isSelfClass'] = false; if (isset($queryMapping['resultClass'])) { if($queryMapping['resultClass'] === '__CLASS__') { + + $queryMapping['isSelfClass'] = true; $queryMapping['resultClass'] = $this->name; + } else if (strlen($this->namespace) > 0 && strpos($queryMapping['resultClass'], '\\') === false) { $queryMapping['resultClass'] = $this->namespace . '\\' . $queryMapping['resultClass']; } @@ -2020,13 +2024,18 @@ class ClassMetadataInfo implements ClassMetadata throw MappingException::missingResultSetMappingEntity($this->name, $resultMapping['name']); } + $entityResult['isSelfClass'] = false; if($entityResult['entityClass'] === '__CLASS__') { + + $entityResult['isSelfClass'] = true; $entityResult['entityClass'] = $this->name; + } else if (strlen($this->namespace) > 0 && strpos($entityResult['entityClass'], '\\') === false) { $entityResult['entityClass'] = $this->namespace . '\\' . $entityResult['entityClass']; } $resultMapping['entities'][$key]['entityClass'] = ltrim($entityResult['entityClass'], '\\'); + $resultMapping['entities'][$key]['isSelfClass'] = $entityResult['isSelfClass']; if (isset($entityResult['fields'])) { foreach ($entityResult['fields'] as $k => $field) { diff --git a/tests/Doctrine/Tests/Models/Company/CompanyContract.php b/tests/Doctrine/Tests/Models/Company/CompanyContract.php index 7787e96be..bc8503dfe 100644 --- a/tests/Doctrine/Tests/Models/Company/CompanyContract.php +++ b/tests/Doctrine/Tests/Models/Company/CompanyContract.php @@ -12,6 +12,48 @@ namespace Doctrine\Tests\Models\Company; * "flexible" = "CompanyFlexContract", * "flexultra" = "CompanyFlexUltraContract" * }) + * + * @NamedNativeQueries({ + * @NamedNativeQuery( + * name = "all-contracts", + * resultClass = "__CLASS__", + * query = "SELECT id, completed, discr FROM company_contracts" + * ), + * @NamedNativeQuery( + * name = "all", + * resultClass = "__CLASS__", + * query = "SELECT id, completed, discr FROM company_contracts" + * ), + * }) + * + * @SqlResultSetMappings({ + * @SqlResultSetMapping( + * name = "mapping-all-contracts", + * entities= { + * @EntityResult( + * entityClass = "__CLASS__", + * discriminatorColumn = "discr", + * fields = { + * @FieldResult("id"), + * @FieldResult("completed"), + * } + * ) + * } + * ), + * @SqlResultSetMapping( + * name = "mapping-all", + * entities= { + * @EntityResult( + * entityClass = "__CLASS__", + * discriminatorColumn = "discr", + * fields = { + * @FieldResult("id"), + * @FieldResult("completed"), + * } + * ) + * } + * ), + * }) */ abstract class CompanyContract { diff --git a/tests/Doctrine/Tests/Models/Company/CompanyFlexContract.php b/tests/Doctrine/Tests/Models/Company/CompanyFlexContract.php index e32288897..121d8ec8e 100644 --- a/tests/Doctrine/Tests/Models/Company/CompanyFlexContract.php +++ b/tests/Doctrine/Tests/Models/Company/CompanyFlexContract.php @@ -3,6 +3,48 @@ namespace Doctrine\Tests\Models\Company; /** * @Entity + * + * @NamedNativeQueries({ + * @NamedNativeQuery( + * name = "all", + * resultClass = "__CLASS__", + * query = "SELECT id, hoursWorked, discr FROM company_contracts" + * ), + * @NamedNativeQuery( + * name = "all-flex", + * resultClass = "CompanyFlexContract", + * query = "SELECT id, hoursWorked, discr FROM company_contracts" + * ), + * }) + * + * @SqlResultSetMappings({ + * @SqlResultSetMapping( + * name = "mapping-all-flex", + * entities= { + * @EntityResult( + * entityClass = "__CLASS__", + * discriminatorColumn = "discr", + * fields = { + * @FieldResult("id"), + * @FieldResult("hoursWorked"), + * } + * ) + * } + * ), + * @SqlResultSetMapping( + * name = "mapping-all", + * entities= { + * @EntityResult( + * entityClass = "CompanyFlexContract", + * discriminatorColumn = "discr", + * fields = { + * @FieldResult("id"), + * @FieldResult("hoursWorked"), + * } + * ) + * } + * ), + * }) */ class CompanyFlexContract extends CompanyContract { diff --git a/tests/Doctrine/Tests/ORM/Functional/NativeQueryTest.php b/tests/Doctrine/Tests/ORM/Functional/NativeQueryTest.php index 0f3e6b533..a6a9016a4 100644 --- a/tests/Doctrine/Tests/ORM/Functional/NativeQueryTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/NativeQueryTest.php @@ -648,4 +648,57 @@ class NativeQueryTest extends \Doctrine\Tests\OrmFunctionalTestCase } + /** + * @group DDC-1663 + */ + public function testNamedNativeQueryInheritance() + { + $contractMetadata = $this->_em->getClassMetadata('Doctrine\Tests\Models\Company\CompanyContract'); + $flexMetadata = $this->_em->getClassMetadata('Doctrine\Tests\Models\Company\CompanyFlexContract'); + + $contractQueries = $contractMetadata->getNamedNativeQueries(); + $flexQueries = $flexMetadata->getNamedNativeQueries(); + + $contractMappings = $contractMetadata->getSqlResultSetMappings(); + $flexMappings = $flexMetadata->getSqlResultSetMappings(); + + + // contract queries + $this->assertEquals('all-contracts', $contractQueries['all-contracts']['name']); + $this->assertEquals('Doctrine\Tests\Models\Company\CompanyContract', $contractQueries['all-contracts']['resultClass']); + + $this->assertEquals('all', $contractQueries['all']['name']); + $this->assertEquals('Doctrine\Tests\Models\Company\CompanyContract', $contractQueries['all']['resultClass']); + + + // flex contract queries + $this->assertEquals('all-contracts', $flexQueries['all-contracts']['name']); + $this->assertEquals('Doctrine\Tests\Models\Company\CompanyFlexContract', $flexQueries['all-contracts']['resultClass']); + + $this->assertEquals('all-flex', $flexQueries['all-flex']['name']); + $this->assertEquals('Doctrine\Tests\Models\Company\CompanyFlexContract', $flexQueries['all-flex']['resultClass']); + + $this->assertEquals('all', $flexQueries['all']['name']); + $this->assertEquals('Doctrine\Tests\Models\Company\CompanyFlexContract', $flexQueries['all']['resultClass']); + + + // contract result mapping + $this->assertEquals('mapping-all-contracts', $contractMappings['mapping-all-contracts']['name']); + $this->assertEquals('Doctrine\Tests\Models\Company\CompanyContract', $contractMappings['mapping-all-contracts']['entities'][0]['entityClass']); + + $this->assertEquals('mapping-all', $contractMappings['mapping-all']['name']); + $this->assertEquals('Doctrine\Tests\Models\Company\CompanyContract', $contractMappings['mapping-all-contracts']['entities'][0]['entityClass']); + + // flex contract result mapping + $this->assertEquals('mapping-all-contracts', $flexMappings['mapping-all-contracts']['name']); + $this->assertEquals('Doctrine\Tests\Models\Company\CompanyFlexContract', $flexMappings['mapping-all-contracts']['entities'][0]['entityClass']); + + $this->assertEquals('mapping-all', $flexMappings['mapping-all']['name']); + $this->assertEquals('Doctrine\Tests\Models\Company\CompanyFlexContract', $flexMappings['mapping-all']['entities'][0]['entityClass']); + + $this->assertEquals('mapping-all-flex', $flexMappings['mapping-all-flex']['name']); + $this->assertEquals('Doctrine\Tests\Models\Company\CompanyFlexContract', $flexMappings['mapping-all-flex']['entities'][0]['entityClass']); + + } + } \ No newline at end of file