Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revived #265: [WIP] Mapping support for Embeddables (VOs). #547

Closed
wants to merge 9 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
92 changes: 58 additions & 34 deletions doctrine-mapping.xsd
Expand Up @@ -5,9 +5,9 @@
xmlns:orm="http://doctrine-project.org/schemas/orm/doctrine-mapping" xmlns:orm="http://doctrine-project.org/schemas/orm/doctrine-mapping"
elementFormDefault="qualified"> elementFormDefault="qualified">


<xs:annotation> <xs:annotation>
<xs:documentation><![CDATA[ <xs:documentation><![CDATA[
This is the XML Schema for the object/relational This is the XML Schema for the object/relational
mapping file used by the Doctrine ORM. mapping file used by the Doctrine ORM.
]]></xs:documentation> ]]></xs:documentation>
</xs:annotation> </xs:annotation>
Expand All @@ -17,31 +17,32 @@
<xs:sequence> <xs:sequence>
<xs:element name="mapped-superclass" type="orm:mapped-superclass" minOccurs="0" maxOccurs="unbounded" /> <xs:element name="mapped-superclass" type="orm:mapped-superclass" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="entity" type="orm:entity" minOccurs="0" maxOccurs="unbounded" /> <xs:element name="entity" type="orm:entity" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="embeddable" type="orm:embeddable" minOccurs="0" maxOccurs="unbounded" />
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/> <xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:sequence> </xs:sequence>
<xs:anyAttribute namespace="##other"/> <xs:anyAttribute namespace="##other"/>
</xs:complexType> </xs:complexType>
</xs:element> </xs:element>

<xs:complexType name="emptyType"> <xs:complexType name="emptyType">
<xs:sequence> <xs:sequence>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/> <xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:sequence> </xs:sequence>
<xs:anyAttribute namespace="##other"/> <xs:anyAttribute namespace="##other"/>
</xs:complexType> </xs:complexType>

<xs:complexType name="cascade-type"> <xs:complexType name="cascade-type">
<xs:sequence> <xs:sequence>
<xs:element name="cascade-all" type="orm:emptyType" minOccurs="0"/> <xs:element name="cascade-all" type="orm:emptyType" minOccurs="0"/>
<xs:element name="cascade-persist" type="orm:emptyType" minOccurs="0"/> <xs:element name="cascade-persist" type="orm:emptyType" minOccurs="0"/>
<xs:element name="cascade-merge" type="orm:emptyType" minOccurs="0"/> <xs:element name="cascade-merge" type="orm:emptyType" minOccurs="0"/>
<xs:element name="cascade-remove" type="orm:emptyType" minOccurs="0"/> <xs:element name="cascade-remove" type="orm:emptyType" minOccurs="0"/>
<xs:element name="cascade-refresh" type="orm:emptyType" minOccurs="0"/> <xs:element name="cascade-refresh" type="orm:emptyType" minOccurs="0"/>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/> <xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:sequence> </xs:sequence>
<xs:anyAttribute namespace="##other"/> <xs:anyAttribute namespace="##other"/>
</xs:complexType> </xs:complexType>

<xs:simpleType name="lifecycle-callback-type"> <xs:simpleType name="lifecycle-callback-type">
<xs:restriction base="xs:token"> <xs:restriction base="xs:token">
<xs:enumeration value="prePersist"/> <xs:enumeration value="prePersist"/>
Expand All @@ -53,7 +54,7 @@
<xs:enumeration value="postLoad"/> <xs:enumeration value="postLoad"/>
</xs:restriction> </xs:restriction>
</xs:simpleType> </xs:simpleType>

<xs:complexType name="lifecycle-callback"> <xs:complexType name="lifecycle-callback">
<xs:sequence> <xs:sequence>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/> <xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
Expand All @@ -62,7 +63,7 @@
<xs:attribute name="method" type="xs:NMTOKEN" use="required" /> <xs:attribute name="method" type="xs:NMTOKEN" use="required" />
<xs:anyAttribute namespace="##other"/> <xs:anyAttribute namespace="##other"/>
</xs:complexType> </xs:complexType>

<xs:complexType name="lifecycle-callbacks"> <xs:complexType name="lifecycle-callbacks">
<xs:sequence> <xs:sequence>
<xs:element name="lifecycle-callback" type="orm:lifecycle-callback" minOccurs="1" maxOccurs="unbounded" /> <xs:element name="lifecycle-callback" type="orm:lifecycle-callback" minOccurs="1" maxOccurs="unbounded" />
Expand Down Expand Up @@ -141,6 +142,7 @@
<xs:element name="named-native-queries" type="orm:named-native-queries" minOccurs="0" maxOccurs="1" /> <xs:element name="named-native-queries" type="orm:named-native-queries" minOccurs="0" maxOccurs="1" />
<xs:element name="id" type="orm:id" minOccurs="0" maxOccurs="unbounded" /> <xs:element name="id" type="orm:id" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="field" type="orm:field" minOccurs="0" maxOccurs="unbounded"/> <xs:element name="field" type="orm:field" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="embedded" type="orm:embedded" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="one-to-one" type="orm:one-to-one" minOccurs="0" maxOccurs="unbounded"/> <xs:element name="one-to-one" type="orm:one-to-one" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="one-to-many" type="orm:one-to-many" minOccurs="0" maxOccurs="unbounded" /> <xs:element name="one-to-many" type="orm:one-to-many" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="many-to-one" type="orm:many-to-one" minOccurs="0" maxOccurs="unbounded" /> <xs:element name="many-to-one" type="orm:many-to-one" minOccurs="0" maxOccurs="unbounded" />
Expand All @@ -158,6 +160,7 @@
<xs:attribute name="read-only" type="xs:boolean" default="false" /> <xs:attribute name="read-only" type="xs:boolean" default="false" />
<xs:anyAttribute namespace="##other"/> <xs:anyAttribute namespace="##other"/>
</xs:complexType> </xs:complexType>



<xs:complexType name="option" mixed="true"> <xs:complexType name="option" mixed="true">
<xs:sequence minOccurs="0" maxOccurs="unbounded"> <xs:sequence minOccurs="0" maxOccurs="unbounded">
Expand Down Expand Up @@ -187,22 +190,33 @@
</xs:complexContent> </xs:complexContent>
</xs:complexType> </xs:complexType>


<xs:complexType name="embeddable" >
<xs:complexContent>
<xs:extension base="orm:entity">
<xs:sequence>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:sequence>
<xs:anyAttribute namespace="##other"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>

<xs:simpleType name="change-tracking-policy"> <xs:simpleType name="change-tracking-policy">
<xs:restriction base="xs:token"> <xs:restriction base="xs:token">
<xs:enumeration value="DEFERRED_IMPLICIT"/> <xs:enumeration value="DEFERRED_IMPLICIT"/>
<xs:enumeration value="DEFERRED_EXPLICIT"/> <xs:enumeration value="DEFERRED_EXPLICIT"/>
<xs:enumeration value="NOTIFY"/> <xs:enumeration value="NOTIFY"/>
</xs:restriction> </xs:restriction>
</xs:simpleType> </xs:simpleType>

<xs:simpleType name="inheritance-type"> <xs:simpleType name="inheritance-type">
<xs:restriction base="xs:token"> <xs:restriction base="xs:token">
<xs:enumeration value="SINGLE_TABLE"/> <xs:enumeration value="SINGLE_TABLE"/>
<xs:enumeration value="JOINED"/> <xs:enumeration value="JOINED"/>
<xs:enumeration value="TABLE_PER_CLASS"/> <xs:enumeration value="TABLE_PER_CLASS"/>
</xs:restriction> </xs:restriction>
</xs:simpleType> </xs:simpleType>

<xs:simpleType name="generator-strategy"> <xs:simpleType name="generator-strategy">
<xs:restriction base="xs:token"> <xs:restriction base="xs:token">
<xs:enumeration value="TABLE"/> <xs:enumeration value="TABLE"/>
Expand All @@ -213,18 +227,18 @@
<xs:enumeration value="CUSTOM" /> <xs:enumeration value="CUSTOM" />
</xs:restriction> </xs:restriction>
</xs:simpleType> </xs:simpleType>

<xs:simpleType name="fk-action"> <xs:simpleType name="fk-action">
<xs:restriction base="xs:token"> <xs:restriction base="xs:token">
<xs:enumeration value="CASCADE"/> <xs:enumeration value="CASCADE"/>
<xs:enumeration value="RESTRICT"/> <xs:enumeration value="RESTRICT"/>
<xs:enumeration value="SET NULL"/> <xs:enumeration value="SET NULL"/>
</xs:restriction> </xs:restriction>
</xs:simpleType> </xs:simpleType>

<xs:simpleType name="fetch-type"> <xs:simpleType name="fetch-type">
<xs:restriction base="xs:token"> <xs:restriction base="xs:token">
<xs:enumeration value="EAGER"/> <xs:enumeration value="EAGER"/>
<xs:enumeration value="LAZY"/> <xs:enumeration value="LAZY"/>
<xs:enumeration value="EXTRA_LAZY"/> <xs:enumeration value="EXTRA_LAZY"/>
</xs:restriction> </xs:restriction>
Expand All @@ -247,7 +261,17 @@
<xs:attribute name="scale" type="xs:integer" use="optional" /> <xs:attribute name="scale" type="xs:integer" use="optional" />
<xs:anyAttribute namespace="##other"/> <xs:anyAttribute namespace="##other"/>
</xs:complexType> </xs:complexType>


<xs:complexType name="embedded">
<xs:sequence>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:sequence>
<xs:attribute name="name" type="xs:NMTOKEN" use="required" />
<xs:attribute name="class" type="xs:NMTOKEN" use="required" />
<xs:attribute name="prefix" type="xs:NMTOKEN" />
<xs:anyAttribute namespace="##other"/>
</xs:complexType>

<xs:complexType name="discriminator-column"> <xs:complexType name="discriminator-column">
<xs:sequence> <xs:sequence>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/> <xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
Expand All @@ -259,7 +283,7 @@
<xs:attribute name="column-definition" type="xs:string" /> <xs:attribute name="column-definition" type="xs:string" />
<xs:anyAttribute namespace="##other"/> <xs:anyAttribute namespace="##other"/>
</xs:complexType> </xs:complexType>

<xs:complexType name="unique-constraint"> <xs:complexType name="unique-constraint">
<xs:sequence> <xs:sequence>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/> <xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
Expand All @@ -268,15 +292,15 @@
<xs:attribute name="columns" type="xs:string" use="required"/> <xs:attribute name="columns" type="xs:string" use="required"/>
<xs:anyAttribute namespace="##other"/> <xs:anyAttribute namespace="##other"/>
</xs:complexType> </xs:complexType>

<xs:complexType name="unique-constraints"> <xs:complexType name="unique-constraints">
<xs:sequence> <xs:sequence>
<xs:element name="unique-constraint" type="orm:unique-constraint" minOccurs="1" maxOccurs="unbounded"/> <xs:element name="unique-constraint" type="orm:unique-constraint" minOccurs="1" maxOccurs="unbounded"/>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/> <xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:sequence> </xs:sequence>
<xs:anyAttribute namespace="##other"/> <xs:anyAttribute namespace="##other"/>
</xs:complexType> </xs:complexType>

<xs:complexType name="index"> <xs:complexType name="index">
<xs:sequence> <xs:sequence>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/> <xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
Expand All @@ -285,15 +309,15 @@
<xs:attribute name="columns" type="xs:string" use="required"/> <xs:attribute name="columns" type="xs:string" use="required"/>
<xs:anyAttribute namespace="##other"/> <xs:anyAttribute namespace="##other"/>
</xs:complexType> </xs:complexType>

<xs:complexType name="indexes"> <xs:complexType name="indexes">
<xs:sequence> <xs:sequence>
<xs:element name="index" type="orm:index" minOccurs="1" maxOccurs="unbounded"/> <xs:element name="index" type="orm:index" minOccurs="1" maxOccurs="unbounded"/>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/> <xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:sequence> </xs:sequence>
<xs:anyAttribute namespace="##other"/> <xs:anyAttribute namespace="##other"/>
</xs:complexType> </xs:complexType>

<xs:complexType name="discriminator-mapping"> <xs:complexType name="discriminator-mapping">
<xs:sequence> <xs:sequence>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/> <xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
Expand All @@ -302,7 +326,7 @@
<xs:attribute name="class" type="xs:string" use="required"/> <xs:attribute name="class" type="xs:string" use="required"/>
<xs:anyAttribute namespace="##other"/> <xs:anyAttribute namespace="##other"/>
</xs:complexType> </xs:complexType>

<xs:complexType name="discriminator-map"> <xs:complexType name="discriminator-map">
<xs:sequence> <xs:sequence>
<xs:element name="discriminator-mapping" type="orm:discriminator-mapping" minOccurs="1" maxOccurs="unbounded"/> <xs:element name="discriminator-mapping" type="orm:discriminator-mapping" minOccurs="1" maxOccurs="unbounded"/>
Expand Down Expand Up @@ -447,7 +471,7 @@
<xs:attribute name="fetch" type="orm:fetch-type" default="LAZY" /> <xs:attribute name="fetch" type="orm:fetch-type" default="LAZY" />
<xs:anyAttribute namespace="##other"/> <xs:anyAttribute namespace="##other"/>
</xs:complexType> </xs:complexType>

<xs:complexType name="many-to-one"> <xs:complexType name="many-to-one">
<xs:sequence> <xs:sequence>
<xs:element name="cascade" type="orm:cascade-type" minOccurs="0" /> <xs:element name="cascade" type="orm:cascade-type" minOccurs="0" />
Expand All @@ -465,7 +489,7 @@
<xs:attribute name="inversed-by" type="xs:NMTOKEN" /> <xs:attribute name="inversed-by" type="xs:NMTOKEN" />
<xs:anyAttribute namespace="##other"/> <xs:anyAttribute namespace="##other"/>
</xs:complexType> </xs:complexType>

<xs:complexType name="one-to-one"> <xs:complexType name="one-to-one">
<xs:sequence> <xs:sequence>
<xs:element name="cascade" type="orm:cascade-type" minOccurs="0" /> <xs:element name="cascade" type="orm:cascade-type" minOccurs="0" />
Expand Down
34 changes: 29 additions & 5 deletions lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php
Expand Up @@ -94,6 +94,7 @@ protected function doLoadMetadata($class, $parent, $rootEntityFound, array $nonS
$class->setDiscriminatorColumn($parent->discriminatorColumn); $class->setDiscriminatorColumn($parent->discriminatorColumn);
$class->setIdGeneratorType($parent->generatorType); $class->setIdGeneratorType($parent->generatorType);
$this->addInheritedFields($class, $parent); $this->addInheritedFields($class, $parent);
$this->addInheritedEmbeddeds($class, $parent);
$this->addInheritedRelations($class, $parent); $this->addInheritedRelations($class, $parent);
$class->setIdentifier($parent->identifier); $class->setIdentifier($parent->identifier);
$class->setVersioned($parent->isVersioned); $class->setVersioned($parent->isVersioned);
Expand Down Expand Up @@ -188,11 +189,12 @@ protected function validateRuntimeMetadata($class, $parent)
} }


$class->validateIdentifier(); $class->validateIdentifier();
$class->validateEmdeddeds();
$class->validateAssocations(); $class->validateAssocations();
$class->validateLifecycleCallbacks($this->getReflectionService()); $class->validateLifecycleCallbacks($this->getReflectionService());


// verify inheritance // verify inheritance
if ( ! $class->isMappedSuperclass && !$class->isInheritanceTypeNone()) { if ( ! $class->isMappedSuperclass && ! $class->isEmbeddable && !$class->isInheritanceTypeNone()) {
if ( ! $parent) { if ( ! $parent) {
if (count($class->discriminatorMap) == 0) { if (count($class->discriminatorMap) == 0) {
throw MappingException::missingDiscriminatorMap($class->name); throw MappingException::missingDiscriminatorMap($class->name);
Expand Down Expand Up @@ -286,7 +288,7 @@ private function getShortName($className)
private function addInheritedFields(ClassMetadata $subClass, ClassMetadata $parentClass) private function addInheritedFields(ClassMetadata $subClass, ClassMetadata $parentClass)
{ {
foreach ($parentClass->fieldMappings as $mapping) { foreach ($parentClass->fieldMappings as $mapping) {
if ( ! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass) { if ( ! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass && ! $parentClass->isEmbeddable) {
$mapping['inherited'] = $parentClass->name; $mapping['inherited'] = $parentClass->name;
} }
if ( ! isset($mapping['declared'])) { if ( ! isset($mapping['declared'])) {
Expand All @@ -299,6 +301,28 @@ private function addInheritedFields(ClassMetadata $subClass, ClassMetadata $pare
} }
} }



/**
* Adds inherited fields to the subclass mapping.
*
* @param \Doctrine\ORM\Mapping\ClassMetadata $subClass
* @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass
*/
private function addInheritedEmbeddeds(ClassMetadata $subClass, ClassMetadata $parentClass)
{
foreach ($parentClass->embeddedMappings as $fieldName => $mapping) {
if ( ! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass && ! $parentClass->isEmbeddable) {
$mapping['inherited'] = $parentClass->name;
}

if ( ! isset($mapping['declared'])) {
$mapping['declared'] = $parentClass->name;
}

$subClass->addInheritedEmbeddedMapping($mapping);
}
}

/** /**
* Adds inherited association mappings to the subclass mapping. * Adds inherited association mappings to the subclass mapping.
* *
Expand All @@ -312,15 +336,15 @@ private function addInheritedFields(ClassMetadata $subClass, ClassMetadata $pare
private function addInheritedRelations(ClassMetadata $subClass, ClassMetadata $parentClass) private function addInheritedRelations(ClassMetadata $subClass, ClassMetadata $parentClass)
{ {
foreach ($parentClass->associationMappings as $field => $mapping) { foreach ($parentClass->associationMappings as $field => $mapping) {
if ($parentClass->isMappedSuperclass) { if ($parentClass->isMappedSuperclass || $parentClass->isEmbeddable) {
if ($mapping['type'] & ClassMetadata::TO_MANY && !$mapping['isOwningSide']) { if ($mapping['type'] & ClassMetadata::TO_MANY && !$mapping['isOwningSide']) {
throw MappingException::illegalToManyAssocationOnMappedSuperclass($parentClass->name, $field); throw MappingException::illegalToManyAssocationOnMappedSuperclass($parentClass->name, $field);
} }
$mapping['sourceEntity'] = $subClass->name; $mapping['sourceEntity'] = $subClass->name;
} }


//$subclassMapping = $mapping; //$subclassMapping = $mapping;
if ( ! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass) { if ( ! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass && ! $parentClass->isEmbeddable) {
$mapping['inherited'] = $parentClass->name; $mapping['inherited'] = $parentClass->name;
} }
if ( ! isset($mapping['declared'])) { if ( ! isset($mapping['declared'])) {
Expand Down Expand Up @@ -554,6 +578,6 @@ protected function getDriver()
*/ */
protected function isEntity(ClassMetadataInterface $class) protected function isEntity(ClassMetadataInterface $class)
{ {
return isset($class->isMappedSuperclass) && $class->isMappedSuperclass === false; return isset($class->isMappedSuperclass) && $class->isMappedSuperclass === false && isset($class->isEmbeddable) && $class->isEmbeddable === false;
} }
} }