Перейти к содержимому

Импорт пользователей из файла - связь, пароли, группы

Заводим сотрудников из файла: разбираем строки, создаём учётные записи и делаем повторный запуск импорта безопасным для уже созданных записей.

Решение

Читаем файл построчно:

$handle = fopen('/home/bitrix/import/users.csv', 'r');
$header = fgetcsv($handle, 0, ';'); // первая строка - имена колонок
while (($row = fgetcsv($handle, 0, ';')) !== false) {
$data = array_combine($header, $row);
// здесь одна строка файла: логин, почта, отдел, должность
}
fclose($handle);

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

Ищем существующую запись перед созданием:

$found = CUser::GetList('ID', 'ASC', ['LOGIN_EQUAL' => $data['login']], ['NAV_PARAMS' => false]);
$existing = $found->Fetch();
// ключ связи выбирают один раз: логин, почта или свой внешний код

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

Создаём или обновляем учётную запись:

$user = new CUser();
$fields = [
'LOGIN' => $data['login'],
'EMAIL' => $data['email'],
'NAME' => $data['name'],
'GROUP_ID' => [$employeesGroupId], // группы задаются сразу
'ACTIVE' => 'Y',
];
if ($existing) {
$user->Update($existing['ID'], $fields);
} else {
$fields['PASSWORD'] = $fields['CONFIRM_PASSWORD'] = randString(12);
$id = $user->Add($fields);
if (!$id) { echo $user->LAST_ERROR, "\n"; } // причина отказа лежит в свойстве
}

Временный пароль генерируется самим скриптом и в файле импорта не хранится. Причина отказа приходит не в возвращаемом значении, а отдельным свойством объекта, и без её печати импорт молча теряет часть строк файла.

Выгружаем существующих пользователей:

$res = CUser::GetList('ID', 'ASC', ['ACTIVE' => 'Y'],
['FIELDS' => ['ID', 'LOGIN', 'EMAIL', 'NAME', 'LAST_NAME']]);
$out = fopen('/home/bitrix/import/export.csv', 'w');
while ($row = $res->Fetch()) {
fputcsv($out, $row, ';'); // пароли не выгружаются никогда
}
fclose($out);

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

Импорт удобно делать в два прохода: сначала разбор файла с проверкой данных и подсчётом, потом запись. Первый проход показывает, сколько записей будет создано и сколько обновлено, и ошибки в файле видно до того, как в базе появилась хоть одна строка.

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

Типичные проблемы

Второй запуск создал полный дубль базы.

Не задан ключ связи. Без поиска существующей записи импорт создаёт новую на каждую строку файла.

Часть строк не импортировалась молча.

Не печатается причина отказа. Метод создания возвращает ложь, а текст ошибки лежит в свойстве объекта.

В именах сотрудников знаки вопроса.

Кодировка файла расходится с кодировкой сайта. Файл перекодируют до импорта, а не правят записи после.

Все импортированные без прав.

Группы не заданы при создании. Пользователь попадает только в группу по умолчанию.

Скрипт падает по памяти на большом файле.

Файл читается целиком. Построчное чтение держит в памяти одну запись независимо от размера.

Частые вопросы

Как импортировать пользователей с фотографиями?

Отдельным полем с путём к файлу: платформа принимает описание файла и копирует его сама. Сами изображения кладут рядом с файлом импорта.

Можно ли импортировать пользовательские поля?

Да, они передаются в том же массиве полей по своему имени. Поле должно существовать у сущности пользователя до импорта.

Как отправить сотрудникам их пароли?

Письмом со ссылкой на смену пароля, а не самим паролем. Ссылка одноразовая, и пароль нигде не хранится в открытом виде.

Что быстрее для больших списков?

Импорт через API остаётся единственным безопасным путём: он проверяет данные и запускает события. Прямая запись в таблицы ломает связанные данные и не создаёт групп.

Смежное

Первоисточник