1
0
mirror of synced 2025-01-17 22:11:41 +03:00

named native query metadata

This commit is contained in:
Fabio B. Silva 2012-02-25 23:13:46 -02:00
parent 530e4840dd
commit 91e4702772
3 changed files with 270 additions and 7 deletions

View File

@ -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
* <tt>Class Table Inheritance</tt>.
*/
const INHERITANCE_TYPE_JOINED = 2;
/**
* SINGLE_TABLE means the class will be persisted according to the rules of
* <tt>Single Table Inheritance</tt>.
*/
const INHERITANCE_TYPE_SINGLE_TABLE = 3;
/**
* TABLE_PER_CLASS means the class will be persisted according to the rules
* of <tt>Concrete Table Inheritance</tt>.
@ -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 <tt>NotifyPropertyChanged</tt> 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,21 @@ 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:
* <pre>
* array(
* 'name' => <query name>,
* 'query' => <sql query>,
* 'resultClass' => <class of the result>,
* 'resultSetMapping' => <name of a SqlResultSetMapping>
* )
* </pre>
*/
public $namedNativeQueries = array();
/**
* READ-ONLY: The field names of all fields that are part of the identifier/primary key
* of the mapped entity class.
@ -1051,6 +1085,33 @@ 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;
}
/**
* Validates & completes the given field mapping.
*
@ -1826,10 +1887,18 @@ class ClassMetadataInfo implements ClassMetadata
*/
public function addNamedQuery(array $queryMapping)
{
if (!isset($queryMapping['name'])) {
throw MappingException::nameIsMandatoryQueryMapping($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 +1909,38 @@ 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::nameIsMandatoryQueryMapping($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']);
}
if (isset($queryMapping['resultClass']) && $queryMapping['resultClass'] === '__CLASS__') {
$queryMapping['resultClass'] = $this->name;
}
$this->namedNativeQueries[$queryMapping['name']] = $queryMapping;
}
/**
* Adds a one-to-one mapping.
*
@ -2061,6 +2162,17 @@ 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 $fieldName
* @return boolean
*/
public function hasNamedNativeQuery($queryName)
{
return isset($this->namedNativeQueries[$queryName]);
}
/**
* Checks whether the class has a mapped association with the given field name.
*

View File

@ -93,6 +93,21 @@ class MappingException extends \Doctrine\ORM\ORMException
return new self("No query found named '$queryName' 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)
{
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 oneToManyRequiresMappedBy($fieldName)
{
return new self("OneToMany mapping on field '$fieldName' requires the 'mappedBy' attribute.");
@ -178,27 +193,31 @@ 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 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 +243,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."

View File

@ -525,6 +525,59 @@ 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 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,6 +592,26 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
$this->assertEquals('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = ?1', $cm->getNamedQuery('userById'));
}
/**
* @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()));
$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()));
}
public function testNamingCollisionNamedQueryShouldThrowException()
{
$cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
@ -558,6 +631,32 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
));
}
/**
* @group DDC-1663
*/
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',
'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-1068
*/
@ -596,6 +695,38 @@ 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'
));
}
/**
* @expectedException \Doctrine\ORM\Mapping\MappingException
* @expectedExceptionMessage Discriminator column name on entity class 'Doctrine\Tests\Models\CMS\CmsUser' is not defined.