[DDC-93] Implement first working version of value objects using a ReflectionProxy object, bypassing changes to UnitOfWork, Persisters and Hydrators.
This commit is contained in:
parent
32988b3cdf
commit
0204a8b69a
@ -136,6 +136,11 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
|
||||
$this->completeIdGeneratorMapping($class);
|
||||
}
|
||||
|
||||
foreach ($class->embeddedClasses as $property => $embeddableClass) {
|
||||
$embeddableMetadata = $this->getMetadataFor($embeddableClass);
|
||||
$class->inlineEmbeddable($property, $embeddableMetadata);
|
||||
}
|
||||
|
||||
if ($parent && $parent->isInheritanceTypeSingleTable()) {
|
||||
$class->setPrimaryTable($parent->table);
|
||||
}
|
||||
|
@ -267,6 +267,13 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
*/
|
||||
public $subClasses = array();
|
||||
|
||||
/**
|
||||
* READ-ONLY: The names of all embedded classes based on properties.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $embeddedClasses = array();
|
||||
|
||||
/**
|
||||
* READ-ONLY: The named queries allowed to be called directly from Repository.
|
||||
*
|
||||
@ -887,6 +894,15 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
$this->reflClass = $reflService->getClass($this->name);
|
||||
|
||||
foreach ($this->fieldMappings as $field => $mapping) {
|
||||
if (isset($mapping['declaredField'])) {
|
||||
$this->reflFields[$field] = new ReflectionProxy(
|
||||
$reflService->getAccessibleProperty($this->name, $mapping['declaredField']),
|
||||
$reflService->getAccessibleProperty($this->embeddedClasses[$mapping['declaredField']], $mapping['originalField']),
|
||||
$this->embeddedClasses[$mapping['declaredField']]
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->reflFields[$field] = isset($mapping['declared'])
|
||||
? $reflService->getAccessibleProperty($mapping['declared'], $field)
|
||||
: $reflService->getAccessibleProperty($this->name, $field);
|
||||
@ -2166,9 +2182,8 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
public function mapField(array $mapping)
|
||||
{
|
||||
$this->_validateAndCompleteFieldMapping($mapping);
|
||||
if (isset($this->fieldMappings[$mapping['fieldName']]) || isset($this->associationMappings[$mapping['fieldName']])) {
|
||||
throw MappingException::duplicateFieldMapping($this->name, $mapping['fieldName']);
|
||||
}
|
||||
$this->assertFieldNotMapped($mapping['fieldName']);
|
||||
|
||||
$this->fieldMappings[$mapping['fieldName']] = $mapping;
|
||||
}
|
||||
|
||||
@ -2416,9 +2431,7 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
{
|
||||
$sourceFieldName = $assocMapping['fieldName'];
|
||||
|
||||
if (isset($this->fieldMappings[$sourceFieldName]) || isset($this->associationMappings[$sourceFieldName])) {
|
||||
throw MappingException::duplicateFieldMapping($this->name, $sourceFieldName);
|
||||
}
|
||||
$this->assertFieldNotMapped($sourceFieldName);
|
||||
|
||||
$this->associationMappings[$sourceFieldName] = $assocMapping;
|
||||
}
|
||||
@ -3030,4 +3043,49 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
|
||||
return $className;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map Embedded Class
|
||||
*
|
||||
* @array $mapping
|
||||
* @return void
|
||||
*/
|
||||
public function mapEmbedded(array $mapping)
|
||||
{
|
||||
$this->assertFieldNotMapped($mapping['fieldName']);
|
||||
|
||||
$this->embeddedClasses[$mapping['fieldName']] = $this->fullyQualifiedClassName($mapping['class']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inline the embeddable class
|
||||
*
|
||||
* @param string $property
|
||||
* @param ClassMetadataInfo $embeddable
|
||||
*/
|
||||
public function inlineEmbeddable($property, ClassMetadataInfo $embeddable)
|
||||
{
|
||||
foreach ($embeddable->fieldMappings as $fieldMapping) {
|
||||
$fieldMapping['declaredField'] = $property;
|
||||
$fieldMapping['originalField'] = $fieldMapping['fieldName'];
|
||||
$fieldMapping['fieldName'] = $property . $fieldMapping['fieldName'];
|
||||
$fieldMapping['columnName'] = $property . "_" . $fieldMapping['columnName'];
|
||||
|
||||
$this->mapField($fieldMapping);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $fieldName
|
||||
* @throws MappingException
|
||||
*/
|
||||
private function assertFieldNotMapped($fieldName)
|
||||
{
|
||||
if (isset($this->fieldMappings[$fieldName]) ||
|
||||
isset($this->associationMappings[$fieldName]) ||
|
||||
isset($this->embeddedClasses[$fieldName])) {
|
||||
|
||||
throw MappingException::duplicateFieldMapping($this->name, $fieldName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -366,6 +366,9 @@ class AnnotationDriver extends AbstractAnnotationDriver
|
||||
}
|
||||
|
||||
$metadata->mapManyToMany($mapping);
|
||||
} else if ($embeddedAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\Embedded')) {
|
||||
$mapping['class'] = $embeddedAnnot->class;
|
||||
$metadata->mapEmbedded($mapping);
|
||||
}
|
||||
}
|
||||
|
||||
|
64
lib/Doctrine/ORM/Mapping/ReflectionProxy.php
Normal file
64
lib/Doctrine/ORM/Mapping/ReflectionProxy.php
Normal file
@ -0,0 +1,64 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
/**
|
||||
* Acts as a proxy to a nested Property structure, making it look like
|
||||
* just a single scalar property.
|
||||
*
|
||||
* This way value objects "just work" without UnitOfWork, Persisters or Hydrators
|
||||
* needing any changes.
|
||||
*/
|
||||
class ReflectionProxy
|
||||
{
|
||||
private $parentProperty;
|
||||
private $childProperty;
|
||||
private $class;
|
||||
|
||||
public function __construct($parentProperty, $childProperty, $class)
|
||||
{
|
||||
$this->parentProperty = $parentProperty;
|
||||
$this->childProperty = $childProperty;
|
||||
$this->class = $class;
|
||||
}
|
||||
|
||||
public function getValue($object)
|
||||
{
|
||||
$embeddedObject = $this->parentProperty->getValue($object);
|
||||
|
||||
if ($embeddedObject === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->childProperty->getValue($embeddedObject);
|
||||
}
|
||||
|
||||
public function setValue($object, $value)
|
||||
{
|
||||
$embeddedObject = $this->parentProperty->getValue($object);
|
||||
|
||||
if ($embeddedObject === null) {
|
||||
$embeddedObject = new $this->class; // TODO
|
||||
$this->parentProperty->setValue($object, $embeddedObject);
|
||||
}
|
||||
|
||||
$this->childProperty->setValue($embeddedObject, $value);
|
||||
}
|
||||
}
|
@ -32,7 +32,11 @@ class ValueObjectsTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$this->_em->clear();
|
||||
|
||||
$person = $this->_em->find(DDC93Person::CLASSNAME, $person->id);
|
||||
|
||||
$this->assertInstanceOf(DDC93Address::CLASSNAME, $person->address);
|
||||
$this->assertEquals('United States of Tara Street', $person->address->street);
|
||||
$this->assertEquals('12345', $person->address->zip);
|
||||
$this->assertEquals('funkytown', $person->address->city);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user