diff --git a/doctrine-mapping.xsd b/doctrine-mapping.xsd
index cd3c8e751..a25dd56f6 100644
--- a/doctrine-mapping.xsd
+++ b/doctrine-mapping.xsd
@@ -162,6 +162,7 @@
+
@@ -274,6 +275,7 @@
+
@@ -294,6 +296,13 @@
+
+
+
+
+
+
+
diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php
index 54ce6ff34..a74ddac8b 100644
--- a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php
+++ b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php
@@ -521,6 +521,14 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
case ClassMetadata::GENERATOR_TYPE_TABLE:
throw new ORMException("TableGenerator not yet implemented.");
break;
+ case ClassMetadata::GENERATOR_TYPE_CUSTOM:
+ $definition = $class->customGeneratorDefinition;
+ if (!class_exists($definition['class'])) {
+ throw new ORMException("Can't instantiate custom generator : " .
+ $definition['class']);
+ }
+ $class->setIdGenerator(new $definition['class']);
+ break;
default:
throw new ORMException("Unknown generator type: " . $class->generatorType);
}
diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php
index 826f986c3..c942621f6 100644
--- a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php
+++ b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php
@@ -99,6 +99,10 @@ class ClassMetadataInfo implements ClassMetadata
* portability is currently not guaranteed.
*/
const GENERATOR_TYPE_UUID = 6;
+ /**
+ * CUSTOM means that customer will use own ID generator that supposedly work
+ */
+ const GENERATOR_TYPE_CUSTOM = 7;
/**
* DEFERRED_IMPLICIT means that changes of entities are calculated at commit-time
* by doing a property-by-property comparison with the original data. This will
@@ -181,6 +185,22 @@ class ClassMetadataInfo implements ClassMetadata
*/
public $rootEntityName;
+ /**
+ * READ-ONLY: The definition of custom generator. Only used for CUSTOM
+ * generator type
+ *
+ * The definition has the following structure:
+ *
+ * array(
+ * 'class' => 'ClassName',
+ * )
+ *
+ *
+ * @var array
+ * @todo Merge with tableGeneratorDefinition into generic generatorDefinition
+ */
+ public $customGeneratorDefinition;
+
/**
* The name of the custom repository class used for the entity class.
* (Optional).
@@ -2146,6 +2166,15 @@ class ClassMetadataInfo implements ClassMetadata
$this->idGenerator = $generator;
}
+ /**
+ * Sets definition
+ * @param array $definition
+ */
+ public function setCustomGeneratorDefinition(array $definition)
+ {
+ $this->customGeneratorDefinition = $definition;
+ }
+
/**
* Sets the definition of the sequence ID generator for this class.
*
diff --git a/lib/Doctrine/ORM/Mapping/CustomIdGenerator.php b/lib/Doctrine/ORM/Mapping/CustomIdGenerator.php
new file mode 100644
index 000000000..4739c3c81
--- /dev/null
+++ b/lib/Doctrine/ORM/Mapping/CustomIdGenerator.php
@@ -0,0 +1,30 @@
+.
+ */
+
+namespace Doctrine\ORM\Mapping;
+
+/**
+ * @Annotation
+ * @Target("PROPERTY")
+ */
+final class CustomIdGenerator implements Annotation
+{
+ /** @var string */
+ public $class;
+}
\ No newline at end of file
diff --git a/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php b/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php
index de6a4939f..94a624c0a 100644
--- a/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php
+++ b/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php
@@ -343,6 +343,10 @@ class AnnotationDriver implements Driver
));
} else if ($tblGeneratorAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\TableGenerator')) {
throw MappingException::tableIdGeneratorNotImplemented($className);
+ } else if ($customGeneratorAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\CustomIdGenerator')) {
+ $metadata->setCustomGeneratorDefinition(array(
+ 'class' => $customGeneratorAnnot->class
+ ));
}
} else if ($oneToOneAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\OneToOne')) {
if ($idAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\Id')) {
diff --git a/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php b/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php
index 290fc6529..bd1632b2c 100644
--- a/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php
+++ b/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php
@@ -39,6 +39,7 @@ require_once __DIR__.'/../UniqueConstraint.php';
require_once __DIR__.'/../Index.php';
require_once __DIR__.'/../JoinTable.php';
require_once __DIR__.'/../SequenceGenerator.php';
+require_once __DIR__.'/../CustomIdGenerator.php';
require_once __DIR__.'/../ChangeTrackingPolicy.php';
require_once __DIR__.'/../OrderBy.php';
require_once __DIR__.'/../NamedQueries.php';
diff --git a/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php b/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php
index 4a07974d9..c60a8c5ae 100644
--- a/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php
+++ b/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php
@@ -259,6 +259,11 @@ class XmlDriver extends AbstractFileDriver
'allocationSize' => (string)$seqGenerator['allocation-size'],
'initialValue' => (string)$seqGenerator['initial-value']
));
+ } else if (isset($idElement->{'custom-id-generator'})) {
+ $customGenerator = $idElement->{'custom-id-generator'};
+ $metadata->setCustomGeneratorDefinition(array(
+ 'class' => (string) $customGenerator['class']
+ ));
} else if (isset($idElement->{'table-generator'})) {
throw MappingException::tableIdGeneratorNotImplemented($className);
}
diff --git a/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php b/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php
index b66f29407..7db57dbad 100644
--- a/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php
+++ b/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php
@@ -200,6 +200,11 @@ class YamlDriver extends AbstractFileDriver
// Check for SequenceGenerator/TableGenerator definition
if (isset($idElement['sequenceGenerator'])) {
$metadata->setSequenceGeneratorDefinition($idElement['sequenceGenerator']);
+ } else if (isset($idElement['customIdGenerator'])) {
+ $customGenerator = $idElement['customIdGenerator'];
+ $metadata->setCustomGeneratorDefinition(array(
+ 'class' => (string) $customGenerator['class']
+ ));
} else if (isset($idElement['tableGenerator'])) {
throw MappingException::tableIdGeneratorNotImplemented($className);
}
diff --git a/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php b/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php
index 4b2da0a70..b15481bdb 100644
--- a/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php
+++ b/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php
@@ -105,6 +105,18 @@ abstract class AbstractMappingDriverTest extends \Doctrine\Tests\OrmTestCase
);
}
+ public function testEntityCustomGenerator()
+ {
+ $class = $this->createClassMetadata('Doctrine\Tests\ORM\Mapping\Animal');
+
+ $this->assertEquals(ClassMetadata::GENERATOR_TYPE_CUSTOM,
+ $class->generatorType, "Generator Type");
+ $this->assertEquals(
+ array("class" => "stdClass"),
+ $class->customGeneratorDefinition,
+ "Custom Generator Definition");
+ }
+
/**
* @depends testEntityTableNameAndInheritance
@@ -650,13 +662,15 @@ class User
abstract class Animal
{
/**
- * @Id @Column(type="string") @GeneratedValue
+ * @Id @Column(type="string") @GeneratedValue(strategy="CUSTOM")
+ * @CustomIdGenerator(class="stdClass")
*/
public $id;
public static function loadMetadata(ClassMetadataInfo $metadata)
{
-
+ $metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_CUSTOM);
+ $metadata->setCustomGeneratorDefinition(array("class" => "stdClass"));
}
}
diff --git a/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataFactoryTest.php b/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataFactoryTest.php
index cd1e4f3ff..04f413c95 100644
--- a/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataFactoryTest.php
+++ b/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataFactoryTest.php
@@ -3,7 +3,6 @@
namespace Doctrine\Tests\ORM\Mapping;
use Doctrine\Tests\Mocks\MetadataDriverMock;
-use Doctrine\Tests\Mocks\DatabasePlatformMock;
use Doctrine\Tests\Mocks\EntityManagerMock;
use Doctrine\Tests\Mocks\ConnectionMock;
use Doctrine\Tests\Mocks\DriverMock;
@@ -25,28 +24,12 @@ class ClassMetadataFactoryTest extends \Doctrine\Tests\OrmTestCase
$mockPlatform->setPrefersSequences(true);
$mockPlatform->setPrefersIdentityColumns(false);
- // Self-made metadata
- $cm1 = new ClassMetadata('Doctrine\Tests\ORM\Mapping\TestEntity1');
- $cm1->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
- $cm1->setPrimaryTable(array('name' => '`group`'));
- // Add a mapped field
- $cm1->mapField(array('fieldName' => 'name', 'type' => 'varchar'));
- // Add a mapped field
- $cm1->mapField(array('fieldName' => 'id', 'type' => 'integer', 'id' => true));
- // and a mapped association
- $cm1->mapOneToOne(array('fieldName' => 'other', 'targetEntity' => 'TestEntity1', 'mappedBy' => 'this'));
- // and an association on the owning side
- $joinColumns = array(
- array('name' => 'other_id', 'referencedColumnName' => 'id')
- );
- $cm1->mapOneToOne(array('fieldName' => 'association', 'targetEntity' => 'TestEntity1', 'joinColumns' => $joinColumns));
- // and an id generator type
- $cm1->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO);
+ $cm1 = $this->_createValidClassMetadata();
// SUT
$cmf = new \Doctrine\ORM\Mapping\ClassMetadataFactory();
$cmf->setEntityManager($entityManager);
- $cmf->setMetadataFor('Doctrine\Tests\ORM\Mapping\TestEntity1', $cm1);
+ $cmf->setMetadataFor($cm1->name, $cm1);
// Prechecks
$this->assertEquals(array(), $cm1->parentClasses);
@@ -57,7 +40,7 @@ class ClassMetadataFactoryTest extends \Doctrine\Tests\OrmTestCase
$this->assertEquals('group', $cm1->table['name']);
// Go
- $cmMap1 = $cmf->getMetadataFor('Doctrine\Tests\ORM\Mapping\TestEntity1');
+ $cmMap1 = $cmf->getMetadataFor($cm1->name);
$this->assertSame($cm1, $cmMap1);
$this->assertEquals('group', $cmMap1->table['name']);
@@ -66,6 +49,46 @@ class ClassMetadataFactoryTest extends \Doctrine\Tests\OrmTestCase
$this->assertTrue($cmMap1->hasField('name'));
}
+ public function testGetMetadataFor_ReturnsLoadedCustomIdGenerator()
+ {
+ $cm1 = $this->_createValidClassMetadata();
+ $cm1->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_CUSTOM);
+ $cm1->customGeneratorDefinition = array(
+ "class" => "Doctrine\Tests\ORM\Mapping\CustomIdGenerator");
+ $cmf = $this->_createTestFactory();
+ $cmf->setMetadataForClass($cm1->name, $cm1);
+
+ $actual = $cmf->getMetadataFor($cm1->name);
+
+ $this->assertEquals(ClassMetadata::GENERATOR_TYPE_CUSTOM,
+ $actual->generatorType);
+ $this->assertInstanceOf("Doctrine\Tests\ORM\Mapping\CustomIdGenerator",
+ $actual->idGenerator);
+ }
+
+ public function testGetMetadataFor_ThrowsExceptionOnUnknownCustomGeneratorClass()
+ {
+ $cm1 = $this->_createValidClassMetadata();
+ $cm1->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_CUSTOM);
+ $cm1->customGeneratorDefinition = array("class" => "NotExistingGenerator");
+ $cmf = $this->_createTestFactory();
+ $cmf->setMetadataForClass($cm1->name, $cm1);
+ $this->setExpectedException("Doctrine\ORM\ORMException");
+
+ $actual = $cmf->getMetadataFor($cm1->name);
+ }
+
+ public function testGetMetadataFor_ThrowsExceptionOnMissingCustomGeneratorDefinition()
+ {
+ $cm1 = $this->_createValidClassMetadata();
+ $cm1->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_CUSTOM);
+ $cmf = $this->_createTestFactory();
+ $cmf->setMetadataForClass($cm1->name, $cm1);
+ $this->setExpectedException("Doctrine\ORM\ORMException");
+
+ $actual = $cmf->getMetadataFor($cm1->name);
+ }
+
public function testHasGetMetadata_NamespaceSeperatorIsNotNormalized()
{
require_once __DIR__."/../../Models/Global/GlobalNamespaceModel.php";
@@ -143,6 +166,44 @@ class ClassMetadataFactoryTest extends \Doctrine\Tests\OrmTestCase
return EntityManagerMock::create($conn, $config, $eventManager);
}
+
+ /**
+ * @return ClassMetadataFactoryTestSubject
+ */
+ protected function _createTestFactory()
+ {
+ $mockDriver = new MetadataDriverMock();
+ $entityManager = $this->_createEntityManager($mockDriver);
+ $cmf = new ClassMetadataFactoryTestSubject();
+ $cmf->setEntityManager($entityManager);
+ return $cmf;
+ }
+
+ /**
+ * @param string $class
+ * @return ClassMetadata
+ */
+ protected function _createValidClassMetadata()
+ {
+ // Self-made metadata
+ $cm1 = new ClassMetadata('Doctrine\Tests\ORM\Mapping\TestEntity1');
+ $cm1->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
+ $cm1->setPrimaryTable(array('name' => '`group`'));
+ // Add a mapped field
+ $cm1->mapField(array('fieldName' => 'name', 'type' => 'varchar'));
+ // Add a mapped field
+ $cm1->mapField(array('fieldName' => 'id', 'type' => 'integer', 'id' => true));
+ // and a mapped association
+ $cm1->mapOneToOne(array('fieldName' => 'other', 'targetEntity' => 'TestEntity1', 'mappedBy' => 'this'));
+ // and an association on the owning side
+ $joinColumns = array(
+ array('name' => 'other_id', 'referencedColumnName' => 'id')
+ );
+ $cm1->mapOneToOne(array('fieldName' => 'association', 'targetEntity' => 'TestEntity1', 'joinColumns' => $joinColumns));
+ // and an id generator type
+ $cm1->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO);
+ return $cm1;
+ }
}
/* Test subject class with overriden factory method for mocking purposes */
@@ -179,3 +240,10 @@ class TestEntity1
private $other;
private $association;
}
+
+class CustomIdGenerator extends \Doctrine\ORM\Id\AbstractIdGenerator
+{
+ public function generate(\Doctrine\ORM\EntityManager $em, $entity)
+ {
+ }
+}
diff --git a/tests/Doctrine/Tests/ORM/Mapping/PHPMappingDriverTest.php b/tests/Doctrine/Tests/ORM/Mapping/PHPMappingDriverTest.php
index b346973cf..e14828e00 100644
--- a/tests/Doctrine/Tests/ORM/Mapping/PHPMappingDriverTest.php
+++ b/tests/Doctrine/Tests/ORM/Mapping/PHPMappingDriverTest.php
@@ -14,17 +14,14 @@ class PHPMappingDriverTest extends AbstractMappingDriverTest
{
$path = __DIR__ . DIRECTORY_SEPARATOR . 'php';
- /*
- // Convert YAML mapping information to PHP
- // Uncomment this code if the YAML changes and you want to update the PHP code
+ // Convert Annotation mapping information to PHP
+ // Uncomment this code if annotations changed and you want to update the PHP code
// for the same mapping information
- $cme = new ClassMetadataExporter();
- $cme->addMappingSource(__DIR__ . DIRECTORY_SEPARATOR . 'yaml');
-
- $exporter = $cme->getExporter('php', $path);
- $exporter->setMetadatas($cme->getMetadatas());
- $exporter->export();
- */
+// $meta = new \Doctrine\ORM\Mapping\ClassMetadataInfo("Doctrine\Tests\ORM\Mapping\Animal");
+// $driver = $this->createAnnotationDriver();
+// $driver->loadMetadataForClass("Doctrine\Tests\ORM\Mapping\Animal", $meta);
+// $exporter = $cme->getExporter('php', $path);
+// echo $exporter->exportClassMetadata($meta);
return new PHPDriver($path);
}
diff --git a/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.ORM.Mapping.Animal.php b/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.ORM.Mapping.Animal.php
new file mode 100644
index 000000000..005178eed
--- /dev/null
+++ b/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.ORM.Mapping.Animal.php
@@ -0,0 +1,30 @@
+setInheritanceType(ClassMetadataInfo::INHERITANCE_TYPE_SINGLE_TABLE);
+$metadata->setDiscriminatorColumn(array(
+ 'name' => 'dtype',
+ 'type' => 'string',
+ 'length' => 255,
+ 'fieldName' => 'dtype',
+ ));
+$metadata->setDiscriminatorMap(array(
+ 'cat' => 'Doctrine\\Tests\\ORM\\Mapping\\Cat',
+ 'dog' => 'Doctrine\\Tests\\ORM\\Mapping\\Dog',
+ ));
+$metadata->setChangeTrackingPolicy(ClassMetadataInfo::CHANGETRACKING_DEFERRED_IMPLICIT);
+$metadata->mapField(array(
+ 'fieldName' => 'id',
+ 'type' => 'string',
+ 'length' => NULL,
+ 'precision' => 0,
+ 'scale' => 0,
+ 'nullable' => false,
+ 'unique' => false,
+ 'id' => true,
+ 'columnName' => 'id',
+ ));
+$metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_CUSTOM);
+$metadata->setCustomGeneratorDefinition(array("class" => "stdClass"));
diff --git a/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.Animal.dcm.xml b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.Animal.dcm.xml
index 6c2a2356f..6981d0ba6 100644
--- a/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.Animal.dcm.xml
+++ b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.Animal.dcm.xml
@@ -8,5 +8,9 @@
+
+
+
+
\ No newline at end of file
diff --git a/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.Animal.dcm.yml b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.Animal.dcm.yml
index cd6ec292c..8fdfe3076 100644
--- a/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.Animal.dcm.yml
+++ b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.Animal.dcm.yml
@@ -3,4 +3,11 @@ Doctrine\Tests\ORM\Mapping\Animal:
inheritanceType: SINGLE_TABLE
discriminatorMap:
cat: Cat
- dog: Dog
\ No newline at end of file
+ dog: Dog
+ id:
+ id:
+ type: integer
+ generator:
+ strategy: CUSTOM
+ customIdGenerator:
+ class: stdClass
\ No newline at end of file
diff --git a/tests/Doctrine/Tests/OrmTestCase.php b/tests/Doctrine/Tests/OrmTestCase.php
index 23099ac77..b9f7b6be5 100644
--- a/tests/Doctrine/Tests/OrmTestCase.php
+++ b/tests/Doctrine/Tests/OrmTestCase.php
@@ -17,7 +17,7 @@ abstract class OrmTestCase extends DoctrineTestCase
/**
* @param array $paths
- * @return \Doctrine\Common\Annotations\AnnotationReader
+ * @return \Doctrine\ORM\Mapping\Driver\AnnotationDriver
*/
protected function createAnnotationDriver($paths = array(), $alias = null)
{