source: pro-violet-viettel/sourcecode/application/libraries/Doctrine/ORM/Mapping/ClassMetadataInfo.php @ 345

Last change on this file since 345 was 345, checked in by quyenla, 11 years ago

collaborator page

File size: 75.6 KB
Line 
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 LGPL. For more information, see
17 * <http://www.doctrine-project.org>.
18 */
19
20namespace Doctrine\ORM\Mapping;
21
22use Doctrine\DBAL\Types\Type;
23use ReflectionClass;
24use Doctrine\Common\Persistence\Mapping\ClassMetadata;
25
26/**
27 * A <tt>ClassMetadata</tt> instance holds all the object-relational mapping metadata
28 * of an entity and it's associations.
29 *
30 * Once populated, ClassMetadata instances are usually cached in a serialized form.
31 *
32 * <b>IMPORTANT NOTE:</b>
33 *
34 * The fields of this class are only public for 2 reasons:
35 * 1) To allow fast READ access.
36 * 2) To drastically reduce the size of a serialized instance (private/protected members
37 *    get the whole class name, namespace inclusive, prepended to every property in
38 *    the serialized representation).
39 *
40 * @author Roman Borschel <roman@code-factory.org>
41 * @author Jonathan H. Wage <jonwage@gmail.com>
42 * @since 2.0
43 */
44class ClassMetadataInfo implements ClassMetadata
45{
46    /* The inheritance mapping types */
47    /**
48     * NONE means the class does not participate in an inheritance hierarchy
49     * and therefore does not need an inheritance mapping type.
50     */
51    const INHERITANCE_TYPE_NONE = 1;
52    /**
53     * JOINED means the class will be persisted according to the rules of
54     * <tt>Class Table Inheritance</tt>.
55     */
56    const INHERITANCE_TYPE_JOINED = 2;
57    /**
58     * SINGLE_TABLE means the class will be persisted according to the rules of
59     * <tt>Single Table Inheritance</tt>.
60     */
61    const INHERITANCE_TYPE_SINGLE_TABLE = 3;
62    /**
63     * TABLE_PER_CLASS means the class will be persisted according to the rules
64     * of <tt>Concrete Table Inheritance</tt>.
65     */
66    const INHERITANCE_TYPE_TABLE_PER_CLASS = 4;
67
68    /* The Id generator types. */
69    /**
70     * AUTO means the generator type will depend on what the used platform prefers.
71     * Offers full portability.
72     */
73    const GENERATOR_TYPE_AUTO = 1;
74    /**
75     * SEQUENCE means a separate sequence object will be used. Platforms that do
76     * not have native sequence support may emulate it. Full portability is currently
77     * not guaranteed.
78     */
79    const GENERATOR_TYPE_SEQUENCE = 2;
80    /**
81     * TABLE means a separate table is used for id generation.
82     * Offers full portability.
83     */
84    const GENERATOR_TYPE_TABLE = 3;
85    /**
86     * IDENTITY means an identity column is used for id generation. The database
87     * will fill in the id column on insertion. Platforms that do not support
88     * native identity columns may emulate them. Full portability is currently
89     * not guaranteed.
90     */
91    const GENERATOR_TYPE_IDENTITY = 4;
92    /**
93     * NONE means the class does not have a generated id. That means the class
94     * must have a natural, manually assigned id.
95     */
96    const GENERATOR_TYPE_NONE = 5;
97    /**
98     * DEFERRED_IMPLICIT means that changes of entities are calculated at commit-time
99     * by doing a property-by-property comparison with the original data. This will
100     * be done for all entities that are in MANAGED state at commit-time.
101     *
102     * This is the default change tracking policy.
103     */
104    const CHANGETRACKING_DEFERRED_IMPLICIT = 1;
105    /**
106     * DEFERRED_EXPLICIT means that changes of entities are calculated at commit-time
107     * by doing a property-by-property comparison with the original data. This will
108     * be done only for entities that were explicitly saved (through persist() or a cascade).
109     */
110    const CHANGETRACKING_DEFERRED_EXPLICIT = 2;
111    /**
112     * NOTIFY means that Doctrine relies on the entities sending out notifications
113     * when their properties change. Such entity classes must implement
114     * the <tt>NotifyPropertyChanged</tt> interface.
115     */
116    const CHANGETRACKING_NOTIFY = 3;
117    /**
118     * Specifies that an association is to be fetched when it is first accessed.
119     */
120    const FETCH_LAZY = 2;
121    /**
122     * Specifies that an association is to be fetched when the owner of the
123     * association is fetched.
124     */
125    const FETCH_EAGER = 3;
126    /**
127     * Specifies that an association is to be fetched lazy (on first access) and that
128     * commands such as Collection#count, Collection#slice are issued directly against
129     * the database if the collection is not yet initialized.
130     */
131    const FETCH_EXTRA_LAZY = 4;
132    /**
133     * Identifies a one-to-one association.
134     */
135    const ONE_TO_ONE = 1;
136    /**
137     * Identifies a many-to-one association.
138     */
139    const MANY_TO_ONE = 2;
140    /**
141     * Identifies a one-to-many association.
142     */
143    const ONE_TO_MANY = 4;
144    /**
145     * Identifies a many-to-many association.
146     */
147    const MANY_TO_MANY = 8;
148    /**
149     * Combined bitmask for to-one (single-valued) associations.
150     */
151    const TO_ONE = 3;
152    /**
153     * Combined bitmask for to-many (collection-valued) associations.
154     */
155    const TO_MANY = 12;
156
157    /**
158     * READ-ONLY: The name of the entity class.
159     */
160    public $name;
161
162    /**
163     * READ-ONLY: The namespace the entity class is contained in.
164     *
165     * @var string
166     * @todo Not really needed. Usage could be localized.
167     */
168    public $namespace;
169
170    /**
171     * READ-ONLY: The name of the entity class that is at the root of the mapped entity inheritance
172     * hierarchy. If the entity is not part of a mapped inheritance hierarchy this is the same
173     * as {@link $entityName}.
174     *
175     * @var string
176     */
177    public $rootEntityName;
178
179    /**
180     * The name of the custom repository class used for the entity class.
181     * (Optional).
182     *
183     * @var string
184     */
185    public $customRepositoryClassName;
186
187    /**
188     * READ-ONLY: Whether this class describes the mapping of a mapped superclass.
189     *
190     * @var boolean
191     */
192    public $isMappedSuperclass = false;
193
194    /**
195     * READ-ONLY: The names of the parent classes (ancestors).
196     *
197     * @var array
198     */
199    public $parentClasses = array();
200
201    /**
202     * READ-ONLY: The names of all subclasses (descendants).
203     *
204     * @var array
205     */
206    public $subClasses = array();
207
208    /**
209     * READ-ONLY: The named queries allowed to be called directly from Repository.
210     *
211     * @var array
212     */
213    public $namedQueries = array();
214
215    /**
216     * READ-ONLY: The field names of all fields that are part of the identifier/primary key
217     * of the mapped entity class.
218     *
219     * @var array
220     */
221    public $identifier = array();
222
223    /**
224     * READ-ONLY: The inheritance mapping type used by the class.
225     *
226     * @var integer
227     */
228    public $inheritanceType = self::INHERITANCE_TYPE_NONE;
229
230    /**
231     * READ-ONLY: The Id generator type used by the class.
232     *
233     * @var string
234     */
235    public $generatorType = self::GENERATOR_TYPE_NONE;
236
237    /**
238     * READ-ONLY: The field mappings of the class.
239     * Keys are field names and values are mapping definitions.
240     *
241     * The mapping definition array has the following values:
242     *
243     * - <b>fieldName</b> (string)
244     * The name of the field in the Entity.
245     *
246     * - <b>type</b> (string)
247     * The type name of the mapped field. Can be one of Doctrine's mapping types
248     * or a custom mapping type.
249     *
250     * - <b>columnName</b> (string, optional)
251     * The column name. Optional. Defaults to the field name.
252     *
253     * - <b>length</b> (integer, optional)
254     * The database length of the column. Optional. Default value taken from
255     * the type.
256     *
257     * - <b>id</b> (boolean, optional)
258     * Marks the field as the primary key of the entity. Multiple fields of an
259     * entity can have the id attribute, forming a composite key.
260     *
261     * - <b>nullable</b> (boolean, optional)
262     * Whether the column is nullable. Defaults to FALSE.
263     *
264     * - <b>columnDefinition</b> (string, optional, schema-only)
265     * The SQL fragment that is used when generating the DDL for the column.
266     *
267     * - <b>precision</b> (integer, optional, schema-only)
268     * The precision of a decimal column. Only valid if the column type is decimal.
269     *
270     * - <b>scale</b> (integer, optional, schema-only)
271     * The scale of a decimal column. Only valid if the column type is decimal.
272     *
273     [* - <b>'unique'] (string, optional, schema-only)</b>
274     * Whether a unique constraint should be generated for the column.
275     *
276     * @var array
277     */
278    public $fieldMappings = array();
279
280    /**
281     * READ-ONLY: An array of field names. Used to look up field names from column names.
282     * Keys are column names and values are field names.
283     * This is the reverse lookup map of $_columnNames.
284     *
285     * @var array
286     */
287    public $fieldNames = array();
288
289    /**
290     * READ-ONLY: A map of field names to column names. Keys are field names and values column names.
291     * Used to look up column names from field names.
292     * This is the reverse lookup map of $_fieldNames.
293     *
294     * @var array
295     * @todo We could get rid of this array by just using $fieldMappings[$fieldName]['columnName'].
296     */
297    public $columnNames = array();
298
299    /**
300     * READ-ONLY: The discriminator value of this class.
301     *
302     * <b>This does only apply to the JOINED and SINGLE_TABLE inheritance mapping strategies
303     * where a discriminator column is used.</b>
304     *
305     * @var mixed
306     * @see discriminatorColumn
307     */
308    public $discriminatorValue;
309
310    /**
311     * READ-ONLY: The discriminator map of all mapped classes in the hierarchy.
312     *
313     * <b>This does only apply to the JOINED and SINGLE_TABLE inheritance mapping strategies
314     * where a discriminator column is used.</b>
315     *
316     * @var mixed
317     * @see discriminatorColumn
318     */
319    public $discriminatorMap = array();
320
321    /**
322     * READ-ONLY: The definition of the discriminator column used in JOINED and SINGLE_TABLE
323     * inheritance mappings.
324     *
325     * @var array
326     */
327    public $discriminatorColumn;
328
329    /**
330     * READ-ONLY: The primary table definition. The definition is an array with the
331     * following entries:
332     *
333     * name => <tableName>
334     * schema => <schemaName>
335     * indexes => array
336     * uniqueConstraints => array
337     *
338     * @var array
339     */
340    public $table;
341
342    /**
343     * READ-ONLY: The registered lifecycle callbacks for entities of this class.
344     *
345     * @var array
346     */
347    public $lifecycleCallbacks = array();
348
349    /**
350     * READ-ONLY: The association mappings of this class.
351     *
352     * The mapping definition array supports the following keys:
353     *
354     * - <b>fieldName</b> (string)
355     * The name of the field in the entity the association is mapped to.
356     *
357     * - <b>targetEntity</b> (string)
358     * The class name of the target entity. If it is fully-qualified it is used as is.
359     * If it is a simple, unqualified class name the namespace is assumed to be the same
360     * as the namespace of the source entity.
361     *
362     * - <b>mappedBy</b> (string, required for bidirectional associations)
363     * The name of the field that completes the bidirectional association on the owning side.
364     * This key must be specified on the inverse side of a bidirectional association.
365     *
366     * - <b>inversedBy</b> (string, required for bidirectional associations)
367     * The name of the field that completes the bidirectional association on the inverse side.
368     * This key must be specified on the owning side of a bidirectional association.
369     *
370     * - <b>cascade</b> (array, optional)
371     * The names of persistence operations to cascade on the association. The set of possible
372     * values are: "persist", "remove", "detach", "merge", "refresh", "all" (implies all others).
373     *
374     * - <b>orderBy</b> (array, one-to-many/many-to-many only)
375     * A map of field names (of the target entity) to sorting directions (ASC/DESC).
376     * Example: array('priority' => 'desc')
377     *
378     * - <b>fetch</b> (integer, optional)
379     * The fetching strategy to use for the association, usually defaults to FETCH_LAZY.
380     * Possible values are: ClassMetadata::FETCH_EAGER, ClassMetadata::FETCH_LAZY.
381     *
382     * - <b>joinTable</b> (array, optional, many-to-many only)
383     * Specification of the join table and its join columns (foreign keys).
384     * Only valid for many-to-many mappings. Note that one-to-many associations can be mapped
385     * through a join table by simply mapping the association as many-to-many with a unique
386     * constraint on the join table.
387     *
388     * - <b>indexBy</b> (string, optional, to-many only)
389     * Specification of a field on target-entity that is used to index the collection by.
390     * This field HAS to be either the primary key or a unique column. Otherwise the collection
391     * does not contain all the entities that are actually related.
392     *
393     * A join table definition has the following structure:
394     * <pre>
395     * array(
396     *     'name' => <join table name>,
397     *      'joinColumns' => array(<join column mapping from join table to source table>),
398     *      'inverseJoinColumns' => array(<join column mapping from join table to target table>)
399     * )
400     * </pre>
401     *
402     *
403     * @var array
404     */
405    public $associationMappings = array();
406
407    /**
408     * READ-ONLY: Flag indicating whether the identifier/primary key of the class is composite.
409     *
410     * @var boolean
411     */
412    public $isIdentifierComposite = false;
413
414    /**
415     * READ-ONLY: Flag indicating wheather the identifier/primary key contains at least one foreign key association.
416     *
417     * This flag is necessary because some code blocks require special treatment of this cases.
418     *
419     * @var boolean
420     */
421    public $containsForeignIdentifier = false;
422
423    /**
424     * READ-ONLY: The ID generator used for generating IDs for this class.
425     *
426     * @var AbstractIdGenerator
427     * @todo Remove!
428     */
429    public $idGenerator;
430
431    /**
432     * READ-ONLY: The definition of the sequence generator of this class. Only used for the
433     * SEQUENCE generation strategy.
434     *
435     * The definition has the following structure:
436     * <code>
437     * array(
438     *     'sequenceName' => 'name',
439     *     'allocationSize' => 20,
440     *     'initialValue' => 1
441     * )
442     * </code>
443     *
444     * @var array
445     * @todo Merge with tableGeneratorDefinition into generic generatorDefinition
446     */
447    public $sequenceGeneratorDefinition;
448
449    /**
450     * READ-ONLY: The definition of the table generator of this class. Only used for the
451     * TABLE generation strategy.
452     *
453     * @var array
454     * @todo Merge with tableGeneratorDefinition into generic generatorDefinition
455     */
456    public $tableGeneratorDefinition;
457
458    /**
459     * READ-ONLY: The policy used for change-tracking on entities of this class.
460     *
461     * @var integer
462     */
463    public $changeTrackingPolicy = self::CHANGETRACKING_DEFERRED_IMPLICIT;
464
465    /**
466     * READ-ONLY: A flag for whether or not instances of this class are to be versioned
467     * with optimistic locking.
468     *
469     * @var boolean $isVersioned
470     */
471    public $isVersioned;
472
473    /**
474     * READ-ONLY: The name of the field which is used for versioning in optimistic locking (if any).
475     *
476     * @var mixed $versionField
477     */
478    public $versionField;
479
480    /**
481     * The ReflectionClass instance of the mapped class.
482     *
483     * @var ReflectionClass
484     */
485    public $reflClass;
486
487    /**
488     * Is this entity marked as "read-only"?
489     *
490     * That means it is never considered for change-tracking in the UnitOfWork. It is a very helpful performance
491     * optimization for entities that are immutable, either in your domain or through the relation database
492     * (coming from a view, or a history table for example).
493     *
494     * @var bool
495     */
496    public $isReadOnly = false;
497
498    /**
499     * The ReflectionProperty instances of the mapped class.
500     *
501     * @var array
502     */
503    public $reflFields = array();
504
505    /**
506     * The prototype from which new instances of the mapped class are created.
507     *
508     * @var object
509     */
510    private $_prototype;
511
512    /**
513     * Initializes a new ClassMetadata instance that will hold the object-relational mapping
514     * metadata of the class with the given name.
515     *
516     * @param string $entityName The name of the entity class the new instance is used for.
517     */
518    public function __construct($entityName)
519    {
520        $this->name = $entityName;
521        $this->rootEntityName = $entityName;
522    }
523
524    /**
525     * Gets the ReflectionPropertys of the mapped class.
526     *
527     * @return array An array of ReflectionProperty instances.
528     */
529    public function getReflectionProperties()
530    {
531        return $this->reflFields;
532    }
533
534    /**
535     * Gets a ReflectionProperty for a specific field of the mapped class.
536     *
537     * @param string $name
538     * @return ReflectionProperty
539     */
540    public function getReflectionProperty($name)
541    {
542        return $this->reflFields[$name];
543    }
544
545    /**
546     * Gets the ReflectionProperty for the single identifier field.
547     *
548     * @return ReflectionProperty
549     * @throws BadMethodCallException If the class has a composite identifier.
550     */
551    public function getSingleIdReflectionProperty()
552    {
553        if ($this->isIdentifierComposite) {
554            throw new \BadMethodCallException("Class " . $this->name . " has a composite identifier.");
555        }
556        return $this->reflFields[$this->identifier[0]];
557    }
558
559    /**
560     * Extracts the identifier values of an entity of this class.
561     *
562     * For composite identifiers, the identifier values are returned as an array
563     * with the same order as the field order in {@link identifier}.
564     *
565     * @param object $entity
566     * @return array
567     */
568    public function getIdentifierValues($entity)
569    {
570        if ($this->isIdentifierComposite) {
571            $id = array();
572
573            foreach ($this->identifier as $idField) {
574                $value = $this->reflFields[$idField]->getValue($entity);
575
576                if ($value !== null) {
577                    $id[$idField] = $value;
578                }
579            }
580
581            return $id;
582        }
583
584        $value = $this->reflFields[$this->identifier[0]]->getValue($entity);
585
586        if ($value !== null) {
587            return array($this->identifier[0] => $value);
588        }
589
590        return array();
591    }
592
593    /**
594     * Populates the entity identifier of an entity.
595     *
596     * @param object $entity
597     * @param mixed $id
598     * @todo Rename to assignIdentifier()
599     */
600    public function setIdentifierValues($entity, array $id)
601    {
602        foreach ($id as $idField => $idValue) {
603            $this->reflFields[$idField]->setValue($entity, $idValue);
604        }
605    }
606
607    /**
608     * Sets the specified field to the specified value on the given entity.
609     *
610     * @param object $entity
611     * @param string $field
612     * @param mixed $value
613     */
614    public function setFieldValue($entity, $field, $value)
615    {
616        $this->reflFields[$field]->setValue($entity, $value);
617    }
618
619    /**
620     * Gets the specified field's value off the given entity.
621     *
622     * @param object $entity
623     * @param string $field
624     */
625    public function getFieldValue($entity, $field)
626    {
627        return $this->reflFields[$field]->getValue($entity);
628    }
629
630    /**
631     * Creates a string representation of this instance.
632     *
633     * @return string The string representation of this instance.
634     * @todo Construct meaningful string representation.
635     */
636    public function __toString()
637    {
638        return __CLASS__ . '@' . spl_object_hash($this);
639    }
640
641    /**
642     * Determines which fields get serialized.
643     *
644     * It is only serialized what is necessary for best unserialization performance.
645     * That means any metadata properties that are not set or empty or simply have
646     * their default value are NOT serialized.
647     *
648     * Parts that are also NOT serialized because they can not be properly unserialized:
649     *      - reflClass (ReflectionClass)
650     *      - reflFields (ReflectionProperty array)
651     *
652     * @return array The names of all the fields that should be serialized.
653     */
654    public function __sleep()
655    {
656        // This metadata is always serialized/cached.
657        $serialized = array(
658            'associationMappings',
659            'columnNames', //TODO: Not really needed. Can use fieldMappings[$fieldName]['columnName']
660            'fieldMappings',
661            'fieldNames',
662            'identifier',
663            'isIdentifierComposite', // TODO: REMOVE
664            'name',
665            'namespace', // TODO: REMOVE
666            'table',
667            'rootEntityName',
668            'idGenerator', //TODO: Does not really need to be serialized. Could be moved to runtime.
669        );
670
671        // The rest of the metadata is only serialized if necessary.
672        if ($this->changeTrackingPolicy != self::CHANGETRACKING_DEFERRED_IMPLICIT) {
673            $serialized[] = 'changeTrackingPolicy';
674        }
675
676        if ($this->customRepositoryClassName) {
677            $serialized[] = 'customRepositoryClassName';
678        }
679
680        if ($this->inheritanceType != self::INHERITANCE_TYPE_NONE) {
681            $serialized[] = 'inheritanceType';
682            $serialized[] = 'discriminatorColumn';
683            $serialized[] = 'discriminatorValue';
684            $serialized[] = 'discriminatorMap';
685            $serialized[] = 'parentClasses';
686            $serialized[] = 'subClasses';
687        }
688
689        if ($this->generatorType != self::GENERATOR_TYPE_NONE) {
690            $serialized[] = 'generatorType';
691            if ($this->generatorType == self::GENERATOR_TYPE_SEQUENCE) {
692                $serialized[] = 'sequenceGeneratorDefinition';
693            }
694        }
695
696        if ($this->isMappedSuperclass) {
697            $serialized[] = 'isMappedSuperclass';
698        }
699
700        if ($this->containsForeignIdentifier) {
701            $serialized[] = 'containsForeignIdentifier';
702        }
703
704        if ($this->isVersioned) {
705            $serialized[] = 'isVersioned';
706            $serialized[] = 'versionField';
707        }
708
709        if ($this->lifecycleCallbacks) {
710            $serialized[] = 'lifecycleCallbacks';
711        }
712
713        if ($this->namedQueries) {
714            $serialized[] = 'namedQueries';
715        }
716
717        if ($this->isReadOnly) {
718            $serialized[] = 'isReadOnly';
719        }
720
721        return $serialized;
722    }
723
724    /**
725     * Creates a new instance of the mapped class, without invoking the constructor.
726     *
727     * @return object
728     */
729    public function newInstance()
730    {
731        if ($this->_prototype === null) {
732            $this->_prototype = unserialize(sprintf('O:%d:"%s":0:{}', strlen($this->name), $this->name));
733        }
734
735        return clone $this->_prototype;
736    }
737    /**
738     * Restores some state that can not be serialized/unserialized.
739     *
740     * @param ReflectionService $reflService
741     * @return void
742     */
743    public function wakeupReflection($reflService)
744    {
745        // Restore ReflectionClass and properties
746        $this->reflClass = $reflService->getClass($this->name);
747
748        foreach ($this->fieldMappings as $field => $mapping) {
749            $this->reflFields[$field] = isset($mapping['declared'])
750                ? $reflService->getAccessibleProperty($mapping['declared'], $field)
751                : $reflService->getAccessibleProperty($this->name, $field);
752        }
753
754        foreach ($this->associationMappings as $field => $mapping) {
755            $this->reflFields[$field] = isset($mapping['declared'])
756                ? $reflService->getAccessibleProperty($mapping['declared'], $field)
757                : $reflService->getAccessibleProperty($this->name, $field);
758        }
759    }
760
761    /**
762     * Initializes a new ClassMetadata instance that will hold the object-relational mapping
763     * metadata of the class with the given name.
764     *
765     * @param string $entityName The name of the entity class the new instance is used for.
766     */
767    public function initializeReflection($reflService)
768    {
769        $this->reflClass = $reflService->getClass($this->name);
770        $this->namespace = $reflService->getClassNamespace($this->name);
771        $this->table['name'] = $reflService->getClassShortName($this->name);
772
773        if ($this->reflClass) {
774            $this->name = $this->rootEntityName = $this->reflClass->getName();
775        }
776    }
777
778    /**
779     * Validate Identifier
780     *
781     * @return void
782     */
783    public function validateIdentifier()
784    {
785        // Verify & complete identifier mapping
786        if ( ! $this->identifier && ! $this->isMappedSuperclass) {
787            throw MappingException::identifierRequired($this->name);
788        }
789
790        if ($this->usesIdGenerator() && $this->isIdentifierComposite) {
791            throw MappingException::compositeKeyAssignedIdGeneratorRequired($this->name);
792        }
793    }
794
795    /**
796     * Validate association targets actually exist.
797     *
798     * @return void
799     */
800    public function validateAssocations()
801    {
802        foreach ($this->associationMappings as $field => $mapping) {
803            if ( ! \Doctrine\Common\ClassLoader::classExists($mapping['targetEntity']) ) {
804                throw MappingException::invalidTargetEntityClass($mapping['targetEntity'], $this->name, $mapping['fieldName']);
805            }
806        }
807    }
808
809    /**
810     * Validate lifecycle callbacks
811     *
812     * @param ReflectionService $reflService
813     * @return void
814     */
815    public function validateLifecycleCallbacks($reflService)
816    {
817        foreach ($this->lifecycleCallbacks as $event => $callbacks) {
818            foreach ($callbacks as $callbackFuncName) {
819                if ( ! $reflService->hasPublicMethod($this->name, $callbackFuncName)) {
820                    throw MappingException::lifecycleCallbackMethodNotFound($this->name, $callbackFuncName);
821                }
822            }
823        }
824    }
825
826    /**
827     * Gets the ReflectionClass instance of the mapped class.
828     *
829     * @return ReflectionClass
830     */
831    public function getReflectionClass()
832    {
833        return $this->reflClass;
834    }
835
836    /**
837     * Sets the change tracking policy used by this class.
838     *
839     * @param integer $policy
840     */
841    public function setChangeTrackingPolicy($policy)
842    {
843        $this->changeTrackingPolicy = $policy;
844    }
845
846    /**
847     * Whether the change tracking policy of this class is "deferred explicit".
848     *
849     * @return boolean
850     */
851    public function isChangeTrackingDeferredExplicit()
852    {
853        return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_EXPLICIT;
854    }
855
856    /**
857     * Whether the change tracking policy of this class is "deferred implicit".
858     *
859     * @return boolean
860     */
861    public function isChangeTrackingDeferredImplicit()
862    {
863        return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_IMPLICIT;
864    }
865
866    /**
867     * Whether the change tracking policy of this class is "notify".
868     *
869     * @return boolean
870     */
871    public function isChangeTrackingNotify()
872    {
873        return $this->changeTrackingPolicy == self::CHANGETRACKING_NOTIFY;
874    }
875
876    /**
877     * Checks whether a field is part of the identifier/primary key field(s).
878     *
879     * @param string $fieldName  The field name
880     * @return boolean  TRUE if the field is part of the table identifier/primary key field(s),
881     *                  FALSE otherwise.
882     */
883    public function isIdentifier($fieldName)
884    {
885        if ( ! $this->isIdentifierComposite) {
886            return $fieldName === $this->identifier[0];
887        }
888        return in_array($fieldName, $this->identifier);
889    }
890
891    /**
892     * Check if the field is unique.
893     *
894     * @param string $fieldName  The field name
895     * @return boolean  TRUE if the field is unique, FALSE otherwise.
896     */
897    public function isUniqueField($fieldName)
898    {
899        $mapping = $this->getFieldMapping($fieldName);
900        if ($mapping !== false) {
901            return isset($mapping['unique']) && $mapping['unique'] == true;
902        }
903        return false;
904    }
905
906    /**
907     * Check if the field is not null.
908     *
909     * @param string $fieldName  The field name
910     * @return boolean  TRUE if the field is not null, FALSE otherwise.
911     */
912    public function isNullable($fieldName)
913    {
914        $mapping = $this->getFieldMapping($fieldName);
915        if ($mapping !== false) {
916            return isset($mapping['nullable']) && $mapping['nullable'] == true;
917        }
918        return false;
919    }
920
921    /**
922     * Gets a column name for a field name.
923     * If the column name for the field cannot be found, the given field name
924     * is returned.
925     *
926     * @param string $fieldName The field name.
927     * @return string  The column name.
928     */
929    public function getColumnName($fieldName)
930    {
931        return isset($this->columnNames[$fieldName]) ?
932                $this->columnNames[$fieldName] : $fieldName;
933    }
934
935    /**
936     * Gets the mapping of a (regular) field that holds some data but not a
937     * reference to another object.
938     *
939     * @param string $fieldName  The field name.
940     * @return array  The field mapping.
941     */
942    public function getFieldMapping($fieldName)
943    {
944        if ( ! isset($this->fieldMappings[$fieldName])) {
945            throw MappingException::mappingNotFound($this->name, $fieldName);
946        }
947        return $this->fieldMappings[$fieldName];
948    }
949
950    /**
951     * Gets the mapping of an association.
952     *
953     * @see ClassMetadataInfo::$associationMappings
954     * @param string $fieldName  The field name that represents the association in
955     *                           the object model.
956     * @return array The mapping.
957     */
958    public function getAssociationMapping($fieldName)
959    {
960        if ( ! isset($this->associationMappings[$fieldName])) {
961            throw MappingException::mappingNotFound($this->name, $fieldName);
962        }
963        return $this->associationMappings[$fieldName];
964    }
965
966    /**
967     * Gets all association mappings of the class.
968     *
969     * @return array
970     */
971    public function getAssociationMappings()
972    {
973        return $this->associationMappings;
974    }
975
976    /**
977     * Gets the field name for a column name.
978     * If no field name can be found the column name is returned.
979     *
980     * @param string $columnName    column name
981     * @return string               column alias
982     */
983    public function getFieldName($columnName)
984    {
985        return isset($this->fieldNames[$columnName]) ?
986                $this->fieldNames[$columnName] : $columnName;
987    }
988
989    /**
990     * Gets the named query.
991     *
992     * @see ClassMetadataInfo::$namedQueries
993     * @throws MappingException
994     * @param string $queryName The query name
995     * @return string
996     */
997    public function getNamedQuery($queryName)
998    {
999        if ( ! isset($this->namedQueries[$queryName])) {
1000            throw MappingException::queryNotFound($this->name, $queryName);
1001        }
1002        return $this->namedQueries[$queryName]['dql'];
1003    }
1004
1005    /**
1006     * Gets all named queries of the class.
1007     *
1008     * @return array
1009     */
1010    public function getNamedQueries()
1011    {
1012        return $this->namedQueries;
1013    }
1014
1015    /**
1016     * Validates & completes the given field mapping.
1017     *
1018     * @param array $mapping  The field mapping to validated & complete.
1019     * @return array  The validated and completed field mapping.
1020     */
1021    protected function _validateAndCompleteFieldMapping(array &$mapping)
1022    {
1023        // Check mandatory fields
1024        if ( ! isset($mapping['fieldName']) || strlen($mapping['fieldName']) == 0) {
1025            throw MappingException::missingFieldName($this->name);
1026        }
1027        if ( ! isset($mapping['type'])) {
1028            // Default to string
1029            $mapping['type'] = 'string';
1030        }
1031
1032        // Complete fieldName and columnName mapping
1033        if ( ! isset($mapping['columnName'])) {
1034            $mapping['columnName'] = $mapping['fieldName'];
1035        } else {
1036            if ($mapping['columnName'][0] == '`') {
1037                $mapping['columnName'] = trim($mapping['columnName'], '`');
1038                $mapping['quoted'] = true;
1039            }
1040        }
1041
1042        $this->columnNames[$mapping['fieldName']] = $mapping['columnName'];
1043        if (isset($this->fieldNames[$mapping['columnName']]) || ($this->discriminatorColumn != null && $this->discriminatorColumn['name'] == $mapping['columnName'])) {
1044            throw MappingException::duplicateColumnName($this->name, $mapping['columnName']);
1045        }
1046
1047        $this->fieldNames[$mapping['columnName']] = $mapping['fieldName'];
1048
1049        // Complete id mapping
1050        if (isset($mapping['id']) && $mapping['id'] === true) {
1051            if ($this->versionField == $mapping['fieldName']) {
1052                throw MappingException::cannotVersionIdField($this->name, $mapping['fieldName']);
1053            }
1054
1055            if ( ! in_array($mapping['fieldName'], $this->identifier)) {
1056                $this->identifier[] = $mapping['fieldName'];
1057            }
1058            // Check for composite key
1059            if ( ! $this->isIdentifierComposite && count($this->identifier) > 1) {
1060                $this->isIdentifierComposite = true;
1061            }
1062        }
1063
1064        if (Type::hasType($mapping['type']) && Type::getType($mapping['type'])->canRequireSQLConversion()) {
1065            if (isset($mapping['id']) && $mapping['id'] === true) {
1066                 throw MappingException::sqlConversionNotAllowedForIdentifiers($this->name, $mapping['fieldName'], $mapping['type']);
1067            }
1068
1069            $mapping['requireSQLConversion'] = true;
1070        }
1071    }
1072
1073    /**
1074     * Validates & completes the basic mapping information that is common to all
1075     * association mappings (one-to-one, many-ot-one, one-to-many, many-to-many).
1076     *
1077     * @param array $mapping The mapping.
1078     * @return array The updated mapping.
1079     * @throws MappingException If something is wrong with the mapping.
1080     */
1081    protected function _validateAndCompleteAssociationMapping(array $mapping)
1082    {
1083        if ( ! isset($mapping['mappedBy'])) {
1084            $mapping['mappedBy'] = null;
1085        }
1086        if ( ! isset($mapping['inversedBy'])) {
1087            $mapping['inversedBy'] = null;
1088        }
1089        $mapping['isOwningSide'] = true; // assume owning side until we hit mappedBy
1090
1091        // unset optional indexBy attribute if its empty
1092        if (!isset($mapping['indexBy']) || !$mapping['indexBy']) {
1093            unset($mapping['indexBy']);
1094        }
1095
1096        // If targetEntity is unqualified, assume it is in the same namespace as
1097        // the sourceEntity.
1098        $mapping['sourceEntity'] = $this->name;
1099
1100        if (isset($mapping['targetEntity'])) {
1101            if (strlen($this->namespace) > 0 && strpos($mapping['targetEntity'], '\\') === false) {
1102                $mapping['targetEntity'] = $this->namespace . '\\' . $mapping['targetEntity'];
1103            }
1104
1105            $mapping['targetEntity'] = ltrim($mapping['targetEntity'], '\\');
1106        }
1107
1108        if ( ($mapping['type'] & self::MANY_TO_ONE) > 0 &&
1109                isset($mapping['orphanRemoval']) &&
1110                $mapping['orphanRemoval'] == true) {
1111
1112            throw MappingException::illegalOrphanRemoval($this->name, $mapping['fieldName']);
1113        }
1114
1115        // Complete id mapping
1116        if (isset($mapping['id']) && $mapping['id'] === true) {
1117            if (isset($mapping['orphanRemoval']) && $mapping['orphanRemoval'] == true) {
1118                throw MappingException::illegalOrphanRemovalOnIdentifierAssociation($this->name, $mapping['fieldName']);
1119            }
1120
1121            if ( ! in_array($mapping['fieldName'], $this->identifier)) {
1122                if (count($mapping['joinColumns']) >= 2) {
1123                    throw MappingException::cannotMapCompositePrimaryKeyEntitiesAsForeignId(
1124                        $mapping['targetEntity'], $this->name, $mapping['fieldName']
1125                    );
1126                }
1127
1128                $this->identifier[] = $mapping['fieldName'];
1129                $this->containsForeignIdentifier = true;
1130            }
1131            // Check for composite key
1132            if ( ! $this->isIdentifierComposite && count($this->identifier) > 1) {
1133                $this->isIdentifierComposite = true;
1134            }
1135        }
1136
1137        // Mandatory attributes for both sides
1138        // Mandatory: fieldName, targetEntity
1139        if ( ! isset($mapping['fieldName']) || strlen($mapping['fieldName']) == 0) {
1140            throw MappingException::missingFieldName($this->name);
1141        }
1142        if ( ! isset($mapping['targetEntity'])) {
1143            throw MappingException::missingTargetEntity($mapping['fieldName']);
1144        }
1145
1146        // Mandatory and optional attributes for either side
1147        if ( ! $mapping['mappedBy']) {
1148            if (isset($mapping['joinTable']) && $mapping['joinTable']) {
1149                if (isset($mapping['joinTable']['name']) && $mapping['joinTable']['name'][0] == '`') {
1150                    $mapping['joinTable']['name'] = trim($mapping['joinTable']['name'], '`');
1151                    $mapping['joinTable']['quoted'] = true;
1152                }
1153            }
1154        } else {
1155            $mapping['isOwningSide'] = false;
1156        }
1157
1158        if (isset($mapping['id']) && $mapping['id'] === true && $mapping['type'] & self::TO_MANY) {
1159            throw MappingException::illegalToManyIdentifierAssoaction($this->name, $mapping['fieldName']);
1160        }
1161
1162        // Fetch mode. Default fetch mode to LAZY, if not set.
1163        if ( ! isset($mapping['fetch'])) {
1164            $mapping['fetch'] = self::FETCH_LAZY;
1165        }
1166
1167        // Cascades
1168        $cascades = isset($mapping['cascade']) ? array_map('strtolower', $mapping['cascade']) : array();
1169
1170        if (in_array('all', $cascades)) {
1171            $cascades = array('remove', 'persist', 'refresh', 'merge', 'detach');
1172        }
1173
1174        $mapping['cascade'] = $cascades;
1175        $mapping['isCascadeRemove'] = in_array('remove',  $cascades);
1176        $mapping['isCascadePersist'] = in_array('persist',  $cascades);
1177        $mapping['isCascadeRefresh'] = in_array('refresh',  $cascades);
1178        $mapping['isCascadeMerge'] = in_array('merge',  $cascades);
1179        $mapping['isCascadeDetach'] = in_array('detach',  $cascades);
1180
1181        return $mapping;
1182    }
1183
1184    /**
1185     * Validates & completes a one-to-one association mapping.
1186     *
1187     * @param array $mapping  The mapping to validate & complete.
1188     * @return array The validated & completed mapping.
1189     * @override
1190     */
1191    protected function _validateAndCompleteOneToOneMapping(array $mapping)
1192    {
1193        $mapping = $this->_validateAndCompleteAssociationMapping($mapping);
1194
1195        if (isset($mapping['joinColumns']) && $mapping['joinColumns']) {
1196            $mapping['isOwningSide'] = true;
1197        }
1198
1199        if ($mapping['isOwningSide']) {
1200            if ( ! isset($mapping['joinColumns']) || ! $mapping['joinColumns']) {
1201                // Apply default join column
1202                $mapping['joinColumns'] = array(array(
1203                    'name' => $mapping['fieldName'] . '_id',
1204                    'referencedColumnName' => 'id'
1205                ));
1206            }
1207
1208            $uniqueContraintColumns = array();
1209            foreach ($mapping['joinColumns'] as $key => &$joinColumn) {
1210                if ($mapping['type'] === self::ONE_TO_ONE && ! $this->isInheritanceTypeSingleTable()) {
1211                    if (count($mapping['joinColumns']) == 1) {
1212                        if (! isset($mapping['id']) || ! $mapping['id']) {
1213                            $joinColumn['unique'] = true;
1214                        }
1215                    } else {
1216                        $uniqueContraintColumns[] = $joinColumn['name'];
1217                    }
1218                }
1219                if (empty($joinColumn['name'])) {
1220                    $joinColumn['name'] = $mapping['fieldName'] . '_id';
1221                }
1222                if (empty($joinColumn['referencedColumnName'])) {
1223                    $joinColumn['referencedColumnName'] = 'id';
1224                }
1225                $mapping['sourceToTargetKeyColumns'][$joinColumn['name']] = $joinColumn['referencedColumnName'];
1226                $mapping['joinColumnFieldNames'][$joinColumn['name']] = isset($joinColumn['fieldName'])
1227                        ? $joinColumn['fieldName'] : $joinColumn['name'];
1228            }
1229
1230            if ($uniqueContraintColumns) {
1231                if (!$this->table) {
1232                    throw new \RuntimeException("ClassMetadataInfo::setTable() has to be called before defining a one to one relationship.");
1233                }
1234                $this->table['uniqueConstraints'][$mapping['fieldName']."_uniq"] = array(
1235                    'columns' => $uniqueContraintColumns
1236                );
1237            }
1238
1239            $mapping['targetToSourceKeyColumns'] = array_flip($mapping['sourceToTargetKeyColumns']);
1240        }
1241
1242        $mapping['orphanRemoval']   = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false;
1243        $mapping['isCascadeRemove'] = $mapping['orphanRemoval'] ? true : $mapping['isCascadeRemove'];
1244
1245        if (isset($mapping['id']) && $mapping['id'] === true && !$mapping['isOwningSide']) {
1246            throw MappingException::illegalInverseIdentifierAssocation($this->name, $mapping['fieldName']);
1247        }
1248
1249        return $mapping;
1250    }
1251
1252    /**
1253     * Validates and completes the mapping.
1254     *
1255     * @param array $mapping The mapping to validate and complete.
1256     * @return array The validated and completed mapping.
1257     * @override
1258     */
1259    protected function _validateAndCompleteOneToManyMapping(array $mapping)
1260    {
1261        $mapping = $this->_validateAndCompleteAssociationMapping($mapping);
1262
1263        // OneToMany-side MUST be inverse (must have mappedBy)
1264        if ( ! isset($mapping['mappedBy'])) {
1265            throw MappingException::oneToManyRequiresMappedBy($mapping['fieldName']);
1266        }
1267
1268        $mapping['orphanRemoval']   = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false;
1269        $mapping['isCascadeRemove'] = $mapping['orphanRemoval'] ? true : $mapping['isCascadeRemove'];
1270
1271        if (isset($mapping['orderBy'])) {
1272            if ( ! is_array($mapping['orderBy'])) {
1273                throw new \InvalidArgumentException("'orderBy' is expected to be an array, not ".gettype($mapping['orderBy']));
1274            }
1275        }
1276
1277        return $mapping;
1278    }
1279
1280    protected function _validateAndCompleteManyToManyMapping(array $mapping)
1281    {
1282        $mapping = $this->_validateAndCompleteAssociationMapping($mapping);
1283        if ($mapping['isOwningSide']) {
1284            if (strpos($mapping['sourceEntity'], '\\') !== false) {
1285                $sourceShortName = strtolower(substr($mapping['sourceEntity'], strrpos($mapping['sourceEntity'], '\\') + 1));
1286            } else {
1287                $sourceShortName = strtolower($mapping['sourceEntity']);
1288            }
1289            if (strpos($mapping['targetEntity'], '\\') !== false) {
1290                $targetShortName = strtolower(substr($mapping['targetEntity'], strrpos($mapping['targetEntity'], '\\') + 1));
1291            } else {
1292                $targetShortName = strtolower($mapping['targetEntity']);
1293            }
1294
1295            // owning side MUST have a join table
1296            if ( ! isset($mapping['joinTable']['name'])) {
1297                $mapping['joinTable']['name'] = $sourceShortName .'_' . $targetShortName;
1298            }
1299            if ( ! isset($mapping['joinTable']['joinColumns'])) {
1300                $mapping['joinTable']['joinColumns'] = array(array(
1301                        'name' => $sourceShortName . '_id',
1302                        'referencedColumnName' => 'id',
1303                        'onDelete' => 'CASCADE'));
1304            }
1305            if ( ! isset($mapping['joinTable']['inverseJoinColumns'])) {
1306                $mapping['joinTable']['inverseJoinColumns'] = array(array(
1307                        'name' => $targetShortName . '_id',
1308                        'referencedColumnName' => 'id',
1309                        'onDelete' => 'CASCADE'));
1310            }
1311
1312            foreach ($mapping['joinTable']['joinColumns'] as &$joinColumn) {
1313                if (empty($joinColumn['name'])) {
1314                    $joinColumn['name'] = $sourceShortName . '_id';
1315                }
1316                if (empty($joinColumn['referencedColumnName'])) {
1317                    $joinColumn['referencedColumnName'] = 'id';
1318                }
1319                if (isset($joinColumn['onDelete']) && strtolower($joinColumn['onDelete']) == 'cascade') {
1320                    $mapping['isOnDeleteCascade'] = true;
1321                }
1322                $mapping['relationToSourceKeyColumns'][$joinColumn['name']] = $joinColumn['referencedColumnName'];
1323                $mapping['joinTableColumns'][] = $joinColumn['name'];
1324            }
1325
1326            foreach ($mapping['joinTable']['inverseJoinColumns'] as &$inverseJoinColumn) {
1327                if (empty($inverseJoinColumn['name'])) {
1328                    $inverseJoinColumn['name'] = $targetShortName . '_id';
1329                }
1330                if (empty($inverseJoinColumn['referencedColumnName'])) {
1331                    $inverseJoinColumn['referencedColumnName'] = 'id';
1332                }
1333                if (isset($inverseJoinColumn['onDelete']) && strtolower($inverseJoinColumn['onDelete']) == 'cascade') {
1334                    $mapping['isOnDeleteCascade'] = true;
1335                }
1336                $mapping['relationToTargetKeyColumns'][$inverseJoinColumn['name']] = $inverseJoinColumn['referencedColumnName'];
1337                $mapping['joinTableColumns'][] = $inverseJoinColumn['name'];
1338            }
1339        }
1340
1341        $mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false;
1342
1343        if (isset($mapping['orderBy'])) {
1344            if ( ! is_array($mapping['orderBy'])) {
1345                throw new \InvalidArgumentException("'orderBy' is expected to be an array, not ".gettype($mapping['orderBy']));
1346            }
1347        }
1348
1349        return $mapping;
1350    }
1351
1352    /**
1353     * Gets the identifier (primary key) field names of the class.
1354     *
1355     * @return mixed
1356     */
1357    public function getIdentifierFieldNames()
1358    {
1359        return $this->identifier;
1360    }
1361
1362    /**
1363     * Gets the name of the single id field. Note that this only works on
1364     * entity classes that have a single-field pk.
1365     *
1366     * @return string
1367     * @throws MappingException If the class has a composite primary key.
1368     */
1369    public function getSingleIdentifierFieldName()
1370    {
1371        if ($this->isIdentifierComposite) {
1372            throw MappingException::singleIdNotAllowedOnCompositePrimaryKey($this->name);
1373        }
1374        return $this->identifier[0];
1375    }
1376
1377    /**
1378     * Gets the column name of the single id column. Note that this only works on
1379     * entity classes that have a single-field pk.
1380     *
1381     * @return string
1382     * @throws MappingException If the class has a composite primary key.
1383     */
1384    public function getSingleIdentifierColumnName()
1385    {
1386        return $this->getColumnName($this->getSingleIdentifierFieldName());
1387    }
1388
1389    /**
1390     * INTERNAL:
1391     * Sets the mapped identifier/primary key fields of this class.
1392     * Mainly used by the ClassMetadataFactory to assign inherited identifiers.
1393     *
1394     * @param array $identifier
1395     */
1396    public function setIdentifier(array $identifier)
1397    {
1398        $this->identifier = $identifier;
1399        $this->isIdentifierComposite = (count($this->identifier) > 1);
1400    }
1401
1402    /**
1403     * Gets the mapped identifier field of this class.
1404     *
1405     * @return string $identifier
1406     */
1407    public function getIdentifier()
1408    {
1409        return $this->identifier;
1410    }
1411
1412    /**
1413     * Checks whether the class has a (mapped) field with a certain name.
1414     *
1415     * @return boolean
1416     */
1417    public function hasField($fieldName)
1418    {
1419        return isset($this->fieldMappings[$fieldName]);
1420    }
1421
1422    /**
1423     * Gets an array containing all the column names.
1424     *
1425     * @return array
1426     */
1427    public function getColumnNames(array $fieldNames = null)
1428    {
1429        if ($fieldNames === null) {
1430            return array_keys($this->fieldNames);
1431        } else {
1432            $columnNames = array();
1433            foreach ($fieldNames as $fieldName) {
1434                $columnNames[] = $this->getColumnName($fieldName);
1435            }
1436            return $columnNames;
1437        }
1438    }
1439
1440    /**
1441     * Returns an array with all the identifier column names.
1442     *
1443     * @return array
1444     */
1445    public function getIdentifierColumnNames()
1446    {
1447        $columnNames = array();
1448
1449        foreach ($this->identifier as $idProperty) {
1450            if (isset($this->fieldMappings[$idProperty])) {
1451                $columnNames[] = $this->fieldMappings[$idProperty]['columnName'];
1452
1453                continue;
1454            }
1455
1456            // Association defined as Id field
1457            $joinColumns      = $this->associationMappings[$idProperty]['joinColumns'];
1458            $assocColumnNames = array_map(function ($joinColumn) { return $joinColumn['name']; }, $joinColumns);
1459
1460            $columnNames = array_merge($columnNames, $assocColumnNames);
1461        }
1462
1463        return $columnNames;
1464    }
1465
1466    /**
1467     * Sets the type of Id generator to use for the mapped class.
1468     */
1469    public function setIdGeneratorType($generatorType)
1470    {
1471        $this->generatorType = $generatorType;
1472    }
1473
1474    /**
1475     * Checks whether the mapped class uses an Id generator.
1476     *
1477     * @return boolean TRUE if the mapped class uses an Id generator, FALSE otherwise.
1478     */
1479    public function usesIdGenerator()
1480    {
1481        return $this->generatorType != self::GENERATOR_TYPE_NONE;
1482    }
1483
1484    /**
1485     * @return boolean
1486     */
1487    public function isInheritanceTypeNone()
1488    {
1489        return $this->inheritanceType == self::INHERITANCE_TYPE_NONE;
1490    }
1491
1492    /**
1493     * Checks whether the mapped class uses the JOINED inheritance mapping strategy.
1494     *
1495     * @return boolean TRUE if the class participates in a JOINED inheritance mapping,
1496     *                 FALSE otherwise.
1497     */
1498    public function isInheritanceTypeJoined()
1499    {
1500        return $this->inheritanceType == self::INHERITANCE_TYPE_JOINED;
1501    }
1502
1503    /**
1504     * Checks whether the mapped class uses the SINGLE_TABLE inheritance mapping strategy.
1505     *
1506     * @return boolean TRUE if the class participates in a SINGLE_TABLE inheritance mapping,
1507     *                 FALSE otherwise.
1508     */
1509    public function isInheritanceTypeSingleTable()
1510    {
1511        return $this->inheritanceType == self::INHERITANCE_TYPE_SINGLE_TABLE;
1512    }
1513
1514    /**
1515     * Checks whether the mapped class uses the TABLE_PER_CLASS inheritance mapping strategy.
1516     *
1517     * @return boolean TRUE if the class participates in a TABLE_PER_CLASS inheritance mapping,
1518     *                 FALSE otherwise.
1519     */
1520    public function isInheritanceTypeTablePerClass()
1521    {
1522        return $this->inheritanceType == self::INHERITANCE_TYPE_TABLE_PER_CLASS;
1523    }
1524
1525    /**
1526     * Checks whether the class uses an identity column for the Id generation.
1527     *
1528     * @return boolean TRUE if the class uses the IDENTITY generator, FALSE otherwise.
1529     */
1530    public function isIdGeneratorIdentity()
1531    {
1532        return $this->generatorType == self::GENERATOR_TYPE_IDENTITY;
1533    }
1534
1535    /**
1536     * Checks whether the class uses a sequence for id generation.
1537     *
1538     * @return boolean TRUE if the class uses the SEQUENCE generator, FALSE otherwise.
1539     */
1540    public function isIdGeneratorSequence()
1541    {
1542        return $this->generatorType == self::GENERATOR_TYPE_SEQUENCE;
1543    }
1544
1545    /**
1546     * Checks whether the class uses a table for id generation.
1547     *
1548     * @return boolean  TRUE if the class uses the TABLE generator, FALSE otherwise.
1549     */
1550    public function isIdGeneratorTable()
1551    {
1552        $this->generatorType == self::GENERATOR_TYPE_TABLE;
1553    }
1554
1555    /**
1556     * Checks whether the class has a natural identifier/pk (which means it does
1557     * not use any Id generator.
1558     *
1559     * @return boolean
1560     */
1561    public function isIdentifierNatural()
1562    {
1563        return $this->generatorType == self::GENERATOR_TYPE_NONE;
1564    }
1565
1566    /**
1567     * Gets the type of a field.
1568     *
1569     * @param string $fieldName
1570     * @return \Doctrine\DBAL\Types\Type
1571     */
1572    public function getTypeOfField($fieldName)
1573    {
1574        return isset($this->fieldMappings[$fieldName]) ?
1575                $this->fieldMappings[$fieldName]['type'] : null;
1576    }
1577
1578    /**
1579     * Gets the type of a column.
1580     *
1581     * @return \Doctrine\DBAL\Types\Type
1582     */
1583    public function getTypeOfColumn($columnName)
1584    {
1585        return $this->getTypeOfField($this->getFieldName($columnName));
1586    }
1587
1588    /**
1589     * Gets the name of the primary table.
1590     *
1591     * @return string
1592     */
1593    public function getTableName()
1594    {
1595        return $this->table['name'];
1596    }
1597
1598    /**
1599     * Gets the table name to use for temporary identifier tables of this class.
1600     *
1601     * @return string
1602     */
1603    public function getTemporaryIdTableName()
1604    {
1605        // replace dots with underscores because PostgreSQL creates temporary tables in a special schema
1606        return str_replace('.', '_', $this->getTableName() . '_id_tmp');
1607    }
1608
1609    /**
1610     * Sets the mapped subclasses of this class.
1611     *
1612     * @param array $subclasses The names of all mapped subclasses.
1613     */
1614    public function setSubclasses(array $subclasses)
1615    {
1616        foreach ($subclasses as $subclass) {
1617            if (strpos($subclass, '\\') === false && strlen($this->namespace)) {
1618                $this->subClasses[] = $this->namespace . '\\' . $subclass;
1619            } else {
1620                $this->subClasses[] = $subclass;
1621            }
1622        }
1623    }
1624
1625    /**
1626     * Sets the parent class names.
1627     * Assumes that the class names in the passed array are in the order:
1628     * directParent -> directParentParent -> directParentParentParent ... -> root.
1629     */
1630    public function setParentClasses(array $classNames)
1631    {
1632        $this->parentClasses = $classNames;
1633        if (count($classNames) > 0) {
1634            $this->rootEntityName = array_pop($classNames);
1635        }
1636    }
1637
1638    /**
1639     * Sets the inheritance type used by the class and it's subclasses.
1640     *
1641     * @param integer $type
1642     */
1643    public function setInheritanceType($type)
1644    {
1645        if ( ! $this->_isInheritanceType($type)) {
1646            throw MappingException::invalidInheritanceType($this->name, $type);
1647        }
1648        $this->inheritanceType = $type;
1649    }
1650
1651    /**
1652     * Checks whether a mapped field is inherited from an entity superclass.
1653     *
1654     * @return boolean TRUE if the field is inherited, FALSE otherwise.
1655     */
1656    public function isInheritedField($fieldName)
1657    {
1658        return isset($this->fieldMappings[$fieldName]['inherited']);
1659    }
1660
1661    /**
1662     * Checks whether a mapped association field is inherited from a superclass.
1663     *
1664     * @param string $fieldName
1665     * @return boolean TRUE if the field is inherited, FALSE otherwise.
1666     */
1667    public function isInheritedAssociation($fieldName)
1668    {
1669        return isset($this->associationMappings[$fieldName]['inherited']);
1670    }
1671
1672    /**
1673     * Sets the name of the primary table the class is mapped to.
1674     *
1675     * @param string $tableName The table name.
1676     * @deprecated Use {@link setPrimaryTable}.
1677     */
1678    public function setTableName($tableName)
1679    {
1680        $this->table['name'] = $tableName;
1681    }
1682
1683    /**
1684     * Sets the primary table definition. The provided array supports the
1685     * following structure:
1686     *
1687     * name => <tableName> (optional, defaults to class name)
1688     * indexes => array of indexes (optional)
1689     * uniqueConstraints => array of constraints (optional)
1690     *
1691     * If a key is omitted, the current value is kept.
1692     *
1693     * @param array $table The table description.
1694     */
1695    public function setPrimaryTable(array $table)
1696    {
1697        if (isset($table['name'])) {
1698            if ($table['name'][0] == '`') {
1699                $this->table['name'] = str_replace("`", "", $table['name']);
1700                $this->table['quoted'] = true;
1701            } else {
1702                $this->table['name'] = $table['name'];
1703            }
1704        }
1705
1706        if (isset($table['indexes'])) {
1707            $this->table['indexes'] = $table['indexes'];
1708        }
1709
1710        if (isset($table['uniqueConstraints'])) {
1711            $this->table['uniqueConstraints'] = $table['uniqueConstraints'];
1712        }
1713    }
1714
1715    /**
1716     * Checks whether the given type identifies an inheritance type.
1717     *
1718     * @param integer $type
1719     * @return boolean TRUE if the given type identifies an inheritance type, FALSe otherwise.
1720     */
1721    private function _isInheritanceType($type)
1722    {
1723        return $type == self::INHERITANCE_TYPE_NONE ||
1724                $type == self::INHERITANCE_TYPE_SINGLE_TABLE ||
1725                $type == self::INHERITANCE_TYPE_JOINED ||
1726                $type == self::INHERITANCE_TYPE_TABLE_PER_CLASS;
1727    }
1728
1729    /**
1730     * Adds a mapped field to the class.
1731     *
1732     * @param array $mapping The field mapping.
1733     */
1734    public function mapField(array $mapping)
1735    {
1736        $this->_validateAndCompleteFieldMapping($mapping);
1737        if (isset($this->fieldMappings[$mapping['fieldName']]) || isset($this->associationMappings[$mapping['fieldName']])) {
1738            throw MappingException::duplicateFieldMapping($this->name, $mapping['fieldName']);
1739        }
1740        $this->fieldMappings[$mapping['fieldName']] = $mapping;
1741    }
1742
1743    /**
1744     * INTERNAL:
1745     * Adds an association mapping without completing/validating it.
1746     * This is mainly used to add inherited association mappings to derived classes.
1747     *
1748     * @param array $mapping
1749     */
1750    public function addInheritedAssociationMapping(array $mapping/*, $owningClassName = null*/)
1751    {
1752        if (isset($this->associationMappings[$mapping['fieldName']])) {
1753            throw MappingException::duplicateAssociationMapping($this->name, $mapping['fieldName']);
1754        }
1755        $this->associationMappings[$mapping['fieldName']] = $mapping;
1756    }
1757
1758    /**
1759     * INTERNAL:
1760     * Adds a field mapping without completing/validating it.
1761     * This is mainly used to add inherited field mappings to derived classes.
1762     *
1763     * @param array $mapping
1764     */
1765    public function addInheritedFieldMapping(array $fieldMapping)
1766    {
1767        $this->fieldMappings[$fieldMapping['fieldName']] = $fieldMapping;
1768        $this->columnNames[$fieldMapping['fieldName']] = $fieldMapping['columnName'];
1769        $this->fieldNames[$fieldMapping['columnName']] = $fieldMapping['fieldName'];
1770    }
1771
1772    /**
1773     * INTERNAL:
1774     * Adds a named query to this class.
1775     *
1776     * @throws MappingException
1777     * @param array $queryMapping
1778     */
1779    public function addNamedQuery(array $queryMapping)
1780    {
1781        if (isset($this->namedQueries[$queryMapping['name']])) {
1782            throw MappingException::duplicateQueryMapping($this->name, $queryMapping['name']);
1783        }
1784
1785        $name   = $queryMapping['name'];
1786        $query  = $queryMapping['query'];
1787        $dql    = str_replace('__CLASS__', $this->name, $query);
1788        $this->namedQueries[$name] = array(
1789            'name'  => $name,
1790            'query' => $query,
1791            'dql'   => $dql
1792        );
1793    }
1794
1795    /**
1796     * Adds a one-to-one mapping.
1797     *
1798     * @param array $mapping The mapping.
1799     */
1800    public function mapOneToOne(array $mapping)
1801    {
1802        $mapping['type'] = self::ONE_TO_ONE;
1803        $mapping = $this->_validateAndCompleteOneToOneMapping($mapping);
1804        $this->_storeAssociationMapping($mapping);
1805    }
1806
1807    /**
1808     * Adds a one-to-many mapping.
1809     *
1810     * @param array $mapping The mapping.
1811     */
1812    public function mapOneToMany(array $mapping)
1813    {
1814        $mapping['type'] = self::ONE_TO_MANY;
1815        $mapping = $this->_validateAndCompleteOneToManyMapping($mapping);
1816        $this->_storeAssociationMapping($mapping);
1817    }
1818
1819    /**
1820     * Adds a many-to-one mapping.
1821     *
1822     * @param array $mapping The mapping.
1823     */
1824    public function mapManyToOne(array $mapping)
1825    {
1826        $mapping['type'] = self::MANY_TO_ONE;
1827        // A many-to-one mapping is essentially a one-one backreference
1828        $mapping = $this->_validateAndCompleteOneToOneMapping($mapping);
1829        $this->_storeAssociationMapping($mapping);
1830    }
1831
1832    /**
1833     * Adds a many-to-many mapping.
1834     *
1835     * @param array $mapping The mapping.
1836     */
1837    public function mapManyToMany(array $mapping)
1838    {
1839        $mapping['type'] = self::MANY_TO_MANY;
1840        $mapping = $this->_validateAndCompleteManyToManyMapping($mapping);
1841        $this->_storeAssociationMapping($mapping);
1842    }
1843
1844    /**
1845     * Stores the association mapping.
1846     *
1847     * @param array $assocMapping
1848     */
1849    protected function _storeAssociationMapping(array $assocMapping)
1850    {
1851        $sourceFieldName = $assocMapping['fieldName'];
1852
1853        if (isset($this->fieldMappings[$sourceFieldName]) || isset($this->associationMappings[$sourceFieldName])) {
1854            throw MappingException::duplicateFieldMapping($this->name, $sourceFieldName);
1855        }
1856
1857        $this->associationMappings[$sourceFieldName] = $assocMapping;
1858    }
1859
1860    /**
1861     * Registers a custom repository class for the entity class.
1862     *
1863     * @param string $mapperClassName  The class name of the custom mapper.
1864     */
1865    public function setCustomRepositoryClass($repositoryClassName)
1866    {
1867        if ($repositoryClassName !== null && strpos($repositoryClassName, '\\') === false
1868                && strlen($this->namespace) > 0) {
1869            $repositoryClassName = $this->namespace . '\\' . $repositoryClassName;
1870        }
1871        $this->customRepositoryClassName = $repositoryClassName;
1872    }
1873
1874    /**
1875     * Dispatches the lifecycle event of the given entity to the registered
1876     * lifecycle callbacks and lifecycle listeners.
1877     *
1878     * @param string $event The lifecycle event.
1879     * @param Entity $entity The Entity on which the event occured.
1880     */
1881    public function invokeLifecycleCallbacks($lifecycleEvent, $entity)
1882    {
1883        foreach ($this->lifecycleCallbacks[$lifecycleEvent] as $callback) {
1884            $entity->$callback();
1885        }
1886    }
1887
1888    /**
1889     * Whether the class has any attached lifecycle listeners or callbacks for a lifecycle event.
1890     *
1891     * @param string $lifecycleEvent
1892     * @return boolean
1893     */
1894    public function hasLifecycleCallbacks($lifecycleEvent)
1895    {
1896        return isset($this->lifecycleCallbacks[$lifecycleEvent]);
1897    }
1898
1899    /**
1900     * Gets the registered lifecycle callbacks for an event.
1901     *
1902     * @param string $event
1903     * @return array
1904     */
1905    public function getLifecycleCallbacks($event)
1906    {
1907        return isset($this->lifecycleCallbacks[$event]) ? $this->lifecycleCallbacks[$event] : array();
1908    }
1909
1910    /**
1911     * Adds a lifecycle callback for entities of this class.
1912     *
1913     * @param string $callback
1914     * @param string $event
1915     */
1916    public function addLifecycleCallback($callback, $event)
1917    {
1918        $this->lifecycleCallbacks[$event][] = $callback;
1919    }
1920
1921    /**
1922     * Sets the lifecycle callbacks for entities of this class.
1923     * Any previously registered callbacks are overwritten.
1924     *
1925     * @param array $callbacks
1926     */
1927    public function setLifecycleCallbacks(array $callbacks)
1928    {
1929        $this->lifecycleCallbacks = $callbacks;
1930    }
1931
1932    /**
1933     * Sets the discriminator column definition.
1934     *
1935     * @param array $columnDef
1936     * @see getDiscriminatorColumn()
1937     */
1938    public function setDiscriminatorColumn($columnDef)
1939    {
1940        if ($columnDef !== null) {
1941            if (isset($this->fieldNames[$columnDef['name']])) {
1942                throw MappingException::duplicateColumnName($this->name, $columnDef['name']);
1943            }
1944
1945            if ( ! isset($columnDef['name'])) {
1946                throw MappingException::nameIsMandatoryForDiscriminatorColumns($this->name, $columnDef);
1947            }
1948            if ( ! isset($columnDef['fieldName'])) {
1949                $columnDef['fieldName'] = $columnDef['name'];
1950            }
1951            if ( ! isset($columnDef['type'])) {
1952                $columnDef['type'] = "string";
1953            }
1954            if (in_array($columnDef['type'], array("boolean", "array", "object", "datetime", "time", "date"))) {
1955                throw MappingException::invalidDiscriminatorColumnType($this->name, $columnDef['type']);
1956            }
1957
1958            $this->discriminatorColumn = $columnDef;
1959        }
1960    }
1961
1962    /**
1963     * Sets the discriminator values used by this class.
1964     * Used for JOINED and SINGLE_TABLE inheritance mapping strategies.
1965     *
1966     * @param array $map
1967     */
1968    public function setDiscriminatorMap(array $map)
1969    {
1970        foreach ($map as $value => $className) {
1971            $this->addDiscriminatorMapClass($value, $className);
1972        }
1973    }
1974
1975    /**
1976     * Add one entry of the discriminator map with a new class and corresponding name.
1977     *
1978     * @param string $name
1979     * @param string $className
1980     */
1981    public function addDiscriminatorMapClass($name, $className)
1982    {
1983        if (strlen($this->namespace) > 0 && strpos($className, '\\') === false) {
1984            $className = $this->namespace . '\\' . $className;
1985        }
1986
1987        $className = ltrim($className, '\\');
1988        $this->discriminatorMap[$name] = $className;
1989
1990        if ($this->name == $className) {
1991            $this->discriminatorValue = $name;
1992        } else {
1993            if ( ! class_exists($className)) {
1994                throw MappingException::invalidClassInDiscriminatorMap($className, $this->name);
1995            }
1996            if (is_subclass_of($className, $this->name) && ! in_array($className, $this->subClasses)) {
1997                $this->subClasses[] = $className;
1998            }
1999        }
2000    }
2001
2002    /**
2003     * Checks whether the class has a named query with the given query name.
2004     *
2005     * @param string $fieldName
2006     * @return boolean
2007     */
2008    public function hasNamedQuery($queryName)
2009    {
2010        return isset($this->namedQueries[$queryName]);
2011    }
2012
2013    /**
2014     * Checks whether the class has a mapped association with the given field name.
2015     *
2016     * @param string $fieldName
2017     * @return boolean
2018     */
2019    public function hasAssociation($fieldName)
2020    {
2021        return isset($this->associationMappings[$fieldName]);
2022    }
2023
2024    /**
2025     * Checks whether the class has a mapped association for the specified field
2026     * and if yes, checks whether it is a single-valued association (to-one).
2027     *
2028     * @param string $fieldName
2029     * @return boolean TRUE if the association exists and is single-valued, FALSE otherwise.
2030     */
2031    public function isSingleValuedAssociation($fieldName)
2032    {
2033        return isset($this->associationMappings[$fieldName]) &&
2034                ($this->associationMappings[$fieldName]['type'] & self::TO_ONE);
2035    }
2036
2037    /**
2038     * Checks whether the class has a mapped association for the specified field
2039     * and if yes, checks whether it is a collection-valued association (to-many).
2040     *
2041     * @param string $fieldName
2042     * @return boolean TRUE if the association exists and is collection-valued, FALSE otherwise.
2043     */
2044    public function isCollectionValuedAssociation($fieldName)
2045    {
2046        return isset($this->associationMappings[$fieldName]) &&
2047                ! ($this->associationMappings[$fieldName]['type'] & self::TO_ONE);
2048    }
2049
2050    /**
2051     * Is this an association that only has a single join column?
2052     *
2053     * @param  string $fieldName
2054     * @return bool
2055     */
2056    public function isAssociationWithSingleJoinColumn($fieldName)
2057    {
2058        return (
2059            isset($this->associationMappings[$fieldName]) &&
2060            isset($this->associationMappings[$fieldName]['joinColumns'][0]) &&
2061            !isset($this->associationMappings[$fieldName]['joinColumns'][1])
2062        );
2063    }
2064
2065    /**
2066     * Return the single association join column (if any).
2067     *
2068     * @param string $fieldName
2069     * @return string
2070     */
2071    public function getSingleAssociationJoinColumnName($fieldName)
2072    {
2073        if (!$this->isAssociationWithSingleJoinColumn($fieldName)) {
2074            throw MappingException::noSingleAssociationJoinColumnFound($this->name, $fieldName);
2075        }
2076        return $this->associationMappings[$fieldName]['joinColumns'][0]['name'];
2077    }
2078
2079    /**
2080     * Return the single association referenced join column name (if any).
2081     *
2082     * @param string $fieldName
2083     * @return string
2084     */
2085    public function getSingleAssociationReferencedJoinColumnName($fieldName)
2086    {
2087        if (!$this->isAssociationWithSingleJoinColumn($fieldName)) {
2088            throw MappingException::noSingleAssociationJoinColumnFound($this->name, $fieldName);
2089        }
2090        return $this->associationMappings[$fieldName]['joinColumns'][0]['referencedColumnName'];
2091    }
2092
2093    /**
2094     * Used to retrieve a fieldname for either field or association from a given column,
2095     *
2096     * This method is used in foreign-key as primary-key contexts.
2097     *
2098     * @param  string $columnName
2099     * @return string
2100     */
2101    public function getFieldForColumn($columnName)
2102    {
2103        if (isset($this->fieldNames[$columnName])) {
2104            return $this->fieldNames[$columnName];
2105        } else {
2106            foreach ($this->associationMappings AS $assocName => $mapping) {
2107                if ($this->isAssociationWithSingleJoinColumn($assocName) &&
2108                    $this->associationMappings[$assocName]['joinColumns'][0]['name'] == $columnName) {
2109
2110                    return $assocName;
2111                }
2112            }
2113
2114            throw MappingException::noFieldNameFoundForColumn($this->name, $columnName);
2115        }
2116    }
2117
2118    /**
2119     * Sets the ID generator used to generate IDs for instances of this class.
2120     *
2121     * @param AbstractIdGenerator $generator
2122     */
2123    public function setIdGenerator($generator)
2124    {
2125        $this->idGenerator = $generator;
2126    }
2127
2128    /**
2129     * Sets the definition of the sequence ID generator for this class.
2130     *
2131     * The definition must have the following structure:
2132     * <code>
2133     * array(
2134     *     'sequenceName' => 'name',
2135     *     'allocationSize' => 20,
2136     *     'initialValue' => 1
2137     * )
2138     * </code>
2139     *
2140     * @param array $definition
2141     */
2142    public function setSequenceGeneratorDefinition(array $definition)
2143    {
2144        $this->sequenceGeneratorDefinition = $definition;
2145    }
2146
2147    /**
2148     * Sets the version field mapping used for versioning. Sets the default
2149     * value to use depending on the column type.
2150     *
2151     * @param array $mapping   The version field mapping array
2152     */
2153    public function setVersionMapping(array &$mapping)
2154    {
2155        $this->isVersioned = true;
2156        $this->versionField = $mapping['fieldName'];
2157
2158        if ( ! isset($mapping['default'])) {
2159            if (in_array($mapping['type'], array('integer', 'bigint', 'smallint'))) {
2160                $mapping['default'] = 1;
2161            } else if ($mapping['type'] == 'datetime') {
2162                $mapping['default'] = 'CURRENT_TIMESTAMP';
2163            } else {
2164                throw MappingException::unsupportedOptimisticLockingType($this->name, $mapping['fieldName'], $mapping['type']);
2165            }
2166        }
2167    }
2168
2169    /**
2170     * Sets whether this class is to be versioned for optimistic locking.
2171     *
2172     * @param boolean $bool
2173     */
2174    public function setVersioned($bool)
2175    {
2176        $this->isVersioned = $bool;
2177    }
2178
2179    /**
2180     * Sets the name of the field that is to be used for versioning if this class is
2181     * versioned for optimistic locking.
2182     *
2183     * @param string $versionField
2184     */
2185    public function setVersionField($versionField)
2186    {
2187        $this->versionField = $versionField;
2188    }
2189
2190    /**
2191     * Mark this class as read only, no change tracking is applied to it.
2192     *
2193     * @return void
2194     */
2195    public function markReadOnly()
2196    {
2197        $this->isReadOnly = true;
2198    }
2199
2200    /**
2201     * A numerically indexed list of field names of this persistent class.
2202     *
2203     * This array includes identifier fields if present on this class.
2204     *
2205     * @return array
2206     */
2207    public function getFieldNames()
2208    {
2209        return array_keys($this->fieldMappings);
2210    }
2211
2212    /**
2213     * A numerically indexed list of association names of this persistent class.
2214     *
2215     * This array includes identifier associations if present on this class.
2216     *
2217     * @return array
2218     */
2219    public function getAssociationNames()
2220    {
2221        return array_keys($this->associationMappings);
2222    }
2223
2224    /**
2225     * Returns the target class name of the given association.
2226     *
2227     * @param string $assocName
2228     * @return string
2229     */
2230    public function getAssociationTargetClass($assocName)
2231    {
2232        if ( ! isset($this->associationMappings[$assocName])) {
2233            throw new \InvalidArgumentException("Association name expected, '" . $assocName ."' is not an association.");
2234        }
2235
2236        return $this->associationMappings[$assocName]['targetEntity'];
2237    }
2238
2239    /**
2240     * Get fully-qualified class name of this persistent class.
2241     *
2242     * @return string
2243     */
2244    public function getName()
2245    {
2246        return $this->name;
2247    }
2248
2249    /**
2250     * Gets the (possibly quoted) identifier column names for safe use in an SQL statement.
2251     *
2252     * @param AbstractPlatform $platform
2253     * @return array
2254     */
2255    public function getQuotedIdentifierColumnNames($platform)
2256    {
2257        $quotedColumnNames = array();
2258
2259        foreach ($this->identifier as $idProperty) {
2260            if (isset($this->fieldMappings[$idProperty])) {
2261                $quotedColumnNames[] = isset($this->fieldMappings[$idProperty]['quoted'])
2262                    ? $platform->quoteIdentifier($this->fieldMappings[$idProperty]['columnName'])
2263                    : $this->fieldMappings[$idProperty]['columnName'];
2264
2265                continue;
2266            }
2267
2268            // Association defined as Id field
2269            $joinColumns            = $this->associationMappings[$idProperty]['joinColumns'];
2270            $assocQuotedColumnNames = array_map(
2271                function ($joinColumn) {
2272                    return isset($joinColumn['quoted'])
2273                        ? $platform->quoteIdentifier($joinColumn['name'])
2274                        : $joinColumn['name'];
2275                },
2276                $joinColumns
2277            );
2278
2279            $quotedColumnNames = array_merge($quotedColumnNames, $assocQuotedColumnNames);
2280        }
2281
2282        return $quotedColumnNames;
2283    }
2284
2285    /**
2286     * Gets the (possibly quoted) column name of a mapped field for safe use
2287     * in an SQL statement.
2288     *
2289     * @param string $field
2290     * @param AbstractPlatform $platform
2291     * @return string
2292     */
2293    public function getQuotedColumnName($field, $platform)
2294    {
2295        return isset($this->fieldMappings[$field]['quoted'])
2296            ? $platform->quoteIdentifier($this->fieldMappings[$field]['columnName'])
2297            : $this->fieldMappings[$field]['columnName'];
2298    }
2299
2300    /**
2301     * Gets the (possibly quoted) primary table name of this class for safe use
2302     * in an SQL statement.
2303     *
2304     * @param AbstractPlatform $platform
2305     * @return string
2306     */
2307    public function getQuotedTableName($platform)
2308    {
2309        return isset($this->table['quoted']) ? $platform->quoteIdentifier($this->table['name']) : $this->table['name'];
2310    }
2311
2312    /**
2313     * Gets the (possibly quoted) name of the join table.
2314     *
2315     * @param AbstractPlatform $platform
2316     * @return string
2317     */
2318    public function getQuotedJoinTableName(array $assoc, $platform)
2319    {
2320        return isset($assoc['joinTable']['quoted']) ? $platform->quoteIdentifier($assoc['joinTable']['name']) : $assoc['joinTable']['name'];
2321    }
2322
2323    /**
2324     * @param string $fieldName
2325     * @return bool
2326     */
2327    public function isAssociationInverseSide($fieldName)
2328    {
2329        return isset($this->associationMappings[$fieldName]) && ! $this->associationMappings[$fieldName]['isOwningSide'];
2330    }
2331
2332    /**
2333     * @param string $fieldName
2334     * @return string
2335     */
2336    public function getAssociationMappedByTargetField($fieldName)
2337    {
2338        return $this->associationMappings[$fieldName]['mappedBy'];
2339    }
2340}
Note: See TracBrowser for help on using the repository browser.