Расширение конфигурации таблиц в Wepps Platform: Action-обработчики
Введение
Система управления списками данных в Wepps Platform предоставляет мощный механизм расширения стандартного функционала через Action-обработчики. Это специальные классы, которые автоматически выполняются при различных операциях со списками (просмотр, создание, изменение, удаление), позволяя добавить кастомную логику без изменения ядра системы.
Зачем использовать Action-обработчики?
Проблемы, которые решают Action-обработчики
- Ограниченность стандартного функционала - базовые операции CRUD не всегда достаточны для сложной бизнес-логики
- Необходимость дополнительной обработки данных - автоматическое заполнение полей, валидация, связанные операции
- Производительность списков - оптимизация выборки данных через ограничение отображаемых полей
- Каскадные операции - удаление связанных записей, создание дополнительных сущностей
Когда использовать
- Создание связанных файлов или записей при сохранении элемента
- Фильтрация списка по сложным условиям
- Добавление дополнительных вкладок или полей в форму редактирования
- Каскадное удаление связанных данных
- Оптимизация загрузки больших списков
Основные концепции
Таблица s_Config
Конфигурация таблиц данных хранится в системной таблице s_Config. Помимо базовых настроек отображения, она содержит специальные поля для подключения Action-обработчиков:
| Поле | Тип | Назначение |
|---|---|---|
ItemsFields |
VARCHAR(128) | Перечисление полей для отображения в списке (через запятую) |
ActionShow |
VARCHAR(255) | Класс-обработчик для списка элементов |
ActionShowId |
VARCHAR(255) | Класс-обработчик для просмотра/редактирования элемента |
ActionModify |
VARCHAR(255) | Класс-обработчик после сохранения элемента |
ActionDrop |
VARCHAR(255) | Класс-обработчик перед удалением элемента |
Структура Action-класса
Все Action-обработчики должны соответствовать единому паттерну:
<?php
namespace WeppsAdmin\Lists\Actions;
use WeppsCore\Request;
use WeppsCore\Connect;
class MyAction extends Request {
public $noclose = 1;
public $listSettings = [];
public $listScheme = [];
public $element = [];
public function request($action = "") {
// Получение переданных данных
$this->listSettings = $this->get['listSettings'];
$this->listScheme = $this->get['listScheme'];
$this->element = $this->get['element'];
// Guard clause - ранний выход при несоответствии условий
if ($this->listSettings['TableName'] != 'Products') {
return;
}
// Основная логика обработчика
}
}
Важные особенности:
- Класс находится в
packages/WeppsAdmin/Lists/Actions/ - Наследуется от
WeppsCore\Request - Свойство
$noclose = 1предотвращает автоматическое завершение запроса - Использование Guard Clause паттерна для ранних проверок
Оптимизация производительности: ItemsFields
Ограничение выборки полей
Поле ItemsFields позволяет значительно ускорить загрузку списков, ограничивая SELECT-запрос только необходимыми столбцами.
Пример в админке:
Откройте список s_Config → найдите нужную таблицу → в поле ItemsFields укажите:
Name,Alias,Priority,IsHidden
Что происходит в коде:
// WeppsAdmin/Lists/Lists.php
$listObj = new Data($tableName);
if (!empty($listSettings['ItemsFields'])) {
$listObj->setFields($listSettings['ItemsFields']);
}
$listScheme = $listObj->getScheme();
Результат:
- Вместо
SELECT *будетSELECT Name,Alias,Priority,IsHidden - Уменьшается объем передаваемых данных
- Ускоряется рендеринг таблицы в админке
- Снижается нагрузка на MySQL
Когда использовать:
- Таблица содержит более 10 полей
- Есть тяжелые поля типа
TEXT,LONGTEXT - Список загружается медленно (более 1 секунды)
- Список имеет несколько полей типа
file
ActionShow: Кастомизация списка элементов
Назначение
Класс ActionShow выполняется при загрузке списка элементов и позволяет:
- Добавить условия фильтрации (WHERE)
- Изменить схему отображаемых полей
- Добавить вычисляемые поля
Базовый пример
Настройка в админке:
s_Config → Products → ActionShow = ViewList.php
Класс-обработчик:
<?php
namespace WeppsAdmin\Lists\Actions;
use WeppsCore\Request;
class ViewList extends Request {
public $noclose = 1;
public $condition = "";
public $scheme = [];
public function request($action = "") {
$this->scheme = $this->get['listScheme'];
// Добавляем условие фильтрации
$this->condition = "t.IsActive = 1 AND t.Price > 0";
}
}
Результат: В списке будут показаны только активные товары с указанной ценой.
Продвинутый пример: Фильтрация по категориям
<?php
namespace WeppsAdmin\Lists\Actions;
use WeppsCore\Request;
use WeppsCore\Connect;
class ViewListProducts extends Request {
public $noclose = 1;
public $condition = "";
public $fields = "";
public $scheme = [];
public function request($action = "") {
$this->scheme = $this->get['listScheme'];
// Добавляем JOIN для вывода названия категории
$this->fields = "(SELECT c.Name FROM Categories c WHERE c.Id = t.CategoryId) as CategoryName";
// Фильтр по GET-параметру
if (isset($_GET['category']) && (int)$_GET['category'] > 0) {
$categoryId = (int)$_GET['category'];
$this->condition = "t.CategoryId = {$categoryId}";
}
// Фильтр по складским остаткам
if (isset($_GET['instock'])) {
$this->condition .= ($this->condition ? " AND " : "") . "t.Stock > 0";
}
// Добавляем виртуальное поле в схему
$this->scheme['CategoryName'] = [
'Field' => 'CategoryName',
'Name' => 'Категория',
'Type' => 'text'
];
}
}
Возможности:
$this->condition- WHERE условия (без префикса WHERE)$this->fields- дополнительные поля для SELECT$this->scheme- изменение схемы полей для отображения
ActionShowId: Кастомизация формы элемента
Назначение
Класс ActionShowId выполняется при открытии формы редактирования элемента и позволяет:
- Изменить данные элемента перед отображением
- Добавить дополнительные вкладки
- Подключить кастомные CSS/JS файлы
- Изменить схему полей
Базовый пример: Изменение данных элемента
Настройка в админке:
s_Config → s_Navigator → ActionShowId = ViewItemDirectories.php
Класс-обработчик:
<?php
namespace WeppsAdmin\Lists\Actions;
use WeppsCore\Request;
class ViewItemDirectories extends Request {
public $noclose = 1;
public $scheme = [];
public $element = [];
public function request($action = "") {
$element = $this->get['element'];
$this->element = &$this->get['element'];
$this->scheme = $this->get['listScheme'];
// Обработка режима "Добавить подраздел"
if (strstr($_GET['weppsurl'], '/addNavigator/')) {
foreach ($this->scheme as $key => $value) {
$this->element[$key] = "";
}
$this->element['Id'] = 'add';
$this->element['Name'] = 'Новый раздел';
$this->element['ParentDir_SelectChecked'] = $element['Id'];
$this->element['Template_SelectChecked'] = "";
$this->element['Extension_SelectChecked'] = "";
}
}
}
Результат: При добавлении подраздела автоматически подставляется родительский раздел.
Продвинутый пример: Добавление вкладок
<?php
namespace WeppsAdmin\Lists\Actions;
use WeppsCore\Request;
use WeppsCore\Connect;
use WeppsCore\Smarty;
class ViewItemProducts extends Request {
public $noclose = 1;
public $scheme = [];
public $element = [];
public $settings = [];
public $headers;
public function request($action = "") {
$this->element = &$this->get['element'];
$this->scheme = &$this->get['listScheme'];
$this->settings = &$this->get['listSettings'];
$this->headers = &$this->get['headers'];
// Подключаем кастомный JS
$this->headers->js("/packages/WeppsAdmin/Lists/Actions/ViewItemProducts.{$this->headers::$rand}.js");
// Получаем дополнительные данные
$sql = "SELECT * FROM ProductsVariations WHERE ProductId = ?";
$variations = Connect::$instance->fetch($sql, [$this->element['Id']]);
// Рендерим шаблон вкладки
$smarty = Smarty::getSmarty();
$smarty->assign('variations', $variations);
$smarty->assign('productId', $this->element['Id']);
$smarty->assign('itemGroup', 'Variations');
$tplVariations = $smarty->fetch(
Connect::$projectDev['root'] . '/packages/WeppsAdmin/Lists/Actions/ViewItemProductsVariations.tpl'
);
// Добавляем новую вкладку
$this->settings['ActionShowIdAddons'] = [
[
"title" => "Вариации товара",
"group" => "Variations",
"tpl" => $tplVariations
]
];
}
}
Возможности:
$this->element- данные текущего элемента (по ссылке)$this->scheme- схема полей (по ссылке)$this->settings['ActionShowIdAddons']- массив дополнительных вкладок$this->headers- объект для подключения CSS/JS
Структура вкладки:
title- название вкладки в интерфейсеgroup- уникальный идентификатор группыtpl- HTML-контент вкладки
ActionModify: Постобработка после сохранения
Назначение
Класс ActionModify выполняется сразу после сохранения элемента в БД и позволяет:
- Выполнить дополнительные операции с данными
- Создать связанные записи
- Обновить кэш или индексы
- Сгенерировать файлы или структуры
Базовый пример: Автогенерация URL
Настройка в админке:
s_Config → s_Navigator → ActionModify = SaveItemDirectories.php
Класс-обработчик:
<?php
namespace WeppsAdmin\Lists\Actions;
use WeppsCore\Request;
use WeppsCore\Connect;
use WeppsCore\TextTransforms;
class SaveItemDirectories extends Request {
public $noclose = 1;
public $scheme = [];
public $listSettings = [];
public $element = [];
public function request($action = "") {
$this->scheme = $this->get['listScheme'];
$this->listSettings = $this->get['listSettings'];
$this->element = $this->get['element'];
// Guard clause
if ($this->listSettings['TableName'] != 's_Navigator') {
return;
}
// Генерация URL из названия, если он не указан
if ($this->element['Url'] == '') {
$url = "/" . TextTransforms::translit($this->element['Name'], 2) . "/";
$sql = "UPDATE s_Navigator SET Url = ? WHERE Id = ?";
Connect::$instance->query($sql, [$url, $this->element['Id']]);
$this->element['Url'] = $url;
}
}
}
Результат: При создании нового раздела навигации без URL, он автоматически генерируется из транслитерации названия.
Продвинутый пример: Создание таблицы
<?php
namespace WeppsAdmin\Lists\Actions;
use WeppsCore\Request;
use WeppsCore\Connect;
use WeppsAdmin\Lists\Lists;
use WeppsAdmin\Admin\Admin;
class SaveItemConfig extends Request {
public $noclose = 1;
public $scheme = [];
public $listSettings = [];
public $element = [];
public function request($action = "") {
$this->scheme = $this->get['listScheme'];
$this->listSettings = $this->get['listSettings'];
$this->element = $this->get['element'];
if ($this->listSettings['TableName'] != 's_Config') {
return;
}
// Создаем новую таблицу со стандартной структурой
$str = Lists::addList($this->element['TableName']);
if ($str != "") {
Connect::$db->exec($str);
// Добавляем права доступа для администратора
$perm = Admin::getPermissions(1, ['list' => 's_Config']);
if ($perm['status'] == 1) {
$sql = "UPDATE s_Permissions
SET TableName = CONCAT(TableName, ',', ?)
WHERE Id = 1";
Connect::$instance->query($sql, [$this->element['TableName']]);
}
}
}
}
Результат: При добавлении новой таблицы в s_Config автоматически создается физическая таблица в БД с базовыми полями.
Кейс: Создание файлов шаблонов
<?php
namespace WeppsAdmin\Lists\Actions;
use WeppsCore\Request;
use WeppsCore\Connect;
class SaveItemTemplates extends Request {
public $noclose = 1;
public $scheme = [];
public $listSettings = [];
public $element = [];
public function request($action = "") {
$this->scheme = $this->get['listScheme'];
$this->listSettings = $this->get['listSettings'];
$this->element = $this->get['element'];
if ($this->listSettings['TableName'] != 's_Templates') {
return;
}
$this->copyTpl($this->element['FileTemplate']);
}
private function copyTpl($tpl) {
$tmp = substr($tpl, 0, -4);
$root = Connect::$projectDev['root'] . "/packages/WeppsExtensions/Template/";
// Создаем .tpl файл
if (!is_file("{$root}{$tmp}.tpl")) {
copy("{$root}Template.tpl", "{$root}{$tmp}.tpl");
}
// Создаем .css файл
if (!is_file("{$root}{$tmp}.css")) {
copy("{$root}Template.css", "{$root}{$tmp}.css");
}
// Создаем .js файл
if (!is_file("{$root}{$tmp}.js")) {
copy("{$root}Template.js", "{$root}{$tmp}.js");
}
}
}
Результат: При создании нового шаблона автоматически генерируются файлы .tpl, .css, .js на основе базовых шаблонов.
ActionDrop: Предобработка перед удалением
Назначение
Класс ActionDrop выполняется перед удалением элемента из БД и позволяет:
- Удалить связанные записи (каскадное удаление)
- Удалить физические файлы
- Выполнить проверки перед удалением
- Отменить удаление при определенных условиях
Базовый пример: Удаление таблицы
Настройка в админке:
s_Config → s_Config → ActionDrop = RemoveItemConfig.php
Класс-обработчик:
<?php
namespace WeppsAdmin\Lists\Actions;
use WeppsCore\Request;
use WeppsCore\Connect;
class RemoveItemConfig extends Request {
public $noclose = 1;
public $listSettings = [];
public $id;
public $element = [];
public function request($action = "") {
$this->listSettings = $this->get['listSettings'];
$this->id = (int) $this->get['id'];
if ($this->listSettings['TableName'] != 's_Config') {
return;
}
// Получаем данные удаляемого элемента
$sql = "SELECT * FROM s_Config WHERE Id = ?";
$res = Connect::$instance->fetch($sql, [$this->id]);
if (!isset($res[0]['Id'])) {
return;
}
$this->element = $res[0];
// Удаляем настройки полей таблицы
$sql = "DELETE FROM s_ConfigFields WHERE TableName = ?;\n";
$sql .= "DROP TABLE IF EXISTS {$this->element['TableName']};\n";
Connect::$db->exec($sql);
}
}
Результат: При удалении записи из s_Config автоматически удаляется физическая таблица и все связанные настройки полей.
Продвинутый пример: Каскадное удаление
<?php
namespace WeppsAdmin\Lists\Actions;
use WeppsCore\Request;
use WeppsCore\Connect;
use WeppsCore\NavigatorData;
class RemoveItemDirectories extends Request {
public $noclose = 1;
public $listSettings = [];
private $id;
public $element = [];
public function request($action = "") {
$this->listSettings = $this->get['listSettings'];
$this->id = (int) $this->get['id'];
if ($this->listSettings['TableName'] != 's_Navigator') {
return;
}
// Защита от удаления корневого раздела
if ($this->id == 1) {
Connect::$instance->close();
return;
}
// Получаем все дочерние разделы
$nav = new NavigatorData("s_Navigator");
$child = $nav->getRChild($this->id);
if (count($child) != 0) {
// Удаляем текущий раздел и все дочерние
$str = "0," . implode(",", $child);
$sql = "DELETE FROM s_Navigator WHERE Id IN ({$str})";
Connect::$db->query($sql);
}
}
}
Результат: При удалении раздела навигации автоматически удаляются все вложенные подразделы.
Кейс: Удаление поля из таблицы
<?php
namespace WeppsAdmin\Lists\Actions;
use WeppsCore\Request;
use WeppsCore\Connect;
class RemoveItemConfigFields extends Request {
public $noclose = 1;
public $listSettings = [];
private $id;
public $element = [];
public function request($action = "") {
$this->listSettings = $this->get['listSettings'];
$this->id = (int) $this->get['id'];
if ($this->listSettings['TableName'] != 's_ConfigFields') {
return;
}
$sql = "SELECT * FROM s_ConfigFields WHERE Id = ?";
$res = Connect::$instance->fetch($sql, [$this->id]);
if (!isset($res[0]['Id'])) {
return;
}
$this->element = $res[0];
// Удаляем физический столбец из таблицы
$sql = "ALTER TABLE {$this->element['TableName']}
DROP COLUMN {$this->element['Field']}";
Connect::$instance->query($sql);
}
}
Результат: При удалении записи из s_ConfigFields автоматически удаляется соответствующий столбец из физической таблицы.
Практические примеры
Пример 1: Автоматическое заполнение артикула
Задача: При создании товара автоматически генерировать артикул на основе ID.
<?php
namespace WeppsAdmin\Lists\Actions;
use WeppsCore\Request;
use WeppsCore\Connect;
class SaveItemProducts extends Request {
public $noclose = 1;
public $listSettings = [];
public $element = [];
public function request($action = "") {
$this->listSettings = $this->get['listSettings'];
$this->element = $this->get['element'];
if ($this->listSettings['TableName'] != 'Products') {
return;
}
// Генерация артикула, если не указан
if (empty($this->element['SKU'])) {
$sku = 'PROD-' . str_pad($this->element['Id'], 6, '0', STR_PAD_LEFT);
$sql = "UPDATE Products SET SKU = ? WHERE Id = ?";
Connect::$instance->query($sql, [$sku, $this->element['Id']]);
}
}
}
Настройка: s_Config → Products → ActionModify = SaveItemProducts.php
Пример 2: Фильтр списка по статусу
Задача: Показывать в списке заказов только активные заказы, с возможностью переключения.
<?php
namespace WeppsAdmin\Lists\Actions;
use WeppsCore\Request;
class ViewListOrders extends Request {
public $noclose = 1;
public $condition = "";
public function request($action = "") {
$status = $_GET['status'] ?? 'active';
switch ($status) {
case 'active':
$this->condition = "t.Status IN ('new', 'processing', 'shipping')";
break;
case 'completed':
$this->condition = "t.Status = 'completed'";
break;
case 'cancelled':
$this->condition = "t.Status = 'cancelled'";
break;
default:
$this->condition = "1=1";
}
}
}
Настройка: s_Config → Orders → ActionShow = ViewListOrders.php
Пример 3: Добавление вычисляемого поля
Задача: Показывать общую стоимость товаров в списке (цена × количество).
<?php
namespace WeppsAdmin\Lists\Actions;
use WeppsCore\Request;
class ViewListProducts extends Request {
public $noclose = 1;
public $fields = "";
public $scheme = [];
public function request($action = "") {
$this->scheme = $this->get['listScheme'];
// Добавляем вычисляемое поле
$this->fields = "(t.Price * t.Stock) as TotalValue";
// Регистрируем поле в схеме
$this->scheme['TotalValue'] = [
'Field' => 'TotalValue',
'Name' => 'Общая стоимость',
'Type' => 'digit'
];
}
}
Настройка: s_Config → Products → ActionShow = ViewListProducts.php
Пример 4: Валидация перед удалением
Задача: Запретить удаление категории, если в ней есть товары.
<?php
namespace WeppsAdmin\Lists\Actions;
use WeppsCore\Request;
use WeppsCore\Connect;
use WeppsCore\Utils;
class RemoveItemCategories extends Request {
public $noclose = 1;
public $listSettings = [];
private $id;
public function request($action = "") {
$this->listSettings = $this->get['listSettings'];
$this->id = (int) $this->get['id'];
if ($this->listSettings['TableName'] != 'Categories') {
return;
}
// Проверяем наличие товаров в категории
$sql = "SELECT COUNT(*) as cnt FROM Products WHERE CategoryId = ?";
$res = Connect::$instance->fetch($sql, [$this->id]);
if ($res[0]['cnt'] > 0) {
// Прерываем выполнение и показываем ошибку
Utils::debug("Невозможно удалить категорию: в ней есть товары ({$res[0]['cnt']} шт.)", 1);
Connect::$instance->close();
}
}
}
Настройка: s_Config → Categories → ActionDrop = RemoveItemCategories.php
Передаваемые данные в Action-классы
Для ActionShow (список)
$this->get = [
'listSettings' => [...], // Настройки из s_Config
'listScheme' => [...] // Схема полей из s_ConfigFields
];
Возвращаемые свойства:
$this->condition- WHERE условия$this->fields- дополнительные поля для SELECT$this->scheme- измененная схема полей
Для ActionShowId (просмотр элемента)
$this->get = [
'listSettings' => &[...], // Настройки из s_Config (по ссылке)
'listScheme' => &[...], // Схема полей (по ссылке)
'element' => &[...], // Данные элемента (по ссылке)
'headers' => &$headers // Объект TemplateHeaders (по ссылке)
];
Возможности изменения:
$this->settings['ActionShowIdAddons']- добавление вкладок$this->element- изменение данных элемента$this->scheme- изменение схемы полей$this->headers->js()/$this->headers->css()- подключение файлов
Для ActionModify (сохранение)
$this->get = [
'listSettings' => [...], // Настройки из s_Config
'listScheme' => [...], // Схема полей
'element' => [...] // Сохраненные данные элемента
];
Доступ к данным:
$this->element['Id']- ID сохраненного элемента$this->element['Name']- значения всех полей$this->listSettings['TableName']- имя таблицы
Для ActionDrop (удаление)
$this->get = [
'listSettings' => [...], // Настройки из s_Config
'id' => 123 // ID удаляемого элемента
];
Примечание: Данные элемента нужно получать дополнительным запросом.
Паттерны и лучшие практики
1. Guard Clause (Ранний выход)
Используйте ранний return для проверки условий:
public function request($action = "") {
// Guard clauses
if ($this->listSettings['TableName'] != 'Products') {
return;
}
if (empty($this->element['Id'])) {
return;
}
// Основная логика только для нужной таблицы
// ...
}
Преимущества:
- Улучшенная читаемость кода
- Меньше вложенности
- Явные условия выполнения
2. Передача по ссылке
В ActionShowId данные передаются по ссылке - изменения применяются автоматически:
public function request($action = "") {
// Используем ссылки
$this->element = &$this->get['element'];
$this->scheme = &$this->get['listScheme'];
// Изменения применяются автоматически
$this->element['CustomField'] = 'New Value';
}
3. Безопасность SQL-запросов
Всегда используйте подготовленные запросы:
// НЕПРАВИЛЬНО - SQL-инъекция!
$sql = "SELECT * FROM Products WHERE Id = {$this->element['Id']}";
// ПРАВИЛЬНО - безопасный запрос
$sql = "SELECT * FROM Products WHERE Id = ?";
$res = Connect::$instance->fetch($sql, [$this->element['Id']]);
4. Использование Connect::$instance
Глобальный экземпляр для работы с БД:
// Выборка данных
$sql = "SELECT * FROM table WHERE id = ?";
$result = Connect::$instance->fetch($sql, [$id]);
// Один запрос
$sql = "UPDATE table SET field = ? WHERE id = ?";
Connect::$instance->query($sql, [$value, $id]);
// Несколько запросов
$sql = "DELETE FROM table1 WHERE id = ?;\n";
$sql .= "DELETE FROM table2 WHERE ref_id = ?;\n";
Connect::$db->exec($sql);
5. Обработка ошибок
Используйте Utils::debug() для вывода ошибок:
use WeppsCore\Utils;
if (!$validationPassed) {
Utils::debug("Ошибка валидации: некорректные данные", 1);
Connect::$instance->close();
}
Диагностика и отладка
Проверка выполнения Action-класса
Добавьте временный вывод в начало метода:
public function request($action = "") {
error_log("Action executed: " . __CLASS__);
// ...
}
Просмотр передаваемых данных
public function request($action = "") {
file_put_contents(
Connect::$projectDev['root'] . '/debug.log',
print_r($this->get, true),
FILE_APPEND
);
}
Проверка SQL-запросов
$sql = "SELECT * FROM Products WHERE CategoryId = ?";
error_log("SQL: " . $sql);
error_log("Params: " . print_r([$categoryId], true));
$result = Connect::$instance->fetch($sql, [$categoryId]);
Частые ошибки
1. Отсутствие Guard Clause
// НЕПРАВИЛЬНО - выполняется для всех таблиц
public function request($action = "") {
$sql = "UPDATE Products SET ..."; // Ошибка, если не Products
}
// ПРАВИЛЬНО - проверка таблицы
public function request($action = "") {
if ($this->listSettings['TableName'] != 'Products') {
return;
}
$sql = "UPDATE Products SET ...";
}
2. Забыли $noclose = 1
// НЕПРАВИЛЬНО - запрос завершится преждевременно
class MyAction extends Request {
public function request($action = "") {
// ...
}
}
// ПРАВИЛЬНО
class MyAction extends Request {
public $noclose = 1;
public function request($action = "") {
// ...
}
}
3. Неправильное имя класса
// Файл: SaveItemProducts.php
// НЕПРАВИЛЬНО - имя не совпадает с файлом
class SaveProducts extends Request { }
// ПРАВИЛЬНО
class SaveItemProducts extends Request { }
4. SQL-инъекции
// НЕПРАВИЛЬНО - опасно!
$sql = "DELETE FROM {$this->element['TableName']} WHERE Id = {$id}";
// ПРАВИЛЬНО - использовать только для системных таблиц с валидацией
if (!preg_match('/^[a-zA-Z_]+$/', $this->element['TableName'])) {
return;
}
Резюме
Action-обработчики в Wepps Platform предоставляют мощный механизм расширения стандартного функционала административной панели без изменения ядра системы.
Ключевые возможности:
| Поле | Когда выполняется | Основное применение |
|---|---|---|
ItemsFields |
При загрузке списка | Оптимизация выборки данных |
ActionShow |
При отображении списка | Фильтрация, добавление полей |
ActionShowId |
При открытии элемента | Добавление вкладок, изменение данных |
ActionModify |
После сохранения | Постобработка, создание связей |
ActionDrop |
Перед удалением | Каскадное удаление, валидация |
Основные принципы:
- Один класс = одна ответственность
- Guard Clause для ранних проверок
- Безопасность через подготовленные запросы
- Передача по ссылке в
ActionShowId - Прозрачная интеграция без изменения ядра
Когда использовать:
- ✅ Нужна кастомная логика для конкретной таблицы
- ✅ Требуется автоматизация рутинных операций
- ✅ Необходимо расширить интерфейс редактирования
- ✅ Нужно оптимизировать производительность списков
Когда не использовать:
- ❌ Простые операции, которые можно сделать через схему полей
- ❌ Логика, общая для всех таблиц (лучше изменить ядро)
- ❌ Сложная бизнес-логика (создайте отдельное расширение)