1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Charcoal\Cms\Route; |
4
|
|
|
|
5
|
|
|
use RuntimeException; |
6
|
|
|
use InvalidArgumentException; |
7
|
|
|
|
8
|
|
|
// From Pimple |
9
|
|
|
use Pimple\Container; |
10
|
|
|
|
11
|
|
|
// From PSR-7 |
12
|
|
|
use Psr\Http\Message\RequestInterface; |
13
|
|
|
use Psr\Http\Message\ResponseInterface; |
14
|
|
|
|
15
|
|
|
// From 'charcoal-factory' |
16
|
|
|
use Charcoal\Factory\FactoryInterface; |
17
|
|
|
|
18
|
|
|
// From 'charcoal-translator' |
19
|
|
|
use Charcoal\Translator\TranslatorAwareTrait; |
20
|
|
|
|
21
|
|
|
// From 'charcoal-core' |
22
|
|
|
use Charcoal\Model\ModelInterface; |
23
|
|
|
use Charcoal\Loader\CollectionLoader; |
24
|
|
|
|
25
|
|
|
// From 'charcoal-app' |
26
|
|
|
use Charcoal\App\Route\TemplateRoute; |
27
|
|
|
|
28
|
|
|
// From 'charcoal-object' |
29
|
|
|
use Charcoal\Object\ObjectRoute; |
30
|
|
|
use Charcoal\Object\ObjectRouteInterface; |
31
|
|
|
use Charcoal\Object\RoutableInterface; |
32
|
|
|
|
33
|
|
|
// From 'charcoal-cms' |
34
|
|
|
use Charcoal\Cms\TemplateableInterface; |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* Generic Object Route Handler |
38
|
|
|
* |
39
|
|
|
* Uses implementations of {@see \Charcoal\Object\ObjectRouteInterface} |
40
|
|
|
* to match routes for catch-all routing patterns. |
41
|
|
|
*/ |
42
|
|
|
class GenericRoute extends TemplateRoute |
43
|
|
|
{ |
44
|
|
|
use TranslatorAwareTrait; |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* The URI path. |
48
|
|
|
* |
49
|
|
|
* @var string |
50
|
|
|
*/ |
51
|
|
|
private $path; |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* The object route. |
55
|
|
|
* |
56
|
|
|
* @var ObjectRouteInterface |
57
|
|
|
*/ |
58
|
|
|
private $objectRoute; |
59
|
|
|
|
60
|
|
|
/** |
61
|
|
|
* The target object of the {@see GenericRoute Chainable::$objectRoute}. |
62
|
|
|
* |
63
|
|
|
* @var ModelInterface|RoutableInterface |
64
|
|
|
*/ |
65
|
|
|
private $contextObject; |
66
|
|
|
|
67
|
|
|
/** |
68
|
|
|
* Store the factory instance for the current class. |
69
|
|
|
* |
70
|
|
|
* @var FactoryInterface |
71
|
|
|
*/ |
72
|
|
|
private $modelFactory; |
73
|
|
|
|
74
|
|
|
/** |
75
|
|
|
* Store the collection loader for the current class. |
76
|
|
|
* |
77
|
|
|
* @var CollectionLoader |
78
|
|
|
*/ |
79
|
|
|
private $collectionLoader; |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* The class name of the object route model. |
83
|
|
|
* |
84
|
|
|
* Must be a fully-qualified PHP namespace and an implementation of |
85
|
|
|
* {@see \Charcoal\Object\ObjectRouteInterface}. Used by the model factory. |
86
|
|
|
* |
87
|
|
|
* @var string |
88
|
|
|
*/ |
89
|
|
|
protected $objectRouteClass = ObjectRoute::class; |
90
|
|
|
|
91
|
|
|
/** |
92
|
|
|
* Store the available templates. |
93
|
|
|
* |
94
|
|
|
* @var array |
95
|
|
|
*/ |
96
|
|
|
protected $availableTemplates = []; |
97
|
|
|
|
98
|
|
|
/** |
99
|
|
|
* Returns new template route object. |
100
|
|
|
* |
101
|
|
|
* @param array $data Class depdendencies. |
102
|
|
|
*/ |
103
|
|
|
public function __construct(array $data) |
104
|
|
|
{ |
105
|
|
|
parent::__construct($data); |
106
|
|
|
|
107
|
|
|
$this->setPath(ltrim($data['path'], '/')); |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
/** |
111
|
|
|
* Determine if the URI path resolves to an object. |
112
|
|
|
* |
113
|
|
|
* @param Container $container A DI (Pimple) container. |
114
|
|
|
* @return boolean |
115
|
|
|
*/ |
116
|
|
|
public function pathResolvable(Container $container) |
117
|
|
|
{ |
118
|
|
|
$this->setDependencies($container); |
119
|
|
|
|
120
|
|
|
$object = $this->getObjectRouteFromPath(); |
121
|
|
|
if (!$object || !$object->id()) { |
122
|
|
|
return false; |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
$contextObject = $this->getContextObject(); |
126
|
|
|
if (!$contextObject || !$contextObject->id()) { |
127
|
|
|
return false; |
128
|
|
|
} |
129
|
|
|
|
130
|
|
|
if ($contextObject instanceof RoutableInterface) { |
131
|
|
|
return $contextObject->isActiveRoute(); |
132
|
|
|
} |
133
|
|
|
|
134
|
|
|
if (isset($contextObject['active'])) { |
135
|
|
|
return (bool)$contextObject['active']; |
136
|
|
|
} |
137
|
|
|
|
138
|
|
|
return true; |
139
|
|
|
} |
140
|
|
|
|
141
|
|
|
/** |
142
|
|
|
* Resolve the dynamic route. |
143
|
|
|
* |
144
|
|
|
* @param Container $container A DI (Pimple) container. |
145
|
|
|
* @param RequestInterface $request A PSR-7 compatible Request instance. |
146
|
|
|
* @param ResponseInterface $response A PSR-7 compatible Response instance. |
147
|
|
|
* @return ResponseInterface |
148
|
|
|
*/ |
149
|
|
|
public function __invoke( |
150
|
|
|
Container $container, |
151
|
|
|
RequestInterface $request, |
152
|
|
|
ResponseInterface $response |
153
|
|
|
) { |
154
|
|
|
$response = $this->resolveLatestObjectRoute($request, $response); |
155
|
|
|
|
156
|
|
|
if (!$response->isRedirect()) { |
157
|
|
|
$this->resolveTemplateContextObject(); |
158
|
|
|
|
159
|
|
|
$templateContent = $this->templateContent($container, $request); |
160
|
|
|
|
161
|
|
|
$response->write($templateContent); |
162
|
|
|
} |
163
|
|
|
|
164
|
|
|
return $response; |
165
|
|
|
} |
166
|
|
|
|
167
|
|
|
/** |
168
|
|
|
* Create a route object. |
169
|
|
|
* |
170
|
|
|
* @return ObjectRouteInterface |
171
|
|
|
*/ |
172
|
|
|
public function createRouteObject() |
173
|
|
|
{ |
174
|
|
|
$route = $this->modelFactory()->create($this->objectRouteClass()); |
175
|
|
|
|
176
|
|
|
return $route; |
177
|
|
|
} |
178
|
|
|
|
179
|
|
|
/** |
180
|
|
|
* Retrieve the class name of the object route model. |
181
|
|
|
* |
182
|
|
|
* @return string |
183
|
|
|
*/ |
184
|
|
|
public function objectRouteClass() |
185
|
|
|
{ |
186
|
|
|
return $this->objectRouteClass; |
187
|
|
|
} |
188
|
|
|
|
189
|
|
|
/** |
190
|
|
|
* Inject dependencies from a DI Container. |
191
|
|
|
* |
192
|
|
|
* @param Container $container A dependencies container instance. |
193
|
|
|
* @return void |
194
|
|
|
*/ |
195
|
|
|
protected function setDependencies(Container $container) |
196
|
|
|
{ |
197
|
|
|
$this->setTranslator($container['translator']); |
198
|
|
|
$this->setModelFactory($container['model/factory']); |
199
|
|
|
$this->setCollectionLoader($container['model/collection/loader']); |
200
|
|
|
|
201
|
|
|
if (isset($container['config']['templates'])) { |
202
|
|
|
$this->availableTemplates = $container['config']['templates']; |
203
|
|
|
} |
204
|
|
|
} |
205
|
|
|
|
206
|
|
|
/** |
207
|
|
|
* Determine if the current object route is the latest object route. |
208
|
|
|
* |
209
|
|
|
* This method loads the latest object route from the datasource and compares |
210
|
|
|
* their creation dates. Both instances could be the same object (ID). |
211
|
|
|
* |
212
|
|
|
* @param RequestInterface $request A PSR-7 compatible Request instance. |
213
|
|
|
* @param ResponseInterface $response A PSR-7 compatible Response instance. |
214
|
|
|
* @return ResponseInterface |
215
|
|
|
*/ |
216
|
|
|
protected function resolveLatestObjectRoute( |
217
|
|
|
RequestInterface $request, |
218
|
|
|
ResponseInterface $response |
219
|
|
|
) { |
220
|
|
|
$current = $this->getObjectRouteFromPath(); |
221
|
|
|
$latest = $this->getLatestObjectPathHistory($current); |
222
|
|
|
|
223
|
|
|
// Redirect if latest route is newer |
224
|
|
|
if ($latest && $latest->getCreationDate() > $current->getCreationDate()) { |
225
|
|
|
$redirect = $this->parseRedirect($latest->getSlug(), $request); |
226
|
|
|
$response = $response->withRedirect($redirect, 301); |
227
|
|
|
} |
228
|
|
|
|
229
|
|
|
return $response; |
230
|
|
|
} |
231
|
|
|
|
232
|
|
|
/** |
233
|
|
|
* @return self |
234
|
|
|
*/ |
235
|
|
|
protected function resolveTemplateContextObject() |
236
|
|
|
{ |
237
|
|
|
$config = $this->config(); |
238
|
|
|
|
239
|
|
|
$objectRoute = $this->getObjectRouteFromPath(); |
240
|
|
|
$contextObject = $this->getContextObject(); |
241
|
|
|
$currentLang = $objectRoute->getLang(); |
242
|
|
|
|
243
|
|
|
// Set language according to the route's language |
244
|
|
|
$this->setLocale($currentLang); |
245
|
|
|
|
246
|
|
|
$templateChoice = []; |
247
|
|
|
|
248
|
|
|
// Templateable Objects have specific methods |
249
|
|
|
if ($contextObject instanceof TemplateableInterface) { |
250
|
|
|
$identProperty = $contextObject->property('templateIdent'); |
251
|
|
|
$templateIdent = $contextObject['templateIdent'] ?: $objectRoute->getRouteTemplate(); |
252
|
|
|
$controllerIdent = $contextObject['controllerIdent'] ?: $templateIdent; |
253
|
|
|
$templateChoice = $identProperty->choice($templateIdent); |
254
|
|
|
} else { |
255
|
|
|
// Use global templates to verify for custom paths |
256
|
|
|
$templateIdent = $objectRoute->getRouteTemplate(); |
257
|
|
|
$controllerIdent = $templateIdent; |
258
|
|
|
foreach ($this->availableTemplates as $templateKey => $templateData) { |
259
|
|
|
if (!isset($templateData['value'])) { |
260
|
|
|
$templateData['value'] = $templateKey; |
261
|
|
|
} |
262
|
|
|
|
263
|
|
|
if ($templateData['value'] === $templateIdent) { |
264
|
|
|
$templateChoice = $templateData; |
265
|
|
|
break; |
266
|
|
|
} |
267
|
|
|
} |
268
|
|
|
} |
269
|
|
|
|
270
|
|
|
// Template ident defined in template global config |
271
|
|
|
// Check for custom path / controller |
272
|
|
|
if (isset($templateChoice['template'])) { |
273
|
|
|
$templatePath = $templateChoice['template']; |
274
|
|
|
$templateController = $templateChoice['template']; |
275
|
|
|
} else { |
276
|
|
|
$templatePath = $templateIdent; |
277
|
|
|
$templateController = $controllerIdent; |
278
|
|
|
} |
279
|
|
|
|
280
|
|
|
// Template controller defined in choices, affect it. |
281
|
|
|
if (isset($templateChoice['controller'])) { |
282
|
|
|
$templateController = $templateChoice['controller']; |
283
|
|
|
} |
284
|
|
|
|
285
|
|
|
$config['template'] = $templatePath; |
286
|
|
|
$config['controller'] = $templateController; |
287
|
|
|
|
288
|
|
|
// Always be an array |
289
|
|
|
$templateOptions = []; |
290
|
|
|
|
291
|
|
|
// Custom template options |
292
|
|
|
if (isset($templateChoice['template_options'])) { |
293
|
|
|
$templateOptions = $templateChoice['template_options']; |
294
|
|
|
} |
295
|
|
|
|
296
|
|
|
// Overwrite from custom object template_options |
297
|
|
|
if ($contextObject instanceof TemplateableInterface) { |
298
|
|
|
if (!empty($contextObject['templateOptions'])) { |
299
|
|
|
$templateOptions = $contextObject['templateOptions']; |
300
|
|
|
} |
301
|
|
|
} |
302
|
|
|
|
303
|
|
|
if (isset($templateOptions) && $templateOptions) { |
304
|
|
|
// Not sure what this was about? |
305
|
|
|
$config['template_data'] = array_merge($config['template_data'], $templateOptions); |
306
|
|
|
} |
307
|
|
|
|
308
|
|
|
// Merge Route options from object-route |
309
|
|
|
$routeOptions = $objectRoute->getRouteOptions(); |
310
|
|
|
if ($routeOptions && count($routeOptions)) { |
|
|
|
|
311
|
|
|
$config['template_data'] = array_merge($config['template_data'], $routeOptions); |
312
|
|
|
} |
313
|
|
|
|
314
|
|
|
$this->setConfig($config); |
315
|
|
|
|
316
|
|
|
return $this; |
317
|
|
|
} |
318
|
|
|
|
319
|
|
|
/** |
320
|
|
|
* @param Container $container A DI (Pimple) container. |
321
|
|
|
* @param RequestInterface $request The request to intialize the template with. |
322
|
|
|
* @return string |
323
|
|
|
*/ |
324
|
|
|
protected function createTemplate(Container $container, RequestInterface $request) |
325
|
|
|
{ |
326
|
|
|
$template = parent::createTemplate($container, $request); |
327
|
|
|
|
328
|
|
|
$contextObject = $this->getContextObject(); |
329
|
|
|
$template['contextObject'] = $contextObject; |
330
|
|
|
|
331
|
|
|
return $template; |
332
|
|
|
} |
333
|
|
|
|
334
|
|
|
/** |
335
|
|
|
* Set the class name of the object route model. |
336
|
|
|
* |
337
|
|
|
* @param string $className The class name of the object route model. |
338
|
|
|
* @throws InvalidArgumentException If the class name is not a string. |
339
|
|
|
* @return self |
340
|
|
|
*/ |
341
|
|
|
protected function setObjectRouteClass($className) |
342
|
|
|
{ |
343
|
|
|
if (!is_string($className)) { |
344
|
|
|
throw new InvalidArgumentException( |
345
|
|
|
'Route class name must be a string.' |
346
|
|
|
); |
347
|
|
|
} |
348
|
|
|
|
349
|
|
|
$this->objectRouteClass = $className; |
350
|
|
|
|
351
|
|
|
return $this; |
352
|
|
|
} |
353
|
|
|
|
354
|
|
|
/** |
355
|
|
|
* Get the object associated with the matching object route. |
356
|
|
|
* |
357
|
|
|
* @return RoutableInterface |
358
|
|
|
*/ |
359
|
|
|
protected function getContextObject() |
360
|
|
|
{ |
361
|
|
|
if ($this->contextObject === null) { |
362
|
|
|
$this->contextObject = $this->loadContextObject(); |
363
|
|
|
} |
364
|
|
|
|
365
|
|
|
return $this->contextObject; |
|
|
|
|
366
|
|
|
} |
367
|
|
|
|
368
|
|
|
/** |
369
|
|
|
* Load the object associated with the matching object route. |
370
|
|
|
* |
371
|
|
|
* Validating if the object ID exists is delegated to the |
372
|
|
|
* {@see GenericRoute Chainable::pathResolvable()} method. |
373
|
|
|
* |
374
|
|
|
* @return RoutableInterface |
375
|
|
|
*/ |
376
|
|
|
protected function loadContextObject() |
377
|
|
|
{ |
378
|
|
|
$route = $this->getObjectRouteFromPath(); |
379
|
|
|
|
380
|
|
|
$obj = $this->modelFactory()->create($route->getRouteObjType()); |
381
|
|
|
$obj->load($route->getRouteObjId()); |
382
|
|
|
|
383
|
|
|
return $obj; |
384
|
|
|
} |
385
|
|
|
|
386
|
|
|
/** |
387
|
|
|
* Get the object route matching the URI path. |
388
|
|
|
* |
389
|
|
|
* @return \Charcoal\Object\ObjectRouteInterface |
390
|
|
|
*/ |
391
|
|
|
protected function getObjectRouteFromPath() |
392
|
|
|
{ |
393
|
|
|
if ($this->objectRoute === null) { |
394
|
|
|
$this->objectRoute = $this->loadObjectRouteFromPath(); |
395
|
|
|
} |
396
|
|
|
|
397
|
|
|
return $this->objectRoute; |
398
|
|
|
} |
399
|
|
|
|
400
|
|
|
/** |
401
|
|
|
* Load the object route matching the URI path. |
402
|
|
|
* |
403
|
|
|
* @return \Charcoal\Object\ObjectRouteInterface |
404
|
|
|
*/ |
405
|
|
|
protected function loadObjectRouteFromPath() |
406
|
|
|
{ |
407
|
|
|
// Load current slug |
408
|
|
|
// Slugs are unique |
409
|
|
|
// Slug can be duplicated by adding the front "/" to it hence the order by last_modification_date |
410
|
|
|
$route = $this->createRouteObject(); |
411
|
|
|
$table = '`'.$route->source()->table().'`'; |
412
|
|
|
$where = '`lang` = :lang AND (`slug` = :route1 OR `slug` = :route2)'; |
413
|
|
|
$order = '`last_modification_date` DESC'; |
414
|
|
|
$query = 'SELECT * FROM '.$table.' WHERE '.$where.' ORDER BY '.$order.' LIMIT 1'; |
415
|
|
|
|
416
|
|
|
$route->loadFromQuery($query, [ |
417
|
|
|
'route1' => '/'.$this->path(), |
418
|
|
|
'route2' => $this->path(), |
419
|
|
|
'lang' => $this->translator()->getLocale(), |
420
|
|
|
]); |
421
|
|
|
|
422
|
|
|
return $route; |
423
|
|
|
} |
424
|
|
|
|
425
|
|
|
/** |
426
|
|
|
* Retrieve the latest object route from the given object route's |
427
|
|
|
* associated object. |
428
|
|
|
* |
429
|
|
|
* The object routes are ordered by descending creation date (latest first). |
430
|
|
|
* Should never MISS, the given object route should exist. |
431
|
|
|
* |
432
|
|
|
* @param ObjectRouteInterface $route Routable Object. |
433
|
|
|
* @return ObjectRouteInterface|null |
434
|
|
|
*/ |
435
|
|
|
public function getLatestObjectPathHistory(ObjectRouteInterface $route) |
436
|
|
|
{ |
437
|
|
|
if (!$route->id()) { |
438
|
|
|
return null; |
439
|
|
|
} |
440
|
|
|
|
441
|
|
|
$loader = $this->collectionLoader(); |
442
|
|
|
$loader |
443
|
|
|
->setModel($route) |
|
|
|
|
444
|
|
|
->addFilter('active', true) |
445
|
|
|
->addFilter('route_obj_type', $route->getRouteObjType()) |
446
|
|
|
->addFilter('route_obj_id', $route->getRouteObjId()) |
447
|
|
|
->addFilter('lang', $route->getLang()) |
448
|
|
|
->addOrder('creation_date', 'desc') |
449
|
|
|
->setPage(1) |
450
|
|
|
->setNumPerPage(1); |
451
|
|
|
|
452
|
|
|
if ($route->getRouteOptionsIdent()) { |
453
|
|
|
$loader->addFilter('route_options_ident', $route->getRouteOptionsIdent()); |
454
|
|
|
} else { |
455
|
|
|
$loader->addFilter('route_options_ident', '', [ 'operator' => 'IS NULL' ]); |
456
|
|
|
} |
457
|
|
|
|
458
|
|
|
return $loader->load()->first(); |
459
|
|
|
} |
460
|
|
|
|
461
|
|
|
/** |
462
|
|
|
* SETTERS |
463
|
|
|
*/ |
464
|
|
|
|
465
|
|
|
/** |
466
|
|
|
* Set the specified URI path. |
467
|
|
|
* |
468
|
|
|
* @param string $path The path to use for route resolution. |
469
|
|
|
* @return self |
470
|
|
|
*/ |
471
|
|
|
protected function setPath($path) |
472
|
|
|
{ |
473
|
|
|
$this->path = $path; |
474
|
|
|
|
475
|
|
|
return $this; |
476
|
|
|
} |
477
|
|
|
|
478
|
|
|
/** |
479
|
|
|
* Set an object model factory. |
480
|
|
|
* |
481
|
|
|
* @param FactoryInterface $factory The model factory, to create objects. |
482
|
|
|
* @return self |
483
|
|
|
*/ |
484
|
|
|
protected function setModelFactory(FactoryInterface $factory) |
485
|
|
|
{ |
486
|
|
|
$this->modelFactory = $factory; |
487
|
|
|
|
488
|
|
|
return $this; |
489
|
|
|
} |
490
|
|
|
|
491
|
|
|
/** |
492
|
|
|
* Set a model collection loader. |
493
|
|
|
* |
494
|
|
|
* @param CollectionLoader $loader The collection loader. |
495
|
|
|
* @return self |
496
|
|
|
*/ |
497
|
|
|
public function setCollectionLoader(CollectionLoader $loader) |
498
|
|
|
{ |
499
|
|
|
$this->collectionLoader = $loader; |
500
|
|
|
|
501
|
|
|
return $this; |
502
|
|
|
} |
503
|
|
|
|
504
|
|
|
/** |
505
|
|
|
* Sets the environment's current locale. |
506
|
|
|
* |
507
|
|
|
* @param string $langCode The locale's language code. |
508
|
|
|
* @return void |
509
|
|
|
*/ |
510
|
|
|
protected function setLocale($langCode) |
511
|
|
|
{ |
512
|
|
|
$translator = $this->translator(); |
513
|
|
|
$translator->setLocale($langCode); |
514
|
|
|
|
515
|
|
|
$available = $translator->locales(); |
516
|
|
|
$fallbacks = $translator->getFallbackLocales(); |
517
|
|
|
|
518
|
|
|
array_unshift($fallbacks, $langCode); |
519
|
|
|
$fallbacks = array_unique($fallbacks); |
520
|
|
|
|
521
|
|
|
$locales = []; |
522
|
|
|
foreach ($fallbacks as $code) { |
523
|
|
|
if (isset($available[$code])) { |
524
|
|
|
$locale = $available[$code]; |
525
|
|
|
if (isset($locale['locales'])) { |
526
|
|
|
$choices = (array)$locale['locales']; |
527
|
|
|
array_push($locales, ...$choices); |
528
|
|
|
} elseif (isset($locale['locale'])) { |
529
|
|
|
array_push($locales, $locale['locale']); |
530
|
|
|
} |
531
|
|
|
} |
532
|
|
|
} |
533
|
|
|
|
534
|
|
|
$locales = array_unique($locales); |
535
|
|
|
|
536
|
|
|
if ($locales) { |
|
|
|
|
537
|
|
|
setlocale(LC_ALL, $locales); |
538
|
|
|
} |
539
|
|
|
} |
540
|
|
|
|
541
|
|
|
/** |
542
|
|
|
* GETTERS |
543
|
|
|
*/ |
544
|
|
|
|
545
|
|
|
/** |
546
|
|
|
* Retrieve the URI path. |
547
|
|
|
* |
548
|
|
|
* @return string |
549
|
|
|
*/ |
550
|
|
|
protected function path() |
551
|
|
|
{ |
552
|
|
|
return $this->path; |
553
|
|
|
} |
554
|
|
|
|
555
|
|
|
/** |
556
|
|
|
* Retrieve the object model factory. |
557
|
|
|
* |
558
|
|
|
* @throws RuntimeException If the model factory was not previously set. |
559
|
|
|
* @return FactoryInterface |
560
|
|
|
*/ |
561
|
|
View Code Duplication |
public function modelFactory() |
|
|
|
|
562
|
|
|
{ |
563
|
|
|
if (!isset($this->modelFactory)) { |
564
|
|
|
throw new RuntimeException( |
565
|
|
|
sprintf('Model Factory is not defined for "%s"', get_class($this)) |
566
|
|
|
); |
567
|
|
|
} |
568
|
|
|
|
569
|
|
|
return $this->modelFactory; |
570
|
|
|
} |
571
|
|
|
|
572
|
|
|
/** |
573
|
|
|
* Retrieve the model collection loader. |
574
|
|
|
* |
575
|
|
|
* @throws RuntimeException If the collection loader was not previously set. |
576
|
|
|
* @return CollectionLoader |
577
|
|
|
*/ |
578
|
|
View Code Duplication |
protected function collectionLoader() |
|
|
|
|
579
|
|
|
{ |
580
|
|
|
if (!isset($this->collectionLoader)) { |
581
|
|
|
throw new RuntimeException( |
582
|
|
|
sprintf('Collection Loader is not defined for "%s"', get_class($this)) |
583
|
|
|
); |
584
|
|
|
} |
585
|
|
|
|
586
|
|
|
return $this->collectionLoader; |
587
|
|
|
} |
588
|
|
|
|
589
|
|
|
/** |
590
|
|
|
* @return boolean |
591
|
|
|
*/ |
592
|
|
|
protected function cacheEnabled() |
593
|
|
|
{ |
594
|
|
|
$obj = $this->getContextObject(); |
595
|
|
|
return $obj['cache'] ?: false; |
596
|
|
|
} |
597
|
|
|
|
598
|
|
|
/** |
599
|
|
|
* @return integer |
600
|
|
|
*/ |
601
|
|
|
protected function cacheTtl() |
602
|
|
|
{ |
603
|
|
|
$obj = $this->getContextObject(); |
604
|
|
|
return $obj['cache_ttl'] ?: 0; |
605
|
|
|
} |
606
|
|
|
|
607
|
|
|
/** |
608
|
|
|
* @return string |
609
|
|
|
*/ |
610
|
|
|
protected function cacheIdent() |
611
|
|
|
{ |
612
|
|
|
$obj = $this->getContextObject(); |
613
|
|
|
return $obj->objType().'.'.$obj->id(); |
614
|
|
|
} |
615
|
|
|
} |
616
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.