diff --git a/doctrine-mapping.xsd b/doctrine-mapping.xsd index 80da4eb93..e8b2e26b2 100644 --- a/doctrine-mapping.xsd +++ b/doctrine-mapping.xsd @@ -145,6 +145,8 @@ + + @@ -483,4 +485,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/Doctrine/ORM/Mapping/AssociationOverride.php b/lib/Doctrine/ORM/Mapping/AssociationOverride.php new file mode 100644 index 000000000..f9f7f4912 --- /dev/null +++ b/lib/Doctrine/ORM/Mapping/AssociationOverride.php @@ -0,0 +1,56 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * This annotation is used to override association mapping of property for an entity relationship. + * + * @author Fabio B. Silva + * @since 2.3 + * + * @Annotation + * @Target("ANNOTATION") + */ +final class AssociationOverride implements Annotation +{ + + /** + * The name of the relationship property whose mapping is being overridden + * + * @var string + */ + public $name; + + /** + * The join column that is being mapped to the persistent attribute. + * + * @var array<\Doctrine\ORM\Mapping\JoinColumn> + */ + public $joinColumns; + + + /** + * The join table that maps the relationship. + * + * @var \Doctrine\ORM\Mapping\JoinTable + */ + public $joinTable; + +} diff --git a/lib/Doctrine/ORM/Mapping/AssociationOverrides.php b/lib/Doctrine/ORM/Mapping/AssociationOverrides.php new file mode 100644 index 000000000..b46f47f85 --- /dev/null +++ b/lib/Doctrine/ORM/Mapping/AssociationOverrides.php @@ -0,0 +1,41 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * This annotation is used to override association mappings of relationship properties. + * + * @author Fabio B. Silva + * @since 2.3 + * + * @Annotation + * @Target("CLASS") + */ +final class AssociationOverrides implements Annotation +{ + + /** + * Mapping overrides of relationship properties + * + * @var array<\Doctrine\ORM\Mapping\AssociationOverride> + */ + public $value; + +} \ No newline at end of file diff --git a/lib/Doctrine/ORM/Mapping/AttributeOverride.php b/lib/Doctrine/ORM/Mapping/AttributeOverride.php new file mode 100644 index 000000000..629509c5d --- /dev/null +++ b/lib/Doctrine/ORM/Mapping/AttributeOverride.php @@ -0,0 +1,47 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * This annotation is used to override the mapping of a entity property. + * + * @author Fabio B. Silva + * @since 2.3 + * + * @Annotation + * @Target("ANNOTATION") + */ +final class AttributeOverride implements Annotation +{ + + /** + * The name of the property whose mapping is being overridden. + * + * @var string + */ + public $name; + + /** + * The column definition. + * + * @var \Doctrine\ORM\Mapping\Column + */ + public $column; +} \ No newline at end of file diff --git a/lib/Doctrine/ORM/Mapping/AttributeOverrides.php b/lib/Doctrine/ORM/Mapping/AttributeOverrides.php new file mode 100644 index 000000000..3693b22e7 --- /dev/null +++ b/lib/Doctrine/ORM/Mapping/AttributeOverrides.php @@ -0,0 +1,41 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * This annotation is used to override the mapping of a entity property. + * + * @author Fabio B. Silva + * @since 2.3 + * + * @Annotation + * @Target("CLASS") + */ +final class AttributeOverrides implements Annotation +{ + + /** + * One or more field or property mapping overrides. + * + * @var array<\Doctrine\ORM\Mapping\AttributeOverride> + */ + public $value; + +} \ No newline at end of file diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php index 1f3b00dbf..81acb8021 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php @@ -571,7 +571,7 @@ class ClassMetadataInfo implements ClassMetadata /** * NamingStrategy determining the default column and table names * - * @var \Doctrine\ORM\NamingStrategy + * @var \Doctrine\ORM\Mapping\NamingStrategy */ protected $namingStrategy; @@ -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( @@ -1802,6 +1802,90 @@ class ClassMetadataInfo implements ClassMetadata $this->inheritanceType = $type; } + /** + * Sets the association to override association mapping of property for an entity relationship. + * + * @param string $fieldName + * @param array $overrideMapping + */ + public function setAssociationOverride($fieldName, array $overrideMapping) + { + if ( ! isset($this->associationMappings[$fieldName])) { + throw MappingException::invalidOverrideFieldName($this->name, $fieldName); + } + + $mapping = $this->associationMappings[$fieldName]; + + if (isset($overrideMapping['joinColumns'])) { + $mapping['joinColumns'] = $overrideMapping['joinColumns']; + } + + if (isset($overrideMapping['joinTable'])) { + $mapping['joinTable'] = $overrideMapping['joinTable']; + } + + $mapping['joinColumnFieldNames'] = null; + $mapping['joinTableColumns'] = null; + $mapping['sourceToTargetKeyColumns'] = null; + $mapping['relationToSourceKeyColumns'] = null; + $mapping['relationToTargetKeyColumns'] = null; + + switch ($mapping['type']) { + case self::ONE_TO_ONE: + $mapping = $this->_validateAndCompleteOneToOneMapping($mapping); + break; + case self::ONE_TO_MANY: + $mapping = $this->_validateAndCompleteOneToManyMapping($mapping); + break; + case self::MANY_TO_ONE: + $mapping = $this->_validateAndCompleteOneToOneMapping($mapping); + break; + case self::MANY_TO_MANY: + $mapping = $this->_validateAndCompleteManyToManyMapping($mapping); + break; + } + + $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. * @@ -2362,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']; @@ -2376,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 6a89131fe..9885c43ec 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php @@ -23,7 +23,9 @@ use Doctrine\Common\Cache\ArrayCache, Doctrine\Common\Annotations\AnnotationReader, Doctrine\Common\Annotations\AnnotationRegistry, Doctrine\ORM\Mapping\ClassMetadataInfo, - Doctrine\ORM\Mapping\MappingException; + Doctrine\ORM\Mapping\MappingException, + Doctrine\ORM\Mapping\JoinColumn, + Doctrine\ORM\Mapping\Column; /** * The AnnotationDriver reads the mapping metadata from docblock annotations. @@ -134,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); @@ -260,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( @@ -275,6 +277,43 @@ class AnnotationDriver implements Driver } } + $associationOverrides = array(); + // Evaluate AssociationOverrides annotation + if (isset($classAnnotations['Doctrine\ORM\Mapping\AssociationOverrides'])) { + $associationOverridesAnnot = $classAnnotations['Doctrine\ORM\Mapping\AssociationOverrides']; + + foreach ($associationOverridesAnnot->value as $associationOverride) { + // Check for JoinColummn/JoinColumns annotations + if ($associationOverride->joinColumns) { + $joinColumns = array(); + foreach ($associationOverride->joinColumns as $joinColumn) { + $joinColumns[] = $this->joinColumnToArray($joinColumn); + } + $associationOverrides[$associationOverride->name]['joinColumns'] = $joinColumns; + } + + // Check for JoinTable annotations + if ($associationOverride->joinTable) { + $joinTable = null; + $joinTableAnnot = $associationOverride->joinTable; + $joinTable = array( + 'name' => $joinTableAnnot->name, + 'schema' => $joinTableAnnot->schema + ); + + foreach ($joinTableAnnot->joinColumns as $joinColumn) { + $joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumn); + } + + foreach ($joinTableAnnot->inverseJoinColumns as $joinColumn) { + $joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumn); + } + + $associationOverrides[$associationOverride->name]['joinTable'] = $joinTable; + } + } + } + // Evaluate InheritanceType annotation if (isset($classAnnotations['Doctrine\ORM\Mapping\InheritanceType'])) { $inheritanceTypeAnnot = $classAnnotations['Doctrine\ORM\Mapping\InheritanceType']; @@ -326,24 +365,10 @@ class AnnotationDriver implements Driver $joinColumns = array(); if ($joinColumnAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\JoinColumn')) { - $joinColumns[] = array( - 'name' => $joinColumnAnnot->name, - 'referencedColumnName' => $joinColumnAnnot->referencedColumnName, - 'unique' => $joinColumnAnnot->unique, - 'nullable' => $joinColumnAnnot->nullable, - 'onDelete' => $joinColumnAnnot->onDelete, - 'columnDefinition' => $joinColumnAnnot->columnDefinition, - ); + $joinColumns[] = $this->joinColumnToArray($joinColumnAnnot); } else if ($joinColumnsAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\JoinColumns')) { foreach ($joinColumnsAnnot->value as $joinColumn) { - $joinColumns[] = array( - 'name' => $joinColumn->name, - 'referencedColumnName' => $joinColumn->referencedColumnName, - 'unique' => $joinColumn->unique, - 'nullable' => $joinColumn->nullable, - 'onDelete' => $joinColumn->onDelete, - 'columnDefinition' => $joinColumn->columnDefinition, - ); + $joinColumns[] = $this->joinColumnToArray($joinColumn); } } @@ -354,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; @@ -447,25 +456,11 @@ class AnnotationDriver implements Driver ); foreach ($joinTableAnnot->joinColumns as $joinColumn) { - $joinTable['joinColumns'][] = array( - 'name' => $joinColumn->name, - 'referencedColumnName' => $joinColumn->referencedColumnName, - 'unique' => $joinColumn->unique, - 'nullable' => $joinColumn->nullable, - 'onDelete' => $joinColumn->onDelete, - 'columnDefinition' => $joinColumn->columnDefinition, - ); + $joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumn); } foreach ($joinTableAnnot->inverseJoinColumns as $joinColumn) { - $joinTable['inverseJoinColumns'][] = array( - 'name' => $joinColumn->name, - 'referencedColumnName' => $joinColumn->referencedColumnName, - 'unique' => $joinColumn->unique, - 'nullable' => $joinColumn->nullable, - 'onDelete' => $joinColumn->onDelete, - 'columnDefinition' => $joinColumn->columnDefinition, - ); + $joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumn); } } @@ -486,6 +481,57 @@ class AnnotationDriver implements Driver } } + // Evaluate AssociationOverrides annotation + if (isset($classAnnotations['Doctrine\ORM\Mapping\AssociationOverrides'])) { + $associationOverridesAnnot = $classAnnotations['Doctrine\ORM\Mapping\AssociationOverrides']; + + foreach ($associationOverridesAnnot->value as $associationOverride) { + $override = array(); + $fieldName = $associationOverride->name; + + // Check for JoinColummn/JoinColumns annotations + if ($associationOverride->joinColumns) { + $joinColumns = array(); + foreach ($associationOverride->joinColumns as $joinColumn) { + $joinColumns[] = $this->joinColumnToArray($joinColumn); + } + $override['joinColumns'] = $joinColumns; + } + + // Check for JoinTable annotations + if ($associationOverride->joinTable) { + $joinTable = null; + $joinTableAnnot = $associationOverride->joinTable; + $joinTable = array( + 'name' => $joinTableAnnot->name, + 'schema' => $joinTableAnnot->schema + ); + + foreach ($joinTableAnnot->joinColumns as $joinColumn) { + $joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumn); + } + + foreach ($joinTableAnnot->inverseJoinColumns as $joinColumn) { + $joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumn); + } + + $override['joinTable'] = $joinTable; + } + + $metadata->setAssociationOverride($fieldName, $override); + } + } + + $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) { @@ -574,7 +620,7 @@ class AnnotationDriver implements Driver return $this->_classNames; } - if (!$this->_paths) { + if ( ! $this->_paths) { throw MappingException::pathRequired(); } @@ -629,12 +675,65 @@ 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); } return constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $fetchMode); } + + /** + * Parse the given JoinColumn as array + * + * @param JoinColumn $joinColumn + * @return array + */ + private function joinColumnToArray(JoinColumn $joinColumn) + { + return array( + 'name' => $joinColumn->name, + 'unique' => $joinColumn->unique, + 'nullable' => $joinColumn->nullable, + 'onDelete' => $joinColumn->onDelete, + 'columnDefinition' => $joinColumn->columnDefinition, + 'referencedColumnName' => $joinColumn->referencedColumnName, + ); + } + + /** + * 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 46fa1551b..a9ad6f7bd 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php +++ b/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php @@ -59,4 +59,8 @@ require_once __DIR__.'/../EntityResult.php'; require_once __DIR__.'/../NamedNativeQuery.php'; require_once __DIR__.'/../NamedNativeQueries.php'; require_once __DIR__.'/../SqlResultSetMapping.php'; -require_once __DIR__.'/../SqlResultSetMappings.php'; \ No newline at end of file +require_once __DIR__.'/../SqlResultSetMappings.php'; +require_once __DIR__.'/../AssociationOverride.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 005dc44d1..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']; } @@ -353,10 +314,10 @@ class XmlDriver extends AbstractFileDriver $joinColumns = array(); if (isset($oneToOneElement->{'join-column'})) { - $joinColumns[] = $this->_getJoinColumnMapping($oneToOneElement->{'join-column'}); + $joinColumns[] = $this->joinColumnToArray($oneToOneElement->{'join-column'}); } else if (isset($oneToOneElement->{'join-columns'})) { foreach ($oneToOneElement->{'join-columns'}->{'join-column'} as $joinColumnElement) { - $joinColumns[] = $this->_getJoinColumnMapping($joinColumnElement); + $joinColumns[] = $this->joinColumnToArray($joinColumnElement); } } @@ -437,10 +398,10 @@ class XmlDriver extends AbstractFileDriver $joinColumns = array(); if (isset($manyToOneElement->{'join-column'})) { - $joinColumns[] = $this->_getJoinColumnMapping($manyToOneElement->{'join-column'}); + $joinColumns[] = $this->joinColumnToArray($manyToOneElement->{'join-column'}); } else if (isset($manyToOneElement->{'join-columns'})) { foreach ($manyToOneElement->{'join-columns'}->{'join-column'} as $joinColumnElement) { - $joinColumns[] = $this->_getJoinColumnMapping($joinColumnElement); + $joinColumns[] = $this->joinColumnToArray($joinColumnElement); } } @@ -487,11 +448,11 @@ class XmlDriver extends AbstractFileDriver } foreach ($joinTableElement->{'join-columns'}->{'join-column'} as $joinColumnElement) { - $joinTable['joinColumns'][] = $this->_getJoinColumnMapping($joinColumnElement); + $joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumnElement); } foreach ($joinTableElement->{'inverse-join-columns'}->{'join-column'} as $joinColumnElement) { - $joinTable['inverseJoinColumns'][] = $this->_getJoinColumnMapping($joinColumnElement); + $joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumnElement); } $mapping['joinTable'] = $joinTable; @@ -519,6 +480,62 @@ 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 $overrideElement) { + $fieldName = (string) $overrideElement['name']; + $override = array(); + + // Check for join-columns + if (isset($overrideElement->{'join-columns'})) { + $joinColumns = array(); + foreach ($overrideElement->{'join-columns'}->{'join-column'} as $joinColumnElement) { + $joinColumns[] = $this->joinColumnToArray($joinColumnElement); + } + $override['joinColumns'] = $joinColumns; + } + + // Check for join-table + if ($overrideElement->{'join-table'}) { + $joinTable = null; + $joinTableElement = $overrideElement->{'join-table'}; + + $joinTable = array( + 'name' => (string) $joinTableElement['name'], + 'schema' => (string) $joinTableElement['schema'] + ); + + if (isset($joinTableElement->{'join-columns'})) { + foreach ($joinTableElement->{'join-columns'}->{'join-column'} as $joinColumnElement) { + $joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumnElement); + } + } + + if (isset($joinTableElement->{'inverse-join-columns'})) { + foreach ($joinTableElement->{'inverse-join-columns'}->{'join-column'} as $joinColumnElement) { + $joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumnElement); + } + } + + $override['joinTable'] = $joinTable; + } + + $metadata->setAssociationOverride($fieldName, $override); + } + } + // Evaluate if (isset($xmlRoot->{'lifecycle-callbacks'})) { foreach ($xmlRoot->{'lifecycle-callbacks'}->{'lifecycle-callback'} as $lifecycleCallback) { @@ -563,7 +580,7 @@ class XmlDriver extends AbstractFileDriver * @param $joinColumnElement The XML element. * @return array The mapping array. */ - private function _getJoinColumnMapping(SimpleXMLElement $joinColumnElement) + private function joinColumnToArray(SimpleXMLElement $joinColumnElement) { $joinColumn = array( 'name' => (string)$joinColumnElement['name'], @@ -589,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 d602ed975..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); } @@ -356,14 +317,14 @@ class YamlDriver extends AbstractFileDriver $joinColumns = array(); if (isset($oneToOneElement['joinColumn'])) { - $joinColumns[] = $this->_getJoinColumnMapping($oneToOneElement['joinColumn']); + $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; } - $joinColumns[] = $this->_getJoinColumnMapping($joinColumnElement); + $joinColumns[] = $this->joinColumnToArray($joinColumnElement); } } @@ -438,14 +399,14 @@ class YamlDriver extends AbstractFileDriver $joinColumns = array(); if (isset($manyToOneElement['joinColumn'])) { - $joinColumns[] = $this->_getJoinColumnMapping($manyToOneElement['joinColumn']); + $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; } - $joinColumns[] = $this->_getJoinColumnMapping($joinColumnElement); + $joinColumns[] = $this->joinColumnToArray($joinColumnElement); } } @@ -485,19 +446,19 @@ class YamlDriver extends AbstractFileDriver } foreach ($joinTableElement['joinColumns'] as $name => $joinColumnElement) { - if (!isset($joinColumnElement['name'])) { + if ( ! isset($joinColumnElement['name'])) { $joinColumnElement['name'] = $name; } - $joinTable['joinColumns'][] = $this->_getJoinColumnMapping($joinColumnElement); + $joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumnElement); } foreach ($joinTableElement['inverseJoinColumns'] as $name => $joinColumnElement) { - if (!isset($joinColumnElement['name'])) { + if ( ! isset($joinColumnElement['name'])) { $joinColumnElement['name'] = $name; } - $joinTable['inverseJoinColumns'][] = $this->_getJoinColumnMapping($joinColumnElement); + $joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumnElement); } $mapping['joinTable'] = $joinTable; @@ -527,6 +488,68 @@ class YamlDriver extends AbstractFileDriver } } + // Evaluate associationOverride + if (isset($element['associationOverride']) && is_array($element['associationOverride'])) { + + foreach ($element['associationOverride'] as $fieldName => $associationOverrideElement) { + $override = array(); + + // Check for joinColumn + if (isset($associationOverrideElement['joinColumn'])) { + $joinColumns = array(); + foreach ($associationOverrideElement['joinColumn'] as $name => $joinColumnElement) { + if ( ! isset($joinColumnElement['name'])) { + $joinColumnElement['name'] = $name; + } + $joinColumns[] = $this->joinColumnToArray($joinColumnElement); + } + $override['joinColumns'] = $joinColumns; + } + + // Check for joinTable + if (isset($associationOverrideElement['joinTable'])) { + + $joinTableElement = $associationOverrideElement['joinTable']; + $joinTable = array( + 'name' => $joinTableElement['name'] + ); + + if (isset($joinTableElement['schema'])) { + $joinTable['schema'] = $joinTableElement['schema']; + } + + foreach ($joinTableElement['joinColumns'] as $name => $joinColumnElement) { + if ( ! isset($joinColumnElement['name'])) { + $joinColumnElement['name'] = $name; + } + + $joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumnElement); + } + + foreach ($joinTableElement['inverseJoinColumns'] as $name => $joinColumnElement) { + if ( ! isset($joinColumnElement['name'])) { + $joinColumnElement['name'] = $name; + } + + $joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumnElement); + } + + $override['joinTable'] = $joinTable; + } + + $metadata->setAssociationOverride($fieldName, $override); + } + } + + // 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) { @@ -544,7 +567,7 @@ class YamlDriver extends AbstractFileDriver * @param $joinColumnElement The array join column element * @return array The mapping array. */ - private function _getJoinColumnMapping($joinColumnElement) + private function joinColumnToArray($joinColumnElement) { $joinColumn = array(); if (isset($joinColumnElement['referencedColumnName'])) { @@ -578,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/JoinTable.php b/lib/Doctrine/ORM/Mapping/JoinTable.php index 9ff9d4511..31625ed8f 100644 --- a/lib/Doctrine/ORM/Mapping/JoinTable.php +++ b/lib/Doctrine/ORM/Mapping/JoinTable.php @@ -21,7 +21,7 @@ namespace Doctrine\ORM\Mapping; /** * @Annotation - * @Target("PROPERTY") + * @Target({"PROPERTY","ANNOTATION"}) */ final class JoinTable implements Annotation { diff --git a/lib/Doctrine/ORM/Mapping/MappingException.php b/lib/Doctrine/ORM/Mapping/MappingException.php index 5c3ed8ef2..dcf3a7da8 100644 --- a/lib/Doctrine/ORM/Mapping/MappingException.php +++ b/lib/Doctrine/ORM/Mapping/MappingException.php @@ -83,6 +83,28 @@ class MappingException extends \Doctrine\ORM\ORMException return new self("Invalid mapping file '$fileName' for class '$entityName'."); } + /** + * Exception for invalid property name override. + * + * @param string $className The entity's name + * @param string $fieldName + */ + public static function invalidOverrideFieldName($className, $fieldName) + { + 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/DDC964Address.php b/tests/Doctrine/Tests/Models/DDC964/DDC964Address.php new file mode 100644 index 000000000..f5edaf857 --- /dev/null +++ b/tests/Doctrine/Tests/Models/DDC964/DDC964Address.php @@ -0,0 +1,123 @@ +zip = $zip; + $this->country = $country; + $this->city = $city; + $this->street = $street; + } + + /** + * @return integer + */ + public function getId() + { + return $this->id; + } + + /** + * @return string + */ + public function getCountry() + { + return $this->country; + } + + /** + * @param string $country + */ + public function setCountry($country) + { + $this->country = $country; + } + + /** + * @return string + */ + public function getZip() + { + return $this->zip; + } + + /** + * @param string $zip + */ + public function setZip($zip) + { + $this->zip = $zip; + } + + /** + * @return string + */ + public function getCity() + { + return $this->city; + } + + /** + * @param string $city + */ + public function setCity($city) + { + $this->city = $city; + } + + /** + * @return string + */ + public function getStreet() + { + return $this->street; + } + + /** + * @param string $street + */ + public function setStreet($street) + { + $this->street = $street; + } + +} \ No newline at end of file diff --git a/tests/Doctrine/Tests/Models/DDC964/DDC964Admin.php b/tests/Doctrine/Tests/Models/DDC964/DDC964Admin.php new file mode 100644 index 000000000..e22b973de --- /dev/null +++ b/tests/Doctrine/Tests/Models/DDC964/DDC964Admin.php @@ -0,0 +1,47 @@ +setAssociationOverride('address',array( + 'joinColumns'=>array(array( + 'name' => 'adminaddress_id', + 'referencedColumnName' => 'id', + )) + )); + + $metadata->setAssociationOverride('groups',array( + 'joinTable' => array( + 'name' => 'ddc964_users_admingroups', + 'joinColumns' => array(array( + 'name' => 'adminuser_id', + )), + 'inverseJoinColumns' =>array (array ( + 'name' => 'admingroup_id', + )) + ) + )); + } +} \ No newline at end of file diff --git a/tests/Doctrine/Tests/Models/DDC964/DDC964Group.php b/tests/Doctrine/Tests/Models/DDC964/DDC964Group.php new file mode 100644 index 000000000..526040997 --- /dev/null +++ b/tests/Doctrine/Tests/Models/DDC964/DDC964Group.php @@ -0,0 +1,70 @@ +name = $name; + $this->users = new ArrayCollection(); + } + + /** + * @param string $name + */ + public function setName($name) + { + $this->name = $name; + } + + /** + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * @param DDC964User $user + */ + public function addUser(DDC964User $user) + { + $this->users[] = $user; + } + + /** + * @return ArrayCollection + */ + public function getUsers() + { + return $this->users; + } + +} + diff --git a/tests/Doctrine/Tests/Models/DDC964/DDC964Guest.php b/tests/Doctrine/Tests/Models/DDC964/DDC964Guest.php new file mode 100644 index 000000000..90501187c --- /dev/null +++ b/tests/Doctrine/Tests/Models/DDC964/DDC964Guest.php @@ -0,0 +1,44 @@ +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 new file mode 100644 index 000000000..608f42d96 --- /dev/null +++ b/tests/Doctrine/Tests/Models/DDC964/DDC964User.php @@ -0,0 +1,155 @@ +name = $name; + $this->groups = new ArrayCollection; + } + + /** + * @return integer + */ + public function getId() + { + return $this->id; + } + + /** + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * @param string $name + */ + public function setName($name) + { + $this->name = $name; + } + + /** + * @param DDC964Group $group + */ + public function addGroup(DDC964Group $group) + { + $this->groups->add($group); + $group->addUser($this); + } + + /** + * @return ArrayCollection + */ + public function getGroups() + { + return $this->groups; + } + + /** + * @return DDC964Address + */ + public function getAddress() + { + return $this->address; + } + + /** + * @param DDC964Address $address + */ + public function setAddress(DDC964Address $address) + { + $this->address = $address; + } + + public static function loadMetadata($metadata) + { + $metadata->mapField(array( + 'id' => true, + 'fieldName' => 'id', + 'type' => 'integer', + 'columnName' => 'user_id', + 'length' => 150, + )); + $metadata->mapField(array( + 'fieldName' => 'name', + 'type' => 'string', + 'columnName'=> 'user_name', + 'nullable' => true, + 'unique' => false, + 'length' => 250, + )); + + $metadata->mapManyToOne(array( + 'fieldName' => 'address', + 'targetEntity' => 'DDC964Address', + 'cascade' => array('persist','merge'), + 'joinColumn' => array('name'=>'address_id', 'referencedColumnMame'=>'id'), + )); + + $metadata->mapManyToMany(array( + 'fieldName' => 'groups', + 'targetEntity' => 'DDC964Group', + 'inversedBy' => 'users', + 'cascade' => array('persist','merge','detach'), + 'joinTable' => array( + 'name' => 'ddc964_users_groups', + 'joinColumns' => array(array( + 'name'=>'user_id', + 'referencedColumnName'=>'id', + )), + 'inverseJoinColumns'=>array(array( + 'name'=>'group_id', + 'referencedColumnName'=>'id', + )) + ) + )); + + $metadata->setIdGeneratorType(\Doctrine\ORM\Mapping\ClassMetadataInfo::GENERATOR_TYPE_AUTO); + } +} \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php b/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php index 0efb3aca2..7d44417c3 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php @@ -376,11 +376,8 @@ abstract class AbstractMappingDriverTest extends \Doctrine\Tests\OrmTestCase */ public function testDefaultFieldType() { - $em = $this->_getTestEntityManager(); - $factory = $this->createClassMetadataFactory($em); - - - $class = $factory->getMetadataFor('Doctrine\Tests\Models\DDC1476\DDC1476EntityWithDefaultFieldType'); + $factory = $this->createClassMetadataFactory(); + $class = $factory->getMetadataFor('Doctrine\Tests\Models\DDC1476\DDC1476EntityWithDefaultFieldType'); $this->assertArrayHasKey('id', $class->fieldMappings); @@ -417,7 +414,6 @@ abstract class AbstractMappingDriverTest extends \Doctrine\Tests\OrmTestCase */ public function testIdentifierColumnDefinition() { - $class = $this->createClassMetadata(__NAMESPACE__ . '\DDC1170Entity'); @@ -467,7 +463,6 @@ abstract class AbstractMappingDriverTest extends \Doctrine\Tests\OrmTestCase } /** -<<<<<<< HEAD * @group DDC-889 * @expectedException Doctrine\ORM\Mapping\MappingException * @expectedExceptionMessage Class "Doctrine\Tests\Models\DDC889\DDC889Class" sub class of "Doctrine\Tests\Models\DDC889\DDC889SuperClass" is not a valid entity or mapped super class. @@ -613,6 +608,129 @@ abstract class AbstractMappingDriverTest extends \Doctrine\Tests\OrmTestCase $this->assertEquals(array('name'=>'name','column'=>'name'), $mapping['entities'][0]['fields'][1]); $this->assertEquals($personMetadata->name, $mapping['entities'][0]['entityClass']); } + + /* + * @group DDC-964 + */ + public function testAssociationOverridesMapping() + { + + $factory = $this->createClassMetadataFactory(); + $adminMetadata = $factory->getMetadataFor('Doctrine\Tests\Models\DDC964\DDC964Admin'); + $guestMetadata = $factory->getMetadataFor('Doctrine\Tests\Models\DDC964\DDC964Guest'); + + + // assert groups association mappings + $this->assertArrayHasKey('groups', $guestMetadata->associationMappings); + $this->assertArrayHasKey('groups', $adminMetadata->associationMappings); + + $guestGroups = $guestMetadata->associationMappings['groups']; + $adminGroups = $adminMetadata->associationMappings['groups']; + + // assert not override attributes + $this->assertEquals($guestGroups['fieldName'], $adminGroups['fieldName']); + $this->assertEquals($guestGroups['type'], $adminGroups['type']); + $this->assertEquals($guestGroups['mappedBy'], $adminGroups['mappedBy']); + $this->assertEquals($guestGroups['inversedBy'], $adminGroups['inversedBy']); + $this->assertEquals($guestGroups['isOwningSide'], $adminGroups['isOwningSide']); + $this->assertEquals($guestGroups['fetch'], $adminGroups['fetch']); + $this->assertEquals($guestGroups['isCascadeRemove'], $adminGroups['isCascadeRemove']); + $this->assertEquals($guestGroups['isCascadePersist'], $adminGroups['isCascadePersist']); + $this->assertEquals($guestGroups['isCascadeRefresh'], $adminGroups['isCascadeRefresh']); + $this->assertEquals($guestGroups['isCascadeMerge'], $adminGroups['isCascadeMerge']); + $this->assertEquals($guestGroups['isCascadeDetach'], $adminGroups['isCascadeDetach']); + + // assert not override attributes + $this->assertEquals('ddc964_users_groups', $guestGroups['joinTable']['name']); + $this->assertEquals('user_id', $guestGroups['joinTable']['joinColumns'][0]['name']); + $this->assertEquals('group_id', $guestGroups['joinTable']['inverseJoinColumns'][0]['name']); + + $this->assertEquals(array('user_id'=>'id'), $guestGroups['relationToSourceKeyColumns']); + $this->assertEquals(array('group_id'=>'id'), $guestGroups['relationToTargetKeyColumns']); + $this->assertEquals(array('user_id','group_id'), $guestGroups['joinTableColumns']); + + + $this->assertEquals('ddc964_users_admingroups', $adminGroups['joinTable']['name']); + $this->assertEquals('adminuser_id', $adminGroups['joinTable']['joinColumns'][0]['name']); + $this->assertEquals('admingroup_id', $adminGroups['joinTable']['inverseJoinColumns'][0]['name']); + + $this->assertEquals(array('adminuser_id'=>'id'), $adminGroups['relationToSourceKeyColumns']); + $this->assertEquals(array('admingroup_id'=>'id'), $adminGroups['relationToTargetKeyColumns']); + $this->assertEquals(array('adminuser_id','admingroup_id'), $adminGroups['joinTableColumns']); + + + // assert address association mappings + $this->assertArrayHasKey('address', $guestMetadata->associationMappings); + $this->assertArrayHasKey('address', $adminMetadata->associationMappings); + + $guestAddress = $guestMetadata->associationMappings['address']; + $adminAddress = $adminMetadata->associationMappings['address']; + + // assert not override attributes + $this->assertEquals($guestAddress['fieldName'], $adminAddress['fieldName']); + $this->assertEquals($guestAddress['type'], $adminAddress['type']); + $this->assertEquals($guestAddress['mappedBy'], $adminAddress['mappedBy']); + $this->assertEquals($guestAddress['inversedBy'], $adminAddress['inversedBy']); + $this->assertEquals($guestAddress['isOwningSide'], $adminAddress['isOwningSide']); + $this->assertEquals($guestAddress['fetch'], $adminAddress['fetch']); + $this->assertEquals($guestAddress['isCascadeRemove'], $adminAddress['isCascadeRemove']); + $this->assertEquals($guestAddress['isCascadePersist'], $adminAddress['isCascadePersist']); + $this->assertEquals($guestAddress['isCascadeRefresh'], $adminAddress['isCascadeRefresh']); + $this->assertEquals($guestAddress['isCascadeMerge'], $adminAddress['isCascadeMerge']); + $this->assertEquals($guestAddress['isCascadeDetach'], $adminAddress['isCascadeDetach']); + + // assert override + $this->assertEquals('address_id', $guestAddress['joinColumns'][0]['name']); + $this->assertEquals(array('address_id'=>'id'), $guestAddress['sourceToTargetKeyColumns']); + $this->assertEquals(array('address_id'=>'address_id'), $guestAddress['joinColumnFieldNames']); + $this->assertEquals(array('id'=>'address_id'), $guestAddress['targetToSourceKeyColumns']); + + + $this->assertEquals('adminaddress_id', $adminAddress['joinColumns'][0]['name']); + $this->assertEquals(array('adminaddress_id'=>'id'), $adminAddress['sourceToTargetKeyColumns']); + $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 fd6afe960..26ae1b511 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php @@ -932,6 +932,48 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase $this->setExpectedException("Doctrine\ORM\Mapping\MappingException", "Invalid cascade option(s) specified: 'invalid'. Only 'remove', 'persist', 'refresh', 'merge' and 'detach' are allowed."); $cm->mapManyToOne(array('fieldName' => 'address', 'targetEntity' => 'UnknownClass', 'cascade' => array('invalid'))); + } + + /** + * @group DDC-964 + * @expectedException Doctrine\ORM\Mapping\MappingException + * @expectedExceptionMessage Invalid field override named 'invalidPropertyName' for class 'Doctrine\Tests\Models\DDC964\DDC964Admin + */ + public function testInvalidPropertyAssociationOverrideNameException() + { + $cm = new ClassMetadata('Doctrine\Tests\Models\DDC964\DDC964Admin'); + $cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService); + $cm->mapManyToOne(array('fieldName' => 'address', 'targetEntity' => 'DDC964Address')); + + $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')); } } diff --git a/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC964.DDC964Admin.php b/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC964.DDC964Admin.php new file mode 100644 index 000000000..bb8920e76 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC964.DDC964Admin.php @@ -0,0 +1,21 @@ +setAssociationOverride('address',array( + 'joinColumns'=>array(array( + 'name' => 'adminaddress_id', + 'referencedColumnName' => 'id', + )) +)); + +$metadata->setAssociationOverride('groups',array( + 'joinTable' => array ( + 'name' => 'ddc964_users_admingroups', + 'joinColumns' => array(array( + 'name' => 'adminuser_id', + )), + + 'inverseJoinColumns' =>array (array ( + 'name' => 'admingroup_id', + )) + ) +)); \ No newline at end of file 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 new file mode 100644 index 000000000..5094ecddf --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC964.DDC964Guest.php @@ -0,0 +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 new file mode 100644 index 000000000..7b66deef0 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.Models.DDC964.DDC964User.php @@ -0,0 +1,46 @@ +mapField(array( + 'id' => true, + 'fieldName' => 'id', + 'type' => 'integer', + 'columnName' => 'user_id', + 'length' => 150, +)); +$metadata->mapField(array( + 'fieldName' => 'name', + 'type' => 'string', + 'columnName'=> 'user_name', + 'nullable' => true, + 'unique' => false, + 'length' => 250, +)); + +$metadata->mapManyToOne(array( + 'fieldName' => 'address', + 'targetEntity' => 'DDC964Address', + 'cascade' => array('persist','merge'), + 'joinColumn' => array('name'=>'address_id', 'referencedColumnMame'=>'id'), +)); + +$metadata->mapManyToMany(array( + 'fieldName' => 'groups', + 'targetEntity' => 'DDC964Group', + 'inversedBy' => 'users', + 'cascade' => array('persist','merge','detach'), + 'joinTable' => array( + 'name' => 'ddc964_users_groups', + 'joinColumns' => array(array( + 'name'=>'user_id', + 'referencedColumnName'=>'id', + )), + 'inverseJoinColumns'=>array(array( + 'name'=>'group_id', + 'referencedColumnName'=>'id', + )) + ) +)); + +$metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_AUTO); \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.DDC964.DDC964Admin.dcm.xml b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.DDC964.DDC964Admin.dcm.xml new file mode 100644 index 000000000..f4bb4ebce --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.DDC964.DDC964Admin.dcm.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file 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 new file mode 100644 index 000000000..561066f6b --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.DDC964.DDC964Guest.dcm.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + \ 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 new file mode 100644 index 000000000..68db74b48 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.DDC964.DDC964User.dcm.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC964.DDC964Admin.dcm.yml b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC964.DDC964Admin.dcm.yml new file mode 100644 index 000000000..0b8051d96 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC964.DDC964Admin.dcm.yml @@ -0,0 +1,17 @@ +Doctrine\Tests\Models\DDC964\DDC964Admin: + type: entity + associationOverride: + address: + joinColumn: + adminaddress_id: + name: adminaddress_id + referencedColumnName: id + groups: + joinTable: + name: ddc964_users_admingroups + joinColumns: + adminuser_id: + referencedColumnName: id + inverseJoinColumns: + admingroup_id: + referencedColumnName: id \ No newline at end of file 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 new file mode 100644 index 000000000..ec7936f4a --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC964.DDC964Guest.dcm.yml @@ -0,0 +1,13 @@ +Doctrine\Tests\Models\DDC964\DDC964Guest: + 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 new file mode 100644 index 000000000..3a9ebbf9d --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.DDC964.DDC964User.dcm.yml @@ -0,0 +1,35 @@ +Doctrine\Tests\Models\DDC964\DDC964User: + type: mappedSuperclass + id: + id: + type: integer + 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 + joinColumn: + name: address_id + referencedColumnName: id + cascade: [ persist, merge ] + manyToMany: + groups: + targetEntity: DDC964Group + joinTable: + name: ddc964_users_groups + joinColumns: + user_id: + referencedColumnName: id + inverseJoinColumns: + group_id: + referencedColumnName: id + cascade: [ persist, merge, detach ] \ No newline at end of file