Coverage for Doctrine_Relation

Back to coverage report

1 <?php
2 /*
3  *  $Id: Relation.php 3884 2008-02-22 18:26:35Z jwage $
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
19  * <http://www.phpdoctrine.org>.
20  */
21
22 /**
23  * Doctrine_Relation
24  * This class represents a relation between components
25  *
26  * @package     Doctrine
27  * @subpackage  Relation
28  * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
29  * @link        www.phpdoctrine.org
30  * @since       1.0
31  * @version     $Revision: 3884 $
32  * @author      Konsta Vesterinen <kvesteri@cc.hut.fi>
33  */
34 abstract class Doctrine_Relation implements ArrayAccess
35 {
36     /**
37      * RELATION CONSTANTS
38      */
39
40     /**
41      * constant for ONE_TO_ONE and MANY_TO_ONE aggregate relationships
42      */
43     const ONE_AGGREGATE         = 0;
44
45     /**
46      * constant for ONE_TO_ONE and MANY_TO_ONE composite relationships
47      */
48     const ONE_COMPOSITE         = 1;
49
50     /**
51      * constant for MANY_TO_MANY and ONE_TO_MANY aggregate relationships
52      */
53     const MANY_AGGREGATE        = 2;
54
55     /**
56      * constant for MANY_TO_MANY and ONE_TO_MANY composite relationships
57      */
58     const MANY_COMPOSITE        = 3;
59
60     const ONE   = 0;
61     const MANY  = 2;
62     
63     protected $definition = array('alias'       => true,
64                                   'foreign'     => true,
65                                   'local'       => true,
66                                   'class'       => true,
67                                   'type'        => true,
68                                   'table'       => true,
69                                   'localTable'  => true,
70                                   'name'        => false,
71                                   'refTable'    => false,
72                                   'onDelete'    => false,
73                                   'onUpdate'    => false,
74                                   'deferred'    => false,
75                                   'deferrable'  => false,
76                                   'constraint'  => false,
77                                   'equal'       => false,
78                                   );
79
80     /**
81      * constructor
82      *
83      * @param array $definition         an associative array with the following structure:
84      *          name                    foreign key constraint name
85      *
86      *          local                   the local field(s)
87      *
88      *          foreign                 the foreign reference field(s)
89      *
90      *          table                   the foreign table object
91      *
92      *          localTable              the local table object
93      *
94      *          refTable                the reference table object (if any)
95      *
96      *          onDelete                referential delete action
97      *  
98      *          onUpdate                referential update action
99      *
100      *          deferred                deferred constraint checking 
101      *
102      *          alias                   relation alias
103      *
104      *          type                    the relation type, either Doctrine_Relation::ONE or Doctrine_Relation::MANY
105      *
106      *          constraint              boolean value, true if the relation has an explicit referential integrity constraint
107      *
108      * The onDelete and onUpdate keys accept the following values:
109      *
110      * CASCADE: Delete or update the row from the parent table and automatically delete or
111      *          update the matching rows in the child table. Both ON DELETE CASCADE and ON UPDATE CASCADE are supported.
112      *          Between two tables, you should not define several ON UPDATE CASCADE clauses that act on the same column
113      *          in the parent table or in the child table.
114      *
115      * SET NULL: Delete or update the row from the parent table and set the foreign key column or columns in the
116      *          child table to NULL. This is valid only if the foreign key columns do not have the NOT NULL qualifier 
117      *          specified. Both ON DELETE SET NULL and ON UPDATE SET NULL clauses are supported.
118      *
119      * NO ACTION: In standard SQL, NO ACTION means no action in the sense that an attempt to delete or update a primary 
120      *           key value is not allowed to proceed if there is a related foreign key value in the referenced table.
121      *
122      * RESTRICT: Rejects the delete or update operation for the parent table. NO ACTION and RESTRICT are the same as
123      *           omitting the ON DELETE or ON UPDATE clause.
124      *
125      * SET DEFAULT
126      */
127     public function __construct(array $definition)
128     {
129         $def = array();
130         foreach ($this->definition as $key => $val) {
131             if ( ! isset($definition[$key]) && $val) {
132                 throw new Doctrine_Exception($key . ' is required!');
133             }
134             if (isset($definition[$key])) {
135                 $def[$key] = $definition[$key];
136             } else {
137                 $def[$key] = null;          
138             }
139         }
140
141         $this->definition = $def;
142     }
143
144     /**
145      * hasConstraint
146      * whether or not this relation has an explicit constraint
147      *
148      * @return boolean
149      */
150     public function hasConstraint()
151     {
152         return ($this->definition['constraint'] ||
153                 ($this->definition['onUpdate']) ||
154                 ($this->definition['onDelete']));
155     }
156     public function isDeferred()
157     {
158         return $this->definition['deferred'];
159     }
160
161     public function isDeferrable()
162     {
163         return $this->definition['deferrable'];
164     }
165     public function isEqual()
166     {
167         return $this->definition['equal'];
168     }
169
170     public function offsetExists($offset)
171     {
172         return isset($this->definition[$offset]);
173     }
174
175     public function offsetGet($offset)
176     {
177         if (isset($this->definition[$offset])) {
178             return $this->definition[$offset];
179         }
180         
181         return null;
182     }
183
184     public function offsetSet($offset, $value)
185     {
186         if (isset($this->definition[$offset])) {
187             $this->definition[$offset] = $value;
188         }
189     }
190
191     public function offsetUnset($offset)
192     {
193         $this->definition[$offset] = false;
194     }
195
196     /**
197      * toArray
198      *
199      * @return array
200      */
201     public function toArray() 
202     {
203         return $this->definition;
204     }
205
206     /**
207      * getAlias
208      * returns the relation alias
209      *
210      * @return string
211      */
212     final public function getAlias()
213     {
214         return $this->definition['alias'];
215     }
216
217     /**
218      * getType
219      * returns the relation type, either 0 or 1
220      *
221      * @see Doctrine_Relation MANY_* and ONE_* constants
222      * @return integer
223      */
224     final public function getType()
225     {
226         return $this->definition['type'];
227     }
228
229     /**
230      * getTable
231      * returns the foreign table object
232      *
233      * @return object Doctrine_Table
234      */
235     final public function getTable()
236     {
237         return Doctrine_Manager::getInstance()
238                ->getConnectionForComponent($this->definition['class'])
239                ->getTable($this->definition['class']);
240     }
241
242     /**
243      * getLocal
244      * returns the name of the local column
245      *
246      * @return string
247      */
248     final public function getLocal()
249     {
250         return $this->definition['local'];
251     }
252     
253     /**
254      * getLocalFieldName
255      * returns the field name of the local column
256      */
257     final public function getLocalFieldName()
258     {
259         return $this->definition['localTable']->getFieldName($this->definition['local']);
260     }
261
262     /**
263      * getForeign
264      * returns the name of the foreignkey column where
265      * the localkey column is pointing at
266      *
267      * @return string
268      */
269     final public function getForeign()
270     {
271         return $this->definition['foreign'];
272     }
273     
274     /**
275      * getLocalFieldName
276      * returns the field name of the local column
277      */
278     final public function getForeignFieldName()
279     {
280         return $this->definition['table']->getFieldName($this->definition['foreign']);
281     }
282
283     /**
284      * isComposite
285      * returns whether or not this relation is a composite relation
286      *
287      * @return boolean
288      */
289     final public function isComposite()
290     {
291         return ($this->definition['type'] == Doctrine_Relation::ONE_COMPOSITE ||
292                 $this->definition['type'] == Doctrine_Relation::MANY_COMPOSITE);
293     }
294
295     /**
296      * isOneToOne
297      * returns whether or not this relation is a one-to-one relation
298      *
299      * @return boolean
300      */
301     final public function isOneToOne()
302     {
303         return ($this->definition['type'] == Doctrine_Relation::ONE_AGGREGATE ||
304                 $this->definition['type'] == Doctrine_Relation::ONE_COMPOSITE);
305     }
306
307     /**
308      * getRelationDql
309      *
310      * @param integer $count
311      * @return string
312      */
313     public function getRelationDql($count)
314     {
315         $component = $this->getTable()->getComponentName();
316
317         $dql  = 'FROM ' . $component
318               . ' WHERE ' . $component . '.' . $this->definition['foreign']
319               . ' IN (' . substr(str_repeat('?, ', $count), 0, -2) . ')';
320
321         return $dql;
322     }
323
324     /**
325      * fetchRelatedFor
326      *
327      * fetches a component related to given record
328      *
329      * @param Doctrine_Record $record
330      * @return Doctrine_Record|Doctrine_Collection
331      */
332     abstract public function fetchRelatedFor(Doctrine_Record $record);
333
334     /**
335      * __toString
336      *
337      * @return string
338      */
339     public function __toString()
340     {
341         $r[] = "<pre>";
342         foreach ($this->definition as $k => $v) {
343             if (is_object($v)) {
344                 $v = 'Object(' . get_class($v) . ')';
345             }
346             $r[] = $k . ' : ' . $v;
347         }
348         $r[] = "</pre>";
349         return implode("\n", $r);
350     }
351 }