Skip to content

Commit 1eabede

Browse files
authored
Merge pull request #3 from locomotivemtl/mcaskill-patch-3
Fixed ModelLoader Cache
2 parents af4dfa5 + c2487c0 commit 1eabede

File tree

3 files changed

+217
-64
lines changed

3 files changed

+217
-64
lines changed

src/Charcoal/Model/Service/ModelLoader.php

Lines changed: 174 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,22 @@
22

33
namespace Charcoal\Model\Service;
44

5-
use \ArrayAccess;
6-
use \Exception;
7-
use \InvalidArgumentException;
5+
use ArrayAccess;
6+
use Exception;
7+
use LogicException;
8+
use InvalidArgumentException;
89

9-
// Module `charcoal-factory` dependencies
10-
use \Charcoal\Factory\FactoryInterface;
10+
// From PSR-6
11+
use Psr\Cache\CacheItemPoolInterface;
12+
13+
// From 'charcoal-factory'
14+
use Charcoal\Factory\FactoryInterface;
15+
16+
// From 'charcoal-core'
17+
use Charcoal\Model\ModelInterface;
1118

1219
/**
13-
* Load a single model from its source.
20+
* Load a single model from its source, of from cache.
1421
*
1522
* Use the magic methods to load automatically from __call and __get; this allows
1623
* for easy integration in templating engines like Mustache.
@@ -20,144 +27,220 @@
2027
final class ModelLoader implements ArrayAccess
2128
{
2229
/**
23-
* @var string $objType
30+
* The object type.
31+
*
32+
* The base model (FQCN) to load objects of.
33+
*
34+
* @var string
2435
*/
2536
private $objType;
2637

2738
/**
28-
* @var string $objKey
39+
* The object key.
40+
*
41+
* The model property name, or SQL column name, to load objects by.
42+
*
43+
* @var string
2944
*/
3045
private $objKey;
3146

3247
/**
33-
* @var FactoryInterface $factory
48+
* The model factory.
49+
*
50+
* @var FactoryInterface
3451
*/
3552
private $factory;
3653

54+
/**
55+
* The PSR-6 caching service.
56+
*
57+
* @var CacheItemPoolInterface
58+
*/
59+
private $cachePool;
60+
3761
/**
3862
* Construct a Model Loader with the dependencies
3963
*
40-
* # Required dependencies
64+
* # Required Dependencies
4165
*
4266
* - `obj_type`
4367
* - `factory`
68+
* - `cache`
69+
*
70+
* # Optional Dependencies
4471
*
45-
* # Optional dependencies
4672
* - `obj_key`
4773
*
48-
* @param array $data Class dependencies.
74+
* @param array $data Loader dependencies.
4975
*/
5076
public function __construct(array $data)
5177
{
5278
$this->setObjType($data['obj_type']);
5379
$this->setFactory($data['factory']);
80+
$this->setCachePool($data['cache']);
5481

5582
if (isset($data['obj_key'])) {
5683
$this->setObjKey($data['obj_key']);
5784
}
5885
}
5986

87+
// Magic Methods
88+
// =============================================================================================
89+
6090
/**
61-
* @param string $ident The object ident to load.
62-
* @param mixed $args Unused, method arguments.
63-
* @return \Charcoal\Model\ModelInterface
91+
* Retrieve an object by its key.
92+
*
93+
* @param string|integer $ident The object identifier to load.
94+
* @param mixed $args Unused; Method arguments.
95+
* @return ModelInterface
6496
*/
6597
public function __call($ident, $args = null)
6698
{
6799
unset($args);
100+
68101
return $this->load($ident);
69102
}
70103

71104
/**
72-
* @param string $ident The object ident to load.
73-
* @return \Charcoal\Model\ModelInterface
105+
* Retrieve an object by its key.
106+
*
107+
* @param string|integer $ident The object identifier to load.
108+
* @return ModelInterface
74109
*/
75110
public function __get($ident)
76111
{
77112
return $this->load($ident);
78113
}
79114

80-
81115
/**
82-
* @param string $ident The object ident to load.
116+
* Determine if an object exists by its key.
117+
*
118+
* @todo Needs implementation
119+
* @param string $ident The object identifier to lookup.
83120
* @return boolean
84121
*/
85122
public function __isset($ident)
86123
{
87-
// TODO
88124
return true;
89125
}
90126

91127
/**
92-
* @param string $ident The object ident to unset.
93-
* @throws Exception This method should never be called.
128+
* Remove an object by its key.
129+
*
130+
* @param string|integer $ident The object identifier to remove.
131+
* @throws LogicException This method should never be called.
94132
* @return void
95133
*/
96134
public function __unset($ident)
97135
{
98-
throw new Exception(
136+
throw new LogicException(
99137
'Can not unset value on a loader'
100138
);
101139
}
102140

141+
// Satisfies ArrayAccess
142+
// =============================================================================================
143+
103144
/**
104-
* @param string $ident The object ident to verify.
145+
* Determine if an object exists by its key.
146+
*
147+
* @todo Needs implementation
148+
* @see ArrayAccess::offsetExists
149+
* @param string $ident The object identifier to lookup.
105150
* @return boolean
106151
*/
107152
public function offsetExists($ident)
108153
{
109-
// TODO
110154
return true;
111155
}
112156

113157
/**
114-
* @param string $ident The object ident to load.
115-
* @return \Charcoal\Model\ModelInterface
158+
* Retrieve an object by its key.
159+
*
160+
* @see ArrayAccess::offsetGet
161+
* @param string|integer $ident The object identifier to load.
162+
* @return ModelInterface
116163
*/
117164
public function offsetGet($ident)
118165
{
119166
return $this->load($ident);
120167
}
121168

122169
/**
123-
* @param string $ident The object ident to set.
124-
* @param mixed $val The value to set.
125-
* @throws Exception This method should never be called.
170+
* Add an object.
171+
*
172+
* @see ArrayAccess::offsetSet
173+
* @param string|integer $ident The $object identifier.
174+
* @param mixed $obj The object to add.
175+
* @throws LogicException This method should never be called.
126176
* @return void
127177
*/
128-
public function offsetSet($ident, $val)
178+
public function offsetSet($ident, $obj)
129179
{
130-
throw new Exception(
180+
throw new LogicException(
131181
'Can not set value on a loader'
132182
);
133183
}
134184

135185
/**
136-
* @param string $ident The object ident to unset.
137-
* @throws Exception This method should never be called.
186+
* Remove an object by its key.
187+
*
188+
* @see ArrayAccess::offsetUnset()
189+
* @param string|integer $ident The object identifier to remove.
190+
* @throws LogicException This method should never be called.
138191
* @return void
139192
*/
140193
public function offsetUnset($ident)
141194
{
142-
throw new Exception(
195+
throw new LogicException(
143196
'Can not unset value on a loader'
144197
);
145198
}
146199

200+
// =============================================================================================
201+
147202
/**
148-
* @param string $ident The object ident to load.
149-
* @return \Charcoal\Model\ModelInterface
203+
* Retrieve an object, by its key, from its source or from the cache.
204+
*
205+
* When the cache is enabled, only the object's _data_ is stored. This prevents issues
206+
* when unserializing a class that might have dependencies.
207+
*
208+
* @param string|integer $ident The object identifier to load.
209+
* @param boolean $useCache If FALSE, ignore the cached object. Defaults to TRUE.
210+
* @param boolean $reloadObj If TRUE, refresh the cached object. Defaults to FALSE.
211+
* @return ModelInterface
150212
*/
151-
public function load($ident)
213+
public function load($ident, $useCache = true, $reloadObj = false)
152214
{
153-
return $this->loadFromSource($ident);
215+
if (!$useCache) {
216+
return $this->loadFromSource($ident);
217+
}
218+
219+
$cacheKey = $this->cacheKey($ident);
220+
$cacheItem = $this->cachePool->getItem($cacheKey);
221+
222+
if (!$reloadObj) {
223+
$objData = $cacheItem->get();
224+
if ($cacheItem->isHit()) {
225+
$obj = $this->factory->create($this->objType);
226+
$obj->setData($objData);
227+
228+
return $obj;
229+
}
230+
}
231+
232+
$obj = $this->loadFromSource($ident);
233+
$objData = $obj->data();
234+
$this->cachePool->save($cacheItem->set($objData));
235+
236+
return $obj;
154237
}
155238

156239
/**
157-
* Load an objet from soure
240+
* Load an objet from its soure.
158241
*
159-
* @param mixed $ident The object ident to load.
160-
* @return \Charcoal\Model\ModelInterface
242+
* @param string|integer $ident The object identifier to load.
243+
* @return ModelInterface
161244
*/
162245
private function loadFromSource($ident)
163246
{
@@ -167,12 +250,33 @@ private function loadFromSource($ident)
167250
} else {
168251
$obj->load($ident);
169252
}
253+
170254
return $obj;
171255
}
172256

173257
/**
174-
* @param string $objType The object type to load with this loader.
175-
* @throws InvalidArgumentException If the obj type is not a string.
258+
* Generate a cache key.
259+
*
260+
* @param string|integer $ident The object identifier to load.
261+
* @return string
262+
*/
263+
private function cacheKey($ident)
264+
{
265+
if ($this->objKey === null) {
266+
$model = $this->factory->get($this->objType);
267+
$this->setObjKey($model->key());
268+
}
269+
270+
$cacheKey = 'objects/'.str_replace('/', '.', $this->objType.'.'.$this->objKey.'.'.$ident);
271+
272+
return $cacheKey;
273+
}
274+
275+
/**
276+
* Set the object type.
277+
*
278+
* @param string $objType The object type to load with this loader.
279+
* @throws InvalidArgumentException If the object type is not a string.
176280
* @return ModelLoader Chainable
177281
*/
178282
private function setObjType($objType)
@@ -182,33 +286,56 @@ private function setObjType($objType)
182286
'Can not set model loader object type: not a string'
183287
);
184288
}
289+
185290
$this->objType = $objType;
186291
return $this;
187292
}
188293

189294
/**
190-
* @param string $objKey The object key to use for laoding.
191-
* @throws InvalidArgumentException If the obj key is not a string.
295+
* Set the object key.
296+
*
297+
* @param string $objKey The object key to use for laoding.
298+
* @throws InvalidArgumentException If the object key is not a string.
192299
* @return ModelLoader Chainable
193300
*/
194301
private function setObjKey($objKey)
195302
{
303+
if (empty($objKey) && !is_numeric($objKey)) {
304+
$this->objKey = null;
305+
return $this;
306+
}
307+
196308
if (!is_string($objKey)) {
197309
throw new InvalidArgumentException(
198310
'Can not set model loader object type: not a string'
199311
);
200312
}
313+
201314
$this->objKey = $objKey;
202315
return $this;
203316
}
204317

205318
/**
206-
* @param FactoryInterface $factory The factory to use to create models.
319+
* Set the model factory.
320+
*
321+
* @param FactoryInterface $factory The factory to create models.
207322
* @return ModelLoader Chainable
208323
*/
209324
private function setFactory(FactoryInterface $factory)
210325
{
211326
$this->factory = $factory;
212327
return $this;
213328
}
329+
330+
/**
331+
* Set the cache pool handler.
332+
*
333+
* @param CacheItemPoolInterface $cachePool A PSR-6 compatible cache pool.
334+
* @return ModelLoader Chainable
335+
*/
336+
private function setCachePool(CacheItemPoolInterface $cachePool)
337+
{
338+
$this->cachePool = $cachePool;
339+
return $this;
340+
}
214341
}

0 commit comments

Comments
 (0)