Files
ave-cms/adminx/modules/Rubrics/RubricTrash.php
T
2026-07-30 16:34:14 +03:00

258 lines
9.4 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/*
|--------------------------------------------------------------------------------------
| AVE.cms
|--------------------------------------------------------------------------------------
| @package AVE.cms
| @file adminx/modules/Rubrics/RubricTrash.php
| @author AVE.cms <support@ave-cms.ru>
| @copyright 2007-2026 (c) AVE.cms
| @link https://ave-cms.ru
| @version 3.3
*/
namespace App\Adminx\Rubrics;
defined('BASEPATH') || die('Direct access to this location is not allowed.');
use App\Common\DatabaseSchema;
use App\Common\SystemTables;
use App\Content\ContentTables;
use App\Helpers\Json;
use DB;
/**
* Корзина рубрик: полный обратимый снимок рубрики со всей её схемой.
*
* Удаление рубрики разрешено только для пустой рубрики без зависимостей
* (см. Model::deleteRubric), поэтому на её строки, поля и группы ничто не
* ссылается. Это позволяет честно убрать рубрику из активных таблиц (никакие
* списки/пикеры фильтровать не нужно), а рядом сохранить слепок для
* восстановления. Идентификаторы автоинкрементные — MySQL не переиспользует
* освободившиеся, поэтому восстановление с исходными id безопасно.
*/
class RubricTrash
{
const VERSION = 1;
public static function table()
{
return ContentTables::table('rubric_trash');
}
public static function available()
{
try { return DatabaseSchema::tableExists(self::table()); }
catch (\Throwable $e) { return false; }
}
/**
* Логические таблицы рубрики в порядке восстановления (родитель первым).
* Ключ — это столбец связи с рубрикой; для самой таблицы рубрик это Id.
*/
protected static function tables()
{
return array(
array('name' => 'rubric', 'table' => Model::rubricsTable(), 'key' => 'Id'),
array('name' => 'group', 'table' => Model::groupsTable(), 'key' => 'rubric_id'),
array('name' => 'field', 'table' => Model::fieldsTable(), 'key' => 'rubric_id'),
array('name' => 'template', 'table' => Model::templatesTable(), 'key' => 'rubric_id'),
array('name' => 'permission', 'table' => Model::permissionsTable(), 'key' => 'rubric_id'),
array('name' => 'view', 'table' => AdminView::table(), 'key' => 'rubric_id'),
array('name' => 'fieldset_link', 'table' => RubricFieldSetLinks::linksTable(), 'key' => 'rubric_id'),
array('name' => 'revision', 'table' => RubricRevisions::table(), 'key' => 'rubric_id'),
);
}
/**
* Снять полный слепок рубрики в корзину. Вызывается внутри транзакции
* удаления, до каскадных DELETE. Возвращает id записи корзины или 0.
*/
public static function capture($rubricId, $authorId = 0)
{
$rubricId = (int) $rubricId;
if ($rubricId <= 0 || !self::available()) {
return 0;
}
$rubricRow = DB::query('SELECT * FROM ' . Model::rubricsTable() . ' WHERE Id=%i LIMIT 1', $rubricId)->getAssoc();
if (!$rubricRow) {
return 0;
}
$payload = array('version' => self::VERSION, 'rubric_id' => $rubricId, 'tables' => array());
$fieldsCount = 0;
$groupsCount = 0;
foreach (self::tables() as $definition) {
if (!self::tableReady($definition['table'])) {
continue;
}
$rows = DB::query(
'SELECT * FROM ' . $definition['table'] . ' WHERE ' . $definition['key'] . '=%i',
$rubricId
)->getAll() ?: array();
$payload['tables'][$definition['name']] = $rows;
if ($definition['name'] === 'field') { $fieldsCount = count($rows); }
if ($definition['name'] === 'group') { $groupsCount = count($rows); }
}
$json = Json::encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
if (!is_string($json)) {
throw new \RuntimeException('Не удалось сохранить рубрику в корзину');
}
DB::Insert(self::table(), array(
'rubric_id' => $rubricId,
'rubric_title' => mb_substr((string) (isset($rubricRow['rubric_title']) ? $rubricRow['rubric_title'] : ''), 0, 255, 'UTF-8'),
'rubric_alias' => mb_substr((string) (isset($rubricRow['rubric_alias']) ? $rubricRow['rubric_alias'] : ''), 0, 255, 'UTF-8'),
'fields_count' => $fieldsCount,
'groups_count' => $groupsCount,
'payload_json' => $json,
'author_id' => (int) $authorId,
'author_name' => self::authorName((int) $authorId),
'deleted_at' => time(),
));
return (int) DB::insertId();
}
public static function listing($limit = 100)
{
if (!self::available()) {
return array();
}
$rows = DB::query(
'SELECT id,rubric_id,rubric_title,rubric_alias,fields_count,groups_count,author_id,author_name,deleted_at'
. ' FROM ' . self::table() . ' ORDER BY deleted_at DESC,id DESC LIMIT %i',
max(1, min(500, (int) $limit))
)->getAll() ?: array();
return array_map(array(__CLASS__, 'formatRow'), $rows);
}
public static function one($id)
{
if (!self::available()) {
return null;
}
$row = DB::query('SELECT * FROM ' . self::table() . ' WHERE id=%i LIMIT 1', (int) $id)->getAssoc();
return $row ? self::formatRow($row) : null;
}
public static function count()
{
if (!self::available()) {
return 0;
}
return (int) DB::query('SELECT COUNT(*) FROM ' . self::table())->getValue();
}
/**
* Восстановить рубрику из корзины: реинсертить все строки с исходными id
* и убрать запись корзины. Бросает исключение при конфликте id.
*/
public static function restore($trashId)
{
if (!self::available()) {
throw new \RuntimeException('Корзина недоступна');
}
$row = DB::query('SELECT * FROM ' . self::table() . ' WHERE id=%i LIMIT 1', (int) $trashId)->getAssoc();
if (!$row) {
throw new \RuntimeException('Запись корзины не найдена');
}
$payload = Json::toArray((string) $row['payload_json']);
$rubricId = (int) (isset($payload['rubric_id']) ? $payload['rubric_id'] : $row['rubric_id']);
$stored = isset($payload['tables']) && is_array($payload['tables']) ? $payload['tables'] : array();
if ($rubricId <= 0 || empty($stored['rubric'])) {
throw new \RuntimeException('Слепок рубрики повреждён');
}
if (DB::query('SELECT Id FROM ' . Model::rubricsTable() . ' WHERE Id=%i LIMIT 1', $rubricId)->getValue()) {
throw new \RuntimeException('Рубрика с id #' . $rubricId . ' уже существует — восстановление невозможно');
}
DB::startTransaction();
try {
foreach (self::tables() as $definition) {
$rows = isset($stored[$definition['name']]) && is_array($stored[$definition['name']])
? $stored[$definition['name']]
: array();
if (!$rows || !self::tableReady($definition['table'])) {
continue;
}
foreach ($rows as $record) {
if (is_array($record) && $record) {
DB::Insert($definition['table'], $record);
}
}
}
DB::Delete(self::table(), 'id=%i', (int) $trashId);
DB::commit();
} catch (\Throwable $e) {
DB::rollback();
throw $e;
}
Model::clearRubricCachePublic($rubricId);
return $rubricId;
}
public static function purge($trashId)
{
if (!self::available()) {
return false;
}
$exists = (int) DB::query('SELECT COUNT(*) FROM ' . self::table() . ' WHERE id=%i', (int) $trashId)->getValue();
if ($exists < 1) {
return false;
}
DB::Delete(self::table(), 'id=%i', (int) $trashId);
return true;
}
protected static function formatRow(array $row)
{
$deleted = isset($row['deleted_at']) ? (int) $row['deleted_at'] : 0;
return array(
'id' => (int) $row['id'],
'rubric_id' => (int) $row['rubric_id'],
'rubric_title' => (string) (isset($row['rubric_title']) ? $row['rubric_title'] : ''),
'rubric_alias' => (string) (isset($row['rubric_alias']) ? $row['rubric_alias'] : ''),
'fields_count' => (int) (isset($row['fields_count']) ? $row['fields_count'] : 0),
'groups_count' => (int) (isset($row['groups_count']) ? $row['groups_count'] : 0),
'author_id' => (int) (isset($row['author_id']) ? $row['author_id'] : 0),
'author_name' => (string) (isset($row['author_name']) ? $row['author_name'] : ''),
'deleted_at' => $deleted,
'deleted_label' => $deleted > 0 ? date('d.m.Y H:i:s', $deleted) : '-',
);
}
protected static function tableReady($table)
{
try { return DatabaseSchema::tableExists($table); }
catch (\Throwable $e) { return false; }
}
protected static function authorName($id)
{
if ((int) $id <= 0) { return ''; }
$row = DB::query('SELECT name,login,email FROM ' . SystemTables::table('users') . ' WHERE id=%i LIMIT 1', (int) $id)->getAssoc();
if (!$row) { return '#' . (int) $id; }
if (!empty($row['name'])) { return (string) $row['name']; }
if (!empty($row['login'])) { return '@' . (string) $row['login']; }
return !empty($row['email']) ? (string) $row['email'] : '#' . (int) $id;
}
}