1: <?php
2:
3: namespace Charcoal\Source;
4:
5: use Exception;
6: use InvalidArgumentException;
7:
8:
9: use Psr\Log\LoggerAwareInterface;
10: use Psr\Log\LoggerAwareTrait;
11:
12:
13: use Charcoal\Config\ConfigurableInterface;
14: use Charcoal\Config\ConfigurableTrait;
15:
16:
17: use Charcoal\Model\ModelInterface;
18:
19: use Charcoal\Source\SourceConfig;
20: use Charcoal\Source\SourceInterface;
21: use Charcoal\Source\Filter;
22: use Charcoal\Source\FilterInterface;
23: use Charcoal\Source\Order;
24: use Charcoal\Source\OrderInterface;
25: use Charcoal\Source\Pagination;
26: use Charcoal\Source\PaginationInterface;
27:
28: 29: 30:
31: abstract class AbstractSource implements
32: SourceInterface,
33: ConfigurableInterface,
34: LoggerAwareInterface
35: {
36: use ConfigurableTrait;
37: use LoggerAwareTrait;
38:
39: 40: 41:
42: private $model = null;
43:
44: 45: 46:
47: private $properties = [];
48:
49: 50: 51: 52:
53: protected $filters = [];
54:
55: 56: 57: 58:
59: protected $orders = [];
60:
61: 62: 63: 64:
65: protected $pagination = null;
66:
67: 68: 69: 70:
71: public function __construct($dependencies)
72: {
73: $this->setLogger($dependencies['logger']);
74: }
75:
76: 77: 78: 79: 80:
81: public function reset()
82: {
83: $this->properties = [];
84: $this->filters = [];
85: $this->orders = [];
86: $this->pagination = null;
87: return $this;
88: }
89:
90: 91: 92: 93: 94: 95:
96: public function setData(array $data)
97: {
98: foreach ($data as $prop => $val) {
99: $func = [$this, $this->setter($prop)];
100: if (is_callable($func)) {
101: call_user_func($func, $val);
102: unset($data[$prop]);
103: } else {
104: $this->{$prop} = $val;
105: }
106: }
107: return $this;
108: }
109:
110: 111: 112: 113: 114: 115:
116: public function setModel(ModelInterface $model)
117: {
118: $this->model = $model;
119: return $this;
120: }
121:
122: 123: 124: 125: 126: 127:
128: public function model()
129: {
130: if ($this->model === null) {
131: throw new Exception(
132: 'No model set.'
133: );
134: }
135: return $this->model;
136: }
137:
138: 139: 140:
141: public function hasModel()
142: {
143: return ($this->model !== null);
144: }
145:
146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156:
157: public function setProperties(array $properties)
158: {
159: $this->properties = [];
160: foreach ($properties as $p) {
161: $this->addProperty($p);
162: }
163: return $this;
164: }
165:
166: 167: 168:
169: public function properties()
170: {
171: return $this->properties;
172: }
173:
174: 175: 176: 177: 178:
179: public function addProperty($property)
180: {
181: if (!is_string($property)) {
182: throw new InvalidArgumentException(
183: 'Property must be a string.'
184: );
185: }
186: if ($property=='') {
187: throw new InvalidArgumentException(
188: 'Property can not be empty.'
189: );
190: }
191: $this->properties[] = $property;
192: return $this;
193: }
194:
195: 196: 197: 198:
199: public function setFilters(array $filters)
200: {
201: $this->filters = [];
202: foreach ($filters as $f) {
203: $this->addFilter($f);
204: }
205: return $this;
206: }
207:
208: 209: 210:
211: public function filters()
212: {
213: return $this->filters;
214: }
215:
216: 217: 218: 219: 220: 221: 222: 223: 224: 225: 226: 227: 228: 229: 230: 231: 232:
233: public function addFilter($param, $val = null, array $options = null)
234: {
235: if ($param instanceof FilterInterface) {
236: $filter = $param;
237: } elseif (is_array($param)) {
238: $filter = $this->createFilter();
239: $filter->setData($param);
240: } elseif (is_string($param) && $val !== null) {
241: $filter = $this->createFilter();
242: $filter->setProperty($param);
243: $filter->setVal($val);
244: if (is_array($options)) {
245: $filter->setData($options);
246: }
247: } else {
248: throw new InvalidArgumentException(
249: 'Parameter must be an array or a property ident.'
250: );
251: }
252:
253: if ($this->hasModel()) {
254: $property = $filter->property();
255: if ($property) {
256: $p = $this->model()->p($property);
257: if ($p) {
258: if ($p->l10n()) {
259: $filter->setProperty($p->l10nIdent());
260: }
261:
262: if ($p->multiple()) {
263: $filter->setOperator('FIND_IN_SET');
264: }
265: }
266: }
267: }
268:
269: $this->filters[] = $filter;
270:
271: return $this;
272: }
273:
274: 275: 276:
277: protected function createFilter()
278: {
279: $filter = new Filter();
280: return $filter;
281: }
282:
283: 284: 285: 286:
287: public function setOrders(array $orders)
288: {
289: $this->orders = [];
290: foreach ($orders as $o) {
291: $this->addOrder($o);
292: }
293: return $this;
294: }
295:
296: 297: 298:
299: public function orders()
300: {
301: return $this->orders;
302: }
303:
304: 305: 306: 307: 308: 309: 310:
311: public function addOrder($param, $mode = 'asc', array $orderOptions = null)
312: {
313: if ($param instanceof OrderInterface) {
314: $order = $param;
315: } elseif (is_array($param)) {
316: $order = $this->createOrder();
317: $order->setData($param);
318: } elseif (is_string($param)) {
319: $order = $this->createOrder();
320: $order->setProperty($param);
321: $order->setMode($mode);
322: if (isset($orderOptions['values'])) {
323: $order->setValues($orderOptions['values']);
324: }
325: } else {
326: throw new InvalidArgumentException(
327: 'Parameter must be an OrderInterface object or a property ident.'
328: );
329: }
330:
331: if ($this->hasModel()) {
332: $property = $order->property();
333: if ($property) {
334: $p = $this->model()->p($property);
335: if ($p) {
336: if ($p->l10n()) {
337: $order->setProperty($p->l10nIdent());
338: }
339: }
340: }
341: }
342:
343: $this->orders[] = $order;
344:
345: return $this;
346: }
347:
348: 349: 350:
351: protected function createOrder()
352: {
353: $order = new Order();
354: return $order;
355: }
356:
357: 358: 359: 360: 361:
362: public function setPagination($param)
363: {
364: if ($param instanceof PaginationInterface) {
365: $this->pagination = $param;
366: } elseif (is_array($param)) {
367: $pagination = $this->createPagination();
368: $pagination->setData($param);
369: $this->pagination = $pagination;
370: } else {
371: throw new InvalidArgumentException(
372: 'Can not set pagination, invalid argument.'
373: );
374: }
375: return $this;
376: }
377:
378: 379: 380: 381: 382: 383: 384: 385:
386: public function pagination()
387: {
388: if ($this->pagination === null) {
389: $this->pagination = $this->createPagination();
390: }
391: return $this->pagination;
392: }
393:
394: 395: 396:
397: protected function createPagination()
398: {
399: $pagination = new Pagination();
400: return $pagination;
401: }
402:
403: 404: 405: 406: 407:
408: public function setPage($page)
409: {
410: if (!is_numeric($page)) {
411: throw new InvalidArgumentException(
412: 'Page must be an integer.'
413: );
414: }
415: $this->pagination()->setPage((int)$page);
416: return $this;
417: }
418:
419: 420: 421:
422: public function page()
423: {
424: return $this->pagination()->page();
425: }
426:
427: 428: 429: 430: 431:
432: public function setNumPerPage($num)
433: {
434: if (!is_numeric($num)) {
435: throw new InvalidArgumentException(
436: 'Num must be an integer.'
437: );
438: }
439: $this->pagination()->setNumPerPage((int)$num);
440: return $this;
441: }
442:
443: 444: 445:
446: public function numPerPage()
447: {
448: return $this->pagination()->numPerPage();
449: }
450:
451: 452: 453: 454: 455: 456:
457: public function createConfig(array $data = null)
458: {
459: $config = new SourceConfig();
460: if (is_array($data)) {
461: $config->merge($data);
462: }
463: return $config;
464: }
465:
466: 467: 468: 469: 470:
471: abstract public function loadItem($ident, StorableInterface $item = null);
472:
473: 474: 475: 476:
477: abstract public function loadItems(StorableInterface $item = null);
478:
479: 480: 481: 482: 483: 484:
485: abstract public function saveItem(StorableInterface $item);
486:
487: 488: 489: 490: 491: 492: 493:
494: abstract public function updateItem(StorableInterface $item, array $properties = null);
495:
496: 497: 498: 499: 500: 501:
502: abstract public function deleteItem(StorableInterface $item = null);
503:
504: 505: 506: 507: 508: 509: 510:
511: protected function getter($key, $case = 'camel')
512: {
513: $getter = $key;
514:
515: if ($case == 'camel') {
516: return $this->camelize($getter);
517: } elseif ($case == 'pascal') {
518: return $this->pascalize($getter);
519: } else {
520: return $getter;
521: }
522: }
523:
524: 525: 526: 527: 528: 529: 530:
531: protected function setter($key, $case = 'camel')
532: {
533: $setter = 'set_'.$key;
534:
535: if ($case == 'camel') {
536: return $this->camelize($setter);
537: } elseif ($case == 'pascal') {
538: return $this->pascalize($setter);
539: } else {
540: return $setter;
541: }
542: }
543:
544: 545: 546: 547: 548: 549:
550: private function camelize($str)
551: {
552: return lcfirst($this->pascalize($str));
553: }
554:
555: 556: 557: 558: 559: 560:
561: private function pascalize($str)
562: {
563: return implode('', array_map('ucfirst', explode('_', $str)));
564: }
565: }
566: