http://phing.info/

Source Code Coverage

Designed for use with PHPUnit2, Xdebug and Phing.

Methods: 20 LOC: 415 Statements: 124

Source file Statements Methods Total coverage
Entity.php 96.8% 100.0% 97.2%
   
1
<?php
2
/**
3
 * Xyster Framework
4
 *
5
 * This source file is subject to the new BSD license that is bundled
6
 * with this package in the file LICENSE.txt.
7
 * It is also available through the world-wide-web at this URL:
8
 * http://www.opensource.org/licenses/bsd-license.php
9
 *
10
 * @category  Xyster
11
 * @package   Xyster_Orm
12
 * @copyright Copyright (c) 2007-2008 Irrational Logic (http://irrationallogic.net)
13
 * @license   http://www.opensource.org/licenses/bsd-license.php New BSD License
14
 * @version   $Id: Entity.php 199 2008-01-19 21:54:40Z doublecompile $
15
 */
16
/**
17
 * A data entity: the basic data unit of the ORM package
18
 *
19
 * @category  Xyster
20
 * @package   Xyster_Orm
21
 * @copyright Copyright (c) 2007-2008 Irrational Logic (http://irrationallogic.net)
22
 * @license   http://www.opensource.org/licenses/bsd-license.php New BSD License
23
 */
24
class Xyster_Orm_Entity
25
{
26
    /**
27
     * The "base" values from object instantiation
28
     *
29
     * @var array
30
     */
31
    protected $_base = array();
32
33
    /**
34
     * For quick modification checking
35
     *
36
     * @var boolean
37
     */
38
    protected $_dirty = false;
39
40
    /**
41
     * Related entities or sets
42
     *
43
     * @var array
44
     */
45
    protected $_related = array();
46
47
    /**
48
     * The entity values
49
     *
50
     * @var array
51
     */
52
    protected $_values = array();
53
54
    /**
55
     * Entity meta-data (fields, relations, etc.)
56
     *
57
     * @var Xyster_Orm_Entity_Meta[]
58
     */
59
    static private $_meta = array();
60
61
    /**
62
     * Creates a new entity
63
     *
64
     * @param array $values Values for the entity
65
     */
66
    public function __construct( array $values = null )
67
    {
68 109
        if (! $this->_getMeta() instanceof Xyster_Orm_Entity_Meta ) {
69
            require_once 'Xyster/Orm/Entity/Exception.php';
70
            throw new Xyster_Orm_Entity_Exception('The metadata for ' . get_class($this) . 'has not been setup');
71
        }
72
73 109
        foreach( $this->_getMeta()->getFieldNames() as $name ) {
74 109
            $this->_values[$name] = null;
75 109
        }
76
77 109
        if ( $values ) {
78 94
            $this->import($values);
79 94
        }
80
    }
81
82
    /**
83
     * Adds an entity meta information object
84
     *
85
     * This shouldn't be called except by the Xyster_Orm_Mapper
86
     *
87
     * @param Xyster_Orm_Entity_Meta $meta
88
     */
89
    static public function setMeta( Xyster_Orm_Entity_Meta $meta )
90
    {
91 223
        self::$_meta[ $meta->getEntityName() ] = $meta;
92
    }
93
94
    /**
95
     * Overloader for getting/setting linked properties
96
     *
97
     * @param string $name  The method name
98
     * @param array $args  Any arguments used
99
     * @magic
100
     * @return mixed  The result of the method
101
     */
102
    public function __call( $name, array $args )
103
    {
104 39
        $action = strtolower(substr($name, 0, 3));
105 39
        $field = strtolower($name[3]) . substr($name, 4);
106 39
        if ( array_key_exists($field, $this->_values) ) {
107 32
            if ( $action == 'get' ) {
108 1
                return $this->_values[$field];
109 31
            } else if ( $action == 'set' ) {
110 31
                $this->_setField($field, $args[0]);
111 31
            }
112 31
        } else {
113 19
            if ( $action == 'get' ) {
114 1
                return $this->_getRelated($field);
115 18
            } else if ( $action == 'set' ) {
116 18
                $this->_setRelated($field, $args[0]);
117 16
            }
118
        }
119
    }
120
121
    /**
122
     * Overloader for getting fields
123
     *
124
     * @magic
125
     * @param string $name
126
     * @return mixed The value of the field
127
     * @throws Xyster_Orm_Entity_Exception if the field is invalid
128
     */
129
    public function __get( $name )
130
    {
131 58
        $isField = array_key_exists($name, $this->_values);
132 58
        if ( !$isField && !$this->_getMeta()->isRelation($name) ) {
133 1
            require_once 'Xyster/Orm/Entity/Exception.php';
134 1
            throw new Xyster_Orm_Entity_Exception("'" . $name . "' is not a valid field or relation name");
135 0
        }
136 57
        return ( $isField ) ? $this->_values[$name] : $this->_getRelated($name);
137
    }
138
139
    /**
140
     * Overloader for setting fields
141
     *
142
     * @magic
143
     * @param string $name The field name
144
     * @param mixed $value The field value
145
     * @throws Xyster_Orm_Entity_Exception if the field is invalid
146
     */
147
    public function __set( $name, $value )
148
    {
149 37
        $this->{'set'.ucfirst($name)}( $value );
150
    }
151
152
    /**
153
     * Gets the original values of the entity
154
     *
155
     * @return array
156
     */
157
    public function getBase()
158
    {
159 18
        return $this->_base;
160
    }
161
162
    /**
163
     * Gets the fields that have been changed since the entity was created
164
     *
165
     * @return array
166
     */
167
    public function getDirtyFields()
168
    {
169 8
        if ( !$this->_base ) {
170 1
            return $this->_values;
171 0
        }
172 7
        return array_diff_assoc($this->_values, $this->_base);
173
    }
174
175
    /**
176
     * Gets the primary key of the entity
177
     *
178
     * @param boolean $base True to return the original primary key (if changed)
179
     * @return mixed An array or scalar key value
180
     */
181
    public function getPrimaryKey( $base = false )
182
    {
183 81
        return array_intersect_key((!$base ? $this->_values : $this->_base),
184 81
            array_flip($this->_getMeta()->getPrimary()));
185
    }
186
187
    /**
188
     * Gets the primary key of the entity as a Xyster_Data_Criterion
189
     *
190
     * @param boolean $base True to return the original primary key (if changed)
191
     * @return Xyster_Data_Criterion The primary key
192
     */
193
    public function getPrimaryKeyAsCriterion( $base = false )
194
    {
195 21
        $key = $this->getPrimaryKey($base);
196
197
        // build a criterion object based on the primary key(s)
198 21
        $criteria = null;
199 21
        foreach( $key as $name => $value ) {
200 21
            require_once 'Xyster/Data/Expression.php';
201 21
            $thiskey = Xyster_Data_Expression::eq($name, $value);
202 21
            if ( !$criteria ) {
203 21
                $criteria = $thiskey;
204 21
            } else if ( $criteria instanceof Xyster_Data_Expression ) {
205 4
                require_once 'Xyster/Data/Junction.php';
206 4
                $criteria = Xyster_Data_Junction::all($criteria, $thiskey);
207 4
            } else if ( $criteria instanceof Xyster_Data_Junction ) {
208 3
                $criteria->add($thiskey);
209 3
            }
210 21
        }
211
212 21
        return $criteria;
213
    }
214
215
    /**
216
     * Gets the primary key of the entity as a string
217
     *
218
     * @param boolean $base True to return the original primary key (if changed)
219
     * @return string A string representation of the primary key
220
     */
221
    public function getPrimaryKeyAsString( $base = false )
222
    {
223 2
    	$pk = $this->getPrimaryKey($base);
224
225 2
    	if ( is_array($pk) ) {
226 2
	    	$string = array();
227 2
	        foreach( $pk as $key => $value ) {
228 2
	            $string[] = $key . '=' . $value;
229 2
	        }
230 2
	        $pk = implode(',', $string);
231 2
    	}
232
233 2
    	return $pk;
234
    }
235
236
    /**
237
     * Imports the values in the array into the corresponding fields
238
     *
239
     * @param array $values
240
     */
241
    public function import( array $values )
242
    {
243 95
        foreach( array_keys($this->_values) as $field ) {
244 95
            $this->_values[$field] = array_key_exists($field, $values) ?
245 95
                $values[$field] : null;
246 95
        }
247 95
        $this->_base = $this->_values;
248
    }
249
250
    /**
251
     * Determines whether or not this entity has changed values since creation
252
     *
253
     * @return boolean Whether this entity has changed
254
     */
255
    public function isDirty()
256
    {
257 12
        return $this->_dirty;
258
    }
259
260
    /**
261
     * Checks whether a related entity or set has been loaded
262
     *
263
     * @param string $name The name of the relation
264
     * @return boolean true if the relation has been loaded
265
     * @throws Xyster_Orm_Exception if the relationship name is invalid
266
     */
267
    public function isLoaded( $name )
268
    {
269 26
        $this->_getMeta()->getRelation($name); // to test validity
270 26
	    return array_key_exists($name, $this->_related);
271
    }
272
273
    /**
274
     * Sets the entity as dirty or clean
275
     *
276
     * This is only used by the transactional layer or associated collections to
277
     * notify changes in an entity's state.  You shouldn't call this method
278
     * directly.
279
     *
280
     * @param boolean $dirty Whether the entity should be set dirty
281
     */
282
    public function setDirty( $dirty = true )
283
    {
284 24
        $this->_dirty = $dirty;
285
    }
286
287
    /**
288
     * Returns an array copy of the entity
289
     *
290
     * @return array The entity values
291
     */
292
    public function toArray()
293
    {
294 25
        return $this->_values;
295
    }
296
297
    /**
298
     * Returns a string value of this entity
299
     *
300
     * @magic
301
     * @return string
302
     */
303
    public function __toString()
304
    {
305 9
        $string = get_class($this) . ' [';
306 9
        $first = true;
307 9
        foreach( $this->_values as $name => $value ) {
308 9
            if ( !$first ) {
309 9
                $string .= ',';
310 9
            }
311 9
            $string .= $name . '=' . $value;
312 9
            $first = false;
313 9
        }
314 9
        return $string . ']';
315
    }
316
317
    /**
318
     * Gets a related property
319
     *
320
     * @param string $name  The name of the property
321
     * @return mixed  The related Xyster_Orm_Entity or Xyster_Orm_Set
322
     * @throws Xyster_Orm_Exception if the property name is invalid
323
     */
324
    protected function _getRelated( $name )
325
    {
326 21
        if ( !array_key_exists($name, $this->_related) ) {
327 17
            $this->_related[$name] = $this->_getMeta()->getRelation($name)->load($this);
328 17
        }
329 21
        return $this->_related[$name];
330
    }
331
332
    /**
333
     * The base method for setting fields
334
     *
335
     * If overriding this method, make sure to call the parent or else the
336
     * entity won't mark itself dirty.
337
     *
338
     * @param string $name
339
     * @param mixed $value
340
     */
341
    protected function _setField( $name, $value )
342
    {
343 31
        $this->_dirty = true;
344 31
        $this->_values[$name] = $value;
345
    }
346
347
    /**
348
     * Sets a linked property and registers the entity as dirty
349
     *
350
     * @param string $name  The name of the property
351
     * @param mixed $value The new property value
352
     * @throws Xyster_Orm_Exception if the property name is invalid
353
     * @throws Xyster_Orm_Exception if the value is incorrect for the property
354
     */
355
    protected function _setRelated( $name, $value )
356
    {
357 18
        $info = $this->_getMeta()->getRelation($name);
358 18
        $class = $info->getTo();
359
360 18
        if ( !$info->isCollection() ) {
361 17
            if ( $value !== null && ! $value instanceof $class ) {
362 1
                require_once 'Xyster/Orm/Exception.php';
363 1
                throw new Xyster_Orm_Exception("'" . $name . "' must be an instance of '" . $class . "'");
364 0
            }
365 16
        } else {
366 3
            $setClass = get_class($this->_getMeta()->getMapperFactory()->get($class)->getSet());
367 3
            if (! $value instanceof $setClass ) {
368 1
                require_once 'Xyster/Orm/Exception.php';
369 1
                throw new Xyster_Orm_Exception("'" . $name . "' must be an instance of '" . $setClass . "'");
370 0
            }
371
        }
372
373 16
        if ( $info->isCollection() ) {
374
375 2
            $value->relateTo($info, $this);
376
377 16
        } else if ( $value === null ) {
378
379 2
            $fkeyNames = $info->getId();
380 2
            foreach( $fkeyNames as $fkeyName ) {
381 2
                $this->{'set'.ucfirst($fkeyName)}(null);
382 2
            }
383
384 16
        } else if ( $value->getPrimaryKey() ) {
385
386 15
            $fkeyNames = $info->getId();
387 15
            $key = $value->getPrimaryKey();
388 15
            $keyNames = array_keys($key);
389 15
            for( $i=0; $i<count($key); $i++ ) {
390 15
                $keyValue = $key[ $keyNames[$i] ];
391 15
                $fkeyName = $fkeyNames[$i];
392
                // compare values so the entity isn't marked dirty unnecessarily
393 15
                if ( $this->$fkeyName != $keyValue ) {
394 10
                    $this->{'set'.ucfirst($fkeyName)}($keyValue);
395 10
                }
396 15
            }
397
398 15
        }
399
400 16
        $this->_related[$name] = $value;
401 16
        $this->_dirty = true;
402
    }
403
404
    /**
405
     * Gets the entity meta
406
     *
407
     * @return Xyster_Orm_Entity_Meta
408
     */
409
    protected function _getMeta()
410
    {
411 111
        $class = get_class($this);
412 111
        return array_key_exists($class, self::$_meta) ?
413 111
            self::$_meta[$class] : null;
414
    }
415
}


Report generated at 2008-01-20T12:13:39-05:00