1
0
mirror of synced 2025-01-27 18:41:46 +03:00

[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:
beberlei 2010-02-14 19:38:22 +00:00
parent e83f1517ad
commit ab3a6cc16e
11 changed files with 113 additions and 17 deletions

View File

@ -219,6 +219,7 @@
<xs:attribute name="field" type="xs:NMTOKEN" use="required" /> <xs:attribute name="field" type="xs:NMTOKEN" use="required" />
<xs:attribute name="mapped-by" type="xs:NMTOKEN" /> <xs:attribute name="mapped-by" type="xs:NMTOKEN" />
<xs:attribute name="fetch" type="orm:fetch-type" default="LAZY" /> <xs:attribute name="fetch" type="orm:fetch-type" default="LAZY" />
<xs:attribute name="order-by" type="xs:string" />
</xs:complexType> </xs:complexType>
<xs:complexType name="one-to-many"> <xs:complexType name="one-to-many">
@ -230,6 +231,7 @@
<xs:attribute name="field" type="xs:NMTOKEN" use="required" /> <xs:attribute name="field" type="xs:NMTOKEN" use="required" />
<xs:attribute name="orphan-removal" type="xs:boolean" default="false" /> <xs:attribute name="orphan-removal" type="xs:boolean" default="false" />
<xs:attribute name="fetch" type="orm:fetch-type" default="LAZY" /> <xs:attribute name="fetch" type="orm:fetch-type" default="LAZY" />
<xs:attribute name="order-by" type="xs:string" />
</xs:complexType> </xs:complexType>
<xs:complexType name="many-to-one"> <xs:complexType name="many-to-one">

View File

@ -302,6 +302,11 @@ class AnnotationDriver implements Driver
$mapping['cascade'] = $oneToManyAnnot->cascade; $mapping['cascade'] = $oneToManyAnnot->cascade;
$mapping['orphanRemoval'] = $oneToManyAnnot->orphanRemoval; $mapping['orphanRemoval'] = $oneToManyAnnot->orphanRemoval;
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\AssociationMapping::FETCH_' . $oneToManyAnnot->fetch); $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); $metadata->mapOneToMany($mapping);
} else if ($manyToOneAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\ManyToOne')) { } else if ($manyToOneAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\ManyToOne')) {
$mapping['joinColumns'] = $joinColumns; $mapping['joinColumns'] = $joinColumns;
@ -348,6 +353,11 @@ class AnnotationDriver implements Driver
$mapping['mappedBy'] = $manyToManyAnnot->mappedBy; $mapping['mappedBy'] = $manyToManyAnnot->mappedBy;
$mapping['cascade'] = $manyToManyAnnot->cascade; $mapping['cascade'] = $manyToManyAnnot->cascade;
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\AssociationMapping::FETCH_' . $manyToManyAnnot->fetch); $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); $metadata->mapManyToMany($mapping);
} }
} }

View File

@ -124,6 +124,8 @@ final class SequenceGenerator extends Annotation {
} }
final class ChangeTrackingPolicy extends Annotation {} final class ChangeTrackingPolicy extends Annotation {}
final class OrderBy extends Annotation {}
/* Annotations for lifecycle callbacks */ /* Annotations for lifecycle callbacks */
final class HasLifecycleCallbacks extends Annotation {} final class HasLifecycleCallbacks extends Annotation {}
final class PrePersist extends Annotation {} final class PrePersist extends Annotation {}

View File

@ -266,6 +266,10 @@ class XmlDriver extends AbstractFileDriver
if (isset($oneToManyElement->{'orphan-removal'})) { if (isset($oneToManyElement->{'orphan-removal'})) {
$mapping['orphanRemoval'] = (bool)$oneToManyElement->{'orphan-removal'}; $mapping['orphanRemoval'] = (bool)$oneToManyElement->{'orphan-removal'};
} }
if (isset($oneToManyElement['order-by'])) {
$mapping['orderBy'] = (string)$oneToManyElement['order-by'];
}
$metadata->mapOneToMany($mapping); $metadata->mapOneToMany($mapping);
} }
@ -353,6 +357,10 @@ class XmlDriver extends AbstractFileDriver
if (isset($manyToManyElement->{'orphan-removal'})) { if (isset($manyToManyElement->{'orphan-removal'})) {
$mapping['orphanRemoval'] = (bool)$manyToManyElement->{'orphan-removal'}; $mapping['orphanRemoval'] = (bool)$manyToManyElement->{'orphan-removal'};
} }
if (isset($manyToManyElement['order-by'])) {
$mapping['orderBy'] = (string)$manyToManyElement['order-by'];
}
$metadata->mapManyToMany($mapping); $metadata->mapManyToMany($mapping);
} }

View File

@ -278,6 +278,10 @@ class YamlDriver extends AbstractFileDriver
if (isset($oneToManyElement['cascade'])) { if (isset($oneToManyElement['cascade'])) {
$mapping['cascade'] = $oneToManyElement['cascade']; $mapping['cascade'] = $oneToManyElement['cascade'];
} }
if (isset($oneToManyElement['orderBy'])) {
$mapping['orderBy'] = $oneToManyElement['orderBy'];
}
$metadata->mapOneToMany($mapping); $metadata->mapOneToMany($mapping);
} }
@ -366,6 +370,10 @@ class YamlDriver extends AbstractFileDriver
$mapping['cascade'] = $manyToManyElement['cascade']; $mapping['cascade'] = $manyToManyElement['cascade'];
} }
if (isset($manyToManyElement['orderBy'])) {
$mapping['orderBy'] = $manyToManyElement['orderBy'];
}
$metadata->mapManyToMany($mapping); $metadata->mapManyToMany($mapping);
} }
} }

View File

@ -59,6 +59,11 @@ class ManyToManyMapping extends AssociationMapping
/** FUTURE: The key column mapping, if any. The key column holds the keys of the Collection. */ /** FUTURE: The key column mapping, if any. The key column holds the keys of the Collection. */
//public $keyColumn; //public $keyColumn;
/**
* Order this collection by the given SQL snippet.
*/
public $orderBy = null;
/** /**
* Initializes a new ManyToManyMapping. * Initializes a new ManyToManyMapping.
@ -135,6 +140,10 @@ class ManyToManyMapping extends AssociationMapping
$this->joinTableColumns[] = $inverseJoinColumn['name']; $this->joinTableColumns[] = $inverseJoinColumn['name'];
} }
} }
if (isset($mapping['orderBy'])) {
$this->orderBy = $mapping['orderBy'];
}
} }
public function getJoinTableColumnNames() public function getJoinTableColumnNames()

View File

@ -51,6 +51,11 @@ class OneToManyMapping extends AssociationMapping
/** FUTURE: The key column mapping, if any. The key column holds the keys of the Collection. */ /** FUTURE: The key column mapping, if any. The key column holds the keys of the Collection. */
//public $keyColumn; //public $keyColumn;
/**
* Order this collection by the given SQL snippet.
*/
public $orderBy = null;
/** /**
* Initializes a new OneToManyMapping. * Initializes a new OneToManyMapping.
* *
@ -79,6 +84,10 @@ class OneToManyMapping extends AssociationMapping
$this->orphanRemoval = isset($mapping['orphanRemoval']) ? $this->orphanRemoval = isset($mapping['orphanRemoval']) ?
(bool) $mapping['orphanRemoval'] : false; (bool) $mapping['orphanRemoval'] : false;
if (isset($mapping['orderBy'])) {
$this->orderBy = $mapping['orderBy'];
}
} }
/** /**

View File

@ -18,9 +18,6 @@ abstract class AbstractMappingDriverTest extends \Doctrine\Tests\OrmTestCase
$mappingDriver = $this->_loadDriver(); $mappingDriver = $this->_loadDriver();
$class = new ClassMetadata($className); $class = new ClassMetadata($className);
$this->assertFalse($mappingDriver->isTransient($className));
$mappingDriver->loadMetadataForClass($className, $class); $mappingDriver->loadMetadataForClass($className, $class);
return $class; return $class;
@ -64,7 +61,7 @@ abstract class AbstractMappingDriverTest extends \Doctrine\Tests\OrmTestCase
public function testIdentifier($class) public function testIdentifier($class)
{ {
$this->assertEquals(array('id'), $class->identifier); $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; return $class;
} }
@ -116,6 +113,9 @@ abstract class AbstractMappingDriverTest extends \Doctrine\Tests\OrmTestCase
$this->assertFalse($class->associationMappings['phonenumbers']->isCascadeDetach); $this->assertFalse($class->associationMappings['phonenumbers']->isCascadeDetach);
$this->assertFalse($class->associationMappings['phonenumbers']->isCascadeMerge); $this->assertFalse($class->associationMappings['phonenumbers']->isCascadeMerge);
// Test Order By
$this->assertEquals('%alias%.number ASC', $class->associationMappings['phonenumbers']->orderBy);
return $class; return $class;
} }
@ -135,6 +135,8 @@ abstract class AbstractMappingDriverTest extends \Doctrine\Tests\OrmTestCase
$this->assertTrue($class->associationMappings['groups']->isCascadeDetach); $this->assertTrue($class->associationMappings['groups']->isCascadeDetach);
$this->assertTrue($class->associationMappings['groups']->isCascadeMerge); $this->assertTrue($class->associationMappings['groups']->isCascadeMerge);
$this->assertNull($class->associationMappings['groups']->orderBy);
return $class; return $class;
} }
@ -177,20 +179,57 @@ abstract class AbstractMappingDriverTest extends \Doctrine\Tests\OrmTestCase
} }
} }
class User { /**
private $id; * @Entity
private $name; * @HasLifecycleCallbacks
private $email; * @Table(name="cms_users")
private $address; */
private $phonenumbers; class User
private $groups; {
/** @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() public function doStuffOnPrePersist()
{ {
} }
/**
* @PostPersist
*/
public function doStuffOnPostPersist() public function doStuffOnPostPersist()
{ {

View File

@ -7,7 +7,7 @@ use Doctrine\ORM\Events;
require_once __DIR__ . '/../../TestInit.php'; require_once __DIR__ . '/../../TestInit.php';
class AnnotationDriverTest extends \Doctrine\Tests\OrmTestCase class AnnotationDriverTest extends AbstractMappingDriverTest
{ {
/** /**
* @group DDC-268 * @group DDC-268
@ -27,7 +27,7 @@ class AnnotationDriverTest extends \Doctrine\Tests\OrmTestCase
*/ */
public function testColumnWithMissingTypeDefaultsToString() 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 = new \Doctrine\Common\Annotations\AnnotationReader(new \Doctrine\Common\Cache\ArrayCache());
$reader->setDefaultAnnotationNamespace('Doctrine\ORM\Mapping\\'); $reader->setDefaultAnnotationNamespace('Doctrine\ORM\Mapping\\');
$annotationDriver = new \Doctrine\ORM\Mapping\Driver\AnnotationDriver($reader); $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); $annotationDriver->loadMetadataForClass('Doctrine\Tests\ORM\Mapping\InvalidColumn', $cm);
$this->assertEquals('string', $cm->fieldMappings['id']['type']); $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 * @Entity
*/ */
class InvalidColumn class ColumnWithoutType
{ {
/** @Id @Column */ /** @Id @Column */
public $id; public $id;
} }

View File

@ -24,7 +24,7 @@
<join-column name="address_id" referenced-column-name="id"/> <join-column name="address_id" referenced-column-name="id"/>
</one-to-one> </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>
<cascade-persist/> <cascade-persist/>
</cascade> </cascade>

View File

@ -27,6 +27,7 @@ Doctrine\Tests\ORM\Mapping\User:
phonenumbers: phonenumbers:
targetEntity: Phonenumber targetEntity: Phonenumber
mappedBy: user mappedBy: user
orderBy: %alias%.number ASC
cascade: [ persist ] cascade: [ persist ]
manyToMany: manyToMany:
groups: groups: