Файл options.php в модуле Битрикса отвечает за страницу настроек: ключи API, адреса, флаги, всё, что не хочется зашивать в код. Админка сама находит этот файл по идентификатору модуля и показывает его содержимое в общем списке настроек. Ниже я собираю такую страницу с нуля: каркас с вкладками, сохранение, чтение значений в коде модуля и права доступа. Код рассчитан на модуль в каталоге /local/modules/, потому что кастомизацию Битрикс по документации ведёт через /local, а не через /bitrix.
Страница настроек модуля Битрикс: где лежит файл и как открывается
Модуль живёт в каталоге /local/modules/vendor.mymodule/, где vendor.mymodule это идентификатор модуля в формате «производитель.название». Минимальный набор файлов выглядит так:
install/index.php- класс установщика модуля;include.php- подключается приLoader::includeModule();options.php- сама страница настроек;default_option.php- значения параметров по умолчанию.
Страница открывается по адресу /bitrix/admin/settings.php?mid=vendor.mymodule&lang=ru. Параметр mid это идентификатор модуля, и именно по нему settings.php подключает ваш options.php. Файл исполняется в контексте административной страницы, поэтому глобальные $USER и $APPLICATION в нём доступны без дополнительных действий. Модуль должен быть установлен, иначе страницу настроек для него админка не покажет.
Первое правило, которое я соблюдаю всегда: в начале файла проверка прав. Настройки с ключами API нельзя отдавать никому, кроме администратора, поэтому файл начинается с if (!$USER->IsAdmin()) { return; }.
Каркас настроек модуля: вкладки и поля
Страница настроек строится из трёх штатных кусков: массив вкладок, объект CAdminTabControl и функция __AdmSettingsDrawRow(), которая рисует строку таблицы по описанию поля. Описание поля это массив из четырёх элементов: имя параметра, подпись, значение по умолчанию и массив с типом. Поддерживаются типы text, textarea, checkbox и selectbox. Для первого типа вторым элементом идёт ширина поля, для textarea число строк и колонок, для selectbox массив пар «значение - подпись».
Ниже полный рабочий options.php с одной вкладкой и тремя полями. Адрес API, ключ и флаг отладки я взял потому, что почти любая интеграция упирается именно в такой набор.
<?php
use Bitrix\Main\Config\Option;
use Bitrix\Main\HttpApplication;
use Bitrix\Main\Loader;
if (!$USER->IsAdmin()) {
return;
}
$module_id = 'vendor.mymodule';
Loader::includeModule($module_id);
$request = HttpApplication::getInstance()->getContext()->getRequest();
$options = [
['api_url', 'Адрес API', 'https://api.example.com', ['text', 50]],
['api_key', 'Ключ API', '', ['text', 50]],
['debug', 'Писать отладочный лог', 'N', ['checkbox']],
];
$aTabs = [
[
'DIV' => 'edit1',
'TAB' => 'Основные',
'TITLE' => 'Основные настройки',
'OPTIONS' => $options,
],
];
$tabControl = new CAdminTabControl('tabControl', $aTabs);
if ($request->isPost() && $request->getPost('Update') !== null && check_bitrix_sessid()) {
foreach ($aTabs as $aTab) {
foreach ($aTab['OPTIONS'] as $arOption) {
$name = $arOption[0];
$value = $request->getPost($name);
if ($arOption[3][0] === 'checkbox') {
$value = ($value === 'Y') ? 'Y' : 'N';
}
Option::set($module_id, $name, (string)$value);
}
}
LocalRedirect(
$APPLICATION->GetCurPage() . '?mid=' . urlencode($module_id)
. '&lang=' . LANGUAGE_ID . '&' . $tabControl->ActiveTabParam()
);
}
$tabControl->Begin();
?>
<form method="post" action="<?= $APPLICATION->GetCurPage() ?>?mid=<?= urlencode($module_id) ?>&lang=<?= LANGUAGE_ID ?>">
<?= bitrix_sessid_post() ?>
<?php
foreach ($aTabs as $aTab) {
$tabControl->BeginNextTab();
foreach ($aTab['OPTIONS'] as $arOption) {
__AdmSettingsDrawRow($module_id, $arOption);
}
}
$tabControl->Buttons();
?>
<input type="submit" name="Update" value="Сохранить" class="adm-btn-save">
<?php $tabControl->End(); ?>
</form>
Две детали, на которых спотыкаются чаще всего. Первая: чекбокс в POST не приходит, если он не отмечен, поэтому без ветки с checkbox выключить флаг через интерфейс будет невозможно, старое значение «Y» останется навсегда. Вторая: после сохранения я делаю редирект на ту же страницу. Тогда обновление страницы в браузере не отправит форму повторно, а ActiveTabParam() возвращает пользователя на ту вкладку, где он нажимал кнопку.
Бесплатный материал
🎁 Полезный скрипт в подарок
Подпишитесь на Telegram - пришлю готовый скрипт по этой теме.
Без спама. Отписка в 1 клик.
Сохранение параметров через Option::set
Параметры модуля Битрикс хранит в таблице настроек, а работать с ней нужно через Option::set() и Option::get() из D7. Старый COption::SetOptionString() тоже живёт в ядре, оба варианта пишут в одно место, но в новом коде я беру D7: документация сама отправляет с процедурных методов на новое ядро. Первый аргумент всегда идентификатор модуля, второй имя параметра, третий значение. Значение это строка, поэтому массивы перед записью нужно сериализовать, а числа приводить обратно при чтении.
Перед тем как писать настройки, стоит решить, где им вообще место. Вот как я раскладываю:
| Способ | Где хранится | Когда выбираю |
|---|---|---|
Option::set |
таблица настроек в базе | параметры, которые меняет администратор: ключи, адреса, флаги |
/bitrix/.settings.php |
файл конфигурации ядра | параметры окружения, которые не должны меняться из интерфейса |
| собственная таблица D7 ORM | отдельная таблица модуля | списки записей: правила, соответствия, справочники |
Список из десятка правил в одном строковом параметре это путь к боли. Для таких данных нужна отдельная таблица, а в options.php остаются только скалярные параметры.
Если в форме есть секретное значение, например токен, помните про диагностику. На проектах я не вывожу объекты Битрикса через print_r или var_dump: внутри результата выборки может оказаться подключение к базе с паролем MySQL. Для отладки настроек хватает вывода скалярных значений: echo Option::get(...).
Несколько сайтов и параметры по сайту
Четвёртый аргумент Option::set() это идентификатор сайта. Если настройки у разных сайтов на одной установке должны отличаться, я добавляю на страницу выбор сайта и передаю его в оба метода. Для одного сайта этот аргумент не нужен, и усложнять форму раньше времени бессмысленно.
Чтение настроек в коде и значения по умолчанию
В коде модуля параметр читается так:
<?php
use Bitrix\Main\Config\Option;
$apiUrl = Option::get('vendor.mymodule', 'api_url', '');
$debug = Option::get('vendor.mymodule', 'debug', 'N') === 'Y';
Третий аргумент это значение на случай, если параметр ни разу не сохраняли. Если его опустить, Битрикс попробует взять значение из файла default_option.php в корне модуля. Файл объявляет массив, имя которого строится из идентификатора модуля с заменой точки на подчёркивание и суффиксом _default_option:
<?php
$vendor_mymodule_default_option = [
'api_url' => 'https://api.example.com',
'api_key' => '',
'debug' => 'N',
];
Я держу значения по умолчанию в двух местах сразу: в default_option.php и в описании полей на странице настроек. Дублирование выглядит неряшливо, но на практике оно спасает: форма показывает то же значение, которое использует код, пока администратор ничего не сохранил.
Кэш и «не вижу новое значение»
Option::set() сбрасывает кэш настроек сам, поэтому после сохранения через форму новое значение доступно сразу. Проблемы начинаются, когда параметр правят мимо API, прямым SQL по таблице настроек. Тогда сайт продолжает читать старое значение из кэша. Я правлю настройки только через API, и если нужно массово выставить параметры скриптом, подключаю ядро из командной строки и вызываю тот же Option::set().
Права доступа и вкладка доступа в настройках модуля
Проверки IsAdmin() достаточно, пока настройки нужны только администратору. Если модуль должен различать роли, например «читает отчёты» и «меняет ключи», в Битрикс есть стандартная вкладка прав. Её подключают отдельной вкладкой в массиве $aTabs с 'DIV' => 'edit2' и подтягивают в форме так же, как остальные:
<?php
$tabControl->BeginNextTab();
require_once $_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/admin/group_rights.php';
Дальше права читаются в коде модуля через $APPLICATION->GetGroupRight('vendor.mymodule'), а результат сравнивается с буквенными уровнями доступа. Если вкладка прав нужна, сохранение этого блока тоже должно быть в обработчике формы. Подробности лучше сверить с документацией на вашей версии ядра, потому что набор уровней задаётся установщиком модуля, а не страницей настроек. Для модуля из двух параметров, которым пользуется один человек, вкладку прав я не добавляю.
Типичные ошибки при работе со страницей настроек
За несколько модулей, которые я дорабатывал или писал, набрался стабильный список граблей:
- Нет
check_bitrix_sessid(). Форма сохраняется, но любой сторонний сайт может отправить на неё POST от имени залогиненного администратора. Проверка сессии иbitrix_sessid_post()в форме обязательны. - Чекбокс не снимается. Описано выше: отсутствие поля в POST нужно трактовать как «N».
- Страница настроек пустая. Чаще всего
options.phpзавершился наreturnиз-за прав или модуль не установлен. Проверьте, что вы зашли под администратором и идентификатор вmidсовпадает с именем каталога модуля. - Файлы правятся в
/bitrix/modules/. Всё, что вы там меняете, перезатрёт обновление ядра. Свой модуль живёт в/local/modules/, и при совпадении имён приоритет у/local. - Секреты в открытом виде. Токен в текстовом поле виден любому администратору. Для платёжных ключей я ограничиваю круг тех, кто вообще заходит на страницу, и не пишу значения в логи.
- Бэкапы рядом с файлом. Копия
options.php.bakв каталоге модуля может отдаваться веб-сервером. Резервные копии кладу внеpublic_html.
Если страница нужна под конкретную интеграцию, а времени разбираться с установщиком, правами и кэшем нет, такие задачи я закрываю в рамках услуги по доработке и поддержке 1С-Битрикс, от 3 000 ₽ в час. Для типовой страницы с парой полей хватает кода из этой статьи.
Проверка страницы перед передачей клиенту
Перед тем как отдать модуль, я прохожу один и тот же короткий сценарий. Открываю страницу под администратором и под пользователем без прав, во втором случае страница должна быть пустой. Сохраняю форму, отмечаю чекбокс, сохраняю, снимаю, снова сохраняю, и после каждого шага перезагружаю страницу. Проверяю, что Option::get() в коде модуля возвращает новое значение сразу, без чистки кэша. Затем удаляю параметр из базы через API и убеждаюсь, что подставилось значение из default_option.php. Весь прогон занимает несколько минут, зато ошибки с чекбоксом и сессией ловятся до клиента, а не после.
Условия и возможности 1С-Битрикс меняются, поэтому перед внедрением я сверяюсь с актуальной документацией разработчика.
Частые вопросы
Куда класть options.php в модуле?
В корень каталога модуля: /local/modules/vendor.mymodule/options.php. Файл не нужно регистрировать отдельно, админка подключает его сама, когда вы открываете settings.php с параметром mid, равным идентификатору модуля.
Чем Option::set отличается от COption::SetOptionString?
Оба метода пишут в одно хранилище настроек. Option::set относится к D7, COption::SetOptionString к старому ядру. В новом коде я использую D7, а старый вызов встречаю в чужих модулях и не переписываю, пока он работает.
Можно ли хранить в настройках модуля массив?
Технически да: значение это строка, и массив можно сериализовать, например в JSON. Но для списков правил и соответствий удобнее собственная таблица D7 ORM, её проще редактировать и фильтровать. В параметры стоит класть скалярные значения.
Почему после сохранения форма показывает старое значение?
Чаще всего причина в том, что Option::get() в форме читает значение с другим идентификатором модуля или другим сайтом, чем Option::set() при сохранении. Вторая причина: параметр правили прямым SQL, и кэш не сбросился. Запись только через API снимает обе проблемы.