'cat_id', // объекты где основной ключ не ID при сохранении '_sys_cat_admin' => 'cat_id']; /** * Конструктор * @param string $obj_name имя таблицы в DB * @param integer $depth текущия глубина рекурсии (для предотвращения бесконечной рекурсии) */ function __construct( $obj_name, $depth = 0 ) { global $statistica; $statistica['objects_add']++; $this->recursion_depth = ($depth+1); $this->obj_name = $obj_name; // Определяем тип объекта по имени //if ( strpos($obj_name, '_sys_') !== false ) { // All objects is local in basic mode $this->global_type = 1; //} if ($depth <= 2 ) { // Не более 3х вложений!! $this->getAttrs(); } $this->attrs_vals = array(); } /** * Получить экземпляр базы данных, связанный с объектом * @return [type] [description] */ function getDB() { global $db, $core_db; if ( $this->global_type == 1 ) { return $core_db; } else { return $db; } } /** * Pагружаем атрибуты в объект * @return [type] [description] */ private function getAttrs() { global $objects_cache; $curdb = $this->getDB(); if ( $objects_cache[$this->obj_name] ) { foreach( $objects_cache[$this->obj_name] as $name => $arr ) { $this->attrs[$name]['name'] = $name; $this->attrs[$name]['type'] = $arr['attr_type']; $this->attrs[$name]['templ'] = str_replace('[', '{', str_replace(']', '}', $arr['attr_templ'])); $this->attrs[$name]['desc'] = $arr['attr_desc']; $this->attrs[$name]['hide'] = $arr['attr_hide']; $this->attrs[$name]['mand'] = $arr['attr_mand']; $this->attrs[$name]['attr_obj'] = $this->getAttrObj( $name, $arr['attr_type'] ); } } else { $db2 = $curdb->q("SELECT * FROM `_sys_datatypes` WHERE `obj_name` = '".$this->obj_name."' AND !`_sys_deleted` ORDER BY `attr_order` ASC"); while( $db2->r() ) { $name=$db2->f('attr_name'); $objects_cache[$this->obj_name][$name] = $db2->lastRow; $this->attrs[$name]['name'] = $name; $this->attrs[$name]['type'] = $db2->f('attr_type'); $this->attrs[$name]['templ'] = str_replace('[', '{', str_replace(']', '}', $db2->f('attr_templ'))); $this->attrs[$name]['desc'] = $db2->f('attr_desc'); $this->attrs[$name]['hide'] = $db2->f('attr_hide'); $this->attrs[$name]['mand'] = $db2->f('attr_mand'); $this->attrs[$name]['attr_obj'] = $this->getAttrObj( $name, $db2->f('attr_type') ); } } } /** * Получить объект атрибута * @param string $attr_name имя атрибута * @param string $attr_type тип атрибута * @return object */ function getAttrObj( $attr_name, $attr_type ) { @list($type, $obj_link) = explode( '|', $attr_type ); $classname = 'attr_' . $type; if ( class_exists( $classname ) ) { $attr_obj = new $classname( $attr_name, $this); return $attr_obj; } else { die( 'Заявленный класс "' . $classname . '" - не определен' ); } } /** * Расширенный конструктор SQL запросов * @param string $sql_from что берём * @param string $sql_where с каким where ограничением * @param integer $show_deleted забираем ли удалённые объекты (по умолчанию нет) * @return string SQL запрос */ function getSelectEx( $sql_from ="*", $sql_where = "", $show_deleted = 0) { $sql = "SELECT " . $sql_from; $sql .= " FROM `" . $this->obj_name . "` WHERE 1 "; if (!$show_deleted && isset($this->attrs['_sys_deleted']) ) { $sql .= " AND !`".$this->obj_name."`.`_sys_deleted` "; } $sql .= $sql_where; return $sql; } /** * Конструктор SQL запросов * @param string $sql_where_in where ограничение * @param integer $show_deleted забираем ли удалённые объекты (по умолчанию нет) * @return string SQL запрос */ function getSelect($sql_where_in = "", $show_deleted = 0) { global $auth, $cat; $sql_joins = ''; /* Access deleted */ $sql_where = ''; if (!$show_deleted && isset($this->attrs['_sys_deleted']) ) { $sql_where .= " AND !`".$this->obj_name."`.`_sys_deleted` "; } /* Access Group */ if ( isset($this->attrs['access_group']) && ( $auth->user_id != _ROOT_USER_ID ) ) { if ( !$auth->is_login() ) { $sql_where .= " AND !`".$this->obj_name."`.`access_group` "; } else { $tmp_sql[] = ' !`'.$this->obj_name.'`.`access_group` '; foreach( $auth->groups as $v ) { $tmp_sql[] = ' FIND_IN_SET('.$v.',`'.$this->obj_name.'`.`access_group`) '; } if ($tmp_sql ) { $sql_where .= " AND (" . implode(" OR ", $tmp_sql ) . " ) "; } else { $sql_where .= " AND !`".$this->obj_name."`.`access_group` "; } } } /* access geo */ if ( isset($this->attrs['access_city']) && !defined('_ADMINKA_') ) { global $geoip; $sql_where .= " AND ( FIND_IN_SET(".$geoip->city_id.", `".$this->obj_name."`.`access_city` ) OR `".$this->obj_name."`.`access_city` = '' ) "; } /* Global where */ if ( $this->sql_where ) { $sql_where = $this->sql_where .' '. $sql_where; } /* incoming arg */ $sql_where .= $sql_where_in; /* SELECT */ $sql = "SELECT "; $sqls[] = " `".$this->obj_name."`.`id` "; foreach( $this->attrs as $k => $arr ) { $sqls[] = " `".$this->obj_name."`.`".$k."` "; } $sql .= implode(",", $sqls); $sql .= " FROM `" . $this->obj_name . "` " . $sql_joins . " WHERE 1 "; $sql .= $sql_where; return $sql; } /** * Загружаем данные в объект по ID * @param integer $id ID из таблицы * @param boolean $usecache Использовать ли кэш */ function byId( $id, $usecache = true ) { global $cached_objects; $this->diffed = array(); $this->save_sql = ''; if ($cached_objects[$this->obj_name][$id] && $usecache) { $this->attrs_vals = $cached_objects[$this->obj_name][$id]; $this->id = $id; } else { $curdb = $this->getDB(); $db2 = $curdb->q( $this->getSelect( ' AND `'. $this->obj_name.'`.`id` = "'.$id.'" ', 1 ) ); $this->attrs_vals = array(); $this->id = 0; if ( $db2->r() ) { foreach( $this->attrs as $k => $arr ) { $this->attrs_vals[$k] = $db2->f( $k ); } $this->id = $id; $cached_objects[$this->obj_name][$id] = $this->attrs_vals; } else { return false; } } } /** * Сохраняет объект * @param boolean $do_log логировать ли сохранение * @param boolean $delayed отложенный UPDATE/INSERT */ function save($do_log = true, $delayed = false ) { $curdb = $this->getDB(); /* только измененные атрибуты */ if ( empty( $this->diffed ) ) return false; $this->diffed = array_unique($this->diffed); foreach( $this->diffed as $key ) { if ( $this->attrs[$key] ) { $this->attrs[$key]['attr_obj']->presave(); } } foreach( $this->diffed as $key ) { $val = $this->attrs_vals[$key]; if (is_array($val)) { $val = trim_array($val); $val = implode(',', $val); } $val = db_escape_string( $val ); $update[] = ' `'.$this->obj_name.'`.`'.$key.'` = "' . $val . '" ' ; $insert[$key] = $val; } // Расширенный лог для товаров $this->itemObjLog(); if ( $this->id ) { if ($do_log) { if ($this->global_type == 1 ) { $db2 = $curdb->q('SELECT * FROM `'.$this->obj_name.'` WHERE `id` = ' . intval( $this->id ) ); if ( $db2->nr() ) { $old_d = $db2->lastRow; $new_d = $this->attrs_vals; foreach( $old_d as $k=>$v ) { if ( $k == 'id' ) continue; if ( $k == 'full' ) continue; if ( $new_d[$k] != $v ) { $s_old[$k] = $v; $s_new[$k] = $new_d[$k]; } } if ( $s_old || $s_new ) { global $auth; $db2->q("INSERT INTO `_sys_obj_log` (`datetime`,`user_id`,`obj_name`,`obj_id`,`old_data`,`user_ip`,`new_data`) VALUES ('".time()."','".$auth->user_id."','".$this->obj_name."','".$this->id."','".db_escape_string(serialize($s_old))."','".$_SERVER['REMOTE_ADDR']."','".mysql_real_escape_string(serialize($s_new))."') "); } } } } if ( $delayed ) { $sql = 'UPDATE LOW_PRIORITY `'.$this->obj_name.'` SET '; } else { $sql = 'UPDATE `'.$this->obj_name.'` SET '; } $sql .= implode( ',', $update ); if (isset($this->exceptPrimaryKeys[$this->obj_name]) ) { $saveKey = $this->exceptPrimaryKeys[$this->obj_name]; $sql .= ' WHERE `'.$this->obj_name.'`.`'.$saveKey.'` = "' . $this->get($saveKey) . '"'; } else { $sql .= ' WHERE `'.$this->obj_name.'`.`id` = "' . $this->id . '"'; } $curdb->q( $sql ); $this->save_sql = $sql; global $cached_objects; unset($cached_objects[$this->obj_name][$this->id]); } else { $sql = 'INSERT INTO `' . $this->obj_name . '` (`' . implode( '`, `', array_keys( $insert ) ) . '`) VALUES ( "' . implode( '", "', array_values( $insert ) ) . '")' ; $db2 = $curdb->q( $sql ); $this->id = $db2->insert_id(); $this->save_sql = $sql; if ($do_log) { if ($this->global_type == 1 ) { global $auth; $db2->q("INSERT INTO `_sys_obj_log` (`datetime`,`user_id`,`obj_name`,`obj_id`,`old_data`,`user_ip`,`new_data`) VALUES ('".time()."','".$auth->user_id."','".$this->obj_name."','".$this->id."','','".$_SERVER['REMOTE_ADDR']."','') "); } } } foreach( $this->diffed as $key ) { if ( $this->attrs[$key] ) { $this->attrs[$key]['attr_obj']->postsave(); } } } /** * Сбрасывает (обнуляет) объект */ public function reset() { $this->byId(0); $this->save_sql = ''; $this->diffed = []; } /** * Создает новые объект с данными текущего, но без ID */ function copy() { $this->id = 0; $this->save_sql = ''; foreach($this->attrs_vals as $k => $v) { $this->diffed[] = $k; } } /** * Сохраняет объект как копию * @param boolean $do_log логировать ли запрос */ function saveAsCopy($do_log = true) { $this->id = 0; $this->save_sql = ''; foreach( $this->attrs as $key => $val ) { $this->diffed[] = $key; } return $this->save($do_log); } /** * Удаляет объект */ function del() { $curdb = $this->getDB(); if ( $this->id ) { if ( isset($this->attrs['_sys_deleted']) ) { $curdb->q('UPDATE `'.$this->obj_name.'` SET `'.$this->obj_name.'`.`_sys_deleted` = "1" WHERE `'.$this->obj_name.'`.`id` = "'.$this->id.'"'); } else { $curdb->q('DELETE FROM `'.$this->obj_name.'` WHERE `'.$this->obj_name.'`.`id` = "'.$this->id.'"'); } } global $cached_objects; unset($cached_objects[$this->obj_name][$this->id]); } /** * Заплоняет объект строчкой из базы данных * @param db $db ссылка на подключение к базе данных */ function lRow( $db ) { $this->diffed = array(); foreach( $this->attrs as $key => $val ) { $this->attrs_vals[$key] = $db->f( $key ); } $this->id = $db->f('id'); global $cached_objects; $cached_objects[$this->obj_name][$db->f('id')] = $this->attrs_vals; return $this; } /** * Заполняет объект из POST данных формы */ function lPost() { if ( intval( $_POST['id']) ) { $this->byId( intval( $_POST['id']) ); } foreach( $this->attrs as $k => $val ) { if (in_array($k, $this->force_hidden ) ) continue; if ( !isset($_FILES[$k]) && !isset($_POST[$k]) && (!$this->attrs[$k]['mand']) ) { if ( substr( $val['name'], -8 ) != '_preview' ) { $this->set($k,''); } } else { $val['attr_obj']->lPost(); } // $this->diffed[] = $k; } $this->id = intval($_POST['id']); } /** * Меняет данных атрибута (сеттер) * @param string $attr_name имя атрибута * @param string $value данные */ function set( $attr_name, $value ) { // Проверка на совпадение, с учётом приведения типов // в this->attrs_vals всегда лежат string if ( (string)$this->attrs_vals[$attr_name] === (string)$value ) return false; if ( $this->attrs_vals[$attr_name] === '0' && $value === "") return false; if ( $this->attrs_vals[$attr_name] === '0' && $value === false ) return false; if ( $this->attrs_vals[$attr_name] === '1' && $value === true ) return false; if ( is_numeric($this->attrs_vals[$attr_name]) && is_numeric($value) && $this->attrs_vals[$attr_name] == $value ) return false; $this->attrs_vals[$attr_name] = $value; if (!in_array($attr_name, $this->diffed)) { $this->diffed[] = $attr_name; } return true; } /** * Получает данные атрибута из объекта * @param string $attr_name имя атрибута * @return string */ function get( $attr_name ) { return $this->attrs_vals[$attr_name]; } /** * Отрисовка атрибутов через шаблонизатор Render * @param string $tpl переменная-шаблон * @param boolean $crop укороченный вывод * @param array $params устарело - не используется * @return [type] [description] */ function assign( $tpl, $crop = false, $params = array() ) { global $R; $vars = $R->getVars($tpl); foreach( $this->attrs as $key => $val ) { if ( in_array( $key, $vars ) ) { if ($this->recursion_depth < 3 ) { $tpl = $R->set( $key, $this->attrs[$key]['attr_obj']->render_text($crop), $tpl ); } else { $tpl = $R->set( $key, $this->get($key), $tpl ); } } $tpl = $R->set( $key.'_real', $this->get($key), $tpl ); $tpl = $R->set( $key.'_money', number_format((float)$this->get($key), 2), $tpl ); } $tpl = $R->set( "id", $this->id, $tpl ); $tpl = $R->set( $this->obj_name."_id", $this->id, $tpl ); return $tpl; } function validate() { $valid = true; foreach( $this->attrs as $attr_name=>$attr ) { if (in_array($attr_name, $this->force_hidden ) ) continue; if ($attr['mand'] && !$this->attrs_vals[$attr_name]) { $this->errors[$attr_name] = 'Поле не заполнено'; $valid = false; } elseif ( $this->attrs_vals[$attr_name] ) { $err = $attr['attr_obj']->validate(); if ( $err && $err !== true ) { $this->errors[$attr_name] = $err; $valid = false; } } } return $valid; } function set_hidden( $attr_name ) { $this->force_hidden[] = $attr_name; } function set_no_hidden( $attr_arr = array() ) { foreach( $this->attrs as $k => $v ) { if (!in_array($k, $attr_arr ) ) { $this->force_hidden[] = $k; } } } function setSqlWhere( $attr_name, $sql_where ) { if ( $this->attrs[$attr_name] ) { $this->attrs[$attr_name]['attr_obj']->setSqlWhere($sql_where); } } function addSqlWhere( $attr_name, $sql_where ) { if ( $this->attrs[$attr_name] ) { $this->attrs[$attr_name]['attr_obj']->setSqlWhere( $this->attrs[$attr_name]['attr_obj']->sql_where . $sql_where); } } function setError($attr_name, $error) { $this->errors[$attr_name] = $error; } function getSize( $sql_where = '' ) { $db2 = $this->getDB()->q( $this->getSelectEx(' count(*) as `c` ', $sql_where ) ); $db2->nr(); return intval( $db2->f('c') ); } function getList( $sql_where = '', $value = 'title' ) { $db2 = $this->getDB()->q( $this->getSelect( $sql_where ) ); $lst = array(); while($db2->nr()) { $lst[$db2->f('id')] = $db2->f($value); } return $lst; } function itemObjLog() { global $auth; // array('shop_item', 'shop_item_variants','shop_item_chars','shop_item_images') if (!in_array($this->obj_name, array('shop_item', 'shop_item_variants',/*'shop_item_chars',*/'shop_item_images') ) ) return; $skipFields = array('nostock_tmp', 'stock_data', 'stock_roz', 'in_stock', 'stock_im', 'in_stock_im'); $data = array(); $data['datetime'] = time(); $data['ip'] = $_SERVER['REMOTE_ADDR']; $data['user_id'] = $auth->user_id; $data['obj_name'] = $this->obj_name; $data['script_name'] = $_SERVER['PWD'].'/'.$_SERVER['PHP_SELF']; switch( $this->obj_name ) { case 'shop_item': $data['item_id'] = $this->id; $data['variant_id'] = 0; break; case 'shop_item_variants': $data['item_id'] = $this->get('item_id'); $data['variant_id'] = $this->id; break; default: $data['item_id'] = $this->get('item_id'); $data['variant_id'] = $this->id; break; } $old = array(); if ($this->id ) { /* OldVals */ $curdb = $this->getDB(); $db2 = $curdb->q('SELECT * FROM `'.$this->obj_name.'` WHERE `id` = ' . intval( $this->id ) ); if ( $db2->nr() ) { $old = $db2->lastRow; } } foreach( $this->diffed as $field ) { if (in_array( $field, $skipFields ) ) continue; $newval = $this->attrs_vals[$field]; $oldval = $old[$field]; if ($newval == $oldval ) continue; $data['field_name'] = $this->attrs[$field]['name']; if (!$newval) { $data['change_type'] = 3; } elseif( !$oldval ) { $data['change_type'] = 1; } else { $data['change_type'] = 2; } $data['old_value'] = db_escape_string($oldval); $data['new_value'] = db_escape_string($newval); $sql = 'INSERT DELAYED INTO `shop_item_change_log` (`'.implode("`, `",array_keys($data)).'`) VALUES ("'.implode('", "', $data).'") '; $curdb = $this->getDB(); $db2 = $curdb->q( $sql ); } } function getCollection($sql_where = "", $show_deleted = 0) { return new cobject_collection($this, $sql_where, $show_deleted); } } /** * Класс для коллекции объектов. * Так удобнее работать с blade шаблонами */ class cobject_collection { var $obj; var $db; // Готовим запрос в базу function __construct($obj, $sql_where, $show_deleted = 0) { $this->obj = $obj; $this->db = $this->obj->getDB()->q($this->obj->getSelect($sql_where)); } // Следующий экземпляр коллекции public function next() { if ($this->db->nr()) { $this->obj->lRow($this->db); return true; } else { return false; } } // Получение свойства public function __get($key) { return $this->render($key); } // Получение реального значения свойства public function get($key) { return $this->obj->get($key); } // Отрисовка public function render($key, $crop = false) { if (isset($this->obj->attrs[$key])) { if ($this->obj->recursion_depth < 3 ) { return $this->obj->attrs[$key]['attr_obj']->render_text($crop); } else { return $this->obj->get($key); } } else { if ($key == 'id') return $this->obj->id; return ''; } } } ?>