Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/ValueObjects'
Browse files Browse the repository at this point in the history
Conflicts:
	lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php
  • Loading branch information
schmittjoh committed Nov 1, 2013
2 parents 514dd4e + 9613f1d commit 38b041d
Show file tree
Hide file tree
Showing 9 changed files with 359 additions and 8 deletions.
5 changes: 5 additions & 0 deletions lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php
Expand Up @@ -140,6 +140,11 @@ protected function doLoadMetadata($class, $parent, $rootEntityFound, array $nonS
$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);
}
Expand Down
83 changes: 76 additions & 7 deletions lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php
Expand Up @@ -246,6 +246,13 @@ class ClassMetadataInfo implements ClassMetadata
*/
public $isMappedSuperclass = false;

/**
* READ-ONLY: Wheather this class describes the mapping of an embeddable class.
*
* @var boolean
*/
public $isEmbeddedClass = false;

/**
* READ-ONLY: The names of the parent classes (ancestors).
*
Expand All @@ -260,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.
*
Expand Down Expand Up @@ -884,6 +898,15 @@ public function wakeupReflection($reflService)
$this->reflClass = $reflService->getClass($this->name);

foreach ($this->fieldMappings as $field => $mapping) {
if (isset($mapping['declaredField'])) {
$this->reflFields[$field] = new ReflectionEmbeddedProperty(
$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);
Expand Down Expand Up @@ -925,8 +948,12 @@ public function initializeReflection($reflService)
*/
public function validateIdentifier()
{
if ($this->isMappedSuperclass || $this->isEmbeddedClass) {
return;
}

// Verify & complete identifier mapping
if ( ! $this->identifier && ! $this->isMappedSuperclass) {
if ( ! $this->identifier) {
throw MappingException::identifierRequired($this->name);
}

Expand Down Expand Up @@ -2162,9 +2189,8 @@ private function _isInheritanceType($type)
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;
}

Expand Down Expand Up @@ -2412,9 +2438,7 @@ protected function _storeAssociationMapping(array $assocMapping)
{
$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;
}
Expand Down Expand Up @@ -3044,4 +3068,49 @@ public function getMetadataValue($name) {

return null;
}

/**
* 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']; // TODO: Change DQL parser to accept this dot notation
$fieldMapping['columnName'] = $property . "_" . $fieldMapping['columnName']; // TODO: Use naming strategy

$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);
}
}
}
5 changes: 5 additions & 0 deletions lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php
Expand Up @@ -85,6 +85,8 @@ public function loadMetadataForClass($className, ClassMetadata $metadata)
$mappedSuperclassAnnot = $classAnnotations['Doctrine\ORM\Mapping\MappedSuperclass'];
$metadata->setCustomRepositoryClass($mappedSuperclassAnnot->repositoryClass);
$metadata->isMappedSuperclass = true;
} else if (isset($classAnnotations['Doctrine\ORM\Mapping\Embeddable'])) {
$metadata->isEmbeddedClass = true;
} else {
throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className);
}
Expand Down Expand Up @@ -364,6 +366,9 @@ public function loadMetadataForClass($className, ClassMetadata $metadata)
}

$metadata->mapManyToMany($mapping);
} else if ($embeddedAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\Embedded')) {
$mapping['class'] = $embeddedAnnot->class;
$metadata->mapEmbedded($mapping);
}
}

Expand Down
4 changes: 3 additions & 1 deletion lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php
Expand Up @@ -19,6 +19,8 @@

require_once __DIR__.'/../Annotation.php';
require_once __DIR__.'/../Entity.php';
require_once __DIR__.'/../Embeddable.php';
require_once __DIR__.'/../Embedded.php';
require_once __DIR__.'/../MappedSuperclass.php';
require_once __DIR__.'/../InheritanceType.php';
require_once __DIR__.'/../DiscriminatorColumn.php';
Expand Down Expand Up @@ -64,4 +66,4 @@
require_once __DIR__.'/../AssociationOverrides.php';
require_once __DIR__.'/../AttributeOverride.php';
require_once __DIR__.'/../AttributeOverrides.php';
require_once __DIR__.'/../EntityListeners.php';
require_once __DIR__.'/../EntityListeners.php';
28 changes: 28 additions & 0 deletions lib/Doctrine/ORM/Mapping/Embeddable.php
@@ -0,0 +1,28 @@
<?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;

/**
* @Annotation
* @Target("PROPERTY")
*/
final class Embeddable implements Annotation
{
}
32 changes: 32 additions & 0 deletions lib/Doctrine/ORM/Mapping/Embedded.php
@@ -0,0 +1,32 @@
<?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;

/**
* @Annotation
* @Target("CLASS")
*/
final class Embedded implements Annotation
{
/**
* @var string
*/
public $class;
}
66 changes: 66 additions & 0 deletions lib/Doctrine/ORM/Mapping/ReflectionEmbeddedProperty.php
@@ -0,0 +1,66 @@
<?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.
*
* TODO: Move this class into Common\Reflection
*/
class ReflectionEmbeddedProperty
{
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);
}
}
1 change: 1 addition & 0 deletions lib/Doctrine/ORM/Tools/SchemaTool.php
Expand Up @@ -126,6 +126,7 @@ private function processingNotRequired($class, array $processedClasses)
return (
isset($processedClasses[$class->name]) ||
$class->isMappedSuperclass ||
$class->isEmbeddedClass ||
($class->isInheritanceTypeSingleTable() && $class->name != $class->rootEntityName)
);
}
Expand Down

0 comments on commit 38b041d

Please sign in to comment.