sql result set mapping metadata
This commit is contained in:
parent
91e4702772
commit
2b996128af
7 changed files with 325 additions and 18 deletions
|
@ -262,15 +262,29 @@ class ClassMetadataInfo implements ClassMetadata
|
|||
* A native SQL named query definition has the following structure:
|
||||
* <pre>
|
||||
* array(
|
||||
* 'name' => <query name>,
|
||||
* 'query' => <sql query>,
|
||||
* 'resultClass' => <class of the result>,
|
||||
* 'resultSetMapping' => <name of a SqlResultSetMapping>
|
||||
* 'name' => <query name>,
|
||||
* 'query' => <sql query>,
|
||||
* 'resultClass' => <class of the result>,
|
||||
* 'resultSetMapping' => <name of a SqlResultSetMapping>
|
||||
* )
|
||||
* </pre>
|
||||
*/
|
||||
public $namedNativeQueries = array();
|
||||
|
||||
/**
|
||||
* READ-ONLY: The mappings of the results of native SQL queries.
|
||||
*
|
||||
* A native result mapping definition has the following structure:
|
||||
* <pre>
|
||||
* array(
|
||||
* 'name' => <result name>,
|
||||
* 'entities' => array(<entity result mapping>),
|
||||
* 'columns' => array(<column result mapping>)
|
||||
* )
|
||||
* </pre>
|
||||
*/
|
||||
public $sqlResultSetMappings = array();
|
||||
|
||||
/**
|
||||
* READ-ONLY: The field names of all fields that are part of the identifier/primary key
|
||||
* of the mapped entity class.
|
||||
|
@ -782,6 +796,14 @@ class ClassMetadataInfo implements ClassMetadata
|
|||
$serialized[] = 'namedQueries';
|
||||
}
|
||||
|
||||
if ($this->namedNativeQueries) {
|
||||
$serialized[] = 'namedNativeQueries';
|
||||
}
|
||||
|
||||
if ($this->sqlResultSetMappings) {
|
||||
$serialized[] = 'sqlResultSetMappings';
|
||||
}
|
||||
|
||||
if ($this->isReadOnly) {
|
||||
$serialized[] = 'isReadOnly';
|
||||
}
|
||||
|
@ -1112,6 +1134,33 @@ class ClassMetadataInfo implements ClassMetadata
|
|||
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.
|
||||
*
|
||||
|
@ -1888,7 +1937,7 @@ class ClassMetadataInfo implements ClassMetadata
|
|||
public function addNamedQuery(array $queryMapping)
|
||||
{
|
||||
if (!isset($queryMapping['name'])) {
|
||||
throw MappingException::nameIsMandatoryQueryMapping($this->name);
|
||||
throw MappingException::nameIsMandatoryForQueryMapping($this->name);
|
||||
}
|
||||
|
||||
if (isset($this->namedQueries[$queryMapping['name']])) {
|
||||
|
@ -1919,7 +1968,7 @@ class ClassMetadataInfo implements ClassMetadata
|
|||
public function addNamedNativeQuery(array $queryMapping)
|
||||
{
|
||||
if (!isset($queryMapping['name'])) {
|
||||
throw MappingException::nameIsMandatoryQueryMapping($this->name);
|
||||
throw MappingException::nameIsMandatoryForQueryMapping($this->name);
|
||||
}
|
||||
|
||||
if (isset($this->namedNativeQueries[$queryMapping['name']])) {
|
||||
|
@ -1941,6 +1990,38 @@ class ClassMetadataInfo implements ClassMetadata
|
|||
$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']);
|
||||
}
|
||||
|
||||
if ($entityResult['entityClass'] === '__CLASS__') {
|
||||
$resultMapping['entities'][$key]['entityClass'] = $this->name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->sqlResultSetMappings[$resultMapping['name']] = $resultMapping;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a one-to-one mapping.
|
||||
*
|
||||
|
@ -2154,7 +2235,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)
|
||||
|
@ -2165,7 +2246,7 @@ class ClassMetadataInfo implements ClassMetadata
|
|||
/**
|
||||
* Checks whether the class has a named native query with the given query name.
|
||||
*
|
||||
* @param string $fieldName
|
||||
* @param string $queryName
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasNamedNativeQuery($queryName)
|
||||
|
@ -2173,6 +2254,17 @@ class ClassMetadataInfo implements ClassMetadata
|
|||
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.
|
||||
*
|
||||
|
|
42
lib/Doctrine/ORM/Mapping/ColumnResult.php
Normal file
42
lib/Doctrine/ORM/Mapping/ColumnResult.php
Normal file
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
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 <fabio.bat.silva@gmail.com>
|
||||
* @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;
|
||||
|
||||
}
|
|
@ -93,12 +93,17 @@ 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 nameIsMandatoryQueryMapping($className)
|
||||
public static function nameIsMandatoryForQueryMapping($className)
|
||||
{
|
||||
return new self("Query name on entity class '$className' is not defined.");
|
||||
}
|
||||
|
@ -108,6 +113,16 @@ class MappingException extends \Doctrine\ORM\ORMException
|
|||
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 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.");
|
||||
|
@ -211,6 +226,11 @@ class MappingException extends \Doctrine\ORM\ORMException
|
|||
return new self('Query named "'.$queryName.'" in "'.$entity.'" was already declared, but it must be declared only once');
|
||||
}
|
||||
|
||||
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);
|
||||
|
|
|
@ -37,4 +37,4 @@ final class NamedNativeQueries implements Annotation
|
|||
* @var array<\Doctrine\ORM\Mapping\NamedNativeQuery>
|
||||
*/
|
||||
public $value;
|
||||
}
|
||||
}
|
|
@ -60,4 +60,4 @@ final class NamedNativeQuery implements Annotation
|
|||
*/
|
||||
public $resultSetMapping;
|
||||
|
||||
}
|
||||
}
|
|
@ -37,4 +37,4 @@ final class SqlResultSetMappings implements Annotation
|
|||
* @var array<\Doctrine\ORM\Mapping\SqlResultSetMapping>
|
||||
*/
|
||||
public $value;
|
||||
}
|
||||
}
|
|
@ -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');
|
||||
|
@ -558,6 +581,85 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
|
|||
$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
|
||||
*/
|
||||
|
@ -612,14 +714,15 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
|
|||
$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);
|
||||
|
||||
|
||||
$this->setExpectedException('Doctrine\ORM\Mapping\MappingException');
|
||||
|
||||
$cm->addNamedQuery(array(
|
||||
'name' => 'userById',
|
||||
'query' => 'SELECT u FROM __CLASS__ u WHERE u.id = ?1'
|
||||
|
@ -633,15 +736,15 @@ 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);
|
||||
|
||||
|
||||
$this->setExpectedException('Doctrine\ORM\Mapping\MappingException');
|
||||
|
||||
$cm->addNamedNativeQuery(array(
|
||||
'name' => 'find-all',
|
||||
'query' => 'SELECT * FROM cms_users',
|
||||
|
@ -657,6 +760,36 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
|
|||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
|
@ -727,6 +860,26 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
|
|||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @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.
|
||||
|
|
Loading…
Add table
Reference in a new issue