http://phing.info/

Source Code Coverage

Designed for use with PHPUnit2, Xdebug and Phing.

Methods: 19 LOC: 399 Statements: 115

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


Report generated at 2007-11-05T09:09:01-05:00