http://phing.info/

Source Code Coverage

Designed for use with PHPUnit2, Xdebug and Phing.

Methods: 22 LOC: 500 Statements: 179

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


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