diff --git a/doctrine-mapping.xsd b/doctrine-mapping.xsd
index c212c8671..95fd62425 100644
--- a/doctrine-mapping.xsd
+++ b/doctrine-mapping.xsd
@@ -219,6 +219,7 @@
+
@@ -230,6 +231,7 @@
+
diff --git a/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php b/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php
index b10be89f4..de2375ce6 100644
--- a/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php
+++ b/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php
@@ -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);
}
}
diff --git a/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php b/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php
index 152a1719f..5ad0eb3f2 100644
--- a/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php
+++ b/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php
@@ -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 {}
diff --git a/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php b/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php
index 9e625a29c..f32ada9a0 100644
--- a/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php
+++ b/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php
@@ -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);
}
diff --git a/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php b/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php
index 36a21f87a..4e4196130 100644
--- a/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php
+++ b/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php
@@ -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);
}
}
diff --git a/lib/Doctrine/ORM/Mapping/ManyToManyMapping.php b/lib/Doctrine/ORM/Mapping/ManyToManyMapping.php
index 3a576c1de..0fbb4df8a 100644
--- a/lib/Doctrine/ORM/Mapping/ManyToManyMapping.php
+++ b/lib/Doctrine/ORM/Mapping/ManyToManyMapping.php
@@ -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()
diff --git a/lib/Doctrine/ORM/Mapping/OneToManyMapping.php b/lib/Doctrine/ORM/Mapping/OneToManyMapping.php
index e6bf21339..45a3cf75d 100644
--- a/lib/Doctrine/ORM/Mapping/OneToManyMapping.php
+++ b/lib/Doctrine/ORM/Mapping/OneToManyMapping.php
@@ -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'];
+ }
}
/**
diff --git a/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php b/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php
index 3cc253d9f..b7c9b108f 100644
--- a/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php
+++ b/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php
@@ -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()
{
diff --git a/tests/Doctrine/Tests/ORM/Mapping/AnnotationDriverTest.php b/tests/Doctrine/Tests/ORM/Mapping/AnnotationDriverTest.php
index b99c72986..5c3341619 100644
--- a/tests/Doctrine/Tests/ORM/Mapping/AnnotationDriverTest.php
+++ b/tests/Doctrine/Tests/ORM/Mapping/AnnotationDriverTest.php
@@ -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;
}
-
diff --git a/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.User.dcm.xml b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.User.dcm.xml
index 193227faf..1a344790e 100644
--- a/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.User.dcm.xml
+++ b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.User.dcm.xml
@@ -24,7 +24,7 @@
-
+
diff --git a/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.User.dcm.yml b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.User.dcm.yml
index 846e071cf..450dfd7e5 100644
--- a/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.User.dcm.yml
+++ b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.User.dcm.yml
@@ -27,6 +27,7 @@ Doctrine\Tests\ORM\Mapping\User:
phonenumbers:
targetEntity: Phonenumber
mappedBy: user
+ orderBy: %alias%.number ASC
cascade: [ persist ]
manyToMany:
groups: