1: <?php defined('_JOOS_CORE') or exit();
2:
3: require_once 'database/interface.php';
4: require_once 'database/mysqli.php';
5:
6: class joosDatabase extends joosDatabaseMysqli
7: {
8: }
9:
10: /**
11: * Библиотека ORM расширения для гибкой работы с информацией в юазе данных
12: *
13: * @version 1.0
14: * @package Core\Libraries
15: * @subpackage Database
16: * @author Joostina Team <info@joostina.ru>
17: * @copyright (C) 2007-2012 Joostina Team
18: * @license MIT License http://www.opensource.org/licenses/mit-license.php
19: * Информация об авторах и лицензиях стороннего кода в составе Joostina CMS: docs/copyrights
20: *
21: * */
22: class joosModel
23: {
24: /**
25: * Название таблицы, используемой текущей моделью
26: *
27: * @var string
28: */
29: protected $_tbl;
30:
31: /**
32: * Название поля первичного ключа таблицы, чаще всего ID
33: * По данному полю производится идентификация объекта, и по правильному оно должно содержать уникальное значение
34: *
35: * @var string
36: */
37: protected $_tbl_key;
38:
39: /**
40: * Текст ошибки работы с активной моделью
41: *
42: * @var string
43: */
44: protected $_error;
45:
46: /**
47: * Объект базы данных
48: *
49: * @var joosDatabase
50: */
51: protected $_db;
52:
53: /**
54: * "Мягкое" удаление объектов БД
55: * Если в модели переопределить это значение в TRUE - то запись перед удалением будет копироваться в общесистемную корзину
56: *
57: * @var bool
58: */
59: protected $_soft_delete = FALSE;
60:
61: /**
62: * Название текущего класса модели
63: *
64: * @var string
65: */
66: protected $__obj_name;
67: protected $_validation_error_messages = array();
68:
69: /**
70: * Инициализация модели
71: *
72: * @param string $table название используемой таблицы, можно с преффиксом, например #__news
73: * @param string $key Название поля первичного ключа таблицы,
74: */
75: public function __construct($table, $key)
76: {
77: $this->_tbl = $table;
78: $this->_tbl_key = $key;
79: $this->_db = joosDatabase::instance();
80: }
81:
82: /**
83: * Возвращает назание текущей модели
84: * @return string
85: */
86: public function get_class_name()
87: {
88: return get_class($this);
89: }
90:
91: /**
92: * Аналог метода get_class_name
93: * @return string
94: */
95: public function get_model_name()
96: {
97: return $this->get_class_name();
98: }
99:
100: /**
101: * Загрушка для функции получения данных о расширенных возможностях управления данными
102: * @return array
103: */
104: public function get_extrainfo()
105: {
106: return array();
107: }
108:
109: /**
110: * Заглушка получения информации о полях
111: * @return array
112: */
113: public function get_fieldinfo()
114: {
115: return array();
116: }
117:
118: /**
119: * Заглушка получения информации о таблице модели
120: * @return array
121: */
122: public function get_tableinfo()
123: {
124: return array();
125: }
126:
127: /**
128: * Заглушка получения информации о вкладках для оформления информации
129: *
130: * @return array
131: */
132: public function get_tabsinfo()
133: {
134: return array();
135: }
136:
137: /**
138: * Заглушка получения правил валидации полей модели
139: *
140: * @return array
141: */
142: protected function get_validate_rules()
143: {
144: return array();
145: }
146:
147: /**
148: * Получение массива ошибок валидации модели
149: *
150: * @return bool|array массив ошибок или
151: */
152: public function get_validation_error_messages()
153: {
154: return count($this->_validation_error_messages) > 0 ? $this->_validation_error_messages : false;
155: }
156:
157: /**
158: * Валидация полей модели
159: *
160: * @return boolean
161: */
162: public function validate()
163: {
164: $rules = $this->get_validate_rules();
165:
166: $valid = true;
167: foreach ($rules as $rule) {
168: $message = joosValidateHelper::valid($this->$rule[0], $rule[1], (isset($rule['message']) ? $rule['message'] : false));
169: if ($message !== TRUE) {
170: $this->_validation_error_messages[$rule[0]][] = $message;
171: $valid = false;
172: }
173: ;
174: }
175:
176: return $valid;
177: }
178:
179: /**
180: * Магический метод восстановления объекта
181: * Используется при прямом кэшировании модели
182: *
183: * @param array $values - массив значений востановленного объекта
184: *
185: * @return stdClass восстановленный объект модели
186: */
187: public static function __set_state(array $values)
188: {
189: // формируем объект по сохранённым параметрам
190: $obj = new $values['__obj_name']($values['_tbl'], $values['_tbl_key']);
191: // заполняем сохранёнными параметрами настоящие поля модели
192: $obj->bind($values);
193:
194: return $obj;
195: }
196:
197: /**
198: * Подготовка модели к кэшированию
199: * @return stdClass подготовленный к кэшированию объект
200: */
201: public function to_cache()
202: {
203: $obj = clone $this;
204: // удаляем ненужную ссылку на ресурс базы данных и стек ошибок
205: unset($obj->_db, $obj->_error);
206: // сохраняем оригинальное название модели
207: $obj->__obj_name = get_class($obj);
208:
209: return $obj;
210: }
211:
212: /**
213: * Возвращает название ключевого поя текущей модели
214: * @return string
215: */
216: public function get_key_field()
217: {
218: return $this->_tbl_key;
219: }
220:
221: /**
222: * Получение массива публичных свойств - полей текущей модели
223: * @staticvar string $cache статичная переменная для внутреннеего кеширования свойств
224: * @return array
225: */
226: public function get_public_properties()
227: {
228: static $cache = null;
229:
230: if ($cache===null) {
231: $cache = array();
232: foreach (get_class_vars(get_class($this)) as $key => $val) {
233: if (substr($key, 0, 1) != '_') {
234: $cache[] = $key;
235: }
236: }
237: }
238:
239: return $cache;
240: }
241:
242: /**
243: * Очищает значения публоичных свойств модели от HTML тэгов
244: * Пример $this->filter( array('desc','extra') );
245: *
246: * @param array $ignoreList массив названий полей модели, которые НЕ требуется очистить от HTML кода
247: */
248: public function filter(array $ignoreList = null)
249: {
250: $ignore = is_array($ignoreList);
251:
252: $filter = joosInputFilter::instance();
253: foreach ($this->get_public_properties() as $k) {
254:
255: if ($ignore && in_array($k, $ignoreList)) {
256:
257: continue;
258: }
259: $this->$k = $filter->process($this->$k);
260: }
261: }
262:
263: /**
264: * Получение текста ошибки при работе с текущей моделью
265: * @return string
266: */
267: public function get_errors()
268: {
269: return $this->_error;
270: }
271:
272: /**
273: * Получение значения поля
274: *
275: * @param string $_property название поля
276: *
277: * @return string значение поля
278: */
279: public function get($_property)
280: {
281: return isset($this->$_property) ? $this->$_property : null;
282: }
283:
284: /**
285: * Установка значения конкретного поля модели
286: *
287: * @param string $_property название модели
288: * @param string $_value значение поля для установки
289: */
290: public function set($_property, $_value)
291: {
292: $this->$_property = $_value;
293: }
294:
295: /**
296: * Сброс значения полей активной модели
297: *
298: * @param string $value значение, устанавливаемое во все поля активной модели
299: */
300: public function reset($value = null)
301: {
302: $keys = $this->get_public_properties();
303: foreach ($keys as $k) {
304:
305: $this->$k = $value;
306: }
307: }
308:
309: /**
310: * Заполнение значения полей модели значениями ассоциативного массива
311: *
312: * @param array $array двумерный массив "название поля"=>"значение поля"
313: * @param string $ignore название аттрибута для игнорирования
314: *
315: * @return boolean результат заполнения
316: */
317: public function bind(array $array, $ignore = '')
318: {
319: return $this->_db->bind_array_to_object($array, $this, $ignore);
320: }
321:
322: /**
323: * Загрузка данных в модель непосредственно из БД по значению ключевого поля
324: * В случае успешного выполнения заполняет поля модели значениями из БД выбранными по ключевому полю
325: *
326: * @param integer $oid значение уникального ключевого поля, по которому необходимо делать выборку в БД
327: *
328: * @return boolean результат заполнения свойств модели
329: */
330: public function load($oid)
331: {
332: // сброс установок для обнуления назначенных ранее свойств объекта ( проблема с isset($obj->id) )
333: $this->reset();
334:
335: $query = 'SELECT * FROM ' . $this->_tbl . ' WHERE ' . $this->_tbl_key . ' = ' . $this->_db->get_quoted($oid);
336: $result = $this->_db->set_query($query)->load_object($this);
337:
338: $events_name = 'model.on_load.' . $this->get_class_name();
339: joosEvents::has_events($events_name) ? joosEvents::fire_events($events_name, $result, $this) : null;
340:
341: return $result;
342: }
343:
344: /**
345: * Загрузка данных в модель непосредственно из БД по значению произвольного поля
346: * В случае успешного выполнения заполняет поля модели значениями первого результата из БД выбранными по указанному
347: *
348: * @param string $field название произвольного поля модели
349: * @param string $value значение произвольного поля модели
350: *
351: * @return boolean результат заполнения свойств модели
352: */
353: public function load_by_field($field, $value)
354: {
355: $this->reset();
356:
357: $query = 'SELECT * FROM ' . $this->_db->get_name_quote($this->_tbl) . ' WHERE ' . $this->_db->get_name_quote($field) . ' = ' . $this->_db->get_quoted($value);
358:
359: return $this->_db->set_query($query, 0, 1)->load_object($this);
360: }
361:
362: /**
363: * Сохранение свойств модели в БД
364: * Производит непосредственно запись в БД значений заполненных полей модели. При этом сами свойства должны быть указаны ранее, методом bind, либо set, либо прямого присвоения $news->title='Новость 1'
365: *
366: * @param bool $update_nulls флаг обновления неопределённых свойств
367: * @param bool $forced_Insert флаг принудительной вставки. Необходимо в случаях, когда значение ключевого поля уже задано, но всё-равно необходимо создать новую запись (например, в компоненте категорий: category_id известно, но в таблице `categories_details` нужно создать запись с этим ключом )
368: *
369: * @return boolean результат сохранения модели
370: */
371: public function store($update_nulls = false, $forced_Insert = false)
372: {
373: $k = $this->_tbl_key;
374:
375: $this->before_store();
376:
377: if ((isset($this->$k) && $this->$k != 0) && !$forced_Insert) {
378:
379: // дата последней модификации
380: if (property_exists($this, 'modified_at') && $this->modified_at == null) {
381: $this->modified_at = JCURRENT_SERVER_TIME;
382: }
383:
384: $this->before_update();
385: $ret = $this->_db->update_object($this->_tbl, $this, $this->_tbl_key, $update_nulls);
386: $this->after_update();
387: } else {
388:
389: // дата создания объекта
390: if (property_exists($this, 'created_at') && $this->created_at == null) {
391: $this->created_at = JCURRENT_SERVER_TIME;
392: }
393:
394: $this->before_insert();
395: $ret = $this->_db->insert_object($this->_tbl, $this, $this->_tbl_key);
396: $this->after_insert();
397: }
398:
399: if (!$ret) {
400: return false;
401: } else {
402: $this->after_store();
403:
404: return true;
405: }
406: }
407:
408: /**
409: * Прямое обновление значения полей объекта модели
410: *
411: * @return bool результат выполнения обновления
412: */
413: public function update()
414: {
415: return (bool) $this->_db->update_object($this->_tbl, $this, $this->_tbl_key, false);
416: }
417:
418: /**
419: * Переопределяемая функция проверки правильности заполнения полей модели
420: *
421: * @return boolean результат проверки
422: */
423: public function check()
424: {
425: return true;
426: }
427:
428: /**
429: * Метод, выполняемый до обновления значений модели
430: *
431: * @return boolean
432: */
433: protected function before_update()
434: {
435: return true;
436: }
437:
438: /**
439: * Метод, выполняемый после обновления значений модели
440: *
441: * @return boolean
442: */
443: protected function after_update()
444: {
445: return true;
446: }
447:
448: /**
449: * Метод выполняемый до добавления значений модели
450: *
451: * @return boolean
452: */
453: protected function before_insert()
454: {
455: return true;
456: }
457:
458: /**
459: * Метод выполняемый после вставки значений модели
460: *
461: * @return boolean
462: */
463: protected function after_insert()
464: {
465: return true;
466: }
467:
468: /**
469: * Метод выполняемый до сохранения значений модели ( вставка / обновление )
470: *
471: * @return boolean
472: */
473: protected function before_store()
474: {
475: return true;
476: }
477:
478: /**
479: * Метод выполняемый после полного сохранения данных модели ( вставка / обновление )
480: *
481: * @return boolean
482: */
483: protected function after_store()
484: {
485: return true;
486: }
487:
488: /**
489: * Метод выполняемый до удаления конкретной записи модели
490: *
491: * @return boolean
492: */
493: protected function before_delete()
494: {
495: return true;
496: }
497:
498: /**
499: * Метод выполняемый после удаления конкретной записи модели
500: *
501: * @return boolean
502: */
503: protected function after_delete()
504: {
505: return true;
506: }
507:
508: /**
509: * Удаление записи в БД по значению ключевого поля
510: * Производит непосредственное удаление записи из БД
511: *
512: * @param mixed $oid значение ключевого поля
513: *
514: * @return boolean результат удаления
515: */
516: public function delete($oid)
517: {
518: $k = $this->_tbl_key;
519:
520: if ($oid) {
521: $this->$k = (int) $oid;
522: }
523:
524: $this->before_delete();
525:
526: // активируем "мягкое удаление", т.е. сохраняем копию в корзине
527: if ($this->_soft_delete) {
528: joosTrash::add($this);
529: }
530:
531: $query = "DELETE FROM $this->_tbl WHERE $this->_tbl_key = " . $this->_db->get_quoted($this->$k);
532: $this->_db->set_query($query);
533:
534: if ($this->_db->query()) {
535: $this->after_delete();
536:
537: return true;
538: } else {
539: return false;
540: }
541: }
542:
543: /**
544: * Удаление неограниченного числа записей в БД через указание массива значений ключевого, либо произвольного поля
545: * Производит непосредственное удаление записей из БД принимая массив значений вида array(1,15,16,22)
546: *
547: * @param array $oid массив значений ключевого
548: * @param string|bool $key название ключевого поля, по умолчанию - название ключевого поля текущей модели
549: * @param string|bool $table название таблицы, в которой необходимо произвести удаление, по умолчанию - таблица текущей модели
550: *
551: * @return boolean результат удаления записей
552: */
553: public function delete_array(array $oid = array(), $key = false, $table = false)
554: {
555: $key = $key ? $key : $this->_tbl_key;
556: $table = $table ? $table : $this->_tbl;
557:
558: $table = $this->_db->get_name_quote($table);
559:
560: // "мягкое" удаление объектов
561: if ($this->_soft_delete) {
562:
563: $obj = clone $this;
564: foreach ($oid as $cur_id) {
565: $obj->load($cur_id);
566: joosTrash::add($obj);
567: $obj->reset();
568: }
569: unset($obj);
570: }
571:
572: $obj = clone $this;
573: foreach ($oid as &$cur_id) {
574: $obj->{$key} = $cur_id;
575: $obj->before_delete();
576: $cur_id = $this->_db->get_quoted($cur_id);
577: }
578:
579: $query = "DELETE FROM $table WHERE $key IN (" . implode(',', $oid) . ')';
580:
581: if ($this->_db->set_query($query)->query()) {
582: return true;
583: } else {
584: return false;
585: }
586: }
587:
588: /**
589: * Удаление элементов в БД через указание произвольных условий
590: *
591: * @param array $params массив параметров для формирования условий удаления
592: *
593: * @return boolean результат удаления
594: */
595: public function delete_list(array $params = array())
596: {
597: $where = isset($params['where']) ? 'WHERE ' . $params['where'] . "\n" : '';
598:
599: $this->_db->set_query("DELETE FROM $this->_tbl " . $where);
600:
601: if ($this->_db->query()) {
602: return true;
603: } else {
604: return false;
605: }
606: }
607:
608: /**
609: * Копирование неограниченного числа записей в БД через указание массива значений ключевого, либо произвольного поля
610: *
611: * @param array $oid массив значений ключевого
612: * @param string|bool $key название ключевого поля, по умолчанию - название ключевого поля текущей модели
613: * @param string|bool $table название таблицы, в которой необходимо произвести копирование, по умолчанию - таблица текущей модели
614: *
615: * @return boolean результат копирования записей
616: */
617: public function copy_array(array $oid = array(), $key = false, $table = false)
618: {
619: $key = $key ? $key : $this->_tbl_key;
620: $table = $table ? $table : $this->_tbl;
621:
622: $table = $this->_db->get_name_quote($table);
623:
624: $query = "SELECT * FROM $table WHERE $key IN (" . implode(',', $oid) . ')';
625: $rows = $this->_db->set_query($query)->load_object_list();
626:
627: foreach ($rows as $row) {
628:
629: $row->$key = null;
630: $this->_db->insert_object($this->_tbl, $row, $this->_tbl_key);
631: }
632:
633: return true;
634: }
635:
636: /**
637: * Сохранение свойств модели в БД
638: *
639: * @param array $source массив свойств название поля=>значение поля для заполнения свойств модели ( см. self::bind )
640: * @param string $ignore название аттрибута для игнорирования
641: *
642: * @return boolean результат сохранения
643: */
644: public function save(array $source, $ignore = '')
645: {
646: if ($source && !$this->bind($source, $ignore)) {
647: return false;
648: }
649: if (!$this->check()) {
650: return false;
651: }
652:
653: if (!$this->store()) {
654: return false;
655: }
656:
657: $this->_error = '';
658:
659: return true;
660: }
661:
662: /**
663: * @param array $cid
664: * @param int $state
665: *
666: * @return bool
667: */
668: public function set_state_group(array $cid = null, $state = 1)
669: {
670: if (count($cid) < 1) {
671: $this->_error = 'Ничего не было выбрано';
672:
673: return false;
674: }
675:
676: $cids = $this->_tbl_key . '=' . implode(' OR ' . $this->_tbl_key . '=', $cid);
677:
678: $query = "UPDATE $this->_tbl SET state = " . (int) $state . " WHERE ($cids)";
679:
680: if (!$this->_db->set_query($query)->query()) {
681: return false;
682: }
683:
684: $this->_error = '';
685:
686: return true;
687: }
688:
689: /**
690: * Булево изменение содержимого указанного столбца. Используется для смены статуса элемента
691: * Меняет значение указанного поля на противопложное
692: *
693: * @param string $field_name название свойства модели для изменения на противоположное
694: *
695: * @return boolean результат смены значения поля
696: */
697: public function change_state($field_name)
698: {
699: $key = $this->{$this->_tbl_key};
700:
701: return $this->_db->set_query("UPDATE `$this->_tbl` SET `$field_name` = !`$field_name` WHERE $this->_tbl_key = $key", 0, 1)->query();
702: }
703:
704: /**
705: * Возвращает число записей в таблице БД активной модели
706: *
707: * @param string $where дополнительное условие для подсчета числа записей, например "WHERE state=1"
708: *
709: * @return int число записей
710: */
711: public function count($where = '')
712: {
713: $sql = "SELECT count(*) FROM $this->_tbl " . $where;
714:
715: return $this->_db->set_query($sql)->load_result();
716: }
717:
718: /**
719: * Возвращает сумму по определенному полю
720: *
721: * @param string $field поле, по которому считаем
722: * @param string $where дополнительное условие
723: *
724: * @return int число записей
725: */
726: public function sum($field, $where = '')
727: {
728: $sql = "SELECT sum($field) FROM $this->_tbl " . $where;
729:
730: return $this->_db->set_query($sql)->load_result();
731: }
732:
733: /**
734: * Возвращает массив результатов выборки
735: *
736: * @param array $params массив параметров для уточнее области выборки результата
737: * <pre>
738: * select - список поле для выборки, по умолчанию * (все поля)
739: * where - условие WHERE для выборки
740: * join - данные о объединённой выборке с использованием сторонних таблиц
741: * group - название поля для группировки результата, пример - " user_id "
742: * order - название поля и направление сортировки результата, пример - " id DESC ", либо "id DESC, title ASC"
743: * offset - смещение для выборки результата, по умолчанию - 0
744: * limit - лимит выборки для результата, по молчанию - 0, т.е. ВСЕ записи
745: * key - название ключевого поля, для использования в качестве ключа ассоциативного массива результатов. По умолчанию использует ключевое поле модели. key=>FALSE если необходимо сделать простой массив ( 0=>array(),1=>array() )
746: * <pre>
747: *
748: * @return array ассоциативный или обычный массив результатов
749: */
750: public function get_list(array $params = array())
751: {
752: $tbl_key = isset($params['key']) ? $params['key'] : null;
753:
754: return $this->_db->set_query($this->get_query_list($params))->load_object_list($tbl_key);
755: }
756:
757: /**
758: * Функция, формирующая SQL-запрос для метода get_list()
759: *
760: * @param array $params Те же параметры, что и для get_list
761: * @return string SQL-запрос
762: */
763: private function get_query_list($params)
764: {
765: $select = isset($params['select']) ? $params['select'] . "\n" : '*';
766: $where = isset($params['where']) ? 'WHERE ' . $params['where'] . "\n" : '';
767: $join = isset($params['join']) ? $params['join'] . "\n" : '';
768: $group = isset($params['group']) ? 'GROUP BY ' . $params['group'] . "\n" : '';
769: $order = isset($params['order']) ? 'ORDER BY ' . $params['order'] . "\n" : '';
770: $pseudonim = isset($params['pseudonim']) ? ' AS ' . $params['pseudonim'] . ' ' : '';
771:
772: $limit = isset($params['limit']) ? 'LIMIT ' . $params['limit'] . "\n" : '';
773: $limit = (isset($params['limit']) && isset($params['offset'])) ? 'LIMIT ' . $params['offset'] . ',' . $params['limit'] . "\n" : $limit;
774:
775: return "SELECT $select FROM $this->_tbl $pseudonim $join " . $where . $group . $order . $limit;
776: }
777:
778: /**
779: * Версия метода get_list с кэшированием
780: *
781: * @param array $params Те же параметры, что и для get_list
782: * @param int $cache_time Время жизни кэша
783: * @return array Закэшированное значение
784: */
785: public function get_list_cache(array $params = array(), $cache_time = 86400)
786: {
787: $cache = joosCache::instance();
788: $key = md5($this->get_query_list($params));
789:
790: if (($value = $cache->get($key)) === NULL) {
791:
792: $value = $this->get_list($params);
793: $cache->set($key, $value, $cache_time);
794: }
795:
796: return $value;
797: }
798:
799: /**
800: * Возвращает ассоциативный двумерный массив возможных значений модели
801: *
802: * @param array $key_val - массив array( 'key'=>'название поля - ключа','value'=>'название поля - значения' ). По умолчанияю key=>id, value=>title
803: * @param array $params массив параметров для уточнее области выборки результата
804: * <pre>
805: * select - список поле для выборки, по умолчанию key,value (поля указанные в $key_val)
806: * where - условие WHERE для выборки
807: * order - название поля и направление сортировки результата, пример - " id DESC ", либо "id DESC, title ASC"
808: * offset - смещение для выборки результата, по умолчанию - 0
809: * limit - лимит выборки для результата, по молчанию - 0, т.е. ВСЕ записи
810: * table - название таблицы, из которой необходимо сделать выборку. По умолчанию - таблица текущей модели
811: * <pre>
812: *
813: * @return array - ассоциативный массив результата
814: */
815: public function get_selector(array $key_val = array(), array $params = array())
816: {
817: $key = isset($key_val['key']) ? $key_val['key'] : 'id';
818: $value = isset($key_val['value']) ? $key_val['value'] : 'title';
819:
820: $select = isset($params['select']) ? $params['select'] : $key . ',' . $value;
821: $where = isset($params['where']) ? 'WHERE ' . $params['where'] : '';
822: $order = isset($params['order']) ? ' ORDER BY ' . $params['order'] : '';
823: $offset = isset($params['offset']) ? (int) $params['offset'] : 0;
824: $limit = isset($params['limit']) ? (int) $params['limit'] : 0;
825: $tablename = isset($params['table']) ? $params['table'] : $this->_tbl;
826:
827: $opts = $this->_db->set_query("SELECT $select FROM $tablename " . $where . $order, $offset, $limit)->load_assoc_list();
828:
829: $return = array();
830: foreach ($opts as $opt) {
831: $return[$opt[$key]] = $opt[$value];
832: }
833:
834: return $return;
835: }
836:
837: // отношение один-ко-многим, список выбранных значений из многих
838: public function get_select_one_to_many($table_values, $table_keys, $key_parent, $key_children, array $params = array())
839: {
840: $select = isset($params['select']) ? $params['select'] : 't_val.*';
841: $where = isset($params['where']) ? 'WHERE ' . $params['where'] : "WHERE t_key.$key_parent = $this->{$this->_tbl_key} ";
842: $order = isset($params['order']) ? 'ORDER BY ' . $params['order'] : '';
843: $offset = isset($params['offset']) ? (int) $params['offset'] : 0;
844: $limit = isset($params['limit']) ? (int) $params['limit'] : 0;
845: $join = isset($params['join']) ? $params['join'] : 'LEFT JOIN';
846:
847: $sql = "SELECT $select FROM $table_values AS t_val $join $table_keys AS t_key ON t_val.id=t_key.$key_children $where $order";
848:
849: return $this->_db->set_query($sql, $offset, $limit)->load_assoc_list('id');
850: }
851:
852: // сохранение значение одного ко многим
853: public function save_one_to_many($name_table_keys, $key_name, $value_name, $key_value, array $values)
854: {
855: if ($key_value == null || $key_value == '') {
856: return false;
857: }
858:
859: //сначала чистим все предыдущие связи
860: $this->_db->set_query("DELETE FROM $name_table_keys WHERE $key_name=$key_value ")->query();
861:
862: // фомируем массив сохраняемых значений
863: $vals = array();
864: foreach ($values as $value) {
865: $vals[] = " ($key_value, $value ) ";
866: }
867:
868: if (count($vals) == 0) {
869: return true;
870: }
871:
872: $values = implode(',', $vals);
873:
874: $sql = "INSERT IGNORE INTO $name_table_keys ( $key_name,$value_name ) VALUES $values";
875:
876: return $this->_db->set_query($sql)->query();
877: }
878:
879: // селектор выбора отношений один-ко-многим
880: public function get_one_to_many_selectors($name, $table_values, $table_keys, $key_parent, $key_children, array $selected_ids = array(), array $params = array())
881: {
882: $params['select'] = isset($params['select']) ? $params['select'] : 't_val.id, t_val.title';
883: $params['select_children'] = isset($params['select_children']) ? $params['select_children'] : array();
884:
885: $childrens = $this->get_selector($params['select_children'], array('table' => $table_values));
886:
887: $rets = array();
888: foreach ($childrens as $key => $value) {
889: $el_id = $name . $key;
890: $checked = (bool) isset($selected_ids[$key]);
891: $rets[] = '<label class="checkbox">';
892: $rets[] = joosHtml::checkbox($name . '[]', $key, $checked, 'id="' . $el_id . '" ');
893: $rets[] = $value;
894: $rets[] = '</label>';
895: //$rets[] = forms::label($el_id, $value);
896: }
897:
898: return implode("\n\t", $rets);
899: }
900:
901: /**
902: * Загрузка значение текущей модели через указание произвольных свойств модели
903: *
904: * @param array $params массив параметров для условий выборки
905: *
906: * @return boolean результат поиска и загрузки значений в свойства текущей модели
907: */
908: public function find(array $params = array('select' => '*'))
909: {
910: $query = $this->get_find_query_from_params($params);
911:
912: return $query === false ? false : $this->_db->set_query($query)->load_object($this);
913: }
914:
915: /**
916: * Построение строки запроса из параметров метода find();
917: *
918: * @param $params массив параметров запроса
919: * @return bool|string
920: */
921: private function get_find_query_from_params($params)
922: {
923: $fmtsql = "SELECT {$params['select']} FROM $this->_tbl WHERE %s";
924: $tmp = array();
925: foreach (get_object_vars($this) as $k => $v) {
926:
927: if (is_array($v) or is_object($v) or $k[0] == '_' or $v===null) {
928: continue;
929: }
930:
931: $v = strval($v);
932:
933: if ($v == '') {
934: $val = "''";
935: } else {
936: $val = $this->_db->get_quoted($v);
937: }
938:
939: $tmp[] = $this->_db->get_name_quote($k) . '=' . $val;
940: }
941:
942: // если в параметрах не было ни одного заполненного поля
943: if (count($tmp) == 0) {
944:
945: return false;
946: }
947:
948: return sprintf($fmtsql, implode(' AND ', $tmp));
949: }
950:
951: /**
952: * Кэширующая обертка над фунцией find
953: *
954: * @param array $params Параметры к методу find
955: * @param int $cache_time Время кэширования
956: * @return boolean Найденный объект
957: *
958: * @todo проверить обоснованность использования $find_result
959: */
960: public function find_cache(array $params = array('select' => '*'), $cache_time = 86400)
961: {
962: $cache = joosCache::instance();
963: $key = md5($this->get_find_query_from_params($params));
964:
965: if (($value = $cache->get($key)) === NULL) {
966:
967: $find_result = $this->find($params);
968: //в кэше надо хранить не только значение, но еще и результат поиска
969: $cache->set($key, array($find_result, $this->to_cache()), $cache_time);
970: } else {
971:
972: //достаем объект и мэппим его поля на текущий объект
973: list($find_result, $obj) = $value;
974: foreach ($obj as $k => $v) {
975:
976: $this->$k = $v;
977: }
978: }
979:
980: return $find_result;
981: }
982:
983: /**
984: * Поиск записей, удовлетворяющих указаным свойствам объекта
985: *
986: * @param array $params массив параметров для уточнения области поиска записей
987: * <pre>
988: * select - список поле для выборки, по умолчанию * (все поля)
989: * where - условие WHERE для выборки
990: * order - название поля и направление сортировки результата, пример - " id DESC ", либо "id DESC, title ASC"
991: * offset - смещение для выборки результата, по умолчанию - 0
992: * limit - лимит выборки для результата, по молчанию - 0, т.е. ВСЕ записи
993: * key - название ключевого поля, для использования в качестве ключа ассоциативного массива результатов. По умолчанию использует ключевое поле модели. key=>FALSE если необходимо сделать простой массив ( 0=>array(),1=>array() )
994: * <pre>
995: *
996: * @return array ассоциативный массив результата поиска
997: */
998: public function find_all(array $params = array())
999: {
1000: $def_param = array('select' => '*');
1001: $params += $def_param;
1002: $fmtsql = "SELECT {$params['select']} FROM $this->_tbl WHERE %s";
1003: $fmtsql .= isset($params['order']) ? ' ORDER BY ' . $params['order'] : '';
1004:
1005: $tmp = array();
1006:
1007: if (isset($params['where'])) {
1008: $tmp[] = $params['where'];
1009: }
1010:
1011: foreach (get_object_vars($this) as $k => $v) {
1012:
1013: if (is_array($v) or is_object($v) or $k[0] == '_' or empty($v)) {
1014: continue;
1015: }
1016:
1017: $v = strval($v);
1018:
1019: if ($v == '') {
1020: $val = "''";
1021: } else {
1022: $val = $this->_db->get_quoted($v);
1023: }
1024: $tmp[] = $this->_db->get_name_quote($k) . '=' . $val;
1025: }
1026: $tmp = count($tmp) > 0 ? $tmp : array('true');
1027:
1028: $offset = isset($params['offset']) ? (int) $params['offset'] : 0;
1029: $limit = isset($params['limit']) ? (int) $params['limit'] : 0;
1030:
1031: $tbl_key = isset($params['key']) ? $params['key'] : $this->_tbl_key;
1032:
1033: return $this->_db->set_query(sprintf($fmtsql, implode(' AND ', $tmp)), $offset, $limit)->load_object_list($tbl_key);
1034: }
1035:
1036: /**
1037: * Возвращает максимальное значение по заданному полю
1038: *
1039: * @param string $name Имя поля
1040: *
1041: * @return integer максимальное значение
1042: */
1043: public function get_max_by_field($name)
1044: {
1045: $query = 'SELECT ' . $name . ' AS max FROM ' . $this->_tbl . ' ORDER BY ' . $name . ' DESC';
1046:
1047: return $this->_db->set_query($query)->load_result();
1048: }
1049:
1050: /**
1051: * Вставка массива значений в таблицу текущего объекта
1052: *
1053: * @example
1054: * $values = array(
1055: * 0 => array(
1056: * 'counter' => 111,
1057: * 'name' => 'первая запись',
1058: * ),
1059: * 1 => array(
1060: * 'name' => ' вторая запись ',
1061: * 'counter' => 2222
1062: * ),
1063: * 2 => array(
1064: * 'name' => ' третья запись',
1065: * 'counter' => 123456
1066: * ),
1067: * );
1068: *
1069: * @param array $array_values
1070: * @return bool результат вставки массива
1071: */
1072: public function insert_array(array $array_values)
1073: {
1074: return $this->_db->insert_array($this->_tbl, $this, $array_values);
1075: }
1076:
1077: // @todo обновление неограниченного числа записей
1078: public function update_all( $attributes, $condition, $params )
1079: {
1080: }
1081:
1082: }
1083:
1084: /**
1085: * Обработка ошибок работы с базой данных
1086: *
1087: */
1088: class joosDatabaseException extends joosException
1089: {
1090: public function __construct($message = '', array $params = array())
1091: {
1092: if (JDEBUG) {
1093: // при включенной отладке покажем полные данные о ошибке
1094: parent::__construct(strtr($message, $params));
1095: } else {
1096: // при выключенной отладке - общую информацию о системе
1097: joosPages::error_database(strtr($message, $params));
1098:
1099: }
1100:
1101: }
1102:
1103: }
1104: