Перейти к содержимому

Своя форма элемента инфоблока - файлы до и после формы

Разбираем, как вклиниться в штатную форму правки элемента инфоблока: файл до формы и файл после формы, своя проверка значений и действие после сохранения. Заодно смотрим, почему такая подмена переживает обновление платформы.

Механика

Штатную форму правки элемента рисует сам модуль инфоблоков, и страница у неё одна на все инфоблоки сразу. Набор полей и свойств в ней меняется, а разметка и порядок обработки остаются общими. Поэтому вклиниваются в неё не своей вёрсткой, а заранее отведёнными точками подключения.

Таких точек ровно две, и обе задаются в настройках конкретного инфоблока. Первая подключает ваш файл до вывода формы, вторая сразу после него. Пути хранятся полями самого инфоблока, поэтому соседние инфоблоки чужой код не выполняют.

Файл до формы работает раньше сохранения элемента и раньше любого вывода на страницу. Здесь принимают отправленные данные, сверяют их со своими требованиями и решают судьбу записи. Здесь же объявляют функции, которые ядро вызовет позже по ходу сохранения.

Файл после формы выполняется тогда, когда разметка формы уже отправлена в браузер. Проверять в нём значения поздно, зато дорисовать подсказку, скрипт или блок помощи самое время.

Своя проверка останавливает сохранение объектом ошибки инфоблока. Ядро подхватывает такой объект и печатает сообщение над формой привычным способом. Редактор видит обычную ошибку платформы, а не постороннее сообщение посреди страницы.

Вместе с ошибкой взводят признак возврата введённых значений. Без него форма перечитает элемент из базы и затрёт всё, что человек успел набрать руками.

Хук после сохранения вызывается уже после записи элемента и его свойств. К этому моменту идентификатор нового элемента известен, и его можно использовать дальше. Объявляют такую функцию именно в файле до формы, иначе ядро не найдёт её вовремя.

Полная замена формы собственным файлом страницы решает больше задач, но обходится дороже. Такая страница не наследует штатную панель кнопок, и первой пропадает кнопка настройки формы. Возвращают её собственным контекстным меню вместе с остальными нужными кнопками.

Штатная настройка формы закрывает заметную часть запросов вообще без единой строки кода. Порядок полей, свои вкладки и подсказки к свойствам собираются в ней мышкой. Код нужен там, где требуется проверка значений или действие после сохранения элемента.

Всё, что лежит в каталогах ядра платформы, обновление рано или поздно перезаписывает. Файлы до и после формы держат в каталоге проекта, а в инфоблоке хранится только путь к ним. Такая подмена переживает обновление именно потому, что ядро остаётся нетронутым.

Шаги

  1. Проверить, решается ли задача штатной настройкой формы без всякого кода.
  2. Завести файлы до и после формы в каталоге проекта, а не в каталогах ядра.
  3. Прописать пути к этим файлам в настройках нужного инфоблока.
  4. Написать проверку значений в файле до формы и вернуть введённые данные при ошибке.
  5. Объявить там же функцию после сохранения и дописать вывод в файле после формы.

Код

Смотрим, что назначено инфоблоку сейчас:

\Bitrix\Main\Loader::includeModule('iblock');
$arIBlock = CIBlock::GetArrayByID($iblockId);
printf("до формы: %s\n", $arIBlock['EDIT_FILE_BEFORE'] ?: 'не задан');
// пути хранятся у самого инфоблока, а не в настройках модуля
printf("после формы: %s\n", $arIBlock['EDIT_FILE_AFTER'] ?: 'не задан');
// пути хранятся относительно корня сайта и действуют только для этого инфоблока

Оба пути принадлежат самому инфоблоку, а не сайту целиком. Инфоблок товаров и инфоблок торговых предложений настраивают отдельно, даже когда файл у них общий.

Назначаем оба файла из кода:

$ib = new CIBlock(); // Update у этого класса нестатический
$ib->Update($iblockId, [
'EDIT_FILE_BEFORE' => '/local/php_interface/include/iblock_edit/catalog_before.php',
// путь считается от корня сайта, файл вне каталога ядра переживёт обновление
'EDIT_FILE_AFTER' => '/local/php_interface/include/iblock_edit/catalog_after.php',
]);
// то же самое задаётся руками в настройках инфоблока

Каталог для таких файлов платформа сама не создаёт. Его заводят руками, и путь в настройках инфоблока считается от корня сайта, а не от каталога модуля.

Смотрим состав запроса перед проверкой:

/local/php_interface/include/iblock_edit/catalog_before.php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
file_put_contents($_SERVER['DOCUMENT_ROOT'] . '/upload/form_post.log',
print_r($_POST, true));
}
// имена полей у свойств разных типов различаются: их берут из этого дампа

Имена полей формы у свойств зависят от типа свойства и от его множественности. Гадать по документации дольше, чем один раз посмотреть настоящий запрос формы.

Проверяем значения и останавливаем сохранение:

// проверка идёт до записи: значения ещё лежат в массиве отправки формы
if ($_SERVER['REQUEST_METHOD'] === 'POST' && trim($_POST['DETAIL_TEXT']) === '') {
$error = new _CIBlockError(2, 'DESCRIPTION_REQUIRED',
'Введите текст статьи');
$bVarsFromForm = true; // форма покажет введённое, а не данные из базы
}

Объект ошибки инфоблока выводится над формой штатным оформлением платформы. Признак возврата значений спасает набранное: без него редактор потеряет весь текст и наберёт его заново.

Дописываем действие после сохранения:

function BXIBlockAfterSave($arFields) // объявляется в файле ДО формы
// имя функции фиксировано: платформа ищет именно его и вызывает сама
{
// элемент уже записан: идентификатор известен даже при создании нового
CIBlockElement::SetPropertyValuesEx($arFields['ID'], $arFields['IBLOCK_ID'],
['SLUG' => 'item-' . $arFields['ID']]);
}

Хук срабатывает после записи элемента и его свойств, а не вместо неё. Отменить сохранение из него уже нельзя: для отмены есть проверка выше по файлу.

Дорисовываем подсказку в файле после формы:

/local/php_interface/include/iblock_edit/catalog_after.php
echo '<div class="adm-info-message">Текст статьи короче 300 знаков '
. 'поисковые системы обычно игнорируют.</div>';
// проверять значения здесь поздно: форма уже ушла в браузер

Файл после формы удобен для подсказок, счётчиков и своих скриптов. Всё, что влияет на сохранение, ставят в файл до формы, иначе оно просто не успеет сработать.

Собираем своё поле, когда форму заменяют целиком:

$tabControl->BeginCustomField('UF_MAP', 'Точка на карте');
echo $tabControl->GetCustomLabelHTML(); // подпись в общем оформлении формы
echo '<input type="text" name="UF_MAP" value="">';
$tabControl->EndCustomField('UF_MAP');
// имя формы для своих скриптов даёт GetFormName()

Методы своего поля держат разметку в общем виде административной формы. Без них поле выпадает из таблицы формы и выглядит чужеродной вставкой.

Возвращаем кнопки панели своей странице правки:

$menu = new CAdminContextMenu([
['TEXT' => CIBlock::GetArrayByID($iblockId, 'ELEMENT_EDIT'),
'LINK' => 'iblock_element_admin.php?IBLOCK_ID=' . $iblockId, 'ICON' => 'btn_list'],
]);
$menu->Show();
// сюда же возвращают пункт настройки формы: своя страница штатной панели не наследует

Подписи кнопок берут из системных фраз самого инфоблока. Так они остаются переведёнными и совпадают с надписями на соседних штатных страницах.

Ограничения

Файлы до и после формы подключает только страница правки в административной части. Сохранение элемента из кода, из компонента формы или обменом с внешней системой их не выполняет.

Проверка в файле до формы не заменяет собой обработчик события. Данные, которые обязаны быть верными при любом способе записи, проверяют событием элемента инфоблока.

Настройка принадлежит одному инфоблоку и не наследуется от типа инфоблоков. Каждый новый инфоблок с той же логикой прописывают отдельно, включая инфоблок торговых предложений.

Полная замена формы своей страницей лишает элемент штатной панели кнопок. Кнопка настройки формы, переход к списку и удаление возвращаются только собственным контекстным меню.

Административная часть платформы остаётся на старом ядре целиком. Современного набора классов для формы элемента нет, и код здесь пишут в процедурном стиле.

Типичные проблемы

Правка формы пропала после обновления платформы.

Изменения внесены прямо в файл страницы правки внутри каталога модуля инфоблоков. Обновление перезаписывает файлы ядра целиком, вместе со всеми вставками.

Каталога для файлов формы вообще нет на сайте.

Платформа этот каталог сама не создаёт и пустым его не поставляет. Его заводят руками, а путь к файлу прописывают в настройках инфоблока.

После ошибки проверки форма очистилась.

Не взведён признак возврата введённых значений из запроса. Форма перечитала элемент из базы и затёрла всё, что редактор набрал.

Функция после сохранения ни разу не сработала.

Она объявлена в файле после формы, а сохранение к этому моменту давно прошло. Объявлять её нужно в файле до формы, который выполняется раньше записи.

Своя вкладка не видна при правке торгового предложения.

Файлы назначены инфоблоку товаров, а предложения лежат в отдельном инфоблоке. Настройку прописывают обоим инфоблокам, иначе форма предложения останется штатной.

У формы элемента пропала кнопка настройки формы.

Штатная форма заменена собственной страницей правки, а панель кнопок такая страница не наследует. Кнопки возвращают своим контекстным меню в начале страницы.

Частые вопросы

Как изменить форму элемента, например добавить поясняющий текст рядом с полем?

Файлом после формы: он подключается сразу за разметкой и печатает что угодно. Путь к нему прописывают в настройках нужного инфоблока.

Как добавить подсказку для поля, а не для свойства?

Штатная настройка формы даёт подсказки только свойствам. Для полей подсказку выводят своим файлом после формы или скриптом на странице.

Как добавить свои вкладки в форму редактирования элемента инфоблока?

Сначала стоит открыть штатную настройку формы: вкладки и перенос свойств делаются там мышкой. Код нужен, только когда вкладка собирает данные не из свойств.

Можно ли показать сообщение в форме, не трогая кастомизацию формы?

Штатной точки для этого нет, но подмена формы и не требуется. Сообщение печатают из файла до формы или после неё, а сама форма остаётся штатной.

Работает ли то же самое для highload-блока?

Нет, файлы до и после формы есть только у инфоблока. Форму highload-блока меняют своим типом поля или отдельной страницей правки.

Смежное

Первоисточник