Привет, это Дима, бэкенд-разработчик. Сегодня я расскажу о решении задачи, с которой мы столкнулись при реализации нестандартной логики инфоблоков на проекте с битриксовой многосайтовостью. Эта статья будет полезна разработчикам, которые занимаются настройкой Битрикс и хотят узнать больше о его возможностях. Если вы хотите пропустить вводную часть про базовую настройку, смело листайте вниз к разделу Личный опыт. Информационный блок (инфоблок) — это некая сущность для хранения информации какого-либо блока на сайте. Он используется для хранения стандартной информации блоков сайта: название, id, текст для превью, картинка для превью и т.д. Инфоблоки содержат стандартные поля для записи, которые охватывают большую часть случаев применения. Для остальных случаев вам пригодится эта статья :) Новое свойство можно создать в несколько простых шагов: Придумать название и символьный код для свойства. Выбрать необходимый тип свойства — они бывают базовыми и пользовательскими. Пользовательские помогают в решении специфических задач: сделать привязку к Яндекс Карте, или блоков на сайте к типам пользователей.  Выбрать, множественное это свойство или нет. Например, добавление нескольких изображений для карточки товара реализуется через этот параметр. Задать дополнительные параметры в расширенном редакторе при необходимости.   Бывают ситуации, когда базовых типов недостаточно для реализации определенной бизнес-логики: расписание занятий у тренера, время приема у врачей. Тогда на помощь приходят пользовательские типы свойств. Сложность в том, что простого инструмента для такой настройки нет, и реализуется это через код.   Для создания нового тип свойства необходимо в файле init.php добавить слушатель события OnIBlockPropertyBuildList из модуля iblock и написать обработчик для данного события в виде класса с функцией GetUserTypeDescription, которая возвращает массив с информацией о типе свойств. Правилом хорошего тона считается вынесение логики слушателей в отдельный файл events.php, который потом подключается в файле init.php, чтобы не загромождать один файл огромным количеством логики. Необходимыми элементами являются: USER_TYPE_ID – уникальный идентификатор типа свойства. USER_TYPE – cимвольный код типа свойства. CLASS_NAME – название класса, который реализует логику типа свойства (чаще всего данный метод и логика свойства располагаются в одном классе, но можно разделить их на разные). DESCRIPTION – название типа свойства, которое будет отображаться в административной панели. PROPERTY_TYPE – тип базового свойства, от которого наследуется стандартная логика работа со свойством. Пример такой функции: Созданный тип свойства появится в административной панели, но будет отображаться пустым — так как нет реализованного метода отображения свойства с пользовательским типом. Переходим к созданию метода: Реализуем метод GetPropertyFieldHtml в классе, название которого указанно в поле CLASS_NAME. Он должен находиться в массиве, который возвращается методом GetUserTypeDescription. Добавим в массив поле вида: 'GetPropertyFieldHtml' => [CLASS, 'GetPropertyFieldHtml']. В созданном методе сформируем отображение свойств с описываемым типом свойства и вернем его результатом работы функции (в виде строки). Рассмотрим другие параметры для массива 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' с телом, в котором будет формироваться отображение для множественного свойства.   Делюсь итоговым вариантом на скриншоте: Надеюсь, эта статья была вам полезна! Буду рад, если вы поделитесь своими историями в комментариях  Веб-интегратор.