1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
12:
13: namespace DcGeneral\Data;
14:
15: use DcGeneral\Exception\DcGeneralRuntimeException;
16:
17: 18: 19: 20: 21: 22: 23:
24: class DefaultDataProvider implements DataProviderInterface
25: {
26: 27: 28: 29: 30:
31: protected $strSource = null;
32:
33: 34: 35: 36: 37: 38: 39:
40: protected $objDatabase = null;
41:
42: 43: 44:
45: public function __construct()
46: {
47: $this->objDatabase = \Database::getInstance();
48: }
49:
50: 51: 52: 53: 54:
55: public function setBaseConfig(array $arrConfig)
56: {
57:
58: if (!isset($arrConfig['source']))
59: {
60: throw new DcGeneralRuntimeException('Missing table name.');
61: }
62:
63: $this->strSource = $arrConfig['source'];
64: }
65:
66: 67: 68:
69: public function getEmptyConfig()
70: {
71: return DefaultConfig::init();
72: }
73:
74: 75: 76:
77: public function getEmptyModel()
78: {
79: $objModel = new DefaultModel();
80: $objModel->setProviderName($this->strSource);
81: return $objModel;
82: }
83:
84: 85: 86:
87: public function getEmptyCollection()
88: {
89: return new DefaultCollection();
90: }
91:
92: 93: 94: 95: 96: 97: 98: 99: 100:
101: protected function buildFieldQuery($objConfig)
102: {
103: $strFields = '*';
104:
105: if ($objConfig->getIdOnly())
106: {
107: $strFields = 'id';
108: }
109: elseif (!is_null($objConfig->getFields()))
110: {
111: $strFields = implode(', ', $objConfig->getFields());
112:
113: if (!stristr($strFields, 'DISTINCT'))
114: {
115: $strFields = 'id, ' . $strFields;
116: }
117: }
118:
119: return $strFields;
120: }
121:
122: 123: 124: 125: 126: 127: 128: 129: 130:
131: protected function getAndOrFilter($operation, &$params)
132: {
133:
134: if (is_array($operation['childs']))
135: {
136: trigger_error('Filter array uses deprecated entry "childs", please use "children" instead.', E_USER_DEPRECATED);
137: $operation['children'] = $operation['childs'];
138: }
139:
140: $children = $operation['children'];
141:
142: if (!$children)
143: {
144: return '';
145: }
146:
147: $combine = array();
148: foreach ($children as $child)
149: {
150:
151: $combine[] = $this->calculateSubfilter($child, $params);
152: }
153:
154: return implode(sprintf(' %s ', $operation['operation']), $combine);
155: }
156:
157: 158: 159: 160: 161: 162: 163: 164: 165:
166: protected function getFilterForComparingOperator($operation, &$params)
167: {
168: $params[] = $operation['value'];
169:
170: return sprintf('(%s %s ?)', $operation['property'], $operation['operation']);
171: }
172:
173: 174: 175: 176: 177: 178: 179: 180: 181:
182: protected function getFilterForInList($operation, &$params)
183: {
184: $params = array_merge($params, array_values($operation['values']));
185: $wildcards = rtrim(str_repeat('?,', count($operation['values'])), ',');
186:
187: return sprintf('(%s IN (%s))', $operation['property'], $wildcards);
188: }
189:
190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200:
201: protected function getFilterForLike($operation, &$params)
202: {
203: $wildcards = str_replace(array('*', '?'), array('%', '_'), $operation['value']);
204: $params[] = $wildcards;
205:
206: return sprintf('(%s LIKE ?)', $operation['property'], $wildcards);
207: }
208:
209: 210: 211: 212: 213: 214: 215: 216: 217: 218: 219: 220: 221: 222: 223: 224: 225: 226: 227: 228: 229: 230: 231: 232: 233: 234: 235: 236: 237: 238: 239: 240: 241: 242:
243: protected function calculateSubfilter($arrFilter, array &$arrParams)
244: {
245: if (!is_array($arrFilter))
246: {
247: throw new DcGeneralRuntimeException('Error Processing sub filter: ' . var_export($arrFilter, true), 1);
248: }
249:
250: switch ($arrFilter['operation'])
251: {
252: case 'AND':
253: case 'OR':
254: return $this->getAndOrFilter($arrFilter, $arrParams);
255:
256: case '=':
257: case '>':
258: case '<':
259: return $this->getFilterForComparingOperator($arrFilter, $arrParams);
260:
261: case 'IN':
262: return $this->getFilterForInList($arrFilter, $arrParams);
263:
264: case 'LIKE':
265: return $this->getFilterForLike($arrFilter, $arrParams);
266:
267: default:
268: }
269:
270: throw new DcGeneralRuntimeException('Error processing filter array ' . var_export($arrFilter, true), 1);
271: }
272:
273: 274: 275: 276: 277: 278: 279: 280: 281:
282: protected function buildWhereQuery($objConfig, array &$arrParams = null)
283: {
284: $arrParams || $arrParams = array();
285:
286: $arrQuery = array();
287:
288: $arrQuery['filter'] = $this->buildFilterQuery($objConfig, $arrParams);
289:
290: $arrQuery = array_filter($arrQuery, 'strlen');
291:
292: return count($arrQuery) ? ' WHERE ' . implode(' AND ', $arrQuery) : '';
293: }
294:
295: 296: 297: 298: 299: 300: 301: 302: 303:
304: protected function buildFilterQuery($objConfig, array &$arrParams = null)
305: {
306: $arrParams || $arrParams = array();
307:
308: $strReturn = $this->calculateSubfilter(
309: array(
310: 'operation' => 'AND',
311: 'children' => $objConfig->getFilter()
312: ), $arrParams
313: );
314:
315:
316: return $strReturn ? $strReturn : '';
317: }
318:
319: 320: 321: 322: 323: 324: 325:
326: protected function buildSortingQuery($objConfig)
327: {
328: $arrSorting = $objConfig->getSorting();
329: $strReturn = '';
330: $arrFields = array();
331:
332: if (!is_null($arrSorting) && is_array($arrSorting) && count($arrSorting) > 0)
333: {
334: foreach ($arrSorting as $strField => $strOrder)
335: {
336: if (!in_array($strOrder, array(DCGE::MODEL_SORTING_ASC, DCGE::MODEL_SORTING_DESC)))
337: {
338: $strOrder = DCGE::MODEL_SORTING_ASC;
339: }
340:
341: $arrFields[] = $strField . ' ' . $strOrder;
342: }
343:
344: $strReturn .= ' ORDER BY ' . implode(', ', $arrFields);
345: }
346:
347: return $strReturn;
348: }
349:
350: 351: 352: 353: 354:
355: public function delete($item)
356: {
357: $id = null;
358: if (is_numeric($item) || is_string($item))
359: {
360: $id = $item;
361: }
362: elseif (is_object($item) && $item instanceof ModelInterface && strlen($item->getID()) != 0)
363: {
364: $id = $item->getID();
365: }
366: else
367: {
368: throw new DcGeneralRuntimeException("ID missing or given object not of type 'ModelInterface'.");
369: }
370:
371:
372: $this->insertUndo(
373: sprintf(
374: 'DELETE FROM %1$s WHERE id = %2$s',
375: $this->strSource,
376: $id
377: ),
378: sprintf(
379: 'SELECT * FROM %1$s WHERE id = %2$s',
380: $this->strSource,
381: $id
382: ),
383: $this->strSource
384: );
385:
386: $this->objDatabase
387: ->prepare(sprintf('DELETE FROM %s WHERE id=?', $this->strSource))
388: ->execute($id);
389: }
390:
391: 392: 393: 394: 395: 396: 397:
398: protected function createModelFromDatabaseResult($dbResult)
399: {
400: $objModel = $this->getEmptyModel();
401:
402:
403: foreach ($dbResult->row() as $key => $value)
404: {
405: if ($key == 'id')
406: {
407: $objModel->setID($value);
408: }
409:
410: $objModel->setProperty($key, deserialize($value));
411: }
412:
413: return $objModel;
414: }
415:
416: 417: 418:
419: public function fetch(ConfigInterface $objConfig)
420: {
421: if ($objConfig->getId() != null)
422: {
423: $strQuery = sprintf(
424: 'SELECT %s FROM %s WHERE id = ?',
425: $this->buildFieldQuery($objConfig),
426: $this->strSource
427: );
428:
429: $dbResult = $this->objDatabase
430: ->prepare($strQuery)
431: ->execute($objConfig->getId());
432: }
433: else
434: {
435: $arrParams = array();
436:
437: $query = sprintf(
438: 'SELECT %s FROM %s',
439: $this->buildFieldQuery($objConfig),
440: $this->strSource
441: );
442: $query .= $this->buildWhereQuery($objConfig, $arrParams);
443: $query .= $this->buildSortingQuery($objConfig);
444:
445:
446: $dbResult = $this->objDatabase
447: ->prepare($query)
448: ->limit(1, 0)
449: ->executeUncached($arrParams);
450: }
451:
452: if ($dbResult->numRows == 0)
453: {
454: return null;
455: }
456:
457: return $this->createModelFromDatabaseResult($dbResult);
458: }
459:
460: 461: 462:
463: public function fetchAll(ConfigInterface $objConfig)
464: {
465: $arrParams = array();
466:
467: $query = sprintf(
468: 'SELECT %s FROM %s',
469: $this->buildFieldQuery($objConfig),
470: $this->strSource
471: );
472: $query .= $this->buildWhereQuery($objConfig, $arrParams);
473: $query .= $this->buildSortingQuery($objConfig);
474:
475:
476: $objDatabaseQuery = $this->objDatabase->prepare($query);
477:
478: if ($objConfig->getAmount() != 0)
479: {
480: $objDatabaseQuery->limit($objConfig->getAmount(), $objConfig->getStart());
481: }
482:
483: $dbResult = $objDatabaseQuery->executeUncached($arrParams);
484:
485: if ($objConfig->getIdOnly())
486: {
487: return $dbResult->fetchEach('id');
488: }
489:
490: $objCollection = $this->getEmptyCollection();
491:
492: if ($dbResult->numRows == 0)
493: {
494: return $objCollection;
495: }
496:
497: while ($dbResult->next())
498: {
499: $objCollection->add($this->createModelFromDatabaseResult($dbResult));
500: }
501:
502: return $objCollection;
503: }
504:
505: 506: 507: 508: 509:
510: public function getFilterOptions(ConfigInterface $objConfig)
511: {
512: $arrProperties = $objConfig->getFields();
513: $strProperty = $arrProperties[0];
514:
515: if (count($arrProperties) <> 1)
516: {
517: throw new DcGeneralRuntimeException('objConfig must contain exactly one property to be retrieved.');
518: }
519:
520: $arrParams = array();
521:
522: $objValues = $this->objDatabase
523: ->prepare(sprintf('SELECT DISTINCT(%s) FROM %s %s',
524: $strProperty,
525: $this->strSource,
526: $this->buildWhereQuery($objConfig, $arrParams)
527: ))
528: ->executeUncached($arrParams);
529:
530: $objCollection = $this->getEmptyCollection();
531: while ($objValues->next())
532: {
533: $objNewModel = $this->getEmptyModel();
534: $objNewModel->setProperty($strProperty, $objValues->$strProperty);
535: $objCollection->add($objNewModel);
536: }
537:
538: return $objCollection;
539: }
540:
541: 542: 543:
544: public function getCount(ConfigInterface $objConfig)
545: {
546: $arrParams = array();
547:
548: $query = sprintf(
549: 'SELECT COUNT(*) AS count FROM %s',
550: $this->strSource
551: );
552: $query .= $this->buildWhereQuery($objConfig, $arrParams);
553:
554: $objCount = $this->objDatabase
555: ->prepare($query)
556: ->executeUncached($arrParams);
557:
558: return $objCount->count;
559: }
560:
561: 562: 563:
564: public function isUniqueValue($strField, $varNew, $intId = null)
565: {
566: $objUnique = $this->objDatabase
567: ->prepare('SELECT * FROM ' . $this->strSource . ' WHERE ' . $strField . ' = ? ')
568: ->executeUncached($varNew);
569:
570: if ($objUnique->numRows == 0)
571: {
572: return true;
573: }
574:
575: if (($objUnique->numRows == 1) && ($objUnique->id == $intId))
576: {
577: return true;
578: }
579:
580: return false;
581: }
582:
583: 584: 585:
586: public function resetFallback($strField)
587: {
588: $this->objDatabase->query('UPDATE ' . $this->strSource . ' SET ' . $strField . ' = \'\'');
589: }
590:
591: 592: 593:
594: public function save(ModelInterface $objItem)
595: {
596: $arrSet = array();
597:
598: foreach ($objItem as $key => $value)
599: {
600: if ($key == 'id')
601: {
602: continue;
603: }
604:
605: if (is_array($value))
606: {
607: $arrSet[$key] = serialize($value);
608: }
609: else
610: {
611: $arrSet[$key] = $value;
612: }
613: }
614:
615: if ($objItem->getID() == null || $objItem->getID() == '')
616: {
617: $objInsert = $this->objDatabase
618: ->prepare(sprintf('INSERT INTO %s %%s', $this->strSource))
619: ->set($arrSet)
620: ->execute();
621:
622: if (strlen($objInsert->insertId) != 0)
623: {
624: $objItem->setID($objInsert->insertId);
625: }
626: }
627: else
628: {
629: $this->objDatabase
630: ->prepare(sprintf('UPDATE %s %%s WHERE id=?', $this->strSource))
631: ->set($arrSet)
632: ->execute($objItem->getID());
633: }
634:
635: return $objItem;
636: }
637:
638: 639: 640:
641: public function saveEach(CollectionInterface $objItems)
642: {
643: foreach ($objItems as $value)
644: {
645: $this->save($value);
646: }
647: }
648:
649: 650: 651:
652: public function fieldExists($strField)
653: {
654: return $this->objDatabase->fieldExists($strField, $this->strSource);
655: }
656:
657: 658: 659:
660: public function getVersion($mixID, $mixVersion)
661: {
662: $objVersion = $this->objDatabase
663: ->prepare('SELECT * FROM tl_version WHERE pid=? AND version=? AND fromTable=?')
664: ->execute($mixID, $mixVersion, $this->strSource);
665:
666: if ($objVersion->numRows == 0)
667: {
668: return null;
669: }
670:
671: $arrData = deserialize($objVersion->data);
672:
673: if (!is_array($arrData) || count($arrData) == 0)
674: {
675: return null;
676: }
677:
678: $objModel = $this->getEmptyModel();
679: $objModel->setID($mixID);
680: foreach ($arrData as $key => $value)
681: {
682: if ($key == 'id')
683: {
684: continue;
685: }
686:
687: $objModel->setProperty($key, $value);
688: }
689:
690: return $objModel;
691: }
692:
693: 694: 695: 696: 697: 698: 699: 700: 701: 702:
703: public function getVersions($mixID, $blnOnlyActive = false)
704: {
705: $sql = 'SELECT tstamp, version, username, active FROM tl_version WHERE fromTable = ? AND pid = ?';
706: if ($blnOnlyActive)
707: {
708: $sql .= ' AND active = 1';
709: }
710: else
711: {
712: $sql .= ' ORDER BY version DESC';
713: }
714:
715: $arrVersion = $this->objDatabase
716: ->prepare($sql)
717: ->execute($this->strSource, $mixID)
718: ->fetchAllAssoc();
719:
720: if (count($arrVersion) == 0)
721: {
722: return null;
723: }
724:
725: $objCollection = $this->getEmptyCollection();
726:
727: foreach ($arrVersion as $versionValue)
728: {
729: $objReturn = $this->getEmptyModel();
730: $objReturn->setID($mixID);
731:
732: foreach ($versionValue as $key => $value)
733: {
734: if ($key == 'id')
735: {
736: continue;
737: }
738:
739: $objReturn->setProperty($key, $value);
740: }
741:
742: $objCollection->add($objReturn);
743: }
744:
745: return $objCollection;
746: }
747:
748: 749: 750: 751: 752: 753: 754: 755: 756:
757: public function saveVersion(ModelInterface $objModel, $strUsername)
758: {
759: $objCount = $this->objDatabase
760: ->prepare('SELECT count(*) as mycount FROM tl_version WHERE pid=? AND fromTable = ?')
761: ->execute($objModel->getID(), $this->strSource);
762:
763: $mixNewVersion = (intval($objCount->mycount) + 1);
764:
765: $mixData = $objModel->getPropertiesAsArray();
766: $mixData['id'] = $objModel->getID();
767:
768: $arrInsert = array();
769: $arrInsert['pid'] = $objModel->getID();
770: $arrInsert['tstamp'] = time();
771: $arrInsert['version'] = $mixNewVersion;
772: $arrInsert['fromTable'] = $this->strSource;
773: $arrInsert['username'] = $strUsername;
774: $arrInsert['data'] = serialize($mixData);
775:
776: $this->objDatabase->prepare('INSERT INTO tl_version %s')
777: ->set($arrInsert)
778: ->execute();
779:
780: $this->setVersionActive($objModel->getID(), $mixNewVersion);
781: }
782:
783: 784: 785: 786: 787: 788: 789: 790: 791:
792: public function setVersionActive($mixID, $mixVersion)
793: {
794: $this->objDatabase
795: ->prepare('UPDATE tl_version SET active=\'\' WHERE pid = ? AND fromTable = ?')
796: ->execute($mixID, $this->strSource);
797:
798: $this->objDatabase
799: ->prepare('UPDATE tl_version SET active = 1 WHERE pid = ? AND version = ? AND fromTable = ?')
800: ->execute($mixID, $mixVersion, $this->strSource);
801: }
802:
803: 804: 805: 806: 807: 808: 809:
810: public function getActiveVersion($mixID)
811: {
812: $objVersionID = $this->objDatabase
813: ->prepare('SELECT version FROM tl_version WHERE pid = ? AND fromTable = ? AND active = 1')
814: ->execute($mixID, $this->strSource);
815:
816: if ($objVersionID->numRows == 0)
817: {
818: return null;
819: }
820:
821: return $objVersionID->version;
822: }
823:
824: 825: 826: 827: 828: 829: 830: 831: 832:
833: public function sameModels($objModel1, $objModel2)
834: {
835: foreach ($objModel1 as $key => $value)
836: {
837: if ($key == 'id')
838: {
839: continue;
840: }
841:
842: if (is_array($value))
843: {
844: if (!is_array($objModel2->getProperty($key)))
845: {
846: return false;
847: }
848:
849: if (serialize($value) != serialize($objModel2->getProperty($key)))
850: {
851: return false;
852: }
853: }
854: elseif ($value != $objModel2->getProperty($key))
855: {
856: return false;
857: }
858: }
859:
860: return true;
861: }
862:
863: 864: 865: 866: 867: 868: 869: 870: 871: 872: 873: 874: 875:
876: protected function insertUndo($strSourceSQL, $strSaveSQL, $strTable)
877: {
878:
879: $arrResult = $this->objDatabase
880: ->prepare($strSaveSQL)
881: ->executeUncached()
882: ->fetchAllAssoc();
883:
884:
885: if (count($arrResult) == 0)
886: {
887: return;
888: }
889:
890:
891: $arrSave = array();
892: foreach ($arrResult as $value)
893: {
894: $arrSave[$strTable][] = $value;
895: }
896:
897: $strPrefix = '<span style="color:#b3b3b3; padding-right:3px;">(DC General)</span>';
898: $objUser = \BackendUser::getInstance();
899:
900:
901: $this->objDatabase
902: ->prepare('INSERT INTO tl_undo (pid, tstamp, fromTable, query, affectedRows, data) VALUES (?, ?, ?, ?, ?, ?)')
903: ->execute(
904: $objUser->id,
905: time(),
906: $strTable,
907: $strPrefix .
908: $strSourceSQL,
909: count($arrSave[$strTable]),
910: serialize($arrSave)
911: );
912: }
913: }
914: