1: <?php
2: 3: 4: 5: 6: 7:
8:
9: namespace Wei;
10:
11: 12: 13: 14: 15: 16: 17:
18: class Record extends Base implements \ArrayAccess, \IteratorAggregate, \Countable
19: {
20:
21: const SELECT = 0;
22: const DELETE = 1;
23: const UPDATE = 2;
24:
25:
26: const STATE_DIRTY = 0;
27: const STATE_CLEAN = 1;
28:
29: 30: 31: 32: 33:
34: protected $table;
35:
36: 37: 38: 39: 40:
41: protected $fullTable;
42:
43: 44: 45: 46: 47: 48: 49:
50: protected $fields = array();
51:
52: 53: 54: 55: 56:
57: protected $primaryKey = 'id';
58:
59: 60: 61: 62: 63:
64: protected $isNew = true;
65:
66: 67: 68: 69: 70:
71: protected $data = array();
72:
73: 74: 75: 76: 77:
78: protected $isChanged = false;
79:
80: 81: 82: 83: 84:
85: protected $changedData = array();
86:
87: 88: 89: 90: 91:
92: protected $isDestroyed = false;
93:
94: 95: 96: 97: 98:
99: protected $detached = false;
100:
101: 102: 103: 104: 105:
106: protected $loaded = false;
107:
108: 109: 110: 111: 112:
113: protected $isColl;
114:
115: 116: 117: 118: 119:
120: protected $sqlParts = array(
121: 'select' => array(),
122: 'from' => null,
123: 'join' => array(),
124: 'set' => array(),
125: 'where' => null,
126: 'groupBy' => array(),
127: 'having' => null,
128: 'orderBy' => array(),
129: 'limit' => null,
130: 'offset' => null,
131: );
132:
133: 134: 135: 136: 137: 138:
139: protected $indexBy;
140:
141: 142: 143:
144: protected $sql;
145:
146: 147: 148: 149: 150:
151: protected $params = array();
152:
153: 154: 155: 156: 157:
158: protected $paramTypes = array();
159:
160: 161: 162: 163: 164:
165: protected $type = self::SELECT;
166:
167: 168: 169: 170: 171:
172: protected $state = self::STATE_CLEAN;
173:
174: 175: 176: 177: 178:
179: protected $afterLoad;
180:
181: 182: 183: 184: 185:
186: protected $afterFind;
187:
188: 189: 190: 191: 192:
193: protected $beforeSave;
194:
195: 196: 197: 198: 199:
200: protected $afterSave;
201:
202: 203: 204: 205: 206:
207: protected $beforeCreate;
208:
209: 210: 211: 212: 213:
214: protected $afterCreate;
215:
216: 217: 218: 219: 220:
221: protected $beforeUpdate;
222:
223: 224: 225: 226: 227:
228: protected $afterUpdate;
229:
230: 231: 232: 233: 234:
235: protected $beforeDestroy;
236:
237: 238: 239: 240: 241:
242: protected $afterDestroy;
243:
244: 245: 246: 247: 248:
249: public function __construct(array $options = array())
250: {
251: parent::__construct($options);
252:
253:
254: $this->changedData = array();
255: $this->isChanged = false;
256:
257: $this->triggerCallback('afterLoad');
258: }
259:
260: 261: 262: 263: 264:
265: public function getTable()
266: {
267: return $this->table;
268: }
269:
270: 271: 272: 273: 274: 275:
276: public function setTable($table)
277: {
278: return $this->from($table);
279: }
280:
281: 282: 283: 284: 285: 286:
287: public function toArray($returnFields = array())
288: {
289: if (!$this->isColl) {
290: if (!$returnFields) {
291: $fields = $this->getFields();
292: return $this->data + array_combine($fields, array_pad(array(), count($fields), null));
293: } else {
294: return array_intersect_key($this->data, array_flip($returnFields));
295: }
296: } else {
297: $data = array();
298:
299: foreach ($this->data as $key => $record) {
300: $data[$key] = $record->toArray($returnFields);
301: }
302: return $data;
303: }
304: }
305:
306: 307: 308: 309: 310: 311:
312: public function toJson($returnFields = array())
313: {
314: return json_encode($this->toArray($returnFields));
315: }
316:
317: 318: 319: 320: 321: 322:
323: public function fromArray($data)
324: {
325: foreach ($data as $key => $value) {
326: $this->set($key, $value);
327: }
328: return $this;
329: }
330:
331: 332: 333: 334: 335: 336:
337: protected function setData(array $data)
338: {
339: return $this->fromArray($data);
340: }
341:
342: 343: 344: 345: 346: 347:
348: public function save($data = array())
349: {
350:
351: $data && $this->fromArray($data);
352:
353:
354: if (!$this->isColl) {
355:
356:
357: if ($this->isDestroyed) {
358: return $this;
359: }
360:
361:
362: if ($this->detached) {
363: $this->db->delete($this->table, array($this->primaryKey => $this->data[$this->primaryKey]));
364: $this->isDestroyed = true;
365: return $this;
366: }
367:
368:
369: $isNew = $this->isNew;
370: $this->triggerCallback('beforeSave');
371: $this->triggerCallback($isNew ? 'beforeCreate' : 'beforeUpdate');
372:
373:
374: if ($isNew) {
375:
376: if (array_key_exists($this->primaryKey, $this->data) && !$this->data[$this->primaryKey]) {
377: unset($this->data[$this->primaryKey]);
378: }
379:
380: $this->db->insert($this->table, $this->data);
381: $this->isNew = false;
382:
383:
384: if (!isset($this->data[$this->primaryKey]) || !$this->data[$this->primaryKey]) {
385:
386: $sequence = sprintf('%s_%s_seq', $this->fullTable, $this->primaryKey);
387: $this->data[$this->primaryKey] = $this->db->lastInsertId($sequence);
388: }
389:
390: } else {
391: if ($this->isChanged) {
392: $data = array_intersect_key($this->data, $this->changedData);
393: $this->db->update($this->table, $data, array(
394: $this->primaryKey => $this->data[$this->primaryKey]
395: ));
396: }
397: }
398:
399:
400: $this->changedData = array();
401: $this->isChanged = false;
402:
403:
404: $this->triggerCallback($isNew ? 'afterCreate' : 'afterUpdate');
405: $this->triggerCallback('afterSave');
406:
407: } else {
408: foreach ($this->data as $record) {
409: $record->save();
410: }
411: }
412:
413: return $this;
414: }
415:
416: 417: 418: 419: 420: 421:
422: public function destroy($conditions = false)
423: {
424: $this->andWhere($conditions);
425: !$this->loaded && $this->loadData(0);
426:
427: if (!$this->isColl) {
428: $this->triggerCallback('beforeDestroy');
429: $this->db->delete($this->table, array($this->primaryKey => $this->data[$this->primaryKey]));
430: $this->isDestroyed = true;
431: $this->triggerCallback('afterDestroy');
432: } else {
433: foreach ($this->data as $record) {
434: $record->destroy();
435: }
436: }
437:
438: return $this;
439: }
440:
441: 442: 443: 444: 445:
446: public function reload()
447: {
448: $this->data = (array)$this->db->select($this->table, $this->get($this->primaryKey));
449: $this->changedData = array();
450: $this->isChanged = false;
451: $this->triggerCallback('afterLoad');
452: return $this;
453: }
454:
455: 456: 457: 458: 459: 460: 461: 462:
463: public function saveColl($data, $extraData = array(), $sort = false)
464: {
465: if (!is_array($data)) {
466: return $this;
467: }
468:
469:
470: $newData = array();
471: foreach ($this->data as $key => $record) {
472: unset($this->data[$key]);
473:
474: if ($record instanceof $this) {
475: $newData[$record['id']] = $record;
476: }
477: }
478: $this->data = $newData;
479:
480:
481: foreach ($data as $index => $row) {
482: if (!array_filter($row)) {
483: unset($data[$index]);
484: }
485: }
486:
487:
488: $existIds = array();
489: foreach ($data as $row) {
490: if (isset($row['id']) && $row['id'] !== null) {
491: $existIds[] = $row['id'];
492: }
493: }
494:
495: foreach ($this->data as $key => $record) {
496: if (!in_array($record['id'], $existIds)) {
497: $record->destroy();
498: unset($this->data[$key]);
499: }
500: }
501:
502:
503: foreach ($data as $index => $row) {
504: if ($sort) {
505: $row[$sort] = $index;
506: }
507: if (isset($row['id']) && isset($this->data[$row['id']])) {
508: $this->data[$row['id']]->fromArray($row);
509: } else {
510: $this[] = $this->db($this->table)->fromArray($extraData + $row);
511: }
512: }
513:
514:
515: return $this->save();
516: }
517:
518: 519: 520: 521: 522: 523: 524:
525: public function get($name)
526: {
527:
528: if (!$this->isColl && !in_array($name, $this->getFields())) {
529: throw new \InvalidArgumentException(sprintf(
530: 'Field "%s" not found in record class "%s"',
531: $name,
532: get_class($this)
533: ));
534: }
535: return isset($this->data[$name]) ? $this->data[$name] : null;
536: }
537:
538: 539: 540: 541: 542: 543: 544: 545:
546: public function set($name, $value = null)
547: {
548: $this->loaded = true;
549:
550:
551: if (!$this->data && $value instanceof static) {
552: $this->isColl = true;
553: }
554:
555: if (!$this->isColl) {
556: if (in_array($name, $this->getFields())) {
557: $this->changedData[$name] = isset($this->data[$name]) ? $this->data[$name] : null;
558: $this->data[$name] = $value;
559: $this->isChanged = true;
560: }
561: } else {
562: if (!$value instanceof static) {
563: throw new \InvalidArgumentException('Value for collection must be an instance of Wei\Record');
564: } else {
565:
566: if ($name === null) {
567: $this->data[] = $value;
568: } else {
569: $this->data[$name] = $value;
570: }
571: }
572: }
573: return $this;
574: }
575:
576: 577: 578: 579: 580: 581: 582:
583: public function setAll($name, $value)
584: {
585: foreach ($this->data as $record) {
586: $record[$name] = $value;
587: }
588: return $this;
589: }
590:
591: 592: 593: 594: 595: 596:
597: public function getAll($name)
598: {
599: $data = array();
600: foreach ($this->data as $record) {
601: $data[] = $record[$name];
602: }
603: return $data;
604: }
605:
606: 607: 608: 609: 610: 611:
612: public function remove($name)
613: {
614: if (!$this->isColl) {
615: if (array_key_exists($name, $this->data)) {
616: $this->data[$name] = null;
617: }
618: } else {
619: unset($this->data[$name]);
620: }
621: return $this;
622: }
623:
624: 625: 626: 627: 628: 629: 630:
631: public function incr($name, $offset)
632: {
633: $this[$name] = (object)($name . ' + ' . $offset);
634: return $this;
635: }
636:
637: 638: 639: 640: 641: 642: 643:
644: public function decr($name, $offset)
645: {
646: $this[$name] = (object)($name . ' - ' . $offset);
647: return $this;
648: }
649:
650: 651: 652: 653: 654: 655:
656: public function detach($bool = true)
657: {
658: $this->detached = (bool)$bool;
659: return $this;
660: }
661:
662: 663: 664: 665: 666:
667: public function isNew()
668: {
669: return $this->isNew;
670: }
671:
672: 673: 674: 675: 676: 677:
678: public function isChanged($field = null)
679: {
680: if ($field) {
681: return array_key_exists($field, $this->changedData);
682: }
683: return $this->isChanged;
684: }
685:
686: 687: 688: 689: 690:
691: public function isDestroyed()
692: {
693: return $this->isDestroyed;
694: }
695:
696: 697: 698: 699: 700:
701: public function isDetached()
702: {
703: return $this->detached;
704: }
705:
706: public function isColl()
707: {
708: return $this->isColl;
709: }
710:
711: 712: 713: 714: 715:
716: public function isLoaded()
717: {
718: return $this->loaded;
719: }
720:
721: 722: 723: 724: 725: 726:
727: public function setPrimaryKey($primaryKey)
728: {
729: $this->primaryKey = $primaryKey;
730: return $this;
731: }
732:
733: 734: 735: 736: 737:
738: public function getPrimaryKey()
739: {
740: return $this->primaryKey;
741: }
742:
743: 744: 745: 746: 747:
748: public function getFields()
749: {
750: if (empty($this->fields)) {
751: $this->fields = $this->db->getTableFields($this->fullTable, true);
752: }
753: return $this->fields;
754: }
755:
756: 757: 758: 759: 760: 761:
762: public function getChangedData($field = null)
763: {
764: if ($field) {
765: return isset($this->changedData[$field]) ? $this->changedData[$field] : null;
766: }
767: return $this->changedData;
768: }
769:
770: 771: 772: 773: 774:
775: public function getState()
776: {
777: return $this->state;
778: }
779:
780: 781: 782: 783: 784:
785: public function execute()
786: {
787: if ($this->type == self::SELECT) {
788: $this->loaded = true;
789: return $this->db->fetchAll($this->getSql(), $this->params, $this->paramTypes);
790: } else {
791: return $this->db->executeUpdate($this->getSql(), $this->params, $this->paramTypes);
792: }
793: }
794:
795: 796: 797: 798: 799: 800:
801: public function find($conditions = false)
802: {
803: $this->isColl = false;
804: $data = $this->fetch($conditions);
805: if ($data) {
806: $this->data = $data + $this->data;
807: $this->triggerCallback('afterFind');
808: return $this;
809: } else {
810: return false;
811: }
812: }
813:
814: 815: 816: 817: 818: 819: 820:
821: public function findOrInit($conditions, $data = array())
822: {
823: if (!$this->find($conditions)) {
824:
825: $this->isNew = true;
826:
827: !is_array($conditions) && $conditions = array($this->primaryKey => $conditions);
828:
829:
830: if (is_object($data) && method_exists($data, 'toArray')) {
831: $data = $data->toArray();
832: }
833:
834: $this->fromArray($conditions + $data);
835: }
836: return $this;
837: }
838:
839: 840: 841: 842: 843: 844: 845:
846: public function findOne($conditions = false)
847: {
848: if ($this->find($conditions)) {
849: return $this;
850: } else {
851: throw new \Exception('Record not found', 404);
852: }
853: }
854:
855: 856: 857: 858: 859: 860:
861: public function findAll($conditions = false)
862: {
863: $this->isColl = true;
864: $data = $this->fetchAll($conditions);
865:
866: $records = array();
867: foreach ($data as $key => $row) {
868:
869: $records[$key] = $this->db->init($this->table, $row, false);
870: $records[$key]->triggerCallback('afterFind');
871: }
872:
873: $this->data = $records;
874: return $this;
875: }
876:
877: 878: 879: 880: 881: 882:
883: public function findById($value)
884: {
885: return $this->find(array($this->primaryKey => $value));
886: }
887:
888: 889: 890: 891: 892: 893:
894: public function findOneById($value)
895: {
896: return $this->findOne(array($this->primaryKey => $value));
897: }
898:
899: 900: 901: 902: 903: 904: 905:
906: public function findOrInitById($value, $data = array())
907: {
908: return $this->findOrInit(array($this->primaryKey => $value), $data);
909: }
910:
911: 912: 913: 914: 915: 916:
917: public function fetch($conditions = false)
918: {
919: $this->andWhere($conditions);
920: $this->limit(1);
921: $data = $this->execute();
922: return $data ? $data[0] : false;
923: }
924:
925: 926: 927: 928: 929: 930:
931: public function fetchColumn($conditions = false)
932: {
933: $data = $this->fetch($conditions);
934: return $data ? current($data) : false;
935: }
936:
937: 938: 939: 940: 941: 942:
943: public function fetchAll($conditions = false)
944: {
945: $this->andWhere($conditions);
946: $data = $this->execute();
947: if ($this->indexBy) {
948: $data = $this->executeIndexBy($data, $this->indexBy);
949: }
950: return $data;
951: }
952:
953: 954: 955: 956: 957: 958: 959:
960: public function count($conditions = false, $count = '1')
961: {
962: $this->andWhere($conditions);
963:
964: $select = $this->sqlParts['select'];
965: $this->select('COUNT(' . $count . ')');
966: $count = (int)$this->db->fetchColumn($this->getSqlForSelect(true), $this->params);
967: $this->sqlParts['select'] = $select;
968:
969: return $count;
970: }
971:
972: 973: 974: 975: 976: 977:
978: public function countBySubQuery($conditions = false)
979: {
980: $this->andWhere($conditions);
981: return (int)$this->db->fetchColumn($this->getSqlForCount(), $this->params);
982: }
983:
984: 985: 986: 987: 988:
989: public function length()
990: {
991: return $this->size();
992: }
993:
994: 995: 996: 997: 998:
999: public function size()
1000: {
1001: $this->loadData(0);
1002: return count($this->data);
1003: }
1004:
1005: 1006: 1007: 1008: 1009: 1010:
1011: public function update($set = array())
1012: {
1013: $this->add('set', $set, true);
1014: $this->type = self::UPDATE;
1015: return $this->execute();
1016: }
1017:
1018: 1019: 1020: 1021: 1022: 1023:
1024: public function delete($conditions = false)
1025: {
1026: $this->andWhere($conditions);
1027: $this->type = self::DELETE;
1028: return $this->execute();
1029: }
1030:
1031: 1032: 1033: 1034: 1035: 1036:
1037: public function offset($offset)
1038: {
1039: $offset < 0 && $offset = 0;
1040: return $this->add('offset', (int)$offset);
1041: }
1042:
1043: 1044: 1045: 1046: 1047: 1048:
1049: public function limit($limit)
1050: {
1051: $limit < 1 && $limit = 1;
1052: return $this->add('limit', (int)$limit);
1053: }
1054:
1055: 1056: 1057: 1058: 1059: 1060:
1061: public function page($page)
1062: {
1063: $limit = $this->getSqlPart('limit');
1064: if (!$limit) {
1065: $limit = 10;
1066: $this->add('limit', $limit);
1067: }
1068: return $this->offset(($page - 1) * $limit);
1069: }
1070:
1071: 1072: 1073: 1074: 1075: 1076: 1077:
1078: public function select($select = null)
1079: {
1080: $this->type = self::SELECT;
1081:
1082: if (empty($select)) {
1083: return $this;
1084: }
1085:
1086: $selects = is_array($select) ? $select : func_get_args();
1087: return $this->add('select', $selects, false);
1088: }
1089:
1090: 1091: 1092: 1093: 1094: 1095:
1096: public function addSelect($select = null)
1097: {
1098: $this->type = self::SELECT;
1099:
1100: if (empty($select)) {
1101: return $this;
1102: }
1103:
1104: $selects = is_array($select) ? $select : func_get_args();
1105: return $this->add('select', $selects, true);
1106: }
1107:
1108: 1109: 1110: 1111: 1112: 1113:
1114: public function from($from)
1115: {
1116: $pos = strpos($from, ' ');
1117: if (false !== $pos) {
1118: $this->table = substr($from, 0, $pos);
1119: } else {
1120: $this->table = $from;
1121: }
1122: $this->fullTable = $this->db->getTable($this->table);
1123: return $this->add('from', $this->db->getTable($from));
1124: }
1125:
1126: 1127: 1128: 1129: 1130: 1131: 1132:
1133: public function join($table, $on = null)
1134: {
1135: return $this->innerJoin($table, $on);
1136: }
1137:
1138: 1139: 1140: 1141: 1142: 1143: 1144:
1145: public function innerJoin($table, $on = null)
1146: {
1147: return $this->add('join', array('type' => 'inner', 'table' => $table, 'on' => $on), true);
1148: }
1149:
1150: 1151: 1152: 1153: 1154: 1155: 1156:
1157: public function leftJoin($table, $on = null)
1158: {
1159: return $this->add('join', array('type' => 'left', 'table' => $table, 'on' => $on), true);
1160: }
1161:
1162: 1163: 1164: 1165: 1166: 1167: 1168:
1169: public function rightJoin($table, $on = null)
1170: {
1171: return $this->add('join', array('type' => 'right', 'table' => $table, 'on' => $on), true);
1172: }
1173:
1174: 1175: 1176: 1177: 1178: 1179: 1180: 1181: 1182: 1183: 1184: 1185: 1186: 1187: 1188: 1189:
1190: public function where($conditions, $params = array(), $types = array())
1191: {
1192: if ($conditions === false) {
1193: return $this;
1194: } else {
1195: $conditions = $this->processCondition($conditions, $params, $types);
1196: return $this->add('where', $conditions);
1197: }
1198: }
1199:
1200: 1201: 1202: 1203: 1204: 1205: 1206: 1207: 1208:
1209: public function andWhere($conditions, $params = array(), $types = array())
1210: {
1211: if ($conditions === false) {
1212: return $this;
1213: } else {
1214: $conditions = $this->processCondition($conditions, $params, $types);
1215: return $this->add('where', $conditions, true, 'AND');
1216: }
1217: }
1218:
1219: 1220: 1221: 1222: 1223: 1224: 1225: 1226: 1227:
1228: public function orWhere($conditions, $params = array(), $types = array())
1229: {
1230: $conditions = $this->processCondition($conditions, $params, $types);
1231: return $this->add('where', $conditions, true, 'OR');
1232: }
1233:
1234: 1235: 1236: 1237: 1238: 1239: 1240:
1241: public function groupBy($groupBy)
1242: {
1243: if (empty($groupBy)) {
1244: return $this;
1245: }
1246:
1247: $groupBy = is_array($groupBy) ? $groupBy : func_get_args();
1248: return $this->add('groupBy', $groupBy, false);
1249: }
1250:
1251: 1252: 1253: 1254: 1255: 1256:
1257: public function addGroupBy($groupBy)
1258: {
1259: if (empty($groupBy)) {
1260: return $this;
1261: }
1262: $groupBy = is_array($groupBy) ? $groupBy : func_get_args();
1263: return $this->add('groupBy', $groupBy, true);
1264: }
1265:
1266: 1267: 1268: 1269: 1270: 1271: 1272: 1273: 1274:
1275: public function having($conditions, $params = array(), $types = array())
1276: {
1277: $conditions = $this->processCondition($conditions, $params, $types);
1278: return $this->add('having', $conditions);
1279: }
1280:
1281: 1282: 1283: 1284: 1285: 1286: 1287: 1288: 1289:
1290: public function andHaving($conditions, $params = array(), $types = array())
1291: {
1292: $conditions = $this->processCondition($conditions, $params, $types);
1293: return $this->add('having', $conditions, true, 'AND');
1294: }
1295:
1296: 1297: 1298: 1299: 1300: 1301: 1302: 1303: 1304:
1305: public function orHaving($conditions, $params = array(), $types = array())
1306: {
1307: $conditions = $this->processCondition($conditions, $params, $types);
1308: return $this->add('having', $conditions, true, 'OR');
1309: }
1310:
1311: 1312: 1313: 1314: 1315: 1316: 1317: 1318:
1319: public function orderBy($sort, $order = 'ASC')
1320: {
1321: return $this->add('orderBy', $sort . ' ' . ($order ? : 'ASC'), false);
1322: }
1323:
1324: 1325: 1326: 1327: 1328: 1329: 1330:
1331: public function addOrderBy($sort, $order = 'ASC')
1332: {
1333: return $this->add('orderBy', $sort . ' ' . ($order ? : 'ASC'), true);
1334: }
1335:
1336: 1337: 1338: 1339: 1340: 1341:
1342: public function desc($field)
1343: {
1344: return $this->addOrderBy($field, 'DESC');
1345: }
1346:
1347: 1348: 1349: 1350: 1351: 1352:
1353: public function asc($field)
1354: {
1355: return $this->addOrderBy($field, 'ASC');
1356: }
1357:
1358: 1359: 1360: 1361: 1362: 1363:
1364: public function indexBy($field)
1365: {
1366:
1367: if (!empty($this->data)) {
1368: $this->data = $this->executeIndexBy($this->data, $field);
1369: }
1370: $this->indexBy = $field;
1371: return $this;
1372: }
1373:
1374: 1375: 1376: 1377: 1378: 1379:
1380: protected function executeIndexBy($data, $field)
1381: {
1382: if (!$data) {
1383: return $data;
1384: }
1385:
1386: if (!array_key_exists($field, $data[0]) && !($data[0] instanceof \ArrayAccess && $data[0]->offsetExists($field))) {
1387: throw new \RuntimeException(sprintf('Index field "%s" not found in fetched data', $field));
1388: }
1389:
1390: foreach ($data as $key => $row) {
1391: $data[$row[$field]] = $row;
1392: unset($data[$key]);
1393: }
1394:
1395: return $data;
1396: }
1397:
1398: 1399: 1400: 1401: 1402: 1403:
1404: public function getSqlPart($name)
1405: {
1406: return isset($this->sqlParts[$name]) ? $this->sqlParts[$name] : false;
1407: }
1408:
1409: 1410: 1411: 1412: 1413:
1414: public function getSqlParts()
1415: {
1416: return $this->sqlParts;
1417: }
1418:
1419: 1420: 1421: 1422: 1423: 1424:
1425: public function resetSqlParts($name = null)
1426: {
1427: if (is_null($name)) {
1428: $name = array_keys($this->sqlParts);
1429: }
1430: foreach ($name as $queryPartName) {
1431: $this->resetSqlPart($queryPartName);
1432: }
1433: return $this;
1434: }
1435:
1436: 1437: 1438: 1439: 1440: 1441:
1442: public function resetSqlPart($name)
1443: {
1444: $this->sqlParts[$name] = is_array($this->sqlParts[$name]) ? array() : null;
1445: $this->state = self::STATE_DIRTY;
1446: return $this;
1447: }
1448:
1449: 1450: 1451: 1452: 1453: 1454: 1455: 1456:
1457: public function setParameter($key, $value, $type = null)
1458: {
1459: if ($type !== null) {
1460: $this->paramTypes[$key] = $type;
1461: }
1462:
1463: $this->params[$key] = $value;
1464: return $this;
1465: }
1466:
1467: 1468: 1469: 1470: 1471: 1472:
1473: public function getParameter($key)
1474: {
1475: return isset($this->params[$key]) ? $this->params[$key] : null;
1476: }
1477:
1478: 1479: 1480: 1481: 1482: 1483: 1484:
1485: public function setParameters(array $params, array $types = array())
1486: {
1487: $this->paramTypes = $types;
1488: $this->params = $params;
1489: return $this;
1490: }
1491:
1492: 1493: 1494: 1495: 1496:
1497: public function getParameters()
1498: {
1499: return $this->params;
1500: }
1501:
1502: 1503: 1504: 1505: 1506:
1507: public function getSql()
1508: {
1509: if ($this->sql !== null && $this->state === self::STATE_CLEAN) {
1510: return $this->sql;
1511: }
1512:
1513: switch ($this->type) {
1514: case self::DELETE:
1515: $this->sql = $this->getSqlForDelete();
1516: break;
1517:
1518: case self::UPDATE:
1519: $this->sql = $this->getSqlForUpdate();
1520: break;
1521:
1522: case self::SELECT:
1523: default:
1524: $this->sql = $this->getSqlForSelect();
1525: break;
1526: }
1527:
1528: $this->state = self::STATE_CLEAN;
1529:
1530: return $this->sql;
1531: }
1532:
1533: 1534: 1535: 1536: 1537: 1538:
1539: protected function getSqlForSelect($count = false)
1540: {
1541: $parts = $this->sqlParts;
1542:
1543: if (!$parts['select']) {
1544: $parts['select'] = array('*');
1545: }
1546:
1547: $query = 'SELECT ' . implode(', ', $parts['select']) . ' FROM ' . $parts['from'];
1548:
1549:
1550: foreach ($parts['join'] as $join) {
1551: $query .= ' ' . strtoupper($join['type'])
1552: . ' JOIN ' . $join['table']
1553: . ' ON ' . $join['on'];
1554: }
1555:
1556: $query .= ($parts['where'] !== null ? ' WHERE ' . ((string)$parts['where']) : '')
1557: . ($parts['groupBy'] ? ' GROUP BY ' . implode(', ', $parts['groupBy']) : '')
1558: . ($parts['having'] !== null ? ' HAVING ' . ((string)$parts['having']) : '');
1559:
1560: if (false === $count) {
1561: $query .= ($parts['orderBy'] ? ' ORDER BY ' . implode(', ', $parts['orderBy']) : '')
1562: . ($parts['limit'] !== null ? ' LIMIT ' . $parts['limit'] : '')
1563: . ($parts['offset'] !== null ? ' OFFSET ' . $parts['offset'] : '');
1564: }
1565:
1566: return $query;
1567: }
1568:
1569: 1570: 1571:
1572: protected function getSqlForCount()
1573: {
1574: return "SELECT COUNT(*) FROM (" . $this->getSqlForSelect(true) . ") wei_count";
1575: }
1576:
1577: 1578: 1579: 1580: 1581:
1582: protected function getSqlForUpdate()
1583: {
1584: $query = 'UPDATE ' . $this->sqlParts['from']
1585: . ' SET ' . implode(", ", $this->sqlParts['set'])
1586: . ($this->sqlParts['where'] !== null ? ' WHERE ' . ((string)$this->sqlParts['where']) : '');
1587: return $query;
1588: }
1589:
1590: 1591: 1592: 1593: 1594:
1595: protected function getSqlForDelete()
1596: {
1597: return 'DELETE FROM ' . $this->sqlParts['from'] . ($this->sqlParts['where'] !== null ? ' WHERE ' . ((string)$this->sqlParts['where']) : '');
1598: }
1599:
1600: 1601: 1602: 1603: 1604: 1605:
1606: public function offsetExists($offset)
1607: {
1608: $this->loadData($offset);
1609: return isset($this->data[$offset]);
1610: }
1611:
1612: 1613: 1614: 1615: 1616: 1617:
1618: public function offsetGet($offset)
1619: {
1620: $this->loadData($offset);
1621: return $this->get($offset);
1622: }
1623:
1624: 1625: 1626: 1627: 1628: 1629:
1630: public function offsetSet($offset, $value)
1631: {
1632: $this->loadData($offset);
1633: $this->set($offset, $value);
1634: }
1635:
1636: 1637: 1638: 1639: 1640:
1641: public function offsetUnset($offset)
1642: {
1643: $this->loadData($offset);
1644: $this->remove($offset);
1645: }
1646:
1647: 1648: 1649: 1650: 1651:
1652: public function getIterator()
1653: {
1654: $this->loadData(0);
1655: return new \ArrayIterator($this->data);
1656: }
1657:
1658: 1659: 1660: 1661: 1662: 1663: 1664: 1665: 1666: 1667: 1668: 1669:
1670: protected function add($sqlPartName, $sqlPart, $append = false, $type = null)
1671: {
1672: $this->isNew = false;
1673:
1674: if (!$sqlPart) {
1675: return $this;
1676: }
1677:
1678: $isArray = is_array($sqlPart);
1679: $isMultiple = is_array($this->sqlParts[$sqlPartName]);
1680:
1681: if ($isMultiple && !$isArray) {
1682: $sqlPart = array($sqlPart);
1683: }
1684:
1685: $this->state = self::STATE_DIRTY;
1686:
1687: if ($append) {
1688: if ($sqlPartName == 'where' || $sqlPartName == 'having') {
1689: if ($this->sqlParts[$sqlPartName]) {
1690: $this->sqlParts[$sqlPartName] = '(' . $this->sqlParts[$sqlPartName] . ') ' . $type . ' (' . $sqlPart . ')';
1691: } else {
1692: $this->sqlParts[$sqlPartName] = $sqlPart;
1693: }
1694: } elseif ($sqlPartName == 'orderBy' || $sqlPartName == 'groupBy' || $sqlPartName == 'select' || $sqlPartName == 'set') {
1695: foreach ($sqlPart as $part) {
1696: $this->sqlParts[$sqlPartName][] = $part;
1697: }
1698: } elseif ($isMultiple) {
1699: $this->sqlParts[$sqlPartName][] = $sqlPart;
1700: }
1701: return $this;
1702: }
1703:
1704: $this->sqlParts[$sqlPartName] = $sqlPart;
1705: return $this;
1706: }
1707:
1708: 1709: 1710: 1711: 1712: 1713: 1714: 1715:
1716: protected function processCondition($conditions, $params, $types)
1717: {
1718:
1719: if (is_numeric($conditions) || empty($conditions)) {
1720: $conditions = array($this->primaryKey => $conditions);
1721: }
1722:
1723: if (is_array($conditions)) {
1724: $where = array();
1725: $params = array();
1726: foreach ($conditions as $field => $condition) {
1727: if (is_array($condition)) {
1728: $where[] = $field . ' IN (' . implode(', ', array_pad(array(), count($condition), '?')) . ')';
1729: $params = array_merge($params, $condition);
1730: } else {
1731: $where[] = $field . " = ?";
1732: $params[] = $condition;
1733: }
1734: }
1735: $conditions = implode(' AND ', $where);
1736: }
1737:
1738: if ($params !== false) {
1739: if (is_array($params)) {
1740: $this->params = array_merge($this->params, $params);
1741: $this->paramTypes = array_merge($this->paramTypes, $types);
1742: } else {
1743: $this->params[] = $params;
1744: if ($types) {
1745: $this->paramTypes[] = $types;
1746: }
1747: }
1748: }
1749:
1750: return $conditions;
1751: }
1752:
1753: 1754: 1755: 1756: 1757:
1758: protected function loadData($offset)
1759: {
1760: if (!$this->loaded && !$this->isNew) {
1761: if (is_numeric($offset) || is_null($offset)) {
1762: $this->findAll();
1763: } else {
1764: $this->find();
1765: }
1766: }
1767: }
1768:
1769: 1770: 1771: 1772: 1773: 1774:
1775: public function filter(\Closure $fn)
1776: {
1777: $data = array_filter($this->data, $fn);
1778: return $this->db->init($this->table, $data, $this->isNew);
1779: }
1780:
1781: 1782: 1783: 1784: 1785:
1786: protected function triggerCallback($name)
1787: {
1788: $this->$name();
1789: $this->$name && call_user_func($this->$name, $this, $this->wei);
1790: }
1791:
1792: 1793: 1794:
1795: protected function afterLoad()
1796: {
1797: }
1798:
1799: 1800: 1801:
1802: protected function afterFind()
1803: {
1804: }
1805:
1806: 1807: 1808:
1809: public function beforeSave()
1810: {
1811: }
1812:
1813: 1814: 1815:
1816: public function afterSave()
1817: {
1818: }
1819:
1820: 1821: 1822:
1823: public function beforeCreate()
1824: {
1825: }
1826:
1827: 1828: 1829:
1830: public function afterCreate()
1831: {
1832: }
1833:
1834: 1835: 1836:
1837: public function beforeUpdate()
1838: {
1839: }
1840:
1841: 1842: 1843:
1844: public function afterUpdate()
1845: {
1846: }
1847:
1848: 1849: 1850:
1851: public function beforeDestroy()
1852: {
1853: }
1854:
1855: 1856: 1857:
1858: public function afterDestroy()
1859: {
1860: }
1861: }
1862: