Relation.php 8.34 KB
Newer Older
doctrine's avatar
doctrine committed
1
<?php
lsmith's avatar
lsmith committed
2
/*
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
 *  $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
 * <http://www.phpdoctrine.com>.
 */
doctrine's avatar
doctrine committed
21 22
/**
 * Doctrine_Relation
23
 * This class represents a relation between components
doctrine's avatar
doctrine committed
24
 *
lsmith's avatar
lsmith committed
25 26 27 28 29 30 31 32
 * @package     Doctrine
 * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
 * @category    Object Relational Mapping
 * @link        www.phpdoctrine.com
 * @since       1.0
 * @version     $Revision$
 * @author      Konsta Vesterinen <kvesteri@cc.hut.fi>
 */
lsmith's avatar
lsmith committed
33 34
abstract class Doctrine_Relation
{
doctrine's avatar
doctrine committed
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
    /**
     * RELATION CONSTANTS
     */

    /**
     * constant for ONE_TO_ONE and MANY_TO_ONE aggregate relationships
     */
    const ONE_AGGREGATE         = 0;
    /**
     * constant for ONE_TO_ONE and MANY_TO_ONE composite relationships
     */
    const ONE_COMPOSITE         = 1;
    /**
     * constant for MANY_TO_MANY and ONE_TO_MANY aggregate relationships
     */
    const MANY_AGGREGATE        = 2;
    /**
     * constant for MANY_TO_MANY and ONE_TO_MANY composite relationships
     */
    const MANY_COMPOSITE        = 3;

    /**
doctrine's avatar
doctrine committed
57
     * @var Doctrine_Table $table   foreign factory
doctrine's avatar
doctrine committed
58
     */
pookey's avatar
pookey committed
59
    protected $table;
doctrine's avatar
doctrine committed
60 61 62
    /**
     * @var string $local           local field
     */
pookey's avatar
pookey committed
63
    protected $local;
doctrine's avatar
doctrine committed
64 65 66
    /**
     * @var string $foreign         foreign field
     */
pookey's avatar
pookey committed
67
    protected $foreign;
doctrine's avatar
doctrine committed
68 69 70
    /**
     * @var integer $type           bind type
     */
pookey's avatar
pookey committed
71
    protected $type;
72 73 74
    /**
     * @var string $alias           relation alias
     */
pookey's avatar
pookey committed
75
    protected $alias;
doctrine's avatar
doctrine committed
76

doctrine's avatar
doctrine committed
77 78 79 80 81
    /**
     * @param Doctrine_Table $table
     * @param string $local
     * @param string $foreign
     * @param integer $type
doctrine's avatar
doctrine committed
82
     * @param string $alias
doctrine's avatar
doctrine committed
83
     */
lsmith's avatar
lsmith committed
84 85
    public function __construct(Doctrine_Table $table, $local, $foreign, $type, $alias)
    {
doctrine's avatar
doctrine committed
86 87 88 89
        $this->table    = $table;
        $this->local    = $local;
        $this->foreign  = $foreign;
        $this->type     = $type;
doctrine's avatar
doctrine committed
90
        $this->alias    = $alias;
doctrine's avatar
doctrine committed
91
    }
92
    /**
zYne's avatar
zYne committed
93 94 95 96
     * getAlias
     * returns the relation alias
     *
     * @return string
97
     */
lsmith's avatar
lsmith committed
98 99
    final public function getAlias()
    {
100 101
        return $this->alias;
    }
doctrine's avatar
doctrine committed
102
    /**
zYne's avatar
zYne committed
103 104 105 106 107
     * getType
     * returns the relation type, either 0 or 1
     *
     * @see Doctrine_Relation MANY_* and ONE_* constants
     * @return integer
doctrine's avatar
doctrine committed
108
     */
lsmith's avatar
lsmith committed
109 110
    final public function getType()
    {
doctrine's avatar
doctrine committed
111 112 113
        return $this->type;
    }
    /**
zYne's avatar
zYne committed
114 115 116 117
     * getTable
     * returns the foreign table object
     *
     * @return object Doctrine_Table
doctrine's avatar
doctrine committed
118
     */
lsmith's avatar
lsmith committed
119 120
    final public function getTable()
    {
doctrine's avatar
doctrine committed
121 122 123
        return $this->table;
    }
    /**
zYne's avatar
zYne committed
124 125 126 127
     * getLocal
     * returns the name of the local column
     *
     * @return string
doctrine's avatar
doctrine committed
128
     */
lsmith's avatar
lsmith committed
129 130
    final public function getLocal()
    {
doctrine's avatar
doctrine committed
131 132 133
        return $this->local;
    }
    /**
zYne's avatar
zYne committed
134 135 136 137 138
     * getForeign
     * returns the name of the foreignkey column where
     * the localkey column is pointing at
     *
     * @return string
doctrine's avatar
doctrine committed
139
     */
lsmith's avatar
lsmith committed
140 141
    final public function getForeign()
    {
doctrine's avatar
doctrine committed
142 143
        return $this->foreign;
    }
144 145 146 147 148 149
    /**
     * isComposite
     * returns whether or not this relation is a composite relation
     *
     * @return boolean
     */
lsmith's avatar
lsmith committed
150 151
    final public function isComposite()
    {
152 153 154
        return ($this->type == Doctrine_Relation::ONE_COMPOSITE ||
                $this->type == Doctrine_Relation::MANY_COMPOSITE);
    }
155 156 157 158 159 160
    /**
     * isOneToOne
     * returns whether or not this relation is a one-to-one relation
     *
     * @return boolean
     */
lsmith's avatar
lsmith committed
161 162
    final public function isOneToOne()
    {
163 164 165
        return ($this->type == Doctrine_Relation::ONE_AGGREGATE ||
                $this->type == Doctrine_Relation::ONE_COMPOSITE);
    }
lsmith's avatar
lsmith committed
166
    /**
pookey's avatar
pookey committed
167 168 169 170 171
     * getRelationDql
     *
     * @param integer $count
     * @return string
     */
lsmith's avatar
lsmith committed
172 173
    public function getRelationDql($count)
    {
zYne's avatar
zYne committed
174 175 176
        $dql  = 'FROM ' . $this->table->getComponentName()
              . ' WHERE ' . $this->table->getComponentName() . '.' . $this->foreign
              . ' IN (' . substr(str_repeat('?, ', $count), 0, -2) . ')';
lsmith's avatar
lsmith committed
177

pookey's avatar
pookey committed
178 179
        return $dql;
    }
180 181 182 183 184 185 186 187 188 189 190 191
    /**
     * getDeleteOperations
     *
     * get the records that need to be deleted in order to change the old collection
     * to the new one
     *
     * The algorithm here is very simple and definitely not
     * the fastest one, since we have to iterate through the collections twice.
     * the complexity of this algorithm is O(n^2)
     *
     * We iterate through the old collection and get the records
     * that do not exists in the new collection (Doctrine_Records that need to be deleted).
zYne's avatar
zYne committed
192 193 194 195
     *
     * @param Doctrine_Collection $old
     * @param Doctrine_Collection $new
     * @return array
196
     */
lsmith's avatar
lsmith committed
197 198
    public static function getDeleteOperations(Doctrine_Collection $old, Doctrine_Collection $new)
    {
199 200
        $r = array();

lsmith's avatar
lsmith committed
201
        foreach ($old as $k => $record) {
202 203
            $id = $record->getIncremented();

lsmith's avatar
lsmith committed
204
            if (empty($id)) {
205
                continue;
lsmith's avatar
lsmith committed
206
            }
207
            $found = false;
lsmith's avatar
lsmith committed
208 209
            foreach ($new as $k2 => $record2) {
                if ($record2->getIncremented() === $record->getIncremented()) {
210 211 212 213 214
                    $found = true;
                    break;
                }
            }

lsmith's avatar
lsmith committed
215
            if ( ! $found)  {
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
                $r[] = $record;
                unset($old[$k]);
            }
        }

        return $r;
    }
    /**
     * getInsertOperations
     *
     * get the records that need to be added in order to change the old collection
     * to the new one
     *
     * The algorithm here is very simple and definitely not
     * the fastest one, since we have to iterate through the collections twice.
     * the complexity of this algorithm is O(n^2)
     *
     * We iterate through the old collection and get the records
     * that exists only in the new collection (Doctrine_Records that need to be added).
zYne's avatar
zYne committed
235 236 237 238
     *
     * @param Doctrine_Collection $old
     * @param Doctrine_Collection $new
     * @return array
239
     */
lsmith's avatar
lsmith committed
240 241
    public static function getInsertOperations(Doctrine_Collection $old, Doctrine_Collection $new)
    {
242 243
        $r = array();

lsmith's avatar
lsmith committed
244
        foreach ($new as $k => $record) {
245 246 247
            $found = false;

            $id = $record->getIncremented();
lsmith's avatar
lsmith committed
248 249 250
            if ( ! empty($id)) {
                foreach ($old as $k2 => $record2) {
                    if ($record2->getIncremented() === $record->getIncremented()) {
251 252 253 254 255
                        $found = true;
                        break;
                    }
                }
            }
lsmith's avatar
lsmith committed
256
            if ( ! $found) {
257 258 259 260 261 262 263
                $old[] = $record;
                $r[] = $record;
            }
        }

        return $r;
    }
zYne's avatar
zYne committed
264 265 266 267 268 269 270 271 272
    /**
     * fetchRelatedFor
     *
     * fetches a component related to given record
     *
     * @param Doctrine_Record $record
     * @return Doctrine_Record|Doctrine_Collection
     */
    abstract public function fetchRelatedFor(Doctrine_Record $record);
doctrine's avatar
doctrine committed
273 274
    /**
     * __toString
275 276
     *
     * @return string
doctrine's avatar
doctrine committed
277
     */
lsmith's avatar
lsmith committed
278 279
    public function __toString()
    {
doctrine's avatar
doctrine committed
280 281 282 283 284 285 286 287 288 289 290
        $r[] = "<pre>";
        $r[] = "Class       : ".get_class($this);
        $r[] = "Component   : ".$this->table->getComponentName();
        $r[] = "Table       : ".$this->table->getTableName();
        $r[] = "Local key   : ".$this->local;
        $r[] = "Foreign key : ".$this->foreign;
        $r[] = "Type        : ".$this->type;
        $r[] = "</pre>";
        return implode("\n", $r);
    }
}