+7 495 008 8452 пн.-пт. 10:00 – 17:00
Если у вас возникли какие либо вопросы которые вы не смогли решить по нашим публикациям самостоятельно,
то ждем ваше обращение в нашей службе тех поддержки.


Выбор пользователей: сложная логика в фильтре.

При использовании CUser::GetList нет возможности фильтровать пользователей, используя логику в фильтре. ORM в новом ядре и класс Bitrix\Main\UserTable позволяет решить эту проблему.
Допустим, нам нужно выбрать пользователя 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);
}
Все хорошо до тех пор, пока мы не решим применить фильтрацию по группам. Например, дополнительно, выбрать пользователей с UF_SHMS= 770, состоящих в группе 6.
Соответствие группа-пользователь хранится в таблице 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);
}
Из-за того, что пользователь может состоять в нескольких группах. Получим несколько одинаковых записей. Это решается добавлением параметра "data_doubling"=>false в getList
$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);
}
Все бы хорошо, но на каждый встречающийся Bitrix\Main\UserGroupTable:USER делается один подзапрос, т.е. будет
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();
}
echo getSqlForGroup(6); выберет нам пользователей 6 группы
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);
}


Назад в раздел

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












CAPTCHA