Содержание
С момента написания первой верии модуля для 1С-Битрикс прошло около года, но именно теперь потребовалось внедрить поддержку мультисайтовости. Первым делом нужно было переделать настройки, но по ходу дела этот вопрос был раскрыт более шире.
Здесь вскользь что-то там упоминается про настройки. Этого недостаточно, пришлось искать на сторонних сайтах как сделать настройки для своего модуля 1С-Битрикс.
Оказалось не сложно ... до тех пор пока не потребовалась поддержка мультисайтовости. Но это можно подсмотреть в модулях "из коробки", например хороший пример модуль бизнес процессы файл настроек которого располагается по пути bitrix/modules/bizproc/options.php
.
В общем, обширным источником знаний об организации настроек в своем модуле для 1С-Битрикс являются исходники модулей из коробки.
Дополнительно можно посмотреть написанный мной модуль для 1С-Битрикс (одна из причин появления этой статьи).
Организация настроек модуля храниться в файле options.php
в директории модуля.
Это выглядит как-то не консистентно, с одной стороны инфраструктура движка предоставляет богатый функционал для работы с движком и модулем интернет-магазина (есть даже почти PSR4) с возможностью отделить логику от представления, с другой стороны настройки модулей без таких возможностей.
Однако, по ходу реализации настроек модуля можно попытаться отделить логику от представления, но не будем пока отходить от принятой концепции :)
Файл будет содрежать следующие разделы кода:
Потребуется 3 объекта ядра движка запрос, настроки, загрузчик:
phpuse Bitrix\Main\HttpApplication; use Bitrix\Main\Config\Option; use Bitrix\Main\Loader;
Затем необходимо получить текущий запрос и загрузить модуль если в логике настроек (сохранение/проверка) используется код модуля, как было в моем случае:
php$request = HttpApplication::getInstance()->getContext()->getRequest(); // идентификатор модуля это название его директории, а файл настроек находится по пути idModule/options.php $idModule = dirname(__DIR__); Loader::includeModule($idModule);
Теперь массив настроек:
php$aTabs = [ [ "DIV" => "css класс", "TAB" => "Название вкладки настроек модуля", "OPTIONS" => [ [ "id_point", // идентификатор пункта настроек "Label", // надпись возле пункта настроек "default", // значение по умолчанию // опиание GUI элемента, для каждого вида свое описание [ "text", 11 ] ], ... ] ] ];
Страница настроек модуля состоит из вкладок, в массиве $aTabs
, каждая вкладка должна располагаться в отдельном массиве.
В ключе OPTIONS
располагаются непосредственно настройки вкладки. Рассмотрим несколько видов GUI элементов:
text
- тестовое полеphp[ "token", "Токен компании:", "", // по умолчанию пусто [ "text", 20 // размер строки ] ]
checkbox
, принимает значения Y
(отмечено) или N
(не отмечено)php[ "only2", "Создавать только второй чек:", "N", // по умолчанию не отмечено ["checkbox"] ]
selectbox
- выдападающий списокphp/* $aPaySystems - ассоциативный массив систем оплат где: - ключ - идентификатор системы оплаты - значение - название */ [ "paysystem", "Создавать чек для оплаты:", array_keys($aPaySystems)[0], [ "selectbox", $aPaySystems ] ]
Отправка настроек осуществляется POST
запросом на тот же файл options.php
(в следующем разделе разберем этот момент). Поэтому следующим разделом в файле options.php
будет обработка отправленных настроек.
Для начала нужно проверить что пришел POST
запрос с актуальным идентификатором сессии:
phpif($request->isPost() && check_bitrix_sessid()) { // принятие // проверка // сохранение }
Теперь внутри мы можем получить отправленные настройки проверить и сохранить. Разделим эти процессы и сначала соберем настройки на основании массива настроек $aTabs["OPTIONS"]
:
php$aOptions = []; foreach($aTab["OPTIONS"] as $aOption) { if(!is_array($aOption)) continue; if($request["apply"]) { if($type == "checkbox") $aOptions[$aOption[0]] = $request->getPost($aOption[0]); else $aOptions[$aOption[0]] = trim($request->getPost($aOption[0])); } else if($request["default"]) $aOptions[$aOption[0]] = trim($aOption[2]); }
Теперь имея массив отправленных юзером данных, можно проверить корректность настроек, если есть в этом необходимость. Мне надо было проверить авторизационные данные клиента внешнего сервиса.
И наконец сохранение:
php// сохранение настроек foreach($aOptions as $sKey => $sValue) Option::set($idModule, $sKey, $sValue); // перенаправление юзера на страницу настроек модуля, иначе при обновлении этой страницы с модулем форма будет отправлена повторно LocalRedirect($APPLICATION->GetCurPage()."?mid=".$idModule."&lang=".LANG);
Здесь логика смешивается с представлением ...
Рендерить будем при помощи класса CAdminTabControl для многостраничных форм и магической незадокументированной функциией __AdmSettingsDrawList
, упоминание о ней можно найти здесь и здесь.
php$oTabControl = new CAdminTabControl("tabControl", $aTabs); $oTabControl->Begin(); ?><form action="<?php echo($APPLICATION->GetCurPage()); ?>?mid=<?php echo($idModule); ?>&lang=<?php echo(LANG); ?>" method="post"> <?php $oTabControl->BeginNextTab(); foreach($aTabs as $aTab) { if($aTab["OPTIONS"]) { $oTabControl->BeginNextTab(); __AdmSettingsDrawList($idModule, $aTab["OPTIONS"]); } } $oTabControl->Buttons(); ?><input type="submit" name="apply" value="Применить" class="adm-btn-save" /> <?php echo(bitrix_sessid_post()); ?> </form> <?php $oTabControl->End();
А вот здесь начинается интересное ...
В самом начале был упомянут модуль бизнес процессы, файл настроек которого расположен по пути bitrix/modules/bizproc/options.php
. Это наиболее простой и наглядный пример организации настроек для модуля с поддержкой мультисайтовости 1С-Битрикс.
Что же меняется в базовой реализации настроек модуля при поддержке мультисайтовости? Немного:
$aTabs
остается как и преждеphp$aSiteIds = GetSites(); // ... // получить массив сайтов [lid => name, ...] function GetSites() { $dbRes = Bitrix\Main\SiteTable::getList(); $aSites = []; while ($aSite = $dbRes->fetch()) $aSites[$aSite["LID"]] = $aSite["NAME"]; return $aSites; }
b_lang
и b_landing_site
.
Основная обертка формы и вкладки настроек такая же. Однако, кроме вкладок настроек модуля, теперь нужны подвкладки внутри вкладок, создать их можно при помощи CAdminViewTabControl
, документацию по которой не удалось найти. Создаем подвкладки:
phpforeach($aSiteIds as $sSiteId => $sSiteName) { $aSubTabs[] = [ "DIV" => "opt_site_$sSiteId", "TAB" => "$sSiteName ($sSiteId)", 'TITLE' => '', "OPTIONS" => $aTabs["OPTIONS"] ]; } $subTabControl = new CAdminViewTabControl("subTabControl", $aSubTabs);
__AdmSettingsDrawList
здесь уже не подходит, так как она рендерит верстку вне подвкладок. Штатного средства 1С-Битрикс найти не удалось, поэтому была написана своя функция рендера настроек (очень не хотелось писать верстку, лучше генератор верстки) RenderSettings
:
phpfunction RenderSettings($idModule, $aSettings, $sSiteId) { $a = []; foreach($aSettings as $value) { $name = $value[0]; $name2 = $value[0]."_".$sSiteId; $desc = $value[1]; $default = $value[2]; $type = $value[3][0]; $val = $value[3][1]; //$realval = Option::get($idModule, $name, $default, $sSiteId); $realval = settings::get($name, $sSiteId); $realval = ($realval !== null ? $realval : $default); $inner = ""; switch($type) { case "text": $inner = '<input type="text" size="'.$val.'" maxlength="255" value="'.$realval.'" name="'.$name2.'">'; break; case "selectbox": $aOptions = []; foreach($val as $key2 => $val2) $aOptions[] = '<option value="'.$key2.'"'.($realval == $key2 ? 'selected=""' : '').'>'.$val2.'</option>'; $inner = '<select name="'.$name2.'">'.implode("\n", $aOptions).'</select>'; break; case "checkbox": $inner = '<input type="checkbox" id="'.$name2.'" name="'.$name2.'" value="'.$realval.'"'.($realval == "Y" ? 'checked=""' : '').' class="adm-designed-checkbox"> <label class="adm-designed-checkbox-label" for="'.$name2.'" title=""></label>'; break; default: break; } $a[] = '<div style="display: block; margin-bottom: 5px;"><label style="display: inline-block;width:50%;text-align: right;">'.$desc.'</label>'.$inner.'</div>'; } return implode("\n", $a); }
Функция может обработать только text
, select
, checkbox
, другое пока не понадобилось. На вход принимает 3 аргумента:
$idModule
- идентификатор модуля$aSettings
- $aTabs["OPTIONS"]
$sSiteId
- LID сайта_LID
, чтобы их можно было как-то отличить между сайтами.Прежде чем пройтись циклом по вкладкам массива $aTabs
необходимо стартовать процесс обработки вкладок и затем отрендерить настройки:
php$subTabControl->Begin(); foreach ($aSiteIds as $sSiteId => $sSiteName) { $subTabControl->BeginNextTab(); foreach($aTabs as $aTab) { if($aTab["OPTIONS"]) echo RenderSettings($idModule, $aTab["OPTIONS"], $sSiteId); } } $subTabControl->End();
Имея мультисайтовость имеем одинаковые имена настроек для каждого сайт, но разные значения. Названия GUI элементов у нас имеют постфикс с LID
сайта, значит проходимся по массиву сайтов, используя LID
сайта проходимся по массиву настроек и пробуем извлечь настройки:
php$aOptions = []; foreach($aSiteIds as $sSiteId => $name) { $aOptions[$sSiteId] = []; foreach($aTabs as $aTab) { foreach($aTab["OPTIONS"] as $aOption) { if(!is_array($aOption)) continue; $type = $aOption[3][0]; if($oRequest["apply"]) { if($type == "checkbox") $aOptions[$sSiteId][$aOption[0]] = $oRequest->getPost($aOption[0]."_".$sSiteId) !== null ? "Y": "N"; else $aOptions[$sSiteId][$aOption[0]] = trim($oRequest->getPost($aOption[0]."_".$sSiteId)); } else if($oRequest["default"]) $aOptions[$sSiteId][$aOption[0]] = trim($aOption[2]); } } }
checkbox
, если он не отмечен на стороне клиента, то при отправке формы название элемента не будет в массиве данных, поэтому checkbox
обрабатываем по особенному.