Работа с пользователями Bitrix: дата регистрации и агрегированное UF‑поле
Дата регистрации в b_user
Штатным способом дату создания записи пользователя обычно не трогают, но иногда после миграции или синхронизации нужно выровнять поле под фактический момент появления учётной записи.
Операция затрагивает одну строку по числовому ID; делайте снимок данных и проверку на тестовой копии, прежде чем повторить на боевой базе.
UPDATE `b_user`
SET `DATE_REGISTER` = '2015-11-05 00:00:00'
WHERE `ID` = 4732;Структура «без огромного списка UF_*»
Если свойств немного заранее известное множество, проще использовать штатный набор пользовательских полей. Если же нужен произвольный набор ключей (флаги мастера уведомлений, промежуточные данные виджета и т. п.), разумнее завести одно UF‑поле и хранить в нём сериализованный объект — так вы не упираетесь в лимит столбцов профиля и не плодите редко используемые поля.
В административной части один раз добавляете, например, строковое поле UF_APP_PAYLOAD с достаточной длиной (или переходите на хранение в текстовом типе).
Ниже — компактная обёртка: повторное чтение кэшируется в памяти PHP на время запроса, сохранение пересериализует объект и вызывает CUser::Update только когда вы это явно просите.
GetID();
if ($numericId <= 0) {
throw new RuntimeException('Неавторизованный контекст');
}
return self::$memo[$numericId] ?? (self::$memo[$numericId] = new self($numericId));
}
private StdClass $bag;
private int $userId;
private function __construct(int $userId)
{
$this->userId = $userId;
$this->hydrate();
}
public function __get(string $key)
{
return $this->bag->{$key} ?? null;
}
public function __set(string $key, $value): void
{
$this->bag->{$key} = $value;
}
public function __isset(string $key): bool
{
return isset($this->bag->{$key});
}
public function persist(): void
{
$api = new CUser();
$api->Update($this->userId, [
'UF_APP_PAYLOAD' => serialize($this->bag),
]);
}
private function hydrate(): void
{
$row = CUser::GetByID($this->userId)->Fetch();
if (!$row || empty($row['UF_APP_PAYLOAD'])) {
$this->bag = new StdClass();
return;
}
$candidate = @unserialize($row['UF_APP_PAYLOAD'], ['allowed_classes' => false]);
$this->bag = is_object($candidate) ? $candidate : new StdClass();
}
}
try {
$extras = UserPayloadFacade::forCurrent();
$welcomeQueue = isset($extras->letters) ? (object)$extras->letters : new StdClass();
} catch (Throwable $failure) {
$welcomeQueue = new StdClass();
}@unserialize здесь упрощённо: для продакшена стоит валидировать размер строки и логировать битые данные так же аккуратно, как любую пользовательскую прослойку над БД.
Что держать в голове
- Сериализация привязана к версии PHP; при апгрейде интерпретатора планируйте миграцию формата (JSON вместо
serialize) на длинном горизонте. - Блокировки конкурентных сохранений: два параллельных запроса могут затереть чужое изменение, если каждый читает копию, меняет и пишет целиком — при высокой гонке лучше нормализовать отдельные поля или ввести версионность.
- Индексировать или фильтровать по содержимому «мешка» средствами SQL неудобно; для частых фильтров по значению выносите поле из агрегата.
Не хотите копаться сами?
Починю за 1-3 дня. Без предоплаты — оплата по результату.
15+ лет опыта с 1С-Битрикс · Без предоплаты · 7 дней гарантии