root/trunk/lib/Doctrine/ORM/Mapping/ClassMetadata.php @ 7100

Revision 7100, 12.4 KB (checked in by beberlei, 6 months ago)

[2.0] DDC-268 - Exchanged DoctrineException? for MappingException? and added missing exception method (thanks to Christian Heinrich for the patch)

  • Property svn:eol-style set to LF
RevLine 
[4952]1<?php
2/*
3 *  $Id$
4 *
5 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
6 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
7 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
8 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
9 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
10 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
11 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
12 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
13 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
14 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
15 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
16 *
17 * This software consists of voluntary contributions made by many individuals
18 * and is licensed under the LGPL. For more information, see
[5476]19 * <http://www.doctrine-project.org>.
[4952]20 */
21
[5396]22namespace Doctrine\ORM\Mapping;
[4952]23
24/**
[6186]25 * A <tt>ClassMetadata</tt> instance holds all the object-relational mapping metadata
26 * of an entity and it's associations.
[6166]27 *
28 * Once populated, ClassMetadata instances are usually cached in a serialized form.
[4952]29 *
[5720]30 * <b>IMPORTANT NOTE:</b>
31 *
32 * The fields of this class are only public for 2 reasons:
33 * 1) To allow fast READ access.
34 * 2) To drastically reduce the size of a serialized instance (private/protected members
35 *    get the whole class name, namespace inclusive, prepended to every property in
36 *    the serialized representation).
37 *
[4952]38 * @author Roman Borschel <roman@code-factory.org>
[6430]39 * @author Jonathan H. Wage <jonwage@gmail.com>
[4952]40 * @since 2.0
41 */
[6971]42class ClassMetadata extends ClassMetadataInfo
[4952]43{
44    /**
[5334]45     * The ReflectionClass instance of the mapped class.
46     *
47     * @var ReflectionClass
48     */
[5720]49    public $reflClass;
[5334]50
51    /**
52     * The ReflectionProperty instances of the mapped class.
53     *
54     * @var array
55     */
[6411]56    public $reflFields = array();
[5313]57
[4952]58    /**
[5468]59     * Initializes a new ClassMetadata instance that will hold the object-relational mapping
60     * metadata of the class with the given name.
[4952]61     *
[6112]62     * @param string $entityName The name of the entity class the new instance is used for.
[4952]63     */
[5313]64    public function __construct($entityName)
[4952]65    {
[5720]66        $this->name = $entityName;
[6174]67        $this->reflClass = new \ReflectionClass($entityName);
68        $this->namespace = $this->reflClass->getNamespaceName();
69        $this->primaryTable['name'] = $this->reflClass->getShortName();
[5720]70        $this->rootEntityName = $entityName;
[6644]71       
72        //$this->prototype = unserialize(sprintf('O:%d:"%s":0:{}', strlen($this->name), $this->name));
[4952]73    }
[5313]74
[5329]75    /**
76     * Gets the ReflectionClass instance of the mapped class.
77     *
78     * @return ReflectionClass
79     */
[5313]80    public function getReflectionClass()
[4952]81    {
[5720]82        return $this->reflClass;
[4952]83    }
84
[5329]85    /**
86     * Gets the ReflectionPropertys of the mapped class.
87     *
88     * @return array An array of ReflectionProperty instances.
89     */
[5313]90    public function getReflectionProperties()
91    {
[5720]92        return $this->reflFields;
[5313]93    }
94
[5329]95    /**
[5636]96     * INTERNAL:
97     * Adds a reflection property. Usually only used by the ClassMetadataFactory
98     * while processing inheritance mappings.
99     *
100     * @param array $props
101     */
102    public function addReflectionProperty($propName, \ReflectionProperty $property)
103    {
[5720]104        $this->reflFields[$propName] = $property;
[5636]105    }
106
107    /**
[5329]108     * Gets a ReflectionProperty for a specific field of the mapped class.
109     *
110     * @param string $name
111     * @return ReflectionProperty
112     */
[5313]113    public function getReflectionProperty($name)
114    {
[5720]115        return $this->reflFields[$name];
[5313]116    }
117
[5332]118    /**
[5476]119     * Gets the ReflectionProperty for the single identifier field.
[5332]120     *
[5476]121     * @return ReflectionProperty
[6791]122     * @throws BadMethodCallException If the class has a composite identifier.
[5332]123     */
[5329]124    public function getSingleIdReflectionProperty()
125    {
[5720]126        if ($this->isIdentifierComposite) {
[6791]127            throw new \BadMethodCallException("Class " . $this->name . " has a composite identifier.");
[5329]128        }
[5720]129        return $this->reflFields[$this->identifier[0]];
[5329]130    }
[4952]131   
132    /**
[5476]133     * Validates & completes the given field mapping.
[4952]134     *
135     * @param array $mapping  The field mapping to validated & complete.
136     * @return array  The validated and completed field mapping.
[7041]137     *
138     * @throws MappingException
[4952]139     */
[6427]140    protected function _validateAndCompleteFieldMapping(array &$mapping)
[4952]141    {
[6427]142        parent::_validateAndCompleteFieldMapping($mapping);
[5350]143
[5359]144        // Store ReflectionProperty of mapped field
[7043]145        $refProp = $this->reflClass->getProperty($mapping['fieldName']);
146        $refProp->setAccessible(true);
147        $this->reflFields[$mapping['fieldName']] = $refProp;
[5332]148    }
[5313]149
[4952]150    /**
[5706]151     * Extracts the identifier values of an entity of this class.
[5880]152     *
153     * For composite identifiers, the identifier values are returned as an array
154     * with the same order as the field order in {@link identifier}.
[5706]155     *
156     * @param object $entity
157     * @return mixed
158     */
159    public function getIdentifierValues($entity)
160    {
[5720]161        if ($this->isIdentifierComposite) {
[5706]162            $id = array();
[5720]163            foreach ($this->identifier as $idField) {
164                $value = $this->reflFields[$idField]->getValue($entity);
[5706]165                if ($value !== null) {
[6791]166                    $id[$idField] = $value;
[5706]167                }
168            }
169            return $id;
170        } else {
[6791]171            return array($this->identifier[0] => $this->reflFields[$this->identifier[0]]->getValue($entity));
[5706]172        }
173    }
[6575]174   
175    public function getColumnValues($entity, array $columns)
176    {
177        $values = array();
178        foreach ($columns as $column) {
179            $values[] = $this->reflFields[$this->fieldNames[$column]]->getValue($entity);
180        }
181        return $values;
182    }
[5706]183
184    /**
185     * Populates the entity identifier of an entity.
186     *
187     * @param object $entity
188     * @param mixed $id
[5716]189     * @todo Rename to assignIdentifier()
[5706]190     */
191    public function setIdentifierValues($entity, $id)
192    {
[5720]193        if ($this->isIdentifierComposite) {
[5706]194            foreach ((array)$id as $idField => $idValue) {
[5720]195                $this->reflFields[$idField]->setValue($entity, $idValue);
[5706]196            }
197        } else {
[5720]198            $this->reflFields[$this->identifier[0]]->setValue($entity, $id);
[5706]199        }
200    }
[5716]201
202    /**
203     * Sets the specified field to the specified value on the given entity.
204     *
205     * @param object $entity
206     * @param string $field
207     * @param mixed $value
208     */
209    public function setFieldValue($entity, $field, $value)
210    {
[5720]211        $this->reflFields[$field]->setValue($entity, $value);
[5716]212    }
213
214    /**
215     * Sets the field mapped to the specified column to the specified value on the given entity.
216     *
217     * @param object $entity
218     * @param string $field
219     * @param mixed $value
220     */
221    public function setColumnValue($entity, $column, $value)
222    {
[5720]223        $this->reflFields[$this->fieldNames[$column]]->setValue($entity, $value);
[5716]224    }
[4952]225
226    /**
227     * Stores the association mapping.
228     *
[5468]229     * @param AssociationMapping $assocMapping
[4952]230     */
[6427]231    protected function _storeAssociationMapping(AssociationMapping $assocMapping)
[4952]232    {
[6427]233        parent::_storeAssociationMapping($assocMapping);
[5359]234
235        // Store ReflectionProperty of mapped field
[6427]236        $sourceFieldName = $assocMapping->sourceFieldName;
[7041]237       
[7043]238            $refProp = $this->reflClass->getProperty($sourceFieldName);
239            $refProp->setAccessible(true);
240            $this->reflFields[$sourceFieldName] = $refProp;
[4952]241    }
242   
243    /**
[5468]244     * Dispatches the lifecycle event of the given entity to the registered
[4952]245     * lifecycle callbacks and lifecycle listeners.
246     *
247     * @param string $event  The lifecycle event.
248     * @param Entity $entity  The Entity on which the event occured.
249     */
[5396]250    public function invokeLifecycleCallbacks($lifecycleEvent, $entity)
[4952]251    {
[6112]252        foreach ($this->lifecycleCallbacks[$lifecycleEvent] as $callback) {
[4952]253            $entity->$callback();
254        }
255    }
256   
257    /**
[6222]258     * Gets the (possibly quoted) column name of a mapped field for safe use
259     * in an SQL statement.
260     *
261     * @param string $field
262     * @param AbstractPlatform $platform
263     * @return string
264     */
265    public function getQuotedColumnName($field, $platform)
266    {
267        return isset($this->fieldMappings[$field]['quoted']) ?
268                $platform->quoteIdentifier($this->fieldMappings[$field]['columnName']) :
269                $this->fieldMappings[$field]['columnName'];
270    }
271   
272    /**
273     * Gets the (possibly quoted) primary table name of this class for safe use
274     * in an SQL statement.
275     *
276     * @param AbstractPlatform $platform
277     * @return string
278     */
279    public function getQuotedTableName($platform)
280    {
281        return isset($this->primaryTable['quoted']) ?
282                $platform->quoteIdentifier($this->primaryTable['name']) :
283                $this->primaryTable['name'];
284    }
285   
286    /**
287     * Gets the (possibly quoted) name of the discriminator column for safe use
288     * in an SQL statement.
289     *
290     * @param AbstractPlatform $platform
291     * @return string
292     */
293    public function getQuotedDiscriminatorColumnName($platform)
294    {
295        return isset($this->discriminatorColumn['quoted']) ?
296                $platform->quoteIdentifier($this->discriminatorColumn['name']) :
297                $this->discriminatorColumn['name'];
298    }
299   
300    /**
[5627]301     * Creates a string representation of this instance.
302     *
303     * @return string The string representation of this instance.
[5838]304     * @todo Construct meaningful string representation.
[5627]305     */
[4952]306    public function __toString()
307    {
[5329]308        return __CLASS__ . '@' . spl_object_hash($this);
[4952]309    }
[6222]310   
311    /**
312     * Determines which fields get serialized.
313     *
314     * Parts that are NOT serialized because they can not be properly unserialized:
315     *      - reflClass (ReflectionClass)
316     *      - reflFields (ReflectionProperty array)
317     *
318     * @return array The names of all the fields that should be serialized.
319     */
320    public function __sleep()
321    {
322        return array(
[6971]323            'associationMappings', // unserialization bottleneck with many assocs
[6222]324            'changeTrackingPolicy',
[6971]325            'columnNames', //TODO: Not really needed. Can use fieldMappings[$fieldName]['columnName']
[6222]326            'customRepositoryClassName',
327            'discriminatorColumn',
328            'discriminatorValue',
[6422]329            'discriminatorMap',
[6222]330            'fieldMappings',
[6971]331            'fieldNames', //TODO: Not all of this stuff needs to be serialized. Only type, columnName and fieldName.
[6222]332            'generatorType',
333            'identifier',
[6971]334            'idGenerator', //TODO: Does not really need to be serialized. Could be moved to runtime.
[6222]335            'inheritanceType',
336            'inheritedAssociationFields',
[6908]337            'inverseMappings', //TODO: Remove!
[6222]338            'isIdentifierComposite',
339            'isMappedSuperclass',
340            'isVersioned',
341            'lifecycleCallbacks',
342            'name',
343            'parentClasses',
344            'primaryTable',
345            'rootEntityName',
346            'subClasses',
347            'versionField'
348        );
349    }
350   
351    /**
[6565]352     * Restores some state that can not be serialized/unserialized.
[6222]353     *
354     * @return void
355     */
356    public function __wakeup()
357    {
358        // Restore ReflectionClass and properties
359        $this->reflClass = new \ReflectionClass($this->name);
[7041]360       
[7030]361        foreach ($this->fieldMappings as $field => $mapping) {
[7043]362                if (isset($mapping['inherited'])) {
363                    $reflField = new \ReflectionProperty($mapping['inherited'], $field);
364                } else {
365                    $reflField = $this->reflClass->getProperty($field);
366                }
[7041]367                   
[7043]368            $reflField->setAccessible(true);
369            $this->reflFields[$field] = $reflField;
[6222]370        }
[7041]371       
[6222]372        foreach ($this->associationMappings as $field => $mapping) {
[7043]373            if (isset($this->inheritedAssociationFields[$field])) {
374                $reflField = new \ReflectionProperty($this->inheritedAssociationFields[$field], $field);
375            } else {
376                $reflField = $this->reflClass->getProperty($field);
377            }
[7041]378               
[7043]379            $reflField->setAccessible(true);
380            $this->reflFields[$field] = $reflField;
[6222]381        }
[6644]382       
383        //$this->prototype = unserialize(sprintf('O:%d:"%s":0:{}', strlen($this->name), $this->name));
[6222]384    }
[6644]385   
386    //public $prototype;
[6120]387}
Note: See TracBrowser for help on using the browser.