From d1b2f93acb4a3d076674b4ba1de263bce4f14eb2 Mon Sep 17 00:00:00 2001 From: jwage Date: Mon, 5 Oct 2009 04:11:29 +0000 Subject: [PATCH] [2.0] Splitting ClassMetadata in to ClassMetadataInfo and other bug fixes --- lib/Doctrine/Common/IsolatedClassLoader.php | 1 - lib/Doctrine/ORM/EntityManager.php | 2 +- lib/Doctrine/ORM/EntityRepository.php | 6 +- lib/Doctrine/ORM/Mapping/ClassMetadata.php | 1582 +--------------- .../ORM/Mapping/ClassMetadataFactory.php | 8 +- .../ORM/Mapping/ClassMetadataInfo.php | 1662 +++++++++++++++++ .../ORM/Mapping/Driver/AnnotationDriver.php | 4 +- lib/Doctrine/ORM/Mapping/Driver/Driver.php | 6 +- lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php | 12 +- .../ORM/Mapping/Driver/YamlDriver.php | 12 +- .../ORM/Tools/Cli/Tasks/AbstractTask.php | 3 - .../Tools/Cli/Tasks/ConvertMappingTask.php | 18 +- .../Tools/Export/ClassmetadataExporter.php | 155 -- .../Tools/Export/Driver/AbstractExporter.php | 28 +- .../Export/Driver/AnnotationExporter.php | 206 +- .../ORM/Tools/Export/Driver/PhpExporter.php | 4 +- .../ORM/Tools/Export/Driver/XmlExporter.php | 15 +- .../ORM/Tools/Export/Driver/YamlExporter.php | 12 +- .../Tools/Export/Driver/annotation.tpl.php | 23 +- lib/Doctrine/ORM/Tools/SchemaTool.php | 1 - 20 files changed, 1928 insertions(+), 1832 deletions(-) create mode 100644 lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php delete mode 100644 lib/Doctrine/ORM/Tools/Export/ClassmetadataExporter.php diff --git a/lib/Doctrine/Common/IsolatedClassLoader.php b/lib/Doctrine/Common/IsolatedClassLoader.php index 975ee5812..fd5098f4c 100644 --- a/lib/Doctrine/Common/IsolatedClassLoader.php +++ b/lib/Doctrine/Common/IsolatedClassLoader.php @@ -108,7 +108,6 @@ class IsolatedClassLoader $class = ($this->_basePath !== null ? $this->_basePath . DIRECTORY_SEPARATOR : '') . str_replace($this->_namespaceSeparator, DIRECTORY_SEPARATOR, $className) . $this->_fileExtension; - require $class; return true; diff --git a/lib/Doctrine/ORM/EntityManager.php b/lib/Doctrine/ORM/EntityManager.php index 21a969c76..43797fb09 100644 --- a/lib/Doctrine/ORM/EntityManager.php +++ b/lib/Doctrine/ORM/EntityManager.php @@ -472,7 +472,7 @@ class EntityManager $metadata = $this->getClassMetadata($entityName); $customRepositoryClassName = $metadata->getCustomRepositoryClass(); if ($customRepositoryClassName !== null) { - $repository = new $customRepositoryClassName($entityName, $metadata); + $repository = new $customRepositoryClassName($this, $metadata); } else { $repository = new EntityRepository($this, $metadata); } diff --git a/lib/Doctrine/ORM/EntityRepository.php b/lib/Doctrine/ORM/EntityRepository.php index 557b0ca4a..1c2c11d43 100644 --- a/lib/Doctrine/ORM/EntityRepository.php +++ b/lib/Doctrine/ORM/EntityRepository.php @@ -35,9 +35,9 @@ namespace Doctrine\ORM; */ class EntityRepository { - private $_entityName; - private $_em; - private $_class; + protected $_entityName; + protected $_em; + protected $_class; /** * Initializes a new EntityRepository. diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadata.php b/lib/Doctrine/ORM/Mapping/ClassMetadata.php index 4fc92b84f..f3d2d8c14 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadata.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadata.php @@ -40,312 +40,8 @@ use Doctrine\Common\DoctrineException; * @author Roman Borschel * @since 2.0 */ -final class ClassMetadata +final class ClassMetadata extends ClassMetadataInfo { - /* The inheritance mapping types */ - /** - * NONE means the class does not participate in an inheritance hierarchy - * and therefore does not need an inheritance mapping type. - */ - const INHERITANCE_TYPE_NONE = 1; - /** - * JOINED means the class will be persisted according to the rules of - * Class Table Inheritance. - */ - const INHERITANCE_TYPE_JOINED = 2; - /** - * SINGLE_TABLE means the class will be persisted according to the rules of - * Single Table Inheritance. - */ - const INHERITANCE_TYPE_SINGLE_TABLE = 3; - /** - * TABLE_PER_CLASS means the class will be persisted according to the rules - * of Concrete Table Inheritance. - */ - const INHERITANCE_TYPE_TABLE_PER_CLASS = 4; - - /* The Id generator types. */ - /** - * AUTO means the generator type will depend on what the used platform prefers. - * Offers full portability. - */ - const GENERATOR_TYPE_AUTO = 1; - /** - * SEQUENCE means a separate sequence object will be used. Platforms that do - * not have native sequence support may emulate it. Full portability is currently - * not guaranteed. - */ - const GENERATOR_TYPE_SEQUENCE = 2; - /** - * TABLE means a separate table is used for id generation. - * Offers full portability. - */ - const GENERATOR_TYPE_TABLE = 3; - /** - * IDENTITY means an identity column is used for id generation. The database - * will fill in the id column on insertion. Platforms that do not support - * native identity columns may emulate them. Full portability is currently - * not guaranteed. - */ - const GENERATOR_TYPE_IDENTITY = 4; - /** - * NONE means the class does not have a generated id. That means the class - * must have a natural id. - */ - const GENERATOR_TYPE_NONE = 5; - /** - * DEFERRED_IMPLICIT means that changes of entities are calculated at commit-time - * by doing a property-by-property comparison with the original data. This will - * be done for all entities that are in MANAGED state at commit-time. - * - * This is the default change tracking policy. - */ - const CHANGETRACKING_DEFERRED_IMPLICIT = 1; - /** - * DEFERRED_EXPLICIT means that changes of entities are calculated at commit-time - * by doing a property-by-property comparison with the original data. This will - * be done only for entities that were explicitly saved (through save() or cascade). - */ - const CHANGETRACKING_DEFERRED_EXPLICIT = 2; - /** - * NOTIFY means that Doctrine relies on the entities sending out notifications - * when their properties change. Such entity classes must implement - * the NotifyPropertyChanged interface. - */ - const CHANGETRACKING_NOTIFY = 3; - - /** - * The name of the entity class. - */ - public $name; - - /** - * The namespace the entity class is contained in. - * - * @var string - */ - public $namespace; - - /** - * The name of the entity class that is at the root of the entity inheritance - * hierarchy. If the entity is not part of an inheritance hierarchy this is the same - * as $_entityName. - * - * @var string - */ - public $rootEntityName; - - /** - * The name of the custom repository class used for the entity class. - * (Optional). - * - * @var string - */ - public $customRepositoryClassName; - - /** - * Whether this class describes the mapping of a mapped superclass. - * - * @var boolean - */ - public $isMappedSuperclass = false; - - /** - * The names of the parent classes (ancestors). - * - * @var array - */ - public $parentClasses = array(); - - /** - * The names of all subclasses. - * - * @var array - */ - public $subClasses = array(); - - /** - * The field names of all fields that are part of the identifier/primary key - * of the mapped entity class. - * - * @var array - */ - public $identifier = array(); - - /** - * The inheritance mapping type used by the class. - * - * @var integer - */ - public $inheritanceType = self::INHERITANCE_TYPE_NONE; - - /** - * The Id generator type used by the class. - * - * @var string - */ - public $generatorType = self::GENERATOR_TYPE_NONE; - - /** - * The field mappings of the class. - * Keys are field names and values are mapping definitions. - * - * The mapping definition array has the following values: - * - * - fieldName (string) - * The name of the field in the Entity. - * - * - type (object Doctrine\DBAL\Types\* or custom type) - * The type of the column. Can be one of Doctrine's portable types - * or a custom type. - * - * - columnName (string, optional) - * The column name. Optional. Defaults to the field name. - * - * - length (integer, optional) - * The database length of the column. Optional. Default value taken from - * the type. - * - * - id (boolean, optional) - * Marks the field as the primary key of the Entity. Multiple fields of an - * entity can have the id attribute, forming a composite key. - * - * - idGenerator (string, optional) - * Either: idGenerator => 'nameOfGenerator', usually only for TABLE/SEQUENCE generators - * Or: idGenerator => 'identity' or 'auto' or 'table' or 'sequence' - * Note that 'auto', 'table', 'sequence' and 'identity' are reserved names and - * therefore cant be used as a generator name! - * - * - nullable (boolean, optional) - * Whether the column is nullable. Defaults to FALSE. - * - * - columnDefinition (string, optional, schema-only) - * The SQL fragment that is used when generating the DDL for the column. - * - * - precision (integer, optional, schema-only) - * The precision of a decimal column. Only valid if the column type is decimal. - * - * - scale (integer, optional, schema-only) - * The scale of a decimal column. Only valid if the column type is decimal. - * - * - index (string, optional, schema-only) - * Whether an index should be generated for the column. - * The value specifies the name of the index. To create a multi-column index, - * just use the same name for several mappings. - * - * - foreignKey (string, optional, schema-only) - * - * @var array - */ - public $fieldMappings = array(); - - /** - * An array of field names. Used to look up field names from column names. - * Keys are column names and values are field names. - * This is the reverse lookup map of $_columnNames. - * - * @var array - */ - public $fieldNames = array(); - - /** - * A map of field names to column names. Keys are field names and values column names. - * Used to look up column names from field names. - * This is the reverse lookup map of $_fieldNames. - * - * @var array - */ - public $columnNames = array(); - - /** - * A map of column names as they appear in an SQL result set to column names as they - * are defined in the mapping. This includes the columns of all mapped fields as well - * as any join columns and discriminator columns. - * - * @var array - */ - public $resultColumnNames = array(); - - /** - * Whether to automatically OUTER JOIN subtypes when a basetype is queried. - * - * This does only apply to the JOINED inheritance mapping strategy. - * - * @var boolean - */ - //public $joinSubclasses = true; - - /** - * The discriminator value of this class. - * - * This does only apply to the JOINED and SINGLE_TABLE inheritance mapping strategies - * where a discriminator column is used. - * - * @var mixed - * @see _discriminatorColumn - */ - public $discriminatorValue; - - /** - * The discriminator map of all mapped classes in the hierarchy. - * - * This does only apply to the JOINED and SINGLE_TABLE inheritance mapping strategies - * where a discriminator column is used. - * - * @var mixed - * @see _discriminatorColumn - */ - public $discriminatorMap = array(); - - /** - * The definition of the descriminator column used in JOINED and SINGLE_TABLE - * inheritance mappings. - * - * @var array - */ - public $discriminatorColumn; - - /** - * The primary table definition. The definition is an array with the - * following entries: - * - * name => - * schema => - * indexes => array - * uniqueConstraints => array - * - * @var array - */ - public $primaryTable; - - /** - * The registered lifecycle callbacks for entities of this class. - * - * @var array - */ - public $lifecycleCallbacks = array(); - - /** - * The association mappings. All mappings, inverse and owning side. - * - * @var array - */ - public $associationMappings = array(); - - /** - * List of inverse association mappings, indexed by mappedBy field name. - * - * @var array - */ - public $inverseMappings = array(); - - /** - * Flag indicating whether the identifier/primary key of the class is composite. - * - * @var boolean - */ - public $isIdentifierComposite = false; - /** * The ReflectionClass instance of the mapped class. * @@ -360,66 +56,6 @@ final class ClassMetadata */ public $reflFields = array(); - /** - * The ID generator used for generating IDs for this class. - * - * @var AbstractIdGenerator - */ - public $idGenerator; - - /** - * The definition of the sequence generator of this class. Only used for the - * SEQUENCE generation strategy. - * - * @var array - */ - public $sequenceGeneratorDefinition; - - /** - * The definition of the table generator of this class. Only used for the - * TABLE generation strategy. - * - * @var array - */ - public $tableGeneratorDefinition; - - /** - * The policy used for change-tracking on entities of this class. - * - * @var integer - */ - public $changeTrackingPolicy = self::CHANGETRACKING_DEFERRED_IMPLICIT; - - /** - * The SQL INSERT string for entities of this class. - * - * @var string - */ - public $insertSql; - - /** - * A map of field names to class names, where the field names are association - * fields that have been inherited from another class and values are the names - * of the classes that define the association. - * - * @var array - */ - public $inheritedAssociationFields = array(); - - /** - * A flag for whether or not instances of this class are to be versioned with optimistic locking. - * - * @var boolean $isVersioned - */ - public $isVersioned; - - /** - * The name of the field which is used for versioning in optimistic locking (if any). - * - * @var mixed $versionField - */ - public $versionField; - /** * Initializes a new ClassMetadata instance that will hold the object-relational mapping * metadata of the class with the given name. @@ -455,56 +91,6 @@ final class ClassMetadata return $this->reflFields; } - /** - * Gets the change tracking policy used by this class. - * - * @return integer - */ - public function getChangeTrackingPolicy() - { - return $this->changeTrackingPolicy; - } - - /** - * Sets the change tracking policy used by this class. - * - * @param integer $policy - */ - public function setChangeTrackingPolicy($policy) - { - $this->changeTrackingPolicy = $policy; - } - - /** - * Whether the change tracking policy of this class is "deferred explicit". - * - * @return boolean - */ - public function isChangeTrackingDeferredExplicit() - { - return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_EXPLICIT; - } - - /** - * Whether the change tracking policy of this class is "deferred implicit". - * - * @return boolean - */ - public function isChangeTrackingDeferredImplicit() - { - return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_IMPLICIT; - } - - /** - * Whether the change tracking policy of this class is "notify". - * - * @return boolean - */ - public function isChangeTrackingNotify() - { - return $this->changeTrackingPolicy == self::CHANGETRACKING_NOTIFY; - } - /** * INTERNAL: * Adds a reflection property. Usually only used by the ClassMetadataFactory @@ -541,208 +127,6 @@ final class ClassMetadata } return $this->reflFields[$this->identifier[0]]; } - - /** - * Gets the name of the mapped class. - * - * @return string - */ - public function getClassName() - { - return $this->name; - } - - /** - * Gets the name of the class in the entity hierarchy that owns the field with - * the given name. The owning class is the one that defines the field. - * - * @param string $fieldName - * @return string - */ - public function getOwningClass($fieldName) - { - if ($this->inheritanceType == self::INHERITANCE_TYPE_NONE) { - return $this->name; - } else { - $mapping = $this->getFieldMapping($fieldName); - return $mapping['inherited']; - } - } - - /** - * Gets the name of the root class of the mapped entity hierarchy. If the entity described - * by this ClassMetadata instance is not participating in a hierarchy, this is the same as the - * name returned by {@link getClassName()}. - * - * @return string The name of the root class of the entity hierarchy. - */ - public function getRootClassName() - { - return $this->rootEntityName; - } - - /** - * Checks whether a field is part of the identifier/primary key field(s). - * - * @param string $fieldName The field name - * @return boolean TRUE if the field is part of the table identifier/primary key field(s), - * FALSE otherwise. - */ - public function isIdentifier($fieldName) - { - if ( ! $this->isIdentifierComposite) { - return $fieldName === $this->identifier[0]; - } - return in_array($fieldName, $this->identifier); - } - - /** - * Checks if the class has a composite identifier. - * - * @param string $fieldName The field name - * @return boolean TRUE if the identifier is composite, FALSE otherwise. - */ - public function isIdentifierComposite() - { - return $this->isIdentifierComposite; - } - - /** - * Check if the field is unique. - * - * @param string $fieldName The field name - * @return boolean TRUE if the field is unique, FALSE otherwise. - */ - public function isUniqueField($fieldName) - { - $mapping = $this->getFieldMapping($fieldName); - if ($mapping !== false) { - return isset($mapping['unique']) && $mapping['unique'] == true; - } - return false; - } - - /** - * Check if the field is not null. - * - * @param string $fieldName The field name - * @return boolean TRUE if the field is not null, FALSE otherwise. - */ - public function isNullable($fieldName) - { - $mapping = $this->getFieldMapping($fieldName); - if ($mapping !== false) { - return isset($mapping['nullable']) && $mapping['nullable'] == true; - } - return false; - } - - /** - * Gets a column name for a field name. - * If the column name for the field cannot be found, the given field name - * is returned. - * - * @param string $fieldName The field name. - * @return string The column name. - */ - public function getColumnName($fieldName) - { - return isset($this->columnNames[$fieldName]) ? - $this->columnNames[$fieldName] : $fieldName; - } - - /** - * Gets the mapping of a (regular) field that holds some data but not a - * reference to another object. - * - * @param string $fieldName The field name. - * @return array The field mapping. - */ - public function getFieldMapping($fieldName) - { - if ( ! isset($this->fieldMappings[$fieldName])) { - throw MappingException::mappingNotFound($fieldName); - } - return $this->fieldMappings[$fieldName]; - } - - /** - * Gets the mapping of an association. - * - * @param string $fieldName The field name that represents the association in - * the object model. - * @return Doctrine\ORM\Mapping\AssociationMapping The mapping. - */ - public function getAssociationMapping($fieldName) - { - if ( ! isset($this->associationMappings[$fieldName])) { - throw MappingException::mappingNotFound($fieldName); - } - return $this->associationMappings[$fieldName]; - } - - /** - * Gets the inverse association mapping for the given target class name and - * owning fieldname. - * - * @param string $mappedByFieldName The field on the - * @return Doctrine\ORM\Mapping\AssociationMapping The mapping or NULL if there is no such - * inverse association mapping. - */ - public function getInverseAssociationMapping($targetClassName, $mappedByFieldName) - { - return isset($this->inverseMappings[$targetClassName][$mappedByFieldName]) ? - $this->inverseMappings[$targetClassName][$mappedByFieldName] : null; - } - - /** - * Checks whether the class has an inverse association mapping that points to the - * specified class and ha the specified mappedBy field. - * - * @param string $targetClassName The name of the target class. - * @param string $mappedByFieldName The name of the mappedBy field that points to the field on - * the target class that owns the association. - * @return boolean - */ - public function hasInverseAssociationMapping($targetClassName, $mappedByFieldName) - { - return isset($this->inverseMappings[$targetClassName][$mappedByFieldName]); - } - - /** - * Gets all association mappings of the class. - * - * @return array - */ - public function getAssociationMappings() - { - return $this->associationMappings; - } - - /** - * Gets all association mappings of the class. - * - * Alias for getAssociationMappings(). - * - * @return array - */ - public function getAssociations() - { - return $this->associationMappings; - } - - /** - * Gets the field name for a column name. - * If no field name can be found the column name is returned. - * - * @param string $columnName column name - * @return string column alias - */ - public function getFieldName($columnName) - { - return isset($this->fieldNames[$columnName]) ? - $this->fieldNames[$columnName] : $columnName; - } /** * Validates & completes the given field mapping. @@ -750,123 +134,15 @@ final class ClassMetadata * @param array $mapping The field mapping to validated & complete. * @return array The validated and completed field mapping. */ - private function _validateAndCompleteFieldMapping(array &$mapping) + protected function _validateAndCompleteFieldMapping(array &$mapping) { - // Check mandatory fields - if ( ! isset($mapping['fieldName'])) { - throw MappingException::missingFieldName(); - } - if ( ! isset($mapping['type'])) { - throw MappingException::missingType(); - } - - // Complete fieldName and columnName mapping - if ( ! isset($mapping['columnName'])) { - $mapping['columnName'] = $mapping['fieldName']; - } else { - if ($mapping['columnName'][0] == '`') { - $mapping['columnName'] = trim($mapping['columnName'], '`'); - $mapping['quoted'] = true; - } - } - - $this->columnNames[$mapping['fieldName']] = $mapping['columnName']; - $this->fieldNames[$mapping['columnName']] = $mapping['fieldName']; - - // Complete id mapping - if (isset($mapping['id']) && $mapping['id'] === true) { - if ( ! in_array($mapping['fieldName'], $this->identifier)) { - $this->identifier[] = $mapping['fieldName']; - } - // Check for composite key - if ( ! $this->isIdentifierComposite && count($this->identifier) > 1) { - $this->isIdentifierComposite = true; - } - } + parent::_validateAndCompleteFieldMapping($mapping); // Store ReflectionProperty of mapped field $refProp = $this->reflClass->getProperty($mapping['fieldName']); $refProp->setAccessible(true); $this->reflFields[$mapping['fieldName']] = $refProp; } - - /** - * Maps an embedded value object. - * - * @todo Implementation. - */ - /*public function mapEmbeddedValue() - { - //... - }*/ - - /** - * Gets the identifier (primary key) field names of the class. - * - * @return mixed - * @deprecated Use getIdentifierFieldNames() - */ - public function getIdentifier() - { - return $this->identifier; - } - - /** - * Gets the identifier (primary key) field names of the class. - * - * @return mixed - */ - public function getIdentifierFieldNames() - { - return $this->identifier; - } - - /** - * Gets the name of the single id field. Note that this only works on - * entity classes that have a single-field pk. - * - * @return string - */ - public function getSingleIdentifierFieldName() - { - if ($this->isIdentifierComposite) { - throw DoctrineException::singleIdNotAllowedOnCompositePrimaryKey(); - } - return $this->identifier[0]; - } - - /** - * Gets the column name of the single id column. Note that this only works on - * entity classes that have a single-field pk. - * - * @return string - */ - public function getSingleIdentifierColumnName() - { - return $this->getColumnName($this->getSingleIdentifierFieldName()); - } - - /** - * INTERNAL: - * Sets the mapped identifier/primary key fields of this class. - * Mainly used by the ClassMetadataFactory to assign inherited identifiers. - * - * @param array $identifier - */ - public function setIdentifier(array $identifier) - { - $this->identifier = $identifier; - } - - /** - * Checks whether the class has a (mapped) field with a certain name. - * - * @return boolean - */ - public function hasField($fieldName) - { - return isset($this->reflFields[$fieldName]); - } /** * Extracts the identifier values of an entity of this class. @@ -934,580 +210,23 @@ final class ClassMetadata { $this->reflFields[$this->fieldNames[$column]]->setValue($entity, $value); } - - /** - * Gets all field mappings. - * - * @return array - */ - public function getFieldMappings() - { - return $this->fieldMappings; - } - /** - * Gets an array containing all the column names. - * - * @return array - */ - public function getColumnNames(array $fieldNames = null) - { - if ($fieldNames === null) { - return array_keys($this->fieldNames); - } else { - $columnNames = array(); - foreach ($fieldNames as $fieldName) { - $columnNames[] = $this->getColumnName($fieldName); - } - return $columnNames; - } - } - - /** - * Returns an array with all the identifier column names. - * - * @return array - */ - public function getIdentifierColumnNames() - { - if ($this->isIdentifierComposite) { - $columnNames = array(); - foreach ($this->identifier as $idField) { - $columnNames[] = $this->fieldMappings[$idField]['columnName']; - } - return $columnNames; - } else { - return array($this->fieldMappings[$this->identifier[0]]['columnName']); - } - } - - /** - * Returns an array containing all the field names. - * - * @return array - */ - public function getFieldNames() - { - return array_values($this->fieldNames); - } - - /** - * Gets the Id generator type used by the class. - * - * @return string - */ - public function getIdGeneratorType() - { - return $this->generatorType; - } - - /** - * Sets the type of Id generator to use for the mapped class. - */ - public function setIdGeneratorType($generatorType) - { - $this->generatorType = $generatorType; - } - - /** - * Checks whether the mapped class uses an Id generator. - * - * @return boolean TRUE if the mapped class uses an Id generator, FALSE otherwise. - */ - public function usesIdGenerator() - { - return $this->generatorType != self::GENERATOR_TYPE_NONE; - } - - /** - * - * @return boolean - */ - public function isInheritanceTypeNone() - { - return $this->inheritanceType == self::INHERITANCE_TYPE_NONE; - } - - /** - * Checks whether the mapped class uses the JOINED inheritance mapping strategy. - * - * @return boolean TRUE if the class participates in a JOINED inheritance mapping, - * FALSE otherwise. - */ - public function isInheritanceTypeJoined() - { - return $this->inheritanceType == self::INHERITANCE_TYPE_JOINED; - } - - /** - * Checks whether the mapped class uses the SINGLE_TABLE inheritance mapping strategy. - * - * @return boolean TRUE if the class participates in a SINGLE_TABLE inheritance mapping, - * FALSE otherwise. - */ - public function isInheritanceTypeSingleTable() - { - return $this->inheritanceType == self::INHERITANCE_TYPE_SINGLE_TABLE; - } - - /** - * Checks whether the mapped class uses the TABLE_PER_CLASS inheritance mapping strategy. - * - * @return boolean TRUE if the class participates in a TABLE_PER_CLASS inheritance mapping, - * FALSE otherwise. - */ - public function isInheritanceTypeTablePerClass() - { - return $this->inheritanceType == self::INHERITANCE_TYPE_TABLE_PER_CLASS; - } - - /** - * Checks whether the class uses an identity column for the Id generation. - * - * @return boolean TRUE if the class uses the IDENTITY generator, FALSE otherwise. - */ - public function isIdGeneratorIdentity() - { - return $this->generatorType == self::GENERATOR_TYPE_IDENTITY; - } - - /** - * Checks whether the class uses a sequence for id generation. - * - * @return boolean TRUE if the class uses the SEQUENCE generator, FALSE otherwise. - */ - public function isIdGeneratorSequence() - { - return $this->generatorType == self::GENERATOR_TYPE_SEQUENCE; - } - - /** - * Checks whether the class uses a table for id generation. - * - * @return boolean TRUE if the class uses the TABLE generator, FALSE otherwise. - */ - public function isIdGeneratorTable() - { - $this->generatorType == self::GENERATOR_TYPE_TABLE; - } - - /** - * Checks whether the class has a natural identifier/pk (which means it does - * not use any Id generator. - * - * @return boolean - */ - public function isIdentifierNatural() - { - return $this->generatorType == self::GENERATOR_TYPE_NONE; - } - - /** - * Gets the type of a field. - * - * @param string $fieldName - * @return Doctrine\DBAL\Types\Type - */ - public function getTypeOfField($fieldName) - { - return isset($this->fieldMappings[$fieldName]) ? - $this->fieldMappings[$fieldName]['type'] : null; - } - - /** - * Gets the type of a column. - * - * @return Doctrine\DBAL\Types\Type - */ - public function getTypeOfColumn($columnName) - { - return $this->getTypeOfField($this->getFieldName($columnName)); - } - - /** - * Gets the name of the primary table. - * - * @return string - */ - public function getTableName() - { - return $this->primaryTable['name']; - } - - /** - * Gets the table name to use for temporary identifier tables of this class. - * - * @return string - */ - public function getTemporaryIdTableName() - { - return $this->primaryTable['name'] . '_id_tmp'; - } - - /** - * Gets the inheritance mapping type used by the mapped class. - * - * @return string - */ - public function getInheritanceType() - { - return $this->inheritanceType; - } - - /** - * Sets the mapped subclasses of this class. - * - * @param array $subclasses The names of all mapped subclasses. - */ - public function setSubclasses(array $subclasses) - { - foreach ($subclasses as $subclass) { - if (strpos($subclass, '\\') === false) { - $this->subClasses[] = $this->namespace . '\\' . $subclass; - } else { - $this->subClasses[] = $subclass; - } - } - } - - /** - * Gets the names of all subclasses. - * - * @return array The names of all subclasses. - */ - public function getSubclasses() - { - return $this->subClasses; - } - - /** - * Checks whether the class has any persistent subclasses. - * - * @return boolean TRUE if the class has one or more persistent subclasses, FALSE otherwise. - */ - public function hasSubclasses() - { - return ! $this->subClasses; - } - - /** - * Gets the names of all parent classes. - * - * @return array The names of all parent classes. - */ - public function getParentClasses() - { - return $this->parentClasses; - } - - /** - * Sets the parent class names. - * Assumes that the class names in the passed array are in the order: - * directParent -> directParentParent -> directParentParentParent ... -> root. - */ - public function setParentClasses(array $classNames) - { - $this->parentClasses = $classNames; - if (count($classNames) > 0) { - $this->rootEntityName = array_pop($classNames); - } - } - - /** - * Checks whether the class has any persistent parent classes. - * - * @return boolean TRUE if the class has one or more persistent parent classes, FALSE otherwise. - */ - public function hasParentClasses() - { - return ! $this->parentClasses; - } - - /** - * Sets the inheritance type used by the class and it's subclasses. - * - * @param integer $type - */ - public function setInheritanceType($type) - { - if ( ! $this->_isInheritanceType($type)) { - throw MappingException::invalidInheritanceType($type); - } - $this->inheritanceType = $type; - } - - /** - * Checks whether a mapped field is inherited from a superclass. - * - * @return boolean TRUE if the field is inherited, FALSE otherwise. - */ - public function isInheritedField($fieldName) - { - return isset($this->fieldMappings[$fieldName]['inherited']); - } - - /** - * Sets the name of the primary table the class is mapped to. - * - * @param string $tableName The table name. - * @deprecated - */ - public function setTableName($tableName) - { - $this->primaryTable['name'] = $tableName; - } - - /** - * Sets the primary table definition. The provided array must have the - * following structure: - * - * name => - * schema => - * catalog => - * - * @param array $primaryTableDefinition - */ - public function setPrimaryTable(array $primaryTableDefinition) - { - $this->primaryTable = $primaryTableDefinition; - } - - /** - * Gets the primary table definition. - * - * @see setPrimaryTable() - * @return array - */ - public function getPrimaryTable() - { - return $this->primaryTable; - } - - /** - * Checks whether the given type identifies an inheritance type. - * - * @param string $type - * @return boolean - */ - private function _isInheritanceType($type) - { - return $type == self::INHERITANCE_TYPE_NONE || - $type == self::INHERITANCE_TYPE_SINGLE_TABLE || - $type == self::INHERITANCE_TYPE_JOINED || - $type == self::INHERITANCE_TYPE_TABLE_PER_CLASS; - } - - /** - * Checks whether the given type identifies an id generator type. - * - * @param string $type - * @return boolean - */ - private function _isIdGeneratorType($type) - { - return $type == self::GENERATOR_TYPE_AUTO || - $type == self::GENERATOR_TYPE_IDENTITY || - $type == self::GENERATOR_TYPE_SEQUENCE || - $type == self::GENERATOR_TYPE_TABLE || - $type == self::GENERATOR_TYPE_NONE; - } - - /** - * Makes some automatic additions to the association mapping to make the life - * easier for the user, and store join columns in the metadata. - * - * @param array $mapping - * @todo Pass param by ref? - */ - private function _completeAssociationMapping(array $mapping) - { - $mapping['sourceEntity'] = $this->name; - if (isset($mapping['targetEntity']) && strpos($mapping['targetEntity'], '\\') === false) { - $mapping['targetEntity'] = $this->namespace . '\\' . $mapping['targetEntity']; - } - return $mapping; - } - - /** - * Adds a field mapping. - * - * @param array $mapping The field mapping. - */ - public function mapField(array $mapping) - { - $this->_validateAndCompleteFieldMapping($mapping); - if (isset($this->fieldMappings[$mapping['fieldName']])) { - throw MappingException::duplicateFieldMapping($mapping['fieldName']); - } - $this->fieldMappings[$mapping['fieldName']] = $mapping; - } - - /** - * INTERNAL: - * Adds an association mapping without completing/validating it. - * This is mainly used to add inherited association mappings to derived classes. - * - * @param AssociationMapping $mapping - * @param string $owningClassName The name of the class that defined this mapping. - * @todo Rename: addInheritedAssociationMapping - */ - public function addAssociationMapping(AssociationMapping $mapping, $owningClassName = null) - { - $sourceFieldName = $mapping->sourceFieldName; - if (isset($this->associationMappings[$sourceFieldName])) { - throw MappingException::duplicateFieldMapping(); - } - $this->associationMappings[$sourceFieldName] = $mapping; - if ($owningClassName !== null) { - $this->inheritedAssociationFields[$sourceFieldName] = $owningClassName; - } - $this->_registerMappingIfInverse($mapping); - } - - /** - * INTERNAL: - * Adds a field mapping without completing/validating it. - * This is mainly used to add inherited field mappings to derived classes. - * - * @param array $mapping - * @todo Rename: addInheritedFieldMapping - */ - public function addFieldMapping(array $fieldMapping) - { - $this->fieldMappings[$fieldMapping['fieldName']] = $fieldMapping; - $this->columnNames[$fieldMapping['fieldName']] = $fieldMapping['columnName']; - $this->fieldNames[$fieldMapping['columnName']] = $fieldMapping['fieldName']; - } - - /** - * Adds a one-to-one mapping. - * - * @param array $mapping The mapping. - */ - public function mapOneToOne(array $mapping) - { - $mapping = $this->_completeAssociationMapping($mapping); - $oneToOneMapping = new OneToOneMapping($mapping); - $this->_storeAssociationMapping($oneToOneMapping); - } - - /** - * Registers the mapping as an inverse mapping, if it is a mapping on the - * inverse side of an association mapping. - * - * @param AssociationMapping The mapping to register as inverse if it is a mapping - * for the inverse side of an association. - */ - private function _registerMappingIfInverse(AssociationMapping $assoc) - { - if ($assoc->isInverseSide()) { - $this->inverseMappings[$assoc->targetEntityName][$assoc->mappedByFieldName] = $assoc; - } - } - - /** - * Adds a one-to-many mapping. - * - * @param array $mapping The mapping. - */ - public function mapOneToMany(array $mapping) - { - $mapping = $this->_completeAssociationMapping($mapping); - $oneToManyMapping = new OneToManyMapping($mapping); - $this->_storeAssociationMapping($oneToManyMapping); - } - - /** - * Adds a many-to-one mapping. - * - * @param array $mapping The mapping. - */ - public function mapManyToOne(array $mapping) - { - // A many-to-one mapping is simply a one-one backreference - $this->mapOneToOne($mapping); - } - - /** - * Adds a many-to-many mapping. - * - * @param array $mapping The mapping. - */ - public function mapManyToMany(array $mapping) - { - $mapping = $this->_completeAssociationMapping($mapping); - $manyToManyMapping = new ManyToManyMapping($mapping); - $this->_storeAssociationMapping($manyToManyMapping); - } - /** * Stores the association mapping. * * @param AssociationMapping $assocMapping */ - private function _storeAssociationMapping(AssociationMapping $assocMapping) + protected function _storeAssociationMapping(AssociationMapping $assocMapping) { - $sourceFieldName = $assocMapping->sourceFieldName; - if (isset($this->associationMappings[$sourceFieldName])) { - throw MappingException::duplicateFieldMapping(); - } - $this->associationMappings[$sourceFieldName] = $assocMapping; - $this->_registerMappingIfInverse($assocMapping); + parent::_storeAssociationMapping($assocMapping); // Store ReflectionProperty of mapped field + $sourceFieldName = $assocMapping->sourceFieldName; $refProp = $this->reflClass->getProperty($sourceFieldName); $refProp->setAccessible(true); $this->reflFields[$sourceFieldName] = $refProp; } - /** - * Registers a custom repository class for the entity class. - * - * @param string $mapperClassName The class name of the custom mapper. - */ - public function setCustomRepositoryClass($repositoryClassName) - { - $this->customRepositoryClassName = $repositoryClassName; - } - - /** - * Gets the name of the custom repository class used for the entity class. - * - * @return string|null The name of the custom repository class or NULL if the entity - * class does not have a custom repository class. - */ - public function getCustomRepositoryClass() - { - return $this->customRepositoryClassName; - } - - /** - * Sets whether sub classes should be automatically OUTER JOINed when a base - * class is queried in a class hierarchy that uses the JOINED inheritance mapping - * strategy. - * - * This options does only apply to the JOINED inheritance mapping strategy. - * - * @param boolean $bool - * @see getJoinSubClasses() - */ - /*public function setJoinSubClasses($bool) - { - $this->joinSubclasses = (bool)$bool; - }*/ - - /** - * Gets whether the class mapped by this instance should OUTER JOIN sub classes - * when a base class is queried. - * - * @return - * @see setJoinSubClasses() - */ - /*public function getJoinSubClasses() - { - return $this->joinSubclasses; - }*/ - /** * Dispatches the lifecycle event of the given entity to the registered * lifecycle callbacks and lifecycle listeners. @@ -1522,295 +241,6 @@ final class ClassMetadata } } - /** - * Whether the class has any attached lifecycle listeners or callbacks for a lifecycle event. - * - * @param string $lifecycleEvent - * @return boolean - */ - public function hasLifecycleCallbacks($lifecycleEvent) - { - return isset($this->lifecycleCallbacks[$lifecycleEvent]); - } - - /** - * Gets the registered lifecycle callbacks for an event. - * - * @param string $event - * @return array - */ - public function getLifecycleCallbacks($event) - { - return isset($this->lifecycleCallbacks[$event]) ? $this->lifecycleCallbacks[$event] : array(); - } - - /** - * Adds a lifecycle callback for entities of this class. - * - * Note: If the same callback is registered more than once, the old one - * will be overridden. - * - * @param string $callback - * @param string $event - */ - public function addLifecycleCallback($callback, $event) - { - $this->lifecycleCallbacks[$event][] = $callback; - } - - /** - * Sets the discriminator column definition. - * - * @param array $columnDef - * @see getDiscriminatorColumn() - */ - public function setDiscriminatorColumn($columnDef) - { - if ($columnDef !== null) { - if ( ! isset($columnDef['name'])) { - throw new MappingException("'name' attribute is mandatory for discriminator columns."); - } - if ( ! isset($columnDef['fieldName'])) { - $columnDef['fieldName'] = $columnDef['name']; - } - $this->discriminatorColumn = $columnDef; - } - } - - /** - * Gets the discriminator column definition. - * - * The discriminator column definition is an array with the following keys: - * name: The name of the column - * type: The type of the column (only integer and string supported) - * length: The length of the column (applies only if type is string) - * - * A discriminator column is used for JOINED and SINGLE_TABLE inheritance mappings. - * - * @return array - * @see setDiscriminatorColumn() - */ - public function getDiscriminatorColumn() - { - return $this->discriminatorColumn; - } - - /** - * Sets the discriminator values used by this class. - * Used for JOINED and SINGLE_TABLE inheritance mapping strategies. - * - * @param array $map - */ - public function setDiscriminatorMap(array $map) - { - foreach ($map as $value => $className) { - if (strpos($className, '\\') === false) { - $className = $this->namespace . '\\' . $className; - } - $this->discriminatorMap[$value] = $className; - if ($this->name == $className) { - $this->discriminatorValue = $value; - } else if (is_subclass_of($className, $this->name)) { - $this->subClasses[] = $className; - } - } - } - - /** - * Gets the discriminator value of this class. - * Used for JOINED and SINGLE_TABLE inheritance mapping strategies. - * - * @return array - */ - public function getDiscriminatorValue() - { - return $this->discriminatorValue; - } - - /** - * Checks whether the given column name is the discriminator column. - * - * @param string $columnName - * @return boolean - */ - public function isDiscriminatorColumn($columnName) - { - return $columnName === $this->discriminatorColumn['name']; - } - - /** - * Checks whether the class has a mapped association with the given field name. - * - * @param string $fieldName - * @return boolean - */ - public function hasAssociation($fieldName) - { - return isset($this->associationMappings[$fieldName]); - } - - /** - * Checks whether the class has a mapped association for the specified field - * and if yes, checks whether it is a single-valued association (to-one). - * - * @param string $fieldName - * @return boolean TRUE if the association exists and is single-valued, FALSE otherwise. - */ - public function isSingleValuedAssociation($fieldName) - { - return isset($this->associationMappings[$fieldName]) && - $this->associationMappings[$fieldName]->isOneToOne(); - } - - /** - * Checks whether the class has a mapped association for the specified field - * and if yes, checks whether it is a collection-valued association (to-many). - * - * @param string $fieldName - * @return boolean TRUE if the association exists and is collection-valued, FALSE otherwise. - */ - public function isCollectionValuedAssociation($fieldName) - { - return isset($this->associationMappings[$fieldName]) && - ! $this->associationMappings[$fieldName]->isOneToOne(); - } - - /** - * Gets the name of the ID generator used for this class. - * Only classes that use a SEQUENCE or TABLE ID generation strategy have a generator name. - * - * @return string|null The name of the ID generator or NULL if this class does not - * use a named ID generator. - */ - /*public function getIdGeneratorName() - { - return $this->idGeneratorName; - }*/ - - /** - * Sets the ID generator used to generate IDs for instances of this class. - * - * @param AbstractIdGenerator $generator - */ - public function setIdGenerator($generator) - { - $this->idGenerator = $generator; - } - - /** - * Gets the ID generator used to generate IDs for instances of this class. - * - * @return AbstractIdGenerator - */ - public function getIdGenerator() - { - return $this->idGenerator; - } - - /** - * Gets the definition of the sequence ID generator for this class. - * - * The definition has the following structure: - * - * array( - * 'sequenceName' => 'name', - * 'allocationSize' => 20, - * 'initialValue' => 1 - * ) - * - * - * @return array|null An array with the generator definition or NULL if this class - * has no sequence generator definition. - */ - public function getSequenceGeneratorDefinition() - { - return $this->sequenceGeneratorDefinition; - } - - /** - * Sets the definition of the sequence ID generator for this class. - * - * The definition must have the following structure: - * - * array( - * 'sequenceName' => 'name', - * 'allocationSize' => 20, - * 'initialValue' => 1 - * ) - * - * - * @param array $definition - */ - public function setSequenceGeneratorDefinition(array $definition) - { - $this->sequenceGeneratorDefinition = $definition; - } - - /** - * Sets the version field mapping used for versioning. Sets the default - * value to use depending on the column type - * - * @param array $mapping The version field mapping array - * @return void - */ - public function setVersionMapping(array &$mapping) - { - $this->isVersioned = true; - $this->versionField = $mapping['fieldName']; - - if ( ! isset($mapping['default'])) { - if ($mapping['type'] == 'integer') { - $mapping['default'] = 1; - } else if ($mapping['type'] == 'datetime') { - $mapping['default'] = 'CURRENT_TIMESTAMP'; - } else { - throw DoctrineException::unsupportedOptimisticLockingType($mapping['type']); - } - } - } - - /** - * Checks whether this class is versioned for optimistic locking. - * - * @return boolean TRUE if this class is versioned for optimistic locking, FALSE otherwise. - */ - public function isVersioned() - { - return $this->isVersioned; - } - - /** - * Sets whether this class is to be versioned for optimistic locking. - * - * @param boolean $bool - */ - public function setVersioned($bool) - { - $this->isVersioned = $bool; - } - - /** - * Gets the name of the field that is used for versioning if this class is versioned - * for optimistic locking. - * - * @return string - */ - public function getVersionField() - { - return $this->versionField; - } - - /** - * Sets the name of the field that is to be used for versioning if this class is - * versioned for optimistic locking. - * - * @param string $versionField - */ - public function setVersionField($versionField) - { - $this->versionField = $versionField; - } - /** * Gets the (possibly quoted) column name of a mapped field for safe use * in an SQL statement. diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php index 005bcfd57..4cd74d105 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php @@ -319,14 +319,12 @@ class ClassMetadataFactory if ($assoc->isOneToOne() && $assoc->isOwningSide) { foreach ($assoc->targetToSourceKeyColumns as $sourceCol) { $columns[] = $assoc->getQuotedJoinColumnName($sourceCol, $this->_targetPlatform); - $values[] = '?'; // Add column mapping for SQL result sets $class->resultColumnNames[$this->_targetPlatform->getSqlResultCasing($sourceCol)] = $sourceCol; } } } else if ($class->name != $class->rootEntityName || ! $class->isIdGeneratorIdentity() || $class->identifier[0] != $name) { $columns[] = $class->getQuotedColumnName($name, $this->_targetPlatform); - $values[] = '?'; // Add column mapping for SQL result sets $columnName = $class->columnNames[$name]; $class->resultColumnNames[$this->_targetPlatform->getSqlResultCasing($columnName)] = $columnName; @@ -347,14 +345,12 @@ class ClassMetadataFactory if ($assoc->isOwningSide && $assoc->isOneToOne()) { foreach ($assoc->targetToSourceKeyColumns as $sourceCol) { $columns[] = $assoc->getQuotedJoinColumnName($sourceCol, $this->_targetPlatform); - $values[] = '?'; // Add column mapping for SQL result sets $class->resultColumnNames[$this->_targetPlatform->getSqlResultCasing($sourceCol)] = $sourceCol; } } } else if ($class->generatorType != ClassMetadata::GENERATOR_TYPE_IDENTITY || $class->identifier[0] != $name) { $columns[] = $class->getQuotedColumnName($name, $this->_targetPlatform); - $values[] = '?'; // Add column mapping for SQL result sets $columnName = $class->columnNames[$name]; $class->resultColumnNames[$this->_targetPlatform->getSqlResultCasing($columnName)] = $columnName; @@ -371,7 +367,6 @@ class ClassMetadataFactory if ($class->isInheritanceTypeSingleTable() || $class->isInheritanceTypeJoined() && $class->name == $class->rootEntityName) { $columns[] = $class->getQuotedDiscriminatorColumnName($this->_targetPlatform); - $values[] = '?'; } // Add column mapping for SQL result sets $columnName = $class->discriminatorColumn['name']; @@ -384,6 +379,9 @@ class ClassMetadataFactory $class->getQuotedColumnName($class->identifier[0], $this->_targetPlatform) ); } else { + $columns = array_unique($columns); + $values = array_fill(0, count($columns), '?'); + $class->insertSql = 'INSERT INTO ' . $class->getQuotedTableName($this->_targetPlatform) . ' (' . implode(', ', $columns) . ') ' diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php new file mode 100644 index 000000000..d73ba560f --- /dev/null +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php @@ -0,0 +1,1662 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +use Doctrine\Common\DoctrineException; + +/** + * A ClassMetadata instance holds all the object-relational mapping metadata + * of an entity and it's associations. + * + * Once populated, ClassMetadata instances are usually cached in a serialized form. + * + * IMPORTANT NOTE: + * + * The fields of this class are only public for 2 reasons: + * 1) To allow fast READ access. + * 2) To drastically reduce the size of a serialized instance (private/protected members + * get the whole class name, namespace inclusive, prepended to every property in + * the serialized representation). + * + * @author Roman Borschel + * @since 2.0 + */ +class ClassMetadataInfo +{ + /* The inheritance mapping types */ + /** + * NONE means the class does not participate in an inheritance hierarchy + * and therefore does not need an inheritance mapping type. + */ + const INHERITANCE_TYPE_NONE = 1; + /** + * JOINED means the class will be persisted according to the rules of + * Class Table Inheritance. + */ + const INHERITANCE_TYPE_JOINED = 2; + /** + * SINGLE_TABLE means the class will be persisted according to the rules of + * Single Table Inheritance. + */ + const INHERITANCE_TYPE_SINGLE_TABLE = 3; + /** + * TABLE_PER_CLASS means the class will be persisted according to the rules + * of Concrete Table Inheritance. + */ + const INHERITANCE_TYPE_TABLE_PER_CLASS = 4; + + /* The Id generator types. */ + /** + * AUTO means the generator type will depend on what the used platform prefers. + * Offers full portability. + */ + const GENERATOR_TYPE_AUTO = 1; + /** + * SEQUENCE means a separate sequence object will be used. Platforms that do + * not have native sequence support may emulate it. Full portability is currently + * not guaranteed. + */ + const GENERATOR_TYPE_SEQUENCE = 2; + /** + * TABLE means a separate table is used for id generation. + * Offers full portability. + */ + const GENERATOR_TYPE_TABLE = 3; + /** + * IDENTITY means an identity column is used for id generation. The database + * will fill in the id column on insertion. Platforms that do not support + * native identity columns may emulate them. Full portability is currently + * not guaranteed. + */ + const GENERATOR_TYPE_IDENTITY = 4; + /** + * NONE means the class does not have a generated id. That means the class + * must have a natural id. + */ + const GENERATOR_TYPE_NONE = 5; + /** + * DEFERRED_IMPLICIT means that changes of entities are calculated at commit-time + * by doing a property-by-property comparison with the original data. This will + * be done for all entities that are in MANAGED state at commit-time. + * + * This is the default change tracking policy. + */ + const CHANGETRACKING_DEFERRED_IMPLICIT = 1; + /** + * DEFERRED_EXPLICIT means that changes of entities are calculated at commit-time + * by doing a property-by-property comparison with the original data. This will + * be done only for entities that were explicitly saved (through save() or cascade). + */ + const CHANGETRACKING_DEFERRED_EXPLICIT = 2; + /** + * NOTIFY means that Doctrine relies on the entities sending out notifications + * when their properties change. Such entity classes must implement + * the NotifyPropertyChanged interface. + */ + const CHANGETRACKING_NOTIFY = 3; + + /** + * The name of the entity class. + */ + public $name; + + /** + * The namespace the entity class is contained in. + * + * @var string + */ + public $namespace; + + /** + * The name of the entity class that is at the root of the entity inheritance + * hierarchy. If the entity is not part of an inheritance hierarchy this is the same + * as $_entityName. + * + * @var string + */ + public $rootEntityName; + + /** + * The name of the custom repository class used for the entity class. + * (Optional). + * + * @var string + */ + public $customRepositoryClassName; + + /** + * Whether this class describes the mapping of a mapped superclass. + * + * @var boolean + */ + public $isMappedSuperclass = false; + + /** + * The names of the parent classes (ancestors). + * + * @var array + */ + public $parentClasses = array(); + + /** + * The names of all subclasses. + * + * @var array + */ + public $subClasses = array(); + + /** + * The field names of all fields that are part of the identifier/primary key + * of the mapped entity class. + * + * @var array + */ + public $identifier = array(); + + /** + * The inheritance mapping type used by the class. + * + * @var integer + */ + public $inheritanceType = self::INHERITANCE_TYPE_NONE; + + /** + * The Id generator type used by the class. + * + * @var string + */ + public $generatorType = self::GENERATOR_TYPE_NONE; + + /** + * The field mappings of the class. + * Keys are field names and values are mapping definitions. + * + * The mapping definition array has the following values: + * + * - fieldName (string) + * The name of the field in the Entity. + * + * - type (object Doctrine\DBAL\Types\* or custom type) + * The type of the column. Can be one of Doctrine's portable types + * or a custom type. + * + * - columnName (string, optional) + * The column name. Optional. Defaults to the field name. + * + * - length (integer, optional) + * The database length of the column. Optional. Default value taken from + * the type. + * + * - id (boolean, optional) + * Marks the field as the primary key of the Entity. Multiple fields of an + * entity can have the id attribute, forming a composite key. + * + * - idGenerator (string, optional) + * Either: idGenerator => 'nameOfGenerator', usually only for TABLE/SEQUENCE generators + * Or: idGenerator => 'identity' or 'auto' or 'table' or 'sequence' + * Note that 'auto', 'table', 'sequence' and 'identity' are reserved names and + * therefore cant be used as a generator name! + * + * - nullable (boolean, optional) + * Whether the column is nullable. Defaults to FALSE. + * + * - columnDefinition (string, optional, schema-only) + * The SQL fragment that is used when generating the DDL for the column. + * + * - precision (integer, optional, schema-only) + * The precision of a decimal column. Only valid if the column type is decimal. + * + * - scale (integer, optional, schema-only) + * The scale of a decimal column. Only valid if the column type is decimal. + * + * - index (string, optional, schema-only) + * Whether an index should be generated for the column. + * The value specifies the name of the index. To create a multi-column index, + * just use the same name for several mappings. + * + * - foreignKey (string, optional, schema-only) + * + * @var array + */ + public $fieldMappings = array(); + + /** + * An array of field names. Used to look up field names from column names. + * Keys are column names and values are field names. + * This is the reverse lookup map of $_columnNames. + * + * @var array + */ + public $fieldNames = array(); + + /** + * A map of field names to column names. Keys are field names and values column names. + * Used to look up column names from field names. + * This is the reverse lookup map of $_fieldNames. + * + * @var array + */ + public $columnNames = array(); + + /** + * A map of column names as they appear in an SQL result set to column names as they + * are defined in the mapping. This includes the columns of all mapped fields as well + * as any join columns and discriminator columns. + * + * @var array + */ + public $resultColumnNames = array(); + + /** + * Whether to automatically OUTER JOIN subtypes when a basetype is queried. + * + * This does only apply to the JOINED inheritance mapping strategy. + * + * @var boolean + */ + //public $joinSubclasses = true; + + /** + * The discriminator value of this class. + * + * This does only apply to the JOINED and SINGLE_TABLE inheritance mapping strategies + * where a discriminator column is used. + * + * @var mixed + * @see _discriminatorColumn + */ + public $discriminatorValue; + + /** + * The discriminator map of all mapped classes in the hierarchy. + * + * This does only apply to the JOINED and SINGLE_TABLE inheritance mapping strategies + * where a discriminator column is used. + * + * @var mixed + * @see _discriminatorColumn + */ + public $discriminatorMap = array(); + + /** + * The definition of the descriminator column used in JOINED and SINGLE_TABLE + * inheritance mappings. + * + * @var array + */ + public $discriminatorColumn; + + /** + * The primary table definition. The definition is an array with the + * following entries: + * + * name => + * schema => + * indexes => array + * uniqueConstraints => array + * + * @var array + */ + public $primaryTable; + + /** + * The registered lifecycle callbacks for entities of this class. + * + * @var array + */ + public $lifecycleCallbacks = array(); + + /** + * The association mappings. All mappings, inverse and owning side. + * + * @var array + */ + public $associationMappings = array(); + + /** + * List of inverse association mappings, indexed by mappedBy field name. + * + * @var array + */ + public $inverseMappings = array(); + + /** + * Flag indicating whether the identifier/primary key of the class is composite. + * + * @var boolean + */ + public $isIdentifierComposite = false; + + /** + * The ID generator used for generating IDs for this class. + * + * @var AbstractIdGenerator + */ + public $idGenerator; + + /** + * The definition of the sequence generator of this class. Only used for the + * SEQUENCE generation strategy. + * + * @var array + */ + public $sequenceGeneratorDefinition; + + /** + * The definition of the table generator of this class. Only used for the + * TABLE generation strategy. + * + * @var array + */ + public $tableGeneratorDefinition; + + /** + * The policy used for change-tracking on entities of this class. + * + * @var integer + */ + public $changeTrackingPolicy = self::CHANGETRACKING_DEFERRED_IMPLICIT; + + /** + * The SQL INSERT string for entities of this class. + * + * @var string + */ + public $insertSql; + + /** + * A map of field names to class names, where the field names are association + * fields that have been inherited from another class and values are the names + * of the classes that define the association. + * + * @var array + */ + public $inheritedAssociationFields = array(); + + /** + * A flag for whether or not instances of this class are to be versioned with optimistic locking. + * + * @var boolean $isVersioned + */ + public $isVersioned; + + /** + * The name of the field which is used for versioning in optimistic locking (if any). + * + * @var mixed $versionField + */ + public $versionField; + + /** + * Initializes a new ClassMetadata instance that will hold the object-relational mapping + * metadata of the class with the given name. + * + * @param string $entityName The name of the entity class the new instance is used for. + */ + public function __construct($entityName) + { + $this->name = $entityName; + $this->rootEntityName = $entityName; + } + + /** + * Gets the change tracking policy used by this class. + * + * @return integer + */ + public function getChangeTrackingPolicy() + { + return $this->changeTrackingPolicy; + } + + /** + * Sets the change tracking policy used by this class. + * + * @param integer $policy + */ + public function setChangeTrackingPolicy($policy) + { + $this->changeTrackingPolicy = $policy; + } + + /** + * Whether the change tracking policy of this class is "deferred explicit". + * + * @return boolean + */ + public function isChangeTrackingDeferredExplicit() + { + return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_EXPLICIT; + } + + /** + * Whether the change tracking policy of this class is "deferred implicit". + * + * @return boolean + */ + public function isChangeTrackingDeferredImplicit() + { + return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_IMPLICIT; + } + + /** + * Whether the change tracking policy of this class is "notify". + * + * @return boolean + */ + public function isChangeTrackingNotify() + { + return $this->changeTrackingPolicy == self::CHANGETRACKING_NOTIFY; + } + + /** + * Gets the name of the mapped class. + * + * @return string + */ + public function getClassName() + { + return $this->name; + } + + /** + * Gets the name of the class in the entity hierarchy that owns the field with + * the given name. The owning class is the one that defines the field. + * + * @param string $fieldName + * @return string + */ + public function getOwningClass($fieldName) + { + if ($this->inheritanceType == self::INHERITANCE_TYPE_NONE) { + return $this->name; + } else { + $mapping = $this->getFieldMapping($fieldName); + return $mapping['inherited']; + } + } + + /** + * Gets the name of the root class of the mapped entity hierarchy. If the entity described + * by this ClassMetadata instance is not participating in a hierarchy, this is the same as the + * name returned by {@link getClassName()}. + * + * @return string The name of the root class of the entity hierarchy. + */ + public function getRootClassName() + { + return $this->rootEntityName; + } + + /** + * Checks whether a field is part of the identifier/primary key field(s). + * + * @param string $fieldName The field name + * @return boolean TRUE if the field is part of the table identifier/primary key field(s), + * FALSE otherwise. + */ + public function isIdentifier($fieldName) + { + if ( ! $this->isIdentifierComposite) { + return $fieldName === $this->identifier[0]; + } + return in_array($fieldName, $this->identifier); + } + + /** + * Checks if the class has a composite identifier. + * + * @param string $fieldName The field name + * @return boolean TRUE if the identifier is composite, FALSE otherwise. + */ + public function isIdentifierComposite() + { + return $this->isIdentifierComposite; + } + + /** + * Check if the field is unique. + * + * @param string $fieldName The field name + * @return boolean TRUE if the field is unique, FALSE otherwise. + */ + public function isUniqueField($fieldName) + { + $mapping = $this->getFieldMapping($fieldName); + if ($mapping !== false) { + return isset($mapping['unique']) && $mapping['unique'] == true; + } + return false; + } + + /** + * Check if the field is not null. + * + * @param string $fieldName The field name + * @return boolean TRUE if the field is not null, FALSE otherwise. + */ + public function isNullable($fieldName) + { + $mapping = $this->getFieldMapping($fieldName); + if ($mapping !== false) { + return isset($mapping['nullable']) && $mapping['nullable'] == true; + } + return false; + } + + /** + * Gets a column name for a field name. + * If the column name for the field cannot be found, the given field name + * is returned. + * + * @param string $fieldName The field name. + * @return string The column name. + */ + public function getColumnName($fieldName) + { + return isset($this->columnNames[$fieldName]) ? + $this->columnNames[$fieldName] : $fieldName; + } + + /** + * Gets the mapping of a (regular) field that holds some data but not a + * reference to another object. + * + * @param string $fieldName The field name. + * @return array The field mapping. + */ + public function getFieldMapping($fieldName) + { + if ( ! isset($this->fieldMappings[$fieldName])) { + throw MappingException::mappingNotFound($fieldName); + } + return $this->fieldMappings[$fieldName]; + } + + /** + * Gets the mapping of an association. + * + * @param string $fieldName The field name that represents the association in + * the object model. + * @return Doctrine\ORM\Mapping\AssociationMapping The mapping. + */ + public function getAssociationMapping($fieldName) + { + if ( ! isset($this->associationMappings[$fieldName])) { + throw MappingException::mappingNotFound($fieldName); + } + return $this->associationMappings[$fieldName]; + } + + /** + * Gets the inverse association mapping for the given target class name and + * owning fieldname. + * + * @param string $mappedByFieldName The field on the + * @return Doctrine\ORM\Mapping\AssociationMapping The mapping or NULL if there is no such + * inverse association mapping. + */ + public function getInverseAssociationMapping($targetClassName, $mappedByFieldName) + { + return isset($this->inverseMappings[$targetClassName][$mappedByFieldName]) ? + $this->inverseMappings[$targetClassName][$mappedByFieldName] : null; + } + + /** + * Checks whether the class has an inverse association mapping that points to the + * specified class and ha the specified mappedBy field. + * + * @param string $targetClassName The name of the target class. + * @param string $mappedByFieldName The name of the mappedBy field that points to the field on + * the target class that owns the association. + * @return boolean + */ + public function hasInverseAssociationMapping($targetClassName, $mappedByFieldName) + { + return isset($this->inverseMappings[$targetClassName][$mappedByFieldName]); + } + + /** + * Gets all association mappings of the class. + * + * @return array + */ + public function getAssociationMappings() + { + return $this->associationMappings; + } + + /** + * Gets all association mappings of the class. + * + * Alias for getAssociationMappings(). + * + * @return array + */ + public function getAssociations() + { + return $this->associationMappings; + } + + /** + * Gets the field name for a column name. + * If no field name can be found the column name is returned. + * + * @param string $columnName column name + * @return string column alias + */ + public function getFieldName($columnName) + { + return isset($this->fieldNames[$columnName]) ? + $this->fieldNames[$columnName] : $columnName; + } + + /** + * Validates & completes the given field mapping. + * + * @param array $mapping The field mapping to validated & complete. + * @return array The validated and completed field mapping. + */ + protected function _validateAndCompleteFieldMapping(array &$mapping) + { + // Check mandatory fields + if ( ! isset($mapping['fieldName'])) { + throw MappingException::missingFieldName(); + } + if ( ! isset($mapping['type'])) { + throw MappingException::missingType(); + } + + // Complete fieldName and columnName mapping + if ( ! isset($mapping['columnName'])) { + $mapping['columnName'] = $mapping['fieldName']; + } else { + if ($mapping['columnName'][0] == '`') { + $mapping['columnName'] = trim($mapping['columnName'], '`'); + $mapping['quoted'] = true; + } + } + + $this->columnNames[$mapping['fieldName']] = $mapping['columnName']; + $this->fieldNames[$mapping['columnName']] = $mapping['fieldName']; + + // Complete id mapping + if (isset($mapping['id']) && $mapping['id'] === true) { + if ( ! in_array($mapping['fieldName'], $this->identifier)) { + $this->identifier[] = $mapping['fieldName']; + } + // Check for composite key + if ( ! $this->isIdentifierComposite && count($this->identifier) > 1) { + $this->isIdentifierComposite = true; + } + } + } + + /** + * Maps an embedded value object. + * + * @todo Implementation. + */ + /*public function mapEmbeddedValue() + { + //... + }*/ + + /** + * Gets the identifier (primary key) field names of the class. + * + * @return mixed + * @deprecated Use getIdentifierFieldNames() + */ + public function getIdentifier() + { + return $this->identifier; + } + + /** + * Gets the identifier (primary key) field names of the class. + * + * @return mixed + */ + public function getIdentifierFieldNames() + { + return $this->identifier; + } + + /** + * Gets the name of the single id field. Note that this only works on + * entity classes that have a single-field pk. + * + * @return string + */ + public function getSingleIdentifierFieldName() + { + if ($this->isIdentifierComposite) { + throw DoctrineException::singleIdNotAllowedOnCompositePrimaryKey(); + } + return $this->identifier[0]; + } + + /** + * Gets the column name of the single id column. Note that this only works on + * entity classes that have a single-field pk. + * + * @return string + */ + public function getSingleIdentifierColumnName() + { + return $this->getColumnName($this->getSingleIdentifierFieldName()); + } + + /** + * INTERNAL: + * Sets the mapped identifier/primary key fields of this class. + * Mainly used by the ClassMetadataFactory to assign inherited identifiers. + * + * @param array $identifier + */ + public function setIdentifier(array $identifier) + { + $this->identifier = $identifier; + } + + /** + * Checks whether the class has a (mapped) field with a certain name. + * + * @return boolean + */ + public function hasField($fieldName) + { + return isset($this->fieldMappings[$fieldName]); + } + + /** + * Gets all field mappings. + * + * @return array + */ + public function getFieldMappings() + { + return $this->fieldMappings; + } + + /** + * Gets an array containing all the column names. + * + * @return array + */ + public function getColumnNames(array $fieldNames = null) + { + if ($fieldNames === null) { + return array_keys($this->fieldNames); + } else { + $columnNames = array(); + foreach ($fieldNames as $fieldName) { + $columnNames[] = $this->getColumnName($fieldName); + } + return $columnNames; + } + } + + /** + * Returns an array with all the identifier column names. + * + * @return array + */ + public function getIdentifierColumnNames() + { + if ($this->isIdentifierComposite) { + $columnNames = array(); + foreach ($this->identifier as $idField) { + $columnNames[] = $this->fieldMappings[$idField]['columnName']; + } + return $columnNames; + } else { + return array($this->fieldMappings[$this->identifier[0]]['columnName']); + } + } + + /** + * Returns an array containing all the field names. + * + * @return array + */ + public function getFieldNames() + { + return array_values($this->fieldNames); + } + + /** + * Gets the Id generator type used by the class. + * + * @return string + */ + public function getIdGeneratorType() + { + return $this->generatorType; + } + + /** + * Sets the type of Id generator to use for the mapped class. + */ + public function setIdGeneratorType($generatorType) + { + $this->generatorType = $generatorType; + } + + /** + * Checks whether the mapped class uses an Id generator. + * + * @return boolean TRUE if the mapped class uses an Id generator, FALSE otherwise. + */ + public function usesIdGenerator() + { + return $this->generatorType != self::GENERATOR_TYPE_NONE; + } + + /** + * + * @return boolean + */ + public function isInheritanceTypeNone() + { + return $this->inheritanceType == self::INHERITANCE_TYPE_NONE; + } + + /** + * Checks whether the mapped class uses the JOINED inheritance mapping strategy. + * + * @return boolean TRUE if the class participates in a JOINED inheritance mapping, + * FALSE otherwise. + */ + public function isInheritanceTypeJoined() + { + return $this->inheritanceType == self::INHERITANCE_TYPE_JOINED; + } + + /** + * Checks whether the mapped class uses the SINGLE_TABLE inheritance mapping strategy. + * + * @return boolean TRUE if the class participates in a SINGLE_TABLE inheritance mapping, + * FALSE otherwise. + */ + public function isInheritanceTypeSingleTable() + { + return $this->inheritanceType == self::INHERITANCE_TYPE_SINGLE_TABLE; + } + + /** + * Checks whether the mapped class uses the TABLE_PER_CLASS inheritance mapping strategy. + * + * @return boolean TRUE if the class participates in a TABLE_PER_CLASS inheritance mapping, + * FALSE otherwise. + */ + public function isInheritanceTypeTablePerClass() + { + return $this->inheritanceType == self::INHERITANCE_TYPE_TABLE_PER_CLASS; + } + + /** + * Checks whether the class uses an identity column for the Id generation. + * + * @return boolean TRUE if the class uses the IDENTITY generator, FALSE otherwise. + */ + public function isIdGeneratorIdentity() + { + return $this->generatorType == self::GENERATOR_TYPE_IDENTITY; + } + + /** + * Checks whether the class uses a sequence for id generation. + * + * @return boolean TRUE if the class uses the SEQUENCE generator, FALSE otherwise. + */ + public function isIdGeneratorSequence() + { + return $this->generatorType == self::GENERATOR_TYPE_SEQUENCE; + } + + /** + * Checks whether the class uses a table for id generation. + * + * @return boolean TRUE if the class uses the TABLE generator, FALSE otherwise. + */ + public function isIdGeneratorTable() + { + $this->generatorType == self::GENERATOR_TYPE_TABLE; + } + + /** + * Checks whether the class has a natural identifier/pk (which means it does + * not use any Id generator. + * + * @return boolean + */ + public function isIdentifierNatural() + { + return $this->generatorType == self::GENERATOR_TYPE_NONE; + } + + /** + * Gets the type of a field. + * + * @param string $fieldName + * @return Doctrine\DBAL\Types\Type + */ + public function getTypeOfField($fieldName) + { + return isset($this->fieldMappings[$fieldName]) ? + $this->fieldMappings[$fieldName]['type'] : null; + } + + /** + * Gets the type of a column. + * + * @return Doctrine\DBAL\Types\Type + */ + public function getTypeOfColumn($columnName) + { + return $this->getTypeOfField($this->getFieldName($columnName)); + } + + /** + * Gets the name of the primary table. + * + * @return string + */ + public function getTableName() + { + return $this->primaryTable['name']; + } + + /** + * Gets the table name to use for temporary identifier tables of this class. + * + * @return string + */ + public function getTemporaryIdTableName() + { + return $this->primaryTable['name'] . '_id_tmp'; + } + + /** + * Gets the inheritance mapping type used by the mapped class. + * + * @return string + */ + public function getInheritanceType() + { + return $this->inheritanceType; + } + + /** + * Sets the mapped subclasses of this class. + * + * @param array $subclasses The names of all mapped subclasses. + */ + public function setSubclasses(array $subclasses) + { + foreach ($subclasses as $subclass) { + if (strpos($subclass, '\\') === false) { + $this->subClasses[] = $this->namespace . '\\' . $subclass; + } else { + $this->subClasses[] = $subclass; + } + } + } + + /** + * Gets the names of all subclasses. + * + * @return array The names of all subclasses. + */ + public function getSubclasses() + { + return $this->subClasses; + } + + /** + * Checks whether the class has any persistent subclasses. + * + * @return boolean TRUE if the class has one or more persistent subclasses, FALSE otherwise. + */ + public function hasSubclasses() + { + return ! $this->subClasses; + } + + /** + * Gets the names of all parent classes. + * + * @return array The names of all parent classes. + */ + public function getParentClasses() + { + return $this->parentClasses; + } + + /** + * Sets the parent class names. + * Assumes that the class names in the passed array are in the order: + * directParent -> directParentParent -> directParentParentParent ... -> root. + */ + public function setParentClasses(array $classNames) + { + $this->parentClasses = $classNames; + if (count($classNames) > 0) { + $this->rootEntityName = array_pop($classNames); + } + } + + /** + * Checks whether the class has any persistent parent classes. + * + * @return boolean TRUE if the class has one or more persistent parent classes, FALSE otherwise. + */ + public function hasParentClasses() + { + return ! $this->parentClasses; + } + + /** + * Sets the inheritance type used by the class and it's subclasses. + * + * @param integer $type + */ + public function setInheritanceType($type) + { + if ( ! $this->_isInheritanceType($type)) { + throw MappingException::invalidInheritanceType($type); + } + $this->inheritanceType = $type; + } + + /** + * Checks whether a mapped field is inherited from a superclass. + * + * @return boolean TRUE if the field is inherited, FALSE otherwise. + */ + public function isInheritedField($fieldName) + { + return isset($this->fieldMappings[$fieldName]['inherited']); + } + + /** + * Sets the name of the primary table the class is mapped to. + * + * @param string $tableName The table name. + * @deprecated + */ + public function setTableName($tableName) + { + $this->primaryTable['name'] = $tableName; + } + + /** + * Sets the primary table definition. The provided array must have the + * following structure: + * + * name => + * schema => + * catalog => + * + * @param array $primaryTableDefinition + */ + public function setPrimaryTable(array $primaryTableDefinition) + { + $this->primaryTable = $primaryTableDefinition; + } + + /** + * Gets the primary table definition. + * + * @see setPrimaryTable() + * @return array + */ + public function getPrimaryTable() + { + return $this->primaryTable; + } + + /** + * Checks whether the given type identifies an inheritance type. + * + * @param string $type + * @return boolean + */ + private function _isInheritanceType($type) + { + return $type == self::INHERITANCE_TYPE_NONE || + $type == self::INHERITANCE_TYPE_SINGLE_TABLE || + $type == self::INHERITANCE_TYPE_JOINED || + $type == self::INHERITANCE_TYPE_TABLE_PER_CLASS; + } + + /** + * Checks whether the given type identifies an id generator type. + * + * @param string $type + * @return boolean + */ + private function _isIdGeneratorType($type) + { + return $type == self::GENERATOR_TYPE_AUTO || + $type == self::GENERATOR_TYPE_IDENTITY || + $type == self::GENERATOR_TYPE_SEQUENCE || + $type == self::GENERATOR_TYPE_TABLE || + $type == self::GENERATOR_TYPE_NONE; + } + + /** + * Makes some automatic additions to the association mapping to make the life + * easier for the user, and store join columns in the metadata. + * + * @param array $mapping + * @todo Pass param by ref? + */ + private function _completeAssociationMapping(array $mapping) + { + $mapping['sourceEntity'] = $this->name; + if (isset($mapping['targetEntity']) && strpos($mapping['targetEntity'], '\\') === false) { + $mapping['targetEntity'] = $this->namespace . '\\' . $mapping['targetEntity']; + } + return $mapping; + } + + /** + * Adds a field mapping. + * + * @param array $mapping The field mapping. + */ + public function mapField(array $mapping) + { + $this->_validateAndCompleteFieldMapping($mapping); + if (isset($this->fieldMappings[$mapping['fieldName']])) { + throw MappingException::duplicateFieldMapping($mapping['fieldName']); + } + $this->fieldMappings[$mapping['fieldName']] = $mapping; + } + + /** + * INTERNAL: + * Adds an association mapping without completing/validating it. + * This is mainly used to add inherited association mappings to derived classes. + * + * @param AssociationMapping $mapping + * @param string $owningClassName The name of the class that defined this mapping. + * @todo Rename: addInheritedAssociationMapping + */ + public function addAssociationMapping(AssociationMapping $mapping, $owningClassName = null) + { + $sourceFieldName = $mapping->sourceFieldName; + if (isset($this->associationMappings[$sourceFieldName])) { + throw MappingException::duplicateFieldMapping(); + } + $this->associationMappings[$sourceFieldName] = $mapping; + if ($owningClassName !== null) { + $this->inheritedAssociationFields[$sourceFieldName] = $owningClassName; + } + $this->_registerMappingIfInverse($mapping); + } + + /** + * INTERNAL: + * Adds a field mapping without completing/validating it. + * This is mainly used to add inherited field mappings to derived classes. + * + * @param array $mapping + * @todo Rename: addInheritedFieldMapping + */ + public function addFieldMapping(array $fieldMapping) + { + $this->fieldMappings[$fieldMapping['fieldName']] = $fieldMapping; + $this->columnNames[$fieldMapping['fieldName']] = $fieldMapping['columnName']; + $this->fieldNames[$fieldMapping['columnName']] = $fieldMapping['fieldName']; + } + + /** + * Adds a one-to-one mapping. + * + * @param array $mapping The mapping. + */ + public function mapOneToOne(array $mapping) + { + $mapping = $this->_completeAssociationMapping($mapping); + $oneToOneMapping = new OneToOneMapping($mapping); + $this->_storeAssociationMapping($oneToOneMapping); + } + + /** + * Registers the mapping as an inverse mapping, if it is a mapping on the + * inverse side of an association mapping. + * + * @param AssociationMapping The mapping to register as inverse if it is a mapping + * for the inverse side of an association. + */ + private function _registerMappingIfInverse(AssociationMapping $assoc) + { + if ($assoc->isInverseSide()) { + $this->inverseMappings[$assoc->targetEntityName][$assoc->mappedByFieldName] = $assoc; + } + } + + /** + * Adds a one-to-many mapping. + * + * @param array $mapping The mapping. + */ + public function mapOneToMany(array $mapping) + { + $mapping = $this->_completeAssociationMapping($mapping); + $oneToManyMapping = new OneToManyMapping($mapping); + $this->_storeAssociationMapping($oneToManyMapping); + } + + /** + * Adds a many-to-one mapping. + * + * @param array $mapping The mapping. + */ + public function mapManyToOne(array $mapping) + { + // A many-to-one mapping is simply a one-one backreference + $this->mapOneToOne($mapping); + } + + /** + * Adds a many-to-many mapping. + * + * @param array $mapping The mapping. + */ + public function mapManyToMany(array $mapping) + { + $mapping = $this->_completeAssociationMapping($mapping); + $manyToManyMapping = new ManyToManyMapping($mapping); + $this->_storeAssociationMapping($manyToManyMapping); + } + + /** + * Stores the association mapping. + * + * @param AssociationMapping $assocMapping + */ + protected function _storeAssociationMapping(AssociationMapping $assocMapping) + { + $sourceFieldName = $assocMapping->sourceFieldName; + if (isset($this->associationMappings[$sourceFieldName])) { + throw MappingException::duplicateFieldMapping(); + } + $this->associationMappings[$sourceFieldName] = $assocMapping; + $this->_registerMappingIfInverse($assocMapping); + } + + /** + * Registers a custom repository class for the entity class. + * + * @param string $mapperClassName The class name of the custom mapper. + */ + public function setCustomRepositoryClass($repositoryClassName) + { + $this->customRepositoryClassName = $repositoryClassName; + } + + /** + * Gets the name of the custom repository class used for the entity class. + * + * @return string|null The name of the custom repository class or NULL if the entity + * class does not have a custom repository class. + */ + public function getCustomRepositoryClass() + { + return $this->customRepositoryClassName; + } + + /** + * Sets whether sub classes should be automatically OUTER JOINed when a base + * class is queried in a class hierarchy that uses the JOINED inheritance mapping + * strategy. + * + * This options does only apply to the JOINED inheritance mapping strategy. + * + * @param boolean $bool + * @see getJoinSubClasses() + */ + /*public function setJoinSubClasses($bool) + { + $this->joinSubclasses = (bool)$bool; + }*/ + + /** + * Gets whether the class mapped by this instance should OUTER JOIN sub classes + * when a base class is queried. + * + * @return + * @see setJoinSubClasses() + */ + /*public function getJoinSubClasses() + { + return $this->joinSubclasses; + }*/ + + /** + * Dispatches the lifecycle event of the given entity to the registered + * lifecycle callbacks and lifecycle listeners. + * + * @param string $event The lifecycle event. + * @param Entity $entity The Entity on which the event occured. + */ + public function invokeLifecycleCallbacks($lifecycleEvent, $entity) + { + foreach ($this->lifecycleCallbacks[$lifecycleEvent] as $callback) { + $entity->$callback(); + } + } + + /** + * Whether the class has any attached lifecycle listeners or callbacks for a lifecycle event. + * + * @param string $lifecycleEvent + * @return boolean + */ + public function hasLifecycleCallbacks($lifecycleEvent) + { + return isset($this->lifecycleCallbacks[$lifecycleEvent]); + } + + /** + * Gets the registered lifecycle callbacks for an event. + * + * @param string $event + * @return array + */ + public function getLifecycleCallbacks($event) + { + return isset($this->lifecycleCallbacks[$event]) ? $this->lifecycleCallbacks[$event] : array(); + } + + /** + * Adds a lifecycle callback for entities of this class. + * + * Note: If the same callback is registered more than once, the old one + * will be overridden. + * + * @param string $callback + * @param string $event + */ + public function addLifecycleCallback($callback, $event) + { + $this->lifecycleCallbacks[$event][] = $callback; + } + + /** + * Sets the discriminator column definition. + * + * @param array $columnDef + * @see getDiscriminatorColumn() + */ + public function setDiscriminatorColumn($columnDef) + { + if ($columnDef !== null) { + if ( ! isset($columnDef['name'])) { + throw new MappingException("'name' attribute is mandatory for discriminator columns."); + } + if ( ! isset($columnDef['fieldName'])) { + $columnDef['fieldName'] = $columnDef['name']; + } + $this->discriminatorColumn = $columnDef; + } + } + + /** + * Gets the discriminator column definition. + * + * The discriminator column definition is an array with the following keys: + * name: The name of the column + * type: The type of the column (only integer and string supported) + * length: The length of the column (applies only if type is string) + * + * A discriminator column is used for JOINED and SINGLE_TABLE inheritance mappings. + * + * @return array + * @see setDiscriminatorColumn() + */ + public function getDiscriminatorColumn() + { + return $this->discriminatorColumn; + } + + /** + * Sets the discriminator values used by this class. + * Used for JOINED and SINGLE_TABLE inheritance mapping strategies. + * + * @param array $map + */ + public function setDiscriminatorMap(array $map) + { + foreach ($map as $value => $className) { + if (strpos($className, '\\') === false) { + $className = $this->namespace . '\\' . $className; + } + $this->discriminatorMap[$value] = $className; + if ($this->name == $className) { + $this->discriminatorValue = $value; + } else if (is_subclass_of($className, $this->name)) { + $this->subClasses[] = $className; + } + } + } + + /** + * Gets the discriminator value of this class. + * Used for JOINED and SINGLE_TABLE inheritance mapping strategies. + * + * @return array + */ + public function getDiscriminatorValue() + { + return $this->discriminatorValue; + } + + /** + * Checks whether the given column name is the discriminator column. + * + * @param string $columnName + * @return boolean + */ + public function isDiscriminatorColumn($columnName) + { + return $columnName === $this->discriminatorColumn['name']; + } + + /** + * Checks whether the class has a mapped association with the given field name. + * + * @param string $fieldName + * @return boolean + */ + public function hasAssociation($fieldName) + { + return isset($this->associationMappings[$fieldName]); + } + + /** + * Checks whether the class has a mapped association for the specified field + * and if yes, checks whether it is a single-valued association (to-one). + * + * @param string $fieldName + * @return boolean TRUE if the association exists and is single-valued, FALSE otherwise. + */ + public function isSingleValuedAssociation($fieldName) + { + return isset($this->associationMappings[$fieldName]) && + $this->associationMappings[$fieldName]->isOneToOne(); + } + + /** + * Checks whether the class has a mapped association for the specified field + * and if yes, checks whether it is a collection-valued association (to-many). + * + * @param string $fieldName + * @return boolean TRUE if the association exists and is collection-valued, FALSE otherwise. + */ + public function isCollectionValuedAssociation($fieldName) + { + return isset($this->associationMappings[$fieldName]) && + ! $this->associationMappings[$fieldName]->isOneToOne(); + } + + /** + * Gets the name of the ID generator used for this class. + * Only classes that use a SEQUENCE or TABLE ID generation strategy have a generator name. + * + * @return string|null The name of the ID generator or NULL if this class does not + * use a named ID generator. + */ + /*public function getIdGeneratorName() + { + return $this->idGeneratorName; + }*/ + + /** + * Sets the ID generator used to generate IDs for instances of this class. + * + * @param AbstractIdGenerator $generator + */ + public function setIdGenerator($generator) + { + $this->idGenerator = $generator; + } + + /** + * Gets the ID generator used to generate IDs for instances of this class. + * + * @return AbstractIdGenerator + */ + public function getIdGenerator() + { + return $this->idGenerator; + } + + /** + * Gets the definition of the sequence ID generator for this class. + * + * The definition has the following structure: + * + * array( + * 'sequenceName' => 'name', + * 'allocationSize' => 20, + * 'initialValue' => 1 + * ) + * + * + * @return array|null An array with the generator definition or NULL if this class + * has no sequence generator definition. + */ + public function getSequenceGeneratorDefinition() + { + return $this->sequenceGeneratorDefinition; + } + + /** + * Sets the definition of the sequence ID generator for this class. + * + * The definition must have the following structure: + * + * array( + * 'sequenceName' => 'name', + * 'allocationSize' => 20, + * 'initialValue' => 1 + * ) + * + * + * @param array $definition + */ + public function setSequenceGeneratorDefinition(array $definition) + { + $this->sequenceGeneratorDefinition = $definition; + } + + /** + * Sets the version field mapping used for versioning. Sets the default + * value to use depending on the column type + * + * @param array $mapping The version field mapping array + * @return void + */ + public function setVersionMapping(array &$mapping) + { + $this->isVersioned = true; + $this->versionField = $mapping['fieldName']; + + if ( ! isset($mapping['default'])) { + if ($mapping['type'] == 'integer') { + $mapping['default'] = 1; + } else if ($mapping['type'] == 'datetime') { + $mapping['default'] = 'CURRENT_TIMESTAMP'; + } else { + throw DoctrineException::unsupportedOptimisticLockingType($mapping['type']); + } + } + } + + /** + * Checks whether this class is versioned for optimistic locking. + * + * @return boolean TRUE if this class is versioned for optimistic locking, FALSE otherwise. + */ + public function isVersioned() + { + return $this->isVersioned; + } + + /** + * Sets whether this class is to be versioned for optimistic locking. + * + * @param boolean $bool + */ + public function setVersioned($bool) + { + $this->isVersioned = $bool; + } + + /** + * Gets the name of the field that is used for versioning if this class is versioned + * for optimistic locking. + * + * @return string + */ + public function getVersionField() + { + return $this->versionField; + } + + /** + * Sets the name of the field that is to be used for versioning if this class is + * versioned for optimistic locking. + * + * @param string $versionField + */ + public function setVersionField($versionField) + { + $this->versionField = $versionField; + } +} \ No newline at end of file diff --git a/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php b/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php index 642619120..de8de8eed 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php @@ -24,7 +24,7 @@ namespace Doctrine\ORM\Mapping\Driver; use Doctrine\Common\DoctrineException, Doctrine\Common\Cache\ArrayCache, Doctrine\Common\Annotations\AnnotationReader, - Doctrine\ORM\Mapping\ClassMetadata, + Doctrine\ORM\Mapping\ClassMetadataInfo, Doctrine\ORM\Mapping\MappingException; require __DIR__ . '/DoctrineAnnotations.php'; @@ -59,7 +59,7 @@ class AnnotationDriver implements Driver /** * {@inheritdoc} */ - public function loadMetadataForClass($className, ClassMetadata $metadata) + public function loadMetadataForClass($className, ClassMetadataInfo $metadata) { $class = $metadata->getReflectionClass(); diff --git a/lib/Doctrine/ORM/Mapping/Driver/Driver.php b/lib/Doctrine/ORM/Mapping/Driver/Driver.php index ce0cf2388..6b6dd8554 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/Driver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/Driver.php @@ -21,7 +21,7 @@ namespace Doctrine\ORM\Mapping\Driver; -use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Mapping\ClassMetadataInfo; /** * Contract for metadata drivers. @@ -38,9 +38,9 @@ interface Driver * Loads the metadata for the specified class into the provided container. * * @param string $className - * @param ClassMetadata $metadata + * @param ClassMetadataInfo $metadata */ - public function loadMetadataForClass($className, ClassMetadata $metadata); + public function loadMetadataForClass($className, ClassMetadataInfo $metadata); /** * Whether the class with the specified name should have its metadata loaded. diff --git a/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php b/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php index 7104628c8..103456e2d 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php @@ -21,7 +21,7 @@ namespace Doctrine\ORM\Mapping\Driver; -use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Mapping\ClassMetadataInfo; /** * XmlDriver is a metadata driver that enables mapping through XML files. @@ -44,10 +44,8 @@ class XmlDriver extends AbstractFileDriver * @param string $className * @param ClassMetadata $metadata */ - public function loadMetadataForClass($className, ClassMetadata $metadata) + public function loadMetadataForClass($className, ClassMetadataInfo $metadata) { - $class = $metadata->getReflectionClass(); - $xmlRoot = $this->getElement($className); if ($xmlRoot->getName() == 'entity') { @@ -327,11 +325,7 @@ class XmlDriver extends AbstractFileDriver // Evaluate if (isset($xmlRoot->{'lifecycle-callbacks'})) { foreach ($xmlRoot->{'lifecycle-callbacks'}->{'lifecycle-callback'} as $lifecycleCallback) { - $method = $class->getMethod((string)$lifecycleCallback['method']); - - if ($method->isPublic()) { - $metadata->addLifecycleCallback($method->getName(), constant('\Doctrine\ORM\Events::' . (string)$lifecycleCallback['type'])); - } + $metadata->addLifecycleCallback((string)$lifecycleCallback['method'], constant('\Doctrine\ORM\Events::' . (string)$lifecycleCallback['type'])); } } } diff --git a/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php b/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php index e54c591e8..e29aaac94 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php @@ -21,7 +21,7 @@ namespace Doctrine\ORM\Mapping\Driver; -use Doctrine\ORM\Mapping\ClassMetadata, +use Doctrine\ORM\Mapping\ClassMetadataInfo, Doctrine\Common\DoctrineException, Doctrine\ORM\Mapping\MappingException; @@ -47,10 +47,8 @@ class YamlDriver extends AbstractFileDriver { protected $_fileExtension = '.dcm.yml'; - public function loadMetadataForClass($className, ClassMetadata $metadata) + public function loadMetadataForClass($className, ClassMetadataInfo $metadata) { - $class = $metadata->getReflectionClass(); - $element = $this->getElement($className); if ($element['type'] == 'entity') { @@ -320,11 +318,7 @@ class YamlDriver extends AbstractFileDriver // Evaluate lifeCycleCallbacks if (isset($element['lifecycleCallbacks'])) { foreach ($element['lifecycleCallbacks'] as $method => $type) { - $method = $class->getMethod($method); - - if ($method->isPublic()) { - $metadata->addLifecycleCallback($method->getName(), constant('\Doctrine\ORM\Events::' . $type)); - } + $metadata->addLifecycleCallback($method, constant('\Doctrine\ORM\Events::' . $type)); } } } diff --git a/lib/Doctrine/ORM/Tools/Cli/Tasks/AbstractTask.php b/lib/Doctrine/ORM/Tools/Cli/Tasks/AbstractTask.php index 25c233c01..9e0413ad4 100644 --- a/lib/Doctrine/ORM/Tools/Cli/Tasks/AbstractTask.php +++ b/lib/Doctrine/ORM/Tools/Cli/Tasks/AbstractTask.php @@ -164,9 +164,6 @@ abstract class AbstractTask if ( ! isset($this->_arguments['config'])) { if (file_exists('./cli-config.php')) { require './cli-config.php'; - } else { - $this->_printer->writeln('--config option or cli-config.php in the same directory required', 'ERROR'); - return false; } } else { require $this->_arguments['config']; diff --git a/lib/Doctrine/ORM/Tools/Cli/Tasks/ConvertMappingTask.php b/lib/Doctrine/ORM/Tools/Cli/Tasks/ConvertMappingTask.php index 2c10a988e..ee9c1ac66 100644 --- a/lib/Doctrine/ORM/Tools/Cli/Tasks/ConvertMappingTask.php +++ b/lib/Doctrine/ORM/Tools/Cli/Tasks/ConvertMappingTask.php @@ -21,7 +21,7 @@ namespace Doctrine\ORM\Tools\Cli\Tasks; -use Doctrine\ORM\Tools\Export\ClassmetadataExporter; +use Doctrine\ORM\Tools\Export\ClassMetadataExporter; /** * CLI Task to convert your mapping information between the various formats @@ -86,12 +86,14 @@ class ConvertMappingTask extends AbstractTask $args = $this->getArguments(); $printer = $this->getPrinter(); - if (!(isset($args['from']) && isset($args['to']) && isset($args['dest']))) - { + if (!(isset($args['from']) && isset($args['to']) && isset($args['dest']))) { $printer->writeln('You must include a value for all four options: --from, --to and --dest', 'ERROR'); return false; } - + if ($args['to'] != 'annotation' && $args['extend']) { + $printer->writeln('You can only use the --extend argument when converting to annoations.'); + return false; + } return true; } @@ -100,7 +102,7 @@ class ConvertMappingTask extends AbstractTask $printer = $this->getPrinter(); $args = $this->getArguments(); - $cme = new ClassmetadataExporter(); + $cme = new ClassMetadataExporter(); $from = (array) $args['from']; foreach ($from as $path) { $type = $this->_determinePathType($path); @@ -111,6 +113,12 @@ class ConvertMappingTask extends AbstractTask } $exporter = $cme->getExporter($args['to']); + if (isset($args['extend'])) { + $exporter->setClassToExtend($args['extend']); + } + if (isset($args['num-spaces'])) { + $exporter->setNumSpaces($args['num-spaces']); + } $exporter->setOutputDir($args['dest']); $printer->writeln(sprintf('Exporting %s mapping information to directory: "%s"', $args['to'], $args['dest']), 'INFO'); diff --git a/lib/Doctrine/ORM/Tools/Export/ClassmetadataExporter.php b/lib/Doctrine/ORM/Tools/Export/ClassmetadataExporter.php deleted file mode 100644 index 6401a2e2e..000000000 --- a/lib/Doctrine/ORM/Tools/Export/ClassmetadataExporter.php +++ /dev/null @@ -1,155 +0,0 @@ -. - */ - -namespace Doctrine\ORM\Tools\Export; - -use Doctrine\ORM\EntityManager; -use Doctrine\ORM\Mapping\Classmetadata; - -/** - * Class used for converting your mapping information between the - * supported formats: yaml, xml, and php/annotation. - * - * [php] - * // Unify all your mapping information which is written in php, xml, yml - * // and convert it to a single set of yaml files. - * - * $cme = new Doctrine\ORM\Tools\Export\ClassmetadataExporter(); - * $cme->addMappingDir(__DIR__ . '/Entities', 'php'); - * $cme->addMappingDir(__DIR__ . '/xml', 'xml'); - * $cme->addMappingDir(__DIR__ . '/yaml', 'yaml'); - * - * $exporter = $cme->getExporter('yaml'); - * $exporter->setOutputDir(__DIR__ . '/new_yaml'); - * $exporter->export(); - * - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL - * @link www.doctrine-project.org - * @since 2.0 - * @version $Revision$ - * @author Jonathan Wage - */ -class ClassmetadataExporter -{ - private $_exporterDrivers = array( - 'xml' => 'Doctrine\ORM\Tools\Export\Driver\XmlExporter', - 'yaml' => 'Doctrine\ORM\Tools\Export\Driver\YamlExporter', - 'yml' => 'Doctrine\ORM\Tools\Export\Driver\YamlExporter', - 'php' => 'Doctrine\ORM\Tools\Export\Driver\PhpExporter', - 'annotation' => 'Doctrine\ORM\Tools\Export\Driver\AnnotationExporter' - ); - - private $_mappingDrivers = array( - 'annotation' => 'Doctrine\ORM\Mapping\Driver\AnnotationDriver', - 'yaml' => 'Doctrine\ORM\Mapping\Driver\YamlDriver', - 'yml' => 'Doctrine\ORM\Mapping\Driver\YamlDriver', - 'xml' => 'Doctrine\ORM\Mapping\Driver\XmlDriver' - ); - - private $_mappingDirectories = array(); - - public function addMappingDir($dir, $type) - { - if ($type === 'php') { - $this->_mappingDirectories[] = array($dir, $type); - } else { - if ( ! isset($this->_mappingDrivers[$type])) { - throw DoctrineException::invalidMappingDriverType($type); - } - - $class = $this->_mappingDrivers[$type]; - if (is_subclass_of($class, 'Doctrine\ORM\Mapping\Driver\AbstractFileDriver')) { - $driver = new $class($dir, constant($class . '::PRELOAD')); - } else { - $reader = new \Doctrine\Common\Annotations\AnnotationReader(new \Doctrine\Common\Cache\ArrayCache); - $reader->setDefaultAnnotationNamespace('Doctrine\ORM\Mapping\\'); - $driver = new $class($reader); - } - $this->_mappingDirectories[] = array($dir, $driver); - } - } - - private function _getMetadataInstances() - { - $classes = array(); - - foreach ($this->_mappingDirectories as $d) { - list($dir, $driver) = $d; - if ($driver == 'php') { - $iter = new \FilesystemIterator($dir); - - foreach ($iter as $item) { - include $item->getPathName(); - $vars = get_defined_vars(); - foreach ($vars as $var) { - if ($var instanceof \Doctrine\ORM\Mapping\ClassMetadata) { - $classes[] = $var; - } - } - } - $classes = array_unique($classes); - $classes = array_values($classes); - } else { - if ($driver instanceof \Doctrine\ORM\Mapping\Driver\AnnotationDriver) { - $iter = new \FilesystemIterator($dir); - - $declared = get_declared_classes(); - foreach ($iter as $item) { - $baseName = $item->getBaseName(); - if ($baseName[0] == '.') { - continue; - } - require_once $item->getPathName(); - } - $declared = array_diff(get_declared_classes(), $declared); - - foreach ($declared as $className) { - if ( ! $driver->isTransient($className)) { - $metadata = new ClassMetadata($className); - $driver->loadMetadataForClass($className, $metadata); - $classes[] = $metadata; - } - } - } else { - $preloadedClasses = $driver->preload(true); - foreach ($preloadedClasses as $className) { - $metadata = new ClassMetadata($className); - $driver->loadMetadataForClass($className, $metadata); - $classes[] = $metadata; - } - } - } - } - - return $classes; - } - - public function getExporter($type, $dir = null) - { - if ( ! isset($this->_exporterDrivers[$type])) { - throw DoctrineException::invalidExporterDriverType($type); - } - - $class = $this->_exporterDrivers[$type]; - return new $class($this->_getMetadataInstances()); - } -} \ No newline at end of file diff --git a/lib/Doctrine/ORM/Tools/Export/Driver/AbstractExporter.php b/lib/Doctrine/ORM/Tools/Export/Driver/AbstractExporter.php index 5373e56af..669ea01f0 100644 --- a/lib/Doctrine/ORM/Tools/Export/Driver/AbstractExporter.php +++ b/lib/Doctrine/ORM/Tools/Export/Driver/AbstractExporter.php @@ -22,7 +22,7 @@ namespace Doctrine\ORM\Tools\Export\Driver; -use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Mapping\ClassMetadataInfo; /** * Abstract base class which is to be used for the Exporter drivers @@ -101,28 +101,28 @@ abstract class AbstractExporter * Converts a single ClassMetadata instance to the exported format * and returns it * - * @param ClassMetadata $metadata + * @param ClassMetadataInfo $metadata * @return mixed $exported */ - abstract public function exportClassMetadata(ClassMetadata $metadata); + abstract public function exportClassMetadata(ClassMetadataInfo $metadata); protected function _getInheritanceTypeString($type) { switch ($type) { - case ClassMetadata::INHERITANCE_TYPE_NONE: + case ClassMetadataInfo::INHERITANCE_TYPE_NONE: return 'NONE'; break; - case ClassMetadata::INHERITANCE_TYPE_JOINED: + case ClassMetadataInfo::INHERITANCE_TYPE_JOINED: return 'JOINED'; break; - case ClassMetadata::INHERITANCE_TYPE_SINGLE_TABLE: + case ClassMetadataInfo::INHERITANCE_TYPE_SINGLE_TABLE: return 'SINGLE_TABLE'; break; - case ClassMetadata::INHERITANCE_TYPE_TABLE_PER_CLASS: + case ClassMetadataInfo::INHERITANCE_TYPE_TABLE_PER_CLASS: return 'PER_CLASS'; break; } @@ -132,15 +132,15 @@ abstract class AbstractExporter { switch ($policy) { - case ClassMetadata::CHANGETRACKING_DEFERRED_IMPLICIT: + case ClassMetadataInfo::CHANGETRACKING_DEFERRED_IMPLICIT: return 'DEFERRED_IMPLICIT'; break; - case ClassMetadata::CHANGETRACKING_DEFERRED_EXPLICIT: + case ClassMetadataInfo::CHANGETRACKING_DEFERRED_EXPLICIT: return 'DEFERRED_EXPLICIT'; break; - case ClassMetadata::CHANGETRACKING_NOTIFY: + case ClassMetadataInfo::CHANGETRACKING_NOTIFY: return 'NOTIFY'; break; } @@ -150,19 +150,19 @@ abstract class AbstractExporter { switch ($type) { - case ClassMetadata::GENERATOR_TYPE_AUTO: + case ClassMetadataInfo::GENERATOR_TYPE_AUTO: return 'AUTO'; break; - case ClassMetadata::GENERATOR_TYPE_SEQUENCE: + case ClassMetadataInfo::GENERATOR_TYPE_SEQUENCE: return 'SEQUENCE'; break; - case ClassMetadata::GENERATOR_TYPE_TABLE: + case ClassMetadataInfo::GENERATOR_TYPE_TABLE: return 'TABLE'; break; - case ClassMetadata::GENERATOR_TYPE_IDENTITY: + case ClassMetadataInfo::GENERATOR_TYPE_IDENTITY: return 'IDENTITY'; break; } diff --git a/lib/Doctrine/ORM/Tools/Export/Driver/AnnotationExporter.php b/lib/Doctrine/ORM/Tools/Export/Driver/AnnotationExporter.php index 290f57b23..5dac6e9a2 100644 --- a/lib/Doctrine/ORM/Tools/Export/Driver/AnnotationExporter.php +++ b/lib/Doctrine/ORM/Tools/Export/Driver/AnnotationExporter.php @@ -22,7 +22,7 @@ namespace Doctrine\ORM\Tools\Export\Driver; -use Doctrine\ORM\Mapping\ClassMetadata, +use Doctrine\ORM\Mapping\ClassMetadataInfo, Doctrine\ORM\Mapping\AssociationMapping; /** @@ -37,26 +37,194 @@ use Doctrine\ORM\Mapping\ClassMetadata, class AnnotationExporter extends AbstractExporter { protected $_extension = '.php'; + protected $_isNew = false; + protected $_outputPath; + protected $_numSpaces = 4; + protected $_classToExtend; + protected $_currentCode; + + public function hasProperty($property, $metadata) + { + if ($this->_isNew) { + return false; + } else { + return strpos($this->_currentCode, '$' . $property) !== false ? true : false; + } + } + + public function hasMethod($method, $metadata) + { + if ($this->_isNew) { + return false; + } else { + return strpos($this->_currentCode, 'function ' . $method) !== false ? true : false; + } + } /** * Converts a single ClassMetadata instance to the exported format * and returns it * - * @param ClassMetadata $metadata + * @param ClassMetadataInfo $metadata * @return mixed $exported */ - public function exportClassMetadata(ClassMetadata $metadata) + public function exportClassMetadata(ClassMetadataInfo $metadata) { + if (file_exists($this->_outputPath)) { + $this->_currentCode = file_get_contents($this->_outputPath); + } + ob_start(); - include('annotation.tpl.php'); + include($this->_isNew ? 'annotation.tpl.php' : 'annotation_body.tpl.php'); $code = ob_get_contents(); ob_end_clean(); $code = str_replace(array('[?php', '?]'), array(''), $code); + $code = explode("\n", $code); + + if ($this->_currentCode) { + $body = $code; + $code = $this->_currentCode; + $code = explode("\n", $code); + unset($code[array_search('}', $code)]); + foreach ($body as $line) { + $code[] = $line; + } + $code[] = '}'; + } + + $code = array_values($code); + + // Remove empty lines before last "}" + for ($i = count($code) - 1; $i > 0; --$i) { + $line = trim($code[$i]); + if ($line && $line != '}') { + break; + } + if ( ! $line) { + unset($code[$i]); + } + } + $code = array_values($code); + $code = implode("\n", $code); + return $code; } - private function _getTableAnnotation($metadata) + public function setNumSpaces($numSpaces) + { + $this->_numSpaces = $numSpaces; + } + + public function extendsClass() + { + return $this->_classToExtend ? true : false; + } + + public function getClassToExtend() + { + return $this->_classToExtend; + } + + public function setClassToExtend($classToExtend) + { + $this->_classToExtend = $classToExtend; + } + + public function getClassToExtendName() + { + $refl = new \ReflectionClass($this->getClassToExtend()); + return $refl->getShortName(); + } + + public function getClassToExtendNamespace() + { + $refl = new \ReflectionClass($this->getClassToExtend()); + return $refl->getNamespaceName() ? $refl->getNamespaceName():$refl->getShortName(); + } + + public function getClassName($metadata) + { + return substr($metadata->name, strpos($metadata->name, '\\') + 1, strlen($metadata->name)); + } + + public function getNamespace($metadata) + { + return substr($metadata->name, 0, strrpos($metadata->name, '\\')); + } + + public function addMethod($type, $fieldName, $metadata, array &$methods) + { + $methodName = $type . ucfirst($fieldName); + if ($this->hasMethod($methodName, $metadata)) { + return false; + } + + $method = array(); + $method[] = str_repeat(' ', $this->_numSpaces) . '/**'; + if ($type == 'get') { + $method[] = str_repeat(' ', $this->_numSpaces) . ' * Get ' . $fieldName; + } else if ($type == 'set') { + $method[] = str_repeat(' ', $this->_numSpaces) . ' * Set ' . $fieldName; + } else if ($type == 'add') { + $method[] = str_repeat(' ', $this->_numSpaces) . ' * Add ' . $fieldName; + } + $method[] = str_repeat(' ', $this->_numSpaces) . ' */'; + + if ($type == 'get') { + $method[] = str_repeat(' ', $this->_numSpaces) . 'public function ' . $methodName . '()'; + } else if ($type == 'set') { + $method[] = str_repeat(' ', $this->_numSpaces) . 'public function ' . $methodName . '($value)'; + } else if ($type == 'add') { + $method[] = str_repeat(' ', $this->_numSpaces) . 'public function ' . $methodName . '($value)'; + } + + $method[] = str_repeat(' ', $this->_numSpaces) . '{'; + if ($type == 'get') { + $method[] = str_repeat(' ', $this->_numSpaces) . str_repeat(' ', $this->_numSpaces) . 'return $this->' . $fieldName . ';'; + } else if ($type == 'set') { + $method[] = str_repeat(' ', $this->_numSpaces) . str_repeat(' ', $this->_numSpaces) . '$this->' . $fieldName . ' = $value;'; + } else if ($type == 'add') { + $method[] = str_repeat(' ', $this->_numSpaces) . str_repeat(' ', $this->_numSpaces) . '$this->' . $fieldName . '[] = $value;'; + } + + $method[] = str_repeat(' ', $this->_numSpaces) . '}'; + $method[] = "\n"; + + $methods[] = implode("\n", $method); + } + + public function getMethods($metadata) + { + $methods = array(); + + foreach ($metadata->fieldMappings as $fieldMapping) { + $this->addMethod('set', $fieldMapping['fieldName'], $metadata, $methods); + $this->addMethod('get', $fieldMapping['fieldName'], $metadata, $methods); + } + + foreach ($metadata->associationMappings as $associationMapping) { + if ($associationMapping instanceof \Doctrine\ORM\Mapping\OneToOneMapping) { + $this->addMethod('set', $associationMapping->sourceFieldName, $metadata, $methods); + $this->addMethod('get', $associationMapping->sourceFieldName, $metadata, $methods); + } else if ($associationMapping instanceof \Doctrine\ORM\Mapping\OneToManyMapping) { + if ($associationMapping->isOwningSide) { + $this->addMethod('set', $associationMapping->sourceFieldName, $metadata, $methods); + $this->addMethod('get', $associationMapping->sourceFieldName, $metadata, $methods); + } else { + $this->addMethod('add', $associationMapping->sourceFieldName, $metadata, $methods); + $this->addMethod('get', $associationMapping->sourceFieldName, $metadata, $methods); + } + } else if ($associationMapping instanceof \Doctrine\ORM\Mapping\ManyToManyMapping) { + $this->addMethod('add', $associationMapping->sourceFieldName, $metadata, $methods); + $this->addMethod('get', $associationMapping->sourceFieldName, $metadata, $methods); + } + } + + return $methods; + } + + public function getTableAnnotation($metadata) { $table = array(); $table[] = 'name=' . $metadata->primaryTable['name']; @@ -66,21 +234,21 @@ class AnnotationExporter extends AbstractExporter return '@Table(' . implode(', ', $table) . ')'; } - private function _getAssociationMappingAnnotation(AssociationMapping $associationMapping, ClassMetadata $metadata) + public function getAssociationMappingAnnotation(AssociationMapping $associationMapping, ClassMetadataInfo $metadata) { // TODO: This function still needs to be written :) $lines = array(); - $lines[] = ' /**'; - $lines[] = ' *'; - $lines[] = ' */'; + $lines[] = str_repeat(' ', $this->_numSpaces) . '/**'; + $lines[] = str_repeat(' ', $this->_numSpaces) . ' *'; + $lines[] = str_repeat(' ', $this->_numSpaces) . ' */'; return implode("\n", $lines); } - private function _getFieldMappingAnnotation(array $fieldMapping, ClassMetadata $metadata) + public function getFieldMappingAnnotation(array $fieldMapping, ClassMetadataInfo $metadata) { $lines = array(); - $lines[] = ' /**'; + $lines[] = str_repeat(' ', $this->_numSpaces) . '/**'; $column = array(); if (isset($fieldMapping['type'])) { @@ -110,11 +278,11 @@ class AnnotationExporter extends AbstractExporter if (isset($fieldMapping['unique'])) { $column[] = 'unique=' . var_export($fieldMapping['unique'], true); } - $lines[] = ' * @Column(' . implode(', ', $column) . ')'; + $lines[] = str_repeat(' ', $this->_numSpaces) . ' * @Column(' . implode(', ', $column) . ')'; if (isset($fieldMapping['id']) && $fieldMapping['id']) { - $lines[] = ' * @Id'; + $lines[] = str_repeat(' ', $this->_numSpaces) . ' * @Id'; if ($generatorType = $this->_getIdGeneratorTypeString($metadata->generatorType)) { - $lines[] = ' * @GeneratedValue(strategy="' . $generatorType . '")'; + $lines[] = str_repeat(' ', $this->_numSpaces).' * @GeneratedValue(strategy="' . $generatorType . '")'; } if ($metadata->sequenceGeneratorDefinition) { $sequenceGenerator = array(); @@ -127,13 +295,13 @@ class AnnotationExporter extends AbstractExporter if (isset($metadata->sequenceGeneratorDefinition['initialValue'])) { $sequenceGenerator[] = 'initialValue="' . $metadata->sequenceGeneratorDefinition['initialValue'] . '"'; } - $lines[] = ' * @SequenceGenerator(' . implode(', ', $sequenceGenerator) . ')'; + $lines[] = str_repeat(' ', $this->_numSpaces) . ' * @SequenceGenerator(' . implode(', ', $sequenceGenerator) . ')'; } } if (isset($fieldMapping['version']) && $fieldMapping['version']) { - $lines[] = ' * @Version'; + $lines[] = str_repeat(' ', $this->_numSpaces) . ' * @Version'; } - $lines[] = ' */'; + $lines[] = str_repeat(' ', $this->_numSpaces) . ' */'; return implode("\n", $lines); } @@ -156,6 +324,10 @@ class AnnotationExporter extends AbstractExporter if ( ! is_dir($outputDir)) { mkdir($outputDir, 0777, true); } + if ( ! file_exists($outputPath)) { + $this->_isNew = true; + } + $this->_outputPath = $outputPath; $output = $this->exportClassMetadata($metadata); file_put_contents($outputPath, $output); } diff --git a/lib/Doctrine/ORM/Tools/Export/Driver/PhpExporter.php b/lib/Doctrine/ORM/Tools/Export/Driver/PhpExporter.php index 51c484ee7..28687e789 100644 --- a/lib/Doctrine/ORM/Tools/Export/Driver/PhpExporter.php +++ b/lib/Doctrine/ORM/Tools/Export/Driver/PhpExporter.php @@ -41,10 +41,10 @@ class PhpExporter extends AbstractExporter * Converts a single ClassMetadata instance to the exported format * and returns it * - * @param ClassMetadata $metadata + * @param ClassMetadataInfo $metadata * @return mixed $exported */ - public function exportClassMetadata(ClassMetadata $metadata) + public function exportClassMetadata(ClassMetadataInfo $metadata) { $lines = array(); $lines[] = '"); @@ -170,11 +173,11 @@ class XmlExporter extends AbstractExporter } foreach ($metadata->associationMappings as $name => $associationMapping) { - if ($associationMapping instanceof \Doctrine\ORM\Mapping\OneToOneMapping) { + if ($associationMapping instanceof OneToOneMapping) { $associationMappingXml = $root->addChild('one-to-one'); - } else if ($associationMapping instanceof \Doctrine\ORM\Mapping\OneToManyMapping) { + } else if ($associationMapping instanceof OneToManyMapping) { $associationMappingXml = $root->addChild('one-to-many'); - } else if ($associationMapping instanceof \Doctrine\ORM\Mapping\ManyToManyMapping) { + } else if ($associationMapping instanceof ManyToManyMapping) { $associationMappingXml = $root->addChild('many-to-many'); } diff --git a/lib/Doctrine/ORM/Tools/Export/Driver/YamlExporter.php b/lib/Doctrine/ORM/Tools/Export/Driver/YamlExporter.php index a06c77a7f..c0519efd2 100644 --- a/lib/Doctrine/ORM/Tools/Export/Driver/YamlExporter.php +++ b/lib/Doctrine/ORM/Tools/Export/Driver/YamlExporter.php @@ -22,7 +22,7 @@ namespace Doctrine\ORM\Tools\Export\Driver; -use Doctrine\ORM\Mapping\ClassMetadata, +use Doctrine\ORM\Mapping\ClassMetadataInfo, Doctrine\ORM\Mapping\OneToOneMapping, Doctrine\ORM\Mapping\OneToManyMapping, Doctrine\ORM\Mapping\ManyToManyMapping; @@ -46,10 +46,10 @@ class YamlExporter extends AbstractExporter * * TODO: Should this code be pulled out in to a toArray() method in ClassMetadata * - * @param ClassMetadata $metadata + * @param ClassMetadataInfo $metadata * @return mixed $exported */ - public function exportClassMetadata(ClassMetadata $metadata) + public function exportClassMetadata(ClassMetadataInfo $metadata) { $array = array(); if ($metadata->isMappedSuperclass) { @@ -114,7 +114,7 @@ class YamlExporter extends AbstractExporter ), ); - if ($associationMapping instanceof \Doctrine\ORM\Mapping\OneToOneMapping) { + if ($associationMapping instanceof OneToOneMapping) { $oneToOneMappingArray = array( 'mappedBy' => $associationMapping->mappedByFieldName, 'joinColumns' => $associationMapping->joinColumns, @@ -123,7 +123,7 @@ class YamlExporter extends AbstractExporter $associationMappingArray = array_merge($associationMappingArray, $oneToOneMappingArray); $array['oneToOne'][$name] = $associationMappingArray; - } else if ($associationMapping instanceof \Doctrine\ORM\Mapping\OneToManyMapping) { + } else if ($associationMapping instanceof OneToManyMapping) { $oneToManyMappingArray = array( 'mappedBy' => $associationMapping->mappedByFieldName, 'orphanRemoval' => $associationMapping->orphanRemoval, @@ -131,7 +131,7 @@ class YamlExporter extends AbstractExporter $associationMappingArray = array_merge($associationMappingArray, $oneToManyMappingArray); $array['oneToMany'][$name] = $associationMappingArray; - } else if ($associationMapping instanceof \Doctrine\ORM\Mapping\ManyToManyMapping) { + } else if ($associationMapping instanceof ManyToManyMapping) { $manyToManyMappingArray = array( 'mappedBy' => $associationMapping->mappedByFieldName, 'joinTable' => $associationMapping->joinTable, diff --git a/lib/Doctrine/ORM/Tools/Export/Driver/annotation.tpl.php b/lib/Doctrine/ORM/Tools/Export/Driver/annotation.tpl.php index 4c2908aea..0a028dbb9 100644 --- a/lib/Doctrine/ORM/Tools/Export/Driver/annotation.tpl.php +++ b/lib/Doctrine/ORM/Tools/Export/Driver/annotation.tpl.php @@ -1,6 +1,10 @@ [?php -namespace namespace ?>; +namespace getNamespace($metadata) ?>; +extendsClass()): ?> + +use getClassToExtendNamespace() ?>; + /** isMappedSuperclass): ?> @@ -8,19 +12,10 @@ namespace namespace ?>; * @Entity - * _getTableAnnotation($metadata)."\n" ?> + * getTableAnnotation($metadata)."\n" ?> */ -class getReflectionClass()->getShortName()."\n" ?> +class getClassName($metadata) ?>extendsClass()): ?> extends getClassToExtendName() ?> + { -fieldMappings as $fieldMapping): ?> -_getFieldMappingAnnotation($fieldMapping, $metadata)."\n" ?> - private $; - - -associationMappings as $associationMapping): ?> -_getAssociationMappingAnnotation($associationMapping, $metadata)."\n" ?> - private $sourceFieldName ?>; - - - + } \ No newline at end of file diff --git a/lib/Doctrine/ORM/Tools/SchemaTool.php b/lib/Doctrine/ORM/Tools/SchemaTool.php index 69f907a6a..5c101b3df 100644 --- a/lib/Doctrine/ORM/Tools/SchemaTool.php +++ b/lib/Doctrine/ORM/Tools/SchemaTool.php @@ -630,7 +630,6 @@ class SchemaTool foreach ($newJoinColumns as $name => $joinColumn) { $changes['add'][$name] = $joinColumn; } - $sql[] = $this->_platform->getAlterTableSql($tableName, $changes); }