В дополнение к статье о создании собственного типа пользовательских полей хочу рассказать как делать аналогичные свойства для информационных блоков, т.к. эти свойства относятся к другому модулю, а именно «информационные блоки» (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 .='&nbsp;время приёма: с&nbsp;<input type="time" name="'. $fieldName .'[TIME_FROM]" value="'. $timeFrom . '">';
        $html .='&nbsp;по&nbsp;<input type="time" name="'. $fieldName .'[TIME_TO]" value="'. $timeTo .'">';
        if($timeFrom!='' && $timeTo!=''){
            $html .= '&nbsp;&nbsp;<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(), это позволяет избежать ряд ошибок при хранении данных в БД.

Данное свойство позволит вам реализовать страницу со списком врачей или других специалистов компании с удобным редактированием времени приёма.



Полезная статья?
(Голосов: 27, Рейтинг: 4.39)
Вам также могут понравиться
Английский для программистов

Английский для программистов

Почему IT-специалисту необходимо освоить английский язык? Разбираем в статье.

Как подключить CSS и JS файлы к шаблону 1С Битрикс

Как подключить CSS и JS файлы к шаблону 1С Битрикс

Как правильно подключать стили и скрипты к шаблону 1С Битрикс.

Генерация оглавления статьи

Генерация оглавления статьи

В статье рассмотрен пример функции для генерации оглавления статьи блога или новости


Комментарии
Защита от автоматических сообщений
CAPTCHA
Введите слово на картинке