Added support to NamedQueries through ClassMetadata.
This commit is contained in:
parent
3eea19dcfa
commit
a31289b9d7
@ -56,6 +56,17 @@
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="named-query">
|
||||
<xs:attribute name="name" type="xs:string" use="required" />
|
||||
<xs:attribute name="query" type="xs:string" use="required" />
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="named-queries">
|
||||
<xs:sequence>
|
||||
<xs:element name="named-query" type="orm:named-query" minOccurs="1" maxOccurs="unbounded" />
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="entity">
|
||||
<xs:sequence>
|
||||
<xs:element name="indexes" type="orm:indexes" minOccurs="0"/>
|
||||
@ -63,6 +74,7 @@
|
||||
<xs:element name="discriminator-column" type="orm:discriminator-column" minOccurs="0"/>
|
||||
<xs:element name="discriminator-map" type="orm:discriminator-map" minOccurs="0"/>
|
||||
<xs:element name="lifecycle-callbacks" type="orm:lifecycle-callbacks" minOccurs="0" maxOccurs="1" />
|
||||
<xs:element name="named-queries" type="orm:named-queries" minOccurs="0" maxOccurs="1" />
|
||||
<xs:element name="id" type="orm:id" minOccurs="0" maxOccurs="1" />
|
||||
<xs:element name="field" type="orm:field" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xs:element name="one-to-one" type="orm:one-to-one" minOccurs="0" maxOccurs="unbounded"/>
|
||||
|
@ -78,6 +78,17 @@ class EntityRepository implements ObjectRepository
|
||||
->from($this->_entityName, $alias);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Query instance based on a predefined metadata named query.
|
||||
*
|
||||
* @param string $queryName
|
||||
* @return Query
|
||||
*/
|
||||
public function createNamedQuery($queryName)
|
||||
{
|
||||
return $this->_em->createQuery($this->_class->getNamedQuery($queryName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the repository, causing all managed entities to become detached.
|
||||
*/
|
||||
|
@ -275,6 +275,7 @@ class ClassMetadata extends ClassMetadataInfo
|
||||
{
|
||||
// This metadata is always serialized/cached.
|
||||
$serialized = array(
|
||||
'namedQueries',
|
||||
'associationMappings',
|
||||
'columnNames', //TODO: Not really needed. Can use fieldMappings[$fieldName]['columnName']
|
||||
'fieldMappings',
|
||||
|
@ -204,6 +204,13 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
*/
|
||||
public $subClasses = array();
|
||||
|
||||
/**
|
||||
* READ-ONLY: The named queries allowed to be called directly from Repository.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $namedQueries = array();
|
||||
|
||||
/**
|
||||
* READ-ONLY: The field names of all fields that are part of the identifier/primary key
|
||||
* of the mapped entity class.
|
||||
@ -655,6 +662,32 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
$this->fieldNames[$columnName] : $columnName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the named query.
|
||||
*
|
||||
* @see ClassMetadataInfo::$namedQueries
|
||||
* @throws MappingException
|
||||
* @param string $queryName The query name
|
||||
* @return string
|
||||
*/
|
||||
public function getNamedQuery($queryName)
|
||||
{
|
||||
if ( ! isset($this->namedQueries[$queryName])) {
|
||||
throw MappingException::queryNotFound($this->name, $queryName);
|
||||
}
|
||||
return $this->namedQueries[$queryName];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all named queries of the class.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getNamedQueries()
|
||||
{
|
||||
return $this->namedQueries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates & completes the given field mapping.
|
||||
*
|
||||
@ -1368,8 +1401,7 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
* Adds an association mapping without completing/validating it.
|
||||
* This is mainly used to add inherited association mappings to derived classes.
|
||||
*
|
||||
* @param AssociationMapping $mapping
|
||||
* @param string $owningClassName The name of the class that defined this mapping.
|
||||
* @param array $mapping
|
||||
*/
|
||||
public function addInheritedAssociationMapping(array $mapping/*, $owningClassName = null*/)
|
||||
{
|
||||
@ -1385,7 +1417,6 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
* This is mainly used to add inherited field mappings to derived classes.
|
||||
*
|
||||
* @param array $mapping
|
||||
* @todo Rename: addInheritedFieldMapping
|
||||
*/
|
||||
public function addInheritedFieldMapping(array $fieldMapping)
|
||||
{
|
||||
@ -1394,6 +1425,22 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
$this->fieldNames[$fieldMapping['columnName']] = $fieldMapping['fieldName'];
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL:
|
||||
* Adds a named query to this class.
|
||||
*
|
||||
* @throws MappingException
|
||||
* @param array $queryMapping
|
||||
*/
|
||||
public function addNamedQuery(array $queryMapping)
|
||||
{
|
||||
if (isset($this->namedQueries[$queryMapping['name']])) {
|
||||
throw MappingException::duplicateQueryMapping($this->name, $queryMapping['name']);
|
||||
}
|
||||
$query = str_replace('__CLASS__', $this->name, $queryMapping['query']);
|
||||
$this->namedQueries[$queryMapping['name']] = $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a one-to-one mapping.
|
||||
*
|
||||
@ -1584,6 +1631,17 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the class has a named query with the given query name.
|
||||
*
|
||||
* @param string $fieldName
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasNamedQuery($queryName)
|
||||
{
|
||||
return isset($this->namedQueries[$queryName]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the class has a mapped association with the given field name.
|
||||
*
|
||||
|
@ -165,6 +165,18 @@ class AnnotationDriver implements Driver
|
||||
$metadata->setPrimaryTable($primaryTable);
|
||||
}
|
||||
|
||||
// Evaluate NamedQueries annotation
|
||||
if (isset($classAnnotations['Doctrine\ORM\Mapping\NamedQueries'])) {
|
||||
$namedQueriesAnnot = $classAnnotations['Doctrine\ORM\Mapping\NamedQueries'];
|
||||
|
||||
foreach ($namedQueriesAnnot->value as $namedQuery) {
|
||||
$metadata->addNamedQuery(array(
|
||||
'name' => $namedQuery->name,
|
||||
'query' => $namedQuery->query
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluate InheritanceType annotation
|
||||
if (isset($classAnnotations['Doctrine\ORM\Mapping\InheritanceType'])) {
|
||||
$inheritanceTypeAnnot = $classAnnotations['Doctrine\ORM\Mapping\InheritanceType'];
|
||||
|
@ -127,6 +127,12 @@ final class SequenceGenerator extends Annotation {
|
||||
final class ChangeTrackingPolicy extends Annotation {}
|
||||
final class OrderBy extends Annotation {}
|
||||
|
||||
final class NamedQueries extends Annotation {}
|
||||
final class NamedQuery extends Annotation {
|
||||
public $name;
|
||||
public $query;
|
||||
}
|
||||
|
||||
/* Annotations for lifecycle callbacks */
|
||||
final class HasLifecycleCallbacks extends Annotation {}
|
||||
final class PrePersist extends Annotation {}
|
||||
|
@ -69,6 +69,16 @@ class XmlDriver extends AbstractFileDriver
|
||||
|
||||
$metadata->setPrimaryTable($table);
|
||||
|
||||
// Evaluate named queries
|
||||
if (isset($xmlRoot['named-queries'])) {
|
||||
foreach ($xmlRoot->{'named-queries'}->{'named-query'} as $namedQueryElement) {
|
||||
$metadata->addNamedQuery(array(
|
||||
'name' => (string)$namedQueryElement['name'],
|
||||
'query' => (string)$namedQueryElement['query']
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/* not implemented specially anyway. use table = schema.table
|
||||
if (isset($xmlRoot['schema'])) {
|
||||
$metadata->table['schema'] = (string)$xmlRoot['schema'];
|
||||
|
@ -62,6 +62,21 @@ class YamlDriver extends AbstractFileDriver
|
||||
}
|
||||
$metadata->setPrimaryTable($table);
|
||||
|
||||
// Evaluate named queries
|
||||
if (isset($element['namedQueries'])) {
|
||||
foreach ($element['namedQueries'] as $name => $queryMapping) {
|
||||
if (is_string($queryMapping)) {
|
||||
$queryMapping = array('query' => $queryMapping);
|
||||
}
|
||||
|
||||
if ( ! isset($queryMapping['name'])) {
|
||||
$queryMapping['name'] = $name;
|
||||
}
|
||||
|
||||
$metadata->addNamedQuery($queryMapping);
|
||||
}
|
||||
}
|
||||
|
||||
/* not implemented specially anyway. use table = schema.table
|
||||
if (isset($element['schema'])) {
|
||||
$metadata->table['schema'] = $element['schema'];
|
||||
|
@ -73,6 +73,11 @@ class MappingException extends \Doctrine\ORM\ORMException
|
||||
return new self("No mapping found for field '$fieldName' on class '$className'.");
|
||||
}
|
||||
|
||||
public static function queryNotFound($className, $queryName)
|
||||
{
|
||||
return new self("No query found named '$queryName' on class '$className'.");
|
||||
}
|
||||
|
||||
public static function oneToManyRequiresMappedBy($fieldName)
|
||||
{
|
||||
return new self("OneToMany mapping on field '$fieldName' requires the 'mappedBy' attribute.");
|
||||
@ -160,6 +165,10 @@ class MappingException extends \Doctrine\ORM\ORMException
|
||||
return new self('Property "'.$fieldName.'" in "'.$entity.'" was already declared, but it must be declared only once');
|
||||
}
|
||||
|
||||
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) {
|
||||
return new self('Single id is not allowed on composite primary key in entity '.$entity);
|
||||
}
|
||||
|
@ -7,6 +7,9 @@ use Doctrine\Common\Collections\ArrayCollection;
|
||||
/**
|
||||
* @Entity
|
||||
* @Table(name="cms_users")
|
||||
* @NamedQueries({
|
||||
* @NamedQuery(name="all", query="SELECT u FROM __CLASS__ u")
|
||||
* })
|
||||
*/
|
||||
class CmsUser
|
||||
{
|
||||
|
@ -288,5 +288,24 @@ class EntityRepositoryTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$this->assertType('Doctrine\Tests\Models\CMS\CmsAddress', $address);
|
||||
$this->assertEquals($addressId, $address->id);
|
||||
}
|
||||
|
||||
public function testValidNamedQueryRetrieval()
|
||||
{
|
||||
$repos = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsUser');
|
||||
|
||||
$query = $repos->createNamedQuery('all');
|
||||
|
||||
$this->assertType('Doctrine\ORM\Query', $query);
|
||||
$this->assertEquals('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u', $query->getDQL());
|
||||
}
|
||||
|
||||
public function testInvalidNamedQueryRetrieval()
|
||||
{
|
||||
$repos = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsUser');
|
||||
|
||||
$this->setExpectedException('Doctrine\ORM\Mapping\MappingException');
|
||||
|
||||
$repos->createNamedQuery('invalidNamedQuery');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -390,4 +390,60 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
|
||||
$cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
|
||||
$cm->mapField(array('fieldName' => ''));
|
||||
}
|
||||
|
||||
public function testRetrievalOfNamedQueries()
|
||||
{
|
||||
$cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
|
||||
|
||||
$this->assertEquals(0, count($cm->getNamedQueries()));
|
||||
|
||||
$cm->addNamedQuery(array(
|
||||
'name' => 'userById',
|
||||
'query' => 'SELECT u FROM __CLASS__ u WHERE u.id = ?1'
|
||||
));
|
||||
|
||||
$this->assertEquals(1, count($cm->getNamedQueries()));
|
||||
}
|
||||
|
||||
public function testExistanceOfNamedQuery()
|
||||
{
|
||||
$cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
|
||||
|
||||
$cm->addNamedQuery(array(
|
||||
'name' => 'all',
|
||||
'query' => 'SELECT u FROM __CLASS__ u'
|
||||
));
|
||||
|
||||
$this->assertTrue($cm->hasNamedQuery('all'));
|
||||
$this->assertFalse($cm->hasNamedQuery('userById'));
|
||||
}
|
||||
|
||||
public function testRetrieveOfNamedQuery()
|
||||
{
|
||||
$cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
|
||||
|
||||
$cm->addNamedQuery(array(
|
||||
'name' => 'userById',
|
||||
'query' => 'SELECT u FROM __CLASS__ u WHERE u.id = ?1'
|
||||
));
|
||||
|
||||
$this->assertEquals('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = ?1', $cm->getNamedQuery('userById'));
|
||||
}
|
||||
|
||||
public function testNamingCollisionNamedQueryShouldThrowException()
|
||||
{
|
||||
$cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
|
||||
|
||||
$this->setExpectedException('Doctrine\ORM\Mapping\MappingException');
|
||||
|
||||
$cm->addNamedQuery(array(
|
||||
'name' => 'userById',
|
||||
'query' => 'SELECT u FROM __CLASS__ u WHERE u.id = ?1'
|
||||
));
|
||||
|
||||
$cm->addNamedQuery(array(
|
||||
'name' => 'userById',
|
||||
'query' => 'SELECT u FROM __CLASS__ u WHERE u.id = ?1'
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,10 @@ $metadata->setChangeTrackingPolicy(ClassMetadataInfo::CHANGETRACKING_DEFERRED_IM
|
||||
$metadata->addLifecycleCallback('doStuffOnPrePersist', 'prePersist');
|
||||
$metadata->addLifecycleCallback('doOtherStuffOnPrePersistToo', 'prePersist');
|
||||
$metadata->addLifecycleCallback('doStuffOnPostPersist', 'postPersist');
|
||||
$metadata->addNamedQuery(array(
|
||||
'name' => 'all',
|
||||
'query' => 'SELECT u FROM __CLASS__ u'
|
||||
));
|
||||
$metadata->mapField(array(
|
||||
'id' => true,
|
||||
'fieldName' => 'id',
|
||||
|
@ -22,6 +22,10 @@
|
||||
<lifecycle-callback type="postPersist" method="doStuffOnPostPersist"/>
|
||||
</lifecycle-callbacks>
|
||||
|
||||
<named-queries>
|
||||
<named-query name="all" query="SELECT u FROM __CLASS__ u"/>
|
||||
</named-queries>
|
||||
|
||||
<id name="id" type="integer" column="id">
|
||||
<generator strategy="AUTO"/>
|
||||
<sequence-generator sequence-name="tablename_seq" allocation-size="100" initial-value="1" />
|
||||
|
@ -1,6 +1,8 @@
|
||||
Doctrine\Tests\ORM\Mapping\User:
|
||||
type: entity
|
||||
table: cms_users
|
||||
namedQueries:
|
||||
all: SELECT u FROM __CLASS__ u
|
||||
id:
|
||||
id:
|
||||
type: integer
|
||||
|
Loading…
Reference in New Issue
Block a user