Фильтр элементов инфоблока в системе 1С-Битрикс

Активация механизма фильтрации

Для инициализации фильтрационного механизма выполните следующие шаги:

  1. Инициализация глобального фильтра\ Перед вызовом компонента определите глобальный массив с параметрами фильтрации:

    $GLOBALS['arrFilter'] = array(
       'ACTIVE' => 'Y',
       '!PROPERTY_DISCOUNTS' => false
    );
  2. Конфигурация компонента\ В параметрах компонента активируйте фильтр и укажите его идентификатор:

    $APPLICATION->IncludeComponent(
       'bitrix:news.list',
       '',
       array(
           'USE_FILTER' => 'Y',
           'FILTER_NAME' => 'arrFilter',
           // ... другие параметры
       )
    );

Базовые сценарии фильтрации

1. Условия по свойствам элемента

  • Наличие заполненного свойства:

    $GLOBALS['arrFilter'] = array('!PROPERTY_DISCOUNTS' => false);
  • Фильтрация по идентификаторам:

    // Одиночный элемент
    $GLOBALS['arrFilter'] = array('ID' => 12);
    
    // Множество элементов
    $GLOBALS['arrFilter'] = array('ID' => array(10, 11, 12));

2. Временные интервалы

  • Элементы, созданные сегодня:

    $GLOBALS['arrFilter'] = array('>=DATE_ACTIVE_FROM' => date('d.m.Y'));
  • Элементы за последние N дней (пример для 2 дней):

    $GLOBALS['arrFilter'] = array(
      '>=DATE_CREATE' => date('Y-m-d H:i:s', time() - 86400*2),
      '<=DATE_CREATE' => date('Y-m-d H:i:s')
    );

3. Работа со свойствами

  • Фильтр по значению списка (CODE свойства = HIT):

    $GLOBALS['arrFilter'] = array('=PROPERTY_HIT_VALUE' => 'Yes');
  • Фильтр по ID свойства (ID свойства = 15):

    $GLOBALS['arrFilter'] = array('PROPERTY_15_VALUE' => 'Yes');

4. Пространственная фильтрация

  • Элементы раздела:

    $GLOBALS['arrFilter'] = array('SECTION_ID' => 90);
  • Наличие на складе:

    $GLOBALS['arrFilter'] = array(
      '>CATALOG_STORE_AMOUNT_1' => 0, // ID склада = 1
      '>CATALOG_PRICE_5' => 0         // ID типа цены = 5
    );

Расширенные условия выборки

Операторы сравнения

Оператор Описание Пример
! Не равно '!ID' => 10
>< Между '><DATE_ACTIVE_FROM' => array($from, $to)
!>< Не между '!><PRICE' => array(100, 200)
false без значения

Специфика работы с датами

1. Форматы данных

  • Встроенные поля (DATE_ACTIVE_FROM):\ Используйте формат DD.MM.YYYY HH:MM:SS

    $filter = array('>=DATE_ACTIVE_FROM' => '01.01.2023 00:00:00');
  • НЕ встроенные поля : При поиске по НЕ встроенным полям (НЕ «начало активности», НЕ «конец активности») обязательно конвертировать дату в формат хранения YYYY-MM-DD HH:MI:SS (DateTime::format('Y-m-d H:i:s')), например выбор записей не старше 31 дня:

    $dateNow = new DateTime('now', new DateTimeZone('UTC'));
    $dateNow->modify('-31 day');
    $arFilter['>=PROPERTY_PAY_DATE'] = $dateNow->format('Y-m-d H:i:s');

    Выбрать записи не старше 31 дня

    $now = new DateTime();
    $arFilter = array(">DATE_CREATE" => $now->modify('-31 day')->format('d.m.Y H:i:s'));
  • Пользовательские свойства (тип Дата):\ Применяйте формат YYYY-MM-DD HH:MM:SS

    $date = (new DateTime())->format('Y-m-d H:i:s');
    $filter = array('>=PROPERTY_CUSTOM_DATE' => $date);

2. Практические кейсы

  • Новости за текущий месяц:

    $from = (new DateTime('first day of this month'))->format('d.m.Y H:i:s');
    $to = (new DateTime('last day of this month'))->format('d.m.Y H:i:s');
    $GLOBALS['arrFilter'] = array('><DATE_ACTIVE_FROM' => array($from, $to));
  • Активные события на дату:

    $targetDate = (new DateTime('2023-10-01'))->format('d.m.Y H:i:s');
    $filter = array(
      '<=DATE_ACTIVE_FROM' => $targetDate,
      '>=DATE_ACTIVE_TO' => $targetDate
    );

Оптимизация производительности

Для сложных выборок используйте прямое обращение к БД:

$query = $DB->Query("
    SELECT ID, NAME 
    FROM b_iblock_element 
    WHERE 
        IBLOCK_ID = 10 AND
        ACTIVE_FROM BETWEEN '2023-01-01' AND '2023-12-31'
    ORDER BY DATE_CREATE DESC
");

Рекомендация: При работе с прямыми SQL-запросами учитывайте структуру таблиц (b_iblock_element, b_iblock_property) и используйте подготовленные выражения для защиты от SQL-инъекций.


ЕЩЕ ФИЛЬТРЫ


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

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

<?php
$BITRIX_DATETIME_FORMAT = 'd.m.Y H:i:s';

$activeYear = isset($_REQUEST['year']) ? (int)$_REQUEST['year'] : null;
$activeMonth = isset($_REQUEST['month']) ? (int)$_REQUEST['month'] : null;
$activeDay = isset($_REQUEST['day']) ? (int)$_REQUEST['day'] : null;
$activeCompany = isset($_REQUEST['company']) ? (int)$_REQUEST['company'] : null;

$arFilter = array("IBLOCK_ID"=>BlockId::_()->news);

switch(true) {
  case isset($activeDay) && isset($activeMonth) && isset($activeYear): // новости за день
    $dateBegin = new DateTime(sprintf('%1$04d-%2$02d-%3$02d 00:00:00', $activeYear, $activeMonth, $activeDay), new DateTimeZone('UTC'));
    $dateEnd = clone $dateBegin;
    $dateEnd->modify('+1 day -1 second');
    break;
  case isset($activeMonth) && isset($activeYear): // новости за месяц
    $dateBegin = new DateTime(sprintf('%1$04d-%2$02d-%3$02d 00:00:00', $activeYear, $activeMonth, $activeDay), new DateTimeZone('UTC'));
    $dateEnd = clone $dateBegin;
    $dateEnd->modify('+1 month -1 second');
    break;
  default:
}

if (isset($dateBegin) && isset($dateEnd)) {
  $arFilter['><DATE_ACTIVE_FROM'] = array($dateBegin->format($BITRIX_DATETIME_FORMAT), $dateEnd->format($BITRIX_DATETIME_FORMAT));
}

$GLOBALS['arFilter'] = $arFilter;

Выбор событий с интервалом

Допустим, событие (акция) имеет продолжительность, которую указываем в полях «начало активности» и «конец активности», без учета времени, только дата.

«Конец активности» в данном случае служит для указания конца интервала времени, а не снятия с публикации (хотя такое тоже возможно).

Тогда выбор текущего события выглядит так:

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

$arFilter = array("IBLOCK_ID" => $this->arParams['IBLOCK_ID'], "ACTIVE"=>"Y");
$arSort = array("SORT" => "DESC");
$arSelect = array("IBLOCK_ID", "ID", "CODE", "NAME", "PREVIEW_TEXT", "PREVIEW_PICTURE", "ACTIVE_FROM", "ACTIVE_TO", "TAGS");

// для архива событий на определенную дату
$dateBegin = new DateTime(sprintf('%1$04d-%2$02d-%3$02d 00:00:00', $_REQUEST['year'], $_REQUEST['month'], $_REQUEST['day']), new DateTimeZone('UTC'));

// для актуальных событий
$dateBegin = new DateTime(sprintf('%1$04d-%2$02d-%3$02d 00:00:00', date('Y'), date('m'), date('d')), new DateTimeZone('UTC'));

$arDayFilter = $arFilter;
$dateEnd = clone $dateBegin;
$dateBegin->modify('+1 day -1 second');
$arDayFilter['<=DATE_ACTIVE_FROM'] = $dateBegin->format($BITRIX_DATETIME_FORMAT);
$arDayFilter['>=DATE_ACTIVE_TO'] = $dateEnd->format($BITRIX_DATETIME_FORMAT);

$itemsListDay = CIBlockElement::GetList($arSort, $arDayFilter, false, false, $arSelect);

Быстрый выбор новостей в календарь за месяц из нескольких инфоблоков

Высокая скорость обеспечивается прямым обращением к базе данных. В качестве даты новости - поле «Начало активности».

$this-\>arParams['IBLOCK_ID'] - массив номеров инфоблоков

class BAEventsEngine {

  const MYSQL_DATETIME_FORMAT = 'Y-m-d H:i:s';

  public function getDates($year, $month) {
    global $DB;
    $dates = array();
    $monthDates = array();
    $this->calcStartEndDate($year, $month);
    $query = "SELECT EXTRACT(DAY FROM `ACTIVE_FROM`) AS `day`, `PREVIEW_PICTURE` as `pic`, `NAME` as `name`, `ACTIVE_TO` as `act_to`
              FROM `b_iblock_element`
              WHERE IBLOCK_ID IN (".implode(', ', (array)$this->arParams['IBLOCK_ID']).")
              AND ACTIVE = 'Y'
              AND (ACTIVE_FROM BETWEEN '".$this->dateBegin->format(self::MYSQL_DATETIME_FORMAT)."' AND '".$this->dateEnd->format(self::MYSQL_DATETIME_FORMAT)."')
              ORDER BY SORT";
    $CDatabaseRes = $DB->Query($query, false, '');
    if ($CDatabaseRes->SelectedRowsCount() > 0) {
      while ($result = $CDatabaseRes->Fetch()) {
        $day =& $dates[$result['day']];
        if(!isset($day)) $day = array();

        if(!isset($day['pic'])) {
          $day['pic'] = $result['pic'];
        }

        if($result['act_to'] == null) {
          $monthDates[100][] = $result;
        } else {
          $monthDates[$result['day']][] = $result;
        }

      }
      unset($day);
      ksort($monthDates);
    }
    return array($dates, $monthDates);
  }

  private $dateBegin, $dateEnd;

  private function calcStartEndDate($year, $month) {
    $this->dateBegin = new DateTime(sprintf('%1$04d-%2$02d-%3$02d 00:00:00', $year, $month, 1), new DateTimeZone('UTC'));
    $this->dateEnd = clone($this->dateBegin);
    $this->dateEnd->modify("+1 month -1 second");
  }

}

Если события в таком календаре имеют продолжительность, возможно узнать, активно ли событие активность в данный день. Перед $query вставить:

$dayActivites = $this->isDaysActiveCond($this->dateBegin);

Модифицировать $query:

$query = "SELECT EXTRACT(DAY FROM `ACTIVE_FROM`) AS `day`, `PREVIEW_PICTURE` as `pic`, `NAME` as `name`, `ACTIVE_TO` as `act_to`, {$dayActivites} ..."
private function isDaysActiveCond($date) {
  $cond = array();
  $numDaysInMonth = $date->format('t');

  $year = $date->format('Y');
  $month = $date->format('m');

  for($i = 1; $i <= $numDaysInMonth; $i++) {
    $day = sprintf('%1$02d', $i);
    $cond[] = "(`ACTIVE_FROM` <= '{$year}-{$month}-{$day} 23:59:59' AND `ACTIVE_TO` >= '{$year}-{$month}-{$day} 00:00:00') AS `day_{$i}`";
  }
  return implode(', ', $cond);

Таким образом в выборку попадут поля day_1, day_2, ..., содержащие признак активности события в этот день (1).