1
0
mirror of synced 2025-01-09 02:27:10 +03:00

[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:
Benjamin Eberlei 2013-03-27 00:10:30 +01:00
parent 32988b3cdf
commit 0204a8b69a
5 changed files with 140 additions and 6 deletions

View File

@ -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);
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}

View 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);
}
}

View File

@ -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);
}
}