From 85790f0752faa9522a47134f1948a94d6b9819ec Mon Sep 17 00:00:00 2001 From: "Fabio B. Silva" Date: Sat, 14 Apr 2012 02:10:44 -0300 Subject: [PATCH] support for attribute override --- doctrine-mapping.xsd | 16 ++ .../ORM/Mapping/ClassMetadataInfo.php | 50 ++++++- lib/Doctrine/ORM/Mapping/Column.php | 2 +- .../ORM/Mapping/Driver/AnnotationDriver.php | 75 +++++++--- .../Mapping/Driver/DoctrineAnnotations.php | 5 +- lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php | 132 ++++++++++------- .../ORM/Mapping/Driver/YamlDriver.php | 138 +++++++++++------- lib/Doctrine/ORM/Mapping/MappingException.php | 11 ++ .../Tests/Models/DDC964/DDC964Guest.php | 28 ++++ .../Tests/Models/DDC964/DDC964User.php | 16 +- .../ORM/Mapping/AbstractMappingDriverTest.php | 40 +++++ .../Tests/ORM/Mapping/ClassMetadataTest.php | 30 +++- ...ctrine.Tests.Models.DDC964.DDC964Guest.php | 12 ++ ...octrine.Tests.Models.DDC964.DDC964User.php | 11 +- ...ne.Tests.Models.DDC964.DDC964Guest.dcm.xml | 8 + ...ine.Tests.Models.DDC964.DDC964User.dcm.xml | 4 +- ...ne.Tests.Models.DDC964.DDC964Guest.dcm.yml | 13 +- ...ine.Tests.Models.DDC964.DDC964User.dcm.yml | 7 +- 18 files changed, 449 insertions(+), 149 deletions(-) diff --git a/doctrine-mapping.xsd b/doctrine-mapping.xsd index a51f4906d..e8b2e26b2 100644 --- a/doctrine-mapping.xsd +++ b/doctrine-mapping.xsd @@ -146,6 +146,7 @@ + @@ -500,4 +501,19 @@ + + + + + + + + + + + + + + + diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php index b439573ac..81acb8021 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php @@ -1238,7 +1238,7 @@ class ClassMetadataInfo implements ClassMetadata $mapping['isOwningSide'] = true; // assume owning side until we hit mappedBy // unset optional indexBy attribute if its empty - if (!isset($mapping['indexBy']) || !$mapping['indexBy']) { + if ( ! isset($mapping['indexBy']) || !$mapping['indexBy']) { unset($mapping['indexBy']); } @@ -1364,7 +1364,7 @@ class ClassMetadataInfo implements ClassMetadata foreach ($mapping['joinColumns'] as $key => &$joinColumn) { if ($mapping['type'] === self::ONE_TO_ONE && ! $this->isInheritanceTypeSingleTable()) { if (count($mapping['joinColumns']) == 1) { - if (! isset($mapping['id']) || ! $mapping['id']) { + if ( ! isset($mapping['id']) || ! $mapping['id']) { $joinColumn['unique'] = true; } } else { @@ -1383,7 +1383,7 @@ class ClassMetadataInfo implements ClassMetadata } if ($uniqueContraintColumns) { - if (!$this->table) { + if ( ! $this->table) { throw new \RuntimeException("ClassMetadataInfo::setTable() has to be called before defining a one to one relationship."); } $this->table['uniqueConstraints'][$mapping['fieldName']."_uniq"] = array( @@ -1810,7 +1810,7 @@ class ClassMetadataInfo implements ClassMetadata */ public function setAssociationOverride($fieldName, array $overrideMapping) { - if (!isset($this->associationMappings[$fieldName])) { + if ( ! isset($this->associationMappings[$fieldName])) { throw MappingException::invalidOverrideFieldName($this->name, $fieldName); } @@ -1848,6 +1848,44 @@ class ClassMetadataInfo implements ClassMetadata $this->associationMappings[$fieldName] = $mapping; } + /** + * Sets the override for a mapped field. + * + * @param string $fieldName + * @param array $mapping + */ + public function setAttributeOverride($fieldName, array $overrideMapping) + { + if ( ! isset($this->fieldMappings[$fieldName])) { + throw MappingException::invalidOverrideFieldName($this->name, $fieldName); + } + + $mapping = $this->fieldMappings[$fieldName]; + + if (isset($mapping['id'])) { + $overrideMapping['id'] = $mapping['id']; + } + + if ( ! isset($overrideMapping['type']) || $overrideMapping['type'] === null) { + $overrideMapping['type'] = $mapping['type']; + } + + if ( ! isset($overrideMapping['fieldName']) || $overrideMapping['fieldName'] === null) { + $overrideMapping['fieldName'] = $mapping['fieldName']; + } + + if ($overrideMapping['type'] !== $mapping['type']) { + throw MappingException::invalidOverrideFieldType($this->name, $fieldName); + } + + unset($this->fieldMappings[$fieldName]); + unset($this->fieldNames[$mapping['columnName']]); + unset($this->columnNames[$mapping['fieldName']]); + $this->_validateAndCompleteFieldMapping($overrideMapping); + + $this->fieldMappings[$fieldName] = $overrideMapping; + } + /** * Checks whether a mapped field is inherited from an entity superclass. * @@ -2408,7 +2446,7 @@ class ClassMetadataInfo implements ClassMetadata */ public function getSingleAssociationJoinColumnName($fieldName) { - if (!$this->isAssociationWithSingleJoinColumn($fieldName)) { + if ( ! $this->isAssociationWithSingleJoinColumn($fieldName)) { throw MappingException::noSingleAssociationJoinColumnFound($this->name, $fieldName); } return $this->associationMappings[$fieldName]['joinColumns'][0]['name']; @@ -2422,7 +2460,7 @@ class ClassMetadataInfo implements ClassMetadata */ public function getSingleAssociationReferencedJoinColumnName($fieldName) { - if (!$this->isAssociationWithSingleJoinColumn($fieldName)) { + if ( ! $this->isAssociationWithSingleJoinColumn($fieldName)) { throw MappingException::noSingleAssociationJoinColumnFound($this->name, $fieldName); } return $this->associationMappings[$fieldName]['joinColumns'][0]['referencedColumnName']; diff --git a/lib/Doctrine/ORM/Mapping/Column.php b/lib/Doctrine/ORM/Mapping/Column.php index 5f7dd7f64..d1c85509b 100644 --- a/lib/Doctrine/ORM/Mapping/Column.php +++ b/lib/Doctrine/ORM/Mapping/Column.php @@ -21,7 +21,7 @@ namespace Doctrine\ORM\Mapping; /** * @Annotation - * @Target("PROPERTY") + * @Target({"PROPERTY","ANNOTATION"}) */ final class Column implements Annotation { diff --git a/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php b/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php index 053342cf8..9885c43ec 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php @@ -24,7 +24,8 @@ use Doctrine\Common\Cache\ArrayCache, Doctrine\Common\Annotations\AnnotationRegistry, Doctrine\ORM\Mapping\ClassMetadataInfo, Doctrine\ORM\Mapping\MappingException, - Doctrine\ORM\Mapping\JoinColumn; + Doctrine\ORM\Mapping\JoinColumn, + Doctrine\ORM\Mapping\Column; /** * The AnnotationDriver reads the mapping metadata from docblock annotations. @@ -135,7 +136,7 @@ class AnnotationDriver implements Driver public function loadMetadataForClass($className, ClassMetadataInfo $metadata) { $class = $metadata->getReflectionClass(); - if (!$class) { + if ( ! $class) { // this happens when running annotation driver in combination with // static reflection services. This is not the nicest fix $class = new \ReflectionClass($metadata->name); @@ -261,12 +262,12 @@ class AnnotationDriver implements Driver if (isset($classAnnotations['Doctrine\ORM\Mapping\NamedQueries'])) { $namedQueriesAnnot = $classAnnotations['Doctrine\ORM\Mapping\NamedQueries']; - if (!is_array($namedQueriesAnnot->value)) { + if ( ! is_array($namedQueriesAnnot->value)) { throw new \UnexpectedValueException("@NamedQueries should contain an array of @NamedQuery annotations."); } foreach ($namedQueriesAnnot->value as $namedQuery) { - if (!($namedQuery instanceof \Doctrine\ORM\Mapping\NamedQuery)) { + if ( ! ($namedQuery instanceof \Doctrine\ORM\Mapping\NamedQuery)) { throw new \UnexpectedValueException("@NamedQueries should contain an array of @NamedQuery annotations."); } $metadata->addNamedQuery(array( @@ -378,23 +379,7 @@ class AnnotationDriver implements Driver throw MappingException::propertyTypeIsRequired($className, $property->getName()); } - $mapping['type'] = $columnAnnot->type; - $mapping['length'] = $columnAnnot->length; - $mapping['precision'] = $columnAnnot->precision; - $mapping['scale'] = $columnAnnot->scale; - $mapping['nullable'] = $columnAnnot->nullable; - $mapping['unique'] = $columnAnnot->unique; - if ($columnAnnot->options) { - $mapping['options'] = $columnAnnot->options; - } - - if (isset($columnAnnot->name)) { - $mapping['columnName'] = $columnAnnot->name; - } - - if (isset($columnAnnot->columnDefinition)) { - $mapping['columnDefinition'] = $columnAnnot->columnDefinition; - } + $mapping = $this->columnToArray($property->getName(), $columnAnnot); if ($idAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\Id')) { $mapping['id'] = true; @@ -537,6 +522,16 @@ class AnnotationDriver implements Driver } } + $attributeOverrides = array(); + // Evaluate AttributeOverrides annotation + if (isset($classAnnotations['Doctrine\ORM\Mapping\AttributeOverrides'])) { + $attributeOverridesAnnot = $classAnnotations['Doctrine\ORM\Mapping\AttributeOverrides']; + foreach ($attributeOverridesAnnot->value as $attributeOverrideAnnot) { + $attributeOverride = $this->columnToArray($attributeOverrideAnnot->name, $attributeOverrideAnnot->column); + $metadata->setAttributeOverride($attributeOverrideAnnot->name, $attributeOverride); + } + } + // Evaluate @HasLifecycleCallbacks annotation if (isset($classAnnotations['Doctrine\ORM\Mapping\HasLifecycleCallbacks'])) { foreach ($class->getMethods() as $method) { @@ -625,7 +620,7 @@ class AnnotationDriver implements Driver return $this->_classNames; } - if (!$this->_paths) { + if ( ! $this->_paths) { throw MappingException::pathRequired(); } @@ -680,7 +675,7 @@ class AnnotationDriver implements Driver */ private function getFetchMode($className, $fetchMode) { - if(!defined('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $fetchMode)) { + if( ! defined('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $fetchMode)) { throw MappingException::invalidFetchMode($className, $fetchMode); } @@ -705,6 +700,40 @@ class AnnotationDriver implements Driver ); } + /** + * Parse the given Column as array + * + * @param string $fieldName + * @param Column $column + * @return array + */ + private function columnToArray($fieldName, Column $column) + { + $mapping = array( + 'fieldName' => $fieldName, + 'type' => $column->type, + 'scale' => $column->scale, + 'length' => $column->length, + 'unique' => $column->unique, + 'nullable' => $column->nullable, + 'precision' => $column->precision + ); + + if ($column->options) { + $mapping['options'] = $column->options; + } + + if (isset($column->name)) { + $mapping['columnName'] = $column->name; + } + + if (isset($column->columnDefinition)) { + $mapping['columnDefinition'] = $column->columnDefinition; + } + + return $mapping; + } + /** * Factory method for the Annotation Driver * diff --git a/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php b/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php index cb911e522..f5a1a32b5 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php +++ b/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php @@ -61,4 +61,7 @@ require_once __DIR__.'/../NamedNativeQueries.php'; require_once __DIR__.'/../SqlResultSetMapping.php'; require_once __DIR__.'/../SqlResultSetMappings.php'; require_once __DIR__.'/../AssociationOverride.php'; -require_once __DIR__.'/../AssociationOverrides.php'; \ No newline at end of file +require_once __DIR__.'/../AssociationOverrides.php'; +require_once __DIR__.'/../AssociationOverrides.php'; +require_once __DIR__.'/../AttributeOverride.php'; +require_once __DIR__.'/../AttributeOverrides.php'; \ No newline at end of file diff --git a/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php b/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php index 12c50e4a3..bb2dcad99 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php @@ -50,7 +50,7 @@ class XmlDriver extends AbstractFileDriver public function loadMetadataForClass($className, ClassMetadataInfo $metadata) { $xmlRoot = $this->getElement($className); - + if ($xmlRoot->getName() == 'entity') { if (isset($xmlRoot['repository-class'])) { $metadata->setCustomRepositoryClass((string)$xmlRoot['repository-class']); @@ -224,51 +224,8 @@ class XmlDriver extends AbstractFileDriver // Evaluate mappings if (isset($xmlRoot->field)) { foreach ($xmlRoot->field as $fieldMapping) { - $mapping = array( - 'fieldName' => (string)$fieldMapping['name'], - ); - - if (isset($fieldMapping['type'])) { - $mapping['type'] = (string)$fieldMapping['type']; - } - - if (isset($fieldMapping['column'])) { - $mapping['columnName'] = (string)$fieldMapping['column']; - } - - if (isset($fieldMapping['length'])) { - $mapping['length'] = (int)$fieldMapping['length']; - } - - if (isset($fieldMapping['precision'])) { - $mapping['precision'] = (int)$fieldMapping['precision']; - } - - if (isset($fieldMapping['scale'])) { - $mapping['scale'] = (int)$fieldMapping['scale']; - } - - if (isset($fieldMapping['unique'])) { - $mapping['unique'] = ((string)$fieldMapping['unique'] == "false") ? false : true; - } - - if (isset($fieldMapping['nullable'])) { - $mapping['nullable'] = ((string)$fieldMapping['nullable'] == "false") ? false : true; - } - - if (isset($fieldMapping['version']) && $fieldMapping['version']) { - $metadata->setVersionMapping($mapping); - } - - if (isset($fieldMapping['column-definition'])) { - $mapping['columnDefinition'] = (string)$fieldMapping['column-definition']; - } - - if (isset($fieldMapping->options)) { - $mapping['options'] = $this->_parseOptions($fieldMapping->options->children()); - } - - $mappings[] = $mapping; + $mapping = $this->columnToArray($fieldMapping); + $metadata->mapField($mapping); } } @@ -293,6 +250,10 @@ class XmlDriver extends AbstractFileDriver $mapping['type'] = (string)$idElement['type']; } + if (isset($idElement['length'])) { + $mapping['length'] = (string)$idElement['length']; + } + if (isset($idElement['column'])) { $mapping['columnName'] = (string)$idElement['column']; } @@ -519,25 +480,37 @@ class XmlDriver extends AbstractFileDriver } } + // Evaluate association-overrides + if (isset($xmlRoot->{'attribute-overrides'})) { + foreach ($xmlRoot->{'attribute-overrides'}->{'attribute-override'} as $overrideElement) { + $fieldName = (string) $overrideElement['name']; + foreach ($overrideElement->field as $field) { + $mapping = $this->columnToArray($field); + $mapping['fieldName'] = $fieldName; + $metadata->setAttributeOverride($fieldName, $mapping); + } + } + } + // Evaluate association-overrides if (isset($xmlRoot->{'association-overrides'})) { - foreach ($xmlRoot->{'association-overrides'}->{'association-override'} as $associationOverrideElement) { - $fieldName = (string) $associationOverrideElement['name']; + foreach ($xmlRoot->{'association-overrides'}->{'association-override'} as $overrideElement) { + $fieldName = (string) $overrideElement['name']; $override = array(); // Check for join-columns - if (isset($associationOverrideElement->{'join-columns'})) { + if (isset($overrideElement->{'join-columns'})) { $joinColumns = array(); - foreach ($associationOverrideElement->{'join-columns'}->{'join-column'} as $joinColumnElement) { + foreach ($overrideElement->{'join-columns'}->{'join-column'} as $joinColumnElement) { $joinColumns[] = $this->joinColumnToArray($joinColumnElement); } $override['joinColumns'] = $joinColumns; } // Check for join-table - if ($associationOverrideElement->{'join-table'}) { + if ($overrideElement->{'join-table'}) { $joinTable = null; - $joinTableElement = $associationOverrideElement->{'join-table'}; + $joinTableElement = $overrideElement->{'join-table'}; $joinTable = array( 'name' => (string) $joinTableElement['name'], @@ -633,6 +606,61 @@ class XmlDriver extends AbstractFileDriver return $joinColumn; } + /** + * Parse the given field as array + * + * @param SimpleXMLElement $fieldMapping + * @return array + */ + private function columnToArray(SimpleXMLElement $fieldMapping) + { + $mapping = array( + 'fieldName' => (string)$fieldMapping['name'], + ); + + if (isset($fieldMapping['type'])) { + $mapping['type'] = (string)$fieldMapping['type']; + } + + if (isset($fieldMapping['column'])) { + $mapping['columnName'] = (string)$fieldMapping['column']; + } + + if (isset($fieldMapping['length'])) { + $mapping['length'] = (int)$fieldMapping['length']; + } + + if (isset($fieldMapping['precision'])) { + $mapping['precision'] = (int)$fieldMapping['precision']; + } + + if (isset($fieldMapping['scale'])) { + $mapping['scale'] = (int)$fieldMapping['scale']; + } + + if (isset($fieldMapping['unique'])) { + $mapping['unique'] = ((string)$fieldMapping['unique'] == "false") ? false : true; + } + + if (isset($fieldMapping['nullable'])) { + $mapping['nullable'] = ((string)$fieldMapping['nullable'] == "false") ? false : true; + } + + if (isset($fieldMapping['version']) && $fieldMapping['version']) { + $metadata->setVersionMapping($mapping); + } + + if (isset($fieldMapping['column-definition'])) { + $mapping['columnDefinition'] = (string)$fieldMapping['column-definition']; + } + + if (isset($fieldMapping->options)) { + $mapping['options'] = $this->_parseOptions($fieldMapping->options->children()); + } + + return $mapping; + } + /** * Gathers a list of cascade options found in the given cascade element. * diff --git a/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php b/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php index d7ec9af70..68ca0b29d 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php @@ -277,19 +277,7 @@ class YamlDriver extends AbstractFileDriver if (isset($element['fields'])) { foreach ($element['fields'] as $name => $fieldMapping) { - $mapping = array( - 'fieldName' => $name - ); - - if (isset($fieldMapping['type'])) { - $e = explode('(', $fieldMapping['type']); - $fieldMapping['type'] = $e[0]; - $mapping['type'] = $fieldMapping['type']; - - if (isset($e[1])) { - $fieldMapping['length'] = substr($e[1], 0, strlen($e[1]) - 1); - } - } + $mapping = $this->columnToArray($name, $fieldMapping); if (isset($fieldMapping['id'])) { $mapping['id'] = true; @@ -298,33 +286,6 @@ class YamlDriver extends AbstractFileDriver . strtoupper($fieldMapping['generator']['strategy']))); } } - if (isset($fieldMapping['column'])) { - $mapping['columnName'] = $fieldMapping['column']; - } - if (isset($fieldMapping['length'])) { - $mapping['length'] = $fieldMapping['length']; - } - if (isset($fieldMapping['precision'])) { - $mapping['precision'] = $fieldMapping['precision']; - } - if (isset($fieldMapping['scale'])) { - $mapping['scale'] = $fieldMapping['scale']; - } - if (isset($fieldMapping['unique'])) { - $mapping['unique'] = (bool)$fieldMapping['unique']; - } - if (isset($fieldMapping['options'])) { - $mapping['options'] = $fieldMapping['options']; - } - if (isset($fieldMapping['nullable'])) { - $mapping['nullable'] = $fieldMapping['nullable']; - } - if (isset($fieldMapping['version']) && $fieldMapping['version']) { - $metadata->setVersionMapping($mapping); - } - if (isset($fieldMapping['columnDefinition'])) { - $mapping['columnDefinition'] = $fieldMapping['columnDefinition']; - } $metadata->mapField($mapping); } @@ -359,7 +320,7 @@ class YamlDriver extends AbstractFileDriver $joinColumns[] = $this->joinColumnToArray($oneToOneElement['joinColumn']); } else if (isset($oneToOneElement['joinColumns'])) { foreach ($oneToOneElement['joinColumns'] as $name => $joinColumnElement) { - if (!isset($joinColumnElement['name'])) { + if ( ! isset($joinColumnElement['name'])) { $joinColumnElement['name'] = $name; } @@ -441,7 +402,7 @@ class YamlDriver extends AbstractFileDriver $joinColumns[] = $this->joinColumnToArray($manyToOneElement['joinColumn']); } else if (isset($manyToOneElement['joinColumns'])) { foreach ($manyToOneElement['joinColumns'] as $name => $joinColumnElement) { - if (!isset($joinColumnElement['name'])) { + if ( ! isset($joinColumnElement['name'])) { $joinColumnElement['name'] = $name; } @@ -485,7 +446,7 @@ class YamlDriver extends AbstractFileDriver } foreach ($joinTableElement['joinColumns'] as $name => $joinColumnElement) { - if (!isset($joinColumnElement['name'])) { + if ( ! isset($joinColumnElement['name'])) { $joinColumnElement['name'] = $name; } @@ -493,7 +454,7 @@ class YamlDriver extends AbstractFileDriver } foreach ($joinTableElement['inverseJoinColumns'] as $name => $joinColumnElement) { - if (!isset($joinColumnElement['name'])) { + if ( ! isset($joinColumnElement['name'])) { $joinColumnElement['name'] = $name; } @@ -528,16 +489,16 @@ class YamlDriver extends AbstractFileDriver } // Evaluate associationOverride - if (isset($element['associationOverride'])) { + if (isset($element['associationOverride']) && is_array($element['associationOverride'])) { - foreach ($element['associationOverride'] as $fieldName => $associationOverride) { + foreach ($element['associationOverride'] as $fieldName => $associationOverrideElement) { $override = array(); // Check for joinColumn - if (isset($associationOverride['joinColumn'])) { + if (isset($associationOverrideElement['joinColumn'])) { $joinColumns = array(); - foreach ($associationOverride['joinColumn'] as $name => $joinColumnElement) { - if (!isset($joinColumnElement['name'])) { + foreach ($associationOverrideElement['joinColumn'] as $name => $joinColumnElement) { + if ( ! isset($joinColumnElement['name'])) { $joinColumnElement['name'] = $name; } $joinColumns[] = $this->joinColumnToArray($joinColumnElement); @@ -546,9 +507,9 @@ class YamlDriver extends AbstractFileDriver } // Check for joinTable - if (isset($associationOverride['joinTable'])) { + if (isset($associationOverrideElement['joinTable'])) { - $joinTableElement = $associationOverride['joinTable']; + $joinTableElement = $associationOverrideElement['joinTable']; $joinTable = array( 'name' => $joinTableElement['name'] ); @@ -558,7 +519,7 @@ class YamlDriver extends AbstractFileDriver } foreach ($joinTableElement['joinColumns'] as $name => $joinColumnElement) { - if (!isset($joinColumnElement['name'])) { + if ( ! isset($joinColumnElement['name'])) { $joinColumnElement['name'] = $name; } @@ -566,7 +527,7 @@ class YamlDriver extends AbstractFileDriver } foreach ($joinTableElement['inverseJoinColumns'] as $name => $joinColumnElement) { - if (!isset($joinColumnElement['name'])) { + if ( ! isset($joinColumnElement['name'])) { $joinColumnElement['name'] = $name; } @@ -580,6 +541,15 @@ class YamlDriver extends AbstractFileDriver } } + // Evaluate associationOverride + if (isset($element['attributeOverride']) && is_array($element['attributeOverride'])) { + + foreach ($element['attributeOverride'] as $fieldName => $attributeOverrideElement) { + $mapping = $this->columnToArray($fieldName, $attributeOverrideElement); + $metadata->setAttributeOverride($fieldName, $mapping); + } + } + // Evaluate lifeCycleCallbacks if (isset($element['lifecycleCallbacks'])) { foreach ($element['lifecycleCallbacks'] as $type => $methods) { @@ -631,6 +601,68 @@ class YamlDriver extends AbstractFileDriver return $joinColumn; } + /** + * Parse the given column as array + * + * @param string $fieldName + * @param array $column + * @return array + */ + private function columnToArray($fieldName, $column) + { + $mapping = array( + 'fieldName' => $fieldName + ); + + if (isset($column['type'])) { + $params = explode('(', $column['type']); + $column['type'] = $params[0]; + $mapping['type'] = $column['type']; + + if (isset($params[1])) { + $column['length'] = substr($params[1], 0, strlen($params[1]) - 1); + } + } + + if (isset($column['column'])) { + $mapping['columnName'] = $column['column']; + } + + if (isset($column['length'])) { + $mapping['length'] = $column['length']; + } + + if (isset($column['precision'])) { + $mapping['precision'] = $column['precision']; + } + + if (isset($column['scale'])) { + $mapping['scale'] = $column['scale']; + } + + if (isset($column['unique'])) { + $mapping['unique'] = (bool)$column['unique']; + } + + if (isset($column['options'])) { + $mapping['options'] = $column['options']; + } + + if (isset($column['nullable'])) { + $mapping['nullable'] = $column['nullable']; + } + + if (isset($column['version']) && $column['version']) { + $metadata->setVersionMapping($mapping); + } + + if (isset($column['columnDefinition'])) { + $mapping['columnDefinition'] = $column['columnDefinition']; + } + + return $mapping; + } + /** * {@inheritdoc} */ diff --git a/lib/Doctrine/ORM/Mapping/MappingException.php b/lib/Doctrine/ORM/Mapping/MappingException.php index f88109f0c..dcf3a7da8 100644 --- a/lib/Doctrine/ORM/Mapping/MappingException.php +++ b/lib/Doctrine/ORM/Mapping/MappingException.php @@ -94,6 +94,17 @@ class MappingException extends \Doctrine\ORM\ORMException return new self("Invalid field override named '$fieldName' for class '$className'."); } + /** + * Exception for invalid property type override. + * + * @param string $className The entity's name + * @param string $fieldName + */ + public static function invalidOverrideFieldType($className, $fieldName) + { + return new self("The column type of attribute '$fieldName' on class '$className' could not be changed."); + } + public static function mappingNotFound($className, $fieldName) { return new self("No mapping found for field '$fieldName' on class '$className'."); diff --git a/tests/Doctrine/Tests/Models/DDC964/DDC964Guest.php b/tests/Doctrine/Tests/Models/DDC964/DDC964Guest.php index 71654a12b..90501187c 100644 --- a/tests/Doctrine/Tests/Models/DDC964/DDC964Guest.php +++ b/tests/Doctrine/Tests/Models/DDC964/DDC964Guest.php @@ -6,11 +6,39 @@ use Doctrine\Common\Collections\ArrayCollection; /** * @Entity + * @AttributeOverrides({ + * @AttributeOverride(name="id", + * column=@Column( + * name = "guest_id", + * type = "integer", + length = 140 + * ) + * ), + * @AttributeOverride(name="name", + * column=@Column( + * name = "guest_name", + * nullable = false, + * unique = true, + length = 240 + * ) + * ) + * }) */ class DDC964Guest extends DDC964User { public static function loadMetadata($metadata) { + $metadata->setAttributeOverride('id', array( + 'columnName' => 'guest_id', + 'type' => 'integer', + 'length' => 140, + )); + $metadata->setAttributeOverride('name',array( + 'columnName' => 'guest_name', + 'nullable' => false, + 'unique' => true, + 'length' => 240, + )); } } \ No newline at end of file diff --git a/tests/Doctrine/Tests/Models/DDC964/DDC964User.php b/tests/Doctrine/Tests/Models/DDC964/DDC964User.php index 07947e57f..608f42d96 100644 --- a/tests/Doctrine/Tests/Models/DDC964/DDC964User.php +++ b/tests/Doctrine/Tests/Models/DDC964/DDC964User.php @@ -11,13 +11,14 @@ class DDC964User { /** + * @Id * @GeneratedValue - * @Id @Column(type="integer") + * @Column(type="integer", name="user_id", length=150) */ protected $id; /** - * @Column + * @Column(name="user_name", nullable=true, unique=false, length=250) */ protected $name; @@ -112,11 +113,16 @@ class DDC964User 'id' => true, 'fieldName' => 'id', 'type' => 'integer', - 'columnName' => 'id', + 'columnName' => 'user_id', + 'length' => 150, )); $metadata->mapField(array( - 'fieldName' => 'name', - 'type' => 'string', + 'fieldName' => 'name', + 'type' => 'string', + 'columnName'=> 'user_name', + 'nullable' => true, + 'unique' => false, + 'length' => 250, )); $metadata->mapManyToOne(array( diff --git a/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php b/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php index 9e1cbde2d..e83a9dbc2 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php @@ -693,6 +693,46 @@ abstract class AbstractMappingDriverTest extends \Doctrine\Tests\OrmTestCase $this->assertEquals(array('adminaddress_id'=>'adminaddress_id'), $adminAddress['joinColumnFieldNames']); $this->assertEquals(array('id'=>'adminaddress_id'), $adminAddress['targetToSourceKeyColumns']); } + + /** + * @group DDC-964 + */ + public function testAttributeOverridesMapping() + { + + $factory = $this->createClassMetadataFactory(); + $guestMetadata = $factory->getMetadataFor('Doctrine\Tests\Models\DDC964\DDC964Guest'); + $adminMetadata = $factory->getMetadataFor('Doctrine\Tests\Models\DDC964\DDC964Admin'); + + $this->assertTrue($adminMetadata->fieldMappings['id']['id']); + $this->assertEquals('id', $adminMetadata->fieldMappings['id']['fieldName']); + $this->assertEquals('user_id', $adminMetadata->fieldMappings['id']['columnName']); + $this->assertEquals(array('user_id'=>'id','user_name'=>'name'), $adminMetadata->fieldNames); + $this->assertEquals(array('id'=>'user_id','name'=>'user_name'), $adminMetadata->columnNames); + $this->assertEquals(150, $adminMetadata->fieldMappings['id']['length']); + + + $this->assertEquals('name', $adminMetadata->fieldMappings['name']['fieldName']); + $this->assertEquals('user_name', $adminMetadata->fieldMappings['name']['columnName']); + $this->assertEquals(250, $adminMetadata->fieldMappings['name']['length']); + $this->assertTrue($adminMetadata->fieldMappings['name']['nullable']); + $this->assertFalse($adminMetadata->fieldMappings['name']['unique']); + + + $this->assertTrue($guestMetadata->fieldMappings['id']['id']); + $this->assertEquals('guest_id', $guestMetadata->fieldMappings['id']['columnName']); + $this->assertEquals('id', $guestMetadata->fieldMappings['id']['fieldName']); + $this->assertEquals(array('guest_id'=>'id','guest_name'=>'name'), $guestMetadata->fieldNames); + $this->assertEquals(array('id'=>'guest_id','name'=>'guest_name'), $guestMetadata->columnNames); + $this->assertEquals(140, $guestMetadata->fieldMappings['id']['length']); + + $this->assertEquals('name', $guestMetadata->fieldMappings['name']['fieldName']); + $this->assertEquals('guest_name', $guestMetadata->fieldMappings['name']['columnName']); + $this->assertEquals(240, $guestMetadata->fieldMappings['name']['length']); + $this->assertFalse($guestMetadata->fieldMappings['name']['nullable']); + $this->assertTrue($guestMetadata->fieldMappings['name']['unique']); + } + } /** diff --git a/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php b/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php index 61d13f596..26ae1b511 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php @@ -939,7 +939,7 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase * @expectedException Doctrine\ORM\Mapping\MappingException * @expectedExceptionMessage Invalid field override named 'invalidPropertyName' for class 'Doctrine\Tests\Models\DDC964\DDC964Admin */ - public function testInvalidPropertyOverrideNameException() + public function testInvalidPropertyAssociationOverrideNameException() { $cm = new ClassMetadata('Doctrine\Tests\Models\DDC964\DDC964Admin'); $cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService); @@ -947,6 +947,34 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase $cm->setAssociationOverride('invalidPropertyName', array()); } + + /** + * @group DDC-964 + * @expectedException Doctrine\ORM\Mapping\MappingException + * @expectedExceptionMessage Invalid field override named 'invalidPropertyName' for class 'Doctrine\Tests\Models\DDC964\DDC964Guest'. + */ + public function testInvalidPropertyAttributeOverrideNameException() + { + $cm = new ClassMetadata('Doctrine\Tests\Models\DDC964\DDC964Guest'); + $cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService); + $cm->mapField(array('fieldName' => 'name')); + + $cm->setAttributeOverride('invalidPropertyName', array()); + } + + /** + * @group DDC-964 + * @expectedException Doctrine\ORM\Mapping\MappingException + * @expectedExceptionMessage The column type of attribute 'name' on class 'Doctrine\Tests\Models\DDC964\DDC964Guest' could not be changed. + */ + public function testInvalidOverrideAttributeFieldTypeException() + { + $cm = new ClassMetadata('Doctrine\Tests\Models\DDC964\DDC964Guest'); + $cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService); + $cm->mapField(array('fieldName' => 'name', 'type'=>'string')); + + $cm->setAttributeOverride('name', array('type'=>'date')); + } } class MyNamespacedNamingStrategy extends \Doctrine\ORM\Mapping\DefaultNamingStrategy diff --git a/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC964.DDC964Guest.php b/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC964.DDC964Guest.php index b3d9bbc7f..5094ecddf 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC964.DDC964Guest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC964.DDC964Guest.php @@ -1 +1,13 @@ setAttributeOverride('id', array( + 'columnName' => 'guest_id', + 'type' => 'integer', + 'length' => 140, +)); + +$metadata->setAttributeOverride('name',array( + 'columnName' => 'guest_name', + 'nullable' => false, + 'unique' => true, + 'length' => 240, +)); \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC964.DDC964User.php b/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC964.DDC964User.php index a3463d6f9..7b66deef0 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC964.DDC964User.php +++ b/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC964.DDC964User.php @@ -6,11 +6,16 @@ $metadata->mapField(array( 'id' => true, 'fieldName' => 'id', 'type' => 'integer', - 'columnName' => 'id', + 'columnName' => 'user_id', + 'length' => 150, )); $metadata->mapField(array( - 'fieldName' => 'name', - 'type' => 'string', + 'fieldName' => 'name', + 'type' => 'string', + 'columnName'=> 'user_name', + 'nullable' => true, + 'unique' => false, + 'length' => 250, )); $metadata->mapManyToOne(array( diff --git a/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.DDC964.DDC964Guest.dcm.xml b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.DDC964.DDC964Guest.dcm.xml index d3d61d81e..561066f6b 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.DDC964.DDC964Guest.dcm.xml +++ b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.DDC964.DDC964Guest.dcm.xml @@ -5,6 +5,14 @@ http://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd"> + + + + + + + + \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.DDC964.DDC964User.dcm.xml b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.DDC964.DDC964User.dcm.xml index 47333f18e..68db74b48 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.DDC964.DDC964User.dcm.xml +++ b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.DDC964.DDC964User.dcm.xml @@ -5,11 +5,11 @@ http://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd"> - + - + diff --git a/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC964.DDC964Guest.dcm.yml b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC964.DDC964Guest.dcm.yml index 7bfae82ff..ec7936f4a 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC964.DDC964Guest.dcm.yml +++ b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC964.DDC964Guest.dcm.yml @@ -1,2 +1,13 @@ Doctrine\Tests\Models\DDC964\DDC964Guest: - type: entity \ No newline at end of file + type: entity + attributeOverride: + id: + column: guest_id + type: integer + length: 140 + name: + column: guest_name + type: string + length: 240 + nullable: false + unique: true \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC964.DDC964User.dcm.yml b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC964.DDC964User.dcm.yml index 5f91b4053..3a9ebbf9d 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC964.DDC964User.dcm.yml +++ b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC964.DDC964User.dcm.yml @@ -3,12 +3,17 @@ Doctrine\Tests\Models\DDC964\DDC964User: id: id: type: integer - unsigned: true + column: user_id + length: 150 generator: strategy: AUTO fields: name: type: string + column: user_name + length: 250 + nullable: true + unique: false manyToOne: address: targetEntity: DDC964Address