Привет, это Дима, бэкенд-разработчик из Hawking Bros. Сегодня я расскажу о решении задачи, с которой мы столкнулись при реализации нестандартной логики инфоблоков на проекте с битриксовой многосайтовостью.
Эта статья будет полезна разработчикам, которые занимаются настройкой Битрикс и хотят узнать больше о его возможностях. Если вы хотите пропустить вводную часть про базовую настройку, смело листайте вниз к разделу Личный опыт.
Создание пользовательских типов свойств
Информационный блок (инфоблок) — это некая сущность для хранения информации какого-либо блока на сайте. Он используется для хранения стандартной информации блоков сайта: название, id, текст для превью, картинка для превью и т.д. Инфоблоки содержат стандартные поля для записи, которые охватывают большую часть случаев применения. Для остальных случаев вам пригодится эта статья :)
Новое свойство можно создать в несколько простых шагов:
Придумать название и символьный код для свойства.
Выбрать необходимый тип свойства — они бывают базовыми и пользовательскими. Пользовательские помогают в решении специфических задач: сделать привязку к Яндекс Карте, или блоков на сайте к типам пользователей.
Выбрать, множественное это свойство или нет. Например, добавление нескольких изображений для карточки товара реализуется через этот параметр.
Задать дополнительные параметры в расширенном редакторе при необходимости.
Бывают ситуации, когда базовых типов недостаточно для реализации определенной бизнес-логики: расписание занятий у тренера, время приема у врачей. Тогда на помощь приходят пользовательские типы свойств. Сложность в том, что простого инструмента для такой настройки нет, и реализуется это через код.
Для создания нового тип свойства необходимо в файле init.php добавить слушатель события OnIBlockPropertyBuildList
из модуля iblock и написать обработчик для данного события в виде класса с функцией GetUserTypeDescription
, которая возвращает массив с информацией о типе свойств. Правилом хорошего тона считается вынесение логики слушателей в отдельный файл events.php, который потом подключается в файле init.php, чтобы не загромождать один файл огромным количеством логики.
Необходимыми элементами являются:
USER_TYPE_ID
– уникальный идентификатор типа свойства.USER_TYPE
– cимвольный код типа свойства.CLASS_NAME
– название класса, который реализует логику типа свойства (чаще всего данный метод и логика свойства располагаются в одном классе, но можно разделить их на разные).DESCRIPTION
– название типа свойства, которое будет отображаться в административной панели.PROPERTY_TYPE
– тип базового свойства, от которого наследуется стандартная логика работа со свойством.
Пример такой функции:
public function GetUserTypeDescription(): array
{
return [
'USER_TYPE_ID' => 'schedule', //Уникальный идентификатор типа свойств
'USER_TYPE' => 'SCHEDULE', // Символьный код типа свойств
'CLASS_NAME' => __CLASS__, // Название класса, который реализует логику типа свойств
'DESCRIPTION' => 'Расписание', // Название свойства, которое будет отображаться в административной панели
'PROPERTY_TYPE' => Bitrix\Iblock\PropertyTable::TYPE_STRING, // Тип базового свойства, от которого будет браться стандартная логика работы с данным свойством
];
}
Созданный тип свойства появится в административной панели, но будет отображаться пустым — так как нет реализованного метода отображения свойства с пользовательским типом.
Переходим к созданию метода:
Реализуем метод
GetPropertyFieldHtml
в классе, название которого указанно в полеCLASS_NAME.
Он должен находиться в массиве, который возвращается методомGetUserTypeDescription
.Добавим в массив поле вида:
'GetPropertyFieldHtml' => [CLASS, 'GetPropertyFieldHtml']
.В созданном методе сформируем отображение свойств с описываемым типом свойства и вернем его результатом работы функции (в виде строки).
Дополнительные параметры для массива GetUserTypeDescription
Рассмотрим другие параметры для массива GetUserTypeDescription
, которые могут пригодиться в работе:
CheckFields
– метод должен проверить корректность значения свойства и вернуть массив. Пустой, если ошибок нет, и с сообщениями об ошибках, если есть.GetUIFilterProperty
– метод описывает вид поля фильтрации в компоненте main.ui.filter на административных страницах инфоблоков.GetLength
– метод должен вернуть фактическую длину значения свойства. Он нужен только для свойств, значения которых представляют собой сложные структуры (например, массив).ConvertToDB
– Метод должен преобразовать значение свойства в формат, пригодный для сохранения в базе данных. И вернуть массив видаarray("VALUE" => "...", "DESCRIPTION" => "...")
.
Если значение свойства – массив, то разумным будет использование функцииserialize
. А вот Дата/время преобразуется в ODBC формат "YYYY-MM-DD HH:MI:SS". Это определит возможности сортировки и фильтрации по значениям данного свойства.ConvertFromDB
– Метод должен преобразовать значение свойства из формата пригодного для сохранения в базе данных в формат обработки. И вернуть массив видаarray("VALUE" => "...", "DESCRIPTION" => "...")
. Дополняет ConvertToDB.GetPropertyFieldHtmlMulty
– Вывод формы редактирования множественного свойства. Если отсутствует, то используется GetPropertyFieldHtml для каждого значения отдельно (у множественных свойств).GetAdminListViewHTML
– Метод возвращает безопасный HTML отображения значения свойства в списке элементов административной части.GetPublicViewHTML
– Метод должен вернуть безопасный HTML отображения значения свойства в публичной части сайта. Если она вернет пустое значение, то значение отображаться не будет.GetPublicEditHTML
– Метод должен вернуть HTML отображения элемента управления для редактирования значений свойства в публичной части сайта.GetSettingsHTML
– Метод возвращает безопасный HTML отображения настроек свойства для формы редактирования инфоблока.PrepareSettings
– Метод возвращает либо массив с дополнительными настройками свойства, либо весь набор настроек, включая стандартные.
Личный опыт настройки пользовательских типов свойств
На проекте для интернет-магазина строительных материалов задумана реализация многогородности с помощью битриксовой многосайтовости: один город – один физический сайт.
Перед нашей командой стояла задача — реализовать разделение контента между сайтами, чтобы создавать уникальный контент для разных городов, а также использовать на нескольких сайтах уже созданный.
Мы решили сделать это с помощью свойства в инфоблоках, которое позволило бы выбирать несколько сайтов для одной записи. При этом у нас было несколько ограничений: отсутствие свойства для вывода списка сайтов в инфоблоке, и запрет на добавление сайтов вручную через свойство "Список".
Так мы пришли к потребности создать свой пользовательский тип свойства, которое через API Bitrix будет подтягивать все сайты и выводить их в виде обычного списка.
Первая проблема возникла при сохранении выбранных результатов. В своём кастомном типе свойства я указал:
'PROPERTY_TYPE' => Iblock</span>PropertyTable::TYPE_LIST
, так как свойство имеет вид стандартного списка, просто с автоматическими заполняемыми значениями. Это не сработало, и в базу данных выбранные значения свойства не приходили.
Поиск по открытым источникам и работа с ядром привели меня к тому, что для свойства типа список недостаточно просто вывести весь перечень значений. Также следовало записать их в базу данных в отдельную таблицу для всех Enum всех свойств типа список, что при обычном ручном заполнении происходит под капотом Битрикса. Оптимального способа сделать этого я не нашёл, поэтому прибегнул к альтернативе.
Я заменил PROPERTYTYPE
на Iblock</span>PropertyTable::TYPESTRING
, что позволило сохранять в базе просто названия сайтов, а не их ENUM_ID
из таблицы значений полей свойства типа список.
Так как требовалось предусмотреть логику множественного выбора сайтов у 1 элемента, я выбрал «Множественное» свойство, но вместо компактного вывода моей таблицы с множественным выбором значений — как это обычно работает в стандартном свойстве типа Список — мне множество раз вывелась таблица с возможностью в каждой из них выбрать только одно значение.
После долгих поисков в Ядре я нашёл, что искал: проверку на существование в массиве описания типа свойства поля со ссылкой на метод для уникальной вёрстки при выбранном множественном свойстве.
Чтобы сделать уникальную вёрстку для отображения множественного свойства для пользовательского типа необходимо в результат метода GetUserTypeDescription
добавить поле 'GetPropertyFieldHtmlMulty' => [CLASS, 'GetPropertyFieldHtmlMulty']
, а также реализовать метод 'GetPropertyFieldHtmlMulty'
с телом, в котором будет формироваться отображение для множественного свойства.
Делюсь итоговым вариантом на скриншоте:
Надеюсь, эта статья была вам полезна! Буду рад, если вы поделитесь своими историями в комментариях.