Блог Вадима Гареева

Собственный провайдер данных для Диалоговых окон в Битрикс24

14.06.2024

В этой статье мы разберём, как создать собственный провайдер данных для UI Entity Selector в Битрикс24 и подключить его к штатному компоненту без использования REST API. Это позволит выводить нужные вам сущности — например, компании-партнёры из CRM — прямо в диалоговых окнах.

1. Структура модуля

Для начала создадим модуль в папке /local/modules/custom.provider. Организуем внутри следующую файловую структуру:

custom.provider/
├── install/
│   └── index.php
├── lib/
│   └── Provider/
│       └── PartnersCompanyProvider.php
├── .settings.php
└── include.php
    

2. Файл install/index.php

В файле install/index.php опишем регистрацию и удаление модуля:

<?php
if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED !== true) {
    die();
}

class custom_provider extends CModule
{
    public $MODULE_ID = 'custom.provider';
    public $MODULE_VERSION;
    public $MODULE_VERSION_DATE;
    public $MODULE_NAME;
    public $MODULE_DESCRIPTION;

    public function __construct()
    {
        $this->MODULE_NAME = 'Кастомный провайдер данных';
        $this->MODULE_DESCRIPTION = 'Модуль для регистрации кастомного провайдера UI Entity Selector';
    }

    public function DoInstall()
    {
        RegisterModule($this->MODULE_ID);
        return true;
    }

    public function DoUninstall()
    {
        UnRegisterModule($this->MODULE_ID);
        return true;
    }
}

3. Провайдер PartnersCompanyProvider

В классе lib/Provider/PartnersCompanyProvider.php реализуем логику выборки компаний-партнёров из CRM:

<?php

namespace Provider;

use Bitrix\Main\Loader;
use Bitrix\UI\EntitySelector\BaseProvider;
use Bitrix\UI\EntitySelector\Dialog;
use Bitrix\UI\EntitySelector\Item;
use Bitrix\UI\EntitySelector\SearchQuery;
use Bitrix\Crm\CompanyTable;
use Bitrix\UI\EntitySelector\Tab;
use Exception;

/**
 * Class PartnersCompanyProvider
 *
 * Провайдер для выбора компаний-партнёров из CRM.
 *
 * @package Provider
 */
class PartnersCompanyProvider extends BaseProvider
{
    /**
     * Лимит выборки компаний.
     */
    protected const LIMIT = 20;

    /**
     * Конструктор класса.
     *
     * @param array $options Опции для провайдера (например, onlyWithEmail и filter).
     */
    public function __construct(array $options = [])
    {
        $this->options = $options;
        parent::__construct();
    }

    /**
     * Проверяет доступность провайдера.
     *
     * @return bool Возвращает true, если модуль crm подключен.
     */
    public function isAvailable(): bool
    {
        return Loader::includeModule('crm');
    }

    /**
     * Возвращает фильтр по наличию EMAIL.
     *
     * Если передана опция onlyWithEmail, возвращает фильтр, исключающий компании без EMAIL.
     *
     * @return array Массив фильтров.
     */
    protected function getEmailFilter(): array
    {
        return ($this->options['onlyWithEmail'] ?? false)
            ? ['!EMAIL' => '']
            : [];
    }

    /**
     * Возвращает дополнительный фильтр, переданный в опциях.
     *
     * Например, 'filter' => ['UF_CRM_1739464513474' => 'Булочки'].
     *
     * @return array Массив дополнительных фильтров.
     */
    protected function getAdditionalFilter(): array
    {
        return $this->options['filter'] ?? [];
    }

    /**
     * Выполняет запрос к базе для получения списка компаний по заданному фильтру.
     *
     * @param array $filter Фильтр для запроса.
     * @param array $order Порядок сортировки.
     * @param int   $limit Лимит выборки.
     * @return array Список компаний.
     */
    protected function fetchCompanies(array $filter, array $order = [], int $limit = self::LIMIT): array
    {
        try {
            return CompanyTable::getList([
                'filter' => $filter,
                'select' => ['ID', 'TITLE', 'EMAIL'],
                'limit'  => $limit,
                'order'  => $order
            ])->fetchAll();
        } catch (Exception $e) {
            // При необходимости можно добавить логирование ошибки
            return [];
        }
    }

    /**
     * Получает элементы по заданным идентификаторам.
     *
     * @param array $ids Список идентификаторов компаний.
     * @return array Массив объектов Item.
     */
    public function getItems(array $ids): array
    {
        $filter = array_merge(
            ['ID' => $ids],
            $this->getEmailFilter(),
            $this->getAdditionalFilter()
        );

        $companies = $this->fetchCompanies($filter);

        $items = [];
        foreach ($companies as $company) {
            $items[] = $this->makeItem($company);
        }
        return $items;
    }

    /**
     * Заполняет диалог выбора компаний.
     *
     * Добавляет вкладку и элементы компаний в диалог.
     *
     * @param Dialog $dialog Экземпляр диалога выбора.
     * @return void
     */
    public function fillDialog(Dialog $dialog): void
    {
        $dialog->addTab(new Tab([
            'id'    => 'custom_company',
            'title' => 'Партнеры',
            'stub'  => true,
        ]));

        $filter = array_merge(
            $this->getEmailFilter(),
            $this->getAdditionalFilter()
        );

        // Запрашиваем компании с сортировкой по ID в обратном порядке
        $companies = $this->fetchCompanies($filter, ['ID' => 'DESC']);

        foreach ($companies as $company) {
            $dialog->addItem($this->makeItem($company));
        }
    }

    /**
     * Возвращает выбранные элементы по их идентификаторам.
     *
     * @param array $ids Список идентификаторов.
     * @return array Массив объектов Item.
     */
    public function getSelectedItems(array $ids): array
    {
        return $this->getItems($ids);
    }

    /**
     * Выполняет поиск компаний и заполняет диалог результатами.
     *
     * Добавляет дефолтные элементы и найденные элементы в диалог.
     *
     * @param SearchQuery $searchQuery Объект запроса поиска.
     * @param Dialog $dialog Экземпляр диалога выбора.
     * @return void
     */
    public function doSearch(SearchQuery $searchQuery, Dialog $dialog): void
    {
        // Добавляем дефолтные элементы
        $this->fillDialog($dialog);

        $filter = array_merge(
            ['%TITLE' => $searchQuery->getQuery()],
            $this->getEmailFilter(),
            $this->getAdditionalFilter()
        );

        $companies = $this->fetchCompanies($filter);

        foreach ($companies as $company) {
            $dialog->addItem($this->makeItem($company));
        }
    }

    /**
     * Создает объект Item для заданной компании.
     *
     * @param array $company Массив данных компании.
     * @return Item Объект Item для диалога.
     */
    protected function makeItem(array $company): Item
    {
        $companyId = (int)$company['ID'];

        return new Item([
            'id'         => $companyId,
            'entityId'   => 'company',
            'title'      => $company['TITLE'],
            'subtitle'   => $company['EMAIL'] ?? '',
            'linkTitle'  => 'Подробнее',
            'customData' => [
                'id'         => (string)$companyId,
                'entityInfo' => $this->createEntityInfo($companyId, $company),
                'email'      => $company['EMAIL'] ?? '',
                'entityId'   => $companyId,
                'entityType' => 'company',
                'name'       => $company['TITLE'],
            ],
            'tagOptions' => [
                'title' => $company['TITLE'] . ' (' . ($company['EMAIL'] ?? '') . ')'
            ],
            'tabs' => [
                'custom_company',
                'mail_crm_recipient',
            ],
        ]);
    }

    /**
     * Создает массив данных для entityInfo.
     *
     * @param int $companyId Идентификатор компании.
     * @param array $company Массив данных компании.
     * @return array Массив с информацией о компании.
     */
    protected function createEntityInfo(int $companyId, array $company): array
    {
        return [
            'id'            => $companyId,
            'type'          => 'company',
            'typeName'      => 'COMPANY',
            'typeNameTitle' => 'Компания',
            'place'         => 'company',
            'hidden'        => false,
            'title'         => $company['TITLE'],
            'url'           => "/crm/company/details/{$companyId}/",
            'image'         => "/bitrix/images/crm/entity_provider_icons/company.svg",
            'permissions'   => [
                'canUpdate' => true,
            ],
        ];
    }
}

4. Регистрация провайдера

Добавим файл .settings.php для подключения нашего провайдера к UI Entity Selector:

<?php
return [
    'ui.entity-selector' => [
        'value' => [
            'entities' => [
                [
                    'entityId' => 'custom_company',
                    'provider' => [
                        'moduleId'  => 'custom.provider',
                        'className' => '\\Provider\\PartnersCompanyProvider'
                    ],
                ],
            ],
        ],
    ],
];

Инициализируем автозагрузку в include.php:

<?php
\Bitrix\Main\Loader::registerAutoLoadClasses(
    'custom.provider',
    [
        '\\Provider\\PartnersCompanyProvider' => 'lib/Provider/PartnersCompanyProvider.php',
    ]
);

5. Внедрение в компонент

Чтобы использовать провайдер в форме отправки e-mail, добавим в ваш шаблон скрипт /local/templates/components/bitrix/main.mail.form/templates/.default/script.js:

entitiesDialog.push({
    id: 'custom_company',
    dynamicLoad: true,
    dynamicSearch: true,
    options: {
        onlyWithEmail: true,
        filter: {
            'UF_CRM_1739464513474': 'Булочки'
        }
    }
});

Теперь при открытии диалога отправки письма появится вкладка «Партнёры», где будут показаны только те компании, у которых заполнён Email и задано пользовательское поле UF_CRM_1739464513474 со значением «Булочки».

Вкладка Партнёры в диалоге отправки E-mail

Категории: Битрикс

Комментарии (0)

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