В дополнение к статье о создании собственного типа пользовательских полей хочу рассказать как делать аналогичные свойства для информационных блоков, т.к. эти свойства относятся к другому модулю, а именно «информационные блоки» (iblock). Из коробки инфоблоки битрикс предоставляют следующий набор типов свойств:
- Строка
- Число
- Список
- Файл
- Привязка к элементам
- Привязка к разделам
- HTML\Текст
- Видео
- Дата
- Дата\Время
- Деньги
- Привязка к Яндекс.Карте
- Привязка к Google Maps
- Привязка к пользователю
- Привязка к разделам с автозаполнением
- Привязка к теме форума
- Привязка к товарам (SKU)
- Привязка к файлу (на сервере)
- Привязка к элементам в виде списка
- Привязка к элементам по XML_ID
- Привязка к элементам с автозаполнением
- Справочник
- Счётчик
Достаточно внушительный список, однако иногда заказчику нужны какие-то очень специфические решения, которые не удаётся реализовать в полном объёме. Тут битрикс предоставляет возможность расширить доступный перечень свойств.
Рассмотрим пример создания множественного свойства для реализации расписания врачей.
Подготовка и загрузка класса
Структура и способ загрузки класса будет аналогичным с первой статьёй. Так же используем папку /local/ и такую структуру:
Файл init.php
<?php
//Константы
require dirname(__FILE__) . '/constants.php';
//Автозагрузка классов
require dirname(__FILE__) . '/autoload.php';
//Обработка событий
require dirname(__FILE__) . '/event_handler.php';
/**
* обёртка для print_r() и var_dump()
* @param $val - значение
* @param string $name - заголовок
* @param bool $mode - использовать var_dump() или print_r()
* @param bool $die - использовать die() после вывода
*/
function print_p($val, $name = 'Содержимое переменной', $mode = false, $die = false){
global $USER;
if($USER->IsAdmin()){
echo '<pre>'.(!empty($name) ? $name.': ' : ''); if($mode) { var_dump($val); } else { print_r($val); } echo '</pre>';
if($die) die;
}
}
Тут дополнительная функция print_p()
служит для удобства вывода содержимого переменных и отладки.
Файл constants.php
<?php
//Папка с пользовательскими классами
define('APP_CLASS_FOLDER', '/local/php_interface/lib/');
Тут храним константы с путями к ключевым папкам (пока одна).
Файл autoload.php
<?php
use Bitrix\Main\Loader;
//Автозагрузка наших классов
Loader::registerAutoLoadClasses(null, [
'lib\usertype\CUserTypeTimesheet' => APP_CLASS_FOLDER . 'usertype/CUserTypeTimesheet.php',
]);
Тут подгружаем один единственный класс. У меня он будет реализован в файле CUserTypeTimesheet.php
.
Файл event_handler.php
<?php
use Bitrix\Main;
$eventManager = Main\EventManager::getInstance();
//Вешаем обработчик на событие создания списка пользовательских свойств OnUserTypeBuildList
$eventManager->addEventHandler('iblock', 'OnIBlockPropertyBuildList', ['lib\usertype\CUserTypeTimesheet', 'GetUserTypeDescription']);
Тут нам необходимо повесить обработчик на событие построения списка доступных свойств в инфоблоке OnIBlockPropertyBuildList
.
Стандартные классы модуля iblock
Подсмотреть реализацию близкого к вашей задаче свойства, можно подсмотреть в модуле «Информационные блоки». Для этого перейдём в папку /bitrix/modules/iblock/classes/general/, здесь вы найдёте перечень классов в отдельных файлов с префиксом prop_, например:
- prop_date.php
- prop_datetime.php
- prop_html.php
Для реализации собственного класса, вам нужно реализовать в собственном классе как минимум 2 метода:
GetUserTypeDescription()
— метод для описания свойстваGetPropertyFieldHtml()
— метод для вывода html формы свойства
Если вы делаете составное свойство как в моём примере, вам так же потребуется 2 метода контролирующие запись и извлечение значения свойства из базы данных.
ConvertToDB()
— обработка значения перед записью в БДConvertFromDB()
— обработка значения после извлечения из БД, но до вывода в GetPropertyFieldHtml()
Создаём класс для реализации собственного свойства инфоблока
Согласно вышеописанной структуре нам остаётся создать файл самого класса.
<?php
namespace lib\usertype;
use Bitrix\Main\Loader,
Bitrix\Main\Localization\Loc,
Bitrix\Iblock;
/**
* Реализация свойство «Расписание врача»
* Class CUserTypeTimesheet
* @package lib\usertype
*/
class CUserTypeTimesheet
{
/**
* Метод возвращает массив описания собственного типа свойств
* @return array
*/
public function GetUserTypeDescription()
{
return array(
'USER_TYPE_ID' => 'user_timesheet', //Уникальный идентификатор типа свойств
'USER_TYPE' => 'TIMESHEET',
'CLASS_NAME' => __CLASS__,
'DESCRIPTION' => 'Расписание специалиста',
'PROPERTY_TYPE' => Iblock\PropertyTable::TYPE_STRING,
'ConvertToDB' => [__CLASS__, 'ConvertToDB'],
'ConvertFromDB' => [__CLASS__, 'ConvertFromDB'],
'GetPropertyFieldHtml' => [__CLASS__, 'GetPropertyFieldHtml'],
);
}
/**
* Конвертация данных перед сохранением в БД
* @param $arProperty
* @param $value
* @return mixed
*/
public static function ConvertToDB($arProperty, $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;
}
/**
* Конвертируем данные при извлечении из БД
* @param $arProperty
* @param $value
* @param string $format
* @return mixed
*/
public static function ConvertFromDB($arProperty, $value, $format = '')
{
if ($value['VALUE'] != '')
{
try {
$value['VALUE'] = base64_decode($value['VALUE']);
} catch(Bitrix\Main\ObjectException $exception) {
echo $exception->getMessage();
}
}
return $value;
}
/**
* Представление формы редактирования значения
* @param $arUserField
* @param $arHtmlControl
*/
public static function GetPropertyFieldHtml($arProperty, $value, $arHtmlControl)
{
$weekDays = [
'mon' => 'Понедельник',
'tue' => 'Вторник',
'wed' => 'Среда',
'thu' => 'Четверг',
'fri' => 'Пятница',
'sat' => 'Суббота',
'sun' => 'Воскресенье',
];
$itemId = 'row_' . substr(md5($arHtmlControl['VALUE']), 0, 10); //ID для js
$fieldName = htmlspecialcharsbx($arHtmlControl['VALUE']);
//htmlspecialcharsback нужен для того, чтобы избавиться от многобайтовых символов из-за которых не работает unserialize()
$arValue = unserialize(htmlspecialcharsback($value['VALUE']), [stdClass::class]);
$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 = '<div class="property_row" id="'. $itemId .'">';
$html .= '<div class="reception_time">';
$html .= $select;
$timeFrom = ($arValue['TIME_FROM']) ? $arValue['TIME_FROM'] : '';
$timeTo = ($arValue['TIME_TO']) ? $arValue['TIME_TO'] : '';
$html .=' время приёма: с <input type="time" name="'. $fieldName .'[TIME_FROM]" value="'. $timeFrom . '">';
$html .=' по <input type="time" name="'. $fieldName .'[TIME_TO]" value="'. $timeTo .'">';
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;
}
}
Подключаемый класс реализует вот такое свойство:
Через инфоблок свойство естественно создаётся как множественное. Как видите для одного и того же дня недели можно создавать неограниченное количество временных промежутков приёма врача и всё это будет корректно сохраняться.
Обратите внимание что для хранения составных данных я использую сериализацию массива + функцию base64_encode()
, это позволяет избежать ряд ошибок при хранении данных в БД.
Данное свойство позволит вам реализовать страницу со списком врачей или других специалистов компании с удобным редактированием времени приёма.