Импорт пользователей из файла - связь, пароли, группы
Заводим сотрудников из файла: разбираем строки, создаём учётные записи и делаем повторный запуск импорта безопасным для уже созданных записей.
Решение
Читаем файл построчно:
$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 остаётся единственным безопасным путём: он проверяет данные и запускает события. Прямая запись в таблицы ломает связанные данные и не создаёт групп.
Смежное
-
Импорт пользователей - оглавление подтемы
-
Пользователи, группы и права доступа - устройство учётных записей
-
Синхронизация с Active Directory: подключение, отбор, группы - импорт из каталога организации
-
Права доступа на практике - что получает импортированный пользователь
-
Импорт каталога из файла: CSV, повторный запуск, свои поля - тот же приём для товаров
-
Поля разделов и пользователей: выборка, множественные значения, файлы - свои поля у пользователей
-
Рассылка с нуля: сегмент, письмо, отправка, отписка - что делать с загруженными адресами
-
Выгрузка пользователей: отбор, поля, персональные данные - обратное направление
-
Перенос заказов и клиентов со старого сайта: порядок и сверка - вторая половина переезда
-
Синхронизация пользователей с внешней системой: ключ, группы, увольнения - когда обмен идёт регулярно
-
Пользователь не создался при импорте: разбор причин - когда строки файла проходят вхолостую
-
Учётная запись изнутри - из чего состоит запись и что делает платформа при её создании