UF поля пользователя Битрикс, если коротко, это способ прикрутить к стандартной сущности (пользователь, раздел инфоблока, элемент, компания в CRM) свои данные без правки ядра. Работаю с ними на каждом втором проекте на Битриксе: то нужно хранить ID клиента во внешней CRM, то пункт выдачи СДЭК, то согласие на обработку персональных данных с датой подписания. Ниже - как я создаю такие поля через админку, как читаю и пишу их из кода, и какие грабли реально всплывают на практике.
Что такое UF-поля и зачем их вообще заводить
UF расшифровывается как User Field, но по факту это универсальный механизм пользовательских свойств для любой сущности, у которой в модуле есть таблица ENTITY_ID + FIELD_ID. Пользователи (b_user), разделы инфоблоков (iblock_section), элементы (iblock_element), компании и сделки в CRM - везде работает один и тот же движок highloadblock/userfield, только меняется идентификатор сущности вроде USER, IBLOCK_SECTION, CRM_COMPANY.
Плюс такого подхода в том, что поле создаётся один раз в админке или кодом, и сразу появляется в формах редактирования, в фильтрах, в экспорте, в API. Не нужно лезть в таблицу b_user и добавлять туда столбец руками - это сломает обновления модуля и потеряется при следующем major-апдейте.
На практике UF-поля пользователя чаще всего заводят под:
- дополнительные контактные данные (второй телефон, telegram, WhatsApp);
- привязку к внешним системам (ID в 1С, ID в CRM, ID пункта выдачи СДЭК);
- служебные флаги (согласие на рассылку, дата последнего входа в личный кабинет, реферальный код);
- данные для персонализации (город, сегмент, дата рождения для промо-рассылок).
Создание UF-поля пользователя через админку
Самый быстрый способ - раздел Настройки > Пользователи > Поля пользователей в админке. Там задаётся:
- символьный код поля (обязательно с префиксом UF_, например UF_CDEK_PVZ);
- тип данных: строка, число, дата, файл, привязка к элементу или разделу инфоблока, список (enum), HTML/текст;
- множественность (одно значение или массив);
- обязательность заполнения и значение по умолчанию;
- видимость в разных местах (список пользователей, форма редактирования, публичная часть).
Для типа «Список» отдельно заполняются варианты значений (enum) - каждый вариант хранится в таблице b_user_field_enum со своим XML_ID, и именно по XML_ID, а не по тексту, я потом обращаюсь к значению из кода, потому что текст менеджер может переименовать в любой момент.
Важный момент, о который спотыкаются новички: если поле создано с настройкой «Множественное», в коде оно всегда возвращается массивом, даже если в нём одно значение. Забыть об этом в условии if ($arUser['UF_PHONE_2'] == '...') - типичная причина «поле не работает», хотя на деле сравнивается строка с массивом.
Работа с UF-полями пользователя из кода
Через админку поле можно создать один раз руками, но когда доработка ставится на поток или переносится между окружениями (dev/staging/prod), поле нужно создавать кодом - иначе на каждом окружении придётся руками кликать одно и то же.
Создание поля через CUserTypeEntity:
$obUserField = new CUserTypeEntity();
$fieldId = $obUserField->Add([
'ENTITY_ID' => 'USER',
'FIELD_NAME' => 'UF_CDEK_PVZ',
'USER_TYPE_ID' => 'string',
'XML_ID' => 'CDEK_PVZ',
'SORT' => 500,
'MULTIPLE' => 'N',
'MANDATORY' => 'N',
'EDIT_FORM_LABEL' => ['ru' => 'Пункт выдачи СДЭК'],
'LIST_COLUMN_LABEL' => ['ru' => 'ПВЗ СДЭК'],
]);
Чтение значения UF-поля у текущего пользователя:
global $USER;
$rsUser = CUser::GetByID($USER->GetID());
$arUser = $rsUser->Fetch();
echo $arUser['UF_CDEK_PVZ'];
Обновление значения:
$user = new CUser();
$user->Update($userId, [
'UF_CDEK_PVZ' => 'MSK123',
]);
Для выборки пользователей по значению UF-поля использую GetList с фильтром прямо по коду поля - Битрикс сам достроит JOIN к таблице значений:
$rsUsers = CUser::GetList(
$by = 'ID', $order = 'ASC',
['UF_CDEK_PVZ' => 'MSK123'],
['SELECT' => ['UF_CDEK_PVZ']]
);
while ($arUser = $rsUsers->Fetch()) {
echo $arUser['LOGIN'] . ' - ' . $arUser['UF_CDEK_PVZ'] . PHP_EOL;
}
Если поле типа «Список», значение хранится как ID записи в enum-таблице, а не как текст. Чтобы получить читаемое значение, нужен CUserFieldEnum:
$enum = new CUserFieldEnum();
$rsEnum = $enum->GetList([], ['XML_ID' => 'SEGMENT_VIP']);
$arEnum = $rsEnum->Fetch();
echo $arEnum['VALUE'];
Эту связку я обычно оборачиваю в отдельный класс-хелпер, если полей-списков в проекте больше двух-трёх - иначе в контроллерах расползаются одинаковые куски с CUserFieldEnum.
Бесплатный материал
🎁 Полезный скрипт в подарок
Подпишитесь на Telegram - пришлю готовый скрипт по этой теме.
Без спама. Отписка в 1 клик.
UF-поля в разделах и элементах инфоблоков: чем отличаются от полей пользователя
Механизм тот же самый, но ENTITY_ID другой: для разделов инфоблока это IBLOCK_ID_1_SECTION (где 1 - ID инфоблока), для элементов - IBLOCK_1. Из-за этого поле, созданное для одного инфоблока, не появится в другом - привязка жёсткая, и если в проекте десять инфоблоков с одинаковой структурой (например, каталог товаров с несколькими брендами на разных ID), поле придётся создавать под каждый ID отдельно, либо один раз - если инфоблоки объединены общими типами свойств через настройки типа.
На практике для товарных каталогов чаще заводят не UF-поля, а обычные свойства инфоблока (props) - они гибче в плане множественности и наследования по группам. UF-поля для разделов инфоблока я использую точечно: например, привязать к разделу каталога ответственного менеджера (поле типа «Пользователь») или SEO-шаблон, специфичный для этого раздела и не подходящий под общую схему свойств.
Сравнение, где какой механизм уместнее:
| Сценарий | Что использовать | Почему |
|---|---|---|
| Доп. поля у пользователя (телефон, ID в CRM) | UF-поле сущности USER | Единственный штатный механизм для b_user |
| Характеристики товара (цвет, размер) | Свойство инфоблока | Гибкое наследование по разделам и фильтрация в каталоге |
| Служебные данные раздела (ответственный, шаблон) | UF-поле сущности IBLOCK_ID_SECTION | Не нужно городить отдельный инфоблок-справочник |
| Доп. поля в CRM-сделке или компании | UF-поле CRM-сущности | Штатно интегрируется с полями сделки в интерфейсе CRM |
Вывод и редактирование UF-полей в компонентах и на фронте
В компонентах bitrix:main.register и bitrix:main.profile UF-поля пользователя подхватываются автоматически, если они помечены как видимые в форме регистрации или профиля - шаблон компонента сам построит нужный input по типу поля. Проблема в том, что штатная вёрстка этих полей редко подходит под дизайн, и её обычно приходится переопределять в шаблоне компонента вручную под каждый тип поля (строка, список, файл, дата - у каждого своя разметка в result_modifier.php и template.php).
Если поле нужно вывести вне штатных компонентов - на любой произвольной странице личного кабинета, - проще всего собрать форму самому и обработать сохранение через тот же CUser::Update. Я так делаю почти всегда, когда личный кабинет кастомный: меньше зависимости от структуры чужого компонента, легче стилизовать под макет.
Отдельно стоит сказать про мультисайтовость: у UF-полей есть настройка привязки к языковым версиям (для лейблов), но сами значения общие для всех сайтов, если это одна сущность USER. Если нужно разное поведение поля на разных сайтах в рамках одного ядра, логику ветвления придётся писать в коде, само поле останется одно.
Частые ошибки при работе с UF-полями пользователя
- Забыли префикс UF_. Без него поле не будет считаться пользовательским и просто не сохранится через штатный механизм.
- Сравнение множественного поля со строкой. Множественное поле всегда массив, даже с одним значением - сравнивать нужно через in_array или брать первый элемент явно.
- Хранение читаемого текста вместо XML_ID для списков. Менеджер переименует пункт списка в админке - и весь код, завязанный на текст, перестанет работать. Завязывайтесь только на XML_ID.
- Изменение типа поля на боевом проекте без миграции данных. Смена типа (например, строка на список) не конвертирует уже сохранённые значения, они просто перестают быть видны в новой форме.
- Создание одинаковых по смыслу полей под разными кодами в разных модулях. Встречал на аудитах инфраструктуру, где ID клиента в 1С хранился в трёх разных UF-полях с разными кодами - в CRM, в профиле пользователя и в отдельной таблице. Синхронизация между ними держалась на честном слове.
Если нужно навести порядок в существующей структуре полей или спроектировать её с нуля под интеграцию с 1С, CRM или внешним сервисом рассылок, обычно проще один раз заказать поддержку и сопровождение сайтов, чем распутывать задвоенные поля постфактум на живом проекте.
Частые вопросы
Можно ли переименовать код UF-поля после того, как оно уже используется в коде?
Напрямую нет - Битрикс не даёт штатного инструмента переименования FIELD_NAME. Технически можно создать новое поле с нужным кодом, перенести значения скриптом через CUserTypeEntity и CUser::Update, а старое удалить. Делать это стоит только в тестовом окружении с бэкапом базы под рукой.
Почему UF-поле не отображается в форме редактирования пользователя в админке?
Чаще всего причина в настройке SHOW_IN_LIST/EDIT_IN_LIST при создании поля - она отдельная от видимости на публичной части. Проверяю через Настройки > Пользователи > Поля пользователей, открываю поле и смотрю галочки «Показывать в списке» и «Показывать в форме».
Как быстро проверить, что вообще за UF-поля есть у сущности USER в проекте?
Запросом к таблице b_user_field: SELECT FIELD_NAME, USER_TYPE_ID FROM b_user_field WHERE ENTITY_ID = 'USER'. Это быстрее, чем листать админку, особенно на проекте, который достался по наследству без документации.
Работают ли UF-поля пользователя с highload-блоками и REST API Битрикса?
Да, highload-блоки используют тот же движок userfield, что и остальные сущности, поэтому UF-поля можно добавлять и к строкам highload-блока. В REST API (rest_marketplace, стандартные методы user.*) кастомные UF-поля пользователя отдаются вместе со стандартными, если явно не отфильтрованы в SELECT.