Свой тип пользовательского поля - регистрация, формы, значение
Заводим собственный тип пользовательского поля: когда штатных типов не хватает и значение надо хранить, показывать и проверять по своим правилам.
Решение
Регистрируем тип обработчиком события:
use Bitrix\Main\EventManager;
EventManager::getInstance()->addEventHandler('main', 'OnUserTypeBuildList', ['\\Vendor\\ColorType', 'getUserTypeDescription']);// без подключения обработчика тип исчезает из списка, а значения перестают выводитьсяТип живёт ровно до тех пор, пока подключён его обработчик. Отключённый файл с подключением означает не только пропажу типа из списка, но и пустые значения у всех полей, которые им пользуются.
Описываем класс типа:
namespace Vendor;
class ColorType { public static function getUserTypeDescription(): array { return [ 'USER_TYPE_ID' => 'vendor_color', // уникальное имя типа 'CLASS_NAME' => self::class, 'DESCRIPTION' => 'Цвет', 'BASE_TYPE' => 'string', // тип хранения менять потом нельзя ]; }}Описание отдаёт имя типа, его класс и тип хранения значений. Имя должно быть уникальным среди всех решений на проекте, а тип хранения выбирается один раз: сменить его у заполненного поля означает потерять значения.
Рисуем поле в форме редактирования:
public static function getEditFormHTML(array $userField, array $htmlControl): string { return sprintf('<input type="color" name="%s" value="%s">', $htmlControl['NAME'], htmlspecialcharsbx($userField['VALUE'] ?? '#000000'));}Разметка формы ввода остаётся полностью на вашей стороне. Платформа передаёт имя поля и текущее значение, а как выглядит ввод - решает класс, и это же объясняет, почему свой тип стоит заводить только ради особенного ввода.
Проверяем значение и выводим его:
public static function checkFields(array $userField, $value): array { return preg_match('/^#[0-9a-f]{6}$/i', (string)$value) ? [] : [['id' => $userField['FIELD_NAME'], 'text' => 'Ожидается цвет в формате #rrggbb']];}public static function getPublicViewHTML(array $userField, $value): string { return '<span style="background:' . htmlspecialcharsbx($value) . '"></span>';}Проверка значения живёт в самом типе, а не в форме ввода. Значение приходит и из формы, и из кода, и из обмена, поэтому единственное надёжное место для правил - класс типа.
Разметку формы стоит держать простой и обходиться без чужих библиотек. Поле показывается в административной части, где свои правила подключения скриптов, и тяжёлый ввод из внешней библиотеки ломается при первом же обновлении платформы.
Бизнес-процессы видят добавленный свой тип поля далеко не сразу и не везде. Им нужно отдельное описание соответствия, иначе поле в списке свойств документа не появится, а значение из процесса не запишется.
Свой тип стоит заводить только ради особенного ввода или собственной проверки. Ради подписи и списка значений хватает штатных типов, а каждый свой тип - это код, который сопровождается годами.
Типичные проблемы
Тип пропал из списка после переноса сайта.
Файл с подключением обработчика не переехал вместе с остальным кодом. Без этого обработчика платформа о существовании типа не знает.
Значения полей стали пустыми.
Обработчик типа отключён, а поля этого типа в сущностях остались. Платформа не умеет показывать значение неизвестного ей типа.
После смены типа хранения значения исчезли.
Тип хранения задаётся при создании и меняться потом не должен. Перенос значений делают отдельным скриптом заранее, ещё до смены типа поля.
Свой тип не виден в бизнес-процессе.
Для бизнес-процессов свой тип описывается отдельно. Без описания поле в списке свойств документа не появляется.
В поле сохраняется что угодно.
Проверка значения сделана только в разметке самой формы, на стороне браузера. Значение приходит и из кода, и из обмена, минуя форму целиком.
Частые вопросы
Когда свой тип оправдан?
Когда нужен особенный ввод, своя проверка или особый вывод. Ради подписи и набора значений хватает штатных типов.
Где хранится значение своего типа?
В той же таблице значений, что и у штатных типов. Отличается только вид хранимых данных, заданный в описании.
Можно ли использовать свой тип у любой сущности?
Да, типы общие для платформы: поле такого типа заводится и у раздела, и у пользователя, и у заказа.
Что будет с данными, если удалить решение?
Значения останутся в базе, но показывать их будет нечем. Перед удалением тип меняют на штатный с переносом значений.
Смежное
- Пользовательские поля - оглавление подтемы
- Свой тип свойства инфоблока: регистрация, форма, хранение - тот же приём для свойств инфоблока
- Пользовательское поле: заведение, вывод значения, типы - штатные типы полей
- Поля разделов и пользователей: выборка, множественные значения, файлы - чтение значений
- Обработчик события: регистрация, аргументы, отмена действия - как подключается обработчик
- Инфоблоки - устройство хранилища целиком
- Свой модуль: структура, установка, автозагрузка - как упаковать такой тип в модуль