Overview

Namespaces

  • Charcoal
    • Loader
    • Model
      • Service
      • ServiceProvider
    • Source
      • Database
    • Validator

Classes

  • Charcoal\Loader\CollectionLoader
  • Charcoal\Loader\FileLoader
  • Charcoal\Model\AbstractMetadata
  • Charcoal\Model\AbstractModel
  • Charcoal\Model\Collection
  • Charcoal\Model\Model
  • Charcoal\Model\ModelMetadata
  • Charcoal\Model\ModelValidator
  • Charcoal\Model\Service\MetadataLoader
  • Charcoal\Model\Service\ModelBuilder
  • Charcoal\Model\Service\ModelLoader
  • Charcoal\Model\Service\ModelLoaderBuilder
  • Charcoal\Model\ServiceProvider\ModelServiceProvider
  • Charcoal\Source\AbstractSource
  • Charcoal\Source\Database\DatabaseFilter
  • Charcoal\Source\Database\DatabaseOrder
  • Charcoal\Source\Database\DatabasePagination
  • Charcoal\Source\DatabaseSource
  • Charcoal\Source\DatabaseSourceConfig
  • Charcoal\Source\Filter
  • Charcoal\Source\Order
  • Charcoal\Source\Pagination
  • Charcoal\Source\SourceConfig
  • Charcoal\Validator\AbstractValidator
  • Charcoal\Validator\ValidatorResult

Interfaces

  • Charcoal\Model\CollectionInterface
  • Charcoal\Model\DescribableInterface
  • Charcoal\Model\MetadataInterface
  • Charcoal\Model\ModelInterface
  • Charcoal\Source\DatabaseSourceInterface
  • Charcoal\Source\FilterInterface
  • Charcoal\Source\OrderInterface
  • Charcoal\Source\PaginationInterface
  • Charcoal\Source\SourceInterface
  • Charcoal\Source\StorableInterface
  • Charcoal\Validator\ValidatableInterface
  • Charcoal\Validator\ValidatorInterface

Traits

  • Charcoal\Model\DescribableTrait
  • Charcoal\Source\StorableTrait
  • Charcoal\Validator\ValidatableTrait
  • Overview
  • Namespace
  • Class
  1: <?php
  2: 
  3: namespace Charcoal\Model\Service;
  4: 
  5: use ArrayAccess;
  6: use Exception;
  7: use LogicException;
  8: use InvalidArgumentException;
  9: 
 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;
 18: 
 19: /**
 20:  * Load a single model from its source, of from cache.
 21:  *
 22:  * Use the magic methods to load automatically from __call and __get; this allows
 23:  * for easy integration in templating engines like Mustache.
 24:  *
 25:  * > This object is immutable.
 26:  */
 27: final class ModelLoader implements ArrayAccess
 28: {
 29:     /**
 30:      * The object type.
 31:      *
 32:      * The base model (FQCN) to load objects of.
 33:      *
 34:      * @var string
 35:      */
 36:     private $objType;
 37: 
 38:     /**
 39:      * The object key.
 40:      *
 41:      * The model property name, or SQL column name, to load objects by.
 42:      *
 43:      * @var string
 44:      */
 45:     private $objKey;
 46: 
 47:     /**
 48:      * The model factory.
 49:      *
 50:      * @var FactoryInterface
 51:      */
 52:     private $factory;
 53: 
 54:     /**
 55:      * The PSR-6 caching service.
 56:      *
 57:      * @var CacheItemPoolInterface
 58:      */
 59:     private $cachePool;
 60: 
 61:     /**
 62:      * Construct a Model Loader with the dependencies
 63:      *
 64:      * # Required Dependencies
 65:      *
 66:      * - `obj_type`
 67:      * - `factory`
 68:      * - `cache`
 69:      *
 70:      * # Optional Dependencies
 71:      *
 72:      * - `obj_key`
 73:      *
 74:      * @param array $data Loader dependencies.
 75:      */
 76:     public function __construct(array $data)
 77:     {
 78:         $this->setObjType($data['obj_type']);
 79:         $this->setFactory($data['factory']);
 80:         $this->setCachePool($data['cache']);
 81: 
 82:         if (isset($data['obj_key'])) {
 83:             $this->setObjKey($data['obj_key']);
 84:         }
 85:     }
 86: 
 87:     // Magic Methods
 88:     // =============================================================================================
 89: 
 90:     /**
 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
 96:      */
 97:     public function __call($ident, $args = null)
 98:     {
 99:         unset($args);
100: 
101:         return $this->load($ident);
102:     }
103: 
104:     /**
105:      * Retrieve an object by its key.
106:      *
107:      * @param  string|integer $ident The object identifier to load.
108:      * @return ModelInterface
109:      */
110:     public function __get($ident)
111:     {
112:         return $this->load($ident);
113:     }
114: 
115:     /**
116:      * Determine if an object exists by its key.
117:      *
118:      * @todo   Needs implementation
119:      * @param  string $ident The object identifier to lookup.
120:      * @return boolean
121:      */
122:     public function __isset($ident)
123:     {
124:         return true;
125:     }
126: 
127:     /**
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.
132:      * @return void
133:      */
134:     public function __unset($ident)
135:     {
136:         throw new LogicException(
137:             'Can not unset value on a loader'
138:         );
139:     }
140: 
141:     // Satisfies ArrayAccess
142:     // =============================================================================================
143: 
144:     /**
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.
150:      * @return boolean
151:      */
152:     public function offsetExists($ident)
153:     {
154:         return true;
155:     }
156: 
157:     /**
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
163:      */
164:     public function offsetGet($ident)
165:     {
166:         return $this->load($ident);
167:     }
168: 
169:     /**
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.
176:      * @return void
177:      */
178:     public function offsetSet($ident, $obj)
179:     {
180:         throw new LogicException(
181:             'Can not set value on a loader'
182:         );
183:     }
184: 
185:     /**
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.
191:      * @return void
192:      */
193:     public function offsetUnset($ident)
194:     {
195:         throw new LogicException(
196:             'Can not unset value on a loader'
197:         );
198:     }
199: 
200:     // =============================================================================================
201: 
202:     /**
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
212:      */
213:     public function load($ident, $useCache = true, $reloadObj = false)
214:     {
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;
237:     }
238: 
239:     /**
240:      * Load an objet from its soure.
241:      *
242:      * @param  string|integer $ident The object identifier to load.
243:      * @return ModelInterface
244:      */
245:     private function loadFromSource($ident)
246:     {
247:         $obj = $this->factory->create($this->objType);
248:         if ($this->objKey) {
249:             $obj->loadFrom($this->objKey, $ident);
250:         } else {
251:             $obj->load($ident);
252:         }
253: 
254:         return $obj;
255:     }
256: 
257:     /**
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.
280:      * @return ModelLoader Chainable
281:      */
282:     private function setObjType($objType)
283:     {
284:         if (!is_string($objType)) {
285:             throw new InvalidArgumentException(
286:                 'Can not set model loader object type: not a string'
287:             );
288:         }
289: 
290:         $this->objType = $objType;
291:         return $this;
292:     }
293: 
294:     /**
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.
299:      * @return ModelLoader Chainable
300:      */
301:     private function setObjKey($objKey)
302:     {
303:         if (empty($objKey) && !is_numeric($objKey)) {
304:             $this->objKey = null;
305:             return $this;
306:         }
307: 
308:         if (!is_string($objKey)) {
309:             throw new InvalidArgumentException(
310:                 'Can not set model loader object type: not a string'
311:             );
312:         }
313: 
314:         $this->objKey = $objKey;
315:         return $this;
316:     }
317: 
318:     /**
319:      * Set the model factory.
320:      *
321:      * @param  FactoryInterface $factory The factory to create models.
322:      * @return ModelLoader Chainable
323:      */
324:     private function setFactory(FactoryInterface $factory)
325:     {
326:         $this->factory = $factory;
327:         return $this;
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:     }
341: }
342: 
API documentation generated by ApiGen