Работа с фильтрами сотрудника

Как мы уже обсудили, фильтр представляет собой сложную систему из полей, пресетов и выбранных значений, что на практике намного сложнее и неповоротливее чем нам бы хотелось. Если мы зайдем с технической стороны, то фильтр состоит из пресетов и все это складывается в так называемую конфигурацию. Конфигурация индивидуальна.

Конфигурация

Как любая индивидуальная настройка конфигурация фильтра хранится в таблице b_user_option со следующими параметрами:

  • CATEGORY = main.ui.filter (категория всегда фиксирована)
  • NAME = <filterId> (<filterId> - идентификатор фильтра)
  • VALUE = <data> (<data> - строка, сериализованная php-методом serialize для хранения конфигурации).

Из чего состоит конфигурация и что собственно сериализуется?

Структура состоящая из полей:

  • filters (array) - ассоциативный набор пресетов,
  • filter (string) - идентификатор текущего выбранного фильтра,
  • default (string) - идентификатор пресета назначенного по-умолчанию,
  • default_presets (array) - ассоциативный набор пресетов по-умолчанию (доступных в системе),
  • deleted_presets (array) - ассоциативный набор <presetId> => true в котором содержатся перечни исключенных пресетов (из перечня стандартных тех что были удалены).
  • use_pin_preset (bool) - один из флагов отвечающий за закрепление фильтра.

Пример десериализованной конфигурации:

[
	'use_pin_preset'  => true,
	'deleted_presets' => [],
	'default_presets' => [],
	'default' => 'filter_1678877570797',
	'filter'  => 'filter_1678877570797',
	'filters' => [
		'filter_in_work' => [
			'name'   => 'Лиды в работе',
			'fields' => [
				'TITLE'              => '',
				'STATUS_SEMANTIC_ID' => [
					0 => 'P',
				]
			],
			'filter_rows' => 'TITLE,STATUS_SEMANTIC_ID',
			'for_all'     => false,
			'sort'        => '0',
		],
		'filter_1678877570797' => [
			'name'   => 'asd',
			'fields' => [
				'TITLE'               => '',
				'OPPORTUNITY_from'    => '',
				'OPPORTUNITY_to'      => '',
				'OPPORTUNITY_numsel'  => 'exact',
				'DATE_CREATE_datesel' => 'NONE',
				'DATE_CREATE_from'    => '',
				'DATE_CREATE_to'      => '',
				'DATE_CREATE_days'    => '',
				'DATE_CREATE_month'   => '',
				'DATE_CREATE_quarter' => '',
				'DATE_CREATE_year'    => '',
			],
			'filter_rows' => 'TITLE,OPPORTUNITY,DATE_CREATE',
			'sort'        => '5',
			'for_all'     => false,
		],
		'tmp_filter' => [
			'name'   => 'Лиды в работе',
			'fields' => [
				'TITLE'               => '',
				'OPPORTUNITY_from'    => '',
				'OPPORTUNITY_to'      => '',
				'OPPORTUNITY_numsel'  => 'exact',
				'DATE_CREATE_datesel' => 'NONE',
				'DATE_CREATE_from'    => '',
				'DATE_CREATE_to'      => '',
				'DATE_CREATE_days'    => '',
				'DATE_CREATE_month'   => '',
				'DATE_CREATE_quarter' => '',
				'DATE_CREATE_year'    => '',
				'STATUS_SEMANTIC_ID'  => '',
			],
			'filter_rows' => 'TITLE,ASSIGNED_BY_ID,OPPORTUNITY,DATE_CREATE,STATUS_SEMANTIC_ID',
		],
		'default_filter' => [
			'name'        => 'Фильтр',
			'for_all'     => false,
			'sort'        => '4',
			'fields'      => [],
			'filter_rows' => 'TITLE,ASSIGNED_BY_ID,OPPORTUNITY,DATE_CREATE,STATUS_SEMANTIC_ID',
		]
	]
]

Немного отвлечемся и разберем конфигурацию одного пресета. В исходной структуре мы видим 4 пресета: filter_in_work, filter_1678877570797, tmp_filter, default_filter.

Пресеты default_filter и tmp_filter являются системными (один отвечает за фильтр по-умолчанию, а второй за временный, т.е. примененный фильтр). Пресет filter_in_work является изначально заложенным (предустановленными) пресетом. Пресет filter_1678877570797 - это индивидуальный пресет, созданный пользователем.

Рассмотрим структура пресета filter_1678877570797:

'filter_1678877570797' => [
	'name'   => 'asd',
	'fields' => [
		'TITLE'               => '',
		'OPPORTUNITY_from'    => '',
		'OPPORTUNITY_to'      => '',
		'OPPORTUNITY_numsel'  => 'exact',
		'DATE_CREATE_datesel' => 'NONE',
		'DATE_CREATE_from'    => '',
		'DATE_CREATE_to'      => '',
		'DATE_CREATE_days'    => '',
		'DATE_CREATE_month'   => '',
		'DATE_CREATE_quarter' => '',
		'DATE_CREATE_year'    => '',
	],
	'filter_rows' => 'TITLE,OPPORTUNITY,DATE_CREATE',
	'sort'        => '5',
	'for_all'     => false,
],

Он состоит из следующих ключей:

  • name (string) - отображаемое название пресета
  • fields (array) - набор полей
  • filter_rows (string) - перечисление через запятую полей который отображаются в фильтре
  • sort (int|string) - порядковый номер пресета для вывода
  • for_all (bool) - признак того что пресет добавлен не только для указанного сотрудника

Обращаю внимание на набор fields - это не просто набор полей фильтра, а набор подготовленных полей для вывода.

API

Вся работа с внешним отображением ограничивается классом Bitrix\Main\UI\Filter\Options, но при работе с ним нужно помнить что он использует глобальную переменную $USER и зависит от авторизации текущего пользователя, так что поработать с другими будет достаточно сложно.

Мы создаем экземпляр класса передавая ему начальные параметры:

  • $filterId (string) - идентификатор текущего фильтра
  • $filterPresets (array) - набор предустановленных фильтров
  • $commonPresetsId (null|string) - код ключа для хранения пресетов

Пример получения объекта Options:

use \Bitrix\Main\UI\Filter\Options;

$filterId = "MY_FILTER_ID";

$filterPresets = [
	'filter_in_work' => [
		'name'   => 'Лиды в работе',
		'fields' => [
			'TITLE'              => '',
			'STATUS_SEMANTIC_ID' => [
				0 => 'P',
			]
		],
		'filter_rows' => 'TITLE,STATUS_SEMANTIC_ID',
		'for_all'     => false,
		'sort'        => '0',
	],
];

$uiFilter = new Options(
	$filterId,
	$filterPresets
);

Полезные функции

Получив объект фильтра мы можем выполнять различные действия:

  1. Закрепить произвольный пресет:
$uiFilter->pinPreset($presetId = "default_filter");
  1. Получить список полей которые используются в текущем фильтр:
$usedFields = $uiFilter->getUsedFields();
  1. Получить идентификатор/код фильтра по-умолчанию или текущего выбранного
$defaultFilterId = $uiFilter->getDefaultFilterId();

$currentFilterId = $uiFilter->getCurrentFilterId();

Получение фильтра

Продемонстрированный в этой главе фильтр используется исключительно для демонстрационных целей или устаревших систем. Настоятельно рекомендуется использовать механизм описанный в разделе Свой фильтр.

Поскольку фильтр в своем отображении использует и значения указанные в фильтрации в ui-объекте так же есть методы, которые позволяют преобразовать фильтр из запроса в orm-подобный фильтр. Именно для таких целей и существуют два полезных метода - getFilter (для получения фильтра указанных полей) и getFilterLogic (преобразование результата getFilter в логические значения подготовленные для использования в DataManager).

Предположим что у нас имеется набор полей фильтра (подробнее в главе “Поля фильтра” из статьи Обзор) и мы хотим из запроса (REQUEST) получить подготовленный набор.

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

use Bitrix\Main\UI\Filter\FieldAdapter;
use Bitrix\Main\UI\Filter\DateType;
use Bitrix\Main\UI\Filter\Options;

$sourceFields = [
	[
		`id`       => 'STAGE',
		`name`     => "List field example",
		'type'     => FieldAdapter::LIST,
		'required' => false,
		'items'    => [
			"NEW"    => "Новый",
			"FINISH" => "Согласован",
		],
	],
	[
		`id`        => 'DATE',
		`name`      => "Date fields example",
		'type'      => FieldAdapter::DATE,
		'time'      => false,
		'valueType' => [
			DateType::MONTH,
		]
	]
];


$filterId = "MY_FILTER_ID";

$uiFilter = new Options($filterId);

$rawFilter = $uiFilter->getFilter($sourceFields);
/*
$rawFilter = [
	'DATE_datesel' => 'MONTH',
	'DATE_month'   => '3',
	'DATE_quarter' => 1,
	'DATE_year'    => '2023',
	'DATE_from'    => '01.03.2023 00:00:00',
	'DATE_to'      => '31.03.2023 23:59:59',
	'STAGE'        => [
		0 => "FINISH",
	],
	'STAGE_label'  => [
		0 => 'Согласован',
	],
	'PRESET_ID'      => 'tmp_filter',
	'FILTER_ID'      => 'tmp_filter',
	'FILTER_APPLIED' => true,
	'FIND'           => '',
];
*/


$logicFilter = $uiFilter->getFilterLogic($sourceFields);
/*
$logicFilter = [
	">=DATE" => "01.03.2023 00:00:00",
	"<=DATE" => "31.03.2023 23:59:59",
	"STAGE"  => [
		0 => "FINISH"
	]
];
*/