Skip to content

Commit

Permalink
add support for nesting embeddables
Browse files Browse the repository at this point in the history
  • Loading branch information
deeky666 committed Aug 11, 2014
1 parent 723529f commit bca9d31
Show file tree
Hide file tree
Showing 5 changed files with 212 additions and 57 deletions.
38 changes: 37 additions & 1 deletion lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -142,14 +142,22 @@ protected function doLoadMetadata($class, $parent, $rootEntityFound, array $nonS
}

if (!$class->isMappedSuperclass) {

foreach ($class->embeddedClasses as $property => $embeddableClass) {

if (isset($embeddableClass['inherited'])) {
continue;
}

if ($embeddableClass['class'] === $class->name) {
throw MappingException::infiniteEmbeddableNesting($class->name, $property);
}

$embeddableMetadata = $this->getMetadataFor($embeddableClass['class']);

if ($embeddableMetadata->isEmbeddedClass) {
$this->addNestedEmbeddedClasses($embeddableMetadata, $class, $property);
}

$class->inlineEmbeddable($property, $embeddableMetadata);
}
}
Expand Down Expand Up @@ -370,6 +378,34 @@ private function addInheritedEmbeddedClasses(ClassMetadata $subClass, ClassMetad
}
}

/**
* Adds nested embedded classes metadata to a parent class.
*
* @param ClassMetadata $subClass Sub embedded class metadata to add nested embedded classes metadata from.
* @param ClassMetadata $parentClass Parent class to add nested embedded classes metadata to.
* @param string $prefix Embedded classes' prefix to use for nested embedded classes field names.
*/
private function addNestedEmbeddedClasses(ClassMetadata $subClass, ClassMetadata $parentClass, $prefix)
{
foreach ($subClass->embeddedClasses as $property => $embeddableClass) {
if (isset($embeddableClass['inherited'])) {
continue;
}

$embeddableMetadata = $this->getMetadataFor($embeddableClass['class']);

$parentClass->mapEmbedded(array(
'fieldName' => $prefix . '.' . $property,
'class' => $embeddableMetadata->name,
'columnPrefix' => $embeddableClass['columnPrefix'],
'declaredField' => $embeddableClass['declaredField']
? $prefix . '.' . $embeddableClass['declaredField']
: $prefix,
'originalField' => $embeddableClass['originalField'] ?: $property,
));
}
}

/**
* Adds inherited named queries to the subclass mapping.
*
Expand Down
45 changes: 33 additions & 12 deletions lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php
Original file line number Diff line number Diff line change
Expand Up @@ -929,15 +929,31 @@ public function wakeupReflection($reflService)
// Restore ReflectionClass and properties
$this->reflClass = $reflService->getClass($this->name);

$parentReflFields = array();

foreach ($this->embeddedClasses as $property => $embeddedClass) {
if (isset($embeddedClass['declaredField'])) {
$parentReflFields[$property] = new ReflectionEmbeddedProperty(
$parentReflFields[$embeddedClass['declaredField']],
$reflService->getAccessibleProperty(
$this->embeddedClasses[$embeddedClass['declaredField']]['class'],
$embeddedClass['originalField']
),
$embeddedClass['class']
);

continue;
}

$parentReflFields[$property] = $reflService->getAccessibleProperty($this->name, $property);
}

foreach ($this->fieldMappings as $field => $mapping) {
if (isset($mapping['declaredField'])) {
$declaringClass = isset($this->embeddedClasses[$mapping['declaredField']]['declared'])
? $this->embeddedClasses[$mapping['declaredField']]['declared'] : $this->name;

$this->reflFields[$field] = new ReflectionEmbeddedProperty(
$reflService->getAccessibleProperty($declaringClass, $mapping['declaredField']),
$reflService->getAccessibleProperty($this->embeddedClasses[$mapping['declaredField']]['class'], $mapping['originalField']),
$this->embeddedClasses[$mapping['declaredField']]['class']
$parentReflFields[$mapping['declaredField']],
$reflService->getAccessibleProperty($mapping['originalClass'], $mapping['originalField']),
$mapping['originalClass']
);
continue;
}
Expand Down Expand Up @@ -3171,15 +3187,13 @@ public function getMetadataValue($name) {
*/
public function mapEmbedded(array $mapping)
{
if ($this->isEmbeddedClass) {
throw MappingException::noEmbeddablesInEmbeddable($this->name);
}

$this->assertFieldNotMapped($mapping['fieldName']);

$this->embeddedClasses[$mapping['fieldName']] = array(
'class' => $this->fullyQualifiedClassName($mapping['class']),
'columnPrefix' => $mapping['columnPrefix'],
'declaredField' => isset($mapping['declaredField']) ? $mapping['declaredField'] : null,
'originalField' => isset($mapping['originalField']) ? $mapping['originalField'] : null,
);
}

Expand All @@ -3192,8 +3206,15 @@ public function mapEmbedded(array $mapping)
public function inlineEmbeddable($property, ClassMetadataInfo $embeddable)
{
foreach ($embeddable->fieldMappings as $fieldMapping) {
$fieldMapping['declaredField'] = $property;
$fieldMapping['originalField'] = $fieldMapping['fieldName'];
$fieldMapping['originalClass'] = isset($fieldMapping['originalClass'])
? $fieldMapping['originalClass']
: $embeddable->name;
$fieldMapping['declaredField'] = isset($fieldMapping['declaredField'])
? $property . '.' . $fieldMapping['declaredField']
: $property;
$fieldMapping['originalField'] = isset($fieldMapping['originalField'])
? $fieldMapping['originalField']
: $fieldMapping['fieldName'];
$fieldMapping['fieldName'] = $property . "." . $fieldMapping['fieldName'];

if (! empty($this->embeddedClasses[$property]['columnPrefix'])) {
Expand Down
20 changes: 15 additions & 5 deletions lib/Doctrine/ORM/Mapping/MappingException.php
Original file line number Diff line number Diff line change
Expand Up @@ -782,11 +782,21 @@ public static function missingSequenceName($className)
);
}

public static function noEmbeddablesInEmbeddable($className)
/**
* @param string $className
* @param string $propertyName
*
* @return MappingException
*/
public static function infiniteEmbeddableNesting($className, $propertyName)
{
return new self(sprintf(
"You embedded one or more embeddables in embeddable '%s', but this behavior is currently unsupported.",
$className
));
return new self(
sprintf(
'Infinite nesting detected for embedded property %s::%s. ' .
'You cannot embed an embeddable from the same type inside an embeddable.',
$className,
$propertyName
)
);
}
}
Loading

0 comments on commit bca9d31

Please sign in to comment.