http://phing.info/

Source Code Coverage

Designed for use with PHPUnit2, Xdebug and Phing.

Methods: 22 LOC: 495 Statements: 180

Source file Statements Methods Total coverage
Manager.php 93.9% 100.0% 94.6%
   
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: Manager.php 202 2008-01-20 16:20:09Z doublecompile $
15
 */
16
/**
17
 * @see Xyster_Orm_Repository
18
 */
19 1
require_once 'Xyster/Orm/Repository.php';
20
/**
21
 * @see Xyster_Orm_Plugin_Broker
22
 */
23 1
require_once 'Xyster/Orm/Plugin/Broker.php';
24
/**
25
 * The main backend of the orm package
26
 *
27
 * @category  Xyster
28
 * @package   Xyster_Orm
29
 * @copyright Copyright (c) 2007-2008 Irrational Logic (http://irrationallogic.net)
30
 * @license   http://www.opensource.org/licenses/bsd-license.php New BSD License
31
 */
32
class Xyster_Orm_Manager
33
{
34
    /**
35
     * The mapper factory
36
     *
37
     * @var Xyster_Orm_Mapper_Factory_Interface
38
     */
39
    protected $_mapFactory;
40
41
    /**
42
     * The repository for storing entities
43
     *
44
     * @var Xyster_Orm_Repository
45
     */
46
    protected $_repository;
47
48
    /**
49
     * The secondary cache
50
     *
51
     * @var Zend_Cache_Core
52
     */
53
    protected $_secondaryCache;
54
55
    /**
56
     * The plugin broker
57
     *
58
     * @var Xyster_Orm_Plugin_Broker
59
     */
60
    protected $_plugins;
61
62
    /**
63
     * Creates a new Orm_Manager
64
     *
65
     */
66
    public function __construct()
67
    {
68 221
        $this->_plugins = new Xyster_Orm_Plugin_Broker();
69
    }
70
71
    /**
72
     * Clears out the repository
73
     *
74
     */
75
    public function clear()
76
    {
77 3
        $this->_repository = null;
78
    }
79
80
    /**
81
     * Executes a query or report query
82
     *
83
     * @param Xyster_Orm_Query $query
84
     * @return Xyster_Data_Set
85
     */
86
    public function executeQuery( Xyster_Orm_Query $query )
87
    {
88 11
        $set = $this->_mapFactory->get($query->getFrom())->query($query);
89
90 11
        if ( $set instanceof Xyster_Orm_Set ) {
91
        	// add returned entities to the cache
92 7
        	$this->getRepository()->addAll($set);
93 7
        	$this->putAllInSecondaryCache($set);
94 7
        }
95
96 11
        return $set;
97
    }
98
99
    /**
100
     * Gets the first entity found matching a set of criteria
101
     *
102
     * @param string $className
103
     * @param array $criteria
104
     * @return Xyster_Orm_Entity The entity found or null if none
105
     */
106
    public function find( $className, array $criteria )
107
    {
108 3
        if ( $entity = $this->getRepository()->find($className, $criteria) ) {
109
110 1
            return $entity;
111
112 0
        } else {
113
114 2
            $map = $this->getMapperFactory()->get($className);
115 2
            $entity = $map->find($criteria);
116 2
            if ( $entity ) {
117 2
                $this->getRepository()->add($entity);
118 2
                $this->putInSecondaryCache($entity);
119 2
            }
120 2
            return $entity;
121
        }
122
    }
123
124
    /**
125
     * Finds all entities matching a given criteria
126
     *
127
     * @param string $className
128
     * @param mixed $criteria {@link Xyster_Data_Criterion} or associative array
129
     * @param mixed $sorts Array of {@link Xyster_Data_Sort} objects
130
     */
131
    public function findAll( $className, $criteria, $sorts = null )
132
    {
133 10
        $map = $this->getMapperFactory()->get($className);
134 10
        $all = $map->findAll($criteria, $sorts);
135 9
        $this->getRepository()->addAll($all);
136 9
        $this->putAllInSecondaryCache($all);
137 9
        return $all;
138
    }
139
140
    /**
141
     * Gets an entity by class and primary key
142
     *
143
     * @param string $className
144
     * @param mixed $id
145
     * @return Xyster_Orm_Entity
146
     */
147
    public function get( $className, $id )
148
    {
149 24
        $map = $this->getMapperFactory()->get($className);
150
151 24
        if ( is_scalar($id) || is_null($id) ) {
152 17
            $keyNames = $map->getEntityMeta()->getPrimary();
153 17
            $id = array( $keyNames[0] => $id );
154 17
        }
155
156 24
        $entity = $this->getFromCache($className, $id, true);
157 24
        if ( $entity instanceof Xyster_Orm_Entity ) {
158 10
            return $entity;
159 0
        }
160
161 18
        $entity = $map->get($id);
162 18
        if ( $entity instanceof Xyster_Orm_Entity ) {
163 14
            $this->getRepository()->add($entity);
164 14
            $this->putInSecondaryCache($entity);
165 14
            return $entity;
166 0
        }
167 4
        return null;
168
    }
169
170
    /**
171
     * Gets all entities from the data source or a subset if given the keys
172
     *
173
     * @param string $className
174
     * @param array $ids
175
     * @return Xyster_Orm_Set
176
     */
177
    public function getAll( $className, array $ids = null )
178
    {
179 4
        $all = null;
180 4
        $map = $this->getMapperFactory()->get($className);
181
182 4
        if ( is_array($ids) && count($ids) ) {
183
            // we're getting a few entities by primary key
184
185 4
            if ( $this->getRepository()->hasAll($className) ) {
186 1
                $keyNames = $map->getEntityMeta()->getPrimary();
187 1
                $all = $map->getSet();
188 1
                foreach( $ids as $id ) {
189 1
                    if ( is_scalar($id) ) {
190 1
                        $id = array( $keyNames[0] => $id );
191 1
                    }
192 1
                    $entity = $this->getRepository()->get($className, $id);
193 1
                    if ( $entity ) {
194 1
                        $all->add( $entity );
195 1
                    }
196 1
                }
197 1
            } else {
198 3
                $all = $map->getAll($ids);
199 3
                $this->getRepository()->addAll($all);
200 3
                $this->putAllInSecondaryCache($all);
201
            }
202
203 4
        } else {
204
            // we're getting ALL entities from the source
205
206 2
            if ( $this->getRepository()->hasAll($className) ) {
207 1
                $all = $this->getRepository()->getAll($className);
208 1
                if (! $all instanceof Xyster_Orm_Set ) {
209 1
                    $all = $map->getSet($all);
210 1
                }
211 1
            } else {
212 2
                $all = $map->getAll();
213 2
                $this->getRepository()->addAll($all);
214 2
                $this->putAllInSecondaryCache($all);
215 2
                $this->getRepository()->setHasAll($className);
216
            }
217
218
        }
219
220 4
        return $all;
221
    }
222
223
    /**
224
     * Tries to load an entity from the cache
225
     *
226
     * This method will return null if the entity isn't in either cache
227
     *
228
     * @param string $className
229
     * @param mixed $id
230
     * @param boolean $checkSecondary Whether to also check the secondary cache
231
     * @return Xyster_Orm_Entity
232
     */
233
    public function getFromCache( $className, $id, $checkSecondary = false )
234
    {
235 60
        $map = $this->getMapperFactory()->get($className);
236
237 60
        if ( is_scalar($id) || is_null($id) ) {
238 2
            $keyNames = $map->getEntityMeta()->getPrimary();
239 2
            $id = array( $keyNames[0] => $id );
240 2
        }
241
242 60
        $entity = $this->getRepository()->get($className, $id);
243 60
        if ( $entity instanceof Xyster_Orm_Entity ) {
244 23
            return $entity;
245 0
        }
246
247 58
        if ( $checkSecondary ) {
248 18
            $entity = $this->_getFromSecondaryCache($className, $id);
249 18
            if ( $entity instanceof Xyster_Orm_Entity ) {
250 2
                $this->_plugins->postLoad($entity);
251 2
                $this->getRepository()->add($entity);
252 2
                return $entity;
253 0
            }
254 18
        }
255
    }
256
257
    /**
258
     * Gets entities via a many-to-many table
259
     *
260
     * @param string $className
261
     * @param Xyster_Orm_Entity $entity
262
     * @param Xyster_Orm_Relation $relation
263
     * @return Xyster_Orm_Set
264
     */
265
    public function getJoined( Xyster_Orm_Entity $entity, Xyster_Orm_Relation $relation )
266
    {
267 7
        $className = get_class($entity);
268 7
        $joined = $this->_mapFactory->get($className)->getJoined($entity, $relation);
269 7
        $this->getRepository()->addAll($joined);
270 7
        return $joined;
271
    }
272
273
    /**
274
     * Gets the factory for entity mappers
275
     *
276
     * @return Xyster_Orm_Mapper_Factory_Interface
277
     */
278
    public function getMapperFactory()
279
    {
280 102
        if ( !$this->_mapFactory ) {
281 1
            require_once 'Xyster/Orm/Mapper/Factory.php';
282 1
            $this->setMapperFactory(new Xyster_Orm_Mapper_Factory());
283 1
        }
284 102
        return $this->_mapFactory;
285
    }
286
287
    /**
288
     * Gets the plugin broker
289
     *
290
     * @return Xyster_Orm_Plugin_Broker
291
     */
292
    public function getPluginBroker()
293
    {
294 64
        return $this->_plugins;
295
    }
296
297
    /**
298
     * Gets the entity repository
299
     *
300
     * @return Xyster_Orm_Repository
301
     */
302
    public function getRepository()
303
    {
304 64
        if ( !$this->_repository ) {
305 56
            $this->_repository = new Xyster_Orm_Repository($this->getMapperFactory());
306 56
        }
307 64
        return $this->_repository;
308
    }
309
310
    /**
311
     * Gets the secondary repository for storing entities
312
     *
313
     * @return Zend_Cache_Core
314
     */
315
    public function getSecondaryCache()
316
    {
317 41
        return $this->_secondaryCache;
318
    }
319
320
    /**
321
     * Refreshes the values of an entity
322
     */
323
    public function refresh( Xyster_Orm_Entity $entity )
324
    {
325 2
        $this->getMapperFactory()->get(get_class($entity))->refresh($entity);
326
    }
327
328
    /**
329
     * Sets the factory for entity mappers
330
     *
331
     * This method also calls the
332
     * {@link Xyster_Orm_Mapper_Factory_Interface::setManager} method.
333
     *
334
     * @param Xyster_Orm_Mapper_Factory_Interface $mapFactory
335
     */
336
    public function setMapperFactory( Xyster_Orm_Mapper_Factory_Interface $mapFactory )
337
    {
338 221
        $this->_mapFactory = $mapFactory;
339 221
        $mapFactory->setManager($this);
340
    }
341
342
    /**
343
     * Sets the secondary cache for storing entities
344
     *
345
     * If $cache is null, then no secondary cache is used.
346
     *
347
     * @param mixed $cache Either a Cache object, or a string naming a Registry key
348
     */
349
    public function setSecondaryCache($cache = null)
350
    {
351 40
        $this->_secondaryCache = $this->_setupSecondaryCache($cache);
352
    }
353
354
    /**
355
     * Gets an entity from the secondary cache
356
     *
357
     * @param string $className
358
     * @param array $id
359
     * @return Xyster_Orm_Entity the entity found or null if none
360
     */
361
    protected function _getFromSecondaryCache( $className, $id )
362
    {
363 18
        $cache = $this->getSecondaryCache();
364 18
        $entity = null;
365 18
        if ( $cache ) {
366 11
            $map = $this->getMapperFactory()->get($className);
367 11
            $cacheId = array('Xyster_Orm', $map->getDomain(), $className);
368 11
            foreach( $id as $key => $value ) {
369 11
                $cacheId[] = $key . '=' . $value;
370 11
            }
371 11
            $cacheId = md5(implode("/",$cacheId));
372
373 11
            $loaded = $cache->load($cacheId);
374 11
            if ( is_array($loaded) ) {
375 2
                $entity = $this->_shellToEntity($loaded, $map->getEntityMeta());
376 2
            }
377 11
        }
378 18
        return $entity;
379
    }
380
381
    /**
382
     * Puts the entity in the secondary cache
383
     *
384
     * @param Xyster_Orm_Entity $entity
385
     */
386
    public function putInSecondaryCache( Xyster_Orm_Entity $entity )
387
    {
388 33
        $cache = $this->getSecondaryCache();
389 33
        $className = get_class($entity);
390 33
        $map = $this->getMapperFactory()->get($className);
391 33
        $cacheLifetime = $map->getLifetime();
392
393
        // only store the entity if it should be cached longer than the request
394
        // that's why we have the primary repository
395 33
        if ( $cache && $cacheLifetime > -1 ) {
396
397 15
            $cacheId = array('Xyster_Orm', $map->getDomain(), $className);
398 15
            foreach( $entity->getPrimaryKey() as $key => $value ) {
399 15
                $cacheId[] = $key . '=' . $value;
400 15
            }
401 15
            $cacheId = md5(implode("/", $cacheId));
402 15
            $shell = $this->_entityToShell($entity, $map->getEntityMeta());
403 15
            $cache->save($shell, $cacheId, array(), $cacheLifetime);
404 15
        }
405
    }
406
407
    /**
408
     * Convenience method to put all entities in a set in the cache
409
     *
410
     * @param Xyster_Orm_Set $set
411
     */
412
    public function putAllInSecondaryCache( Xyster_Orm_Set $set )
413
    {
414 19
        foreach( $set as $entity ) {
415 18
            $this->putInSecondaryCache($entity);
416 18
        }
417
    }
418
419
    /**
420
     * Turns an entity into a shell for storage
421
     *
422
     * @param Xyster_Orm_Entity $entity
423
     * @param Xyster_Orm_Entity_Meta $meta
424
     * @return array
425
     */
426
    final protected function _entityToShell( Xyster_Orm_Entity $entity, Xyster_Orm_Entity_Meta $meta )
427
    {
428 15
        $related = array();
429 15
        foreach( $meta->getRelations() as $relation ) {
430
            /* @var $relation Xyster_Orm_Relation */
431 15
            $name = $relation->getName();
432 15
            if ( $entity->isLoaded($name) ) {
433 1
                $linked = $entity->$name;
434 1
                if ( $linked instanceof Xyster_Orm_Set ) {
435
                    /* @var $linked Xyster_Orm_Set */
436 1
                    $related[$name] = $linked->getPrimaryKeys();
437 1
                } else if ( $linked instanceof Xyster_Orm_Entity ) {
438
                    /* @var $linked Xyster_Orm_Entity */
439 1
                    $related[$name] = $linked->getPrimaryKey();
440 1
                }
441 1
            }
442 15
        }
443 15
        return array('values' => $entity->toArray(), 'related' => $related);
444
    }
445
446
    /**
447
     * Take the serialized array and turn it back into an entity
448
     *
449
     * @param array $shell
450
     * @return Xyster_Orm_Entity
451
     */
452
    final protected function _shellToEntity( array $shell, Xyster_Orm_Entity_Meta $meta )
453
    {
454 2
        if ( !isset($shell['values']) ) {
455 0
            return;
456 0
        }
457
458 2
        $className = $meta->getEntityName();
459 2
        $entity = new $className($shell['values']);
460 2
        if ( isset($shell['related']) && is_array($shell['related']) ) {
461 1
            foreach( $shell['related'] as $name => $related ) {
462 1
                $relation = $meta->getRelation($name);
463 1
                if ( !$relation->isCollection() ) {
464 0
                    $entity->$name = $this->get($relation->getTo(), $related);
465 0
                } else {
466 1
                    $entity->$name = $this->getAll($relation->getTo(), $related);
467
                }
468 1
            }
469 1
        }
470 2
        $entity->setDirty(false);
471
472 2
        return $entity;
473
    }
474
475
    /**
476
     * @param mixed $cache Either a Cache object, or a string naming a Registry key
477
     * @return Zend_Cache_Core
478
     * @throws Xyster_Orm_Exception
479
     */
480
    final protected function _setupSecondaryCache($cache)
481
    {
482 40
        if ($cache === null) {
483 1
            return null;
484 0
        }
485 40
        if (is_string($cache)) {
486 2
            require_once 'Zend/Registry.php';
487 2
            $cache = Zend_Registry::get($cache);
488 1
        }
489 40
        if (!$cache instanceof Zend_Cache_Core) {
490 1
            require_once 'Xyster/Orm/Exception.php';
491 1
            throw new Xyster_Orm_Exception('Argument must be of type Zend_Cache_Core, or a Registry key where a Zend_Cache_Core object is stored');
492 0
        }
493 40
        return $cache;
494
    }
495
}


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