Полный цикл в digital

Свой тип свойств инфоблока

Готовый код можно скачать в моем репозитории на GitFlic.

Информационный блок, это некая сущность для хранения информации какого-либо блока на сайте. Инфоблоки содержат стандартные поля для записи, которые охватывают большую часть случаев применения:

  1. Строка
  2. Число
  3. Список
  4. Файл
  5. Привязка к элементам
  6. Привязка к разделам
  7. HTML\Текст
  8. Видео
  9. Дата
  10. Дата\Время
  11. Деньги
  12. Привязка к Яндекс.Карте
  13. Привязка к Google Maps
  14. Привязка к пользователю
  15. Привязка к разделам с автозаполнением
  16. Привязка к теме форума
  17. Привязка к товарам (SKU)
  18. Привязка к файлу (на сервере)
  19. Привязка к элементам в виде списка
  20. Привязка к элементам по XML_ID
  21. Привязка к элементам с автозаполнением
  22. Справочник
  23. Счётчик

Бывают ситуации, когда базовых типов недостаточно для реализации определенной бизнес-логики, тогда на помощь приходят пользовательские типы свойств. Сложность в том, что простого инструмента для такой настройки нет, и реализуется это через код. Полный боевой пример можно скачать ниже после статьи.

Подсмотреть реализацию близкого к вашей задаче свойства, можно в модуле Информационные блоки. Для этого перейдём в папку /bitrix/modules/iblock/classes/general/, здесь вы найдёте перечень классов в отдельных файлах с префиксом prop_. По названию фалов можно догадаться к какому типу они относятся:

  • prop_date.php
  • prop_datetime.php
  • prop_html.php

Обычно, кастомный тип свойства создается на обработчике события OnIBlockPropertyBuildList модуля iblock.

Подготовка

Структура проекта:

  • Папка local
    • Папка php_interface
      • Папка lib
        • Папка usertype
        • Файл CUserTypeTimesheet.php
    • Файл autoload.php
    • Файл constants.php
    • Файл event_handler.php
    • Файл init.php

Реализация

Файл 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

  1. USER_TYPE_ID уникальный идентификатор типа свойства
  2. USER_TYPE cимвольный код типа свойства
  3. CLASS_NAME название класса, который реализует логику типа свойства (чаще всего данный метод и логика свойства располагаются в одном классе, но можно разделить их на разные)
  4. DESCRIPTION название типа свойства, которое будет отображаться в административной панели
  5. PROPERTY_TYPE тип базового свойства, от которого наследуется стандартная логика работа со свойством
  6. CheckFields метод должен проверить корректность значения свойства и вернуть массив. Пустой, если ошибок нет, и с сообщениями об ошибках, если есть
  7. GetUIFilterProperty метод описывает вид поля фильтрации в компоненте main.ui.filter на административных страницах инфоблоков
  8. GetLength метод должен вернуть фактическую длину значения свойства. Он нужен только для свойств, значения которых представляют собой сложные структуры (например, массив)
  9. ConvertToDB метод должен преобразовать значение свойства в формат, пригодный для сохранения в базе данных. И вернуть массив вида array("VALUE" => "...", "DESCRIPTION" => "..."). Если значение свойства массив, то разумным будет использование функции serialize. А вот Дата/время преобразуется в ODBC формат "YYYY-MM-DD HH:MI:SS". Это определит возможности сортировки и фильтрации по значениям данного свойства
  10. ConvertFromDB Метод должен преобразовать значение свойства из формата пригодного для сохранения в базе данных в формат обработки. И вернуть массив вида array("VALUE" => "...", "DESCRIPTION" => "..."). Дополняет ConvertToDB
  11. GetPropertyFieldHtmlMulty Вывод формы редактирования множественного свойства. Если отсутствует, то используется GetPropertyFieldHtml для каждого значения отдельно (у множественных свойств)
  12. GetAdminListViewHTML метод возвращает безопасный HTML отображения значения свойства в списке элементов административной части
  13. GetPublicViewHTML метод должен вернуть безопасный HTML отображения значения свойства в публичной части сайта. Если она вернет пустое значение, то значение отображаться не будет
  14. GetPublicEditHTML метод должен вернуть HTML отображения элемента управления для редактирования значений свойства в публичной части сайта
  15. GetSettingsHTML метод возвращает безопасный HTML отображения настроек свойства для формы редактирования инфоблока
  16. PrepareSettings метод возвращает либо массив с дополнительными настройками свойства, либо весь набор настроек, включая стандартные
Заполните форму уже сегодня!
Для начала сотрудничества необходимо заполнить заявку или заказать обратный звонок. В ответ получите коммерческое предложение, которое будет содержать индивидуальную стратегию с учетом требований и поставленных задач
Работаем по будням с 9:00 до 18:00. Заявки, отправленные в выходные, обрабатываем в первый рабочий день до 12:00.
Спасибо, ваш запрос принят и будет обработан!
Эйч Маркетинг