Rajout de doctrine/orm
[zf2.biz/galerie.git] / vendor / doctrine / orm / lib / Doctrine / ORM / Mapping / ClassMetadataInfo.php
1 <?php
2 /*
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.
14  *
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>.
18  */
19
20 namespace Doctrine\ORM\Mapping;
21
22 use BadMethodCallException;
23 use InvalidArgumentException;
24 use RuntimeException;
25 use Doctrine\DBAL\Types\Type;
26 use ReflectionClass;
27 use Doctrine\Common\Persistence\Mapping\ClassMetadata;
28 use Doctrine\Common\ClassLoader;
29
30 /**
31  * A <tt>ClassMetadata</tt> instance holds all the object-relational mapping metadata
32  * of an entity and it's associations.
33  *
34  * Once populated, ClassMetadata instances are usually cached in a serialized form.
35  *
36  * <b>IMPORTANT NOTE:</b>
37  *
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).
43  *
44  * @author Roman Borschel <roman@code-factory.org>
45  * @author Jonathan H. Wage <jonwage@gmail.com>
46  * @since 2.0
47  */
48 class ClassMetadataInfo implements ClassMetadata
49 {
50     /* The inheritance mapping types */
51     /**
52      * NONE means the class does not participate in an inheritance hierarchy
53      * and therefore does not need an inheritance mapping type.
54      */
55     const INHERITANCE_TYPE_NONE = 1;
56
57     /**
58      * JOINED means the class will be persisted according to the rules of
59      * <tt>Class Table Inheritance</tt>.
60      */
61     const INHERITANCE_TYPE_JOINED = 2;
62
63     /**
64      * SINGLE_TABLE means the class will be persisted according to the rules of
65      * <tt>Single Table Inheritance</tt>.
66      */
67     const INHERITANCE_TYPE_SINGLE_TABLE = 3;
68
69     /**
70      * TABLE_PER_CLASS means the class will be persisted according to the rules
71      * of <tt>Concrete Table Inheritance</tt>.
72      */
73     const INHERITANCE_TYPE_TABLE_PER_CLASS = 4;
74
75     /* The Id generator types. */
76     /**
77      * AUTO means the generator type will depend on what the used platform prefers.
78      * Offers full portability.
79      */
80     const GENERATOR_TYPE_AUTO = 1;
81
82     /**
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
85      * not guaranteed.
86      */
87     const GENERATOR_TYPE_SEQUENCE = 2;
88
89     /**
90      * TABLE means a separate table is used for id generation.
91      * Offers full portability.
92      */
93     const GENERATOR_TYPE_TABLE = 3;
94
95     /**
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
99      * not guaranteed.
100      */
101     const GENERATOR_TYPE_IDENTITY = 4;
102
103     /**
104      * NONE means the class does not have a generated id. That means the class
105      * must have a natural, manually assigned id.
106      */
107     const GENERATOR_TYPE_NONE = 5;
108
109     /**
110      * UUID means that a UUID/GUID expression is used for id generation. Full
111      * portability is currently not guaranteed.
112      */
113     const GENERATOR_TYPE_UUID = 6;
114     /**
115      * CUSTOM means that customer will use own ID generator that supposedly work
116      */
117     const GENERATOR_TYPE_CUSTOM = 7;
118     /**
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.
122      *
123      * This is the default change tracking policy.
124      */
125     const CHANGETRACKING_DEFERRED_IMPLICIT = 1;
126
127     /**
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).
131      */
132     const CHANGETRACKING_DEFERRED_EXPLICIT = 2;
133
134     /**
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.
138      */
139     const CHANGETRACKING_NOTIFY = 3;
140
141     /**
142      * Specifies that an association is to be fetched when it is first accessed.
143      */
144     const FETCH_LAZY = 2;
145
146     /**
147      * Specifies that an association is to be fetched when the owner of the
148      * association is fetched.
149      */
150     const FETCH_EAGER = 3;
151
152     /**
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.
156      */
157     const FETCH_EXTRA_LAZY = 4;
158
159     /**
160      * Identifies a one-to-one association.
161      */
162     const ONE_TO_ONE = 1;
163
164     /**
165      * Identifies a many-to-one association.
166      */
167     const MANY_TO_ONE = 2;
168
169     /**
170      * Identifies a one-to-many association.
171      */
172     const ONE_TO_MANY = 4;
173
174     /**
175      * Identifies a many-to-many association.
176      */
177     const MANY_TO_MANY = 8;
178
179     /**
180      * Combined bitmask for to-one (single-valued) associations.
181      */
182     const TO_ONE = 3;
183
184     /**
185      * Combined bitmask for to-many (collection-valued) associations.
186      */
187     const TO_MANY = 12;
188
189     /**
190      * READ-ONLY: The name of the entity class.
191      */
192     public $name;
193
194     /**
195      * READ-ONLY: The namespace the entity class is contained in.
196      *
197      * @var string
198      * @todo Not really needed. Usage could be localized.
199      */
200     public $namespace;
201
202     /**
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}.
206      *
207      * @var string
208      */
209     public $rootEntityName;
210
211     /**
212      * READ-ONLY: The definition of custom generator. Only used for CUSTOM
213      * generator type
214      *
215      * The definition has the following structure:
216      * <code>
217      * array(
218      *     'class' => 'ClassName',
219      * )
220      * </code>
221      *
222      * @var array
223      * @todo Merge with tableGeneratorDefinition into generic generatorDefinition
224      */
225     public $customGeneratorDefinition;
226
227     /**
228      * The name of the custom repository class used for the entity class.
229      * (Optional).
230      *
231      * @var string
232      */
233     public $customRepositoryClassName;
234
235     /**
236      * READ-ONLY: Whether this class describes the mapping of a mapped superclass.
237      *
238      * @var boolean
239      */
240     public $isMappedSuperclass = false;
241
242     /**
243      * READ-ONLY: The names of the parent classes (ancestors).
244      *
245      * @var array
246      */
247     public $parentClasses = array();
248
249     /**
250      * READ-ONLY: The names of all subclasses (descendants).
251      *
252      * @var array
253      */
254     public $subClasses = array();
255
256     /**
257      * READ-ONLY: The named queries allowed to be called directly from Repository.
258      *
259      * @var array
260      */
261     public $namedQueries = array();
262
263     /**
264      * READ-ONLY: The named native queries allowed to be called directly from Repository.
265      *
266      * A native SQL named query definition has the following structure:
267      * <pre>
268      * array(
269      *     'name'               => <query name>,
270      *     'query'              => <sql query>,
271      *     'resultClass'        => <class of the result>,
272      *     'resultSetMapping'   => <name of a SqlResultSetMapping>
273      * )
274      * </pre>
275      */
276     public $namedNativeQueries = array();
277
278     /**
279      * READ-ONLY: The mappings of the results of native SQL queries.
280      *
281      * A native result mapping definition has the following structure:
282      * <pre>
283      * array(
284      *     'name'               => <result name>,
285      *     'entities'           => array(<entity result mapping>),
286      *     'columns'            => array(<column result mapping>)
287      * )
288      * </pre>
289      */
290     public $sqlResultSetMappings = array();
291
292     /**
293      * READ-ONLY: The field names of all fields that are part of the identifier/primary key
294      * of the mapped entity class.
295      *
296      * @var array
297      */
298     public $identifier = array();
299
300     /**
301      * READ-ONLY: The inheritance mapping type used by the class.
302      *
303      * @var integer
304      */
305     public $inheritanceType = self::INHERITANCE_TYPE_NONE;
306
307     /**
308      * READ-ONLY: The Id generator type used by the class.
309      *
310      * @var string
311      */
312     public $generatorType = self::GENERATOR_TYPE_NONE;
313
314     /**
315      * READ-ONLY: The field mappings of the class.
316      * Keys are field names and values are mapping definitions.
317      *
318      * The mapping definition array has the following values:
319      *
320      * - <b>fieldName</b> (string)
321      * The name of the field in the Entity.
322      *
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.
326      *
327      * - <b>columnName</b> (string, optional)
328      * The column name. Optional. Defaults to the field name.
329      *
330      * - <b>length</b> (integer, optional)
331      * The database length of the column. Optional. Default value taken from
332      * the type.
333      *
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.
337      *
338      * - <b>nullable</b> (boolean, optional)
339      * Whether the column is nullable. Defaults to FALSE.
340      *
341      * - <b>columnDefinition</b> (string, optional, schema-only)
342      * The SQL fragment that is used when generating the DDL for the column.
343      *
344      * - <b>precision</b> (integer, optional, schema-only)
345      * The precision of a decimal column. Only valid if the column type is decimal.
346      *
347      * - <b>scale</b> (integer, optional, schema-only)
348      * The scale of a decimal column. Only valid if the column type is decimal.
349      *
350      [* - <b>'unique'] (string, optional, schema-only)</b>
351      * Whether a unique constraint should be generated for the column.
352      *
353      * @var array
354      */
355     public $fieldMappings = array();
356
357     /**
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.
361      *
362      * @var array
363      */
364     public $fieldNames = array();
365
366     /**
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.
370      *
371      * @var array
372      * @todo We could get rid of this array by just using $fieldMappings[$fieldName]['columnName'].
373      */
374     public $columnNames = array();
375
376     /**
377      * READ-ONLY: The discriminator value of this class.
378      *
379      * <b>This does only apply to the JOINED and SINGLE_TABLE inheritance mapping strategies
380      * where a discriminator column is used.</b>
381      *
382      * @var mixed
383      * @see discriminatorColumn
384      */
385     public $discriminatorValue;
386
387     /**
388      * READ-ONLY: The discriminator map of all mapped classes in the hierarchy.
389      *
390      * <b>This does only apply to the JOINED and SINGLE_TABLE inheritance mapping strategies
391      * where a discriminator column is used.</b>
392      *
393      * @var mixed
394      * @see discriminatorColumn
395      */
396     public $discriminatorMap = array();
397
398     /**
399      * READ-ONLY: The definition of the discriminator column used in JOINED and SINGLE_TABLE
400      * inheritance mappings.
401      *
402      * @var array
403      */
404     public $discriminatorColumn;
405
406     /**
407      * READ-ONLY: The primary table definition. The definition is an array with the
408      * following entries:
409      *
410      * name => <tableName>
411      * schema => <schemaName>
412      * indexes => array
413      * uniqueConstraints => array
414      *
415      * @var array
416      */
417     public $table;
418
419     /**
420      * READ-ONLY: The registered lifecycle callbacks for entities of this class.
421      *
422      * @var array
423      */
424     public $lifecycleCallbacks = array();
425
426     /**
427      * READ-ONLY: The association mappings of this class.
428      *
429      * The mapping definition array supports the following keys:
430      *
431      * - <b>fieldName</b> (string)
432      * The name of the field in the entity the association is mapped to.
433      *
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.
438      *
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.
442      *
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.
446      *
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).
450      *
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')
454      *
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.
458      *
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.
464      *
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.
469      *
470      * A join table definition has the following structure:
471      * <pre>
472      * array(
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>)
476      * )
477      * </pre>
478      *
479      *
480      * @var array
481      */
482     public $associationMappings = array();
483
484     /**
485      * READ-ONLY: Flag indicating whether the identifier/primary key of the class is composite.
486      *
487      * @var boolean
488      */
489     public $isIdentifierComposite = false;
490
491     /**
492      * READ-ONLY: Flag indicating wheather the identifier/primary key contains at least one foreign key association.
493      *
494      * This flag is necessary because some code blocks require special treatment of this cases.
495      *
496      * @var boolean
497      */
498     public $containsForeignIdentifier = false;
499
500     /**
501      * READ-ONLY: The ID generator used for generating IDs for this class.
502      *
503      * @var \Doctrine\ORM\Id\AbstractIdGenerator
504      * @todo Remove!
505      */
506     public $idGenerator;
507
508     /**
509      * READ-ONLY: The definition of the sequence generator of this class. Only used for the
510      * SEQUENCE generation strategy.
511      *
512      * The definition has the following structure:
513      * <code>
514      * array(
515      *     'sequenceName' => 'name',
516      *     'allocationSize' => 20,
517      *     'initialValue' => 1
518      * )
519      * </code>
520      *
521      * @var array
522      * @todo Merge with tableGeneratorDefinition into generic generatorDefinition
523      */
524     public $sequenceGeneratorDefinition;
525
526     /**
527      * READ-ONLY: The definition of the table generator of this class. Only used for the
528      * TABLE generation strategy.
529      *
530      * @var array
531      * @todo Merge with tableGeneratorDefinition into generic generatorDefinition
532      */
533     public $tableGeneratorDefinition;
534
535     /**
536      * READ-ONLY: The policy used for change-tracking on entities of this class.
537      *
538      * @var integer
539      */
540     public $changeTrackingPolicy = self::CHANGETRACKING_DEFERRED_IMPLICIT;
541
542     /**
543      * READ-ONLY: A flag for whether or not instances of this class are to be versioned
544      * with optimistic locking.
545      *
546      * @var boolean $isVersioned
547      */
548     public $isVersioned;
549
550     /**
551      * READ-ONLY: The name of the field which is used for versioning in optimistic locking (if any).
552      *
553      * @var mixed $versionField
554      */
555     public $versionField;
556
557     /**
558      * The ReflectionClass instance of the mapped class.
559      *
560      * @var ReflectionClass
561      */
562     public $reflClass;
563
564     /**
565      * Is this entity marked as "read-only"?
566      *
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).
570      *
571      * @var bool
572      */
573     public $isReadOnly = false;
574
575     /**
576      * NamingStrategy determining the default column and table names
577      *
578      * @var \Doctrine\ORM\Mapping\NamingStrategy
579      */
580     protected $namingStrategy;
581
582     /**
583      * The ReflectionProperty instances of the mapped class.
584      *
585      * @var array
586      */
587     public $reflFields = array();
588
589     /**
590      * The prototype from which new instances of the mapped class are created.
591      *
592      * @var object
593      */
594     private $_prototype;
595
596     /**
597      * Initializes a new ClassMetadata instance that will hold the object-relational mapping
598      * metadata of the class with the given name.
599      *
600      * @param string $entityName The name of the entity class the new instance is used for.
601      * @param NamingStrategy $namingStrategy
602      */
603     public function __construct($entityName, NamingStrategy $namingStrategy = null)
604     {
605         $this->name = $entityName;
606         $this->rootEntityName = $entityName;
607         $this->namingStrategy = $namingStrategy ?: new DefaultNamingStrategy();
608     }
609
610     /**
611      * Gets the ReflectionPropertys of the mapped class.
612      *
613      * @return array An array of ReflectionProperty instances.
614      */
615     public function getReflectionProperties()
616     {
617         return $this->reflFields;
618     }
619
620     /**
621      * Gets a ReflectionProperty for a specific field of the mapped class.
622      *
623      * @param string $name
624      * @return \ReflectionProperty
625      */
626     public function getReflectionProperty($name)
627     {
628         return $this->reflFields[$name];
629     }
630
631     /**
632      * Gets the ReflectionProperty for the single identifier field.
633      *
634      * @return \ReflectionProperty
635      * @throws BadMethodCallException If the class has a composite identifier.
636      */
637     public function getSingleIdReflectionProperty()
638     {
639         if ($this->isIdentifierComposite) {
640             throw new BadMethodCallException("Class " . $this->name . " has a composite identifier.");
641         }
642         return $this->reflFields[$this->identifier[0]];
643     }
644
645     /**
646      * Extracts the identifier values of an entity of this class.
647      *
648      * For composite identifiers, the identifier values are returned as an array
649      * with the same order as the field order in {@link identifier}.
650      *
651      * @param object $entity
652      * @return array
653      */
654     public function getIdentifierValues($entity)
655     {
656         if ($this->isIdentifierComposite) {
657             $id = array();
658
659             foreach ($this->identifier as $idField) {
660                 $value = $this->reflFields[$idField]->getValue($entity);
661
662                 if ($value !== null) {
663                     $id[$idField] = $value;
664                 }
665             }
666
667             return $id;
668         }
669
670         $value = $this->reflFields[$this->identifier[0]]->getValue($entity);
671
672         if ($value !== null) {
673             return array($this->identifier[0] => $value);
674         }
675
676         return array();
677     }
678
679     /**
680      * Populates the entity identifier of an entity.
681      *
682      * @param object $entity
683      * @param mixed $id
684      * @todo Rename to assignIdentifier()
685      */
686     public function setIdentifierValues($entity, array $id)
687     {
688         foreach ($id as $idField => $idValue) {
689             $this->reflFields[$idField]->setValue($entity, $idValue);
690         }
691     }
692
693     /**
694      * Sets the specified field to the specified value on the given entity.
695      *
696      * @param object $entity
697      * @param string $field
698      * @param mixed $value
699      */
700     public function setFieldValue($entity, $field, $value)
701     {
702         $this->reflFields[$field]->setValue($entity, $value);
703     }
704
705     /**
706      * Gets the specified field's value off the given entity.
707      *
708      * @param object $entity
709      * @param string $field
710      */
711     public function getFieldValue($entity, $field)
712     {
713         return $this->reflFields[$field]->getValue($entity);
714     }
715
716     /**
717      * Creates a string representation of this instance.
718      *
719      * @return string The string representation of this instance.
720      * @todo Construct meaningful string representation.
721      */
722     public function __toString()
723     {
724         return __CLASS__ . '@' . spl_object_hash($this);
725     }
726
727     /**
728      * Determines which fields get serialized.
729      *
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.
733      *
734      * Parts that are also NOT serialized because they can not be properly unserialized:
735      *      - reflClass (ReflectionClass)
736      *      - reflFields (ReflectionProperty array)
737      *
738      * @return array The names of all the fields that should be serialized.
739      */
740     public function __sleep()
741     {
742         // This metadata is always serialized/cached.
743         $serialized = array(
744             'associationMappings',
745             'columnNames', //TODO: Not really needed. Can use fieldMappings[$fieldName]['columnName']
746             'fieldMappings',
747             'fieldNames',
748             'identifier',
749             'isIdentifierComposite', // TODO: REMOVE
750             'name',
751             'namespace', // TODO: REMOVE
752             'table',
753             'rootEntityName',
754             'idGenerator', //TODO: Does not really need to be serialized. Could be moved to runtime.
755         );
756
757         // The rest of the metadata is only serialized if necessary.
758         if ($this->changeTrackingPolicy != self::CHANGETRACKING_DEFERRED_IMPLICIT) {
759             $serialized[] = 'changeTrackingPolicy';
760         }
761
762         if ($this->customRepositoryClassName) {
763             $serialized[] = 'customRepositoryClassName';
764         }
765
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';
773         }
774
775         if ($this->generatorType != self::GENERATOR_TYPE_NONE) {
776             $serialized[] = 'generatorType';
777             if ($this->generatorType == self::GENERATOR_TYPE_SEQUENCE) {
778                 $serialized[] = 'sequenceGeneratorDefinition';
779             }
780         }
781
782         if ($this->isMappedSuperclass) {
783             $serialized[] = 'isMappedSuperclass';
784         }
785
786         if ($this->containsForeignIdentifier) {
787             $serialized[] = 'containsForeignIdentifier';
788         }
789
790         if ($this->isVersioned) {
791             $serialized[] = 'isVersioned';
792             $serialized[] = 'versionField';
793         }
794
795         if ($this->lifecycleCallbacks) {
796             $serialized[] = 'lifecycleCallbacks';
797         }
798
799         if ($this->namedQueries) {
800             $serialized[] = 'namedQueries';
801         }
802
803         if ($this->namedNativeQueries) {
804             $serialized[] = 'namedNativeQueries';
805         }
806
807         if ($this->sqlResultSetMappings) {
808             $serialized[] = 'sqlResultSetMappings';
809         }
810
811         if ($this->isReadOnly) {
812             $serialized[] = 'isReadOnly';
813         }
814
815         if ($this->customGeneratorDefinition) {
816             $serialized[] = "customGeneratorDefinition";
817         }
818
819         return $serialized;
820     }
821
822     /**
823      * Creates a new instance of the mapped class, without invoking the constructor.
824      *
825      * @return object
826      */
827     public function newInstance()
828     {
829         if ($this->_prototype === null) {
830             $this->_prototype = unserialize(sprintf('O:%d:"%s":0:{}', strlen($this->name), $this->name));
831         }
832
833         return clone $this->_prototype;
834     }
835     /**
836      * Restores some state that can not be serialized/unserialized.
837      *
838      * @param \Doctrine\Common\Persistence\Mapping\ReflectionService $reflService
839      * @return void
840      */
841     public function wakeupReflection($reflService)
842     {
843         // Restore ReflectionClass and properties
844         $this->reflClass = $reflService->getClass($this->name);
845
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);
850         }
851
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);
856         }
857     }
858
859     /**
860      * Initializes a new ClassMetadata instance that will hold the object-relational mapping
861      * metadata of the class with the given name.
862      *
863      * @param \Doctrine\Common\Persistence\Mapping\ReflectionService $reflService The reflection service.
864      */
865     public function initializeReflection($reflService)
866     {
867         $this->reflClass = $reflService->getClass($this->name);
868         $this->namespace = $reflService->getClassNamespace($this->name);
869
870         if ($this->reflClass) {
871             $this->name = $this->rootEntityName = $this->reflClass->getName();
872         }
873
874         $this->table['name'] = $this->namingStrategy->classToTableName($this->name);
875     }
876
877     /**
878      * Validate Identifier
879      *
880      * @throws MappingException
881      * @return void
882      */
883     public function validateIdentifier()
884     {
885         // Verify & complete identifier mapping
886         if ( ! $this->identifier && ! $this->isMappedSuperclass) {
887             throw MappingException::identifierRequired($this->name);
888         }
889
890         if ($this->usesIdGenerator() && $this->isIdentifierComposite) {
891             throw MappingException::compositeKeyAssignedIdGeneratorRequired($this->name);
892         }
893     }
894
895     /**
896      * Validate association targets actually exist.
897      *
898      * @throws MappingException
899      * @return void
900      */
901     public function validateAssocations()
902     {
903         foreach ($this->associationMappings as $mapping) {
904             if ( ! ClassLoader::classExists($mapping['targetEntity']) ) {
905                 throw MappingException::invalidTargetEntityClass($mapping['targetEntity'], $this->name, $mapping['fieldName']);
906             }
907         }
908     }
909
910     /**
911      * Validate lifecycle callbacks
912      *
913      * @param \Doctrine\Common\Persistence\Mapping\ReflectionService $reflService
914      * @throws MappingException
915      * @return void
916      */
917     public function validateLifecycleCallbacks($reflService)
918     {
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);
923                 }
924             }
925         }
926     }
927
928     /**
929      * {@inheritDoc}
930      */
931     public function getReflectionClass()
932     {
933         return $this->reflClass;
934     }
935
936     /**
937      * Sets the change tracking policy used by this class.
938      *
939      * @param integer $policy
940      */
941     public function setChangeTrackingPolicy($policy)
942     {
943         $this->changeTrackingPolicy = $policy;
944     }
945
946     /**
947      * Whether the change tracking policy of this class is "deferred explicit".
948      *
949      * @return boolean
950      */
951     public function isChangeTrackingDeferredExplicit()
952     {
953         return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_EXPLICIT;
954     }
955
956     /**
957      * Whether the change tracking policy of this class is "deferred implicit".
958      *
959      * @return boolean
960      */
961     public function isChangeTrackingDeferredImplicit()
962     {
963         return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_IMPLICIT;
964     }
965
966     /**
967      * Whether the change tracking policy of this class is "notify".
968      *
969      * @return boolean
970      */
971     public function isChangeTrackingNotify()
972     {
973         return $this->changeTrackingPolicy == self::CHANGETRACKING_NOTIFY;
974     }
975
976     /**
977      * Checks whether a field is part of the identifier/primary key field(s).
978      *
979      * @param string $fieldName  The field name
980      * @return boolean  TRUE if the field is part of the table identifier/primary key field(s),
981      *                  FALSE otherwise.
982      */
983     public function isIdentifier($fieldName)
984     {
985         if ( ! $this->isIdentifierComposite) {
986             return $fieldName === $this->identifier[0];
987         }
988         return in_array($fieldName, $this->identifier);
989     }
990
991     /**
992      * Check if the field is unique.
993      *
994      * @param string $fieldName  The field name
995      * @return boolean  TRUE if the field is unique, FALSE otherwise.
996      */
997     public function isUniqueField($fieldName)
998     {
999         $mapping = $this->getFieldMapping($fieldName);
1000         if ($mapping !== false) {
1001             return isset($mapping['unique']) && $mapping['unique'] == true;
1002         }
1003         return false;
1004     }
1005
1006     /**
1007      * Check if the field is not null.
1008      *
1009      * @param string $fieldName  The field name
1010      * @return boolean  TRUE if the field is not null, FALSE otherwise.
1011      */
1012     public function isNullable($fieldName)
1013     {
1014         $mapping = $this->getFieldMapping($fieldName);
1015         if ($mapping !== false) {
1016             return isset($mapping['nullable']) && $mapping['nullable'] == true;
1017         }
1018         return false;
1019     }
1020
1021     /**
1022      * Gets a column name for a field name.
1023      * If the column name for the field cannot be found, the given field name
1024      * is returned.
1025      *
1026      * @param string $fieldName The field name.
1027      * @return string  The column name.
1028      */
1029     public function getColumnName($fieldName)
1030     {
1031         return isset($this->columnNames[$fieldName]) ?
1032                 $this->columnNames[$fieldName] : $fieldName;
1033     }
1034
1035     /**
1036      * Gets the mapping of a (regular) field that holds some data but not a
1037      * reference to another object.
1038      *
1039      * @param string $fieldName  The field name.
1040      * @throws MappingException
1041      * @return array The field mapping.
1042      */
1043     public function getFieldMapping($fieldName)
1044     {
1045         if ( ! isset($this->fieldMappings[$fieldName])) {
1046             throw MappingException::mappingNotFound($this->name, $fieldName);
1047         }
1048         return $this->fieldMappings[$fieldName];
1049     }
1050
1051     /**
1052      * Gets the mapping of an association.
1053      *
1054      * @see ClassMetadataInfo::$associationMappings
1055      * @param string $fieldName  The field name that represents the association in
1056      *                           the object model.
1057      * @throws MappingException
1058      * @return array The mapping.
1059      */
1060     public function getAssociationMapping($fieldName)
1061     {
1062         if ( ! isset($this->associationMappings[$fieldName])) {
1063             throw MappingException::mappingNotFound($this->name, $fieldName);
1064         }
1065         return $this->associationMappings[$fieldName];
1066     }
1067
1068     /**
1069      * Gets all association mappings of the class.
1070      *
1071      * @return array
1072      */
1073     public function getAssociationMappings()
1074     {
1075         return $this->associationMappings;
1076     }
1077
1078     /**
1079      * Gets the field name for a column name.
1080      * If no field name can be found the column name is returned.
1081      *
1082      * @param string $columnName    column name
1083      * @return string               column alias
1084      */
1085     public function getFieldName($columnName)
1086     {
1087         return isset($this->fieldNames[$columnName]) ?
1088                 $this->fieldNames[$columnName] : $columnName;
1089     }
1090
1091     /**
1092      * Gets the named query.
1093      *
1094      * @see ClassMetadataInfo::$namedQueries
1095      * @throws MappingException
1096      * @param string $queryName The query name
1097      * @return string
1098      */
1099     public function getNamedQuery($queryName)
1100     {
1101         if ( ! isset($this->namedQueries[$queryName])) {
1102             throw MappingException::queryNotFound($this->name, $queryName);
1103         }
1104         return $this->namedQueries[$queryName]['dql'];
1105     }
1106
1107     /**
1108      * Gets all named queries of the class.
1109      *
1110      * @return array
1111      */
1112     public function getNamedQueries()
1113     {
1114         return $this->namedQueries;
1115     }
1116
1117     /**
1118      * Gets the named native query.
1119      *
1120      * @see ClassMetadataInfo::$namedNativeQueries
1121      * @throws  MappingException
1122      * @param   string $queryName The query name
1123      * @return  array
1124      */
1125     public function getNamedNativeQuery($queryName)
1126     {
1127         if ( ! isset($this->namedNativeQueries[$queryName])) {
1128             throw MappingException::queryNotFound($this->name, $queryName);
1129         }
1130
1131         return $this->namedNativeQueries[$queryName];
1132     }
1133
1134     /**
1135      * Gets all named native queries of the class.
1136      *
1137      * @return array
1138      */
1139     public function getNamedNativeQueries()
1140     {
1141         return $this->namedNativeQueries;
1142     }
1143
1144     /**
1145      * Gets the result set mapping.
1146      *
1147      * @see ClassMetadataInfo::$sqlResultSetMappings
1148      * @throws  MappingException
1149      * @param   string $name The result set mapping name
1150      * @return  array
1151      */
1152     public function getSqlResultSetMapping($name)
1153     {
1154         if ( ! isset($this->sqlResultSetMappings[$name])) {
1155             throw MappingException::resultMappingNotFound($this->name, $name);
1156         }
1157
1158         return $this->sqlResultSetMappings[$name];
1159     }
1160
1161     /**
1162      * Gets all sql result set mappings of the class.
1163      *
1164      * @return array
1165      */
1166     public function getSqlResultSetMappings()
1167     {
1168         return $this->sqlResultSetMappings;
1169     }
1170
1171     /**
1172      * Validates & completes the given field mapping.
1173      *
1174      * @param array $mapping  The field mapping to validated & complete.
1175      * @throws MappingException
1176      * @return array The validated and completed field mapping.
1177      */
1178     protected function _validateAndCompleteFieldMapping(array &$mapping)
1179     {
1180         // Check mandatory fields
1181         if ( ! isset($mapping['fieldName']) || strlen($mapping['fieldName']) == 0) {
1182             throw MappingException::missingFieldName($this->name);
1183         }
1184         if ( ! isset($mapping['type'])) {
1185             // Default to string
1186             $mapping['type'] = 'string';
1187         }
1188
1189         // Complete fieldName and columnName mapping
1190         if ( ! isset($mapping['columnName'])) {
1191             $mapping['columnName'] = $this->namingStrategy->propertyToColumnName($mapping['fieldName']);
1192         }
1193
1194         if ($mapping['columnName'][0] === '`') {
1195             $mapping['columnName']  = trim($mapping['columnName'], '`');
1196             $mapping['quoted']      = true;
1197         }
1198
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']);
1202         }
1203
1204         $this->fieldNames[$mapping['columnName']] = $mapping['fieldName'];
1205
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']);
1210             }
1211
1212             if ( ! in_array($mapping['fieldName'], $this->identifier)) {
1213                 $this->identifier[] = $mapping['fieldName'];
1214             }
1215             // Check for composite key
1216             if ( ! $this->isIdentifierComposite && count($this->identifier) > 1) {
1217                 $this->isIdentifierComposite = true;
1218             }
1219         }
1220
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']);
1224             }
1225
1226             $mapping['requireSQLConversion'] = true;
1227         }
1228     }
1229
1230     /**
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).
1233      *
1234      * @param array $mapping The mapping.
1235      * @return array The updated mapping.
1236      * @throws MappingException If something is wrong with the mapping.
1237      */
1238     protected function _validateAndCompleteAssociationMapping(array $mapping)
1239     {
1240         if ( ! isset($mapping['mappedBy'])) {
1241             $mapping['mappedBy'] = null;
1242         }
1243         if ( ! isset($mapping['inversedBy'])) {
1244             $mapping['inversedBy'] = null;
1245         }
1246         $mapping['isOwningSide'] = true; // assume owning side until we hit mappedBy
1247
1248         // unset optional indexBy attribute if its empty
1249         if ( ! isset($mapping['indexBy']) || !$mapping['indexBy']) {
1250             unset($mapping['indexBy']);
1251         }
1252
1253         // If targetEntity is unqualified, assume it is in the same namespace as
1254         // the sourceEntity.
1255         $mapping['sourceEntity'] = $this->name;
1256
1257         if (isset($mapping['targetEntity'])) {
1258             if (strlen($this->namespace) > 0 && strpos($mapping['targetEntity'], '\\') === false) {
1259                 $mapping['targetEntity'] = $this->namespace . '\\' . $mapping['targetEntity'];
1260             }
1261
1262             $mapping['targetEntity'] = ltrim($mapping['targetEntity'], '\\');
1263         }
1264
1265         if ( ($mapping['type'] & self::MANY_TO_ONE) > 0 &&
1266                 isset($mapping['orphanRemoval']) &&
1267                 $mapping['orphanRemoval'] == true) {
1268
1269             throw MappingException::illegalOrphanRemoval($this->name, $mapping['fieldName']);
1270         }
1271
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']);
1276             }
1277
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']
1282                     );
1283                 }
1284
1285                 $this->identifier[] = $mapping['fieldName'];
1286                 $this->containsForeignIdentifier = true;
1287             }
1288             // Check for composite key
1289             if ( ! $this->isIdentifierComposite && count($this->identifier) > 1) {
1290                 $this->isIdentifierComposite = true;
1291             }
1292         }
1293
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);
1298         }
1299         if ( ! isset($mapping['targetEntity'])) {
1300             throw MappingException::missingTargetEntity($mapping['fieldName']);
1301         }
1302
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;
1309                 }
1310             }
1311         } else {
1312             $mapping['isOwningSide'] = false;
1313         }
1314
1315         if (isset($mapping['id']) && $mapping['id'] === true && $mapping['type'] & self::TO_MANY) {
1316             throw MappingException::illegalToManyIdentifierAssoaction($this->name, $mapping['fieldName']);
1317         }
1318
1319         // Fetch mode. Default fetch mode to LAZY, if not set.
1320         if ( ! isset($mapping['fetch'])) {
1321             $mapping['fetch'] = self::FETCH_LAZY;
1322         }
1323
1324         // Cascades
1325         $cascades = isset($mapping['cascade']) ? array_map('strtolower', $mapping['cascade']) : array();
1326
1327         if (in_array('all', $cascades)) {
1328             $cascades = array('remove', 'persist', 'refresh', 'merge', 'detach');
1329         }
1330
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'))),
1334                 $this->name,
1335                 $mapping['fieldName']
1336             );
1337         }
1338
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);
1345
1346         return $mapping;
1347     }
1348
1349     /**
1350      * Validates & completes a one-to-one association mapping.
1351      *
1352      * @param array $mapping  The mapping to validate & complete.
1353      * @throws RuntimeException
1354      * @throws MappingException
1355      * @return array The validated & completed mapping.@override
1356      */
1357     protected function _validateAndCompleteOneToOneMapping(array $mapping)
1358     {
1359         $mapping = $this->_validateAndCompleteAssociationMapping($mapping);
1360
1361         if (isset($mapping['joinColumns']) && $mapping['joinColumns']) {
1362             $mapping['isOwningSide'] = true;
1363         }
1364
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()
1371                 ));
1372             }
1373
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;
1380                         }
1381                     } else {
1382                         $uniqueContraintColumns[] = $joinColumn['name'];
1383                     }
1384                 }
1385
1386                 if (empty($joinColumn['name'])) {
1387                     $joinColumn['name'] = $this->namingStrategy->joinColumnName($mapping['fieldName']);
1388                 }
1389
1390                 if (empty($joinColumn['referencedColumnName'])) {
1391                     $joinColumn['referencedColumnName'] = $this->namingStrategy->referenceColumnName();
1392                 }
1393
1394                 if ($joinColumn['name'][0] === '`') {
1395                     $joinColumn['name']   = trim($joinColumn['name'], '`');
1396                     $joinColumn['quoted'] = true;
1397                 }
1398
1399                 if ($joinColumn['referencedColumnName'][0] === '`') {
1400                     $joinColumn['referencedColumnName'] = trim($joinColumn['referencedColumnName'], '`');
1401                     $joinColumn['quoted']               = true;
1402                 }
1403
1404                 $mapping['sourceToTargetKeyColumns'][$joinColumn['name']] = $joinColumn['referencedColumnName'];
1405                 $mapping['joinColumnFieldNames'][$joinColumn['name']] = isset($joinColumn['fieldName'])
1406                         ? $joinColumn['fieldName'] : $joinColumn['name'];
1407             }
1408
1409             if ($uniqueContraintColumns) {
1410                 if ( ! $this->table) {
1411                     throw new RuntimeException("ClassMetadataInfo::setTable() has to be called before defining a one to one relationship.");
1412                 }
1413                 $this->table['uniqueConstraints'][$mapping['fieldName']."_uniq"] = array(
1414                     'columns' => $uniqueContraintColumns
1415                 );
1416             }
1417
1418             $mapping['targetToSourceKeyColumns'] = array_flip($mapping['sourceToTargetKeyColumns']);
1419         }
1420
1421         $mapping['orphanRemoval']   = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false;
1422         $mapping['isCascadeRemove'] = $mapping['orphanRemoval'] ? true : $mapping['isCascadeRemove'];
1423
1424         if (isset($mapping['id']) && $mapping['id'] === true && !$mapping['isOwningSide']) {
1425             throw MappingException::illegalInverseIdentifierAssocation($this->name, $mapping['fieldName']);
1426         }
1427
1428         return $mapping;
1429     }
1430
1431     /**
1432      * Validates and completes the mapping.
1433      *
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
1438      */
1439     protected function _validateAndCompleteOneToManyMapping(array $mapping)
1440     {
1441         $mapping = $this->_validateAndCompleteAssociationMapping($mapping);
1442
1443         // OneToMany-side MUST be inverse (must have mappedBy)
1444         if ( ! isset($mapping['mappedBy'])) {
1445             throw MappingException::oneToManyRequiresMappedBy($mapping['fieldName']);
1446         }
1447
1448         $mapping['orphanRemoval']   = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false;
1449         $mapping['isCascadeRemove'] = $mapping['orphanRemoval'] ? true : $mapping['isCascadeRemove'];
1450
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']));
1454             }
1455         }
1456
1457         return $mapping;
1458     }
1459
1460     protected function _validateAndCompleteManyToManyMapping(array $mapping)
1461     {
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']);
1467             }
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'));
1473             }
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'));
1479             }
1480
1481             foreach ($mapping['joinTable']['joinColumns'] as &$joinColumn) {
1482                 if (empty($joinColumn['name'])) {
1483                     $joinColumn['name'] = $this->namingStrategy->joinKeyColumnName($mapping['sourceEntity'], $joinColumn['referencedColumnName']);
1484                 }
1485
1486                 if (empty($joinColumn['referencedColumnName'])) {
1487                     $joinColumn['referencedColumnName'] = $this->namingStrategy->referenceColumnName();
1488                 }
1489
1490                 if ($joinColumn['name'][0] === '`') {
1491                     $joinColumn['name']   = trim($joinColumn['name'], '`');
1492                     $joinColumn['quoted'] = true;
1493                 }
1494
1495                 if ($joinColumn['referencedColumnName'][0] === '`') {
1496                     $joinColumn['referencedColumnName'] = trim($joinColumn['referencedColumnName'], '`');
1497                     $joinColumn['quoted']               = true;
1498                 }
1499
1500                 if (isset($joinColumn['onDelete']) && strtolower($joinColumn['onDelete']) == 'cascade') {
1501                     $mapping['isOnDeleteCascade'] = true;
1502                 }
1503
1504                 $mapping['relationToSourceKeyColumns'][$joinColumn['name']] = $joinColumn['referencedColumnName'];
1505                 $mapping['joinTableColumns'][] = $joinColumn['name'];
1506             }
1507
1508             foreach ($mapping['joinTable']['inverseJoinColumns'] as &$inverseJoinColumn) {
1509                 if (empty($inverseJoinColumn['name'])) {
1510                     $inverseJoinColumn['name'] = $this->namingStrategy->joinKeyColumnName($mapping['targetEntity'], $inverseJoinColumn['referencedColumnName']);
1511                 }
1512
1513                 if (empty($inverseJoinColumn['referencedColumnName'])) {
1514                     $inverseJoinColumn['referencedColumnName'] = $this->namingStrategy->referenceColumnName();
1515                 }
1516
1517                 if ($inverseJoinColumn['name'][0] === '`') {
1518                     $inverseJoinColumn['name']   = trim($inverseJoinColumn['name'], '`');
1519                     $inverseJoinColumn['quoted'] = true;
1520                 }
1521
1522                 if ($inverseJoinColumn['referencedColumnName'][0] === '`') {
1523                     $inverseJoinColumn['referencedColumnName']  = trim($inverseJoinColumn['referencedColumnName'], '`');
1524                     $inverseJoinColumn['quoted']                = true;
1525                 }
1526
1527                 if (isset($inverseJoinColumn['onDelete']) && strtolower($inverseJoinColumn['onDelete']) == 'cascade') {
1528                     $mapping['isOnDeleteCascade'] = true;
1529                 }
1530
1531                 $mapping['relationToTargetKeyColumns'][$inverseJoinColumn['name']] = $inverseJoinColumn['referencedColumnName'];
1532                 $mapping['joinTableColumns'][] = $inverseJoinColumn['name'];
1533             }
1534         }
1535
1536         $mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false;
1537
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']));
1541             }
1542         }
1543
1544         return $mapping;
1545     }
1546
1547     /**
1548      * {@inheritDoc}
1549      */
1550     public function getIdentifierFieldNames()
1551     {
1552         return $this->identifier;
1553     }
1554
1555     /**
1556      * Gets the name of the single id field. Note that this only works on
1557      * entity classes that have a single-field pk.
1558      *
1559      * @return string
1560      * @throws MappingException If the class has a composite primary key.
1561      */
1562     public function getSingleIdentifierFieldName()
1563     {
1564         if ($this->isIdentifierComposite) {
1565             throw MappingException::singleIdNotAllowedOnCompositePrimaryKey($this->name);
1566         }
1567         return $this->identifier[0];
1568     }
1569
1570     /**
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.
1573      *
1574      * @return string
1575      * @throws MappingException If the class has a composite primary key.
1576      */
1577     public function getSingleIdentifierColumnName()
1578     {
1579         return $this->getColumnName($this->getSingleIdentifierFieldName());
1580     }
1581
1582     /**
1583      * INTERNAL:
1584      * Sets the mapped identifier/primary key fields of this class.
1585      * Mainly used by the ClassMetadataFactory to assign inherited identifiers.
1586      *
1587      * @param array $identifier
1588      */
1589     public function setIdentifier(array $identifier)
1590     {
1591         $this->identifier = $identifier;
1592         $this->isIdentifierComposite = (count($this->identifier) > 1);
1593     }
1594
1595     /**
1596      * Gets the mapped identifier field of this class.
1597      *
1598      * @return array|string $identifier
1599      */
1600     public function getIdentifier()
1601     {
1602         return $this->identifier;
1603     }
1604
1605     /**
1606      * {@inheritDoc}
1607      */
1608     public function hasField($fieldName)
1609     {
1610         return isset($this->fieldMappings[$fieldName]);
1611     }
1612
1613     /**
1614      * Gets an array containing all the column names.
1615      *
1616      * @param array $fieldNames
1617      * @return array
1618      */
1619     public function getColumnNames(array $fieldNames = null)
1620     {
1621         if ($fieldNames === null) {
1622             return array_keys($this->fieldNames);
1623         } else {
1624             $columnNames = array();
1625             foreach ($fieldNames as $fieldName) {
1626                 $columnNames[] = $this->getColumnName($fieldName);
1627             }
1628             return $columnNames;
1629         }
1630     }
1631
1632     /**
1633      * Returns an array with all the identifier column names.
1634      *
1635      * @return array
1636      */
1637     public function getIdentifierColumnNames()
1638     {
1639         $columnNames = array();
1640
1641         foreach ($this->identifier as $idProperty) {
1642             if (isset($this->fieldMappings[$idProperty])) {
1643                 $columnNames[] = $this->fieldMappings[$idProperty]['columnName'];
1644
1645                 continue;
1646             }
1647
1648             // Association defined as Id field
1649             $joinColumns      = $this->associationMappings[$idProperty]['joinColumns'];
1650             $assocColumnNames = array_map(function ($joinColumn) { return $joinColumn['name']; }, $joinColumns);
1651
1652             $columnNames = array_merge($columnNames, $assocColumnNames);
1653         }
1654
1655         return $columnNames;
1656     }
1657
1658     /**
1659      * Sets the type of Id generator to use for the mapped class.
1660      */
1661     public function setIdGeneratorType($generatorType)
1662     {
1663         $this->generatorType = $generatorType;
1664     }
1665
1666     /**
1667      * Checks whether the mapped class uses an Id generator.
1668      *
1669      * @return boolean TRUE if the mapped class uses an Id generator, FALSE otherwise.
1670      */
1671     public function usesIdGenerator()
1672     {
1673         return $this->generatorType != self::GENERATOR_TYPE_NONE;
1674     }
1675
1676     /**
1677      * @return boolean
1678      */
1679     public function isInheritanceTypeNone()
1680     {
1681         return $this->inheritanceType == self::INHERITANCE_TYPE_NONE;
1682     }
1683
1684     /**
1685      * Checks whether the mapped class uses the JOINED inheritance mapping strategy.
1686      *
1687      * @return boolean TRUE if the class participates in a JOINED inheritance mapping,
1688      *                 FALSE otherwise.
1689      */
1690     public function isInheritanceTypeJoined()
1691     {
1692         return $this->inheritanceType == self::INHERITANCE_TYPE_JOINED;
1693     }
1694
1695     /**
1696      * Checks whether the mapped class uses the SINGLE_TABLE inheritance mapping strategy.
1697      *
1698      * @return boolean TRUE if the class participates in a SINGLE_TABLE inheritance mapping,
1699      *                 FALSE otherwise.
1700      */
1701     public function isInheritanceTypeSingleTable()
1702     {
1703         return $this->inheritanceType == self::INHERITANCE_TYPE_SINGLE_TABLE;
1704     }
1705
1706     /**
1707      * Checks whether the mapped class uses the TABLE_PER_CLASS inheritance mapping strategy.
1708      *
1709      * @return boolean TRUE if the class participates in a TABLE_PER_CLASS inheritance mapping,
1710      *                 FALSE otherwise.
1711      */
1712     public function isInheritanceTypeTablePerClass()
1713     {
1714         return $this->inheritanceType == self::INHERITANCE_TYPE_TABLE_PER_CLASS;
1715     }
1716
1717     /**
1718      * Checks whether the class uses an identity column for the Id generation.
1719      *
1720      * @return boolean TRUE if the class uses the IDENTITY generator, FALSE otherwise.
1721      */
1722     public function isIdGeneratorIdentity()
1723     {
1724         return $this->generatorType == self::GENERATOR_TYPE_IDENTITY;
1725     }
1726
1727     /**
1728      * Checks whether the class uses a sequence for id generation.
1729      *
1730      * @return boolean TRUE if the class uses the SEQUENCE generator, FALSE otherwise.
1731      */
1732     public function isIdGeneratorSequence()
1733     {
1734         return $this->generatorType == self::GENERATOR_TYPE_SEQUENCE;
1735     }
1736
1737     /**
1738      * Checks whether the class uses a table for id generation.
1739      *
1740      * @return boolean  TRUE if the class uses the TABLE generator, FALSE otherwise.
1741      */
1742     public function isIdGeneratorTable()
1743     {
1744         return $this->generatorType == self::GENERATOR_TYPE_TABLE;
1745     }
1746
1747     /**
1748      * Checks whether the class has a natural identifier/pk (which means it does
1749      * not use any Id generator.
1750      *
1751      * @return boolean
1752      */
1753     public function isIdentifierNatural()
1754     {
1755         return $this->generatorType == self::GENERATOR_TYPE_NONE;
1756     }
1757
1758     /**
1759      * Checks whether the class use a UUID for id generation
1760      *
1761      * @return boolean
1762      */
1763     public function isIdentifierUuid()
1764     {
1765         return $this->generatorType == self::GENERATOR_TYPE_UUID;
1766     }
1767
1768     /**
1769      * Gets the type of a field.
1770      *
1771      * @param string $fieldName
1772      * @return \Doctrine\DBAL\Types\Type|string
1773      */
1774     public function getTypeOfField($fieldName)
1775     {
1776         return isset($this->fieldMappings[$fieldName]) ?
1777                 $this->fieldMappings[$fieldName]['type'] : null;
1778     }
1779
1780     /**
1781      * Gets the type of a column.
1782      *
1783      * @param string $columnName
1784      * @return \Doctrine\DBAL\Types\Type
1785      */
1786     public function getTypeOfColumn($columnName)
1787     {
1788         return $this->getTypeOfField($this->getFieldName($columnName));
1789     }
1790
1791     /**
1792      * Gets the name of the primary table.
1793      *
1794      * @return string
1795      */
1796     public function getTableName()
1797     {
1798         return $this->table['name'];
1799     }
1800
1801     /**
1802      * Gets the table name to use for temporary identifier tables of this class.
1803      *
1804      * @return string
1805      */
1806     public function getTemporaryIdTableName()
1807     {
1808         // replace dots with underscores because PostgreSQL creates temporary tables in a special schema
1809         return str_replace('.', '_', $this->getTableName() . '_id_tmp');
1810     }
1811
1812     /**
1813      * Sets the mapped subclasses of this class.
1814      *
1815      * @param array $subclasses The names of all mapped subclasses.
1816      */
1817     public function setSubclasses(array $subclasses)
1818     {
1819         foreach ($subclasses as $subclass) {
1820             if (strpos($subclass, '\\') === false && strlen($this->namespace)) {
1821                 $this->subClasses[] = $this->namespace . '\\' . $subclass;
1822             } else {
1823                 $this->subClasses[] = $subclass;
1824             }
1825         }
1826     }
1827
1828     /**
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.
1832      */
1833     public function setParentClasses(array $classNames)
1834     {
1835         $this->parentClasses = $classNames;
1836         if (count($classNames) > 0) {
1837             $this->rootEntityName = array_pop($classNames);
1838         }
1839     }
1840
1841     /**
1842      * Sets the inheritance type used by the class and it's subclasses.
1843      *
1844      * @param integer $type
1845      * @throws MappingException
1846      * @return void
1847      */
1848     public function setInheritanceType($type)
1849     {
1850         if ( ! $this->_isInheritanceType($type)) {
1851             throw MappingException::invalidInheritanceType($this->name, $type);
1852         }
1853         $this->inheritanceType = $type;
1854     }
1855
1856     /**
1857      * Sets the association to override association mapping of property for an entity relationship.
1858      *
1859      * @param string $fieldName
1860      * @param array $overrideMapping
1861      * @throws MappingException
1862      * @return void
1863      */
1864     public function setAssociationOverride($fieldName, array $overrideMapping)
1865     {
1866         if ( ! isset($this->associationMappings[$fieldName])) {
1867             throw MappingException::invalidOverrideFieldName($this->name, $fieldName);
1868         }
1869
1870         $mapping = $this->associationMappings[$fieldName];
1871
1872         if (isset($overrideMapping['joinColumns'])) {
1873             $mapping['joinColumns'] = $overrideMapping['joinColumns'];
1874         }
1875
1876         if (isset($overrideMapping['joinTable'])) {
1877             $mapping['joinTable'] = $overrideMapping['joinTable'];
1878         }
1879
1880         $mapping['joinColumnFieldNames']        = null;
1881         $mapping['joinTableColumns']            = null;
1882         $mapping['sourceToTargetKeyColumns']    = null;
1883         $mapping['relationToSourceKeyColumns']  = null;
1884         $mapping['relationToTargetKeyColumns']  = null;
1885
1886         switch ($mapping['type']) {
1887             case self::ONE_TO_ONE:
1888                 $mapping = $this->_validateAndCompleteOneToOneMapping($mapping);
1889                 break;
1890             case self::ONE_TO_MANY:
1891                 $mapping = $this->_validateAndCompleteOneToManyMapping($mapping);
1892                 break;
1893             case self::MANY_TO_ONE:
1894                 $mapping = $this->_validateAndCompleteOneToOneMapping($mapping);
1895                 break;
1896             case self::MANY_TO_MANY:
1897                 $mapping = $this->_validateAndCompleteManyToManyMapping($mapping);
1898                 break;
1899         }
1900
1901         $this->associationMappings[$fieldName] = $mapping;
1902     }
1903
1904     /**
1905      * Sets the override for a mapped field.
1906      *
1907      * @param string $fieldName
1908      * @param array $overrideMapping
1909      * @throws MappingException
1910      * @param array $overrideMapping
1911      * @return void
1912      */
1913     public function setAttributeOverride($fieldName, array $overrideMapping)
1914     {
1915         if ( ! isset($this->fieldMappings[$fieldName])) {
1916             throw MappingException::invalidOverrideFieldName($this->name, $fieldName);
1917         }
1918
1919         $mapping = $this->fieldMappings[$fieldName];
1920
1921         if (isset($mapping['id'])) {
1922             $overrideMapping['id'] = $mapping['id'];
1923         }
1924
1925         if ( ! isset($overrideMapping['type']) || $overrideMapping['type'] === null) {
1926             $overrideMapping['type'] = $mapping['type'];
1927         }
1928
1929         if ( ! isset($overrideMapping['fieldName']) || $overrideMapping['fieldName'] === null) {
1930             $overrideMapping['fieldName'] = $mapping['fieldName'];
1931         }
1932
1933         if ($overrideMapping['type'] !== $mapping['type']) {
1934             throw MappingException::invalidOverrideFieldType($this->name, $fieldName);
1935         }
1936
1937         unset($this->fieldMappings[$fieldName]);
1938         unset($this->fieldNames[$mapping['columnName']]);
1939         unset($this->columnNames[$mapping['fieldName']]);
1940         $this->_validateAndCompleteFieldMapping($overrideMapping);
1941
1942         $this->fieldMappings[$fieldName] = $overrideMapping;
1943     }
1944
1945     /**
1946      * Checks whether a mapped field is inherited from an entity superclass.
1947      *
1948      * @param string $fieldName
1949      * @return bool TRUE if the field is inherited, FALSE otherwise.
1950      */
1951     public function isInheritedField($fieldName)
1952     {
1953         return isset($this->fieldMappings[$fieldName]['inherited']);
1954     }
1955
1956     /**
1957      * Check if this entity is the root in any entity-inheritance-hierachy.
1958      *
1959      * @return bool
1960      */
1961     public function isRootEntity()
1962     {
1963         return $this->name == $this->rootEntityName;
1964     }
1965
1966     /**
1967      * Checks whether a mapped association field is inherited from a superclass.
1968      *
1969      * @param string $fieldName
1970      * @return boolean TRUE if the field is inherited, FALSE otherwise.
1971      */
1972     public function isInheritedAssociation($fieldName)
1973     {
1974         return isset($this->associationMappings[$fieldName]['inherited']);
1975     }
1976
1977     /**
1978      * Sets the name of the primary table the class is mapped to.
1979      *
1980      * @param string $tableName The table name.
1981      * @deprecated Use {@link setPrimaryTable}.
1982      */
1983     public function setTableName($tableName)
1984     {
1985         $this->table['name'] = $tableName;
1986     }
1987
1988     /**
1989      * Sets the primary table definition. The provided array supports the
1990      * following structure:
1991      *
1992      * name => <tableName> (optional, defaults to class name)
1993      * indexes => array of indexes (optional)
1994      * uniqueConstraints => array of constraints (optional)
1995      *
1996      * If a key is omitted, the current value is kept.
1997      *
1998      * @param array $table The table description.
1999      */
2000     public function setPrimaryTable(array $table)
2001     {
2002         if (isset($table['name'])) {
2003             if ($table['name'][0] === '`') {
2004                 $table['name']          = trim($table['name'], '`');
2005                 $this->table['quoted']  = true;
2006             }
2007
2008             $this->table['name'] = $table['name'];
2009         }
2010
2011         if (isset($table['indexes'])) {
2012             $this->table['indexes'] = $table['indexes'];
2013         }
2014
2015         if (isset($table['uniqueConstraints'])) {
2016             $this->table['uniqueConstraints'] = $table['uniqueConstraints'];
2017         }
2018
2019         if (isset($table['options'])) {
2020             $this->table['options'] = $table['options'];
2021         }
2022     }
2023
2024     /**
2025      * Checks whether the given type identifies an inheritance type.
2026      *
2027      * @param integer $type
2028      * @return boolean TRUE if the given type identifies an inheritance type, FALSe otherwise.
2029      */
2030     private function _isInheritanceType($type)
2031     {
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;
2036     }
2037
2038     /**
2039      * Adds a mapped field to the class.
2040      *
2041      * @param array $mapping The field mapping.
2042      * @throws MappingException
2043      * @return void
2044      */
2045     public function mapField(array $mapping)
2046     {
2047         $this->_validateAndCompleteFieldMapping($mapping);
2048         if (isset($this->fieldMappings[$mapping['fieldName']]) || isset($this->associationMappings[$mapping['fieldName']])) {
2049             throw MappingException::duplicateFieldMapping($this->name, $mapping['fieldName']);
2050         }
2051         $this->fieldMappings[$mapping['fieldName']] = $mapping;
2052     }
2053
2054     /**
2055      * INTERNAL:
2056      * Adds an association mapping without completing/validating it.
2057      * This is mainly used to add inherited association mappings to derived classes.
2058      *
2059      * @param array $mapping
2060      * @throws MappingException
2061      * @return void
2062      */
2063     public function addInheritedAssociationMapping(array $mapping/*, $owningClassName = null*/)
2064     {
2065         if (isset($this->associationMappings[$mapping['fieldName']])) {
2066             throw MappingException::duplicateAssociationMapping($this->name, $mapping['fieldName']);
2067         }
2068         $this->associationMappings[$mapping['fieldName']] = $mapping;
2069     }
2070
2071     /**
2072      * INTERNAL:
2073      * Adds a field mapping without completing/validating it.
2074      * This is mainly used to add inherited field mappings to derived classes.
2075      *
2076      * @param array $fieldMapping
2077      * @return void
2078      */
2079     public function addInheritedFieldMapping(array $fieldMapping)
2080     {
2081         $this->fieldMappings[$fieldMapping['fieldName']] = $fieldMapping;
2082         $this->columnNames[$fieldMapping['fieldName']] = $fieldMapping['columnName'];
2083         $this->fieldNames[$fieldMapping['columnName']] = $fieldMapping['fieldName'];
2084     }
2085
2086     /**
2087      * INTERNAL:
2088      * Adds a named query to this class.
2089      *
2090      * @throws MappingException
2091      * @param array $queryMapping
2092      */
2093     public function addNamedQuery(array $queryMapping)
2094     {
2095         if (!isset($queryMapping['name'])) {
2096             throw MappingException::nameIsMandatoryForQueryMapping($this->name);
2097         }
2098
2099         if (isset($this->namedQueries[$queryMapping['name']])) {
2100             throw MappingException::duplicateQueryMapping($this->name, $queryMapping['name']);
2101         }
2102
2103         if (!isset($queryMapping['query'])) {
2104             throw MappingException::emptyQueryMapping($this->name, $queryMapping['name']);
2105         }
2106
2107         $name   = $queryMapping['name'];
2108         $query  = $queryMapping['query'];
2109         $dql    = str_replace('__CLASS__', $this->name, $query);
2110         $this->namedQueries[$name] = array(
2111             'name'  => $name,
2112             'query' => $query,
2113             'dql'   => $dql
2114         );
2115     }
2116
2117     /**
2118      * INTERNAL:
2119      * Adds a named native query to this class.
2120      *
2121      * @throws MappingException
2122      * @param array $queryMapping
2123      */
2124     public function addNamedNativeQuery(array $queryMapping)
2125     {
2126         if (!isset($queryMapping['name'])) {
2127             throw MappingException::nameIsMandatoryForQueryMapping($this->name);
2128         }
2129
2130         if (isset($this->namedNativeQueries[$queryMapping['name']])) {
2131             throw MappingException::duplicateQueryMapping($this->name, $queryMapping['name']);
2132         }
2133
2134         if (!isset($queryMapping['query'])) {
2135             throw MappingException::emptyQueryMapping($this->name, $queryMapping['name']);
2136         }
2137
2138         if (!isset($queryMapping['resultClass']) && !isset($queryMapping['resultSetMapping'])) {
2139             throw MappingException::missingQueryMapping($this->name, $queryMapping['name']);
2140         }
2141
2142         $queryMapping['isSelfClass'] = false;
2143         if (isset($queryMapping['resultClass'])) {
2144
2145             if($queryMapping['resultClass'] === '__CLASS__') {
2146
2147                 $queryMapping['isSelfClass'] = true;
2148                 $queryMapping['resultClass'] = $this->name;
2149
2150             } else if (strlen($this->namespace) > 0 && strpos($queryMapping['resultClass'], '\\') === false) {
2151                 $queryMapping['resultClass'] = $this->namespace . '\\' . $queryMapping['resultClass'];
2152             }
2153
2154             $queryMapping['resultClass'] = ltrim($queryMapping['resultClass'], '\\');
2155         }
2156
2157         $this->namedNativeQueries[$queryMapping['name']] = $queryMapping;
2158     }
2159
2160     /**
2161      * INTERNAL:
2162      * Adds a sql result set mapping to this class.
2163      *
2164      * @throws MappingException
2165      * @param array $resultMapping
2166      */
2167     public function addSqlResultSetMapping(array $resultMapping)
2168     {
2169         if (!isset($resultMapping['name'])) {
2170             throw MappingException::nameIsMandatoryForSqlResultSetMapping($this->name);
2171         }
2172
2173         if (isset($this->sqlResultSetMappings[$resultMapping['name']])) {
2174             throw MappingException::duplicateResultSetMapping($this->name, $resultMapping['name']);
2175         }
2176
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']);
2181                 }
2182
2183                 $entityResult['isSelfClass'] = false;
2184                 if($entityResult['entityClass'] === '__CLASS__') {
2185
2186                     $entityResult['isSelfClass'] = true;
2187                     $entityResult['entityClass'] = $this->name;
2188
2189                 } else if (strlen($this->namespace) > 0 && strpos($entityResult['entityClass'], '\\') === false) {
2190                     $entityResult['entityClass'] = $this->namespace . '\\' . $entityResult['entityClass'];
2191                 }
2192
2193                 $resultMapping['entities'][$key]['entityClass'] = ltrim($entityResult['entityClass'], '\\');
2194                 $resultMapping['entities'][$key]['isSelfClass'] = $entityResult['isSelfClass'];
2195
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']);
2200                         }
2201
2202                         if (!isset($field['column'])) {
2203                             $fieldName = $field['name'];
2204                             if(strpos($fieldName, '.')){
2205                                 list(, $fieldName) = explode('.', $fieldName);
2206                             }
2207
2208                             $resultMapping['entities'][$key]['fields'][$k]['column'] = $fieldName;
2209                         }
2210                     }
2211                 }
2212             }
2213         }
2214
2215         $this->sqlResultSetMappings[$resultMapping['name']] = $resultMapping;
2216     }
2217
2218     /**
2219      * Adds a one-to-one mapping.
2220      *
2221      * @param array $mapping The mapping.
2222      */
2223     public function mapOneToOne(array $mapping)
2224     {
2225         $mapping['type'] = self::ONE_TO_ONE;
2226         $mapping = $this->_validateAndCompleteOneToOneMapping($mapping);
2227         $this->_storeAssociationMapping($mapping);
2228     }
2229
2230     /**
2231      * Adds a one-to-many mapping.
2232      *
2233      * @param array $mapping The mapping.
2234      */
2235     public function mapOneToMany(array $mapping)
2236     {
2237         $mapping['type'] = self::ONE_TO_MANY;
2238         $mapping = $this->_validateAndCompleteOneToManyMapping($mapping);
2239         $this->_storeAssociationMapping($mapping);
2240     }
2241
2242     /**
2243      * Adds a many-to-one mapping.
2244      *
2245      * @param array $mapping The mapping.
2246      */
2247     public function mapManyToOne(array $mapping)
2248     {
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);
2253     }
2254
2255     /**
2256      * Adds a many-to-many mapping.
2257      *
2258      * @param array $mapping The mapping.
2259      */
2260     public function mapManyToMany(array $mapping)
2261     {
2262         $mapping['type'] = self::MANY_TO_MANY;
2263         $mapping = $this->_validateAndCompleteManyToManyMapping($mapping);
2264         $this->_storeAssociationMapping($mapping);
2265     }
2266
2267     /**
2268      * Stores the association mapping.
2269      *
2270      * @param array $assocMapping
2271      * @throws MappingException
2272      * @return void
2273      */
2274     protected function _storeAssociationMapping(array $assocMapping)
2275     {
2276         $sourceFieldName = $assocMapping['fieldName'];
2277
2278         if (isset($this->fieldMappings[$sourceFieldName]) || isset($this->associationMappings[$sourceFieldName])) {
2279             throw MappingException::duplicateFieldMapping($this->name, $sourceFieldName);
2280         }
2281
2282         $this->associationMappings[$sourceFieldName] = $assocMapping;
2283     }
2284
2285     /**
2286      * Registers a custom repository class for the entity class.
2287      *
2288      * @param string $repositoryClassName The class name of the custom mapper.
2289      * @return void
2290      */
2291     public function setCustomRepositoryClass($repositoryClassName)
2292     {
2293         if ($repositoryClassName !== null && strpos($repositoryClassName, '\\') === false
2294                 && strlen($this->namespace) > 0) {
2295             $repositoryClassName = $this->namespace . '\\' . $repositoryClassName;
2296         }
2297         $this->customRepositoryClassName = $repositoryClassName;
2298     }
2299
2300     /**
2301      * Dispatches the lifecycle event of the given entity to the registered
2302      * lifecycle callbacks and lifecycle listeners.
2303      *
2304      * @param string $lifecycleEvent The lifecycle event.
2305      * @param \Object $entity The Entity on which the event occured.
2306      */
2307     public function invokeLifecycleCallbacks($lifecycleEvent, $entity)
2308     {
2309         foreach ($this->lifecycleCallbacks[$lifecycleEvent] as $callback) {
2310             $entity->$callback();
2311         }
2312     }
2313
2314     /**
2315      * Whether the class has any attached lifecycle listeners or callbacks for a lifecycle event.
2316      *
2317      * @param string $lifecycleEvent
2318      * @return boolean
2319      */
2320     public function hasLifecycleCallbacks($lifecycleEvent)
2321     {
2322         return isset($this->lifecycleCallbacks[$lifecycleEvent]);
2323     }
2324
2325     /**
2326      * Gets the registered lifecycle callbacks for an event.
2327      *
2328      * @param string $event
2329      * @return array
2330      */
2331     public function getLifecycleCallbacks($event)
2332     {
2333         return isset($this->lifecycleCallbacks[$event]) ? $this->lifecycleCallbacks[$event] : array();
2334     }
2335
2336     /**
2337      * Adds a lifecycle callback for entities of this class.
2338      *
2339      * @param string $callback
2340      * @param string $event
2341      */
2342     public function addLifecycleCallback($callback, $event)
2343     {
2344         $this->lifecycleCallbacks[$event][] = $callback;
2345     }
2346
2347     /**
2348      * Sets the lifecycle callbacks for entities of this class.
2349      * Any previously registered callbacks are overwritten.
2350      *
2351      * @param array $callbacks
2352      */
2353     public function setLifecycleCallbacks(array $callbacks)
2354     {
2355         $this->lifecycleCallbacks = $callbacks;
2356     }
2357
2358     /**
2359      * Sets the discriminator column definition.
2360      *
2361      * @param array $columnDef
2362      *
2363      * @param $columnDef
2364      * @throws MappingException
2365      * @return void
2366      * @see getDiscriminatorColumn()
2367      */
2368     public function setDiscriminatorColumn($columnDef)
2369     {
2370         if ($columnDef !== null) {
2371             if ( ! isset($columnDef['name'])) {
2372                 throw MappingException::nameIsMandatoryForDiscriminatorColumns($this->name);
2373             }
2374
2375             if (isset($this->fieldNames[$columnDef['name']])) {
2376                 throw MappingException::duplicateColumnName($this->name, $columnDef['name']);
2377             }
2378
2379             if ( ! isset($columnDef['fieldName'])) {
2380                 $columnDef['fieldName'] = $columnDef['name'];
2381             }
2382
2383             if ( ! isset($columnDef['type'])) {
2384                 $columnDef['type'] = "string";
2385             }
2386
2387             if (in_array($columnDef['type'], array("boolean", "array", "object", "datetime", "time", "date"))) {
2388                 throw MappingException::invalidDiscriminatorColumnType($this->name, $columnDef['type']);
2389             }
2390
2391             $this->discriminatorColumn = $columnDef;
2392         }
2393     }
2394
2395     /**
2396      * Sets the discriminator values used by this class.
2397      * Used for JOINED and SINGLE_TABLE inheritance mapping strategies.
2398      *
2399      * @param array $map
2400      */
2401     public function setDiscriminatorMap(array $map)
2402     {
2403         foreach ($map as $value => $className) {
2404             $this->addDiscriminatorMapClass($value, $className);
2405         }
2406     }
2407
2408     /**
2409      * Add one entry of the discriminator map with a new class and corresponding name.
2410      *
2411      * @param string $name
2412      * @param string $className
2413      * @throws MappingException
2414      * @return void
2415      */
2416     public function addDiscriminatorMapClass($name, $className)
2417     {
2418         if (strlen($this->namespace) > 0 && strpos($className, '\\') === false) {
2419             $className = $this->namespace . '\\' . $className;
2420         }
2421
2422         $className = ltrim($className, '\\');
2423         $this->discriminatorMap[$name] = $className;
2424
2425         if ($this->name == $className) {
2426             $this->discriminatorValue = $name;
2427         } else {
2428             if ( ! class_exists($className)) {
2429                 throw MappingException::invalidClassInDiscriminatorMap($className, $this->name);
2430             }
2431             if (is_subclass_of($className, $this->name) && ! in_array($className, $this->subClasses)) {
2432                 $this->subClasses[] = $className;
2433             }
2434         }
2435     }
2436
2437     /**
2438      * Checks whether the class has a named query with the given query name.
2439      *
2440      * @param string $queryName
2441      * @return boolean
2442      */
2443     public function hasNamedQuery($queryName)
2444     {
2445         return isset($this->namedQueries[$queryName]);
2446     }
2447
2448     /**
2449      * Checks whether the class has a named native query with the given query name.
2450      *
2451      * @param string $queryName
2452      * @return boolean
2453      */
2454     public function hasNamedNativeQuery($queryName)
2455     {
2456         return isset($this->namedNativeQueries[$queryName]);
2457     }
2458
2459     /**
2460      * Checks whether the class has a named native query with the given query name.
2461      *
2462      * @param string $name
2463      * @return boolean
2464      */
2465     public function hasSqlResultSetMapping($name)
2466     {
2467         return isset($this->sqlResultSetMappings[$name]);
2468     }
2469
2470     /**
2471      * {@inheritDoc}
2472      */
2473     public function hasAssociation($fieldName)
2474     {
2475         return isset($this->associationMappings[$fieldName]);
2476     }
2477
2478     /**
2479      * {@inheritDoc}
2480      */
2481     public function isSingleValuedAssociation($fieldName)
2482     {
2483         return isset($this->associationMappings[$fieldName]) &&
2484                 ($this->associationMappings[$fieldName]['type'] & self::TO_ONE);
2485     }
2486
2487     /**
2488      * {@inheritDoc}
2489      */
2490     public function isCollectionValuedAssociation($fieldName)
2491     {
2492         return isset($this->associationMappings[$fieldName]) &&
2493                 ! ($this->associationMappings[$fieldName]['type'] & self::TO_ONE);
2494     }
2495
2496     /**
2497      * Is this an association that only has a single join column?
2498      *
2499      * @param  string $fieldName
2500      * @return bool
2501      */
2502     public function isAssociationWithSingleJoinColumn($fieldName)
2503     {
2504         return (
2505             isset($this->associationMappings[$fieldName]) &&
2506             isset($this->associationMappings[$fieldName]['joinColumns'][0]) &&
2507             !isset($this->associationMappings[$fieldName]['joinColumns'][1])
2508         );
2509     }
2510
2511     /**
2512      * Return the single association join column (if any).
2513      *
2514      * @param string $fieldName
2515      * @throws MappingException
2516      * @return string
2517      */
2518     public function getSingleAssociationJoinColumnName($fieldName)
2519     {
2520         if ( ! $this->isAssociationWithSingleJoinColumn($fieldName)) {
2521             throw MappingException::noSingleAssociationJoinColumnFound($this->name, $fieldName);
2522         }
2523         return $this->associationMappings[$fieldName]['joinColumns'][0]['name'];
2524     }
2525
2526     /**
2527      * Return the single association referenced join column name (if any).
2528      *
2529      * @param string $fieldName
2530      * @throws MappingException
2531      * @return string
2532      */
2533     public function getSingleAssociationReferencedJoinColumnName($fieldName)
2534     {
2535         if ( ! $this->isAssociationWithSingleJoinColumn($fieldName)) {
2536             throw MappingException::noSingleAssociationJoinColumnFound($this->name, $fieldName);
2537         }
2538         return $this->associationMappings[$fieldName]['joinColumns'][0]['referencedColumnName'];
2539     }
2540
2541     /**
2542      * Used to retrieve a fieldname for either field or association from a given column,
2543      *
2544      * This method is used in foreign-key as primary-key contexts.
2545      *
2546      * @param  string $columnName
2547      * @throws MappingException
2548      * @return string
2549      */
2550     public function getFieldForColumn($columnName)
2551     {
2552         if (isset($this->fieldNames[$columnName])) {
2553             return $this->fieldNames[$columnName];
2554         } else {
2555             foreach ($this->associationMappings as $assocName => $mapping) {
2556                 if ($this->isAssociationWithSingleJoinColumn($assocName) &&
2557                     $this->associationMappings[$assocName]['joinColumns'][0]['name'] == $columnName) {
2558
2559                     return $assocName;
2560                 }
2561             }
2562
2563             throw MappingException::noFieldNameFoundForColumn($this->name, $columnName);
2564         }
2565     }
2566
2567     /**
2568      * Sets the ID generator used to generate IDs for instances of this class.
2569      *
2570      * @param \Doctrine\ORM\Id\AbstractIdGenerator $generator
2571      */
2572     public function setIdGenerator($generator)
2573     {
2574         $this->idGenerator = $generator;
2575     }
2576
2577     /**
2578      * Sets definition
2579      * @param array $definition
2580      */
2581     public function setCustomGeneratorDefinition(array $definition)
2582     {
2583         $this->customGeneratorDefinition = $definition;
2584     }
2585
2586     /**
2587      * Sets the definition of the sequence ID generator for this class.
2588      *
2589      * The definition must have the following structure:
2590      * <code>
2591      * array(
2592      *     'sequenceName'   => 'name',
2593      *     'allocationSize' => 20,
2594      *     'initialValue'   => 1
2595      *     'quoted'         => 1
2596      * )
2597      * </code>
2598      *
2599      * @param array $definition
2600      */
2601     public function setSequenceGeneratorDefinition(array $definition)
2602     {
2603         if (isset($definition['name']) && $definition['name'] == '`') {
2604             $definition['name']   = trim($definition['name'], '`');
2605             $definition['quoted'] = true;
2606         }
2607
2608         $this->sequenceGeneratorDefinition = $definition;
2609     }
2610
2611     /**
2612      * Sets the version field mapping used for versioning. Sets the default
2613      * value to use depending on the column type.
2614      *
2615      * @param array $mapping   The version field mapping array
2616      * @throws MappingException
2617      * @return void
2618      */
2619     public function setVersionMapping(array &$mapping)
2620     {
2621         $this->isVersioned = true;
2622         $this->versionField = $mapping['fieldName'];
2623
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';
2629             } else {
2630                 throw MappingException::unsupportedOptimisticLockingType($this->name, $mapping['fieldName'], $mapping['type']);
2631             }
2632         }
2633     }
2634
2635     /**
2636      * Sets whether this class is to be versioned for optimistic locking.
2637      *
2638      * @param boolean $bool
2639      */
2640     public function setVersioned($bool)
2641     {
2642         $this->isVersioned = $bool;
2643     }
2644
2645     /**
2646      * Sets the name of the field that is to be used for versioning if this class is
2647      * versioned for optimistic locking.
2648      *
2649      * @param string $versionField
2650      */
2651     public function setVersionField($versionField)
2652     {
2653         $this->versionField = $versionField;
2654     }
2655
2656     /**
2657      * Mark this class as read only, no change tracking is applied to it.
2658      *
2659      * @return void
2660      */
2661     public function markReadOnly()
2662     {
2663         $this->isReadOnly = true;
2664     }
2665
2666     /**
2667      * {@inheritDoc}
2668      */
2669     public function getFieldNames()
2670     {
2671         return array_keys($this->fieldMappings);
2672     }
2673
2674     /**
2675      * {@inheritDoc}
2676      */
2677     public function getAssociationNames()
2678     {
2679         return array_keys($this->associationMappings);
2680     }
2681
2682     /**
2683      * {@inheritDoc}
2684      * @throws InvalidArgumentException
2685      */
2686     public function getAssociationTargetClass($assocName)
2687     {
2688         if ( ! isset($this->associationMappings[$assocName])) {
2689             throw new InvalidArgumentException("Association name expected, '" . $assocName ."' is not an association.");
2690         }
2691
2692         return $this->associationMappings[$assocName]['targetEntity'];
2693     }
2694
2695     /**
2696      * {@inheritDoc}
2697      */
2698     public function getName()
2699     {
2700         return $this->name;
2701     }
2702
2703     /**
2704      * Gets the (possibly quoted) identifier column names for safe use in an SQL statement.
2705      *
2706      * @deprecated Deprecated since version 2.3 in favor of \Doctrine\ORM\Mapping\QuoteStrategy
2707      *
2708      * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform
2709      * @return array
2710      */
2711     public function getQuotedIdentifierColumnNames($platform)
2712     {
2713         $quotedColumnNames = array();
2714
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'];
2720
2721                 continue;
2722             }
2723
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'];
2731                 },
2732                 $joinColumns
2733             );
2734
2735             $quotedColumnNames = array_merge($quotedColumnNames, $assocQuotedColumnNames);
2736         }
2737
2738         return $quotedColumnNames;
2739     }
2740
2741     /**
2742      * Gets the (possibly quoted) column name of a mapped field for safe use  in an SQL statement.
2743      *
2744      * @deprecated Deprecated since version 2.3 in favor of \Doctrine\ORM\Mapping\QuoteStrategy
2745      *
2746      * @param string $field
2747      * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform
2748      * @return string
2749      */
2750     public function getQuotedColumnName($field, $platform)
2751     {
2752         return isset($this->fieldMappings[$field]['quoted'])
2753             ? $platform->quoteIdentifier($this->fieldMappings[$field]['columnName'])
2754             : $this->fieldMappings[$field]['columnName'];
2755     }
2756
2757     /**
2758      * Gets the (possibly quoted) primary table name of this class for safe use in an SQL statement.
2759      *
2760      * @deprecated Deprecated since version 2.3 in favor of \Doctrine\ORM\Mapping\QuoteStrategy
2761      *
2762      * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform
2763      * @return string
2764      */
2765     public function getQuotedTableName($platform)
2766     {
2767         return isset($this->table['quoted']) ? $platform->quoteIdentifier($this->table['name']) : $this->table['name'];
2768     }
2769
2770     /**
2771      * Gets the (possibly quoted) name of the join table.
2772      *
2773      * @deprecated Deprecated since version 2.3 in favor of \Doctrine\ORM\Mapping\QuoteStrategy
2774      *
2775      * @param array $assoc
2776      * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform
2777      * @return string
2778      */
2779     public function getQuotedJoinTableName(array $assoc, $platform)
2780     {
2781         return isset($assoc['joinTable']['quoted']) ? $platform->quoteIdentifier($assoc['joinTable']['name']) : $assoc['joinTable']['name'];
2782     }
2783
2784     /**
2785      * {@inheritDoc}
2786      */
2787     public function isAssociationInverseSide($fieldName)
2788     {
2789         return isset($this->associationMappings[$fieldName]) && ! $this->associationMappings[$fieldName]['isOwningSide'];
2790     }
2791
2792     /**
2793      * {@inheritDoc}
2794      */
2795     public function getAssociationMappedByTargetField($fieldName)
2796     {
2797         return $this->associationMappings[$fieldName]['mappedBy'];
2798     }
2799
2800     /**
2801      * @param   string $targetClass
2802      * @return  array
2803      */
2804     public function getAssociationsByTargetClass($targetClass)
2805     {
2806         $relations = array();
2807         foreach ($this->associationMappings as $mapping) {
2808             if ($mapping['targetEntity'] == $targetClass) {
2809                 $relations[$mapping['fieldName']] = $mapping;
2810             }
2811         }
2812         return $relations;
2813     }
2814 }