Участники и роли

Объект Bitrix\HumanResources\Item\NodeMember связывает пользователя с подразделением или командой. Для каждого узла объект хранит отдельный набор ролей пользователя.

Сервис Bitrix\HumanResources\Public\Service\NodeMemberService находит связи пользователей с узлами и участников с заданной ролью. С его помощью можно дополнить ORM-запрос Bitrix\Main\UserTable и получить данные пользователей вместе со сведениями об их подразделениях или командах.

Выбрать метод

Выбирайте метод сервиса Bitrix\HumanResources\Public\Service\NodeMemberService по исходным данным и ожидаемому результату.

Задача

Метод

Результат

Найти подразделения или команды известных пользователей

findAllByEntityIds()

Коллекция связей пользователей с узлами

Получить участников узла с заданным числовым идентификатором роли

findAllByRoleIdAndNodeId()

Коллекция участников узла

Найти узлы пользователя по стандартным ролям

findByUserIdAndRoleXmlIds() или findAllByUserIdAndRoleXmlIds() сервиса пользователей

Одна связь, null или коллекция связей

Получить подразделения, которыми управляет пользователь

getManagedDepartmentNodes()

Коллекция связей с подразделениями

Добавить сведения об узлах в запрос к Bitrix\Main\UserTable

injectUserNodeSubquery()

Дополненный ORM-запрос пользователей

Сначала расположить пользователей из тех же подразделений, а руководителей — выше сотрудников

injectUserNodeSort()

ORM-запрос с добавленной сортировкой

Получить сервис участников

Сервис Bitrix\HumanResources\Public\Service\NodeMemberService работает со связями участников подразделений и команд. Перед вызовом подключите модуль humanresources. Получите сервис через контейнер Bitrix\HumanResources\Public\Service\Container.

use Bitrix\HumanResources\Public\Service\Container;
use Bitrix\Main\Loader;

if (!Loader::includeModule('humanresources'))
{
    throw new \RuntimeException('Требуется модуль humanresources');
}

$memberService = Container::getNodeMemberService();

Все следующие примеры используют переменную $memberService из этого блока.

Для работы с руководителями и подчиненными получите сервис Bitrix\HumanResources\Public\Service\Node\UserService с помощью метода Bitrix\HumanResources\Public\Service\Container::getUserService(). Сценарии работы с этим сервисом смотрите в статье Пользователи и управленческая иерархия.

Данные участника узла

Пользователь и участник узла — разные объекты. Для каждого узла сервис возвращает отдельный объект Bitrix\HumanResources\Item\NodeMember.

Объект Bitrix\HumanResources\Item\NodeMember содержит свойства связи. Обращайтесь к ним через объект, например $member->nodeId. Это не параметры метода: сервис заполняет свойства в возвращенном объекте. Знак ? перед типом означает, что свойство может содержать null.

Свойство

Тип

Назначение

id

?int

Идентификатор связи или null для несохраненного объекта

entityType

Bitrix\HumanResources\Type\MemberEntityType

Тип объекта участника

entityId

int

Идентификатор объекта, для пользователя это его ID

nodeId

int

Идентификатор узла

active

?bool

Признак активности участника или null

roles

?array<int>

Идентификаторы ролей в результатах чтения

role

?int

Идентификатор роли для создания или изменения связи либо null

icon

?string

Строковое значение значка, по умолчанию пустая строка

addedBy

?int

Идентификатор пользователя, который добавил участника, или null

createdAt

?Bitrix\Main\Type\DateTime

Дата создания связи или null

updatedAt

?Bitrix\Main\Type\DateTime

Дата изменения связи или null

node

?Bitrix\HumanResources\Item\Node

Объект связанного узла или null, если узел отсутствует

Свойство node использует отложенную загрузку. Сервис загружает объект узла при первом обращении к свойству. Если достаточно идентификатора узла, используйте nodeId: обращение к нему не загружает связанный объект.

Найти связи пользователей с узлами

Для поиска связей участников с узлами используйте методы сервиса findAllByEntityIds() и findAllByRoleIdAndNodeId(). Они возвращают коллекцию Bitrix\HumanResources\Item\Collection\NodeMemberCollection. Она позволяет перебрать найденные связи и получить идентификатор узла и роли участника для каждой связи. Элемент коллекции — это объект Bitrix\HumanResources\Item\NodeMember. Пустая коллекция означает, что подходящих связей нет.

Используйте метод findAllByEntityIds(), чтобы определить, в каких подразделениях или командах состоят известные пользователи. Передайте их идентификаторы в entityIds и укажите нужные типы узлов в nodeTypes. По умолчанию метод ищет пользователей только в активных подразделениях.

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

use Bitrix\HumanResources\Type\NodeEntityType;

$userIds = [25, 31, 42];

// Сервис участников получен в первом примере
$members = $memberService->findAllByEntityIds(
    entityIds: $userIds, // ID пользователей, связи которых нужно найти
    nodeTypes: [
        NodeEntityType::DEPARTMENT, // подразделения
        NodeEntityType::TEAM, // команды
    ],
);

foreach ($members as $member)
{
    echo "Пользователь {$member->entityId}, узел {$member->nodeId}\n";
}

Метод возвращает все найденные связи. Один пользователь может встретиться в коллекции несколько раз, если состоит в нескольких узлах или имеет несколько ролей в одном узле. Каждой роли соответствует отдельный элемент коллекции.

Ограничить поиск узлами и структурой

Передайте идентификаторы известных узлов в параметр nodeIds, чтобы найти связи только в этих узлах. Параметр structureId ограничивает поиск одной структурой, а structureAction — узлами, для которых текущему пользователю доступно указанное действие. Значение Bitrix\HumanResources\Enum\NodeActiveFilter::ONLY_GLOBAL_ACTIVE используется для параметра nodeActiveFilter по умолчанию.

use Bitrix\HumanResources\Enum\NodeActiveFilter;
use Bitrix\HumanResources\Type\NodeEntityType;
use Bitrix\HumanResources\Type\StructureAction;

$userId = 25;
$structureId = 1;

// Сервис участников получен в первом примере
$members = $memberService->findAllByEntityIds(
    entityIds: [$userId], // ID пользователя
    nodeIds: [10, 20, 30], // узлы, в которых нужно искать связь
    nodeTypes: [NodeEntityType::DEPARTMENT], // только подразделения
    structureId: $structureId, // структура, которой принадлежат узлы
    structureAction: StructureAction::ViewAction, // доступные для просмотра узлы
    nodeActiveFilter: NodeActiveFilter::ONLY_GLOBAL_ACTIVE, // стандартный фильтр активности
);

Найти участников узла по роли

Используйте метод findAllByRoleIdAndNodeId(), чтобы получить участников одного узла с заданной ролью. Передайте параметры:

  • roleId — числовой идентификатор роли из массива roles объекта Bitrix\HumanResources\Item\NodeMember,

  • nodeId — идентификатор узла,

  • limit — максимальное количество участников в результате,

  • offset — смещение от начала результата,

  • ascendingSort — порядок сортировки по идентификатору связи: true — по возрастанию, false — по убыванию.

// Сервис участников получен в первом примере
$roleId = null;
$nodeId = null;

foreach ($memberService->findAllByEntityIds(entityIds: [25]) as $member)
{
    $roleId = $member->roles[0] ?? null;
    if ($roleId !== null)
    {
        $nodeId = $member->nodeId;
        break;
    }
}

if ($roleId === null || $nodeId === null)
{
    throw new \RuntimeException('У пользователя нет связи с ролью');
}

$members = $memberService->findAllByRoleIdAndNodeId(
    roleId: $roleId, // ID роли из массива roles объекта NodeMember
    nodeId: $nodeId, // узел, в котором нужно найти участников
    limit: 50, // максимум 50 участников
    offset: 0, // начать с первого участника
    ascendingSort: true, // сортировать по возрастанию
);

Метод возвращает коллекцию Bitrix\HumanResources\Item\Collection\NodeMemberCollection. Пустая коллекция означает, что совпадений нет. Пустую коллекцию метод также возвращает, если roleId или nodeId равен 0, узел не найден либо роль не связана с указанным узлом.

Выбрать стандартную роль

Чтобы найти узлы пользователя по стандартным ролям, выберите метод сервиса Bitrix\HumanResources\Public\Service\Node\UserService:

  • findByUserIdAndRoleXmlIds() — возвращает одну связь или null,

  • findAllByUserIdAndRoleXmlIds() — возвращает все подходящие связи в коллекции Bitrix\HumanResources\Item\Collection\NodeMemberCollection.

Передайте идентификатор пользователя в userId, а внешние идентификаторы ролей — в roleXmlIds. Перечисление Bitrix\HumanResources\Type\NodeMemberRole содержит внешние идентификаторы стандартных ролей подразделений и команд.

Роли подразделений:

  • NodeMemberRole::Head — руководитель, внешний идентификатор MEMBER_HEAD,

  • NodeMemberRole::DeputyHead — заместитель, внешний идентификатор MEMBER_DEPUTY_HEAD,

  • NodeMemberRole::Employee — сотрудник, внешний идентификатор MEMBER_EMPLOYEE.

Роли команд:

  • NodeMemberRole::TeamHead — руководитель команды, внешний идентификатор MEMBER_TEAM_HEAD,

  • NodeMemberRole::TeamDeputyHead — заместитель руководителя команды, внешний идентификатор MEMBER_TEAM_DEPUTY_HEAD,

  • NodeMemberRole::TeamEmployee — участник команды, внешний идентификатор MEMBER_TEAM_EMPLOYEE.

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

use Bitrix\HumanResources\Public\Service\Container;
use Bitrix\HumanResources\Type\NodeMemberRole;

$userId = 25;
$userService = Container::getUserService();
$managedNodes = $userService->findAllByUserIdAndRoleXmlIds(
    userId: $userId,
    roleXmlIds: [
        NodeMemberRole::Head->value, // руководитель подразделения
        NodeMemberRole::TeamHead->value, // руководитель команды
    ],
);

Коллекция $managedNodes содержит связи пользователя с подразделениями и командами, где он назначен руководителем. Чтобы получить только первую подходящую связь, вызовите findByUserIdAndRoleXmlIds() с теми же аргументами. Пустой массив или массив без допустимых внешних идентификаторов ролей дает null для одиночного поиска и пустую коллекцию для множественного.

Дополнительные сценарии смотрите в статье Пользователи и управленческая иерархия.

Получить управляемые подразделения

Используйте метод getManagedDepartmentNodes(), чтобы получить подразделения, где пользователь назначен руководителем или заместителем. Метод работает только с узлами типа Bitrix\HumanResources\Type\NodeEntityType::DEPARTMENT и возвращает коллекцию Bitrix\HumanResources\Item\Collection\NodeMemberCollection.

Передайте идентификатор пользователя в userId. Параметр structureId ограничивает поиск одной структурой. Если передать null, сервис использует структуру по умолчанию.

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

$structureId = 1;

// Сервис участников получен в первом примере
$managedDepartments = $memberService->getManagedDepartmentNodes(
    userId: 25,
    structureId: $structureId, // ID нужной структуры
);

По свойству nodeId определите подразделение, а по массиву roles — роль пользователя в нем.

Добавить данные узлов в запрос пользователей

Чтобы получить пользователей из определенных подразделений или команд одним ORM-запросом, используйте injectUserNodeSubquery(). Метод дополняет запрос Bitrix\Main\UserTable связями с узлами и возвращает измененный объект Bitrix\Main\ORM\Query\Query.

Настройте выборку с помощью параметров:

  • query — запрос к Bitrix\Main\UserTable, который нужно дополнить.

  • nodeTypes — типы узлов. По умолчанию метод выбирает подразделения, значение null включает все типы узлов.

  • active — состояние связей: true — активные, false — неактивные, null — все связи. По умолчанию метод выбирает активные связи.

  • nodeIds — идентификаторы узлов. Значение null или пустой массив включает все узлы выбранных типов.

Пример. Получить пользователей из двух активных подразделений.

use Bitrix\HumanResources\Type\NodeEntityType;
use Bitrix\Main\UserTable;

$query = UserTable::query()
    ->setSelect(['ID', 'NAME', 'LAST_NAME'])
;

// Сервис участников получен в первом примере
$query = $memberService->injectUserNodeSubquery(
    query: $query,
    nodeTypes: [NodeEntityType::DEPARTMENT], // только подразделения
    active: true, // только активные связи участников
    nodeIds: [10, 20], // подразделения, из которых нужны пользователи
);

$users = $query->exec();

После вызова продолжите настройку запроса или выполните его через exec().

Отсортировать пользователей по узлам и ролям

Используйте метод injectUserNodeSort(), чтобы сначала расположить пользователей из тех же подразделений, что и выбранный пользователь, а затем остальных. В каждой части результата руководители располагаются выше сотрудников.

Сначала вызовите injectUserNodeSubquery(). Он регистрирует поле USER_NODE_MEMBER, которое использует сортировка. Затем передайте дополненный запрос в query, а идентификатор выбранного пользователя — в userId.

Связи с несколькими узлами или ролями могут создать несколько строк для одного пользователя. Метод setDistinct() формирует результат с уникальными строками. Вызывайте его до выполнения или подсчета запроса, если результат должен содержать по одной строке на пользователя.

use Bitrix\Main\UserTable;

$currentUserId = 25;
$query = UserTable::query()
    ->setSelect(['ID', 'NAME', 'LAST_NAME'])
;

// Сервис участников получен в первом примере
$query = $memberService->injectUserNodeSubquery($query); // добавить связи с узлами
$query = $memberService->injectUserNodeSort(
    query: $query,
    userId: $currentUserId, // пользователь, чьи подразделения должны быть первыми
);

$query->setDistinct(); // по одной строке на пользователя
$users = $query->exec();

Обработать пустой результат и ошибки

Если метод вернул пустой результат, проверьте условия поиска:

  • модуль humanresources подключен,

  • идентификаторы пользователей, узлов, структуры и роли существуют,

  • nodeTypes содержит Bitrix\HumanResources\Type\NodeEntityType::TEAM для выборки участников команд,

  • фильтр активности включает целевые узлы или связи,

  • параметры nodeIds и structureId включают целевые узлы, а limit и offset не исключают нужные связи,

  • текущему пользователю доступно действие из structureAction.

Пустая коллекция Bitrix\HumanResources\Item\Collection\NodeMemberCollection — корректный результат поиска. Проверяйте количество элементов перед обработкой. Для результата одиночного поиска отдельно проверяйте значение null.

При работе с сервисом учитывайте следующие исключения:

Исключение

На каком этапе возможно

Bitrix\Main\ArgumentException

Подготовка поискового запроса и его аргументов

Bitrix\Main\ObjectPropertyException

Подготовка и чтение данных ORM

Bitrix\Main\SystemException

Подготовка или выполнение операции сервиса

Bitrix\HumanResources\Exception\WrongStructureItemException

Обработка элемента структуры компании

Bitrix\Main\DB\SqlQueryException

Выполнение ORM-запроса через exec()

Обрабатывайте исключения на уровне приложения, где доступен контекст запроса и можно выбрать дальнейшее действие.

При поиске с structureAction ошибка построения фильтра доступа преобразуется в пустую коллекцию Bitrix\HumanResources\Item\Collection\NodeMemberCollection. Поиск без совпадений возвращает такой же результат. Для диагностики сохраняйте исходные идентификаторы и выбранное действие. Права пользователя проверяйте отдельно от содержимого коллекции.

Выбрать сервис или билдер

Выбирайте сервис Bitrix\HumanResources\Public\Service\NodeMemberService для типового поиска связей, выборки по одной роли и расширения запросов Bitrix\Main\UserTable. Используйте Bitrix\HumanResources\Builder\Structure\NodeMemberDataBuilder, когда один запрос должен объединить несколько фильтров по узлам, пользователям, ролям, иерархии или доступу.

Методы поиска и getManagedDepartmentNodes() читают существующие связи. Методы injectUserNodeSubquery() и injectUserNodeSort() изменяют только настройки переданного объекта Bitrix\Main\ORM\Query\Query. Запрос загружает данные после вызова exec().

Сервис Bitrix\HumanResources\Public\Service\NodeMemberService не изменяет роли участников. Для поиска и обхода подразделений и команд используйте сервис узлов. Для получения руководителей и подчиненных, проверки управленческих отношений и назначения пользователей в подразделения используйте сервисы управленческой иерархии.