Пример #1
0
 /**
  * Сохранение сущности
  * @param Entity $entity
  * @throws Error
  */
 function write($entity)
 {
     // @todo
     // Смена родителя/прототипа требует соответсвующие сдвиги в таблице отношений
     // При смене uri нужно обновить uri подчиненных
     $attr = $entity->attributes();
     // Локальные id
     $attr['parent'] = isset($attr['parent']) ? $this->localId($attr['parent']) : 0;
     $attr['proto'] = isset($attr['proto']) ? $this->localId($attr['proto']) : 0;
     $attr['author'] = isset($attr['author']) ? $this->localId($attr['author']) : 0;
     //$this->localId(Auth::get_user()->uri());
     // Подбор уникального имени, если указана необходимость в этом
     if ($entity->is_changed('uri') || !$entity->is_exists()) {
         $q = $this->db->prepare('SELECT 1 FROM {objects} WHERE parent=? AND `name`=? LIMIT 0,1');
         $q->execute(array($attr['parent'], $attr['name']));
         if ($q->fetch()) {
             //Выбор записи по шаблону имени с самым большим префиксом
             $q = $this->db->prepare('SELECT `name` FROM {objects} WHERE parent=? AND `name` REGEXP ? ORDER BY CAST((SUBSTRING_INDEX(`name`, "_", -1)+1) AS SIGNED) DESC LIMIT 0,1');
             $q->execute(array($attr['parent'], '^' . $attr['name'] . '(_[0-9]+)?$'));
             if ($row = $q->fetch(DB::FETCH_ASSOC)) {
                 preg_match('|^' . preg_quote($attr['name']) . '(?:_([0-9]+))?$|u', $row['name'], $match);
                 $attr['name'] .= '_' . (isset($match[1]) ? $match[1] + 1 : 1);
             }
         }
         $entity->name($attr['name']);
         $attr['uri'] = $entity->uri();
     }
     Buffer::set_entity($entity);
     // Локальный идентификатор объекта
     $attr['id'] = $this->localId($entity->uri($entity->is_exists()), true, $new_id);
     // Если смена файла, то удалить текущий файл
     if ($entity->is_changed('file')) {
         $current_file = $entity->changes('file');
         if (is_string($current_file)) {
             File::delete($entity->dir(true) . $current_file);
         }
     }
     // Если привязан файл
     if (!$entity->is_default_file()) {
         if ($entity->is_file()) {
             $file_attache = $entity->file();
             if (is_array($file_attache)) {
                 // Загрузка файла
                 if (!($attr['file'] = Data::save_file($entity, $file_attache))) {
                     $attr['file'] = '';
                 }
             } else {
                 $attr['file'] = basename($file_attache);
             }
         } else {
             // файла нет, но нужно отменить наследование файла
             $attr['file'] = '';
         }
     } else {
         $attr['file'] = '';
     }
     // Уникальность order
     // Если изменено на конкретное значение (не максимальное)
     if ($attr['order'] != Entity::MAX_ORDER && (!$entity->is_exists() || $entity->is_changed('order'))) {
         // Проверка, занят или нет новый order
         $q = $this->db->prepare('SELECT 1 FROM {objects} WHERE `parent`=? AND `order`=?');
         $q->execute(array($attr['parent'], $attr['order']));
         if ($q->fetch()) {
             // Сдвиг order существующих записей, чтоб освободить значение для новой
             $q = $this->db->prepare('
                 UPDATE {objects} SET `order` = `order`+1
                 WHERE `parent`=? AND `order`>=?');
             $q->execute(array($attr['parent'], $attr['order']));
         }
         unset($q);
     } else {
         // Новое максимальное значение для order, если объект новый или явно указано order=null
         if (!$entity->is_exists() || $attr['order'] == Entity::MAX_ORDER) {
             // Порядковое значение вычисляется от максимального существующего
             $q = $this->db->prepare('SELECT MAX(`order`) m FROM {objects} WHERE parent=?');
             $q->execute(array($attr['parent']));
             if ($row = $q->fetch(DB::FETCH_ASSOC)) {
                 $attr['order'] = $row['m'] + 1;
             }
             unset($q);
         }
     }
     $this->db->beginTransaction();
     // Если новое имя или родитель, то обновить свой URI и URI подчиненных
     if ($entity->is_changed('name') || $entity->is_changed('parent')) {
         if ($entity->is_exists()) {
             $current_uri = $entity->uri(true);
             $current_name = $entity->is_changed('name') ? $entity->changes('name') : $attr['name'];
             // Текущий URI
             $names = F::splitRight('/', $current_uri, true);
             $uri = (isset($names[0]) ? $names[0] . '/' : '') . $current_name;
             // Новый URI
             $names = F::splitRight('/', $attr['uri'], true);
             $uri_new = (isset($names[0]) ? $names[0] . '/' : '') . $attr['name'];
             $entity->change('uri', $uri_new);
             // @todo Обновление URI подчиенных в базе
             // нужно знать текущий уковень вложенности и локальный id
             //
             //            $q = $this->db->prepare('UPDATE {ids}, {parents} SET {ids}.uri = CONCAT(?, SUBSTRING(uri, ?)) WHERE {parents}.parent_id = ? AND {parents}.object_id = {ids}.id AND {parents}.is_delete=0');
             //            $v = array($uri_new, mb_strlen($uri)+1, $attr['id']);
             //            $q->execute($v);
             //            // Обновление уровней вложенностей в objects
             //            if (!empty($current) && $current['parent']!=$attr['parent']){
             //                $dl = $attr['parent_cnt'] - $current['parent_cnt'];
             //                $q = $this->db->prepare('UPDATE {objects}, {parents} SET parent_cnt = parent_cnt + ? WHERE {parents}.parent_id = ? AND {parents}.object_id = {objects}.id AND {parents}.is_delete=0');
             //                $q->execute(array($dl, $attr['id']));
             //                // Обновление отношений
             //                $this->makeParents($attr['id'], $attr['parent'], $dl, true);
             //            }
             if (!empty($uri) && is_dir(DIR . $uri)) {
                 // Переименование/перемещение папки объекта
                 $dir = DIR . $uri_new;
                 File::rename(DIR . $uri, $dir);
                 if ($entity->is_changed('name')) {
                     // Переименование файла класса
                     File::rename($dir . '/' . $current_name . '.php', $dir . '/' . $attr['name'] . '.php');
                     // Переименование .info файла
                     File::rename($dir . '/' . $current_name . '.info', $dir . '/' . $attr['name'] . '.info');
                 }
             }
             unset($q);
         }
         // Обновить URI подчиненных объектов не фиксируя изменения
         $entity->updateChildrenUri();
     }
     // Если значение больше 255
     if (mb_strlen($attr['value']) > 255) {
         $q = $this->db->prepare('
             INSERT INTO {text} (`id`, `value`)
             VALUES (:id, :value)
             ON DUPLICATE KEY UPDATE `value` = :value
         ');
         $q->execute(array(':id' => $attr['id'], ':value' => $attr['value']));
         $attr['value'] = mb_substr($attr['value'], 0, 255);
         $attr['is_text'] = 1;
     } else {
         $attr['is_text'] = 0;
     }
     // Запись
     $attr_names = array('id', 'parent', 'proto', 'author', 'order', 'name', 'value', 'file', 'is_text', 'is_draft', 'is_hidden', 'is_link', 'is_mandatory', 'is_property', 'is_relative', 'is_default_value', 'is_default_logic', 'created', 'updated');
     $cnt = sizeof($attr_names);
     // Запись объекта (создание или обновление при наличии)
     // Объект идентифицируется по id
     if (!$entity->is_exists()) {
         $q = $this->db->prepare('
             INSERT INTO {objects} (`' . implode('`, `', $attr_names) . '`)
             VALUES (' . str_repeat('?,', $cnt - 1) . '?)
             ON DUPLICATE KEY UPDATE `' . implode('`=?, `', $attr_names) . '`=?
         ');
         $i = 0;
         foreach ($attr_names as $name) {
             $value = $attr[$name];
             $i++;
             $type = is_int($value) ? DB::PARAM_INT : (is_bool($value) ? DB::PARAM_BOOL : (is_null($value) ? DB::PARAM_NULL : DB::PARAM_STR));
             $q->bindValue($i, $value, $type);
             $q->bindValue($i + $cnt, $value, $type);
         }
         $q->execute();
     } else {
         $q = $this->db->prepare('
             UPDATE {objects} SET `' . implode('`=?, `', $attr_names) . '`=? WHERE id = ?
         ');
         $i = 0;
         foreach ($attr_names as $name) {
             $value = $attr[$name];
             $i++;
             $type = is_int($value) ? DB::PARAM_INT : (is_bool($value) ? DB::PARAM_BOOL : (is_null($value) ? DB::PARAM_NULL : DB::PARAM_STR));
             $q->bindValue($i, $value, $type);
         }
         $q->bindValue(++$i, $attr['id']);
         $q->execute();
     }
     $this->db->commit();
     foreach ($entity->children() as $child) {
         Data::write($child);
     }
     return true;
 }