[2.0] DDC-336 - Support specification of an OrderBy SQL snippet in OneToMany and ManyToMany Associations in Annotation, XML and YAML Drivers
This commit is contained in:
parent
e83f1517ad
commit
ab3a6cc16e
@ -219,6 +219,7 @@
|
||||
<xs:attribute name="field" type="xs:NMTOKEN" use="required" />
|
||||
<xs:attribute name="mapped-by" type="xs:NMTOKEN" />
|
||||
<xs:attribute name="fetch" type="orm:fetch-type" default="LAZY" />
|
||||
<xs:attribute name="order-by" type="xs:string" />
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="one-to-many">
|
||||
@ -230,6 +231,7 @@
|
||||
<xs:attribute name="field" type="xs:NMTOKEN" use="required" />
|
||||
<xs:attribute name="orphan-removal" type="xs:boolean" default="false" />
|
||||
<xs:attribute name="fetch" type="orm:fetch-type" default="LAZY" />
|
||||
<xs:attribute name="order-by" type="xs:string" />
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="many-to-one">
|
||||
|
@ -302,6 +302,11 @@ class AnnotationDriver implements Driver
|
||||
$mapping['cascade'] = $oneToManyAnnot->cascade;
|
||||
$mapping['orphanRemoval'] = $oneToManyAnnot->orphanRemoval;
|
||||
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\AssociationMapping::FETCH_' . $oneToManyAnnot->fetch);
|
||||
|
||||
if ($orderByAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\OrderBy')) {
|
||||
$mapping['orderBy'] = $orderByAnnot->value;
|
||||
}
|
||||
|
||||
$metadata->mapOneToMany($mapping);
|
||||
} else if ($manyToOneAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\ManyToOne')) {
|
||||
$mapping['joinColumns'] = $joinColumns;
|
||||
@ -348,6 +353,11 @@ class AnnotationDriver implements Driver
|
||||
$mapping['mappedBy'] = $manyToManyAnnot->mappedBy;
|
||||
$mapping['cascade'] = $manyToManyAnnot->cascade;
|
||||
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\AssociationMapping::FETCH_' . $manyToManyAnnot->fetch);
|
||||
|
||||
if ($orderByAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\OrderBy')) {
|
||||
$mapping['orderBy'] = $orderByAnnot->value;
|
||||
}
|
||||
|
||||
$metadata->mapManyToMany($mapping);
|
||||
}
|
||||
}
|
||||
|
@ -124,6 +124,8 @@ final class SequenceGenerator extends Annotation {
|
||||
}
|
||||
final class ChangeTrackingPolicy extends Annotation {}
|
||||
|
||||
final class OrderBy extends Annotation {}
|
||||
|
||||
/* Annotations for lifecycle callbacks */
|
||||
final class HasLifecycleCallbacks extends Annotation {}
|
||||
final class PrePersist extends Annotation {}
|
||||
|
@ -266,6 +266,10 @@ class XmlDriver extends AbstractFileDriver
|
||||
if (isset($oneToManyElement->{'orphan-removal'})) {
|
||||
$mapping['orphanRemoval'] = (bool)$oneToManyElement->{'orphan-removal'};
|
||||
}
|
||||
|
||||
if (isset($oneToManyElement['order-by'])) {
|
||||
$mapping['orderBy'] = (string)$oneToManyElement['order-by'];
|
||||
}
|
||||
|
||||
$metadata->mapOneToMany($mapping);
|
||||
}
|
||||
@ -353,6 +357,10 @@ class XmlDriver extends AbstractFileDriver
|
||||
if (isset($manyToManyElement->{'orphan-removal'})) {
|
||||
$mapping['orphanRemoval'] = (bool)$manyToManyElement->{'orphan-removal'};
|
||||
}
|
||||
|
||||
if (isset($manyToManyElement['order-by'])) {
|
||||
$mapping['orderBy'] = (string)$manyToManyElement['order-by'];
|
||||
}
|
||||
|
||||
$metadata->mapManyToMany($mapping);
|
||||
}
|
||||
|
@ -278,6 +278,10 @@ class YamlDriver extends AbstractFileDriver
|
||||
if (isset($oneToManyElement['cascade'])) {
|
||||
$mapping['cascade'] = $oneToManyElement['cascade'];
|
||||
}
|
||||
|
||||
if (isset($oneToManyElement['orderBy'])) {
|
||||
$mapping['orderBy'] = $oneToManyElement['orderBy'];
|
||||
}
|
||||
|
||||
$metadata->mapOneToMany($mapping);
|
||||
}
|
||||
@ -366,6 +370,10 @@ class YamlDriver extends AbstractFileDriver
|
||||
$mapping['cascade'] = $manyToManyElement['cascade'];
|
||||
}
|
||||
|
||||
if (isset($manyToManyElement['orderBy'])) {
|
||||
$mapping['orderBy'] = $manyToManyElement['orderBy'];
|
||||
}
|
||||
|
||||
$metadata->mapManyToMany($mapping);
|
||||
}
|
||||
}
|
||||
|
@ -59,6 +59,11 @@ class ManyToManyMapping extends AssociationMapping
|
||||
|
||||
/** FUTURE: The key column mapping, if any. The key column holds the keys of the Collection. */
|
||||
//public $keyColumn;
|
||||
|
||||
/**
|
||||
* Order this collection by the given SQL snippet.
|
||||
*/
|
||||
public $orderBy = null;
|
||||
|
||||
/**
|
||||
* Initializes a new ManyToManyMapping.
|
||||
@ -135,6 +140,10 @@ class ManyToManyMapping extends AssociationMapping
|
||||
$this->joinTableColumns[] = $inverseJoinColumn['name'];
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($mapping['orderBy'])) {
|
||||
$this->orderBy = $mapping['orderBy'];
|
||||
}
|
||||
}
|
||||
|
||||
public function getJoinTableColumnNames()
|
||||
|
@ -51,6 +51,11 @@ class OneToManyMapping extends AssociationMapping
|
||||
/** FUTURE: The key column mapping, if any. The key column holds the keys of the Collection. */
|
||||
//public $keyColumn;
|
||||
|
||||
/**
|
||||
* Order this collection by the given SQL snippet.
|
||||
*/
|
||||
public $orderBy = null;
|
||||
|
||||
/**
|
||||
* Initializes a new OneToManyMapping.
|
||||
*
|
||||
@ -79,6 +84,10 @@ class OneToManyMapping extends AssociationMapping
|
||||
|
||||
$this->orphanRemoval = isset($mapping['orphanRemoval']) ?
|
||||
(bool) $mapping['orphanRemoval'] : false;
|
||||
|
||||
if (isset($mapping['orderBy'])) {
|
||||
$this->orderBy = $mapping['orderBy'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -18,9 +18,6 @@ abstract class AbstractMappingDriverTest extends \Doctrine\Tests\OrmTestCase
|
||||
$mappingDriver = $this->_loadDriver();
|
||||
|
||||
$class = new ClassMetadata($className);
|
||||
|
||||
$this->assertFalse($mappingDriver->isTransient($className));
|
||||
|
||||
$mappingDriver->loadMetadataForClass($className, $class);
|
||||
|
||||
return $class;
|
||||
@ -64,7 +61,7 @@ abstract class AbstractMappingDriverTest extends \Doctrine\Tests\OrmTestCase
|
||||
public function testIdentifier($class)
|
||||
{
|
||||
$this->assertEquals(array('id'), $class->identifier);
|
||||
$this->assertEquals(ClassMetadata::GENERATOR_TYPE_AUTO, $class->getIdGeneratorType());
|
||||
$this->assertEquals(ClassMetadata::GENERATOR_TYPE_AUTO, $class->getIdGeneratorType(), "ID-Generator is not ClassMetadata::GENERATOR_TYPE_AUTO");
|
||||
|
||||
return $class;
|
||||
}
|
||||
@ -116,6 +113,9 @@ abstract class AbstractMappingDriverTest extends \Doctrine\Tests\OrmTestCase
|
||||
$this->assertFalse($class->associationMappings['phonenumbers']->isCascadeDetach);
|
||||
$this->assertFalse($class->associationMappings['phonenumbers']->isCascadeMerge);
|
||||
|
||||
// Test Order By
|
||||
$this->assertEquals('%alias%.number ASC', $class->associationMappings['phonenumbers']->orderBy);
|
||||
|
||||
return $class;
|
||||
}
|
||||
|
||||
@ -135,6 +135,8 @@ abstract class AbstractMappingDriverTest extends \Doctrine\Tests\OrmTestCase
|
||||
$this->assertTrue($class->associationMappings['groups']->isCascadeDetach);
|
||||
$this->assertTrue($class->associationMappings['groups']->isCascadeMerge);
|
||||
|
||||
$this->assertNull($class->associationMappings['groups']->orderBy);
|
||||
|
||||
return $class;
|
||||
}
|
||||
|
||||
@ -177,20 +179,57 @@ abstract class AbstractMappingDriverTest extends \Doctrine\Tests\OrmTestCase
|
||||
}
|
||||
}
|
||||
|
||||
class User {
|
||||
private $id;
|
||||
private $name;
|
||||
private $email;
|
||||
private $address;
|
||||
private $phonenumbers;
|
||||
private $groups;
|
||||
/**
|
||||
* @Entity
|
||||
* @HasLifecycleCallbacks
|
||||
* @Table(name="cms_users")
|
||||
*/
|
||||
class User
|
||||
{
|
||||
/** @Id @Column(type="int") @generatedValue(strategy="AUTO") */
|
||||
public $id;
|
||||
|
||||
// ... rest of code omitted, irrelevant for the mapping tests
|
||||
/**
|
||||
* @Column(length=50, nullable=true, unique=true)
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* @Column(name="user_email", columnDefinition="CHAR(32) NOT NULL")
|
||||
*/
|
||||
public $email;
|
||||
|
||||
/**
|
||||
* @OneToOne(targetEntity="Address", cascade={"remove"})
|
||||
*/
|
||||
public $address;
|
||||
|
||||
/**
|
||||
*
|
||||
* @OneToMany(targetEntity="Phonenumber", mappedBy="user", cascade={"persist"})
|
||||
* @OrderBy("%alias%.number ASC")
|
||||
*/
|
||||
public $phonenumbers;
|
||||
|
||||
/**
|
||||
* @ManyToMany(targetEntity="Group", cascade={"all"})
|
||||
* @JoinTable(name="cms_user_groups",
|
||||
* joinColumns={@JoinColumn(name="user_id", referencedColumnName="id", nullable=false, unique=false)},
|
||||
* inverseJoinColumns={@JoinColumn(name="group_id", referencedColumnName="id", columnDefinition="INT NULL")}
|
||||
* )
|
||||
*/
|
||||
public $groups;
|
||||
|
||||
/**
|
||||
* @PrePersist
|
||||
*/
|
||||
public function doStuffOnPrePersist()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @PostPersist
|
||||
*/
|
||||
public function doStuffOnPostPersist()
|
||||
{
|
||||
|
||||
|
@ -7,7 +7,7 @@ use Doctrine\ORM\Events;
|
||||
|
||||
require_once __DIR__ . '/../../TestInit.php';
|
||||
|
||||
class AnnotationDriverTest extends \Doctrine\Tests\OrmTestCase
|
||||
class AnnotationDriverTest extends AbstractMappingDriverTest
|
||||
{
|
||||
/**
|
||||
* @group DDC-268
|
||||
@ -27,7 +27,7 @@ class AnnotationDriverTest extends \Doctrine\Tests\OrmTestCase
|
||||
*/
|
||||
public function testColumnWithMissingTypeDefaultsToString()
|
||||
{
|
||||
$cm = new ClassMetadata('Doctrine\Tests\ORM\Mapping\InvalidColumn');
|
||||
$cm = new ClassMetadata('Doctrine\Tests\ORM\Mapping\ColumnWithoutType');
|
||||
$reader = new \Doctrine\Common\Annotations\AnnotationReader(new \Doctrine\Common\Cache\ArrayCache());
|
||||
$reader->setDefaultAnnotationNamespace('Doctrine\ORM\Mapping\\');
|
||||
$annotationDriver = new \Doctrine\ORM\Mapping\Driver\AnnotationDriver($reader);
|
||||
@ -35,14 +35,22 @@ class AnnotationDriverTest extends \Doctrine\Tests\OrmTestCase
|
||||
$annotationDriver->loadMetadataForClass('Doctrine\Tests\ORM\Mapping\InvalidColumn', $cm);
|
||||
$this->assertEquals('string', $cm->fieldMappings['id']['type']);
|
||||
}
|
||||
|
||||
|
||||
protected function _loadDriver()
|
||||
{
|
||||
$cache = new \Doctrine\Common\Cache\ArrayCache();
|
||||
$reader = new \Doctrine\Common\Annotations\AnnotationReader($cache);
|
||||
$reader->setDefaultAnnotationNamespace('Doctrine\ORM\Mapping\\');
|
||||
return new \Doctrine\ORM\Mapping\Driver\AnnotationDriver($reader);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class InvalidColumn
|
||||
class ColumnWithoutType
|
||||
{
|
||||
/** @Id @Column */
|
||||
public $id;
|
||||
}
|
||||
|
||||
|
@ -24,7 +24,7 @@
|
||||
<join-column name="address_id" referenced-column-name="id"/>
|
||||
</one-to-one>
|
||||
|
||||
<one-to-many field="phonenumbers" target-entity="Phonenumber" mapped-by="user">
|
||||
<one-to-many field="phonenumbers" target-entity="Phonenumber" mapped-by="user" order-by="%alias%.number ASC">
|
||||
<cascade>
|
||||
<cascade-persist/>
|
||||
</cascade>
|
||||
|
@ -27,6 +27,7 @@ Doctrine\Tests\ORM\Mapping\User:
|
||||
phonenumbers:
|
||||
targetEntity: Phonenumber
|
||||
mappedBy: user
|
||||
orderBy: %alias%.number ASC
|
||||
cascade: [ persist ]
|
||||
manyToMany:
|
||||
groups:
|
||||
|
Loading…
x
Reference in New Issue
Block a user