При использовании CUser::GetList нет возможности фильтровать пользователей, используя логику в фильтре. ORM в новом ядре и класс Bitrix\Main\UserTable позволяет решить эту проблему.
Допустим, нам нужно выбрать пользователя 20, а также пользователей имеющих значение пользовательского поля UF_SHMS = 770
Код будет следующий:
Все хорошо до тех пор, пока мы не решим применить фильтрацию по группам. Например, дополнительно, выбрать пользователей с UF_SHMS= 770, состоящих в группе 6.
Соответствие группа-пользователь хранится в таблице b_user_group. Для работы с этой таблицей есть класс Bitrix\Main\UserGroupTable. Одному пользователю из b_user может соответствовать несколько записей в b_user_group.
Итого имеем отношение один ко многим. В учебном сказано, что при таком отношении «нужно лишь использовать специальный синтаксис» и приведен пример для параметра select. Это же работает и для filter. Используем
Из-за того, что пользователь может состоять в нескольких группах. Получим несколько одинаковых записей. Это решается добавлением параметра "data_doubling"=>false в getList
В результате получаем подзапрос:
Фильтрация по группе с ID = 6 это не совсем корректно, так как есть ещё ограничение по времени привязки к группам. Поэтому полный фильтр будет такой:
Все бы хорошо, но на каждый встречающийся Bitrix\Main\UserGroupTable:USER делается один подзапрос, т.е. будет
Это не совсем то, что нам нужно.
Поэтому используем свой подзапрос:
echo getSqlForGroup(6); выберет нам пользователей 6 группы
Далее используем это в фильтре:
Допустим, нам нужно выбрать пользователя 20, а также пользователей имеющих значение пользовательского поля UF_SHMS = 770
Код будет следующий:
$arFilter = Array(
Array(
"LOGIC"=>"OR",
Array(
"ID"=>20
),
Array(
"UF_SHMS"=>770
)
)
);
$res = Bitrix\Main\UserTable::getList(Array(
"sel ect"=>Array("ID","NAME"),
"filter"=>$arFilter,
));
while ($arRes = $res->fetch()) {
debugmessage($arRes);
} |
Соответствие группа-пользователь хранится в таблице b_user_group. Для работы с этой таблицей есть класс Bitrix\Main\UserGroupTable. Одному пользователю из b_user может соответствовать несколько записей в b_user_group.
Итого имеем отношение один ко многим. В учебном сказано, что при таком отношении «нужно лишь использовать специальный синтаксис» и приведен пример для параметра select. Это же работает и для filter. Используем
$arFilter = Array(
Array(
"LOGIC"=>"OR",
Array(
"ID"=>20
),
Array(
"UF_SHMS"=>770,
"Bitrix\Main\UserGroupTable:USER.GROUP_ID"=>6
)
)
);
$res = Bitrix\Main\UserTable::getList(Array(
"select"=>Array("ID","NAME"),
"filter"=>$arFilter,
));
while ($arRes = $res->fetch()) {
debugmessage($arRes);
} |
$res = Bitrix\Main\UserTable::getList(Array(
"select"=>Array("ID","NAME"),
"filter"=>$arFilter,
"data_doubling"=>false
));
|
| `main_user_uts_object`.`UF_SHMS` = 770 AND `main_user`.`ID` IN (SELECT `main_user_tmp355191381`.`ID` AS `ID` FR OM `b_user` `main_user_tmp355191381` LEFT JOIN `b_user_group` `main_user_user_group_user_tmp355191381` ON `main_user_user_group_user_tmp355191381`.`USER_ID` = `main_user_tmp355191381`.`ID` WHERE `main_user_user_group_user_tmp355191381`.`GROUP_ID` = 6) |
Фильтрация по группе с ID = 6 это не совсем корректно, так как есть ещё ограничение по времени привязки к группам. Поэтому полный фильтр будет такой:
$connection = Bitrix\Main\Application::getConnection();
$helper = $connection->getSqlHelper();
$ugt = "Bitrix\Main\UserGroupTable:USER.";
$arFilter = Array(
Array(
"LOGIC"=>"OR",
Array(
"ID"=>20
),
Array(
"UF_SHMS"=>770,
"ID"=>4,
Array(
$ugt."GROUP_ID"=>6,
Array(
"LOGIC"=>"OR",
$ugt."DATE_ACTIVE_FROM"=>false,
"<=".$ugt."DATE_ACTIVE_FROM"=>new \Bitrix\Main\DB\SqlEx * pression($helper->getCurrentDateTimeFunction())
),
Array(
"LOGIC"=>"OR",
$ugt."DATE_ACTIVE_TO"=>false,
">=".$ugt."DATE_ACTIVE_TO"=>new \Bitrix\Main\DB\SqlEx * pression($helper->getCurrentDateTimeFunction())
)
)
)
)
);
$res = Bitrix\Main\UserTable::getList(Array(
"select"=>Array("ID","NAME"),
"filter"=>$arFilter,
"data_doubling"=>false
));
while ($arRes = $res->fetch()) {
debugmessage($arRes);
} |
| WHERE `ID` IN (.. WHERE .. GROUP_ID` = 6 `ID` IN (.. WHERE … DATE_ACTIVE_FROM` IS NULL `ID` IN (.. WHERE … DATE_ACTIVE_TO` IS NULL `ID` IN (.. WHERE … DATE_ACTIVE_FROM` <= NOW() `ID` IN (.. WHERE … DATE_ACTIVE_TO` >= NOW() |
Это не совсем то, что нам нужно.
Поэтому используем свой подзапрос:
function getSqlForGroup ($group) {
$connection = Bitrix\Main\Application::getConnection();
$helper = $connection->getSqlHelper();
$query = new \Bitrix\Main\Entity\Query(Bitrix\Main\UserGroupTable::getEntity());
$query->setSelect(Array("D_USER_ID"));
$query->setFilter(Array(
"GROUP_ID"=>$group,
Array(
"LOGIC"=>"OR",
"DATE_ACTIVE_FROM"=>false,
"<=DATE_ACTIVE_FROM"=>new \Bitrix\Main\DB\SqlEx * pression($helper->getCurrentDateTimeFunction())
),
Array(
"LOGIC"=>"OR",
"DATE_ACTIVE_TO"=>false,
">=DATE_ACTIVE_TO"=>new \Bitrix\Main\DB\SqlEx * pression($helper->getCurrentDateTimeFunction())
)
));
$query->registerRuntimeField(0, new \Bitrix\Main\Entity\ExpressionField('D_USER_ID', 'DISTINCT(USER_ID)'));
return $query->getQuery();
} |
| SELECT DISTINCT(USER_ID) AS `D_USER_ID` FR OM `b_user_group` `main_user_group` WH ERE `main_user_group`.`GROUP_ID` = 6 AND ( (`main_user_group`.`DATE_ACTIVE_FROM` IS NULL) OR `main_user_group`.`DATE_ACTIVE_FROM` <= NOW() ) AND ( (`main_user_group`.`DATE_ACTIVE_TO` IS NULL) OR `main_user_group`.`DATE_ACTIVE_TO` >= NOW() ) |
Далее используем это в фильтре:
$arFilter = Array(
Array(
"LOGIC"=>"OR",
Array(
"ID"=>20
),
Array(
"UF_SHMS"=>770,
"ID"=>4,
Array(
"@ID"=>new \Bitrix\Main\DB\SqlEx * pression(getSqlForGroup(6))
)
)
)
);
$res = Bitrix\Main\UserTable::getList(Array(
"select"=>Array("*"),
"filter"=>$arFilter,
"data_doubling"=>false
));
while ($arRes = $res->fetch()) {
debugmessage($arRes);
} |