2
2
3
3
namespace Charcoal \Model \Service ;
4
4
5
- use \ArrayAccess ;
6
- use \Exception ;
7
- use \InvalidArgumentException ;
5
+ use ArrayAccess ;
6
+ use Exception ;
7
+ use LogicException ;
8
+ use InvalidArgumentException ;
8
9
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 ;
11
18
12
19
/**
13
- * Load a single model from its source.
20
+ * Load a single model from its source, of from cache .
14
21
*
15
22
* Use the magic methods to load automatically from __call and __get; this allows
16
23
* for easy integration in templating engines like Mustache.
20
27
final class ModelLoader implements ArrayAccess
21
28
{
22
29
/**
23
- * @var string $objType
30
+ * The object type.
31
+ *
32
+ * The base model (FQCN) to load objects of.
33
+ *
34
+ * @var string
24
35
*/
25
36
private $ objType ;
26
37
27
38
/**
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
29
44
*/
30
45
private $ objKey ;
31
46
32
47
/**
33
- * @var FactoryInterface $factory
48
+ * The model factory.
49
+ *
50
+ * @var FactoryInterface
34
51
*/
35
52
private $ factory ;
36
53
54
+ /**
55
+ * The PSR-6 caching service.
56
+ *
57
+ * @var CacheItemPoolInterface
58
+ */
59
+ private $ cachePool ;
60
+
37
61
/**
38
62
* Construct a Model Loader with the dependencies
39
63
*
40
- * # Required dependencies
64
+ * # Required Dependencies
41
65
*
42
66
* - `obj_type`
43
67
* - `factory`
68
+ * - `cache`
69
+ *
70
+ * # Optional Dependencies
44
71
*
45
- * # Optional dependencies
46
72
* - `obj_key`
47
73
*
48
- * @param array $data Class dependencies.
74
+ * @param array $data Loader dependencies.
49
75
*/
50
76
public function __construct (array $ data )
51
77
{
52
78
$ this ->setObjType ($ data ['obj_type ' ]);
53
79
$ this ->setFactory ($ data ['factory ' ]);
80
+ $ this ->setCachePool ($ data ['cache ' ]);
54
81
55
82
if (isset ($ data ['obj_key ' ])) {
56
83
$ this ->setObjKey ($ data ['obj_key ' ]);
57
84
}
58
85
}
59
86
87
+ // Magic Methods
88
+ // =============================================================================================
89
+
60
90
/**
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
64
96
*/
65
97
public function __call ($ ident , $ args = null )
66
98
{
67
99
unset($ args );
100
+
68
101
return $ this ->load ($ ident );
69
102
}
70
103
71
104
/**
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
74
109
*/
75
110
public function __get ($ ident )
76
111
{
77
112
return $ this ->load ($ ident );
78
113
}
79
114
80
-
81
115
/**
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.
83
120
* @return boolean
84
121
*/
85
122
public function __isset ($ ident )
86
123
{
87
- // TODO
88
124
return true ;
89
125
}
90
126
91
127
/**
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.
94
132
* @return void
95
133
*/
96
134
public function __unset ($ ident )
97
135
{
98
- throw new Exception (
136
+ throw new LogicException (
99
137
'Can not unset value on a loader '
100
138
);
101
139
}
102
140
141
+ // Satisfies ArrayAccess
142
+ // =============================================================================================
143
+
103
144
/**
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.
105
150
* @return boolean
106
151
*/
107
152
public function offsetExists ($ ident )
108
153
{
109
- // TODO
110
154
return true ;
111
155
}
112
156
113
157
/**
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
116
163
*/
117
164
public function offsetGet ($ ident )
118
165
{
119
166
return $ this ->load ($ ident );
120
167
}
121
168
122
169
/**
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.
126
176
* @return void
127
177
*/
128
- public function offsetSet ($ ident , $ val )
178
+ public function offsetSet ($ ident , $ obj )
129
179
{
130
- throw new Exception (
180
+ throw new LogicException (
131
181
'Can not set value on a loader '
132
182
);
133
183
}
134
184
135
185
/**
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.
138
191
* @return void
139
192
*/
140
193
public function offsetUnset ($ ident )
141
194
{
142
- throw new Exception (
195
+ throw new LogicException (
143
196
'Can not unset value on a loader '
144
197
);
145
198
}
146
199
200
+ // =============================================================================================
201
+
147
202
/**
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
150
212
*/
151
- public function load ($ ident )
213
+ public function load ($ ident, $ useCache = true , $ reloadObj = false )
152
214
{
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 ;
154
237
}
155
238
156
239
/**
157
- * Load an objet from soure
240
+ * Load an objet from its soure.
158
241
*
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
161
244
*/
162
245
private function loadFromSource ($ ident )
163
246
{
@@ -167,12 +250,33 @@ private function loadFromSource($ident)
167
250
} else {
168
251
$ obj ->load ($ ident );
169
252
}
253
+
170
254
return $ obj ;
171
255
}
172
256
173
257
/**
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.
176
280
* @return ModelLoader Chainable
177
281
*/
178
282
private function setObjType ($ objType )
@@ -182,33 +286,56 @@ private function setObjType($objType)
182
286
'Can not set model loader object type: not a string '
183
287
);
184
288
}
289
+
185
290
$ this ->objType = $ objType ;
186
291
return $ this ;
187
292
}
188
293
189
294
/**
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.
192
299
* @return ModelLoader Chainable
193
300
*/
194
301
private function setObjKey ($ objKey )
195
302
{
303
+ if (empty ($ objKey ) && !is_numeric ($ objKey )) {
304
+ $ this ->objKey = null ;
305
+ return $ this ;
306
+ }
307
+
196
308
if (!is_string ($ objKey )) {
197
309
throw new InvalidArgumentException (
198
310
'Can not set model loader object type: not a string '
199
311
);
200
312
}
313
+
201
314
$ this ->objKey = $ objKey ;
202
315
return $ this ;
203
316
}
204
317
205
318
/**
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.
207
322
* @return ModelLoader Chainable
208
323
*/
209
324
private function setFactory (FactoryInterface $ factory )
210
325
{
211
326
$ this ->factory = $ factory ;
212
327
return $ this ;
213
328
}
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
+ }
214
341
}
0 commit comments