Полный цикл в digital

Новое универсальное API для CRM в Битрикс24

Вся кастомизация в новом API CRM построена на паттерне Service Locator (Сервис-локатор). В основу нового api заложены такие паттерны как: Factory Method (Фабричный Метод).

Container, Factory, Operation и Action: основные понятия

В новом API появилось много не совсем очевидных слов поэтому давайте разберемся в терминологии:

Container (Контейнер)

Точкой входа в весь механизм является контейнер, контейнер это singleton-класс который с одной стороны обеспечивает простой доступ к сервисам из локатора сервисов, с другой стороны является абстрактной фабрикой для фабрик по работе с сущностями.

Задача: обеспечить точку входа.

Factory (Фабрика)

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

Каждая фабрика является наследником абстрактного класса \Bitrix\Crm\Service\Factory который содержит универсальные методы, работающие на любом наследнике.

Паттерн: реализация паттерна Фабричный Метод (Factory Method).

Задача: фабрика создает объекты Operation (операции) для работы с элементами CRM.

Как работает: DinamicFactory наследуется от базового Service\Factory.

Нас интересует три главных метода фабрики, которые возвращают объекты операций:

  • getAddOperation()Operation\Add (создание)
  • getUpdateOperation()Operation\Update (обновление)
  • getDeleteOperation()Operation\Delete (удаление)
Operation (Операция)

Термин Operation (Операция) наиболее емко описывает необходимый набор действий для достижений конкретной цели.

Представим себе элемент crm, который мы хотим добавить. Добавление, это действие которое мы хотим совершить и запись в базу лишь часть действительно большого процесса — добавления элемента. Здесь мы можем попасть в логическую ловушку и подумать что процесс добавление элемента это и есть фактическое добавление записи в базу данных однако не стоит забывать что в нем так же участвуют обработчики событий, проверка прав, нормализация данных, поисковая индексация и другие важные этапы, в том числе запуск бизнес-процессов запускаемых при добавлении элемента.

Задача: инкапсулирует всю логику выполнения действия над элементом (добавление, изменение, удаление).

Как работает: каждая операция содержит массив Action, отдельные шаги с бизнес-логикой. Операция последовательно выполняет эти шаги.

Action (Действие)

Аналогом обработчиков событий в Битрикс, являются Action (Действие), специальный класс реализующий Bitrix\Crm\Service\Operation\Action, который добавляется к операции.

Задача: это отдельный, атомарный кусок бизнес-логики представляющий ваш кастомный код.

Как работает: вас должен интересовать один главный метод process(). Он принимает объект Bitrix\Crm\Item (элемент, над которым проводится операция) и возвращает Bitrix\Main\Result (успех или ошибку).

С какими сущностями работает новое API

Новый API полностью поддерживается для:

  1. Смарт-процессов
  2. Счетов
  3. Коммерческих предложений
  4. Документов (подсистема подписи)

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

  1. Лиды
  2. Сделки
  3. Контакты
  4. Компании

Как узнать включено или нет новое API

Проверить, включено ли новое API, можно только через php-код. Для проверки можно использовать специальные классы настроек:

use \Bitrix\Crm\Settings;
use \Bitrix\Main\Loader;

Loader::requireModule('crm');

var_dump([
    'deal' => Settings\DealSettings::getCurrent()->isFactoryEnabled(),
    'lead' => Settings\LeadSettings::getCurrent()->isFactoryEnabled(),
    'contact' => Settings\ContactSettings::getCurrent()->isFactoryEnabled(),
    'company => Settings\CompanySettings::getCurrent()->isFactoryEnabled(),
]);

А также можно использовать объекты сущностей:

use \Bitrix\Main\Loader;

Loader::requireModule('crm');

var_dump([
    'deal' => ( new \CCrmDeal() )->isUseOperation(),
    'lead' => ( new \CCrmLead() )->isUseOperation(),
    'contact' => ( new \CCrmContact() )->isUseOperation(),
    'company' => ( new \CCrmCompany() )->isUseOperation(),
]);

Пример результата когда использование выключено:

array(4) {
  ["deal"] => bool(false)
  ["lead"] => bool(false)
  ["contact"] => bool(false)
  ["company"] => bool(false)
}

Как включить/отключить новое API

Включить механизм можно двумя способами:

  1. Через код
  2. Через браузер

Включить через код

Для включения через код, необходимо задействовать механизм настроек, переводим флаг в положение true:

\Bitrix\Main\Loader::requireModule('crm');

\Bitrix\Crm\Settings\DealSettings::getCurrent()->setFactoryEnabled(true);

Аналогично можно сделать для других сущностей, достаточно вместо DealSettings использовать LeadSettings, ContactSettings и CompanySettings соответственно.

Включить через браузер

Для включения через браузер, достаточно перейти в раздел CRM и добавить параметр ?enableFactory=Y. Включить новую настройку может любой пользователь с правами на доступ в CRM.

Отключение

Отключение механизма производится аналогично подключению, с единственной разницей, необходимо передавать false в php-коде и N в адресной строке.

Service Locator

Вся кастомизация в новом API построена на паттерне Service Locator (Сервис-локатор).

Регистрация сервисов

Битрикс заранее регистрирует в системе все классы-фабрики для работы с сущностями CRM. Это происходит в конфигурационных файлах, например в bitrix/modules/crm/.settings.php:

'crm.service.factory.contact' => [
    'className' => '\\Bitrix\\Crm\\Service\\Factory\\Contact',
],

Получение сервиса

В коде любой модуль или компонент может получить стандартную фабрику для работы с контактами, получаем стандартную фабрику контактов через Service Locator:

$factory = \Bitrix\Main\DI\ServiceLocator::getInstance()
->get('crm.service.factory.contact');

Подмена сервиса с целью кастомизации

Чтобы добавить свою логику, мы создаем собственный класс, наследуем его от стандартного и переопределяем нужные методы:

// наш кастомный класс фабрики
class ContactFactory extends \Bitrix\Crm\Service\Factory\Contact
{
    // здесь мы переопределим методы, например, для добавления обработчиков
}

Затем мы подменяем стандартный сервис в локаторе на наш:

// подменяем стандартную фабрику своей
\Bitrix\Main\DI\ServiceLocator::getInstance()->addInstance('crm.service.factory.contact', new ContactFactory());

Результат подмены

Теперь любой код в системе, который запрашивает фабрику контактов, будет получать наш экземпляр ContactFactory. Таким образом, наша логика будет выполняться везде, где используется фабрика:

$factory = \Bitrix\Main\DI\ServiceLocator::getInstance()->get('crm.service.factory.contact');

Упрощенное получение через Container

Для удобства, разработчики Битрикс24 добавили класс-обертку Container. Стандартный способ получить фабрику через Container, после подмены он тоже вернет наш кастомный класс:

$factory = \Bitrix\Crm\Service\Container::getInstance()->getFactory(\CCrmOwnerType::Contact);

Как добавить свой обработчик

Вся магия происходит внутри методов фабрики. Вы переопределяете нужный метод, например для обновления getUpdateOperation, получаете стандартную операцию и добавляете в нее свой Action:

// переопределяем метод получения операции обновления
public function getUpdateOperation(Item $item, Context $context = null): Operation\Update
{
    // 1. получаем стандартную операцию обновления
    $operation = parent::getUpdateOperation($item, $context);

    // 2. добавляем свой Action на этап "ПЕРЕД сохранением"
    $operation->addAction(
        Operation::ACTION_BEFORE_SAVE,
        new ExampleAction()
    );

    return $operation;
}

Метод addAction() принимает два ключевых параметра:

  • Operation::ACTION_BEFORE_SAVE до сохранения элемента в БД
  • Operation::ACTION_AFTER_SAVE после успешного сохранения элемента в БД

Чтобы добавить свою логику, вам нужно:

  1. В фабрике переопределить getAddOperation для кастомизации при создании, getUpdateOperation для кастомизации при обновлении, getDeleteOperation для кастомизации при удалении
  2. Добавить свой Action в нужную операцию с помощью addAction(), указав этап выполнения ACTION_BEFORE_SAVE или ACTION_AFTER_SAVE

Как добавить свой обработчик в смарт-процесс

Разберем на примере смарт-процесса под названием Рекламации с идентификатором 1036:

Создаем кастомную фабрику

Создаем класс-фабрику, которая будет возвращать операцию с вашим обработчиком:

/local/modules/hmarketing.rest/lib/Crm/OrderFactory.php<?php

namespace Hmarketing\Rest\Crm;

use Bitrix\Crm\Service\Factory\Dynamic;
use Bitrix\Crm\Service\Context;
use Bitrix\Crm\Service\Operation;
use Bitrix\Crm\Model\Dynamic\TypeTable;
use Bitrix\Crm\Item;

class OrderFactory extends Dynamic
{
    // идентификатор типа смарт-процесса
    protected int $entityTypeId = 1036;

    public function __construct()
    {
        $type = TypeTable::getByEntityTypeId($this->entityTypeId)->fetchObject();

        if (!is_null($type))
        {
            parent::__construct($type);
        }
        else
        {
            // важно, обработайте случай, если тип не найден
            throw new \Exception("Smart process type with ID {$entityTypeId} not found.");
        }
    }

    // переопределяем метод получения операции обновления
    public function getUpdateOperation(Item $item, Context $context = null): Operation\Update
    {
        // 1. получаем стандартную операцию обновления
        $operation = parent::getUpdateOperation($item, $context);

        // 2. добавляем свой Action на этап "ПЕРЕД сохранением"
        $operation->addAction(
            Operation::ACTION_BEFORE_SAVE,
            new \Hmarketing\Rest\Action\OrderAction()
        );

        return $operation;
    }
}
Подменяем фабрику в сервис-локаторе

Регистрируем нашу фабрику вместо стандартной. Это нужно сделать в точке входа, например в init.php. Ключ сервиса формируется по шаблону crm.service.factory.dynamic.{ID_СУЩНОСТИ}:

/local/php_interface/init.php<?php

// подключаем модуль crm
\Bitrix\Main\Loader::includeModule('crm');

// подключаем кастомный модуль где лежат все файлы hmarketing.rest
\Bitrix\Main\Loader::includeModule('hmarketing.rest');


// подменяем фабрику для конкретного смарт-процесса (ID=1036)
\Bitrix\Main\DI\ServiceLocator::getInstance()->addInstance(
          // ключ для динамического типа
    'crm.service.factory.dynamic.1036',
    new Hmarketing\Rest\Crm\OrderFactory()
);
Создаем Action с бизнес-логикой

Создаем класс, в котором будет реализована ваша логика обработки:

/local/modules/hmarketing.rest/lib/Action/OrderAction.php<?php

namespace Hmarketing\Rest\Action;

use Bitrix\Main\Result;
use Bitrix\Main\Error;
use Bitrix\Crm\Item;
use Bitrix\Crm\Service\Operation\Action;

// ваш кастомный класс фабрики
class OrderAction extends Action
{
    public function process(Item $item): Result
    {
        $result = new Result();

        // для примера, проверим поле сумма
        if ($item->get('UF_CRM_3_1766692211591') < 0)
        {
            $result->addError(new Error("Сумма заказа не может быть отрицательной!"));
        }

        // возвращаем результат операции
        return $result;
    }
}

Создаем свойство типа число:

Если введем отрицательное число, получим ошибку:

Заполните форму уже сегодня!
Для начала сотрудничества необходимо заполнить заявку или заказать обратный звонок. В ответ получите коммерческое предложение, которое будет содержать индивидуальную стратегию с учетом требований и поставленных задач
Работаем по будням с 9:00 до 18:00. Заявки, отправленные в выходные, обрабатываем в первый рабочий день до 12:00.
Спасибо, ваш запрос принят и будет обработан!