AssociationMapping.php 9.85 KB
Newer Older
romanb's avatar
romanb committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
<?php
/*
 * 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
17
 * <http://www.doctrine-project.org>.
romanb's avatar
romanb committed
18 19
 */

20
namespace Doctrine\ORM\Mapping;
romanb's avatar
romanb committed
21 22 23 24

/**
 * Base class for association mappings.
 *
25 26 27 28 29 30 31 32
 * <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
33 34 35
 * @author Roman Borschel <roman@code-factory.org>
 * @since 2.0
 */
36
abstract class AssociationMapping
romanb's avatar
romanb committed
37
{
romanb's avatar
romanb committed
38 39 40 41 42
    /**
     * Specifies that an association is to be fetched when it is first accessed.
     * 
     * @var integer
     */
romanb's avatar
romanb committed
43
    const FETCH_LAZY = 2;
romanb's avatar
romanb committed
44 45 46 47 48 49
    /**
     * Specifies that an association is to be fetched when the owner of the
     * association is fetched. 
     *
     * @var integer
     */
romanb's avatar
romanb committed
50
    const FETCH_EAGER = 3;
51 52 53 54 55 56 57

    /**
     * READ-ONLY: Whether the association cascades delete() operations from the source entity
     * to the target entity/entities.
     *
     * @var boolean
     */
58
    public $isCascadeRemove;
59 60 61 62 63 64 65

    /**
     * READ-ONLY: Whether the association cascades save() operations from the source entity
     * to the target entity/entities.
     *
     * @var boolean
     */
66
    public $isCascadePersist;
67 68 69 70 71 72 73

    /**
     * READ-ONLY: Whether the association cascades refresh() operations from the source entity
     * to the target entity/entities.
     *
     * @var boolean
     */
74
    public $isCascadeRefresh;
75 76 77 78 79 80 81

    /**
     * READ-ONLY: Whether the association cascades merge() operations from the source entity
     * to the target entity/entities.
     *
     * @var boolean
     */
82
    public $isCascadeMerge;
83 84 85 86 87 88 89

    /**
     * READ-ONLY: Whether the association cascades detach() operations from the source entity
     * to the target entity/entities.
     *
     * @var boolean
     */
90
    public $isCascadeDetach;
91

romanb's avatar
romanb committed
92
    /**
93
     * READ-ONLY: The fetch mode used for the association.
romanb's avatar
romanb committed
94 95 96
     *
     * @var integer
     */
97
    public $fetchMode;
98

romanb's avatar
romanb committed
99
    /**
100
     * READ-ONLY: Flag that indicates whether the class that defines this mapping is
romanb's avatar
romanb committed
101 102 103 104
     * the owning side of the association.
     *
     * @var boolean
     */
105
    public $isOwningSide = true;
106

romanb's avatar
romanb committed
107
    /**
108
     * READ-ONLY: The name of the source Entity (the Entity that defines this mapping).
romanb's avatar
romanb committed
109 110 111
     *
     * @var string
     */
112
    public $sourceEntityName;
113

romanb's avatar
romanb committed
114
    /**
115
     * READ-ONLY: The name of the target Entity (the Enitity that is the target of the
romanb's avatar
romanb committed
116 117 118 119
     * association).
     *
     * @var string
     */
120
    public $targetEntityName;
121

romanb's avatar
romanb committed
122
    /**
123
     * READ-ONLY: Identifies the field on the source class (the class this AssociationMapping
romanb's avatar
romanb committed
124 125 126 127 128
     * belongs to) that represents the association and stores the reference to the
     * other entity/entities.
     *
     * @var string
     */
129
    public $sourceFieldName;
130

romanb's avatar
romanb committed
131
    /**
132 133 134
     * READ-ONLY: Identifies the field on the owning side of a bidirectional association that
     * controls the mapping for the association. This is only set on the inverse side
     * of an association.
romanb's avatar
romanb committed
135 136 137
     *
     * @var string
     */
138 139
    public $mappedBy;

romanb's avatar
romanb committed
140
    /**
141 142 143 144 145 146 147 148 149
     * READ-ONLY: Identifies the field on the inverse side of a bidirectional association.
     * This is only set on the owning side of an association.
     *
     * @var string
     */
    public $inversedBy;

    /**
     * READ-ONLY: The join table definition, if any.
romanb's avatar
romanb committed
150
     *
151
     * @var array
romanb's avatar
romanb committed
152
     */
Roman S. Borschel's avatar
Roman S. Borschel committed
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
    public $joinTable;

    /**
     * READ-ONLY: The name of the entity class from which the association was
     * inherited in an inheritance hierarchy.
     *
     * @var string
     */
    public $inherited;

    /**
     * READ-ONLY: The name of the entity or mapped superclass that declares
     * the association field in an inheritance hierarchy.
     *
     * @var string
     */
    public $declared;
170

romanb's avatar
romanb committed
171
    /**
172
     * Initializes a new instance of a class derived from AssociationMapping.
romanb's avatar
romanb committed
173 174 175 176 177
     *
     * @param array $mapping  The mapping definition.
     */
    public function __construct(array $mapping)
    {
Roman S. Borschel's avatar
Roman S. Borschel committed
178
        $this->_validateAndCompleteMapping($mapping);
romanb's avatar
romanb committed
179
    }
180

romanb's avatar
romanb committed
181 182 183 184
    /**
     * Validates & completes the mapping. Mapping defaults are applied here.
     *
     * @param array $mapping
185
     * @throws MappingException If something is wrong with the mapping.
romanb's avatar
romanb committed
186 187 188 189 190
     */
    protected function _validateAndCompleteMapping(array $mapping)
    {        
        // Mandatory attributes for both sides
        if ( ! isset($mapping['fieldName'])) {
191
            throw MappingException::missingFieldName();
romanb's avatar
romanb committed
192
        }
193
        $this->sourceFieldName = $mapping['fieldName'];
romanb's avatar
romanb committed
194 195
        
        if ( ! isset($mapping['sourceEntity'])) {
196
            throw MappingException::missingSourceEntity($mapping['fieldName']);
romanb's avatar
romanb committed
197
        }
198
        $this->sourceEntityName = $mapping['sourceEntity'];
romanb's avatar
romanb committed
199 200
        
        if ( ! isset($mapping['targetEntity'])) {
201
            throw MappingException::missingTargetEntity($mapping['fieldName']);
romanb's avatar
romanb committed
202
        }
203
        $this->targetEntityName = $mapping['targetEntity'];
romanb's avatar
romanb committed
204 205 206 207
        
        // Mandatory and optional attributes for either side
        if ( ! isset($mapping['mappedBy'])) {            
            // Optional
208
            if (isset($mapping['joinTable']) && $mapping['joinTable']) {
209 210 211 212
                if ($mapping['joinTable']['name'][0] == '`') {
                    $mapping['joinTable']['name'] = trim($mapping['joinTable']['name'], '`');
                    $mapping['joinTable']['quoted'] = true;
                }
213
                $this->joinTable = $mapping['joinTable'];   
romanb's avatar
romanb committed
214
            }
215 216 217
            if (isset($mapping['inversedBy'])) {
                $this->inversedBy = $mapping['inversedBy'];
            }
romanb's avatar
romanb committed
218
        } else {
219
            $this->isOwningSide = false;
220
            $this->mappedBy = $mapping['mappedBy'];
romanb's avatar
romanb committed
221 222 223
        }
        
        // Optional attributes for both sides
224
        $this->fetchMode = isset($mapping['fetch']) ? $mapping['fetch'] : self::FETCH_LAZY;
225
        $cascades = isset($mapping['cascade']) ? $mapping['cascade'] : array();
226 227 228 229 230 231 232 233 234 235 236
        
        if (in_array('all', $cascades)) {
            $cascades = array(
               'remove',
               'persist',
               'refresh',
               'merge',
               'detach'
            );
        }
        
237
        $this->isCascadeRemove = in_array('remove',  $cascades);
238 239
        $this->isCascadePersist = in_array('persist', $cascades);
        $this->isCascadeRefresh = in_array('refresh', $cascades);
240 241
        $this->isCascadeMerge = in_array('merge',   $cascades);
        $this->isCascadeDetach = in_array('detach',  $cascades);
romanb's avatar
romanb committed
242
    }
243

romanb's avatar
romanb committed
244 245 246 247 248 249 250
    /**
     * Whether the target entity/entities of the association are eagerly fetched.
     *
     * @return boolean
     */
    public function isEagerlyFetched()
    {
251
        return $this->fetchMode == self::FETCH_EAGER;
romanb's avatar
romanb committed
252
    }
253

romanb's avatar
romanb committed
254 255 256 257 258 259 260
    /**
     * Whether the target entity/entities of the association are lazily fetched.
     *
     * @return boolean
     */
    public function isLazilyFetched()
    {
261
        return $this->fetchMode == self::FETCH_LAZY;
romanb's avatar
romanb committed
262
    }
263

264 265 266 267 268
    /**
     * Whether the association is a one-to-one association.
     *
     * @return boolean
     */
romanb's avatar
romanb committed
269 270 271 272
    public function isOneToOne()
    {
        return false;
    }
273 274 275 276 277 278

    /**
     * Whether the association is a one-to-many association.
     *
     * @return boolean
     */
romanb's avatar
romanb committed
279 280 281 282
    public function isOneToMany()
    {
        return false;
    }
283 284 285 286 287 288

    /**
     * Whether the association is a many-to-many association.
     *
     * @return boolean
     */
romanb's avatar
romanb committed
289 290 291 292
    public function isManyToMany()
    {
        return false;
    }
293

294 295 296 297 298
    /**
     * Whether the association uses a join table for the mapping.
     *
     * @return boolean
     */
299 300
    public function usesJoinTable()
    {
301
        return (bool) $this->joinTable;
302
    }
303 304 305 306 307 308

    /**
     * Checks whether the association has any cascades configured.
     * 
     * @return boolean
     */
309 310 311
    public function hasCascades()
    {
        return $this->isCascadePersist ||
312 313 314 315
               $this->isCascadeRemove ||
               $this->isCascadeRefresh ||
               $this->isCascadeMerge ||
               $this->isCascadeDetach;
316
    }
317

318
    /**
319
     * Loads data in $target domain object using this association.
320
     * The data comes from the association navigated from $sourceEntity
321
     * using $em.
322
     *
323
     * @param object $sourceEntity
324
     * @param object $target            an entity or a collection
325
     * @param EntityManager $em
326 327
     * @param array $joinColumnValues   foreign keys (significative for this
     *                                  association) of $sourceEntity, if needed
328
     */
329
    abstract public function load($sourceEntity, $target, $em, array $joinColumnValues = array());
330
    
331
    /**
Roman S. Borschel's avatar
Roman S. Borschel committed
332 333 334
     * Gets the (possibly quoted) name of the join table.
     *
     * @param AbstractPlatform $platform
335
     * @return string
336
     */
337
    public function getQuotedJoinTableName($platform)
338
    {
339 340 341
        return isset($this->joinTable['quoted'])
            ? $platform->quoteIdentifier($this->joinTable['name'])
            : $this->joinTable['name'];
342
    }
romanb's avatar
romanb committed
343
    
344
}