Yandex Metrika
sanches.free 1 просмотр

Условия фильтрации в выборках 1С‑Битрикс: операторы, даты свойств и интервалы активности

Операторы в ключах фильтра

В большинстве выборок Битрикс первый параметр фильтра — ассоциативный массив; префикс перед именем поля задаёт оператор сравнения.

  • ! — не равно
  • < — меньше
  • <= — меньше или равно
  • > — больше
  • >= — больше или равно
  • >< — значение между двумя границами (массив из двух строк)
  • !>< — вне интервала
  • значение false без префикса — «пусто / не задано» в трактовке конкретного поля

Состав ключей нужно проверять по документации сущности: для свойств типа список, привязка к элементам и т. д. возможны исключения и составные ключи вида PROPERTY_*.

Форматы даты: свойства против «активности»

Даты в пользовательских свойствах инфоблока (не системные поля элемента) обычно сравнивают в том виде, в каком они лежат в базе после сохранения, чаще Y-m-d H:i:s. Для границ «начала» и «конца активности» ядро ориентируется на строку в региональном формате вида d.m.Y H:i:s.

Пример: элементы не старше 31 дня по свойству PAY_DATE:

$anchor = new \DateTime('now', new \DateTimeZone('UTC'));
$anchor->modify('-31 day');

$arFilter['>=PROPERTY_PAY_DATE'] = $anchor->format('Y-m-d H:i:s');

Для DATE_CREATE и схожих полей смешивание форматов легко даёт «пустую» выборку; приведите строку к тому же шаблону, что возвращает ваша конвертация в админке/импорте.

$now = new \DateTime();
$arFilter = [
    '>DATE_CREATE' => $now->modify('-31 day')->format('d.m.Y H:i:s'),
];

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

Если датой анонса служит только «начало активности», а «конец» используется лишь для снятия с показа, удобно строить полуинтервал через оператор «между» по DATE_ACTIVE_FROM.

$bitrixDatetime = 'd.m.Y H:i:s';

$year = isset($_REQUEST['year']) ? (int)$_REQUEST['year'] : 0;
$month = isset($_REQUEST['month']) ? (int)$_REQUEST['month'] : 0;
$day = isset($_REQUEST['day']) ? (int)$_REQUEST['day'] : 0;

$arFilter = ['IBLOCK_ID' => $newsIblockId];

switch (true) {
    case $year > 0 && $month > 0 && $day > 0:
        $periodStart = new \DateTime(
            sprintf('%04d-%02d-%02d 00:00:00', $year, $month, $day),
            new \DateTimeZone('UTC')
        );
        $periodEnd = (clone $periodStart)->modify('+1 day -1 second');
        break;
    case $year > 0 && $month > 0:
        $periodStart = new \DateTime(
            sprintf('%04d-%02d-01 00:00:00', $year, $month),
            new \DateTimeZone('UTC')
        );
        $periodEnd = (clone $periodStart)->modify('+1 month -1 second');
        break;
    default:
        $periodStart = $periodEnd = null;
}

if ($periodStart && $periodEnd) {
    $arFilter['><DATE_ACTIVE_FROM'] = [
        $periodStart->format($bitrixDatetime),
        $periodEnd->format($bitrixDatetime),
    ];
}

События с датой начала и окончания

Когда интервал задаётся связкой DATE_ACTIVE_FROM / DATE_ACTIVE_TO, выбор элементов «которые идут в этот календарный день» сводится к пересечению отрезков: начало действия не позже конца дня, конец действия не раньше начала дня.

$bitrixDatetime = 'd.m.Y H:i:s';

$arFilter = [
    'IBLOCK_ID' => $promoIblockId,
    'ACTIVE' => 'Y',
];

$dayStart = new \DateTime(
    sprintf('%04d-%02d-%02d 00:00:00', (int)$_REQUEST['year'], (int)$_REQUEST['month'], (int)$_REQUEST['day']),
    new \DateTimeZone('UTC')
);
$dayTail = (clone $dayStart)->modify('+1 day -1 second');

$arFilter['>=DATE_ACTIVE_TO'] = $dayStart->format($bitrixDatetime);
$arFilter['<=DATE_ACTIVE_FROM'] = $dayTail->format($bitrixDatetime);

Инвертировать условие «ничего не пересекается» легче на уровне SQL или отдельного слоя приложения — не перегружайте один и тот же массив взаимоисключающими ветками без комментария.

Календарь на месяц и прямой SQL

Чтобы собрать сетку «какие даты месяца заняты» по нескольким инфоблокам с активностью только по ACTIVE_FROM, один проход через GetList с тысячами элементов дороже, чем агрегат в базе с узким интервалом дат.

Строитель запроса обязан экранировать идентификаторы и подставлять границы только из вашего кода, а не из $_REQUEST напрямую: пример ниже упрощённо показывает идею, в реальности используйте #IBLOCK_IDS# с помощью $DB->ForSql или подготовленные выражения.

$monthStart = new \DateTime(sprintf('%04d-%02d-01 00:00:00', $calendarYear, $calendarMonth));
$monthEnd = (clone $monthStart)->modify('+1 month -1 second');
$fromSql = $DB->ForSql($monthStart->format('Y-m-d H:i:s'));
$toSql = $DB->ForSql($monthEnd->format('Y-m-d H:i:s'));

$sql = sprintf(
    "SELECT EXTRACT(DAY FROM ACTIVE_FROM) AS `day`, NAME, PREVIEW_PICTURE"
    . " FROM b_iblock_element"
    . " WHERE ACTIVE = 'Y' AND IBLOCK_ID IN (%s)"
    . " AND ACTIVE_FROM BETWEEN '%s' AND '%s'"
    . " ORDER BY SORT",
    implode(',', array_map('intval', $calendarIblockIds)),
    $fromSql,
    $toSql
);

$res = $DB->Query($sql, false);
while ($row = $res->Fetch()) {
    // группируйте $row по дню календаря
}

Если нужно знать, попадает ли длинное событие на каждый день месяца, добавляются выражения вида пересечения по дню — но объём генерации календаря растёт; часто достаточно выбрать кандидатов SQL и уже в PHP классифицировать строки недели.

Итого

  • Следите за форматом строки под тип поля: свойство, DATE_* системное, активность элемента.
  • Интервалы «за день/месяц» унифицируют через два DateTime (границы периода) и оператор >< там, где возможно.
  • Сложные календари и отчётность по месяцу оправданы прямым SQL только при аккуратной санитизации и ограниченном интервале дат.
  • Подробности преобразования меток времени между форматами и часовыми поясами вынесите в общий справочник по датам на проекте — это снижает дубли в фильтрах.

Не хотите копаться сами?

Починю за 1-3 дня. Без предоплаты — оплата по результату.

15+ лет опыта с 1С-Битрикс · Без предоплаты · 7 дней гарантии