3 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
15 * This software consists of voluntary contributions made by many individuals
16 * and is licensed under the MIT license. For more information, see
17 * <http://www.doctrine-project.org>.
20 namespace Doctrine\ORM\Mapping;
22 use BadMethodCallException;
23 use InvalidArgumentException;
25 use Doctrine\DBAL\Types\Type;
27 use Doctrine\Common\Persistence\Mapping\ClassMetadata;
28 use Doctrine\Common\ClassLoader;
31 * A <tt>ClassMetadata</tt> instance holds all the object-relational mapping metadata
32 * of an entity and it's associations.
34 * Once populated, ClassMetadata instances are usually cached in a serialized form.
36 * <b>IMPORTANT NOTE:</b>
38 * The fields of this class are only public for 2 reasons:
39 * 1) To allow fast READ access.
40 * 2) To drastically reduce the size of a serialized instance (private/protected members
41 * get the whole class name, namespace inclusive, prepended to every property in
42 * the serialized representation).
44 * @author Roman Borschel <roman@code-factory.org>
45 * @author Jonathan H. Wage <jonwage@gmail.com>
48 class ClassMetadataInfo implements ClassMetadata
50 /* The inheritance mapping types */
52 * NONE means the class does not participate in an inheritance hierarchy
53 * and therefore does not need an inheritance mapping type.
55 const INHERITANCE_TYPE_NONE = 1;
58 * JOINED means the class will be persisted according to the rules of
59 * <tt>Class Table Inheritance</tt>.
61 const INHERITANCE_TYPE_JOINED = 2;
64 * SINGLE_TABLE means the class will be persisted according to the rules of
65 * <tt>Single Table Inheritance</tt>.
67 const INHERITANCE_TYPE_SINGLE_TABLE = 3;
70 * TABLE_PER_CLASS means the class will be persisted according to the rules
71 * of <tt>Concrete Table Inheritance</tt>.
73 const INHERITANCE_TYPE_TABLE_PER_CLASS = 4;
75 /* The Id generator types. */
77 * AUTO means the generator type will depend on what the used platform prefers.
78 * Offers full portability.
80 const GENERATOR_TYPE_AUTO = 1;
83 * SEQUENCE means a separate sequence object will be used. Platforms that do
84 * not have native sequence support may emulate it. Full portability is currently
87 const GENERATOR_TYPE_SEQUENCE = 2;
90 * TABLE means a separate table is used for id generation.
91 * Offers full portability.
93 const GENERATOR_TYPE_TABLE = 3;
96 * IDENTITY means an identity column is used for id generation. The database
97 * will fill in the id column on insertion. Platforms that do not support
98 * native identity columns may emulate them. Full portability is currently
101 const GENERATOR_TYPE_IDENTITY = 4;
104 * NONE means the class does not have a generated id. That means the class
105 * must have a natural, manually assigned id.
107 const GENERATOR_TYPE_NONE = 5;
110 * UUID means that a UUID/GUID expression is used for id generation. Full
111 * portability is currently not guaranteed.
113 const GENERATOR_TYPE_UUID = 6;
115 * CUSTOM means that customer will use own ID generator that supposedly work
117 const GENERATOR_TYPE_CUSTOM = 7;
119 * DEFERRED_IMPLICIT means that changes of entities are calculated at commit-time
120 * by doing a property-by-property comparison with the original data. This will
121 * be done for all entities that are in MANAGED state at commit-time.
123 * This is the default change tracking policy.
125 const CHANGETRACKING_DEFERRED_IMPLICIT = 1;
128 * DEFERRED_EXPLICIT means that changes of entities are calculated at commit-time
129 * by doing a property-by-property comparison with the original data. This will
130 * be done only for entities that were explicitly saved (through persist() or a cascade).
132 const CHANGETRACKING_DEFERRED_EXPLICIT = 2;
135 * NOTIFY means that Doctrine relies on the entities sending out notifications
136 * when their properties change. Such entity classes must implement
137 * the <tt>NotifyPropertyChanged</tt> interface.
139 const CHANGETRACKING_NOTIFY = 3;
142 * Specifies that an association is to be fetched when it is first accessed.
144 const FETCH_LAZY = 2;
147 * Specifies that an association is to be fetched when the owner of the
148 * association is fetched.
150 const FETCH_EAGER = 3;
153 * Specifies that an association is to be fetched lazy (on first access) and that
154 * commands such as Collection#count, Collection#slice are issued directly against
155 * the database if the collection is not yet initialized.
157 const FETCH_EXTRA_LAZY = 4;
160 * Identifies a one-to-one association.
162 const ONE_TO_ONE = 1;
165 * Identifies a many-to-one association.
167 const MANY_TO_ONE = 2;
170 * Identifies a one-to-many association.
172 const ONE_TO_MANY = 4;
175 * Identifies a many-to-many association.
177 const MANY_TO_MANY = 8;
180 * Combined bitmask for to-one (single-valued) associations.
185 * Combined bitmask for to-many (collection-valued) associations.
190 * READ-ONLY: The name of the entity class.
195 * READ-ONLY: The namespace the entity class is contained in.
198 * @todo Not really needed. Usage could be localized.
203 * READ-ONLY: The name of the entity class that is at the root of the mapped entity inheritance
204 * hierarchy. If the entity is not part of a mapped inheritance hierarchy this is the same
205 * as {@link $entityName}.
209 public $rootEntityName;
212 * READ-ONLY: The definition of custom generator. Only used for CUSTOM
215 * The definition has the following structure:
218 * 'class' => 'ClassName',
223 * @todo Merge with tableGeneratorDefinition into generic generatorDefinition
225 public $customGeneratorDefinition;
228 * The name of the custom repository class used for the entity class.
233 public $customRepositoryClassName;
236 * READ-ONLY: Whether this class describes the mapping of a mapped superclass.
240 public $isMappedSuperclass = false;
243 * READ-ONLY: The names of the parent classes (ancestors).
247 public $parentClasses = array();
250 * READ-ONLY: The names of all subclasses (descendants).
254 public $subClasses = array();
257 * READ-ONLY: The named queries allowed to be called directly from Repository.
261 public $namedQueries = array();
264 * READ-ONLY: The named native queries allowed to be called directly from Repository.
266 * A native SQL named query definition has the following structure:
269 * 'name' => <query name>,
270 * 'query' => <sql query>,
271 * 'resultClass' => <class of the result>,
272 * 'resultSetMapping' => <name of a SqlResultSetMapping>
276 public $namedNativeQueries = array();
279 * READ-ONLY: The mappings of the results of native SQL queries.
281 * A native result mapping definition has the following structure:
284 * 'name' => <result name>,
285 * 'entities' => array(<entity result mapping>),
286 * 'columns' => array(<column result mapping>)
290 public $sqlResultSetMappings = array();
293 * READ-ONLY: The field names of all fields that are part of the identifier/primary key
294 * of the mapped entity class.
298 public $identifier = array();
301 * READ-ONLY: The inheritance mapping type used by the class.
305 public $inheritanceType = self::INHERITANCE_TYPE_NONE;
308 * READ-ONLY: The Id generator type used by the class.
312 public $generatorType = self::GENERATOR_TYPE_NONE;
315 * READ-ONLY: The field mappings of the class.
316 * Keys are field names and values are mapping definitions.
318 * The mapping definition array has the following values:
320 * - <b>fieldName</b> (string)
321 * The name of the field in the Entity.
323 * - <b>type</b> (string)
324 * The type name of the mapped field. Can be one of Doctrine's mapping types
325 * or a custom mapping type.
327 * - <b>columnName</b> (string, optional)
328 * The column name. Optional. Defaults to the field name.
330 * - <b>length</b> (integer, optional)
331 * The database length of the column. Optional. Default value taken from
334 * - <b>id</b> (boolean, optional)
335 * Marks the field as the primary key of the entity. Multiple fields of an
336 * entity can have the id attribute, forming a composite key.
338 * - <b>nullable</b> (boolean, optional)
339 * Whether the column is nullable. Defaults to FALSE.
341 * - <b>columnDefinition</b> (string, optional, schema-only)
342 * The SQL fragment that is used when generating the DDL for the column.
344 * - <b>precision</b> (integer, optional, schema-only)
345 * The precision of a decimal column. Only valid if the column type is decimal.
347 * - <b>scale</b> (integer, optional, schema-only)
348 * The scale of a decimal column. Only valid if the column type is decimal.
350 [* - <b>'unique'] (string, optional, schema-only)</b>
351 * Whether a unique constraint should be generated for the column.
355 public $fieldMappings = array();
358 * READ-ONLY: An array of field names. Used to look up field names from column names.
359 * Keys are column names and values are field names.
360 * This is the reverse lookup map of $_columnNames.
364 public $fieldNames = array();
367 * READ-ONLY: A map of field names to column names. Keys are field names and values column names.
368 * Used to look up column names from field names.
369 * This is the reverse lookup map of $_fieldNames.
372 * @todo We could get rid of this array by just using $fieldMappings[$fieldName]['columnName'].
374 public $columnNames = array();
377 * READ-ONLY: The discriminator value of this class.
379 * <b>This does only apply to the JOINED and SINGLE_TABLE inheritance mapping strategies
380 * where a discriminator column is used.</b>
383 * @see discriminatorColumn
385 public $discriminatorValue;
388 * READ-ONLY: The discriminator map of all mapped classes in the hierarchy.
390 * <b>This does only apply to the JOINED and SINGLE_TABLE inheritance mapping strategies
391 * where a discriminator column is used.</b>
394 * @see discriminatorColumn
396 public $discriminatorMap = array();
399 * READ-ONLY: The definition of the discriminator column used in JOINED and SINGLE_TABLE
400 * inheritance mappings.
404 public $discriminatorColumn;
407 * READ-ONLY: The primary table definition. The definition is an array with the
410 * name => <tableName>
411 * schema => <schemaName>
413 * uniqueConstraints => array
420 * READ-ONLY: The registered lifecycle callbacks for entities of this class.
424 public $lifecycleCallbacks = array();
427 * READ-ONLY: The association mappings of this class.
429 * The mapping definition array supports the following keys:
431 * - <b>fieldName</b> (string)
432 * The name of the field in the entity the association is mapped to.
434 * - <b>targetEntity</b> (string)
435 * The class name of the target entity. If it is fully-qualified it is used as is.
436 * If it is a simple, unqualified class name the namespace is assumed to be the same
437 * as the namespace of the source entity.
439 * - <b>mappedBy</b> (string, required for bidirectional associations)
440 * The name of the field that completes the bidirectional association on the owning side.
441 * This key must be specified on the inverse side of a bidirectional association.
443 * - <b>inversedBy</b> (string, required for bidirectional associations)
444 * The name of the field that completes the bidirectional association on the inverse side.
445 * This key must be specified on the owning side of a bidirectional association.
447 * - <b>cascade</b> (array, optional)
448 * The names of persistence operations to cascade on the association. The set of possible
449 * values are: "persist", "remove", "detach", "merge", "refresh", "all" (implies all others).
451 * - <b>orderBy</b> (array, one-to-many/many-to-many only)
452 * A map of field names (of the target entity) to sorting directions (ASC/DESC).
453 * Example: array('priority' => 'desc')
455 * - <b>fetch</b> (integer, optional)
456 * The fetching strategy to use for the association, usually defaults to FETCH_LAZY.
457 * Possible values are: ClassMetadata::FETCH_EAGER, ClassMetadata::FETCH_LAZY.
459 * - <b>joinTable</b> (array, optional, many-to-many only)
460 * Specification of the join table and its join columns (foreign keys).
461 * Only valid for many-to-many mappings. Note that one-to-many associations can be mapped
462 * through a join table by simply mapping the association as many-to-many with a unique
463 * constraint on the join table.
465 * - <b>indexBy</b> (string, optional, to-many only)
466 * Specification of a field on target-entity that is used to index the collection by.
467 * This field HAS to be either the primary key or a unique column. Otherwise the collection
468 * does not contain all the entities that are actually related.
470 * A join table definition has the following structure:
473 * 'name' => <join table name>,
474 * 'joinColumns' => array(<join column mapping from join table to source table>),
475 * 'inverseJoinColumns' => array(<join column mapping from join table to target table>)
482 public $associationMappings = array();
485 * READ-ONLY: Flag indicating whether the identifier/primary key of the class is composite.
489 public $isIdentifierComposite = false;
492 * READ-ONLY: Flag indicating wheather the identifier/primary key contains at least one foreign key association.
494 * This flag is necessary because some code blocks require special treatment of this cases.
498 public $containsForeignIdentifier = false;
501 * READ-ONLY: The ID generator used for generating IDs for this class.
503 * @var \Doctrine\ORM\Id\AbstractIdGenerator
509 * READ-ONLY: The definition of the sequence generator of this class. Only used for the
510 * SEQUENCE generation strategy.
512 * The definition has the following structure:
515 * 'sequenceName' => 'name',
516 * 'allocationSize' => 20,
517 * 'initialValue' => 1
522 * @todo Merge with tableGeneratorDefinition into generic generatorDefinition
524 public $sequenceGeneratorDefinition;
527 * READ-ONLY: The definition of the table generator of this class. Only used for the
528 * TABLE generation strategy.
531 * @todo Merge with tableGeneratorDefinition into generic generatorDefinition
533 public $tableGeneratorDefinition;
536 * READ-ONLY: The policy used for change-tracking on entities of this class.
540 public $changeTrackingPolicy = self::CHANGETRACKING_DEFERRED_IMPLICIT;
543 * READ-ONLY: A flag for whether or not instances of this class are to be versioned
544 * with optimistic locking.
546 * @var boolean $isVersioned
551 * READ-ONLY: The name of the field which is used for versioning in optimistic locking (if any).
553 * @var mixed $versionField
555 public $versionField;
558 * The ReflectionClass instance of the mapped class.
560 * @var ReflectionClass
565 * Is this entity marked as "read-only"?
567 * That means it is never considered for change-tracking in the UnitOfWork. It is a very helpful performance
568 * optimization for entities that are immutable, either in your domain or through the relation database
569 * (coming from a view, or a history table for example).
573 public $isReadOnly = false;
576 * NamingStrategy determining the default column and table names
578 * @var \Doctrine\ORM\Mapping\NamingStrategy
580 protected $namingStrategy;
583 * The ReflectionProperty instances of the mapped class.
587 public $reflFields = array();
590 * The prototype from which new instances of the mapped class are created.
597 * Initializes a new ClassMetadata instance that will hold the object-relational mapping
598 * metadata of the class with the given name.
600 * @param string $entityName The name of the entity class the new instance is used for.
601 * @param NamingStrategy $namingStrategy
603 public function __construct($entityName, NamingStrategy $namingStrategy = null)
605 $this->name = $entityName;
606 $this->rootEntityName = $entityName;
607 $this->namingStrategy = $namingStrategy ?: new DefaultNamingStrategy();
611 * Gets the ReflectionPropertys of the mapped class.
613 * @return array An array of ReflectionProperty instances.
615 public function getReflectionProperties()
617 return $this->reflFields;
621 * Gets a ReflectionProperty for a specific field of the mapped class.
623 * @param string $name
624 * @return \ReflectionProperty
626 public function getReflectionProperty($name)
628 return $this->reflFields[$name];
632 * Gets the ReflectionProperty for the single identifier field.
634 * @return \ReflectionProperty
635 * @throws BadMethodCallException If the class has a composite identifier.
637 public function getSingleIdReflectionProperty()
639 if ($this->isIdentifierComposite) {
640 throw new BadMethodCallException("Class " . $this->name . " has a composite identifier.");
642 return $this->reflFields[$this->identifier[0]];
646 * Extracts the identifier values of an entity of this class.
648 * For composite identifiers, the identifier values are returned as an array
649 * with the same order as the field order in {@link identifier}.
651 * @param object $entity
654 public function getIdentifierValues($entity)
656 if ($this->isIdentifierComposite) {
659 foreach ($this->identifier as $idField) {
660 $value = $this->reflFields[$idField]->getValue($entity);
662 if ($value !== null) {
663 $id[$idField] = $value;
670 $value = $this->reflFields[$this->identifier[0]]->getValue($entity);
672 if ($value !== null) {
673 return array($this->identifier[0] => $value);
680 * Populates the entity identifier of an entity.
682 * @param object $entity
684 * @todo Rename to assignIdentifier()
686 public function setIdentifierValues($entity, array $id)
688 foreach ($id as $idField => $idValue) {
689 $this->reflFields[$idField]->setValue($entity, $idValue);
694 * Sets the specified field to the specified value on the given entity.
696 * @param object $entity
697 * @param string $field
698 * @param mixed $value
700 public function setFieldValue($entity, $field, $value)
702 $this->reflFields[$field]->setValue($entity, $value);
706 * Gets the specified field's value off the given entity.
708 * @param object $entity
709 * @param string $field
711 public function getFieldValue($entity, $field)
713 return $this->reflFields[$field]->getValue($entity);
717 * Creates a string representation of this instance.
719 * @return string The string representation of this instance.
720 * @todo Construct meaningful string representation.
722 public function __toString()
724 return __CLASS__ . '@' . spl_object_hash($this);
728 * Determines which fields get serialized.
730 * It is only serialized what is necessary for best unserialization performance.
731 * That means any metadata properties that are not set or empty or simply have
732 * their default value are NOT serialized.
734 * Parts that are also NOT serialized because they can not be properly unserialized:
735 * - reflClass (ReflectionClass)
736 * - reflFields (ReflectionProperty array)
738 * @return array The names of all the fields that should be serialized.
740 public function __sleep()
742 // This metadata is always serialized/cached.
744 'associationMappings',
745 'columnNames', //TODO: Not really needed. Can use fieldMappings[$fieldName]['columnName']
749 'isIdentifierComposite', // TODO: REMOVE
751 'namespace', // TODO: REMOVE
754 'idGenerator', //TODO: Does not really need to be serialized. Could be moved to runtime.
757 // The rest of the metadata is only serialized if necessary.
758 if ($this->changeTrackingPolicy != self::CHANGETRACKING_DEFERRED_IMPLICIT) {
759 $serialized[] = 'changeTrackingPolicy';
762 if ($this->customRepositoryClassName) {
763 $serialized[] = 'customRepositoryClassName';
766 if ($this->inheritanceType != self::INHERITANCE_TYPE_NONE) {
767 $serialized[] = 'inheritanceType';
768 $serialized[] = 'discriminatorColumn';
769 $serialized[] = 'discriminatorValue';
770 $serialized[] = 'discriminatorMap';
771 $serialized[] = 'parentClasses';
772 $serialized[] = 'subClasses';
775 if ($this->generatorType != self::GENERATOR_TYPE_NONE) {
776 $serialized[] = 'generatorType';
777 if ($this->generatorType == self::GENERATOR_TYPE_SEQUENCE) {
778 $serialized[] = 'sequenceGeneratorDefinition';
782 if ($this->isMappedSuperclass) {
783 $serialized[] = 'isMappedSuperclass';
786 if ($this->containsForeignIdentifier) {
787 $serialized[] = 'containsForeignIdentifier';
790 if ($this->isVersioned) {
791 $serialized[] = 'isVersioned';
792 $serialized[] = 'versionField';
795 if ($this->lifecycleCallbacks) {
796 $serialized[] = 'lifecycleCallbacks';
799 if ($this->namedQueries) {
800 $serialized[] = 'namedQueries';
803 if ($this->namedNativeQueries) {
804 $serialized[] = 'namedNativeQueries';
807 if ($this->sqlResultSetMappings) {
808 $serialized[] = 'sqlResultSetMappings';
811 if ($this->isReadOnly) {
812 $serialized[] = 'isReadOnly';
815 if ($this->customGeneratorDefinition) {
816 $serialized[] = "customGeneratorDefinition";
823 * Creates a new instance of the mapped class, without invoking the constructor.
827 public function newInstance()
829 if ($this->_prototype === null) {
830 $this->_prototype = unserialize(sprintf('O:%d:"%s":0:{}', strlen($this->name), $this->name));
833 return clone $this->_prototype;
836 * Restores some state that can not be serialized/unserialized.
838 * @param \Doctrine\Common\Persistence\Mapping\ReflectionService $reflService
841 public function wakeupReflection($reflService)
843 // Restore ReflectionClass and properties
844 $this->reflClass = $reflService->getClass($this->name);
846 foreach ($this->fieldMappings as $field => $mapping) {
847 $this->reflFields[$field] = isset($mapping['declared'])
848 ? $reflService->getAccessibleProperty($mapping['declared'], $field)
849 : $reflService->getAccessibleProperty($this->name, $field);
852 foreach ($this->associationMappings as $field => $mapping) {
853 $this->reflFields[$field] = isset($mapping['declared'])
854 ? $reflService->getAccessibleProperty($mapping['declared'], $field)
855 : $reflService->getAccessibleProperty($this->name, $field);
860 * Initializes a new ClassMetadata instance that will hold the object-relational mapping
861 * metadata of the class with the given name.
863 * @param \Doctrine\Common\Persistence\Mapping\ReflectionService $reflService The reflection service.
865 public function initializeReflection($reflService)
867 $this->reflClass = $reflService->getClass($this->name);
868 $this->namespace = $reflService->getClassNamespace($this->name);
870 if ($this->reflClass) {
871 $this->name = $this->rootEntityName = $this->reflClass->getName();
874 $this->table['name'] = $this->namingStrategy->classToTableName($this->name);
878 * Validate Identifier
880 * @throws MappingException
883 public function validateIdentifier()
885 // Verify & complete identifier mapping
886 if ( ! $this->identifier && ! $this->isMappedSuperclass) {
887 throw MappingException::identifierRequired($this->name);
890 if ($this->usesIdGenerator() && $this->isIdentifierComposite) {
891 throw MappingException::compositeKeyAssignedIdGeneratorRequired($this->name);
896 * Validate association targets actually exist.
898 * @throws MappingException
901 public function validateAssocations()
903 foreach ($this->associationMappings as $mapping) {
904 if ( ! ClassLoader::classExists($mapping['targetEntity']) ) {
905 throw MappingException::invalidTargetEntityClass($mapping['targetEntity'], $this->name, $mapping['fieldName']);
911 * Validate lifecycle callbacks
913 * @param \Doctrine\Common\Persistence\Mapping\ReflectionService $reflService
914 * @throws MappingException
917 public function validateLifecycleCallbacks($reflService)
919 foreach ($this->lifecycleCallbacks as $callbacks) {
920 foreach ($callbacks as $callbackFuncName) {
921 if ( ! $reflService->hasPublicMethod($this->name, $callbackFuncName)) {
922 throw MappingException::lifecycleCallbackMethodNotFound($this->name, $callbackFuncName);
931 public function getReflectionClass()
933 return $this->reflClass;
937 * Sets the change tracking policy used by this class.
939 * @param integer $policy
941 public function setChangeTrackingPolicy($policy)
943 $this->changeTrackingPolicy = $policy;
947 * Whether the change tracking policy of this class is "deferred explicit".
951 public function isChangeTrackingDeferredExplicit()
953 return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_EXPLICIT;
957 * Whether the change tracking policy of this class is "deferred implicit".
961 public function isChangeTrackingDeferredImplicit()
963 return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_IMPLICIT;
967 * Whether the change tracking policy of this class is "notify".
971 public function isChangeTrackingNotify()
973 return $this->changeTrackingPolicy == self::CHANGETRACKING_NOTIFY;
977 * Checks whether a field is part of the identifier/primary key field(s).
979 * @param string $fieldName The field name
980 * @return boolean TRUE if the field is part of the table identifier/primary key field(s),
983 public function isIdentifier($fieldName)
985 if ( ! $this->isIdentifierComposite) {
986 return $fieldName === $this->identifier[0];
988 return in_array($fieldName, $this->identifier);
992 * Check if the field is unique.
994 * @param string $fieldName The field name
995 * @return boolean TRUE if the field is unique, FALSE otherwise.
997 public function isUniqueField($fieldName)
999 $mapping = $this->getFieldMapping($fieldName);
1000 if ($mapping !== false) {
1001 return isset($mapping['unique']) && $mapping['unique'] == true;
1007 * Check if the field is not null.
1009 * @param string $fieldName The field name
1010 * @return boolean TRUE if the field is not null, FALSE otherwise.
1012 public function isNullable($fieldName)
1014 $mapping = $this->getFieldMapping($fieldName);
1015 if ($mapping !== false) {
1016 return isset($mapping['nullable']) && $mapping['nullable'] == true;
1022 * Gets a column name for a field name.
1023 * If the column name for the field cannot be found, the given field name
1026 * @param string $fieldName The field name.
1027 * @return string The column name.
1029 public function getColumnName($fieldName)
1031 return isset($this->columnNames[$fieldName]) ?
1032 $this->columnNames[$fieldName] : $fieldName;
1036 * Gets the mapping of a (regular) field that holds some data but not a
1037 * reference to another object.
1039 * @param string $fieldName The field name.
1040 * @throws MappingException
1041 * @return array The field mapping.
1043 public function getFieldMapping($fieldName)
1045 if ( ! isset($this->fieldMappings[$fieldName])) {
1046 throw MappingException::mappingNotFound($this->name, $fieldName);
1048 return $this->fieldMappings[$fieldName];
1052 * Gets the mapping of an association.
1054 * @see ClassMetadataInfo::$associationMappings
1055 * @param string $fieldName The field name that represents the association in
1057 * @throws MappingException
1058 * @return array The mapping.
1060 public function getAssociationMapping($fieldName)
1062 if ( ! isset($this->associationMappings[$fieldName])) {
1063 throw MappingException::mappingNotFound($this->name, $fieldName);
1065 return $this->associationMappings[$fieldName];
1069 * Gets all association mappings of the class.
1073 public function getAssociationMappings()
1075 return $this->associationMappings;
1079 * Gets the field name for a column name.
1080 * If no field name can be found the column name is returned.
1082 * @param string $columnName column name
1083 * @return string column alias
1085 public function getFieldName($columnName)
1087 return isset($this->fieldNames[$columnName]) ?
1088 $this->fieldNames[$columnName] : $columnName;
1092 * Gets the named query.
1094 * @see ClassMetadataInfo::$namedQueries
1095 * @throws MappingException
1096 * @param string $queryName The query name
1099 public function getNamedQuery($queryName)
1101 if ( ! isset($this->namedQueries[$queryName])) {
1102 throw MappingException::queryNotFound($this->name, $queryName);
1104 return $this->namedQueries[$queryName]['dql'];
1108 * Gets all named queries of the class.
1112 public function getNamedQueries()
1114 return $this->namedQueries;
1118 * Gets the named native query.
1120 * @see ClassMetadataInfo::$namedNativeQueries
1121 * @throws MappingException
1122 * @param string $queryName The query name
1125 public function getNamedNativeQuery($queryName)
1127 if ( ! isset($this->namedNativeQueries[$queryName])) {
1128 throw MappingException::queryNotFound($this->name, $queryName);
1131 return $this->namedNativeQueries[$queryName];
1135 * Gets all named native queries of the class.
1139 public function getNamedNativeQueries()
1141 return $this->namedNativeQueries;
1145 * Gets the result set mapping.
1147 * @see ClassMetadataInfo::$sqlResultSetMappings
1148 * @throws MappingException
1149 * @param string $name The result set mapping name
1152 public function getSqlResultSetMapping($name)
1154 if ( ! isset($this->sqlResultSetMappings[$name])) {
1155 throw MappingException::resultMappingNotFound($this->name, $name);
1158 return $this->sqlResultSetMappings[$name];
1162 * Gets all sql result set mappings of the class.
1166 public function getSqlResultSetMappings()
1168 return $this->sqlResultSetMappings;
1172 * Validates & completes the given field mapping.
1174 * @param array $mapping The field mapping to validated & complete.
1175 * @throws MappingException
1176 * @return array The validated and completed field mapping.
1178 protected function _validateAndCompleteFieldMapping(array &$mapping)
1180 // Check mandatory fields
1181 if ( ! isset($mapping['fieldName']) || strlen($mapping['fieldName']) == 0) {
1182 throw MappingException::missingFieldName($this->name);
1184 if ( ! isset($mapping['type'])) {
1185 // Default to string
1186 $mapping['type'] = 'string';
1189 // Complete fieldName and columnName mapping
1190 if ( ! isset($mapping['columnName'])) {
1191 $mapping['columnName'] = $this->namingStrategy->propertyToColumnName($mapping['fieldName']);
1194 if ($mapping['columnName'][0] === '`') {
1195 $mapping['columnName'] = trim($mapping['columnName'], '`');
1196 $mapping['quoted'] = true;
1199 $this->columnNames[$mapping['fieldName']] = $mapping['columnName'];
1200 if (isset($this->fieldNames[$mapping['columnName']]) || ($this->discriminatorColumn != null && $this->discriminatorColumn['name'] == $mapping['columnName'])) {
1201 throw MappingException::duplicateColumnName($this->name, $mapping['columnName']);
1204 $this->fieldNames[$mapping['columnName']] = $mapping['fieldName'];
1206 // Complete id mapping
1207 if (isset($mapping['id']) && $mapping['id'] === true) {
1208 if ($this->versionField == $mapping['fieldName']) {
1209 throw MappingException::cannotVersionIdField($this->name, $mapping['fieldName']);
1212 if ( ! in_array($mapping['fieldName'], $this->identifier)) {
1213 $this->identifier[] = $mapping['fieldName'];
1215 // Check for composite key
1216 if ( ! $this->isIdentifierComposite && count($this->identifier) > 1) {
1217 $this->isIdentifierComposite = true;
1221 if (Type::hasType($mapping['type']) && Type::getType($mapping['type'])->canRequireSQLConversion()) {
1222 if (isset($mapping['id']) && $mapping['id'] === true) {
1223 throw MappingException::sqlConversionNotAllowedForIdentifiers($this->name, $mapping['fieldName'], $mapping['type']);
1226 $mapping['requireSQLConversion'] = true;
1231 * Validates & completes the basic mapping information that is common to all
1232 * association mappings (one-to-one, many-ot-one, one-to-many, many-to-many).
1234 * @param array $mapping The mapping.
1235 * @return array The updated mapping.
1236 * @throws MappingException If something is wrong with the mapping.
1238 protected function _validateAndCompleteAssociationMapping(array $mapping)
1240 if ( ! isset($mapping['mappedBy'])) {
1241 $mapping['mappedBy'] = null;
1243 if ( ! isset($mapping['inversedBy'])) {
1244 $mapping['inversedBy'] = null;
1246 $mapping['isOwningSide'] = true; // assume owning side until we hit mappedBy
1248 // unset optional indexBy attribute if its empty
1249 if ( ! isset($mapping['indexBy']) || !$mapping['indexBy']) {
1250 unset($mapping['indexBy']);
1253 // If targetEntity is unqualified, assume it is in the same namespace as
1254 // the sourceEntity.
1255 $mapping['sourceEntity'] = $this->name;
1257 if (isset($mapping['targetEntity'])) {
1258 if (strlen($this->namespace) > 0 && strpos($mapping['targetEntity'], '\\') === false) {
1259 $mapping['targetEntity'] = $this->namespace . '\\' . $mapping['targetEntity'];
1262 $mapping['targetEntity'] = ltrim($mapping['targetEntity'], '\\');
1265 if ( ($mapping['type'] & self::MANY_TO_ONE) > 0 &&
1266 isset($mapping['orphanRemoval']) &&
1267 $mapping['orphanRemoval'] == true) {
1269 throw MappingException::illegalOrphanRemoval($this->name, $mapping['fieldName']);
1272 // Complete id mapping
1273 if (isset($mapping['id']) && $mapping['id'] === true) {
1274 if (isset($mapping['orphanRemoval']) && $mapping['orphanRemoval'] == true) {
1275 throw MappingException::illegalOrphanRemovalOnIdentifierAssociation($this->name, $mapping['fieldName']);
1278 if ( ! in_array($mapping['fieldName'], $this->identifier)) {
1279 if (count($mapping['joinColumns']) >= 2) {
1280 throw MappingException::cannotMapCompositePrimaryKeyEntitiesAsForeignId(
1281 $mapping['targetEntity'], $this->name, $mapping['fieldName']
1285 $this->identifier[] = $mapping['fieldName'];
1286 $this->containsForeignIdentifier = true;
1288 // Check for composite key
1289 if ( ! $this->isIdentifierComposite && count($this->identifier) > 1) {
1290 $this->isIdentifierComposite = true;
1294 // Mandatory attributes for both sides
1295 // Mandatory: fieldName, targetEntity
1296 if ( ! isset($mapping['fieldName']) || strlen($mapping['fieldName']) == 0) {
1297 throw MappingException::missingFieldName($this->name);
1299 if ( ! isset($mapping['targetEntity'])) {
1300 throw MappingException::missingTargetEntity($mapping['fieldName']);
1303 // Mandatory and optional attributes for either side
1304 if ( ! $mapping['mappedBy']) {
1305 if (isset($mapping['joinTable']) && $mapping['joinTable']) {
1306 if (isset($mapping['joinTable']['name']) && $mapping['joinTable']['name'][0] === '`') {
1307 $mapping['joinTable']['name'] = trim($mapping['joinTable']['name'], '`');
1308 $mapping['joinTable']['quoted'] = true;
1312 $mapping['isOwningSide'] = false;
1315 if (isset($mapping['id']) && $mapping['id'] === true && $mapping['type'] & self::TO_MANY) {
1316 throw MappingException::illegalToManyIdentifierAssoaction($this->name, $mapping['fieldName']);
1319 // Fetch mode. Default fetch mode to LAZY, if not set.
1320 if ( ! isset($mapping['fetch'])) {
1321 $mapping['fetch'] = self::FETCH_LAZY;
1325 $cascades = isset($mapping['cascade']) ? array_map('strtolower', $mapping['cascade']) : array();
1327 if (in_array('all', $cascades)) {
1328 $cascades = array('remove', 'persist', 'refresh', 'merge', 'detach');
1331 if (count($cascades) !== count(array_intersect($cascades, array('remove', 'persist', 'refresh', 'merge', 'detach')))) {
1332 throw MappingException::invalidCascadeOption(
1333 array_diff($cascades, array_intersect($cascades, array('remove', 'persist', 'refresh', 'merge', 'detach'))),
1335 $mapping['fieldName']
1339 $mapping['cascade'] = $cascades;
1340 $mapping['isCascadeRemove'] = in_array('remove', $cascades);
1341 $mapping['isCascadePersist'] = in_array('persist', $cascades);
1342 $mapping['isCascadeRefresh'] = in_array('refresh', $cascades);
1343 $mapping['isCascadeMerge'] = in_array('merge', $cascades);
1344 $mapping['isCascadeDetach'] = in_array('detach', $cascades);
1350 * Validates & completes a one-to-one association mapping.
1352 * @param array $mapping The mapping to validate & complete.
1353 * @throws RuntimeException
1354 * @throws MappingException
1355 * @return array The validated & completed mapping.@override
1357 protected function _validateAndCompleteOneToOneMapping(array $mapping)
1359 $mapping = $this->_validateAndCompleteAssociationMapping($mapping);
1361 if (isset($mapping['joinColumns']) && $mapping['joinColumns']) {
1362 $mapping['isOwningSide'] = true;
1365 if ($mapping['isOwningSide']) {
1366 if ( ! isset($mapping['joinColumns']) || ! $mapping['joinColumns']) {
1367 // Apply default join column
1368 $mapping['joinColumns'] = array(array(
1369 'name' => $this->namingStrategy->joinColumnName($mapping['fieldName']),
1370 'referencedColumnName' => $this->namingStrategy->referenceColumnName()
1374 $uniqueContraintColumns = array();
1375 foreach ($mapping['joinColumns'] as &$joinColumn) {
1376 if ($mapping['type'] === self::ONE_TO_ONE && ! $this->isInheritanceTypeSingleTable()) {
1377 if (count($mapping['joinColumns']) == 1) {
1378 if ( ! isset($mapping['id']) || ! $mapping['id']) {
1379 $joinColumn['unique'] = true;
1382 $uniqueContraintColumns[] = $joinColumn['name'];
1386 if (empty($joinColumn['name'])) {
1387 $joinColumn['name'] = $this->namingStrategy->joinColumnName($mapping['fieldName']);
1390 if (empty($joinColumn['referencedColumnName'])) {
1391 $joinColumn['referencedColumnName'] = $this->namingStrategy->referenceColumnName();
1394 if ($joinColumn['name'][0] === '`') {
1395 $joinColumn['name'] = trim($joinColumn['name'], '`');
1396 $joinColumn['quoted'] = true;
1399 if ($joinColumn['referencedColumnName'][0] === '`') {
1400 $joinColumn['referencedColumnName'] = trim($joinColumn['referencedColumnName'], '`');
1401 $joinColumn['quoted'] = true;
1404 $mapping['sourceToTargetKeyColumns'][$joinColumn['name']] = $joinColumn['referencedColumnName'];
1405 $mapping['joinColumnFieldNames'][$joinColumn['name']] = isset($joinColumn['fieldName'])
1406 ? $joinColumn['fieldName'] : $joinColumn['name'];
1409 if ($uniqueContraintColumns) {
1410 if ( ! $this->table) {
1411 throw new RuntimeException("ClassMetadataInfo::setTable() has to be called before defining a one to one relationship.");
1413 $this->table['uniqueConstraints'][$mapping['fieldName']."_uniq"] = array(
1414 'columns' => $uniqueContraintColumns
1418 $mapping['targetToSourceKeyColumns'] = array_flip($mapping['sourceToTargetKeyColumns']);
1421 $mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false;
1422 $mapping['isCascadeRemove'] = $mapping['orphanRemoval'] ? true : $mapping['isCascadeRemove'];
1424 if (isset($mapping['id']) && $mapping['id'] === true && !$mapping['isOwningSide']) {
1425 throw MappingException::illegalInverseIdentifierAssocation($this->name, $mapping['fieldName']);
1432 * Validates and completes the mapping.
1434 * @param array $mapping The mapping to validate and complete.
1435 * @throws MappingException
1436 * @throws InvalidArgumentException
1437 * @return array The validated and completed mapping.@override
1439 protected function _validateAndCompleteOneToManyMapping(array $mapping)
1441 $mapping = $this->_validateAndCompleteAssociationMapping($mapping);
1443 // OneToMany-side MUST be inverse (must have mappedBy)
1444 if ( ! isset($mapping['mappedBy'])) {
1445 throw MappingException::oneToManyRequiresMappedBy($mapping['fieldName']);
1448 $mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false;
1449 $mapping['isCascadeRemove'] = $mapping['orphanRemoval'] ? true : $mapping['isCascadeRemove'];
1451 if (isset($mapping['orderBy'])) {
1452 if ( ! is_array($mapping['orderBy'])) {
1453 throw new InvalidArgumentException("'orderBy' is expected to be an array, not ".gettype($mapping['orderBy']));
1460 protected function _validateAndCompleteManyToManyMapping(array $mapping)
1462 $mapping = $this->_validateAndCompleteAssociationMapping($mapping);
1463 if ($mapping['isOwningSide']) {
1464 // owning side MUST have a join table
1465 if ( ! isset($mapping['joinTable']['name'])) {
1466 $mapping['joinTable']['name'] = $this->namingStrategy->joinTableName($mapping['sourceEntity'], $mapping['targetEntity'], $mapping['fieldName']);
1468 if ( ! isset($mapping['joinTable']['joinColumns'])) {
1469 $mapping['joinTable']['joinColumns'] = array(array(
1470 'name' => $this->namingStrategy->joinKeyColumnName($mapping['sourceEntity']),
1471 'referencedColumnName' => $this->namingStrategy->referenceColumnName(),
1472 'onDelete' => 'CASCADE'));
1474 if ( ! isset($mapping['joinTable']['inverseJoinColumns'])) {
1475 $mapping['joinTable']['inverseJoinColumns'] = array(array(
1476 'name' => $this->namingStrategy->joinKeyColumnName($mapping['targetEntity']),
1477 'referencedColumnName' => $this->namingStrategy->referenceColumnName(),
1478 'onDelete' => 'CASCADE'));
1481 foreach ($mapping['joinTable']['joinColumns'] as &$joinColumn) {
1482 if (empty($joinColumn['name'])) {
1483 $joinColumn['name'] = $this->namingStrategy->joinKeyColumnName($mapping['sourceEntity'], $joinColumn['referencedColumnName']);
1486 if (empty($joinColumn['referencedColumnName'])) {
1487 $joinColumn['referencedColumnName'] = $this->namingStrategy->referenceColumnName();
1490 if ($joinColumn['name'][0] === '`') {
1491 $joinColumn['name'] = trim($joinColumn['name'], '`');
1492 $joinColumn['quoted'] = true;
1495 if ($joinColumn['referencedColumnName'][0] === '`') {
1496 $joinColumn['referencedColumnName'] = trim($joinColumn['referencedColumnName'], '`');
1497 $joinColumn['quoted'] = true;
1500 if (isset($joinColumn['onDelete']) && strtolower($joinColumn['onDelete']) == 'cascade') {
1501 $mapping['isOnDeleteCascade'] = true;
1504 $mapping['relationToSourceKeyColumns'][$joinColumn['name']] = $joinColumn['referencedColumnName'];
1505 $mapping['joinTableColumns'][] = $joinColumn['name'];
1508 foreach ($mapping['joinTable']['inverseJoinColumns'] as &$inverseJoinColumn) {
1509 if (empty($inverseJoinColumn['name'])) {
1510 $inverseJoinColumn['name'] = $this->namingStrategy->joinKeyColumnName($mapping['targetEntity'], $inverseJoinColumn['referencedColumnName']);
1513 if (empty($inverseJoinColumn['referencedColumnName'])) {
1514 $inverseJoinColumn['referencedColumnName'] = $this->namingStrategy->referenceColumnName();
1517 if ($inverseJoinColumn['name'][0] === '`') {
1518 $inverseJoinColumn['name'] = trim($inverseJoinColumn['name'], '`');
1519 $inverseJoinColumn['quoted'] = true;
1522 if ($inverseJoinColumn['referencedColumnName'][0] === '`') {
1523 $inverseJoinColumn['referencedColumnName'] = trim($inverseJoinColumn['referencedColumnName'], '`');
1524 $inverseJoinColumn['quoted'] = true;
1527 if (isset($inverseJoinColumn['onDelete']) && strtolower($inverseJoinColumn['onDelete']) == 'cascade') {
1528 $mapping['isOnDeleteCascade'] = true;
1531 $mapping['relationToTargetKeyColumns'][$inverseJoinColumn['name']] = $inverseJoinColumn['referencedColumnName'];
1532 $mapping['joinTableColumns'][] = $inverseJoinColumn['name'];
1536 $mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false;
1538 if (isset($mapping['orderBy'])) {
1539 if ( ! is_array($mapping['orderBy'])) {
1540 throw new InvalidArgumentException("'orderBy' is expected to be an array, not ".gettype($mapping['orderBy']));
1550 public function getIdentifierFieldNames()
1552 return $this->identifier;
1556 * Gets the name of the single id field. Note that this only works on
1557 * entity classes that have a single-field pk.
1560 * @throws MappingException If the class has a composite primary key.
1562 public function getSingleIdentifierFieldName()
1564 if ($this->isIdentifierComposite) {
1565 throw MappingException::singleIdNotAllowedOnCompositePrimaryKey($this->name);
1567 return $this->identifier[0];
1571 * Gets the column name of the single id column. Note that this only works on
1572 * entity classes that have a single-field pk.
1575 * @throws MappingException If the class has a composite primary key.
1577 public function getSingleIdentifierColumnName()
1579 return $this->getColumnName($this->getSingleIdentifierFieldName());
1584 * Sets the mapped identifier/primary key fields of this class.
1585 * Mainly used by the ClassMetadataFactory to assign inherited identifiers.
1587 * @param array $identifier
1589 public function setIdentifier(array $identifier)
1591 $this->identifier = $identifier;
1592 $this->isIdentifierComposite = (count($this->identifier) > 1);
1596 * Gets the mapped identifier field of this class.
1598 * @return array|string $identifier
1600 public function getIdentifier()
1602 return $this->identifier;
1608 public function hasField($fieldName)
1610 return isset($this->fieldMappings[$fieldName]);
1614 * Gets an array containing all the column names.
1616 * @param array $fieldNames
1619 public function getColumnNames(array $fieldNames = null)
1621 if ($fieldNames === null) {
1622 return array_keys($this->fieldNames);
1624 $columnNames = array();
1625 foreach ($fieldNames as $fieldName) {
1626 $columnNames[] = $this->getColumnName($fieldName);
1628 return $columnNames;
1633 * Returns an array with all the identifier column names.
1637 public function getIdentifierColumnNames()
1639 $columnNames = array();
1641 foreach ($this->identifier as $idProperty) {
1642 if (isset($this->fieldMappings[$idProperty])) {
1643 $columnNames[] = $this->fieldMappings[$idProperty]['columnName'];
1648 // Association defined as Id field
1649 $joinColumns = $this->associationMappings[$idProperty]['joinColumns'];
1650 $assocColumnNames = array_map(function ($joinColumn) { return $joinColumn['name']; }, $joinColumns);
1652 $columnNames = array_merge($columnNames, $assocColumnNames);
1655 return $columnNames;
1659 * Sets the type of Id generator to use for the mapped class.
1661 public function setIdGeneratorType($generatorType)
1663 $this->generatorType = $generatorType;
1667 * Checks whether the mapped class uses an Id generator.
1669 * @return boolean TRUE if the mapped class uses an Id generator, FALSE otherwise.
1671 public function usesIdGenerator()
1673 return $this->generatorType != self::GENERATOR_TYPE_NONE;
1679 public function isInheritanceTypeNone()
1681 return $this->inheritanceType == self::INHERITANCE_TYPE_NONE;
1685 * Checks whether the mapped class uses the JOINED inheritance mapping strategy.
1687 * @return boolean TRUE if the class participates in a JOINED inheritance mapping,
1690 public function isInheritanceTypeJoined()
1692 return $this->inheritanceType == self::INHERITANCE_TYPE_JOINED;
1696 * Checks whether the mapped class uses the SINGLE_TABLE inheritance mapping strategy.
1698 * @return boolean TRUE if the class participates in a SINGLE_TABLE inheritance mapping,
1701 public function isInheritanceTypeSingleTable()
1703 return $this->inheritanceType == self::INHERITANCE_TYPE_SINGLE_TABLE;
1707 * Checks whether the mapped class uses the TABLE_PER_CLASS inheritance mapping strategy.
1709 * @return boolean TRUE if the class participates in a TABLE_PER_CLASS inheritance mapping,
1712 public function isInheritanceTypeTablePerClass()
1714 return $this->inheritanceType == self::INHERITANCE_TYPE_TABLE_PER_CLASS;
1718 * Checks whether the class uses an identity column for the Id generation.
1720 * @return boolean TRUE if the class uses the IDENTITY generator, FALSE otherwise.
1722 public function isIdGeneratorIdentity()
1724 return $this->generatorType == self::GENERATOR_TYPE_IDENTITY;
1728 * Checks whether the class uses a sequence for id generation.
1730 * @return boolean TRUE if the class uses the SEQUENCE generator, FALSE otherwise.
1732 public function isIdGeneratorSequence()
1734 return $this->generatorType == self::GENERATOR_TYPE_SEQUENCE;
1738 * Checks whether the class uses a table for id generation.
1740 * @return boolean TRUE if the class uses the TABLE generator, FALSE otherwise.
1742 public function isIdGeneratorTable()
1744 return $this->generatorType == self::GENERATOR_TYPE_TABLE;
1748 * Checks whether the class has a natural identifier/pk (which means it does
1749 * not use any Id generator.
1753 public function isIdentifierNatural()
1755 return $this->generatorType == self::GENERATOR_TYPE_NONE;
1759 * Checks whether the class use a UUID for id generation
1763 public function isIdentifierUuid()
1765 return $this->generatorType == self::GENERATOR_TYPE_UUID;
1769 * Gets the type of a field.
1771 * @param string $fieldName
1772 * @return \Doctrine\DBAL\Types\Type|string
1774 public function getTypeOfField($fieldName)
1776 return isset($this->fieldMappings[$fieldName]) ?
1777 $this->fieldMappings[$fieldName]['type'] : null;
1781 * Gets the type of a column.
1783 * @param string $columnName
1784 * @return \Doctrine\DBAL\Types\Type
1786 public function getTypeOfColumn($columnName)
1788 return $this->getTypeOfField($this->getFieldName($columnName));
1792 * Gets the name of the primary table.
1796 public function getTableName()
1798 return $this->table['name'];
1802 * Gets the table name to use for temporary identifier tables of this class.
1806 public function getTemporaryIdTableName()
1808 // replace dots with underscores because PostgreSQL creates temporary tables in a special schema
1809 return str_replace('.', '_', $this->getTableName() . '_id_tmp');
1813 * Sets the mapped subclasses of this class.
1815 * @param array $subclasses The names of all mapped subclasses.
1817 public function setSubclasses(array $subclasses)
1819 foreach ($subclasses as $subclass) {
1820 if (strpos($subclass, '\\') === false && strlen($this->namespace)) {
1821 $this->subClasses[] = $this->namespace . '\\' . $subclass;
1823 $this->subClasses[] = $subclass;
1829 * Sets the parent class names.
1830 * Assumes that the class names in the passed array are in the order:
1831 * directParent -> directParentParent -> directParentParentParent ... -> root.
1833 public function setParentClasses(array $classNames)
1835 $this->parentClasses = $classNames;
1836 if (count($classNames) > 0) {
1837 $this->rootEntityName = array_pop($classNames);
1842 * Sets the inheritance type used by the class and it's subclasses.
1844 * @param integer $type
1845 * @throws MappingException
1848 public function setInheritanceType($type)
1850 if ( ! $this->_isInheritanceType($type)) {
1851 throw MappingException::invalidInheritanceType($this->name, $type);
1853 $this->inheritanceType = $type;
1857 * Sets the association to override association mapping of property for an entity relationship.
1859 * @param string $fieldName
1860 * @param array $overrideMapping
1861 * @throws MappingException
1864 public function setAssociationOverride($fieldName, array $overrideMapping)
1866 if ( ! isset($this->associationMappings[$fieldName])) {
1867 throw MappingException::invalidOverrideFieldName($this->name, $fieldName);
1870 $mapping = $this->associationMappings[$fieldName];
1872 if (isset($overrideMapping['joinColumns'])) {
1873 $mapping['joinColumns'] = $overrideMapping['joinColumns'];
1876 if (isset($overrideMapping['joinTable'])) {
1877 $mapping['joinTable'] = $overrideMapping['joinTable'];
1880 $mapping['joinColumnFieldNames'] = null;
1881 $mapping['joinTableColumns'] = null;
1882 $mapping['sourceToTargetKeyColumns'] = null;
1883 $mapping['relationToSourceKeyColumns'] = null;
1884 $mapping['relationToTargetKeyColumns'] = null;
1886 switch ($mapping['type']) {
1887 case self::ONE_TO_ONE:
1888 $mapping = $this->_validateAndCompleteOneToOneMapping($mapping);
1890 case self::ONE_TO_MANY:
1891 $mapping = $this->_validateAndCompleteOneToManyMapping($mapping);
1893 case self::MANY_TO_ONE:
1894 $mapping = $this->_validateAndCompleteOneToOneMapping($mapping);
1896 case self::MANY_TO_MANY:
1897 $mapping = $this->_validateAndCompleteManyToManyMapping($mapping);
1901 $this->associationMappings[$fieldName] = $mapping;
1905 * Sets the override for a mapped field.
1907 * @param string $fieldName
1908 * @param array $overrideMapping
1909 * @throws MappingException
1910 * @param array $overrideMapping
1913 public function setAttributeOverride($fieldName, array $overrideMapping)
1915 if ( ! isset($this->fieldMappings[$fieldName])) {
1916 throw MappingException::invalidOverrideFieldName($this->name, $fieldName);
1919 $mapping = $this->fieldMappings[$fieldName];
1921 if (isset($mapping['id'])) {
1922 $overrideMapping['id'] = $mapping['id'];
1925 if ( ! isset($overrideMapping['type']) || $overrideMapping['type'] === null) {
1926 $overrideMapping['type'] = $mapping['type'];
1929 if ( ! isset($overrideMapping['fieldName']) || $overrideMapping['fieldName'] === null) {
1930 $overrideMapping['fieldName'] = $mapping['fieldName'];
1933 if ($overrideMapping['type'] !== $mapping['type']) {
1934 throw MappingException::invalidOverrideFieldType($this->name, $fieldName);
1937 unset($this->fieldMappings[$fieldName]);
1938 unset($this->fieldNames[$mapping['columnName']]);
1939 unset($this->columnNames[$mapping['fieldName']]);
1940 $this->_validateAndCompleteFieldMapping($overrideMapping);
1942 $this->fieldMappings[$fieldName] = $overrideMapping;
1946 * Checks whether a mapped field is inherited from an entity superclass.
1948 * @param string $fieldName
1949 * @return bool TRUE if the field is inherited, FALSE otherwise.
1951 public function isInheritedField($fieldName)
1953 return isset($this->fieldMappings[$fieldName]['inherited']);
1957 * Check if this entity is the root in any entity-inheritance-hierachy.
1961 public function isRootEntity()
1963 return $this->name == $this->rootEntityName;
1967 * Checks whether a mapped association field is inherited from a superclass.
1969 * @param string $fieldName
1970 * @return boolean TRUE if the field is inherited, FALSE otherwise.
1972 public function isInheritedAssociation($fieldName)
1974 return isset($this->associationMappings[$fieldName]['inherited']);
1978 * Sets the name of the primary table the class is mapped to.
1980 * @param string $tableName The table name.
1981 * @deprecated Use {@link setPrimaryTable}.
1983 public function setTableName($tableName)
1985 $this->table['name'] = $tableName;
1989 * Sets the primary table definition. The provided array supports the
1990 * following structure:
1992 * name => <tableName> (optional, defaults to class name)
1993 * indexes => array of indexes (optional)
1994 * uniqueConstraints => array of constraints (optional)
1996 * If a key is omitted, the current value is kept.
1998 * @param array $table The table description.
2000 public function setPrimaryTable(array $table)
2002 if (isset($table['name'])) {
2003 if ($table['name'][0] === '`') {
2004 $table['name'] = trim($table['name'], '`');
2005 $this->table['quoted'] = true;
2008 $this->table['name'] = $table['name'];
2011 if (isset($table['indexes'])) {
2012 $this->table['indexes'] = $table['indexes'];
2015 if (isset($table['uniqueConstraints'])) {
2016 $this->table['uniqueConstraints'] = $table['uniqueConstraints'];
2019 if (isset($table['options'])) {
2020 $this->table['options'] = $table['options'];
2025 * Checks whether the given type identifies an inheritance type.
2027 * @param integer $type
2028 * @return boolean TRUE if the given type identifies an inheritance type, FALSe otherwise.
2030 private function _isInheritanceType($type)
2032 return $type == self::INHERITANCE_TYPE_NONE ||
2033 $type == self::INHERITANCE_TYPE_SINGLE_TABLE ||
2034 $type == self::INHERITANCE_TYPE_JOINED ||
2035 $type == self::INHERITANCE_TYPE_TABLE_PER_CLASS;
2039 * Adds a mapped field to the class.
2041 * @param array $mapping The field mapping.
2042 * @throws MappingException
2045 public function mapField(array $mapping)
2047 $this->_validateAndCompleteFieldMapping($mapping);
2048 if (isset($this->fieldMappings[$mapping['fieldName']]) || isset($this->associationMappings[$mapping['fieldName']])) {
2049 throw MappingException::duplicateFieldMapping($this->name, $mapping['fieldName']);
2051 $this->fieldMappings[$mapping['fieldName']] = $mapping;
2056 * Adds an association mapping without completing/validating it.
2057 * This is mainly used to add inherited association mappings to derived classes.
2059 * @param array $mapping
2060 * @throws MappingException
2063 public function addInheritedAssociationMapping(array $mapping/*, $owningClassName = null*/)
2065 if (isset($this->associationMappings[$mapping['fieldName']])) {
2066 throw MappingException::duplicateAssociationMapping($this->name, $mapping['fieldName']);
2068 $this->associationMappings[$mapping['fieldName']] = $mapping;
2073 * Adds a field mapping without completing/validating it.
2074 * This is mainly used to add inherited field mappings to derived classes.
2076 * @param array $fieldMapping
2079 public function addInheritedFieldMapping(array $fieldMapping)
2081 $this->fieldMappings[$fieldMapping['fieldName']] = $fieldMapping;
2082 $this->columnNames[$fieldMapping['fieldName']] = $fieldMapping['columnName'];
2083 $this->fieldNames[$fieldMapping['columnName']] = $fieldMapping['fieldName'];
2088 * Adds a named query to this class.
2090 * @throws MappingException
2091 * @param array $queryMapping
2093 public function addNamedQuery(array $queryMapping)
2095 if (!isset($queryMapping['name'])) {
2096 throw MappingException::nameIsMandatoryForQueryMapping($this->name);
2099 if (isset($this->namedQueries[$queryMapping['name']])) {
2100 throw MappingException::duplicateQueryMapping($this->name, $queryMapping['name']);
2103 if (!isset($queryMapping['query'])) {
2104 throw MappingException::emptyQueryMapping($this->name, $queryMapping['name']);
2107 $name = $queryMapping['name'];
2108 $query = $queryMapping['query'];
2109 $dql = str_replace('__CLASS__', $this->name, $query);
2110 $this->namedQueries[$name] = array(
2119 * Adds a named native query to this class.
2121 * @throws MappingException
2122 * @param array $queryMapping
2124 public function addNamedNativeQuery(array $queryMapping)
2126 if (!isset($queryMapping['name'])) {
2127 throw MappingException::nameIsMandatoryForQueryMapping($this->name);
2130 if (isset($this->namedNativeQueries[$queryMapping['name']])) {
2131 throw MappingException::duplicateQueryMapping($this->name, $queryMapping['name']);
2134 if (!isset($queryMapping['query'])) {
2135 throw MappingException::emptyQueryMapping($this->name, $queryMapping['name']);
2138 if (!isset($queryMapping['resultClass']) && !isset($queryMapping['resultSetMapping'])) {
2139 throw MappingException::missingQueryMapping($this->name, $queryMapping['name']);
2142 $queryMapping['isSelfClass'] = false;
2143 if (isset($queryMapping['resultClass'])) {
2145 if($queryMapping['resultClass'] === '__CLASS__') {
2147 $queryMapping['isSelfClass'] = true;
2148 $queryMapping['resultClass'] = $this->name;
2150 } else if (strlen($this->namespace) > 0 && strpos($queryMapping['resultClass'], '\\') === false) {
2151 $queryMapping['resultClass'] = $this->namespace . '\\' . $queryMapping['resultClass'];
2154 $queryMapping['resultClass'] = ltrim($queryMapping['resultClass'], '\\');
2157 $this->namedNativeQueries[$queryMapping['name']] = $queryMapping;
2162 * Adds a sql result set mapping to this class.
2164 * @throws MappingException
2165 * @param array $resultMapping
2167 public function addSqlResultSetMapping(array $resultMapping)
2169 if (!isset($resultMapping['name'])) {
2170 throw MappingException::nameIsMandatoryForSqlResultSetMapping($this->name);
2173 if (isset($this->sqlResultSetMappings[$resultMapping['name']])) {
2174 throw MappingException::duplicateResultSetMapping($this->name, $resultMapping['name']);
2177 if (isset($resultMapping['entities'])) {
2178 foreach ($resultMapping['entities'] as $key => $entityResult) {
2179 if (!isset($entityResult['entityClass'])) {
2180 throw MappingException::missingResultSetMappingEntity($this->name, $resultMapping['name']);
2183 $entityResult['isSelfClass'] = false;
2184 if($entityResult['entityClass'] === '__CLASS__') {
2186 $entityResult['isSelfClass'] = true;
2187 $entityResult['entityClass'] = $this->name;
2189 } else if (strlen($this->namespace) > 0 && strpos($entityResult['entityClass'], '\\') === false) {
2190 $entityResult['entityClass'] = $this->namespace . '\\' . $entityResult['entityClass'];
2193 $resultMapping['entities'][$key]['entityClass'] = ltrim($entityResult['entityClass'], '\\');
2194 $resultMapping['entities'][$key]['isSelfClass'] = $entityResult['isSelfClass'];
2196 if (isset($entityResult['fields'])) {
2197 foreach ($entityResult['fields'] as $k => $field) {
2198 if (!isset($field['name'])) {
2199 throw MappingException::missingResultSetMappingFieldName($this->name, $resultMapping['name']);
2202 if (!isset($field['column'])) {
2203 $fieldName = $field['name'];
2204 if(strpos($fieldName, '.')){
2205 list(, $fieldName) = explode('.', $fieldName);
2208 $resultMapping['entities'][$key]['fields'][$k]['column'] = $fieldName;
2215 $this->sqlResultSetMappings[$resultMapping['name']] = $resultMapping;
2219 * Adds a one-to-one mapping.
2221 * @param array $mapping The mapping.
2223 public function mapOneToOne(array $mapping)
2225 $mapping['type'] = self::ONE_TO_ONE;
2226 $mapping = $this->_validateAndCompleteOneToOneMapping($mapping);
2227 $this->_storeAssociationMapping($mapping);
2231 * Adds a one-to-many mapping.
2233 * @param array $mapping The mapping.
2235 public function mapOneToMany(array $mapping)
2237 $mapping['type'] = self::ONE_TO_MANY;
2238 $mapping = $this->_validateAndCompleteOneToManyMapping($mapping);
2239 $this->_storeAssociationMapping($mapping);
2243 * Adds a many-to-one mapping.
2245 * @param array $mapping The mapping.
2247 public function mapManyToOne(array $mapping)
2249 $mapping['type'] = self::MANY_TO_ONE;
2250 // A many-to-one mapping is essentially a one-one backreference
2251 $mapping = $this->_validateAndCompleteOneToOneMapping($mapping);
2252 $this->_storeAssociationMapping($mapping);
2256 * Adds a many-to-many mapping.
2258 * @param array $mapping The mapping.
2260 public function mapManyToMany(array $mapping)
2262 $mapping['type'] = self::MANY_TO_MANY;
2263 $mapping = $this->_validateAndCompleteManyToManyMapping($mapping);
2264 $this->_storeAssociationMapping($mapping);
2268 * Stores the association mapping.
2270 * @param array $assocMapping
2271 * @throws MappingException
2274 protected function _storeAssociationMapping(array $assocMapping)
2276 $sourceFieldName = $assocMapping['fieldName'];
2278 if (isset($this->fieldMappings[$sourceFieldName]) || isset($this->associationMappings[$sourceFieldName])) {
2279 throw MappingException::duplicateFieldMapping($this->name, $sourceFieldName);
2282 $this->associationMappings[$sourceFieldName] = $assocMapping;
2286 * Registers a custom repository class for the entity class.
2288 * @param string $repositoryClassName The class name of the custom mapper.
2291 public function setCustomRepositoryClass($repositoryClassName)
2293 if ($repositoryClassName !== null && strpos($repositoryClassName, '\\') === false
2294 && strlen($this->namespace) > 0) {
2295 $repositoryClassName = $this->namespace . '\\' . $repositoryClassName;
2297 $this->customRepositoryClassName = $repositoryClassName;
2301 * Dispatches the lifecycle event of the given entity to the registered
2302 * lifecycle callbacks and lifecycle listeners.
2304 * @param string $lifecycleEvent The lifecycle event.
2305 * @param \Object $entity The Entity on which the event occured.
2307 public function invokeLifecycleCallbacks($lifecycleEvent, $entity)
2309 foreach ($this->lifecycleCallbacks[$lifecycleEvent] as $callback) {
2310 $entity->$callback();
2315 * Whether the class has any attached lifecycle listeners or callbacks for a lifecycle event.
2317 * @param string $lifecycleEvent
2320 public function hasLifecycleCallbacks($lifecycleEvent)
2322 return isset($this->lifecycleCallbacks[$lifecycleEvent]);
2326 * Gets the registered lifecycle callbacks for an event.
2328 * @param string $event
2331 public function getLifecycleCallbacks($event)
2333 return isset($this->lifecycleCallbacks[$event]) ? $this->lifecycleCallbacks[$event] : array();
2337 * Adds a lifecycle callback for entities of this class.
2339 * @param string $callback
2340 * @param string $event
2342 public function addLifecycleCallback($callback, $event)
2344 $this->lifecycleCallbacks[$event][] = $callback;
2348 * Sets the lifecycle callbacks for entities of this class.
2349 * Any previously registered callbacks are overwritten.
2351 * @param array $callbacks
2353 public function setLifecycleCallbacks(array $callbacks)
2355 $this->lifecycleCallbacks = $callbacks;
2359 * Sets the discriminator column definition.
2361 * @param array $columnDef
2364 * @throws MappingException
2366 * @see getDiscriminatorColumn()
2368 public function setDiscriminatorColumn($columnDef)
2370 if ($columnDef !== null) {
2371 if ( ! isset($columnDef['name'])) {
2372 throw MappingException::nameIsMandatoryForDiscriminatorColumns($this->name);
2375 if (isset($this->fieldNames[$columnDef['name']])) {
2376 throw MappingException::duplicateColumnName($this->name, $columnDef['name']);
2379 if ( ! isset($columnDef['fieldName'])) {
2380 $columnDef['fieldName'] = $columnDef['name'];
2383 if ( ! isset($columnDef['type'])) {
2384 $columnDef['type'] = "string";
2387 if (in_array($columnDef['type'], array("boolean", "array", "object", "datetime", "time", "date"))) {
2388 throw MappingException::invalidDiscriminatorColumnType($this->name, $columnDef['type']);
2391 $this->discriminatorColumn = $columnDef;
2396 * Sets the discriminator values used by this class.
2397 * Used for JOINED and SINGLE_TABLE inheritance mapping strategies.
2401 public function setDiscriminatorMap(array $map)
2403 foreach ($map as $value => $className) {
2404 $this->addDiscriminatorMapClass($value, $className);
2409 * Add one entry of the discriminator map with a new class and corresponding name.
2411 * @param string $name
2412 * @param string $className
2413 * @throws MappingException
2416 public function addDiscriminatorMapClass($name, $className)
2418 if (strlen($this->namespace) > 0 && strpos($className, '\\') === false) {
2419 $className = $this->namespace . '\\' . $className;
2422 $className = ltrim($className, '\\');
2423 $this->discriminatorMap[$name] = $className;
2425 if ($this->name == $className) {
2426 $this->discriminatorValue = $name;
2428 if ( ! class_exists($className)) {
2429 throw MappingException::invalidClassInDiscriminatorMap($className, $this->name);
2431 if (is_subclass_of($className, $this->name) && ! in_array($className, $this->subClasses)) {
2432 $this->subClasses[] = $className;
2438 * Checks whether the class has a named query with the given query name.
2440 * @param string $queryName
2443 public function hasNamedQuery($queryName)
2445 return isset($this->namedQueries[$queryName]);
2449 * Checks whether the class has a named native query with the given query name.
2451 * @param string $queryName
2454 public function hasNamedNativeQuery($queryName)
2456 return isset($this->namedNativeQueries[$queryName]);
2460 * Checks whether the class has a named native query with the given query name.
2462 * @param string $name
2465 public function hasSqlResultSetMapping($name)
2467 return isset($this->sqlResultSetMappings[$name]);
2473 public function hasAssociation($fieldName)
2475 return isset($this->associationMappings[$fieldName]);
2481 public function isSingleValuedAssociation($fieldName)
2483 return isset($this->associationMappings[$fieldName]) &&
2484 ($this->associationMappings[$fieldName]['type'] & self::TO_ONE);
2490 public function isCollectionValuedAssociation($fieldName)
2492 return isset($this->associationMappings[$fieldName]) &&
2493 ! ($this->associationMappings[$fieldName]['type'] & self::TO_ONE);
2497 * Is this an association that only has a single join column?
2499 * @param string $fieldName
2502 public function isAssociationWithSingleJoinColumn($fieldName)
2505 isset($this->associationMappings[$fieldName]) &&
2506 isset($this->associationMappings[$fieldName]['joinColumns'][0]) &&
2507 !isset($this->associationMappings[$fieldName]['joinColumns'][1])
2512 * Return the single association join column (if any).
2514 * @param string $fieldName
2515 * @throws MappingException
2518 public function getSingleAssociationJoinColumnName($fieldName)
2520 if ( ! $this->isAssociationWithSingleJoinColumn($fieldName)) {
2521 throw MappingException::noSingleAssociationJoinColumnFound($this->name, $fieldName);
2523 return $this->associationMappings[$fieldName]['joinColumns'][0]['name'];
2527 * Return the single association referenced join column name (if any).
2529 * @param string $fieldName
2530 * @throws MappingException
2533 public function getSingleAssociationReferencedJoinColumnName($fieldName)
2535 if ( ! $this->isAssociationWithSingleJoinColumn($fieldName)) {
2536 throw MappingException::noSingleAssociationJoinColumnFound($this->name, $fieldName);
2538 return $this->associationMappings[$fieldName]['joinColumns'][0]['referencedColumnName'];
2542 * Used to retrieve a fieldname for either field or association from a given column,
2544 * This method is used in foreign-key as primary-key contexts.
2546 * @param string $columnName
2547 * @throws MappingException
2550 public function getFieldForColumn($columnName)
2552 if (isset($this->fieldNames[$columnName])) {
2553 return $this->fieldNames[$columnName];
2555 foreach ($this->associationMappings as $assocName => $mapping) {
2556 if ($this->isAssociationWithSingleJoinColumn($assocName) &&
2557 $this->associationMappings[$assocName]['joinColumns'][0]['name'] == $columnName) {
2563 throw MappingException::noFieldNameFoundForColumn($this->name, $columnName);
2568 * Sets the ID generator used to generate IDs for instances of this class.
2570 * @param \Doctrine\ORM\Id\AbstractIdGenerator $generator
2572 public function setIdGenerator($generator)
2574 $this->idGenerator = $generator;
2579 * @param array $definition
2581 public function setCustomGeneratorDefinition(array $definition)
2583 $this->customGeneratorDefinition = $definition;
2587 * Sets the definition of the sequence ID generator for this class.
2589 * The definition must have the following structure:
2592 * 'sequenceName' => 'name',
2593 * 'allocationSize' => 20,
2594 * 'initialValue' => 1
2599 * @param array $definition
2601 public function setSequenceGeneratorDefinition(array $definition)
2603 if (isset($definition['name']) && $definition['name'] == '`') {
2604 $definition['name'] = trim($definition['name'], '`');
2605 $definition['quoted'] = true;
2608 $this->sequenceGeneratorDefinition = $definition;
2612 * Sets the version field mapping used for versioning. Sets the default
2613 * value to use depending on the column type.
2615 * @param array $mapping The version field mapping array
2616 * @throws MappingException
2619 public function setVersionMapping(array &$mapping)
2621 $this->isVersioned = true;
2622 $this->versionField = $mapping['fieldName'];
2624 if ( ! isset($mapping['default'])) {
2625 if (in_array($mapping['type'], array('integer', 'bigint', 'smallint'))) {
2626 $mapping['default'] = 1;
2627 } else if ($mapping['type'] == 'datetime') {
2628 $mapping['default'] = 'CURRENT_TIMESTAMP';
2630 throw MappingException::unsupportedOptimisticLockingType($this->name, $mapping['fieldName'], $mapping['type']);
2636 * Sets whether this class is to be versioned for optimistic locking.
2638 * @param boolean $bool
2640 public function setVersioned($bool)
2642 $this->isVersioned = $bool;
2646 * Sets the name of the field that is to be used for versioning if this class is
2647 * versioned for optimistic locking.
2649 * @param string $versionField
2651 public function setVersionField($versionField)
2653 $this->versionField = $versionField;
2657 * Mark this class as read only, no change tracking is applied to it.
2661 public function markReadOnly()
2663 $this->isReadOnly = true;
2669 public function getFieldNames()
2671 return array_keys($this->fieldMappings);
2677 public function getAssociationNames()
2679 return array_keys($this->associationMappings);
2684 * @throws InvalidArgumentException
2686 public function getAssociationTargetClass($assocName)
2688 if ( ! isset($this->associationMappings[$assocName])) {
2689 throw new InvalidArgumentException("Association name expected, '" . $assocName ."' is not an association.");
2692 return $this->associationMappings[$assocName]['targetEntity'];
2698 public function getName()
2704 * Gets the (possibly quoted) identifier column names for safe use in an SQL statement.
2706 * @deprecated Deprecated since version 2.3 in favor of \Doctrine\ORM\Mapping\QuoteStrategy
2708 * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform
2711 public function getQuotedIdentifierColumnNames($platform)
2713 $quotedColumnNames = array();
2715 foreach ($this->identifier as $idProperty) {
2716 if (isset($this->fieldMappings[$idProperty])) {
2717 $quotedColumnNames[] = isset($this->fieldMappings[$idProperty]['quoted'])
2718 ? $platform->quoteIdentifier($this->fieldMappings[$idProperty]['columnName'])
2719 : $this->fieldMappings[$idProperty]['columnName'];
2724 // Association defined as Id field
2725 $joinColumns = $this->associationMappings[$idProperty]['joinColumns'];
2726 $assocQuotedColumnNames = array_map(
2727 function ($joinColumn) use ($platform) {
2728 return isset($joinColumn['quoted'])
2729 ? $platform->quoteIdentifier($joinColumn['name'])
2730 : $joinColumn['name'];
2735 $quotedColumnNames = array_merge($quotedColumnNames, $assocQuotedColumnNames);
2738 return $quotedColumnNames;
2742 * Gets the (possibly quoted) column name of a mapped field for safe use in an SQL statement.
2744 * @deprecated Deprecated since version 2.3 in favor of \Doctrine\ORM\Mapping\QuoteStrategy
2746 * @param string $field
2747 * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform
2750 public function getQuotedColumnName($field, $platform)
2752 return isset($this->fieldMappings[$field]['quoted'])
2753 ? $platform->quoteIdentifier($this->fieldMappings[$field]['columnName'])
2754 : $this->fieldMappings[$field]['columnName'];
2758 * Gets the (possibly quoted) primary table name of this class for safe use in an SQL statement.
2760 * @deprecated Deprecated since version 2.3 in favor of \Doctrine\ORM\Mapping\QuoteStrategy
2762 * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform
2765 public function getQuotedTableName($platform)
2767 return isset($this->table['quoted']) ? $platform->quoteIdentifier($this->table['name']) : $this->table['name'];
2771 * Gets the (possibly quoted) name of the join table.
2773 * @deprecated Deprecated since version 2.3 in favor of \Doctrine\ORM\Mapping\QuoteStrategy
2775 * @param array $assoc
2776 * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform
2779 public function getQuotedJoinTableName(array $assoc, $platform)
2781 return isset($assoc['joinTable']['quoted']) ? $platform->quoteIdentifier($assoc['joinTable']['name']) : $assoc['joinTable']['name'];
2787 public function isAssociationInverseSide($fieldName)
2789 return isset($this->associationMappings[$fieldName]) && ! $this->associationMappings[$fieldName]['isOwningSide'];
2795 public function getAssociationMappedByTargetField($fieldName)
2797 return $this->associationMappings[$fieldName]['mappedBy'];
2801 * @param string $targetClass
2804 public function getAssociationsByTargetClass($targetClass)
2806 $relations = array();
2807 foreach ($this->associationMappings as $mapping) {
2808 if ($mapping['targetEntity'] == $targetClass) {
2809 $relations[$mapping['fieldName']] = $mapping;