diff --git a/doctrine-mapping.xsd b/doctrine-mapping.xsd index a25dd56f6..80da4eb93 100644 --- a/doctrine-mapping.xsd +++ b/doctrine-mapping.xsd @@ -84,6 +84,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -93,6 +138,7 @@ + diff --git a/lib/Doctrine/ORM/EntityRepository.php b/lib/Doctrine/ORM/EntityRepository.php index 75cbf029e..f2f6a396f 100644 --- a/lib/Doctrine/ORM/EntityRepository.php +++ b/lib/Doctrine/ORM/EntityRepository.php @@ -89,6 +89,21 @@ class EntityRepository implements ObjectRepository return $this->_em->createQuery($this->_class->getNamedQuery($queryName)); } + /** + * Creates a native SQL query. + * + * @param string $queryName + * @return NativeQuery + */ + public function createNativeNamedQuery($queryName) + { + $queryMapping = $this->_class->getNamedNativeQuery($queryName); + $rsm = new Query\ResultSetMappingBuilder($this->_em); + $rsm->addNamedNativeQueryMapping($this->_class, $queryMapping); + + return $this->_em->createNativeQuery($queryMapping['query'], $rsm); + } + /** * Clears the repository, causing all managed entities to become detached. */ 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 80498b737..1f3b00dbf 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php @@ -49,16 +49,19 @@ class ClassMetadataInfo implements ClassMetadata * and therefore does not need an inheritance mapping type. */ const INHERITANCE_TYPE_NONE = 1; + /** * JOINED means the class will be persisted according to the rules of * Class Table Inheritance. */ const INHERITANCE_TYPE_JOINED = 2; + /** * SINGLE_TABLE means the class will be persisted according to the rules of * Single Table Inheritance. */ const INHERITANCE_TYPE_SINGLE_TABLE = 3; + /** * TABLE_PER_CLASS means the class will be persisted according to the rules * of Concrete Table Inheritance. @@ -71,17 +74,20 @@ class ClassMetadataInfo implements ClassMetadata * Offers full portability. */ const GENERATOR_TYPE_AUTO = 1; + /** * SEQUENCE means a separate sequence object will be used. Platforms that do * not have native sequence support may emulate it. Full portability is currently * not guaranteed. */ const GENERATOR_TYPE_SEQUENCE = 2; + /** * TABLE means a separate table is used for id generation. * Offers full portability. */ const GENERATOR_TYPE_TABLE = 3; + /** * IDENTITY means an identity column is used for id generation. The database * will fill in the id column on insertion. Platforms that do not support @@ -89,11 +95,13 @@ class ClassMetadataInfo implements ClassMetadata * not guaranteed. */ const GENERATOR_TYPE_IDENTITY = 4; + /** * NONE means the class does not have a generated id. That means the class * must have a natural, manually assigned id. */ const GENERATOR_TYPE_NONE = 5; + /** * UUID means that a UUID/GUID expression is used for id generation. Full * portability is currently not guaranteed. @@ -111,53 +119,64 @@ class ClassMetadataInfo implements ClassMetadata * This is the default change tracking policy. */ const CHANGETRACKING_DEFERRED_IMPLICIT = 1; + /** * DEFERRED_EXPLICIT means that changes of entities are calculated at commit-time * by doing a property-by-property comparison with the original data. This will * be done only for entities that were explicitly saved (through persist() or a cascade). */ const CHANGETRACKING_DEFERRED_EXPLICIT = 2; + /** * NOTIFY means that Doctrine relies on the entities sending out notifications * when their properties change. Such entity classes must implement * the NotifyPropertyChanged interface. */ const CHANGETRACKING_NOTIFY = 3; + /** * Specifies that an association is to be fetched when it is first accessed. */ const FETCH_LAZY = 2; + /** * Specifies that an association is to be fetched when the owner of the * association is fetched. */ const FETCH_EAGER = 3; + /** * Specifies that an association is to be fetched lazy (on first access) and that * commands such as Collection#count, Collection#slice are issued directly against * the database if the collection is not yet initialized. */ const FETCH_EXTRA_LAZY = 4; + /** * Identifies a one-to-one association. */ const ONE_TO_ONE = 1; + /** * Identifies a many-to-one association. */ const MANY_TO_ONE = 2; + /** * Identifies a one-to-many association. */ const ONE_TO_MANY = 4; + /** * Identifies a many-to-many association. */ const MANY_TO_MANY = 8; + /** * Combined bitmask for to-one (single-valued) associations. */ const TO_ONE = 3; + /** * Combined bitmask for to-many (collection-valued) associations. */ @@ -237,6 +256,35 @@ class ClassMetadataInfo implements ClassMetadata */ public $namedQueries = array(); + /** + * READ-ONLY: The named native queries allowed to be called directly from Repository. + * + * A native SQL named query definition has the following structure: + *
+     * array(
+     *     'name'               => ,
+     *     'query'              => ,
+     *     'resultClass'        => ,
+     *     'resultSetMapping'   => 
+     * )
+     * 
+ */ + public $namedNativeQueries = array(); + + /** + * READ-ONLY: The mappings of the results of native SQL queries. + * + * A native result mapping definition has the following structure: + *
+     * array(
+     *     'name'               => ,
+     *     'entities'           => array(),
+     *     'columns'            => array()
+     * )
+     * 
+ */ + public $sqlResultSetMappings = array(); + /** * READ-ONLY: The field names of all fields that are part of the identifier/primary key * of the mapped entity class. @@ -748,6 +796,14 @@ class ClassMetadataInfo implements ClassMetadata $serialized[] = 'namedQueries'; } + if ($this->namedNativeQueries) { + $serialized[] = 'namedNativeQueries'; + } + + if ($this->sqlResultSetMappings) { + $serialized[] = 'sqlResultSetMappings'; + } + if ($this->isReadOnly) { $serialized[] = 'isReadOnly'; } @@ -1051,6 +1107,60 @@ class ClassMetadataInfo implements ClassMetadata return $this->namedQueries; } + /** + * Gets the named native query. + * + * @see ClassMetadataInfo::$namedNativeQueries + * @throws MappingException + * @param string $queryName The query name + * @return array + */ + public function getNamedNativeQuery($queryName) + { + if ( ! isset($this->namedNativeQueries[$queryName])) { + throw MappingException::queryNotFound($this->name, $queryName); + } + + return $this->namedNativeQueries[$queryName]; + } + + /** + * Gets all named native queries of the class. + * + * @return array + */ + public function getNamedNativeQueries() + { + return $this->namedNativeQueries; + } + + /** + * Gets the result set mapping. + * + * @see ClassMetadataInfo::$sqlResultSetMappings + * @throws MappingException + * @param string $name The result set mapping name + * @return array + */ + public function getSqlResultSetMapping($name) + { + if ( ! isset($this->sqlResultSetMappings[$name])) { + throw MappingException::resultMappingNotFound($this->name, $name); + } + + return $this->sqlResultSetMappings[$name]; + } + + /** + * Gets all sql result set mappings of the class. + * + * @return array + */ + public function getSqlResultSetMappings() + { + return $this->sqlResultSetMappings; + } + /** * Validates & completes the given field mapping. * @@ -1826,10 +1936,18 @@ class ClassMetadataInfo implements ClassMetadata */ public function addNamedQuery(array $queryMapping) { + if (!isset($queryMapping['name'])) { + throw MappingException::nameIsMandatoryForQueryMapping($this->name); + } + if (isset($this->namedQueries[$queryMapping['name']])) { throw MappingException::duplicateQueryMapping($this->name, $queryMapping['name']); } + if (!isset($queryMapping['query'])) { + throw MappingException::emptyQueryMapping($this->name, $queryMapping['name']); + } + $name = $queryMapping['name']; $query = $queryMapping['query']; $dql = str_replace('__CLASS__', $this->name, $query); @@ -1840,6 +1958,107 @@ class ClassMetadataInfo implements ClassMetadata ); } + /** + * INTERNAL: + * Adds a named native query to this class. + * + * @throws MappingException + * @param array $queryMapping + */ + public function addNamedNativeQuery(array $queryMapping) + { + if (!isset($queryMapping['name'])) { + throw MappingException::nameIsMandatoryForQueryMapping($this->name); + } + + if (isset($this->namedNativeQueries[$queryMapping['name']])) { + throw MappingException::duplicateQueryMapping($this->name, $queryMapping['name']); + } + + if (!isset($queryMapping['query'])) { + throw MappingException::emptyQueryMapping($this->name, $queryMapping['name']); + } + + if (!isset($queryMapping['resultClass']) && !isset($queryMapping['resultSetMapping'])) { + 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']; + } + + $queryMapping['resultClass'] = ltrim($queryMapping['resultClass'], '\\'); + } + + $this->namedNativeQueries[$queryMapping['name']] = $queryMapping; + } + + /** + * INTERNAL: + * Adds a sql result set mapping to this class. + * + * @throws MappingException + * @param array $resultMapping + */ + public function addSqlResultSetMapping(array $resultMapping) + { + if (!isset($resultMapping['name'])) { + throw MappingException::nameIsMandatoryForSqlResultSetMapping($this->name); + } + + if (isset($this->sqlResultSetMappings[$resultMapping['name']])) { + throw MappingException::duplicateResultSetMapping($this->name, $resultMapping['name']); + } + + if (isset($resultMapping['entities'])) { + foreach ($resultMapping['entities'] as $key => $entityResult) { + if (!isset($entityResult['entityClass'])) { + 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) { + if (!isset($field['name'])) { + throw MappingException::missingResultSetMappingFieldName($this->name, $resultMapping['name']); + } + + if (!isset($field['column'])) { + $fieldName = $field['name']; + if(strpos($fieldName, '.')){ + list(, $fieldName) = explode('.', $fieldName); + } + + $resultMapping['entities'][$key]['fields'][$k]['column'] = $fieldName; + } + } + } + } + } + + $this->sqlResultSetMappings[$resultMapping['name']] = $resultMapping; + } + /** * Adds a one-to-one mapping. * @@ -2053,7 +2272,7 @@ class ClassMetadataInfo implements ClassMetadata /** * Checks whether the class has a named query with the given query name. * - * @param string $fieldName + * @param string $queryName * @return boolean */ public function hasNamedQuery($queryName) @@ -2061,6 +2280,28 @@ class ClassMetadataInfo implements ClassMetadata return isset($this->namedQueries[$queryName]); } + /** + * Checks whether the class has a named native query with the given query name. + * + * @param string $queryName + * @return boolean + */ + public function hasNamedNativeQuery($queryName) + { + return isset($this->namedNativeQueries[$queryName]); + } + + /** + * Checks whether the class has a named native query with the given query name. + * + * @param string $name + * @return boolean + */ + public function hasSqlResultSetMapping($name) + { + return isset($this->sqlResultSetMappings[$name]); + } + /** * Checks whether the class has a mapped association with the given field name. * @@ -2397,4 +2638,19 @@ class ClassMetadataInfo implements ClassMetadata { return $this->associationMappings[$fieldName]['mappedBy']; } + + /** + * @param string $targetClass + * @return array + */ + public function getAssociationsByTargetClass($targetClass) + { + $relations = array(); + foreach ($this->associationMappings as $mapping) { + if ($mapping['targetEntity'] == $targetClass) { + $relations[$mapping['fieldName']] = $mapping; + } + } + return $relations; + } } diff --git a/lib/Doctrine/ORM/Mapping/ColumnResult.php b/lib/Doctrine/ORM/Mapping/ColumnResult.php new file mode 100644 index 000000000..ff5cf272e --- /dev/null +++ b/lib/Doctrine/ORM/Mapping/ColumnResult.php @@ -0,0 +1,42 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * References name of a column in the SELECT clause of a SQL query. + * Scalar result types can be included in the query result by specifying this annotation in the metadata. + * + * @author Fabio B. Silva + * @since 2.3 + * + * @Annotation + * @Target("ANNOTATION") + */ +final class ColumnResult implements Annotation +{ + + /** + * The name of a column in the SELECT clause of a SQL query + * + * @var string + */ + public $name; + +} \ No newline at end of file diff --git a/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php b/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php index 22a1250ba..6a89131fe 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php @@ -204,6 +204,58 @@ class AnnotationDriver implements Driver $metadata->setPrimaryTable($primaryTable); } + // Evaluate NamedNativeQueries annotation + if (isset($classAnnotations['Doctrine\ORM\Mapping\NamedNativeQueries'])) { + $namedNativeQueriesAnnot = $classAnnotations['Doctrine\ORM\Mapping\NamedNativeQueries']; + + foreach ($namedNativeQueriesAnnot->value as $namedNativeQuery) { + $metadata->addNamedNativeQuery(array( + 'name' => $namedNativeQuery->name, + 'query' => $namedNativeQuery->query, + 'resultClass' => $namedNativeQuery->resultClass, + 'resultSetMapping' => $namedNativeQuery->resultSetMapping, + )); + } + } + + // Evaluate SqlResultSetMappings annotation + if (isset($classAnnotations['Doctrine\ORM\Mapping\SqlResultSetMappings'])) { + $sqlResultSetMappingsAnnot = $classAnnotations['Doctrine\ORM\Mapping\SqlResultSetMappings']; + + foreach ($sqlResultSetMappingsAnnot->value as $resultSetMapping) { + $entities = array(); + $columns = array(); + foreach ($resultSetMapping->entities as $entityResultAnnot) { + $entityResult = array( + 'fields' => array(), + 'entityClass' => $entityResultAnnot->entityClass, + 'discriminatorColumn' => $entityResultAnnot->discriminatorColumn, + ); + + foreach ($entityResultAnnot->fields as $fieldResultAnnot) { + $entityResult['fields'][] = array( + 'name' => $fieldResultAnnot->name, + 'column' => $fieldResultAnnot->column + ); + } + + $entities[] = $entityResult; + } + + foreach ($resultSetMapping->columns as $columnResultAnnot) { + $columns[] = array( + 'name' => $columnResultAnnot->name, + ); + } + + $metadata->addSqlResultSetMapping(array( + 'name' => $resultSetMapping->name, + 'entities' => $entities, + 'columns' => $columns + )); + } + } + // Evaluate NamedQueries annotation if (isset($classAnnotations['Doctrine\ORM\Mapping\NamedQueries'])) { $namedQueriesAnnot = $classAnnotations['Doctrine\ORM\Mapping\NamedQueries']; diff --git a/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php b/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php index bd1632b2c..46fa1551b 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php +++ b/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php @@ -53,3 +53,10 @@ require_once __DIR__.'/../PreRemove.php'; require_once __DIR__.'/../PostRemove.php'; require_once __DIR__.'/../PostLoad.php'; require_once __DIR__.'/../PreFlush.php'; +require_once __DIR__.'/../FieldResult.php'; +require_once __DIR__.'/../ColumnResult.php'; +require_once __DIR__.'/../EntityResult.php'; +require_once __DIR__.'/../NamedNativeQuery.php'; +require_once __DIR__.'/../NamedNativeQueries.php'; +require_once __DIR__.'/../SqlResultSetMapping.php'; +require_once __DIR__.'/../SqlResultSetMappings.php'; \ No newline at end of file diff --git a/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php b/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php index 3b2e54ac4..005dc44d1 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php @@ -85,6 +85,58 @@ class XmlDriver extends AbstractFileDriver } } + // Evaluate native named queries + if (isset($xmlRoot->{'named-native-queries'})) { + foreach ($xmlRoot->{'named-native-queries'}->{'named-native-query'} as $nativeQueryElement) { + $metadata->addNamedNativeQuery(array( + 'name' => isset($nativeQueryElement['name']) ? (string)$nativeQueryElement['name'] : null, + 'query' => isset($nativeQueryElement->query) ? (string)$nativeQueryElement->query : null, + 'resultClass' => isset($nativeQueryElement['result-class']) ? (string)$nativeQueryElement['result-class'] : null, + 'resultSetMapping' => isset($nativeQueryElement['result-set-mapping']) ? (string)$nativeQueryElement['result-set-mapping'] : null, + )); + } + } + + // Evaluate sql result set mapping + if (isset($xmlRoot->{'sql-result-set-mappings'})) { + foreach ($xmlRoot->{'sql-result-set-mappings'}->{'sql-result-set-mapping'} as $rsmElement) { + $entities = array(); + $columns = array(); + foreach ($rsmElement as $entityElement) { + // + if (isset($entityElement['entity-class'])) { + $entityResult = array( + 'fields' => array(), + 'entityClass' => (string)$entityElement['entity-class'], + 'discriminatorColumn' => isset($entityElement['discriminator-column']) ? (string)$entityElement['discriminator-column'] : null, + ); + + foreach ($entityElement as $fieldElement) { + $entityResult['fields'][] = array( + 'name' => isset($fieldElement['name']) ? (string)$fieldElement['name'] : null, + 'column' => isset($fieldElement['column']) ? (string)$fieldElement['column'] : null, + ); + } + + $entities[] = $entityResult; + } + + // + if (isset($entityElement['name'])) { + $columns[] = array( + 'name' => (string)$entityElement['name'], + ); + } + } + + $metadata->addSqlResultSetMapping(array( + 'name' => (string)$rsmElement['name'], + 'entities' => $entities, + 'columns' => $columns + )); + } + } + /* not implemented specially anyway. use table = schema.table if (isset($xmlRoot['schema'])) { $metadata->table['schema'] = (string)$xmlRoot['schema']; diff --git a/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php b/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php index 0c4fb43cb..d602ed975 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php @@ -83,6 +83,68 @@ class YamlDriver extends AbstractFileDriver } } + // Evaluate named native queries + if (isset($element['namedNativeQueries'])) { + foreach ($element['namedNativeQueries'] as $name => $mappingElement) { + if (!isset($mappingElement['name'])) { + $mappingElement['name'] = $name; + } + $metadata->addNamedNativeQuery(array( + 'name' => $mappingElement['name'], + 'query' => isset($mappingElement['query']) ? $mappingElement['query'] : null, + 'resultClass' => isset($mappingElement['resultClass']) ? $mappingElement['resultClass'] : null, + 'resultSetMapping' => isset($mappingElement['resultSetMapping']) ? $mappingElement['resultSetMapping'] : null, + )); + } + } + + // Evaluate sql result set mappings + if (isset($element['sqlResultSetMappings'])) { + foreach ($element['sqlResultSetMappings'] as $name => $resultSetMapping) { + if (!isset($resultSetMapping['name'])) { + $resultSetMapping['name'] = $name; + } + + $entities = array(); + $columns = array(); + if (isset($resultSetMapping['entityResult'])) { + foreach ($resultSetMapping['entityResult'] as $entityResultElement) { + $entityResult = array( + 'fields' => array(), + 'entityClass' => isset($entityResultElement['entityClass']) ? $entityResultElement['entityClass'] : null, + 'discriminatorColumn' => isset($entityResultElement['discriminatorColumn']) ? $entityResultElement['discriminatorColumn'] : null, + ); + + if (isset($entityResultElement['fieldResult'])) { + foreach ($entityResultElement['fieldResult'] as $fieldResultElement) { + $entityResult['fields'][] = array( + 'name' => isset($fieldResultElement['name']) ? $fieldResultElement['name'] : null, + 'column' => isset($fieldResultElement['column']) ? $fieldResultElement['column'] : null, + ); + } + } + + $entities[] = $entityResult; + } + } + + + if (isset($resultSetMapping['columnResult'])) { + foreach ($resultSetMapping['columnResult'] as $columnResultAnnot) { + $columns[] = array( + 'name' => isset($columnResultAnnot['name']) ? $columnResultAnnot['name'] : null, + ); + } + } + + $metadata->addSqlResultSetMapping(array( + 'name' => $resultSetMapping['name'], + 'entities' => $entities, + 'columns' => $columns + )); + } + } + /* not implemented specially anyway. use table = schema.table if (isset($element['schema'])) { $metadata->table['schema'] = $element['schema']; @@ -484,10 +546,14 @@ class YamlDriver extends AbstractFileDriver */ private function _getJoinColumnMapping($joinColumnElement) { - $joinColumn = array( - 'name' => $joinColumnElement['name'], - 'referencedColumnName' => $joinColumnElement['referencedColumnName'] - ); + $joinColumn = array(); + if (isset($joinColumnElement['referencedColumnName'])) { + $joinColumn['referencedColumnName'] = (string) $joinColumnElement['referencedColumnName']; + } + + if (isset($joinColumnElement['name'])) { + $joinColumn['name'] = (string) $joinColumnElement['name']; + } if (isset($joinColumnElement['fieldName'])) { $joinColumn['fieldName'] = (string) $joinColumnElement['fieldName']; diff --git a/lib/Doctrine/ORM/Mapping/EntityResult.php b/lib/Doctrine/ORM/Mapping/EntityResult.php new file mode 100644 index 000000000..1be9206e7 --- /dev/null +++ b/lib/Doctrine/ORM/Mapping/EntityResult.php @@ -0,0 +1,58 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * References an entity in the SELECT clause of a SQL query. + * If this annotation is used, the SQL statement should select all of the columns that are mapped to the entity object. + * This should include foreign key columns to related entities. + * The results obtained when insufficient data is available are undefined. + * + * @author Fabio B. Silva + * @since 2.3 + * + * @Annotation + * @Target("ANNOTATION") + */ +final class EntityResult implements Annotation +{ + + /** + * The class of the result + * + * @var string + */ + public $entityClass; + + /** + * Maps the columns specified in the SELECT list of the query to the properties or fields of the entity class. + * + * @var array<\Doctrine\ORM\Mapping\FieldResult> + */ + public $fields = array(); + + /** + * Specifies the column name of the column in the SELECT list that is used to determine the type of the entity instance. + * + * @var string + */ + public $discriminatorColumn; + +} \ No newline at end of file diff --git a/lib/Doctrine/ORM/Mapping/FieldResult.php b/lib/Doctrine/ORM/Mapping/FieldResult.php new file mode 100644 index 000000000..c2c49c68d --- /dev/null +++ b/lib/Doctrine/ORM/Mapping/FieldResult.php @@ -0,0 +1,48 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * Is used to map the columns specified in the SELECT list of the query to the properties or fields of the entity class. + * + * @author Fabio B. Silva + * @since 2.3 + * + * @Annotation + * @Target("ANNOTATION") + */ +final class FieldResult implements Annotation +{ + + /** + * Name of the column in the SELECT clause. + * + * @var string + */ + public $name; + + /** + * Name of the persistent field or property of the class. + * + * @var string + */ + public $column; + +} \ No newline at end of file diff --git a/lib/Doctrine/ORM/Mapping/MappingException.php b/lib/Doctrine/ORM/Mapping/MappingException.php index 3e8022280..5c3ed8ef2 100644 --- a/lib/Doctrine/ORM/Mapping/MappingException.php +++ b/lib/Doctrine/ORM/Mapping/MappingException.php @@ -93,6 +93,41 @@ class MappingException extends \Doctrine\ORM\ORMException return new self("No query found named '$queryName' on class '$className'."); } + public static function resultMappingNotFound($className, $resultName) + { + return new self("No result set mapping found named '$resultName' on class '$className'."); + } + + public static function emptyQueryMapping($entity, $queryName) + { + return new self('Query named "'.$queryName.'" in "'.$entity.'" could not be empty.'); + } + + public static function nameIsMandatoryForQueryMapping($className) + { + return new self("Query name on entity class '$className' is not defined."); + } + + public static function missingQueryMapping($entity, $queryName) + { + return new self('Query named "'.$queryName.'" in "'.$entity.' requires a result class or result set mapping.'); + } + + public static function missingResultSetMappingEntity($entity, $resultName) + { + return new self('Result set mapping named "'.$resultName.'" in "'.$entity.' requires a entity class name.'); + } + + public static function missingResultSetMappingFieldName($entity, $resultName) + { + return new self('Result set mapping named "'.$resultName.'" in "'.$entity.' requires a field name.'); + } + + public static function nameIsMandatoryForSqlResultSetMapping($className) + { + return new self("Result set mapping name on entity class '$className' is not defined."); + } + public static function oneToManyRequiresMappedBy($fieldName) { return new self("OneToMany mapping on field '$fieldName' requires the 'mappedBy' attribute."); @@ -178,27 +213,36 @@ class MappingException extends \Doctrine\ORM\ORMException } /** - * * @param string $entity The entity's name * @param string $fieldName The name of the field that was already declared */ - public static function duplicateFieldMapping($entity, $fieldName) { + public static function duplicateFieldMapping($entity, $fieldName) + { return new self('Property "'.$fieldName.'" in "'.$entity.'" was already declared, but it must be declared only once'); } - public static function duplicateAssociationMapping($entity, $fieldName) { + public static function duplicateAssociationMapping($entity, $fieldName) + { return new self('Property "'.$fieldName.'" in "'.$entity.'" was already declared, but it must be declared only once'); } - public static function duplicateQueryMapping($entity, $queryName) { + public static function duplicateQueryMapping($entity, $queryName) + { return new self('Query named "'.$queryName.'" in "'.$entity.'" was already declared, but it must be declared only once'); } - public static function singleIdNotAllowedOnCompositePrimaryKey($entity) { + public static function duplicateResultSetMapping($entity, $resultName) + { + return new self('Result set mapping named "'.$resultName.'" in "'.$entity.'" was already declared, but it must be declared only once'); + } + + public static function singleIdNotAllowedOnCompositePrimaryKey($entity) + { return new self('Single id is not allowed on composite primary key in entity '.$entity); } - public static function unsupportedOptimisticLockingType($entity, $fieldName, $unsupportedType) { + public static function unsupportedOptimisticLockingType($entity, $fieldName, $unsupportedType) + { return new self('Locking type "'.$unsupportedType.'" (specified in "'.$entity.'", field "'.$fieldName.'") ' .'is not supported by Doctrine.' ); @@ -224,7 +268,8 @@ class MappingException extends \Doctrine\ORM\ORMException * @param string $owningClass The class that declares the discriminator map. * @return self */ - public static function invalidClassInDiscriminatorMap($className, $owningClass) { + public static function invalidClassInDiscriminatorMap($className, $owningClass) + { return new self( "Entity class '$className' used in the discriminator map of class '$owningClass' ". "does not exist." diff --git a/lib/Doctrine/ORM/Mapping/NamedNativeQueries.php b/lib/Doctrine/ORM/Mapping/NamedNativeQueries.php new file mode 100644 index 000000000..f01090ee6 --- /dev/null +++ b/lib/Doctrine/ORM/Mapping/NamedNativeQueries.php @@ -0,0 +1,40 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * Is used to specify an array of native SQL named queries. + * The NamedNativeQueries annotation can be applied to an entity or mapped superclass. + * + * @author Fabio B. Silva + * @since 2.3 + * + * @Annotation + * @Target("CLASS") + */ +final class NamedNativeQueries implements Annotation +{ + /** + * One or more NamedNativeQuery annotations. + * + * @var array<\Doctrine\ORM\Mapping\NamedNativeQuery> + */ + public $value = array(); +} \ No newline at end of file diff --git a/lib/Doctrine/ORM/Mapping/NamedNativeQuery.php b/lib/Doctrine/ORM/Mapping/NamedNativeQuery.php new file mode 100644 index 000000000..052eded64 --- /dev/null +++ b/lib/Doctrine/ORM/Mapping/NamedNativeQuery.php @@ -0,0 +1,63 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * Is used to specify a native SQL named query. + * The NamedNativeQuery annotation can be applied to an entity or mapped superclass. + * + * @author Fabio B. Silva + * @since 2.3 + * + * @Annotation + * @Target("ANNOTATION") + */ +final class NamedNativeQuery implements Annotation +{ + + /** + * The name used to refer to the query with the EntityManager methods that create query objects. + * + * @var string + */ + public $name; + + /** + * The SQL query string. + * + * @var string + */ + public $query; + + /** + * The class of the result. + * + * @var string + */ + public $resultClass; + + /** + * The name of a SqlResultSetMapping, as defined in metadata. + * + * @var string + */ + public $resultSetMapping; + +} \ No newline at end of file diff --git a/lib/Doctrine/ORM/Mapping/SqlResultSetMapping.php b/lib/Doctrine/ORM/Mapping/SqlResultSetMapping.php new file mode 100644 index 000000000..e009ceea2 --- /dev/null +++ b/lib/Doctrine/ORM/Mapping/SqlResultSetMapping.php @@ -0,0 +1,56 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * The SqlResultSetMapping annotation is used to specify the mapping of the result of a native SQL query. + * The SqlResultSetMapping annotation can be applied to an entity or mapped superclass. + * + * @author Fabio B. Silva + * @since 2.3 + * + * @Annotation + * @Target("ANNOTATION") + */ +final class SqlResultSetMapping implements Annotation +{ + + /** + * The name given to the result set mapping, and used to refer to it in the methods of the Query API. + * + * @var string + */ + public $name; + + /** + * Specifies the result set mapping to entities. + * + * @var array<\Doctrine\ORM\Mapping\EntityResult> + */ + public $entities = array(); + + /** + * Specifies the result set mapping to scalar values. + * + * @var array<\Doctrine\ORM\Mapping\ColumnResult> + */ + public $columns = array(); + +} \ No newline at end of file diff --git a/lib/Doctrine/ORM/Mapping/SqlResultSetMappings.php b/lib/Doctrine/ORM/Mapping/SqlResultSetMappings.php new file mode 100644 index 000000000..fa04387a2 --- /dev/null +++ b/lib/Doctrine/ORM/Mapping/SqlResultSetMappings.php @@ -0,0 +1,40 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * Is used to specify an array of mappings. + * The SqlResultSetMappings annotation can be applied to an entity or mapped superclass. + * + * @author Fabio B. Silva + * @since 2.3 + * + * @Annotation + * @Target("CLASS") + */ +final class SqlResultSetMappings implements Annotation +{ + /** + * One or more SqlResultSetMapping annotations. + * + * @var array<\Doctrine\ORM\Mapping\SqlResultSetMapping> + */ + public $value = array(); +} \ No newline at end of file diff --git a/lib/Doctrine/ORM/Query/ResultSetMappingBuilder.php b/lib/Doctrine/ORM/Query/ResultSetMappingBuilder.php index 316a2012c..7c187dd77 100644 --- a/lib/Doctrine/ORM/Query/ResultSetMappingBuilder.php +++ b/lib/Doctrine/ORM/Query/ResultSetMappingBuilder.php @@ -106,4 +106,159 @@ class ResultSetMappingBuilder extends ResultSetMapping } } } -} + + + /** + * Adds the mappings of the results of native SQL queries to the result set. + * + * @param ClassMetadataInfo $class + * @param array $queryMapping + * @return ResultSetMappingBuilder + */ + public function addNamedNativeQueryMapping(ClassMetadataInfo $class, array $queryMapping) + { + if (isset($queryMapping['resultClass'])) { + return $this->addNamedNativeQueryResultClassMapping($class, $queryMapping['resultClass']); + } + + return $this->addNamedNativeQueryResultSetMapping($class, $queryMapping['resultSetMapping']); + } + + /** + * Adds the class mapping of the results of native SQL queries to the result set. + * + * @param ClassMetadataInfo $class + * @param string $resultClassName + * @return ResultSetMappingBuilder + */ + public function addNamedNativeQueryResultClassMapping(ClassMetadataInfo $class, $resultClassName) + { + + $classMetadata = $this->em->getClassMetadata($resultClassName); + $shortName = $classMetadata->reflClass->getShortName(); + $alias = strtolower($shortName[0]).'0'; + + $this->addEntityResult($class->name, $alias); + + if ($classMetadata->discriminatorColumn) { + $discriminatorColumn = $classMetadata->discriminatorColumn; + $this->setDiscriminatorColumn($alias, $discriminatorColumn['name']); + $this->addMetaResult($alias, $discriminatorColumn['name'], $discriminatorColumn['fieldName']); + } + + foreach ($classMetadata->getColumnNames() as $key => $columnName) { + $propertyName = $classMetadata->getFieldName($columnName); + $this->addFieldResult($alias, $columnName, $propertyName); + } + + foreach ($classMetadata->associationMappings as $associationMapping) { + if ($associationMapping['isOwningSide'] && $associationMapping['type'] & ClassMetadataInfo::TO_ONE) { + foreach ($associationMapping['joinColumns'] as $joinColumn) { + $columnName = $joinColumn['name']; + $this->addMetaResult($alias, $columnName, $columnName, $classMetadata->isIdentifier($columnName)); + } + } + } + + return $this; + } + + /** + * Adds the result set mapping of the results of native SQL queries to the result set. + * + * @param ClassMetadataInfo $class + * @param string $resultSetMappingName + * @return ResultSetMappingBuilder + */ + public function addNamedNativeQueryResultSetMapping(ClassMetadataInfo $class, $resultSetMappingName) + { + $counter = 0; + $resultMapping = $class->getSqlResultSetMapping($resultSetMappingName); + $rooShortName = $class->reflClass->getShortName(); + $rootAlias = strtolower($rooShortName[0]) . $counter; + + + if (isset($resultMapping['entities'])) { + foreach ($resultMapping['entities'] as $key => $entityMapping) { + $classMetadata = $this->em->getClassMetadata($entityMapping['entityClass']); + + if ($class->reflClass->name == $classMetadata->reflClass->name) { + $this->addEntityResult($classMetadata->name, $rootAlias); + $this->addNamedNativeQueryEntityResultMapping($classMetadata, $entityMapping, $rootAlias); + } else { + $shortName = $classMetadata->reflClass->getShortName(); + $joinAlias = strtolower($shortName[0]) . ++ $counter; + $associations = $class->getAssociationsByTargetClass($classMetadata->name); + + foreach ($associations as $relation => $mapping) { + $this->addJoinedEntityResult($mapping['targetEntity'], $joinAlias, $rootAlias, $relation); + $this->addNamedNativeQueryEntityResultMapping($classMetadata, $entityMapping, $joinAlias); + } + } + + } + } + + if (isset($resultMapping['columns'])) { + foreach ($resultMapping['columns'] as $entityMapping) { + $this->addScalarResult($entityMapping['name'], $entityMapping['name']); + } + } + + return $this; + } + + /** + * Adds the entity result mapping of the results of native SQL queries to the result set. + * + * @param ClassMetadataInfo $classMetadata + * @param array $entityMapping + * @param string $alias + * @return ResultSetMappingBuilder + */ + public function addNamedNativeQueryEntityResultMapping(ClassMetadataInfo $classMetadata, array $entityMapping, $alias) + { + if (isset($entityMapping['discriminatorColumn']) && $entityMapping['discriminatorColumn']) { + $discriminatorColumn = $entityMapping['discriminatorColumn']; + $this->setDiscriminatorColumn($alias, $discriminatorColumn); + $this->addMetaResult($alias, $discriminatorColumn, $discriminatorColumn); + } + + if (isset($entityMapping['fields']) && !empty($entityMapping['fields'])) { + foreach ($entityMapping['fields'] as $field) { + $fieldName = $field['name']; + $relation = null; + + if(strpos($fieldName, '.')){ + list($relation, $fieldName) = explode('.', $fieldName); + } + + if (isset($classMetadata->associationMappings[$relation])) { + if($relation) { + $associationMapping = $classMetadata->associationMappings[$relation]; + $joinAlias = $alias.$relation; + $parentAlias = $alias; + + $this->addJoinedEntityResult($associationMapping['targetEntity'], $joinAlias, $parentAlias, $relation); + $this->addFieldResult($joinAlias, $field['column'], $fieldName); + }else { + $this->addFieldResult($alias, $field['column'], $fieldName, $classMetadata->name); + } + } else { + if(!isset($classMetadata->fieldMappings[$fieldName])) { + throw new \InvalidArgumentException("Entity '".$classMetadata->name."' has no field '".$fieldName."'. "); + } + $this->addFieldResult($alias, $field['column'], $fieldName, $classMetadata->name); + } + } + + } else { + foreach ($classMetadata->getColumnNames() as $columnName) { + $propertyName = $classMetadata->getFieldName($columnName); + $this->addFieldResult($alias, $columnName, $propertyName); + } + } + + return $this; + } +} \ No newline at end of file diff --git a/tests/Doctrine/Tests/Models/CMS/CmsAddress.php b/tests/Doctrine/Tests/Models/CMS/CmsAddress.php index d32416a5e..9833f3dfb 100644 --- a/tests/Doctrine/Tests/Models/CMS/CmsAddress.php +++ b/tests/Doctrine/Tests/Models/CMS/CmsAddress.php @@ -8,6 +8,57 @@ namespace Doctrine\Tests\Models\CMS; * @author Roman S. Borschel * @Entity * @Table(name="cms_addresses") + * + * @NamedNativeQueries({ + * @NamedNativeQuery( + * name = "find-all", + * resultSetMapping = "mapping-find-all", + * query = "SELECT id, country, city FROM cms_addresses" + * ), + * @NamedNativeQuery( + * name = "find-by-id", + * resultClass = "CmsAddress", + * query = "SELECT * FROM cms_addresses WHERE id = ?" + * ), + * @NamedNativeQuery( + * name = "count", + * resultSetMapping= "mapping-count", + * query = "SELECT COUNT(*) AS count FROM cms_addresses" + * ) + * }) + * + * @SqlResultSetMappings({ + * @SqlResultSetMapping( + * name = "mapping-find-all", + * entities= { + * @EntityResult( + * entityClass = "CmsAddress", + * fields = { + * @FieldResult(name = "id", column="id"), + * @FieldResult(name = "city", column="city"), + * @FieldResult(name = "country", column="country") + * } + * ) + * } + * ), + * @SqlResultSetMapping( + * name = "mapping-without-fields", + * entities= { + * @EntityResult( + * entityClass = "__CLASS__" + * ) + * } + * ), + * @SqlResultSetMapping( + * name = "mapping-count", + * columns = { + * @ColumnResult( + * name = "count" + * ) + * } + * ) + * }) + * */ class CmsAddress { @@ -69,4 +120,72 @@ class CmsAddress $user->setAddress($this); } } + + public static function loadMetadata(\Doctrine\ORM\Mapping\ClassMetadataInfo $metadata) + { + $metadata->setPrimaryTable(array( + 'name' => 'company_person', + )); + + $metadata->addNamedNativeQuery(array ( + 'name' => 'find-all', + 'query' => 'SELECT id, country, city FROM cms_addresses', + 'resultSetMapping' => 'mapping-find-all', + )); + + $metadata->addNamedNativeQuery(array ( + 'name' => 'find-by-id', + 'query' => 'SELECT * FROM cms_addresses WHERE id = ?', + 'resultClass' => 'Doctrine\\Tests\\Models\\CMS\\CmsAddress', + )); + + $metadata->addNamedNativeQuery(array ( + 'name' => 'count', + 'query' => 'SELECT COUNT(*) AS count FROM cms_addresses', + 'resultSetMapping' => 'mapping-count', + )); + + + $metadata->addSqlResultSetMapping(array ( + 'name' => 'mapping-find-all', + 'columns' => array(), + 'entities' => array ( array ( + 'fields' => array ( + array ( + 'name' => 'id', + 'column' => 'id', + ), + array ( + 'name' => 'city', + 'column' => 'city', + ), + array ( + 'name' => 'country', + 'column' => 'country', + ), + ), + 'entityClass' => 'Doctrine\Tests\Models\CMS\CmsAddress', + ), + ), + )); + + $metadata->addSqlResultSetMapping(array ( + 'name' => 'mapping-without-fields', + 'columns' => array(), + 'entities' => array(array ( + 'entityClass' => 'Doctrine\\Tests\\Models\\CMS\\CmsAddress', + 'fields' => array() + ) + ) + )); + + $metadata->addSqlResultSetMapping(array ( + 'name' => 'mapping-count', + 'columns' =>array ( + array ( + 'name' => 'count', + ), + ) + )); + } } \ No newline at end of file diff --git a/tests/Doctrine/Tests/Models/CMS/CmsUser.php b/tests/Doctrine/Tests/Models/CMS/CmsUser.php index 1e5465784..2bae6ed4f 100644 --- a/tests/Doctrine/Tests/Models/CMS/CmsUser.php +++ b/tests/Doctrine/Tests/Models/CMS/CmsUser.php @@ -10,6 +10,113 @@ use Doctrine\Common\Collections\ArrayCollection; * @NamedQueries({ * @NamedQuery(name="all", query="SELECT u FROM __CLASS__ u") * }) + * + * @NamedNativeQueries({ + * @NamedNativeQuery( + * name = "fetchIdAndUsernameWithResultClass", + * resultClass = "CmsUser", + * query = "SELECT id, username FROM cms_users WHERE username = ?" + * ), + * @NamedNativeQuery( + * name = "fetchAllColumns", + * resultClass = "CmsUser", + * query = "SELECT * FROM cms_users WHERE username = ?" + * ), + * @NamedNativeQuery( + * name = "fetchJoinedAddress", + * resultSetMapping= "mappingJoinedAddress", + * query = "SELECT u.id, u.name, u.status, a.id AS a_id, a.country, a.zip, a.city FROM cms_users u INNER JOIN cms_addresses a ON u.id = a.user_id WHERE u.username = ?" + * ), + * @NamedNativeQuery( + * name = "fetchJoinedPhonenumber", + * resultSetMapping= "mappingJoinedPhonenumber", + * query = "SELECT id, name, status, phonenumber AS number FROM cms_users INNER JOIN cms_phonenumbers ON id = user_id WHERE username = ?" + * ), + * @NamedNativeQuery( + * name = "fetchUserPhonenumberCount", + * resultSetMapping= "mappingUserPhonenumberCount", + * query = "SELECT id, name, status, COUNT(phonenumber) AS numphones FROM cms_users INNER JOIN cms_phonenumbers ON id = user_id WHERE username IN (?) GROUP BY id, name, status, username ORDER BY username" + * ), + * @NamedNativeQuery( + * name = "fetchMultipleJoinsEntityResults", + * resultSetMapping= "mappingMultipleJoinsEntityResults", + * query = "SELECT u.id AS u_id, u.name AS u_name, u.status AS u_status, a.id AS a_id, a.zip AS a_zip, a.country AS a_country, COUNT(p.phonenumber) AS numphones FROM cms_users u INNER JOIN cms_addresses a ON u.id = a.user_id INNER JOIN cms_phonenumbers p ON u.id = p.user_id GROUP BY u.id, u.name, u.status, u.username, a.id, a.zip, a.country ORDER BY u.username" + * ), + * }) + * + * @SqlResultSetMappings({ + * @SqlResultSetMapping( + * name = "mappingJoinedAddress", + * entities= { + * @EntityResult( + * entityClass = "__CLASS__", + * fields = { + * @FieldResult(name = "id"), + * @FieldResult(name = "name"), + * @FieldResult(name = "status"), + * @FieldResult(name = "address.zip"), + * @FieldResult(name = "address.city"), + * @FieldResult(name = "address.country"), + * @FieldResult(name = "address.id", column = "a_id"), + * } + * ) + * } + * ), + * @SqlResultSetMapping( + * name = "mappingJoinedPhonenumber", + * entities= { + * @EntityResult( + * entityClass = "CmsUser", + * fields = { + * @FieldResult("id"), + * @FieldResult("name"), + * @FieldResult("status"), + * @FieldResult("phonenumbers.phonenumber" , column = "number"), + * } + * ) + * } + * ), + * @SqlResultSetMapping( + * name = "mappingUserPhonenumberCount", + * entities= { + * @EntityResult( + * entityClass = "CmsUser", + * fields = { + * @FieldResult(name = "id"), + * @FieldResult(name = "name"), + * @FieldResult(name = "status"), + * } + * ) + * }, + * columns = { + * @ColumnResult("numphones") + * } + * ), + * @SqlResultSetMapping( + * name = "mappingMultipleJoinsEntityResults", + * entities= { + * @EntityResult( + * entityClass = "__CLASS__", + * fields = { + * @FieldResult(name = "id", column="u_id"), + * @FieldResult(name = "name", column="u_name"), + * @FieldResult(name = "status", column="u_status"), + * } + * ), + * @EntityResult( + * entityClass = "CmsAddress", + * fields = { + * @FieldResult(name = "id", column="a_id"), + * @FieldResult(name = "zip", column="a_zip"), + * @FieldResult(name = "country", column="a_country"), + * } + * ) + * }, + * columns = { + * @ColumnResult("numphones") + * } + * ) + * }) */ class CmsUser { @@ -140,4 +247,191 @@ class CmsUser } } } + + public static function loadMetadata(\Doctrine\ORM\Mapping\ClassMetadataInfo $metadata) + { + $metadata->setPrimaryTable(array( + 'name' => 'cms_users', + )); + + $metadata->addNamedNativeQuery(array ( + 'name' => 'fetchIdAndUsernameWithResultClass', + 'query' => 'SELECT id, username FROM cms_users WHERE username = ?', + 'resultClass' => 'Doctrine\\Tests\\Models\\CMS\\CmsUser', + )); + + $metadata->addNamedNativeQuery(array ( + 'name' => 'fetchAllColumns', + 'query' => 'SELECT * FROM cms_users WHERE username = ?', + 'resultClass' => 'Doctrine\\Tests\\Models\\CMS\\CmsUser', + )); + + $metadata->addNamedNativeQuery(array ( + 'name' => 'fetchJoinedAddress', + 'query' => 'SELECT u.id, u.name, u.status, a.id AS a_id, a.country, a.zip, a.city FROM cms_users u INNER JOIN cms_addresses a ON u.id = a.user_id WHERE u.username = ?', + 'resultSetMapping' => 'mappingJoinedAddress', + )); + + $metadata->addNamedNativeQuery(array ( + 'name' => 'fetchJoinedPhonenumber', + 'query' => 'SELECT id, name, status, phonenumber AS number FROM cms_users INNER JOIN cms_phonenumbers ON id = user_id WHERE username = ?', + 'resultSetMapping' => 'mappingJoinedPhonenumber', + )); + + $metadata->addNamedNativeQuery(array ( + 'name' => 'fetchUserPhonenumberCount', + 'query' => 'SELECT id, name, status, COUNT(phonenumber) AS numphones FROM cms_users INNER JOIN cms_phonenumbers ON id = user_id WHERE username IN (?) GROUP BY id, name, status, username ORDER BY username', + 'resultSetMapping' => 'mappingUserPhonenumberCount', + )); + + $metadata->addNamedNativeQuery(array ( + "name" => "fetchMultipleJoinsEntityResults", + "resultSetMapping" => "mappingMultipleJoinsEntityResults", + "query" => "SELECT u.id AS u_id, u.name AS u_name, u.status AS u_status, a.id AS a_id, a.zip AS a_zip, a.country AS a_country, COUNT(p.phonenumber) AS numphones FROM cms_users u INNER JOIN cms_addresses a ON u.id = a.user_id INNER JOIN cms_phonenumbers p ON u.id = p.user_id GROUP BY u.id, u.name, u.status, u.username, a.id, a.zip, a.country ORDER BY u.username" + )); + + $metadata->addSqlResultSetMapping(array ( + 'name' => 'mappingJoinedAddress', + 'columns' => array(), + 'entities' => array(array ( + 'fields'=> array ( + array ( + 'name' => 'id', + 'column' => 'id', + ), + array ( + 'name' => 'name', + 'column' => 'name', + ), + array ( + 'name' => 'status', + 'column' => 'status', + ), + array ( + 'name' => 'address.zip', + 'column' => 'zip', + ), + array ( + 'name' => 'address.city', + 'column' => 'city', + ), + array ( + 'name' => 'address.country', + 'column' => 'country', + ), + array ( + 'name' => 'address.id', + 'column' => 'a_id', + ), + ), + 'entityClass' => 'Doctrine\Tests\Models\CMS\CmsUser', + 'discriminatorColumn' => null + ), + ), + )); + + $metadata->addSqlResultSetMapping(array ( + 'name' => 'mappingJoinedPhonenumber', + 'columns' => array(), + 'entities' => array(array( + 'fields'=> array ( + array ( + 'name' => 'id', + 'column' => 'id', + ), + array ( + 'name' => 'name', + 'column' => 'name', + ), + array ( + 'name' => 'status', + 'column' => 'status', + ), + array ( + 'name' => 'phonenumbers.phonenumber', + 'column' => 'number', + ), + ), + 'entityClass' => 'Doctrine\\Tests\\Models\\CMS\\CmsUser', + 'discriminatorColumn' => null + ), + ), + )); + + $metadata->addSqlResultSetMapping(array ( + 'name' => 'mappingUserPhonenumberCount', + 'columns' => array(), + 'entities' => array ( + array( + 'fields' => array ( + array ( + 'name' => 'id', + 'column' => 'id', + ), + array ( + 'name' => 'name', + 'column' => 'name', + ), + array ( + 'name' => 'status', + 'column' => 'status', + ) + ), + 'entityClass' => 'Doctrine\Tests\Models\CMS\CmsUser', + 'discriminatorColumn' => null + ) + ), + 'columns' => array ( + array ( + 'name' => 'numphones', + ) + ) + )); + + $metadata->addSqlResultSetMapping(array( + 'name' => 'mappingMultipleJoinsEntityResults', + 'entities' => array(array( + 'fields' => array( + array( + 'name' => 'id', + 'column' => 'u_id', + ), + array( + 'name' => 'name', + 'column' => 'u_name', + ), + array( + 'name' => 'status', + 'column' => 'u_status', + ) + ), + 'entityClass' => 'Doctrine\Tests\Models\CMS\CmsUser', + 'discriminatorColumn' => null, + ), + array( + 'fields' => array( + array( + 'name' => 'id', + 'column' => 'a_id', + ), + array( + 'name' => 'zip', + 'column' => 'a_zip', + ), + array( + 'name' => 'country', + 'column' => 'a_country', + ), + ), + 'entityClass' => 'Doctrine\Tests\Models\CMS\CmsAddress', + 'discriminatorColumn' => null, + ), + ), + 'columns' => array(array( + 'name' => 'numphones', + ) + ) + )); + + } } 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/Models/Company/CompanyPerson.php b/tests/Doctrine/Tests/Models/Company/CompanyPerson.php index 0dfe9191c..1bc916d81 100644 --- a/tests/Doctrine/Tests/Models/Company/CompanyPerson.php +++ b/tests/Doctrine/Tests/Models/Company/CompanyPerson.php @@ -11,9 +11,39 @@ namespace Doctrine\Tests\Models\Company; * @InheritanceType("JOINED") * @DiscriminatorColumn(name="discr", type="string") * @DiscriminatorMap({ - * "person" = "CompanyPerson", - * "manager" = "CompanyManager", - * "employee" = "CompanyEmployee"}) + * "person" = "CompanyPerson", + * "manager" = "CompanyManager", + * "employee" = "CompanyEmployee" + * }) + * + * @NamedNativeQueries({ + * @NamedNativeQuery( + * name = "fetchAllWithResultClass", + * resultClass = "__CLASS__", + * query = "SELECT id, name, discr FROM company_persons ORDER BY name" + * ), + * @NamedNativeQuery( + * name = "fetchAllWithSqlResultSetMapping", + * resultSetMapping= "mappingFetchAll", + * query = "SELECT id, name, discr AS discriminator FROM company_persons ORDER BY name" + * ) + * }) + * + * @SqlResultSetMappings({ + * @SqlResultSetMapping( + * name = "mappingFetchAll", + * entities= { + * @EntityResult( + * entityClass = "__CLASS__", + * discriminatorColumn = "discriminator", + * fields = { + * @FieldResult("id"), + * @FieldResult("name"), + * } + * ) + * } + * ) + * }) */ class CompanyPerson { @@ -78,5 +108,45 @@ class CompanyPerson $this->spouse->setSpouse($this); } } + + public static function loadMetadata(\Doctrine\ORM\Mapping\ClassMetadataInfo $metadata) + { + + $metadata->setPrimaryTable(array( + 'name' => 'company_person', + )); + + $metadata->addNamedNativeQuery(array ( + 'name' => 'fetchAllWithResultClass', + 'query' => 'SELECT id, name, discr FROM company_persons ORDER BY name', + 'resultClass' => 'Doctrine\\Tests\\Models\\Company\\CompanyPerson', + )); + + $metadata->addNamedNativeQuery(array ( + 'name' => 'fetchAllWithSqlResultSetMapping', + 'query' => 'SELECT id, name, discr AS discriminator FROM company_persons ORDER BY name', + 'resultSetMapping' => 'mappingFetchAll', + )); + + $metadata->addSqlResultSetMapping(array ( + 'name' => 'mappingFetchAll', + 'columns' => array(), + 'entities' => array ( array ( + 'fields' => array ( + array ( + 'name' => 'id', + 'column' => 'id', + ), + array ( + 'name' => 'name', + 'column' => 'name', + ), + ), + 'entityClass' => 'Doctrine\Tests\Models\Company\CompanyPerson', + 'discriminatorColumn' => 'discriminator', + ), + ), + )); + } } diff --git a/tests/Doctrine/Tests/ORM/Functional/NativeQueryTest.php b/tests/Doctrine/Tests/ORM/Functional/NativeQueryTest.php index eb46329f1..a6a9016a4 100644 --- a/tests/Doctrine/Tests/ORM/Functional/NativeQueryTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/NativeQueryTest.php @@ -7,8 +7,11 @@ use Doctrine\ORM\Query\ResultSetMappingBuilder; use Doctrine\Tests\Models\CMS\CmsUser; use Doctrine\Tests\Models\CMS\CmsPhonenumber; use Doctrine\Tests\Models\CMS\CmsAddress; +use Doctrine\Tests\Models\CMS\CmsEmail; +use Doctrine\Tests\Models\CMS\CmsArticle; use Doctrine\Tests\Models\Company\CompanyFixContract; use Doctrine\Tests\Models\Company\CompanyEmployee; +use Doctrine\Tests\Models\Company\CompanyPerson; require_once __DIR__ . '/../../TestInit.php'; @@ -23,6 +26,7 @@ class NativeQueryTest extends \Doctrine\Tests\OrmFunctionalTestCase protected function setUp() { $this->useModelSet('cms'); + $this->useModelSet('company'); parent::setUp(); $this->platform = $this->_em->getConnection()->getDatabasePlatform(); } @@ -329,5 +333,372 @@ class NativeQueryTest extends \Doctrine\Tests\OrmFunctionalTestCase ); $users = $query->getResult(); } -} + + /** + * @group DDC-1663 + */ + public function testBasicNativeNamedQueryWithSqlResultSetMapping() + { + $user = new CmsUser; + $user->name = 'Fabio B. Silva'; + $user->username = 'FabioBatSilva'; + $user->status = 'dev'; + + $addr = new CmsAddress; + $addr->country = 'Brazil'; + $addr->zip = 10827; + $addr->city = 'São Paulo'; + + $user->setAddress($addr); + + $this->_em->clear(); + $this->_em->persist($user); + $this->_em->flush(); + + $this->_em->clear(); + + + $repository = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsAddress'); + $query = $repository->createNativeNamedQuery('find-all'); + $result = $query->getResult(); + + $this->assertCount(1, $result); + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsAddress', $result[0]); + $this->assertEquals($addr->id, $result[0]->id); + $this->assertEquals($addr->city, $result[0]->city); + $this->assertEquals($addr->country, $result[0]->country); + } + + /** + * @group DDC-1663 + */ + public function testBasicNativeNamedQueryWithResultClass() + { + $user = new CmsUser; + $user->name = 'Fabio B. Silva'; + $user->username = 'FabioBatSilva'; + $user->status = 'dev'; + + $email = new CmsEmail(); + $email->email = 'fabio.bat.silva@gmail.com'; + + $user->setEmail($email); + + $this->_em->clear(); + $this->_em->persist($user); + $this->_em->flush(); + + $this->_em->clear(); + + $repository = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsUser'); + + + $result = $repository->createNativeNamedQuery('fetchIdAndUsernameWithResultClass') + ->setParameter(1, 'FabioBatSilva')->getResult(); + + $this->assertEquals(1, count($result)); + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $result[0]); + $this->assertNull($result[0]->name); + $this->assertNull($result[0]->email); + $this->assertEquals($user->id, $result[0]->id); + $this->assertEquals('FabioBatSilva', $result[0]->username); + + $this->_em->clear(); + + + $result = $repository->createNativeNamedQuery('fetchAllColumns') + ->setParameter(1, 'FabioBatSilva')->getResult(); + + $this->assertEquals(1, count($result)); + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $result[0]); + $this->assertEquals($user->id, $result[0]->id); + $this->assertEquals('Fabio B. Silva', $result[0]->name); + $this->assertEquals('FabioBatSilva', $result[0]->username); + $this->assertEquals('dev', $result[0]->status); + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsEmail', $result[0]->email); + } + + + /** + * @group DDC-1663 + */ + public function testJoinedOneToOneNativeNamedQueryWithResultSetMapping() + { + $user = new CmsUser; + $user->name = 'Fabio B. Silva'; + $user->username = 'FabioBatSilva'; + $user->status = 'dev'; + + $addr = new CmsAddress; + $addr->country = 'Brazil'; + $addr->zip = 10827; + $addr->city = 'São Paulo'; + + + $user->setAddress($addr); + + $this->_em->persist($user); + $this->_em->flush(); + + $this->_em->clear(); + + $repository = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsUser'); + + + $result = $repository->createNativeNamedQuery('fetchJoinedAddress') + ->setParameter(1, 'FabioBatSilva')->getResult(); + + $this->assertEquals(1, count($result)); + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $result[0]); + $this->assertEquals('Fabio B. Silva', $result[0]->name); + $this->assertInstanceOf('Doctrine\ORM\PersistentCollection', $result[0]->getPhonenumbers()); + $this->assertFalse($result[0]->getPhonenumbers()->isInitialized()); + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsAddress', $result[0]->getAddress()); + $this->assertTrue($result[0]->getAddress()->getUser() == $result[0]); + $this->assertEquals('Brazil', $result[0]->getAddress()->getCountry()); + $this->assertEquals(10827, $result[0]->getAddress()->getZipCode()); + $this->assertEquals('São Paulo', $result[0]->getAddress()->getCity()); + } + + /** + * @group DDC-1663 + */ + public function testJoinedOneToManyNativeNamedQueryWithResultSetMapping() + { + $user = new CmsUser; + $user->name = 'Fabio B. Silva'; + $user->username = 'FabioBatSilva'; + $user->status = 'dev'; + + $phone = new CmsPhonenumber; + $phone->phonenumber = 424242; + + $user->addPhonenumber($phone); + + $this->_em->persist($user); + $this->_em->flush(); + + $this->_em->clear(); + + $repository = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsUser'); + + $result = $repository->createNativeNamedQuery('fetchJoinedPhonenumber') + ->setParameter(1, 'FabioBatSilva')->getResult(); + + $this->assertEquals(1, count($result)); + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $result[0]); + $this->assertEquals('Fabio B. Silva', $result[0]->name); + $this->assertInstanceOf('Doctrine\ORM\PersistentCollection', $result[0]->getPhonenumbers()); + $this->assertTrue($result[0]->getPhonenumbers()->isInitialized()); + $this->assertEquals(1, count($result[0]->getPhonenumbers())); + $phones = $result[0]->getPhonenumbers(); + $this->assertEquals(424242, $phones[0]->phonenumber); + $this->assertTrue($phones[0]->getUser() === $result[0]); + } + + /** + * @group DDC-1663 + */ + public function testMixedNativeNamedQueryNormalJoin() + { + $user1 = new CmsUser; + $user1->name = 'Fabio B. Silva'; + $user1->username = 'FabioBatSilva'; + $user1->status = 'dev'; + + $user2 = new CmsUser; + $user2->name = 'test tester'; + $user2->username = 'test'; + $user2->status = 'tester'; + + $phone1 = new CmsPhonenumber; + $phone2 = new CmsPhonenumber; + $phone3 = new CmsPhonenumber; + $phone1->phonenumber = 11111111; + $phone2->phonenumber = 22222222; + $phone3->phonenumber = 33333333; + + $user1->addPhonenumber($phone1); + $user1->addPhonenumber($phone2); + $user2->addPhonenumber($phone3); + + $this->_em->persist($user1); + $this->_em->persist($user2); + $this->_em->flush(); + + $this->_em->clear(); + + $repository = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsUser'); + + $result = $repository->createNativeNamedQuery('fetchUserPhonenumberCount') + ->setParameter(1, array('test','FabioBatSilva'))->getResult(); + + $this->assertEquals(2, count($result)); + $this->assertTrue(is_array($result[0])); + $this->assertTrue(is_array($result[1])); + + // first user => 2 phonenumbers + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $result[0][0]); + $this->assertEquals('Fabio B. Silva', $result[0][0]->name); + $this->assertEquals(2, $result[0]['numphones']); + + // second user => 1 phonenumbers + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $result[1][0]); + $this->assertEquals('test tester', $result[1][0]->name); + $this->assertEquals(1, $result[1]['numphones']); + } + + /** + * @group DDC-1663 + */ + public function testNativeNamedQueryInheritance() + { + $person = new CompanyPerson; + $person->setName('Fabio B. Silva'); + + $employee = new CompanyEmployee; + $employee->setName('Fabio Silva'); + $employee->setSalary(100000); + $employee->setDepartment('IT'); + + $this->_em->persist($person); + $this->_em->persist($employee); + + $this->_em->flush(); + $this->_em->clear(); + + $repository = $this->_em->getRepository('Doctrine\Tests\Models\Company\CompanyPerson'); + + $result = $repository->createNativeNamedQuery('fetchAllWithSqlResultSetMapping') + ->getResult(); + + $this->assertEquals(2, count($result)); + $this->assertInstanceOf('Doctrine\Tests\Models\Company\CompanyPerson', $result[0]); + $this->assertInstanceOf('Doctrine\Tests\Models\Company\CompanyEmployee', $result[1]); + $this->assertTrue(is_numeric($result[0]->getId())); + $this->assertTrue(is_numeric($result[1]->getId())); + $this->assertEquals('Fabio B. Silva', $result[0]->getName()); + $this->assertEquals('Fabio Silva', $result[1]->getName()); + + + $this->_em->clear(); + + + $result = $repository->createNativeNamedQuery('fetchAllWithResultClass') + ->getResult(); + + $this->assertEquals(2, count($result)); + $this->assertInstanceOf('Doctrine\Tests\Models\Company\CompanyPerson', $result[0]); + $this->assertInstanceOf('Doctrine\Tests\Models\Company\CompanyEmployee', $result[1]); + $this->assertTrue(is_numeric($result[0]->getId())); + $this->assertTrue(is_numeric($result[1]->getId())); + $this->assertEquals('Fabio B. Silva', $result[0]->getName()); + $this->assertEquals('Fabio Silva', $result[1]->getName()); + } + + /** + * @group DDC-1663 + * DQL : SELECT u, a, COUNT(p) AS numphones FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.address a JOIN u.phonenumbers p + */ + public function testMultipleEntityResults() + { + + $user = new CmsUser; + $user->name = 'Fabio B. Silva'; + $user->username = 'FabioBatSilva'; + $user->status = 'dev'; + + $addr = new CmsAddress; + $addr->country = 'Brazil'; + $addr->zip = 10827; + $addr->city = 'São Paulo'; + + $phone = new CmsPhonenumber; + $phone->phonenumber = 424242; + + + $user->setAddress($addr); + $user->addPhonenumber($phone); + + + $this->_em->clear(); + $this->_em->persist($user); + $this->_em->flush(); + + $this->_em->clear(); + + + $repository = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsUser'); + $query = $repository->createNativeNamedQuery('fetchMultipleJoinsEntityResults'); + $result = $query->getResult(); + + + $this->assertEquals(1, count($result)); + $this->assertTrue(is_array($result[0])); + + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $result[0][0]); + $this->assertEquals('Fabio B. Silva', $result[0][0]->name); + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsAddress', $result[0][0]->getAddress()); + $this->assertTrue($result[0][0]->getAddress()->getUser() == $result[0][0]); + $this->assertEquals('Brazil', $result[0][0]->getAddress()->getCountry()); + $this->assertEquals(10827, $result[0][0]->getAddress()->getZipCode()); + + $this->assertEquals(1, $result[0]['numphones']); + + } + + /** + * @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 diff --git a/tests/Doctrine/Tests/ORM/Hydration/ResultSetMappingTest.php b/tests/Doctrine/Tests/ORM/Hydration/ResultSetMappingTest.php index 785c19b74..801937f2d 100644 --- a/tests/Doctrine/Tests/ORM/Hydration/ResultSetMappingTest.php +++ b/tests/Doctrine/Tests/ORM/Hydration/ResultSetMappingTest.php @@ -93,5 +93,167 @@ class ResultSetMappingTest extends \Doctrine\Tests\OrmTestCase $this->assertTrue($rms->hasParentAlias('p')); $this->assertTrue($rms->isMixedResult()); } + + /** + * @group DDC-1663 + */ + public function testAddNamedNativeQueryResultSetMapping() + { + $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'); + $cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService); + + $cm->mapOneToOne(array( + 'fieldName' => 'email', + 'targetEntity' => 'Doctrine\Tests\Models\CMS\CmsEmail', + 'cascade' => array('persist'), + 'inversedBy' => 'user', + 'orphanRemoval' => false, + 'joinColumns' => array(array( + 'nullable' => true, + 'referencedColumnName' => 'id', + ) + ) + )); + + $cm->addNamedNativeQuery(array( + 'name' => 'find-all', + 'query' => 'SELECT u.id AS user_id, e.id AS email_id, u.name, e.email, u.id + e.id AS scalarColumn FROM cms_users u INNER JOIN cms_emails e ON e.id = u.email_id', + 'resultSetMapping' => 'find-all', + )); + $cm->addSqlResultSetMapping(array( + 'name' => 'find-all', + 'entities' => array( + array( + 'entityClass' => '__CLASS__', + 'fields' => array( + array( + 'name' => 'id', + 'column'=> 'user_id' + ), + array( + 'name' => 'name', + 'column'=> 'name' + ) + ) + ), + array( + 'entityClass' => 'CmsEmail', + 'fields' => array( + array( + 'name' => 'id', + 'column'=> 'email_id' + ), + array( + 'name' => 'email', + 'column'=> 'email' + ) + ) + ) + ), + 'columns' => array( + array( + 'name' => 'scalarColumn' + ) + ) + )); + + + $queryMapping = $cm->getNamedNativeQuery('find-all'); + + $rsm = new \Doctrine\ORM\Query\ResultSetMappingBuilder($this->_em); + $rsm->addNamedNativeQueryMapping($cm, $queryMapping); + + $this->assertEquals('scalarColumn', $rsm->getScalarAlias('scalarColumn')); + + $this->assertEquals('c0', $rsm->getEntityAlias('user_id')); + $this->assertEquals('c0', $rsm->getEntityAlias('name')); + $this->assertEquals('Doctrine\Tests\Models\CMS\CmsUser', $rsm->getClassName('c0')); + $this->assertEquals('Doctrine\Tests\Models\CMS\CmsUser', $rsm->getDeclaringClass('name')); + $this->assertEquals('Doctrine\Tests\Models\CMS\CmsUser', $rsm->getDeclaringClass('user_id')); + + + $this->assertEquals('c1', $rsm->getEntityAlias('email_id')); + $this->assertEquals('c1', $rsm->getEntityAlias('email')); + $this->assertEquals('Doctrine\Tests\Models\CMS\CmsEmail', $rsm->getClassName('c1')); + $this->assertEquals('Doctrine\Tests\Models\CMS\CmsEmail', $rsm->getDeclaringClass('email')); + $this->assertEquals('Doctrine\Tests\Models\CMS\CmsEmail', $rsm->getDeclaringClass('email_id')); + } + + /** + * @group DDC-1663 + */ + public function testAddNamedNativeQueryResultSetMappingWithoutFields() + { + $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'); + $cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService); + + $cm->addNamedNativeQuery(array( + 'name' => 'find-all', + 'query' => 'SELECT u.id AS user_id, e.id AS email_id, u.name, e.email, u.id + e.id AS scalarColumn FROM cms_users u INNER JOIN cms_emails e ON e.id = u.email_id', + 'resultSetMapping' => 'find-all', + )); + $cm->addSqlResultSetMapping(array( + 'name' => 'find-all', + 'entities' => array( + array( + 'entityClass' => '__CLASS__', + ) + ), + 'columns' => array( + array( + 'name' => 'scalarColumn' + ) + ) + )); + + + $queryMapping = $cm->getNamedNativeQuery('find-all'); + + $rsm = new \Doctrine\ORM\Query\ResultSetMappingBuilder($this->_em); + $rsm->addNamedNativeQueryMapping($cm, $queryMapping); + + $this->assertEquals('scalarColumn', $rsm->getScalarAlias('scalarColumn')); + + $this->assertEquals('c0', $rsm->getEntityAlias('id')); + $this->assertEquals('c0', $rsm->getEntityAlias('name')); + $this->assertEquals('c0', $rsm->getEntityAlias('status')); + $this->assertEquals('c0', $rsm->getEntityAlias('username')); + $this->assertEquals('Doctrine\Tests\Models\CMS\CmsUser', $rsm->getClassName('c0')); + $this->assertEquals('Doctrine\Tests\Models\CMS\CmsUser', $rsm->getDeclaringClass('id')); + $this->assertEquals('Doctrine\Tests\Models\CMS\CmsUser', $rsm->getDeclaringClass('name')); + $this->assertEquals('Doctrine\Tests\Models\CMS\CmsUser', $rsm->getDeclaringClass('status')); + $this->assertEquals('Doctrine\Tests\Models\CMS\CmsUser', $rsm->getDeclaringClass('username')); + } + + /** + * @group DDC-1663 + */ + public function testAddNamedNativeQueryResultClass() + { + $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'); + $cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService); + + $cm->addNamedNativeQuery(array( + 'name' => 'find-all', + 'resultClass' => '__CLASS__', + 'query' => 'SELECT * FROM cms_users', + )); + + $queryMapping = $cm->getNamedNativeQuery('find-all'); + + $rsm = new \Doctrine\ORM\Query\ResultSetMappingBuilder($this->_em); + $rsm->addNamedNativeQueryMapping($cm, $queryMapping); + + + $this->assertEquals('c0', $rsm->getEntityAlias('id')); + $this->assertEquals('c0', $rsm->getEntityAlias('name')); + $this->assertEquals('c0', $rsm->getEntityAlias('status')); + $this->assertEquals('c0', $rsm->getEntityAlias('username')); + $this->assertEquals('Doctrine\Tests\Models\CMS\CmsUser', $rsm->getClassName('c0')); + $this->assertEquals('Doctrine\Tests\Models\CMS\CmsUser', $rsm->getDeclaringClass('id')); + $this->assertEquals('Doctrine\Tests\Models\CMS\CmsUser', $rsm->getDeclaringClass('name')); + $this->assertEquals('Doctrine\Tests\Models\CMS\CmsUser', $rsm->getDeclaringClass('status')); + $this->assertEquals('Doctrine\Tests\Models\CMS\CmsUser', $rsm->getDeclaringClass('username')); + } } diff --git a/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php b/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php index 3233c3104..0efb3aca2 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php @@ -467,6 +467,7 @@ abstract class AbstractMappingDriverTest extends \Doctrine\Tests\OrmTestCase } /** +<<<<<<< HEAD * @group DDC-889 * @expectedException Doctrine\ORM\Mapping\MappingException * @expectedExceptionMessage Class "Doctrine\Tests\Models\DDC889\DDC889Class" sub class of "Doctrine\Tests\Models\DDC889\DDC889SuperClass" is not a valid entity or mapped super class. @@ -488,6 +489,130 @@ abstract class AbstractMappingDriverTest extends \Doctrine\Tests\OrmTestCase $factory->getMetadataFor('Doctrine\Tests\Models\DDC889\DDC889Entity'); } + + /** + * @group DDC-1663 + */ + public function testNamedNativeQuery() + { + + $class = $this->createClassMetadata('Doctrine\Tests\Models\CMS\CmsAddress'); + + //named native query + $this->assertCount(3, $class->namedNativeQueries); + $this->assertArrayHasKey('find-all', $class->namedNativeQueries); + $this->assertArrayHasKey('find-by-id', $class->namedNativeQueries); + + + $findAllQuery = $class->getNamedNativeQuery('find-all'); + $this->assertEquals('find-all', $findAllQuery['name']); + $this->assertEquals('mapping-find-all', $findAllQuery['resultSetMapping']); + $this->assertEquals('SELECT id, country, city FROM cms_addresses', $findAllQuery['query']); + + $findByIdQuery = $class->getNamedNativeQuery('find-by-id'); + $this->assertEquals('find-by-id', $findByIdQuery['name']); + $this->assertEquals('Doctrine\Tests\Models\CMS\CmsAddress',$findByIdQuery['resultClass']); + $this->assertEquals('SELECT * FROM cms_addresses WHERE id = ?', $findByIdQuery['query']); + + $countQuery = $class->getNamedNativeQuery('count'); + $this->assertEquals('count', $countQuery['name']); + $this->assertEquals('mapping-count', $countQuery['resultSetMapping']); + $this->assertEquals('SELECT COUNT(*) AS count FROM cms_addresses', $countQuery['query']); + + // result set mapping + $this->assertCount(3, $class->sqlResultSetMappings); + $this->assertArrayHasKey('mapping-count', $class->sqlResultSetMappings); + $this->assertArrayHasKey('mapping-find-all', $class->sqlResultSetMappings); + $this->assertArrayHasKey('mapping-without-fields', $class->sqlResultSetMappings); + + $findAllMapping = $class->getSqlResultSetMapping('mapping-find-all'); + $this->assertEquals('mapping-find-all', $findAllMapping['name']); + $this->assertEquals('Doctrine\Tests\Models\CMS\CmsAddress', $findAllMapping['entities'][0]['entityClass']); + $this->assertEquals(array('name'=>'id','column'=>'id'), $findAllMapping['entities'][0]['fields'][0]); + $this->assertEquals(array('name'=>'city','column'=>'city'), $findAllMapping['entities'][0]['fields'][1]); + $this->assertEquals(array('name'=>'country','column'=>'country'), $findAllMapping['entities'][0]['fields'][2]); + + $withoutFieldsMapping = $class->getSqlResultSetMapping('mapping-without-fields'); + $this->assertEquals('mapping-without-fields', $withoutFieldsMapping['name']); + $this->assertEquals('Doctrine\Tests\Models\CMS\CmsAddress', $withoutFieldsMapping['entities'][0]['entityClass']); + $this->assertEquals(array(), $withoutFieldsMapping['entities'][0]['fields']); + + $countMapping = $class->getSqlResultSetMapping('mapping-count'); + $this->assertEquals('mapping-count', $countMapping['name']); + $this->assertEquals(array('name'=>'count'), $countMapping['columns'][0]); + + } + + /** + * @group DDC-1663 + */ + public function testSqlResultSetMapping() + { + + $userMetadata = $this->createClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'); + $personMetadata = $this->createClassMetadata('Doctrine\Tests\Models\Company\CompanyPerson'); + + // user asserts + $this->assertCount(4, $userMetadata->getSqlResultSetMappings()); + + $mapping = $userMetadata->getSqlResultSetMapping('mappingJoinedAddress'); + $this->assertEquals(array(),$mapping['columns']); + $this->assertEquals('mappingJoinedAddress', $mapping['name']); + $this->assertNull($mapping['entities'][0]['discriminatorColumn']); + $this->assertEquals(array('name'=>'id','column'=>'id'), $mapping['entities'][0]['fields'][0]); + $this->assertEquals(array('name'=>'name','column'=>'name'), $mapping['entities'][0]['fields'][1]); + $this->assertEquals(array('name'=>'status','column'=>'status'), $mapping['entities'][0]['fields'][2]); + $this->assertEquals(array('name'=>'address.zip','column'=>'zip'), $mapping['entities'][0]['fields'][3]); + $this->assertEquals(array('name'=>'address.city','column'=>'city'), $mapping['entities'][0]['fields'][4]); + $this->assertEquals(array('name'=>'address.country','column'=>'country'), $mapping['entities'][0]['fields'][5]); + $this->assertEquals(array('name'=>'address.id','column'=>'a_id'), $mapping['entities'][0]['fields'][6]); + $this->assertEquals($userMetadata->name, $mapping['entities'][0]['entityClass']); + + + $mapping = $userMetadata->getSqlResultSetMapping('mappingJoinedPhonenumber'); + $this->assertEquals(array(),$mapping['columns']); + $this->assertEquals('mappingJoinedPhonenumber', $mapping['name']); + $this->assertNull($mapping['entities'][0]['discriminatorColumn']); + $this->assertEquals(array('name'=>'id','column'=>'id'), $mapping['entities'][0]['fields'][0]); + $this->assertEquals(array('name'=>'name','column'=>'name'), $mapping['entities'][0]['fields'][1]); + $this->assertEquals(array('name'=>'status','column'=>'status'), $mapping['entities'][0]['fields'][2]); + $this->assertEquals(array('name'=>'phonenumbers.phonenumber','column'=>'number'), $mapping['entities'][0]['fields'][3]); + $this->assertEquals($userMetadata->name, $mapping['entities'][0]['entityClass']); + + $mapping = $userMetadata->getSqlResultSetMapping('mappingUserPhonenumberCount'); + $this->assertEquals(array('name'=>'numphones'),$mapping['columns'][0]); + $this->assertEquals('mappingUserPhonenumberCount', $mapping['name']); + $this->assertNull($mapping['entities'][0]['discriminatorColumn']); + $this->assertEquals(array('name'=>'id','column'=>'id'), $mapping['entities'][0]['fields'][0]); + $this->assertEquals(array('name'=>'name','column'=>'name'), $mapping['entities'][0]['fields'][1]); + $this->assertEquals(array('name'=>'status','column'=>'status'), $mapping['entities'][0]['fields'][2]); + $this->assertEquals($userMetadata->name, $mapping['entities'][0]['entityClass']); + + $mapping = $userMetadata->getSqlResultSetMapping('mappingMultipleJoinsEntityResults'); + $this->assertEquals(array('name'=>'numphones'),$mapping['columns'][0]); + $this->assertEquals('mappingMultipleJoinsEntityResults', $mapping['name']); + $this->assertNull($mapping['entities'][0]['discriminatorColumn']); + $this->assertEquals(array('name'=>'id','column'=>'u_id'), $mapping['entities'][0]['fields'][0]); + $this->assertEquals(array('name'=>'name','column'=>'u_name'), $mapping['entities'][0]['fields'][1]); + $this->assertEquals(array('name'=>'status','column'=>'u_status'), $mapping['entities'][0]['fields'][2]); + $this->assertEquals($userMetadata->name, $mapping['entities'][0]['entityClass']); + $this->assertNull($mapping['entities'][1]['discriminatorColumn']); + $this->assertEquals(array('name'=>'id','column'=>'a_id'), $mapping['entities'][1]['fields'][0]); + $this->assertEquals(array('name'=>'zip','column'=>'a_zip'), $mapping['entities'][1]['fields'][1]); + $this->assertEquals(array('name'=>'country','column'=>'a_country'), $mapping['entities'][1]['fields'][2]); + $this->assertEquals('Doctrine\Tests\Models\CMS\CmsAddress', $mapping['entities'][1]['entityClass']); + + //person asserts + $this->assertCount(1, $personMetadata->getSqlResultSetMappings()); + + $mapping = $personMetadata->getSqlResultSetMapping('mappingFetchAll'); + $this->assertEquals(array(),$mapping['columns']); + $this->assertEquals('mappingFetchAll', $mapping['name']); + $this->assertEquals('discriminator', $mapping['entities'][0]['discriminatorColumn']); + $this->assertEquals(array('name'=>'id','column'=>'id'), $mapping['entities'][0]['fields'][0]); + $this->assertEquals(array('name'=>'name','column'=>'name'), $mapping['entities'][0]['fields'][1]); + $this->assertEquals($personMetadata->name, $mapping['entities'][0]['entityClass']); + } } /** diff --git a/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php b/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php index 4d7ec352f..fd6afe960 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php @@ -510,6 +510,29 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase $this->assertEquals(1, count($cm->getNamedQueries())); } + /** + * @group DDC-1663 + */ + public function testRetrievalOfResultSetMappings() + { + $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'); + $cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService); + + + $this->assertEquals(0, count($cm->getSqlResultSetMappings())); + + $cm->addSqlResultSetMapping(array( + 'name' => 'find-all', + 'entities' => array( + array( + 'entityClass' => 'Doctrine\Tests\Models\CMS\CmsUser', + ), + ), + )); + + $this->assertEquals(1, count($cm->getSqlResultSetMappings())); + } + public function testExistanceOfNamedQuery() { $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'); @@ -525,6 +548,138 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase $this->assertFalse($cm->hasNamedQuery('userById')); } + /** + * @group DDC-1663 + */ + public function testRetrieveOfNamedNativeQuery() + { + $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'); + $cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService); + + $cm->addNamedNativeQuery(array( + 'name' => 'find-all', + 'query' => 'SELECT * FROM cms_users', + 'resultSetMapping' => 'result-mapping-name', + 'resultClass' => 'Doctrine\Tests\Models\CMS\CmsUser', + )); + + $cm->addNamedNativeQuery(array( + 'name' => 'find-by-id', + 'query' => 'SELECT * FROM cms_users WHERE id = ?', + 'resultClass' => '__CLASS__', + 'resultSetMapping' => 'result-mapping-name', + )); + + $mapping = $cm->getNamedNativeQuery('find-all'); + $this->assertEquals('SELECT * FROM cms_users', $mapping['query']); + $this->assertEquals('result-mapping-name', $mapping['resultSetMapping']); + $this->assertEquals('Doctrine\Tests\Models\CMS\CmsUser', $mapping['resultClass']); + + $mapping = $cm->getNamedNativeQuery('find-by-id'); + $this->assertEquals('SELECT * FROM cms_users WHERE id = ?', $mapping['query']); + $this->assertEquals('result-mapping-name', $mapping['resultSetMapping']); + $this->assertEquals('Doctrine\Tests\Models\CMS\CmsUser', $mapping['resultClass']); + } + + /** + * @group DDC-1663 + */ + public function testRetrieveOfSqlResultSetMapping() + { + $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'); + $cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService); + + $cm->addSqlResultSetMapping(array( + 'name' => 'find-all', + 'entities' => array( + array( + 'entityClass' => '__CLASS__', + 'fields' => array( + array( + 'name' => 'id', + 'column'=> 'id' + ), + array( + 'name' => 'name', + 'column'=> 'name' + ) + ) + ), + array( + 'entityClass' => 'Doctrine\Tests\Models\CMS\CmsEmail', + 'fields' => array( + array( + 'name' => 'id', + 'column'=> 'id' + ), + array( + 'name' => 'email', + 'column'=> 'email' + ) + ) + ) + ), + 'columns' => array( + array( + 'name' => 'scalarColumn' + ) + ) + )); + + $mapping = $cm->getSqlResultSetMapping('find-all'); + + $this->assertEquals('Doctrine\Tests\Models\CMS\CmsUser', $mapping['entities'][0]['entityClass']); + $this->assertEquals(array('name'=>'id','column'=>'id'), $mapping['entities'][0]['fields'][0]); + $this->assertEquals(array('name'=>'name','column'=>'name'), $mapping['entities'][0]['fields'][1]); + + $this->assertEquals('Doctrine\Tests\Models\CMS\CmsEmail', $mapping['entities'][1]['entityClass']); + $this->assertEquals(array('name'=>'id','column'=>'id'), $mapping['entities'][1]['fields'][0]); + $this->assertEquals(array('name'=>'email','column'=>'email'), $mapping['entities'][1]['fields'][1]); + + $this->assertEquals('scalarColumn', $mapping['columns'][0]['name']); + } + + /** + * @group DDC-1663 + */ + public function testExistanceOfSqlResultSetMapping() + { + $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'); + $cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService); + + $cm->addSqlResultSetMapping(array( + 'name' => 'find-all', + 'entities' => array( + array( + 'entityClass' => 'Doctrine\Tests\Models\CMS\CmsUser', + ), + ), + )); + + $this->assertTrue($cm->hasSqlResultSetMapping('find-all')); + $this->assertFalse($cm->hasSqlResultSetMapping('find-by-id')); + } + + /** + * @group DDC-1663 + */ + public function testExistanceOfNamedNativeQuery() + { + $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'); + $cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService); + + + $cm->addNamedNativeQuery(array( + 'name' => 'find-all', + 'query' => 'SELECT * FROM cms_users', + 'resultClass' => 'Doctrine\Tests\Models\CMS\CmsUser', + 'resultSetMapping' => 'result-mapping-name' + )); + + $this->assertTrue($cm->hasNamedNativeQuery('find-all')); + $this->assertFalse($cm->hasNamedNativeQuery('find-by-id')); + } + public function testRetrieveOfNamedQuery() { $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'); @@ -539,13 +694,34 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase $this->assertEquals('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = ?1', $cm->getNamedQuery('userById')); } - public function testNamingCollisionNamedQueryShouldThrowException() + /** + * @group DDC-1663 + */ + public function testRetrievalOfNamedNativeQueries() { $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'); $cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService); + $this->assertEquals(0, count($cm->getNamedNativeQueries())); - $this->setExpectedException('Doctrine\ORM\Mapping\MappingException'); + $cm->addNamedNativeQuery(array( + 'name' => 'find-all', + 'query' => 'SELECT * FROM cms_users', + 'resultClass' => 'Doctrine\Tests\Models\CMS\CmsUser', + 'resultSetMapping' => 'result-mapping-name' + )); + + $this->assertEquals(1, count($cm->getNamedNativeQueries())); + } + + /** + * @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 + */ + public function testNamingCollisionNamedQueryShouldThrowException() + { + $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'); + $cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService); $cm->addNamedQuery(array( 'name' => 'userById', @@ -558,6 +734,62 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase )); } + /** + * @group DDC-1663 + * + * @expectedException \Doctrine\ORM\Mapping\MappingException + * @expectedExceptionMessage Query named "find-all" in "Doctrine\Tests\Models\CMS\CmsUser" was already declared, but it must be declared only once + */ + public function testNamingCollisionNamedNativeQueryShouldThrowException() + { + $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'); + $cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService); + + $cm->addNamedNativeQuery(array( + 'name' => 'find-all', + 'query' => 'SELECT * FROM cms_users', + 'resultClass' => 'Doctrine\Tests\Models\CMS\CmsUser', + 'resultSetMapping' => 'result-mapping-name' + )); + + $cm->addNamedNativeQuery(array( + 'name' => 'find-all', + 'query' => 'SELECT * FROM cms_users', + 'resultClass' => 'Doctrine\Tests\Models\CMS\CmsUser', + 'resultSetMapping' => 'result-mapping-name' + )); + } + + /** + * @group DDC-1663 + * + * @expectedException \Doctrine\ORM\Mapping\MappingException + * @expectedExceptionMessage Result set mapping named "find-all" in "Doctrine\Tests\Models\CMS\CmsUser" was already declared, but it must be declared only once + */ + public function testNamingCollisionSqlResultSetMappingShouldThrowException() + { + $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'); + $cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService); + + $cm->addSqlResultSetMapping(array( + 'name' => 'find-all', + 'entities' => array( + array( + 'entityClass' => 'Doctrine\Tests\Models\CMS\CmsUser', + ), + ), + )); + + $cm->addSqlResultSetMapping(array( + 'name' => 'find-all', + 'entities' => array( + array( + 'entityClass' => 'Doctrine\Tests\Models\CMS\CmsUser', + ), + ), + )); + } + /** * @group DDC-1068 */ @@ -596,6 +828,58 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase $cm->validateAssocations(); } + /** + * @group DDC-1663 + * + * @expectedException \Doctrine\ORM\Mapping\MappingException + * @expectedExceptionMessage Query name on entity class 'Doctrine\Tests\Models\CMS\CmsUser' is not defined. + */ + public function testNameIsMandatoryForNamedQueryMappingException() + { + $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'); + $cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService); + $cm->addNamedQuery(array( + 'query' => 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u', + )); + } + + /** + * @group DDC-1663 + * + * @expectedException \Doctrine\ORM\Mapping\MappingException + * @expectedExceptionMessage Query name on entity class 'Doctrine\Tests\Models\CMS\CmsUser' is not defined. + */ + public function testNameIsMandatoryForNameNativeQueryMappingException() + { + $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'); + $cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService); + $cm->addNamedQuery(array( + 'query' => 'SELECT * FROM cms_users', + 'resultClass' => 'Doctrine\Tests\Models\CMS\CmsUser', + 'resultSetMapping' => 'result-mapping-name' + )); + } + + /** + * @group DDC-1663 + * + * @expectedException \Doctrine\ORM\Mapping\MappingException + * @expectedExceptionMessage Result set mapping named "find-all" in "Doctrine\Tests\Models\CMS\CmsUser requires a entity class name. + */ + public function testNameIsMandatoryForEntityNameSqlResultSetMappingException() + { + $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'); + $cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService); + $cm->addSqlResultSetMapping(array( + 'name' => 'find-all', + 'entities' => array( + array( + 'fields' => array() + ) + ), + )); + } + /** * @expectedException \Doctrine\ORM\Mapping\MappingException * @expectedExceptionMessage Discriminator column name on entity class 'Doctrine\Tests\Models\CMS\CmsUser' is not defined. diff --git a/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.Models.CMS.CmsAddress.php b/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.Models.CMS.CmsAddress.php new file mode 100644 index 000000000..964065cbd --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.Models.CMS.CmsAddress.php @@ -0,0 +1,69 @@ +setPrimaryTable(array( + 'name' => 'company_person', +)); + + +$metadata->addNamedNativeQuery(array ( + 'name' => 'find-all', + 'query' => 'SELECT id, country, city FROM cms_addresses', + 'resultSetMapping' => 'mapping-find-all', +)); + +$metadata->addNamedNativeQuery(array ( + 'name' => 'find-by-id', + 'query' => 'SELECT * FROM cms_addresses WHERE id = ?', + 'resultClass' => 'Doctrine\\Tests\\Models\\CMS\\CmsAddress', +)); + +$metadata->addNamedNativeQuery(array ( + 'name' => 'count', + 'query' => 'SELECT COUNT(*) AS count FROM cms_addresses', + 'resultSetMapping' => 'mapping-count', +)); + + +$metadata->addSqlResultSetMapping(array ( + 'name' => 'mapping-find-all', + 'columns' => array(), + 'entities' => array ( array ( + 'fields' => array ( + array ( + 'name' => 'id', + 'column' => 'id', + ), + array ( + 'name' => 'city', + 'column' => 'city', + ), + array ( + 'name' => 'country', + 'column' => 'country', + ), + ), + 'entityClass' => 'Doctrine\Tests\Models\CMS\CmsAddress', + ), + ), +)); + +$metadata->addSqlResultSetMapping(array ( + 'name' => 'mapping-without-fields', + 'columns' => array(), + 'entities' => array(array ( + 'entityClass' => 'Doctrine\\Tests\\Models\\CMS\\CmsAddress', + 'fields' => array() + ) + ) +)); + +$metadata->addSqlResultSetMapping(array ( + 'name' => 'mapping-count', + 'columns' =>array ( + array ( + 'name' => 'count', + ), + ) +)); \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.Models.CMS.CmsUser.php b/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.Models.CMS.CmsUser.php new file mode 100644 index 000000000..9484bf750 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.Models.CMS.CmsUser.php @@ -0,0 +1,186 @@ +setPrimaryTable(array( + 'name' => 'cms_users', +)); + +$metadata->addNamedNativeQuery(array ( + 'name' => 'fetchIdAndUsernameWithResultClass', + 'query' => 'SELECT id, username FROM cms_users WHERE username = ?', + 'resultClass' => 'Doctrine\\Tests\\Models\\CMS\\CmsUser', +)); + +$metadata->addNamedNativeQuery(array ( + 'name' => 'fetchAllColumns', + 'query' => 'SELECT * FROM cms_users WHERE username = ?', + 'resultClass' => 'Doctrine\\Tests\\Models\\CMS\\CmsUser', +)); + +$metadata->addNamedNativeQuery(array ( + 'name' => 'fetchJoinedAddress', + 'query' => 'SELECT u.id, u.name, u.status, a.id AS a_id, a.country, a.zip, a.city FROM cms_users u INNER JOIN cms_addresses a ON u.id = a.user_id WHERE u.username = ?', + 'resultSetMapping' => 'mappingJoinedAddress', +)); + +$metadata->addNamedNativeQuery(array ( + 'name' => 'fetchJoinedPhonenumber', + 'query' => 'SELECT id, name, status, phonenumber AS number FROM cms_users INNER JOIN cms_phonenumbers ON id = user_id WHERE username = ?', + 'resultSetMapping' => 'mappingJoinedPhonenumber', +)); + +$metadata->addNamedNativeQuery(array ( + 'name' => 'fetchUserPhonenumberCount', + 'query' => 'SELECT id, name, status, COUNT(phonenumber) AS numphones FROM cms_users INNER JOIN cms_phonenumbers ON id = user_id WHERE username IN (?) GROUP BY id, name, status, username ORDER BY username', + 'resultSetMapping' => 'mappingUserPhonenumberCount', +)); + +$metadata->addNamedNativeQuery(array ( + "name" => "fetchMultipleJoinsEntityResults", + "resultSetMapping" => "mappingMultipleJoinsEntityResults", + "query" => "SELECT u.id AS u_id, u.name AS u_name, u.status AS u_status, a.id AS a_id, a.zip AS a_zip, a.country AS a_country, COUNT(p.phonenumber) AS numphones FROM cms_users u INNER JOIN cms_addresses a ON u.id = a.user_id INNER JOIN cms_phonenumbers p ON u.id = p.user_id GROUP BY u.id, u.name, u.status, u.username, a.id, a.zip, a.country ORDER BY u.username" +)); + +$metadata->addSqlResultSetMapping(array ( + 'name' => 'mappingJoinedAddress', + 'columns' => array(), + 'entities' => array(array ( + 'fields'=> array ( + array ( + 'name' => 'id', + 'column' => 'id', + ), + array ( + 'name' => 'name', + 'column' => 'name', + ), + array ( + 'name' => 'status', + 'column' => 'status', + ), + array ( + 'name' => 'address.zip', + 'column' => 'zip', + ), + array ( + 'name' => 'address.city', + 'column' => 'city', + ), + array ( + 'name' => 'address.country', + 'column' => 'country', + ), + array ( + 'name' => 'address.id', + 'column' => 'a_id', + ), + ), + 'entityClass' => 'Doctrine\Tests\Models\CMS\CmsUser', + 'discriminatorColumn' => null + ), + ), +)); + +$metadata->addSqlResultSetMapping(array ( + 'name' => 'mappingJoinedPhonenumber', + 'columns' => array(), + 'entities' => array(array( + 'fields'=> array ( + array ( + 'name' => 'id', + 'column' => 'id', + ), + array ( + 'name' => 'name', + 'column' => 'name', + ), + array ( + 'name' => 'status', + 'column' => 'status', + ), + array ( + 'name' => 'phonenumbers.phonenumber', + 'column' => 'number', + ), + ), + 'entityClass' => 'Doctrine\\Tests\\Models\\CMS\\CmsUser', + 'discriminatorColumn' => null + ), + ), +)); + +$metadata->addSqlResultSetMapping(array ( + 'name' => 'mappingUserPhonenumberCount', + 'columns' => array(), + 'entities' => array ( + array( + 'fields' => array ( + array ( + 'name' => 'id', + 'column' => 'id', + ), + array ( + 'name' => 'name', + 'column' => 'name', + ), + array ( + 'name' => 'status', + 'column' => 'status', + ) + ), + 'entityClass' => 'Doctrine\Tests\Models\CMS\CmsUser', + 'discriminatorColumn' => null + ) + ), + 'columns' => array ( + array ( + 'name' => 'numphones', + ) + ) +)); + +$metadata->addSqlResultSetMapping(array( + 'name' => 'mappingMultipleJoinsEntityResults', + 'entities' => array(array( + 'fields' => array( + array( + 'name' => 'id', + 'column' => 'u_id', + ), + array( + 'name' => 'name', + 'column' => 'u_name', + ), + array( + 'name' => 'status', + 'column' => 'u_status', + ) + ), + 'entityClass' => 'Doctrine\Tests\Models\CMS\CmsUser', + 'discriminatorColumn' => null, + ), + array( + 'fields' => array( + array( + 'name' => 'id', + 'column' => 'a_id', + ), + array( + 'name' => 'zip', + 'column' => 'a_zip', + ), + array( + 'name' => 'country', + 'column' => 'a_country', + ), + ), + 'entityClass' => 'Doctrine\Tests\Models\CMS\CmsAddress', + 'discriminatorColumn' => null, + ), + ), + 'columns' => array(array( + 'name' => 'numphones', + ) + ) +)); \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.Models.Company.CompanyPerson.php b/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.Models.Company.CompanyPerson.php new file mode 100644 index 000000000..68703f40a --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.Models.Company.CompanyPerson.php @@ -0,0 +1,39 @@ +setPrimaryTable(array( + 'name' => 'company_person', +)); + +$metadata->addNamedNativeQuery(array ( + 'name' => 'fetchAllWithResultClass', + 'query' => 'SELECT id, name, discr FROM company_persons ORDER BY name', + 'resultClass' => 'Doctrine\\Tests\\Models\\Company\\CompanyPerson', +)); + +$metadata->addNamedNativeQuery(array ( + 'name' => 'fetchAllWithSqlResultSetMapping', + 'query' => 'SELECT id, name, discr AS discriminator FROM company_persons ORDER BY name', + 'resultSetMapping' => 'mappingFetchAll', +)); + +$metadata->addSqlResultSetMapping(array ( + 'name' => 'mappingFetchAll', + 'columns' => array(), + 'entities' => array ( array ( + 'fields' => array ( + array ( + 'name' => 'id', + 'column' => 'id', + ), + array ( + 'name' => 'name', + 'column' => 'name', + ), + ), + 'entityClass' => 'Doctrine\Tests\Models\Company\CompanyPerson', + 'discriminatorColumn' => 'discriminator', + ), + ), +)); \ No newline at end of file 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 new file mode 100644 index 000000000..0af5facda --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.CMS.CmsAddress.dcm.xml @@ -0,0 +1,55 @@ + + + + + + + + + SELECT id, country, city FROM cms_addresses + + + + SELECT * FROM cms_addresses WHERE id = ? + + + + SELECT COUNT(*) AS count FROM cms_addresses + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ 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 new file mode 100644 index 000000000..64a545df6 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.CMS.CmsUser.dcm.xml @@ -0,0 +1,139 @@ + + + + + + + + + + + + + SELECT id, username FROM cms_users WHERE username = ? + + + + SELECT * FROM cms_users WHERE username = ? + + + + SELECT u.id, u.name, u.status, a.id AS a_id, a.country, a.zip, a.city FROM cms_users u INNER JOIN cms_addresses a ON u.id = a.user_id WHERE u.username = ? + + + + SELECT id, name, status, phonenumber AS number FROM cms_users INNER JOIN cms_phonenumbers ON id = user_id WHERE username = ? + + + + SELECT id, name, status, COUNT(phonenumber) AS numphones FROM cms_users INNER JOIN cms_phonenumbers ON id = user_id WHERE username IN (?) GROUP BY id, name, status, username ORDER BY username + + + + SELECT u.id AS u_id, u.name AS u_name, u.status AS u_status, a.id AS a_id, a.zip AS a_zip, a.country AS a_country, COUNT(p.phonenumber) AS numphones FROM cms_users u INNER JOIN cms_addresses a ON u.id = a.user_id INNER JOIN cms_phonenumbers p ON u.id = p.user_id GROUP BY u.id, u.name, u.status, u.username, a.id, a.zip, a.country ORDER BY u.username + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 new file mode 100644 index 000000000..c573504e0 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.Company.CompanyPerson.dcm.xml @@ -0,0 +1,57 @@ + + + + + + + + + SELECT id, name, discr FROM company_persons ORDER BY name + + + + SELECT id, name, discr AS discriminator FROM company_persons ORDER BY name + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.CMS.CmsAddress.dcm.yml b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.CMS.CmsAddress.dcm.yml new file mode 100644 index 000000000..604acb293 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.CMS.CmsAddress.dcm.yml @@ -0,0 +1,62 @@ +Doctrine\Tests\Models\CMS\CmsAddress: + type: entity + table: cms_address + namedNativeQueries: + find-all: + resultSetMapping: mapping-find-all + query: SELECT id, country, city FROM cms_addresses + find-by-id: + name: find-by-id + resultClass: CmsAddress + query: SELECT * FROM cms_addresses WHERE id = ? + count: + name: count + resultSetMapping: mapping-count + query: SELECT COUNT(*) AS count FROM cms_addresses + + sqlResultSetMappings: + mapping-find-all: + entityResult: + address: + entityClass: CmsAddress + fieldResult: + 0: + name: id + column: id + 1: + name: city + column: city + 2: + name: country + column: country + mapping-without-fields: + name: mapping-without-fields + entityResult: + address: + entityClass: CmsAddress + mapping-count: + name: mapping-count + columnResult: + count: + name: count + id: + id: + type: integer + generator: + strategy: AUTO + fields: + country: + type: string + length: 50 + city: + type: string + length: 50 + zip: + type: string + length: 50 + oneToOne: + address: + targetEntity: CmsUser + inversedBy: address + joinColumn: + referencedColumnName: id \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.CMS.CmsUser.dcm.yml b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.CMS.CmsUser.dcm.yml new file mode 100644 index 000000000..3a03dd6c3 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.CMS.CmsUser.dcm.yml @@ -0,0 +1,158 @@ +Doctrine\Tests\Models\CMS\CmsUser: + type: entity + table: cms_users + namedQueries: + all: SELECT u FROM __CLASS__ u + namedNativeQueries: + fetchIdAndUsernameWithResultClass: + resultClass: CmsUser + query: SELECT id, username FROM cms_users WHERE username = ? + fetchAllColumns: + name: fetchAllColumns + resultClass: CmsUser + query: SELECT * FROM cms_users WHERE username = ? + fetchJoinedAddress: + name: fetchJoinedAddress + resultSetMapping: mappingJoinedAddress + query: SELECT u.id, u.name, u.status, a.id AS a_id, a.country, a.zip, a.city FROM cms_users u INNER JOIN cms_addresses a ON u.id = a.user_id WHERE u.username = ? + fetchJoinedPhonenumber: + name: fetchJoinedPhonenumber + resultSetMapping: mappingJoinedPhonenumber + query: SELECT id, name, status, phonenumber AS number FROM cms_users INNER JOIN cms_phonenumbers ON id = user_id WHERE username = ? + fetchUserPhonenumberCount: + name: fetchUserPhonenumberCount + resultSetMapping: mappingUserPhonenumberCount + query: SELECT id, name, status, COUNT(phonenumber) AS numphones FROM cms_users INNER JOIN cms_phonenumbers ON id = user_id WHERE username IN (?) GROUP BY id, name, status, username ORDER BY username + fetchMultipleJoinsEntityResults: + name: fetchMultipleJoinsEntityResults + resultSetMapping: mappingMultipleJoinsEntityResults + query: SELECT u.id AS u_id, u.name AS u_name, u.status AS u_status, a.id AS a_id, a.zip AS a_zip, a.country AS a_country, COUNT(p.phonenumber) AS numphones FROM cms_users u INNER JOIN cms_addresses a ON u.id = a.user_id INNER JOIN cms_phonenumbers p ON u.id = p.user_id GROUP BY u.id, u.name, u.status, u.username, a.id, a.zip, a.country ORDER BY u.username + + sqlResultSetMappings: + mappingJoinedAddress: + entityResult: + 0: + entityClass: __CLASS__ + fieldResult: + 0: + name: id + 1: + name: name + 2: + name: status + 3: + name: address.zip + 4: + name: address.city + 5: + name: address.country + 6: + name: address.id + column: a_id + mappingJoinedPhonenumber: + name: mappingJoinedPhonenumber + entityResult: + user: + entityClass: CmsUser + fieldResult: + 0: + name: id + 1: + name: name + 2: + name: status + 3: + name: phonenumbers.phonenumber + column: number + mappingUserPhonenumberCount: + name: mappingUserPhonenumberCount + columnResult: + 0: + name: numphones + entityResult: + user_0: + entityClass: CmsUser + fieldResult: + 0: + name: id + 1: + name: name + 2: + name: status + mappingMultipleJoinsEntityResults: + name: mappingMultipleJoinsEntityResults + columnResult: + 0: + name: numphones + entityResult: + 0: + entityClass: __CLASS__ + fieldResult: + 0: + name: id + column: u_id + 1: + name: name + column: u_name + 2: + name: status + column: u_status + 1: + entityClass: CmsAddress + fieldResult: + 0: + name: id + column: a_id + 1: + name: zip + column: a_zip + 2: + name: country + column: a_country + id: + id: + type: integer + generator: + strategy: AUTO + fields: + name: + type: string + length: 255 + username: + type: string + length: 255 + unique: true + status: + type: string + length: 50 + unique: true + oneToOne: + address: + targetEntity: CmsAddress + orphanRemoval: true + inversedBy: user + joinColumn: + name: address_id + referencedColumnName: id + cascade: [ persist ] + oneToOne: + email: + targetEntity: CmsEmail + orphanRemoval: true + inversedBy: user + joinColumn: + nullable: true + referencedColumnName: id + cascade: [ persist ] + manyToMany: + groups: + targetEntity: CmsGroup + joinTable: + name: cms_users_groups + joinColumns: + user_id: + referencedColumnName: id + inverseJoinColumns: + group_id: + referencedColumnName: id + cascade: [ persist , detach, merge] \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.Company.CompanyPerson.dcm.yml b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.Company.CompanyPerson.dcm.yml new file mode 100644 index 000000000..26846c5de --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.Company.CompanyPerson.dcm.yml @@ -0,0 +1,75 @@ +Doctrine\Tests\Models\Company\CompanyPerson: + type: entity + table: company_persons + inheritanceType: JOINED + discriminatorMap: + person: CompanyPerson + manager: CompanyManager + employee: CompanyEmployee + namedNativeQueries: + fetchAllWithResultClass: + resultClass: __CLASS__ + query: SELECT id, name, discr FROM company_persons ORDER BY name + fetchAllWithSqlResultSetMapping: + name: fetchAllWithSqlResultSetMapping + resultSetMapping: mappingFetchAll + query: SELECT id, name, discr AS discriminator FROM company_persons ORDER BY name + + sqlResultSetMappings: + mappingFetchAll: + entityResult: + 0: + entityClass: __CLASS__ + discriminatorColumn: discriminator + fieldResult: + 0: + name: id + 1: + name: name + id: + id: + type: integer + generator: + strategy: AUTO + fields: + name: + type: string + length: 255 + username: + type: string + length: 255 + unique: true + status: + type: string + length: 50 + unique: true + oneToOne: + address: + targetEntity: CmsAddress + orphanRemoval: true + inversedBy: user + joinColumn: + name: address_id + referencedColumnName: id + cascade: [ persist ] + oneToOne: + email: + targetEntity: CmsEmail + orphanRemoval: true + inversedBy: user + joinColumn: + nullable: true + referencedColumnName: id + cascade: [ persist ] + manyToMany: + groups: + targetEntity: CmsGroup + joinTable: + name: cms_users_groups + joinColumns: + user_id: + referencedColumnName: id + inverseJoinColumns: + group_id: + referencedColumnName: id + cascade: [ persist , detach, merge] \ No newline at end of file diff --git a/tests/Doctrine/Tests/OrmFunctionalTestCase.php b/tests/Doctrine/Tests/OrmFunctionalTestCase.php index 4b92ed5d3..9d9776ba9 100644 --- a/tests/Doctrine/Tests/OrmFunctionalTestCase.php +++ b/tests/Doctrine/Tests/OrmFunctionalTestCase.php @@ -147,6 +147,7 @@ abstract class OrmFunctionalTestCase extends OrmTestCase $conn->executeUpdate('DELETE FROM cms_comments'); $conn->executeUpdate('DELETE FROM cms_articles'); $conn->executeUpdate('DELETE FROM cms_users'); + $conn->executeUpdate('DELETE FROM cms_emails'); } if (isset($this->_usedModelSets['ecommerce'])) {