Свой тип свойств инфоблока
Готовый код можно скачать в моем репозитории на GitFlic.
Информационный блок, это некая сущность для хранения информации какого-либо блока на сайте. Инфоблоки содержат стандартные поля для записи, которые охватывают большую часть случаев применения:
- Строка
- Число
- Список
- Файл
- Привязка к элементам
- Привязка к разделам
- HTML\Текст
- Видео
- Дата
- Дата\Время
- Деньги
- Привязка к Яндекс.Карте
- Привязка к Google Maps
- Привязка к пользователю
- Привязка к разделам с автозаполнением
- Привязка к теме форума
- Привязка к товарам (SKU)
- Привязка к файлу (на сервере)
- Привязка к элементам в виде списка
- Привязка к элементам по XML_ID
- Привязка к элементам с автозаполнением
- Справочник
- Счётчик
Бывают ситуации, когда базовых типов недостаточно для реализации определенной бизнес-логики, тогда на помощь приходят пользовательские типы свойств. Сложность в том, что простого инструмента для такой настройки нет, и реализуется это через код. Полный боевой пример можно скачать ниже после статьи.
Подсмотреть реализацию близкого к вашей задаче свойства, можно в модуле Информационные блоки
. Для этого перейдём в папку /bitrix/modules/iblock/classes/general/
, здесь вы найдёте перечень классов в отдельных файлах с префиксом prop_
. По названию фалов можно догадаться к какому типу они относятся:
prop_date.php
prop_datetime.php
prop_html.php
Обычно, кастомный тип свойства создается на обработчике события OnIBlockPropertyBuildList
модуля iblock
.
Подготовка
Структура проекта:
-
Папка local
-
Папка php_interface
-
Папка lib
- Папка usertype
- Файл CUserTypeTimesheet.php
-
Папка lib
- Файл autoload.php
- Файл constants.php
- Файл event_handler.php
- Файл init.php
-
Папка php_interface
Реализация
Файл init.php
Подключаем файлы:
local/php_interface/init.php<?
//Константы
require_once dirname(__FILE__) . '/constants.php';
//Автозагрузка классов
require_once dirname(__FILE__) . '/autoload.php';
//Обработка событий
require_once dirname(__FILE__) . '/event_handler.php';
Файл constants.php
Храним константу с путем к ключевой папке:
local/php_interface/constants.php<?
// константа APP_CLASS_FOLDER, путь к папке с пользовательскими классами
define('APP_CLASS_FOLDER', '/local/php_interface/lib/');
Файл autoload.php
Подгружаем один единственный класс, у меня он будет реализован в файле CUserTypeTimesheet.php
:
local/php_interface/autoload.php<?
// автозагрузка класса
CModule::AddAutoloadClasses(
'', // не указываем имя модуля
array(
// ключ - имя класса с простанством имен, значение - путь относительно корня сайта к файлу
'lib\usertype\CUserTypeTimesheet' => APP_CLASS_FOLDER . 'usertype/CUserTypeTimesheet.php',
)
);
Файл event_handler.php
Вешаем обработчик на событие построения списка доступных свойств в инфоблоке OnIBlockPropertyBuildList
:
local/php_interface/event_handler.php<?php
// вешаем обработчик на событие создания списка пользовательских свойств OnUserTypeBuildList
addEventHandler('iblock', 'OnIBlockPropertyBuildList', ['lib\usertype\CUserTypeTimesheet', 'GetUserTypeDescription']);
Файл CUserTypeTimesheet.php
Для реализации собственного класса, вам нужно реализовать в собственном классе как минимум два метода:
GetUserTypeDescription()
метод для описания свойстваGetPropertyFieldHtml()
метод для вывода html формы свойства
Если вы делаете составное свойство как в моём примере, вам так же потребуется два метода контролирующие запись и извлечение значения свойства из базы данных:
ConvertToDB()
обработка значения перед записью в БДConvertFromDB()
обработка значения после извлечения из БД, но до вывода вGetPropertyFieldHtml()
local/php_interface/lib/usertype/CUserTypeTimesheet.php<?
namespace lib\usertype;
use Bitrix\Iblock;
/**
* Реализация свойство «Расписание врача»
* Class CUserTypeTimesheet
* @package lib\usertype
*/
class CUserTypeTimesheet
{
/**
* Метод возвращает массив описания собственного типа свойств
* @return array
*/
public function GetUserTypeDescription()
{
return array(
'USER_TYPE_ID' => 'user_timesheet', // уникальный идентификатор типа свойств
'USER_TYPE' => 'TIMESHEET', // cимвольный код типа свойства
'CLASS_NAME' => __CLASS__, // название класса который реализует логику типа свойства, чаще всего данный метод и логика свойства располагаются в одном классе, но можно разделить их на разные
'DESCRIPTION' => 'Расписание специалиста', // название типа свойства которое будет отображаться в административной панели
'PROPERTY_TYPE' => Iblock\PropertyTable::TYPE_STRING, // тип базового свойства, от которого наследуется стандартная логика работа со свойством
'ConvertToDB' => [__CLASS__, 'ConvertToDB'], // конвертация данных перед сохранением в базу данных
'ConvertFromDB' => [__CLASS__, 'ConvertFromDB'], // конвертация данных при извлечении из базы данных
'GetPropertyFieldHtml' => [__CLASS__, 'GetPropertyFieldHtml'], // форма редактирования значения
);
}
/**
* Конвертация данных перед сохранением в базу данных, должен преобразовать значение свойства в формат пригодный для сохранения в базе данных и вернуть массив вида array("VALUE" => "...", "DESCRIPTION" => "..."), вызывается перед сохранением значения свойства в базе данных
* @param $arProperty, метаданные свойства
* @param $value, значение выбрнных свойств, массив вида array("VALUE" => значение, "DESCRIPTION" => описание)
* @return mixed
*/
public static function ConvertToDB($arProperty, $value)
{
// если $value не пустое и заполнено поле до которого часа
if ($value['VALUE']['TIME_FROM'] != '' && $value['VALUE']['TIME_TO'] != '') {
try {
// кодируем данные которые будут записаны в базу данных
$value['VALUE'] = base64_encode(serialize($value['VALUE']));
} catch (Bitrix\Main\ObjectException $exception) {
echo $exception->getMessage();
}
} else {
$value['VALUE'] = '';
}
return $value;
}
/**
* Конвертируем данные при извлечении из базы данных, должен преобразовать значение свойства из формата пригодного для сохранения в базе данных в формат обработки который увидим в админке, возвращает массив вида array("VALUE" => "...", "DESCRIPTION" => "...")
* @param $arProperty, метаданные свойства
* @param $value, значение свойств прочитанное из базы данных, массив вида array("VALUE" => значение, "DESCRIPTION" => описание)
* @param string $format
* @return mixed
*/
public static function ConvertFromDB($arProperty, $value, $format = '')
{
// если $value не пустое
if ($value['VALUE'] != '') {
try {
// раскодируем данные которые были записаны в базу данных
$value['VALUE'] = base64_decode($value['VALUE']);
} catch (Bitrix\Main\ObjectException $exception) {
echo $exception->getMessage();
}
}
return $value;
}
/**
* Представление формы редактирования значения в админке, метод должен вернуть HTML отображения элемента управления для редактирования значений свойства в административной части
* @param $arProperty, метаданные свойства
* @param $value, значение свойства, массив вида array("VALUE" => значение, "DESCRIPTION" => описание)
* @param $arHtmlControl, имена элементов управления для заполнения значения свойства и его описания, массив вида array("VALUE" => html безопасное имя для значения, "DESCRIPTION" => html безопасное имя для описания, "MODE" => может принимать зачение "FORM_FILL" при вызове из формы редактирования элемента или "iblock_element_admin" при редактировании в режиме просмотра списка элементов, а также "EDIT_FORM" при редактировании инфоблока, "FORM_NAME" => имя формы в которую будет встроен элемент управления)
*/
public static function GetPropertyFieldHtml($arProperty, $value, $arHtmlControl)
{
$weekDays = [
'mon' => 'Понедельник',
'tue' => 'Вторник',
'wed' => 'Среда',
'thu' => 'Четверг',
'fri' => 'Пятница',
'sat' => 'Суббота',
'sun' => 'Воскресенье',
];
// ID для js
$itemId = 'row_' . substr(md5($arHtmlControl['VALUE']), 0, 10);
// устанавливаем правильную кодировку
$fieldName = htmlspecialcharsbx($arHtmlControl['VALUE']);
// unserialize преобразование серилизованных массивов, htmlspecialcharsback нужен для того, чтобы избавиться от многобайтовых символов из-за которых не работает unserialize
$arValue = unserialize(htmlspecialcharsback($value['VALUE']), [stdClass::class]);
// формируем раскрывающийся список select с днями недели
$select = '<select class="week_day" name="' . $fieldName . '[WEEK_DAY]">';
foreach ($weekDays as $key => $day) {
if ($arValue['WEEK_DAY'] == $key) {
$select .= '<option value="' . $key . '" selected="selected">' . $day . '</option>';
} else {
$select .= '<option value="' . $key . '">' . $day . '</option>';
}
}
$select .= '</select>';
// HTML каркас для вывода с ID для js
$html = '<div class="property_row" id="' . $itemId . '">';
// HTML каркас для вывода
$html .= '<div class="reception_time">';
// вставляем в каркас раскрывающийся список
$html .= $select;
// вставляем пустое значение для время приема с
$timeFrom = ($arValue['TIME_FROM']) ? $arValue['TIME_FROM'] : '';
// вставляем пустое значение для время приема до
$timeTo = ($arValue['TIME_TO']) ? $arValue['TIME_TO'] : '';
// формируем input поле для время приема с
$html .= ' время приёма: с <input type="time" name="' . $fieldName . '[TIME_FROM]" value="' . $timeFrom . '">';
// формируем input поле для время приема до
$html .= ' по <input type="time" name="' . $fieldName . '[TIME_TO]" value="' . $timeTo . '">';
// кнопка удаления input
if ($timeFrom != '' && $timeTo != '') {
$html .= ' <input type="button" style="height: auto;" value="x" title="Удалить" onclick="document.getElementById(\'' . $itemId . '\').parentNode.parentNode.remove()" />';
}
$html .= '</div>';
$html .= '</div><br/>';
return $html;
}
}
Параметры для массива GetUserTypeDescription
USER_TYPE_ID
уникальный идентификатор типа свойстваUSER_TYPE
cимвольный код типа свойстваCLASS_NAME
название класса, который реализует логику типа свойства (чаще всего данный метод и логика свойства располагаются в одном классе, но можно разделить их на разные)DESCRIPTION
название типа свойства, которое будет отображаться в административной панелиPROPERTY_TYPE
тип базового свойства, от которого наследуется стандартная логика работа со свойством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
метод возвращает либо массив с дополнительными настройками свойства, либо весь набор настроек, включая стандартные