AssociationMapping.php 10.9 KB
Newer Older
romanb's avatar
romanb committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
<?php
/*
 *  $Id$
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information, see
19
 * <http://www.doctrine-project.org>.
romanb's avatar
romanb committed
20 21
 */

22
namespace Doctrine\ORM\Mapping;
romanb's avatar
romanb committed
23 24 25 26

/**
 * Base class for association mappings.
 *
27 28 29 30 31 32 33 34
 * <b>IMPORTANT NOTE:</b>
 *
 * The fields of this class are only public for 2 reasons:
 * 1) To allow fast, internal READ access.
 * 2) To drastically reduce the size of a serialized instance (private/protected members
 *    get the whole class name, namespace inclusive, prepended to every property in
 *    the serialized representation).
 *
romanb's avatar
romanb committed
35 36 37
 * @author Roman Borschel <roman@code-factory.org>
 * @since 2.0
 */
38
abstract class AssociationMapping
romanb's avatar
romanb committed
39
{
romanb's avatar
romanb committed
40 41 42 43 44
    /**
     * Specifies that an association is to be fetched when it is first accessed.
     * 
     * @var integer
     */
romanb's avatar
romanb committed
45
    const FETCH_LAZY = 2;
romanb's avatar
romanb committed
46 47 48 49 50 51
    /**
     * Specifies that an association is to be fetched when the owner of the
     * association is fetched. 
     *
     * @var integer
     */
romanb's avatar
romanb committed
52 53
    const FETCH_EAGER = 3;
    
54 55
    public $isCascadeRemove;
    public $isCascadePersist;
56 57
    public $isCascadeRefresh;
    public $isCascadeMerge;
58
    public $isCascadeDetach;
romanb's avatar
romanb committed
59 60 61 62 63 64
    
    /**
     * The fetch mode used for the association.
     *
     * @var integer
     */
65
    public $fetchMode;
romanb's avatar
romanb committed
66 67 68 69 70 71 72
    
    /**
     * Flag that indicates whether the class that defines this mapping is
     * the owning side of the association.
     *
     * @var boolean
     */
73
    public $isOwningSide = true;
romanb's avatar
romanb committed
74 75 76 77 78 79
    
    /**
     * The name of the source Entity (the Entity that defines this mapping).
     *
     * @var string
     */
80
    public $sourceEntityName;
romanb's avatar
romanb committed
81 82 83 84 85 86 87
    
    /**
     * The name of the target Entity (the Enitity that is the target of the
     * association).
     *
     * @var string
     */
88
    public $targetEntityName;
romanb's avatar
romanb committed
89 90 91 92 93 94 95 96
    
    /**
     * Identifies the field on the source class (the class this AssociationMapping
     * belongs to) that represents the association and stores the reference to the
     * other entity/entities.
     *
     * @var string
     */
97
    public $sourceFieldName;
romanb's avatar
romanb committed
98 99 100 101 102 103 104
    
    /**
     * Identifies the field on the owning side that controls the mapping for the
     * association. This is only set on the inverse side of an association.
     *
     * @var string
     */
105
    public $mappedByFieldName;
romanb's avatar
romanb committed
106 107
    
    /**
108
     * The join table definition, if any.
romanb's avatar
romanb committed
109
     *
110
     * @var array
romanb's avatar
romanb committed
111
     */
112
    public $joinTable = array();
113 114

    //protected $_joinTableInsertSql;
romanb's avatar
romanb committed
115 116
    
    /**
117
     * Initializes a new instance of a class derived from AssociationMapping.
romanb's avatar
romanb committed
118 119 120 121 122
     *
     * @param array $mapping  The mapping definition.
     */
    public function __construct(array $mapping)
    {
123 124 125
        if ($mapping) {
            $this->_validateAndCompleteMapping($mapping);
        }
romanb's avatar
romanb committed
126 127 128 129 130 131 132 133 134 135 136
    }
    
    /**
     * Validates & completes the mapping. Mapping defaults are applied here.
     *
     * @param array $mapping
     */
    protected function _validateAndCompleteMapping(array $mapping)
    {        
        // Mandatory attributes for both sides
        if ( ! isset($mapping['fieldName'])) {
137
            throw MappingException::missingFieldName();
romanb's avatar
romanb committed
138
        }
139
        $this->sourceFieldName = $mapping['fieldName'];
romanb's avatar
romanb committed
140 141
        
        if ( ! isset($mapping['sourceEntity'])) {
142
            throw MappingException::missingSourceEntity($mapping['fieldName']);
romanb's avatar
romanb committed
143
        }
144
        $this->sourceEntityName = $mapping['sourceEntity'];
romanb's avatar
romanb committed
145 146
        
        if ( ! isset($mapping['targetEntity'])) {
147
            throw MappingException::missingTargetEntity($mapping['fieldName']);
romanb's avatar
romanb committed
148
        }
149
        $this->targetEntityName = $mapping['targetEntity'];
romanb's avatar
romanb committed
150 151 152 153 154
        
        // Mandatory and optional attributes for either side
        if ( ! isset($mapping['mappedBy'])) {            
            // Optional
            if (isset($mapping['joinTable'])) {
155 156 157 158
                if ($mapping['joinTable']['name'][0] == '`') {
                    $mapping['joinTable']['name'] = trim($mapping['joinTable']['name'], '`');
                    $mapping['joinTable']['quoted'] = true;
                }
159
                $this->joinTable = $mapping['joinTable'];   
romanb's avatar
romanb committed
160 161
            }
        } else {
162 163
            $this->isOwningSide = false;
            $this->mappedByFieldName = $mapping['mappedBy'];
romanb's avatar
romanb committed
164 165 166
        }
        
        // Optional attributes for both sides
167 168
        $this->fetchMode = isset($mapping['fetch']) ?
                $mapping['fetch'] : self::FETCH_LAZY;
169
        $cascades = isset($mapping['cascade']) ? $mapping['cascade'] : array();
170 171 172 173 174 175 176 177 178 179 180 181
        
        if (in_array('all', $cascades)) {
            $cascades = array(
               'remove',
               'persist',
               'refresh',
               'merge',
               'detach'
            );
        }
        
        $this->isCascadeRemove  = in_array('remove',  $cascades);
182 183
        $this->isCascadePersist = in_array('persist', $cascades);
        $this->isCascadeRefresh = in_array('refresh', $cascades);
184 185
        $this->isCascadeMerge   = in_array('merge',   $cascades);
        $this->isCascadeDetach  = in_array('detach',  $cascades);
romanb's avatar
romanb committed
186 187 188 189 190 191 192 193
    }
    
    /**
     * Whether the association cascades delete() operations from the source entity
     * to the target entity/entities.
     *
     * @return boolean
     */
194
    public function isCascadeRemove()
romanb's avatar
romanb committed
195
    {
196
        return $this->isCascadeRemove;
romanb's avatar
romanb committed
197 198 199 200 201 202 203 204
    }
    
    /**
     * Whether the association cascades save() operations from the source entity
     * to the target entity/entities.
     *
     * @return boolean
     */
205
    public function isCascadePersist()
romanb's avatar
romanb committed
206
    {
207
        return $this->isCascadePersist;
romanb's avatar
romanb committed
208 209 210 211 212 213 214 215 216 217
    }
    
    /**
     * Whether the association cascades refresh() operations from the source entity
     * to the target entity/entities.
     *
     * @return boolean
     */
    public function isCascadeRefresh()
    {
218
        return $this->isCascadeRefresh;
romanb's avatar
romanb committed
219
    }
220 221 222 223 224 225 226 227 228

    /**
     * Whether the association cascades merge() operations from the source entity
     * to the target entity/entities.
     *
     * @return boolean
     */
    public function isCascadeMerge()
    {
229
        return $this->isCascadeMerge;
230
    }
romanb's avatar
romanb committed
231
    
232 233 234 235 236 237 238 239 240 241 242
    /**
     * Whether the association cascades detach() operations from the source entity
     * to the target entity/entities.
     *
     * @return boolean
     */
    public function isCascadeDetach()
    {
        return $this->isCascadeDetach;
    }
    
romanb's avatar
romanb committed
243 244 245 246 247 248 249
    /**
     * Whether the target entity/entities of the association are eagerly fetched.
     *
     * @return boolean
     */
    public function isEagerlyFetched()
    {
250
        return $this->fetchMode == self::FETCH_EAGER;
romanb's avatar
romanb committed
251 252 253 254 255 256 257 258 259
    }
    
    /**
     * Whether the target entity/entities of the association are lazily fetched.
     *
     * @return boolean
     */
    public function isLazilyFetched()
    {
260
        return $this->fetchMode == self::FETCH_LAZY;
romanb's avatar
romanb committed
261 262 263 264 265 266 267 268 269
    }
    
    /**
     * Whether the source entity of this association represents the owning side.
     *
     * @return boolean
     */
    public function isOwningSide()
    {
270
        return $this->isOwningSide;
romanb's avatar
romanb committed
271 272 273 274 275 276 277 278 279
    }
    
    /**
     * Whether the source entity of this association represents the inverse side.
     *
     * @return boolean
     */
    public function isInverseSide()
    {
280
        return ! $this->isOwningSide;
romanb's avatar
romanb committed
281 282 283 284 285 286 287 288 289
    }
    
    /**
     * Gets the name of the source entity class.
     *
     * @return string
     */
    public function getSourceEntityName()
    {
290
        return $this->sourceEntityName;
romanb's avatar
romanb committed
291 292 293 294 295 296 297 298 299
    }
    
    /**
     * Gets the name of the target entity class.
     *
     * @return string
     */
    public function getTargetEntityName()
    {
300
        return $this->targetEntityName;
romanb's avatar
romanb committed
301 302 303
    }
    
    /**
304
     * Gets the join table definition, if any.
romanb's avatar
romanb committed
305
     *
306
     * @return array
romanb's avatar
romanb committed
307 308 309
     */
    public function getJoinTable()
    {
310
        return $this->joinTable;
romanb's avatar
romanb committed
311 312 313 314 315 316 317 318 319
    }
    
    /**
     * Get the name of the field the association is mapped into.
     *
     * @return string
     */
    public function getSourceFieldName()
    {
320
        return $this->sourceFieldName;
romanb's avatar
romanb committed
321 322 323 324
    }
    
    /**
     * Gets the field name of the owning side in a bi-directional association.
325 326
     * This is only set on the inverse side. When invoked on the owning side,
     * NULL is returned.
romanb's avatar
romanb committed
327 328 329 330 331
     *
     * @return string
     */
    public function getMappedByFieldName()
    {
332
        return $this->mappedByFieldName;
romanb's avatar
romanb committed
333
    }
334 335 336 337 338 339

    /**
     * Whether the association is a one-to-one association.
     *
     * @return boolean
     */
romanb's avatar
romanb committed
340 341 342 343
    public function isOneToOne()
    {
        return false;
    }
344 345 346 347 348 349

    /**
     * Whether the association is a one-to-many association.
     *
     * @return boolean
     */
romanb's avatar
romanb committed
350 351 352 353
    public function isOneToMany()
    {
        return false;
    }
354 355 356 357 358 359

    /**
     * Whether the association is a many-to-many association.
     *
     * @return boolean
     */
romanb's avatar
romanb committed
360 361 362 363
    public function isManyToMany()
    {
        return false;
    }
364

365 366 367 368 369
    /**
     * Whether the association uses a join table for the mapping.
     *
     * @return boolean
     */
370 371
    public function usesJoinTable()
    {
372
        return (bool) $this->joinTable;
373
    }
374 375 376 377 378 379 380 381 382
    
    public function hasCascades()
    {
        return $this->isCascadePersist ||
                $this->isCascadeRemove ||
                $this->isCascadeRefresh ||
                $this->isCascadeMerge ||
                $this->isCascadeDetach;
    }
383

384
    /**
385
     * Loads data in $target domain object using this association.
386
     * The data comes from the association navigated from $sourceEntity
387
     * using $em.
388
     *
389
     * @param object $sourceEntity
390
     * @param object $target            an entity or a collection
391
     * @param EntityManager $em
392 393
     * @param array $joinColumnValues   foreign keys (significative for this
     *                                  association) of $sourceEntity, if needed
394
     */
395
    abstract public function load($sourceEntity, $target, $em, array $joinColumnValues = array());
396
    
397
    /**
398 399 400
     * 
     * @param $platform
     * @return unknown_type
401
     */
402
    public function getQuotedJoinTableName($platform)
403
    {
404 405 406
        return isset($this->joinTable['quoted']) ?
                $platform->quoteIdentifier($this->joinTable['name']) :
                $this->joinTable['name'];
407
    }
romanb's avatar
romanb committed
408
    
409
}