Сортировка задач Bitrix24 по номеру в названии

Рассмотрим решение задачи, с которой ко мне обратился читатель блога. Суть проблемы заключается в том, что нужно реализовать сортировку задач в списке «особым способом» — учитывая индексный номер в названии. Давайте попробуем сделать это не затрагивая ядро и компоненты Bitrix24.

Задача

Имеется список задач вида «Индекс. Название задачи», причём индекс может отсутствовать. Требуется при сортировке по названию сперва отобразить все задачи, упорядоченные по индексу, потом все остальные.

Штатная сортировка задач.
Требуемый результат.

Шаг 1. Концепция

Изучим каким образом происходит получение элементов в списке задач и расширим функционал, подставив своё условие сортировки (подробнее о кастомизации Bitrix24 написано в этой статье).

Шаг 2. Поиск удобного метода

Начнём исследование со стандартного компонента отображения списка задач bitrix:tasks.task.list (bitrix/components/bitrix/tasks.task.list).

Проанализировав код класса компонента делаем вывод, что данные по задачам получаются в методе класса getData() через менеджер задач:

/**
 * file: bitrix/components/bitrix/tasks.task.list/class.php
 */
...
  
use Bitrix\Tasks\Manager;

...

class TasksTaskListComponent extends TasksBaseComponent
	implements \Bitrix\Main\Errorable, \Bitrix\Main\Engine\Contract\Controllerable
{
    ...

    protected function getData()
    {
        ...


        $mgrResult = Manager\Task::getList(User::getId(), $getListParameters, $parameters);

        ...
    }

    ...
}

Поскольку в этом месте мы никак не сможем расширить поведение системы, пойдём вглубь и посмотрим метод Bitrix\Tasks\Manager::getList (bitrix/modules/tasks/lib/manager/task.php)

В данном методе нас тоже ничего не интересует. Здесь происходит подготовка и обработка параметров, которые передаются далее в \CTaskItem::fetchListArray()

/**
 * file: bitrix/modules/tasks/lib/manager/task.php
 */

...
  
public static function getList($userId, array $listParameters = [], array $parameters = [])
{
  	
  ...
    
  [$items, $res] = \CTaskItem::fetchListArray(
	$userId,
	($listParameters['order'] ?? null),
	$listParameters['legacyFilter'] ?? null,
	$params,
	$listParameters['select']
  );
  
  ...
}

...

Идём дальше.

\CTaskItem::fetchListArray() отправляет нас в \CTasks::getList(), а тот в свою очередь получает экземпляр провайдера и выполняет получение данных:

/**
 * file: bitrix/modules/tasks/classes/general/task.php
 */

...
  
public static function GetList($arOrder = [], $arFilter = [], $arSelect = [], $arParams = [], array $arGroup = [])
{
	global $DB, $USER_FIELD_MANAGER;

	$provider = new \Bitrix\Tasks\Provider\TaskProvider($DB, $USER_FIELD_MANAGER);
	return $provider->getList($arOrder, $arFilter, $arSelect, $arParams, $arGroup);
}

...

Изучив класс провадйера \Bitrix\Tasks\Provider\TaskProvider (bitrix/modules/tasks/lib/provider/taskprovider.php), становится ясно, каким образом формируется сортировка данных при составлении SQL запроса. Метод makeArSqlOrder() проверяет наличие штатных ключей и параметров с префиксом uf_ (пользовательские свойства) и формирует массив sql команд, которые будут подставлены в общий запрос.

Так как оперируя значениями входящих параметров картину мы не поменяем, будем расширять этот метод.

Шаг 3. Переопределение метода

Пришло время приступить к расширению существующего ыункционала под требуемую задачу.

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

Определим конфигурацию перегрузки следующим образом:

/**
 * file: local/php_interface/override.php
 */

/**
 * Директория с размещёнными классами для перегрузки
 */
$classDirectoryPath = __DIR__ . '/classes/Aclips/Override';

/**
 * Конфигуратор переопределяемых классов
 */
$config = [
    'Bitrix\Tasks\Provider\TaskProvider' => [
        'classPath' => __DIR__ . '/../../bitrix/modules/tasks/lib/provider/taskprovider.php',
        'overrideClass' => 'TaskProvider',
        'replace' => [
            'private function makeArSqlOrder' => 'protected function makeArSqlOrder',
            "private\n" => 'protected '
        ]
    ]
];

...

Блок replace содержит 2 записи. Это нужно для организации доступа к методу и свойствам класса (изначально они приватные).

Класс перегрузки

Создадим класс и расширим метод makeArSqlOrder() таким образом, чтобы сперва добавлялось наше собственное правило сортировки, а затем все остальные.

<?php

 /**
 * file: local/php_interface/classes/Aclips/Override/TaskProvider.php
 */
  
namespace Aclips\Override;


class TaskProvider extends \Bitrix\Tasks\Provider\TaskProvider
{
    protected function makeArSqlOrder(): self
    {
        if(!empty($this->arOrder['TITLE'])) {
            $this->arSqlOrder[] = " IF(CAST(T.TITLE AS UNSIGNED) > 0, CAST(T.TITLE AS UNSIGNED), 1000000000) ".$this->arOrder['TITLE']." ";
        }

        parent::makeArSqlOrder();

        return $this;
    }

}

Данный запрос приведет поле TITLE к числовому значению. Если индекс не указан, то подставим какое-нибудь большое число, таким образом задачи без индекса будут попадать в конец списка (или в начало, в зависимости от сортировки). При сортировке по этому полю получим результат, который требуется по условию задачи.

IF(CAST(T.TITLE AS UNSIGNED) > 0, CAST(T.TITLE AS UNSIGNED), 1000000000)
Результат выполнения запроса.

Итог

Используемый sql запрос не претендует на самый оптимальный вариант сортировки задач и легко может быть оптимизирован и заменён на более производительный. Основной смысл статьи заключается в подходе к анализу работы системы и использованию уже имеющихся знаний. Разумеется, что данный метод пригоден не только для сортировки  и может быть применён для реализации прочих решений связанных с модификацией Bitrix24.

Добавить комментарий