Разрабатываем web-проекты с 2016 года
Назад в блог
Bitrix24
Инструменты для упрощения разработки в Битрикс24
Вадим
PHP-разработчик
Инструменты для упрощения разработки в Битрикс24

В Битрикс24 все чаще используются смарт-процессы в решении многих задач. Чтобы кастомизировать логику работы со смарт-процессами нужно реализовать подмену сервис-контейнера смарт-процессов. Для этого и разработана данная библиотека.

Установка

Библиотека b24/devtools устанавливается через composer:

composer require b24/devtools

Подмена Сервис контейнера

В Битриксе при определенных действиях происходят различные события на элементах смарт-процесса. Чтобы как-то изменить поведение смарт-процесса при определенном событии и существует подмена сервис контейнера. Для реализации подмены необходимо получить символьный код элементах смарт-процесса. Его можно найти в таблице b_crm_dynamic_type.

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

Этот код можно разместить в init.php:

Use B24\Devtools\Crm\Replacement\Container;
new Container([
    TEST' => FactoryTest::class
);

Фабрика смарт-процесса должна в свою очередь наследоваться от
Bitrix\Crm\Service\Factory\Dynamic
Пример фабрики для смарт-процесса TEST:

use Bitrix\Crm\Service\Context;
use Bitrix\Crm\Service\Operation;
use Bitrix\Crm\Service\Factory\Dynamic;
class FactoryTest extends Dynamic
{
  public function getAddOperation(Item $item, Context $context = null): Operation\Add
  {
   $operation = parent::getAddOperation($item, $context);

  $operation->addAction(
   Operation::ACTION_BEFORE_SAVE,
   new AddHandler()
  );
   return $operation;
  }
}

AddHandler - это обработчик события на добавление, который вызовется ДО добавления элемента в базу. Этот обработчик должен наследовать Bitrix\Crm\Service\Operation\Action

use B24\Devtools\Crm\ResultOperationTrait;
use Bitrix\Crm\Service\Operation;
use Bitrix\Crm\Item;
class AddHandler extends Operation\Action
{
  use ResultOperationTrait;

  public function process(Item $item): Result
{
   return $this
    ->error('Ошибка 1')
    ->error('Ошибка 2')
    ->result();
  }
}

В этом обработчике используется trait ResultOperationTrait. В него завёрнут ООП подход для работы с Result, который должен вернуть обработчик.

error() – записывает ошибку в Result и возвращает самого себя же.

result() – возвращает результат.

Таким образом можно собрать несколько ошибок и отдать пользователю

Упрощённая работа с сущностью смарт-процесса

В Битрикс 24 довольно много придётся писать код, чтобы получить ID смарт-процесса и собрать для него фабрику. Что предлагает Битрикс24:

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

То есть нужно заранее знать ID смарт-процесса, а это не очень хорошо, т.к. смарт-процесс может добавляться через миграцию, а также на разных площадках (тестовая, продакшен) могут отличаться ID смарт-процессов. Чтобы этого избежать нужно использовать код смарт-процесса:

$smart = new B24\Devtools\Crm\Smart\SmartProcess('SYMBOL_CODE');
$factory = $smart->getFactory();

Кроме того, в конструктор класса SmartProcess можно передать и значение типа сущности, например \CCrmOwnerType::Deal. Поэтому можно делать даже так:

$smart = new B24\Devtools\Crm\Smart\SmartProcess(\CCrmOwnerType::Deal);

Так же у этого класса есть различные методы:

$smart->getFactory(); // Вернёт фабрику сущности
$smart->getFactoryId(); // ID сущности
$smart->getEntityName(); // Название объекта сущности, CRM_2 (например)
$smart->compileClass(); // Отдаст неймспейс класса ORM смарт-процесса
$smart->getContainer(); // Отсюда же можно вытащить сервис контейнер
$smart->getRelationManager(); // RelationManager

Метод $smart->getEntityName() пригодится для написания миграций для смарт-процесса
Отрывок из миграции:

$pvz = new SmartProcess('PVZ');
$name = $pvz->getEntityName();
$helper = $this->getHelperManager();
$helper->UserTypeEntity()->saveUserTypeEntity([
   'ENTITY_ID' => $ name,
   ………
]);

Работа со связями crm

В Битрикс 24 есть различные связи между сущностями crm (см. таблицу b_crm_entity_relation). В библиотеке реализованы различные методы для работы со связями.

// Чтение связей // $children = \B24\Devtools\Crm\Relation\Manager::searchChildren(\CCrmOwnerType::Quote, 1)
   ->getAll(); // Массив из Bitrix\Crm\ItemIdentifier всех привязанных детей к Предложению.

В Bitrix\Crm\ItemIdentifier хранится ID элемента и EntiryTypeIdсмарт-процесса

$children = \B24\Devtools\Crm\Relation\Manager::searchChildren(\CCrmOwnerType::Quote, 1)
   ->withEntityTypeId(152); // Массив ID всех привязанных детей-элементов смартпроцесса с ID 152

$children = \B24\Devtools\Crm\Relation\Manager::searchChildren(\CCrmOwnerType::Quote, 1)
   ->withOne(function (\Bitrix\Crm\ItemIdentifier $identifier) {
    return $identifier; // если вернуть просто $identifier, тогда Вернёт массив из ItemIdentifier
    return $identifier->getEntityId(); // Вернёт массив из ID элементов в ItemIdentifier
  });
// Если заменить метод searchChildren на searchParents, то будут искаться родители //

// Обновление связей //
\B24\Devtools\Crm\Relation\Manager::update(\CCrmOwnerType::Quote, 1, 152, 1)
   ->isParent() // Например если в связи надо отвязать родителя (Предложения) и привязать к другому Предложению
   ->on(\CCrmOwnerType::Quote, 2) // Привязываем к Предложению с ID = 2
   ->replace(); // Замена

// Удаление какой-то одной связи //
\B24\Devtools\Crm\Relation\Manager::deleteOne(
  parentEntityTypeId: \CCrmOwnerType::Quote,
  parentEntityId: 1,
  childEntityTypeId: 152,
  childEntityId: 1
);
// У Предложения с ID = 1 удаляем все связи со смарт-процессом, у которого ID = 152
\B24\Devtools\Crm\Relation\Manager::deleteWithType(
  parentEntityTypeId: \CCrmOwnerType::Quote,
  parentEntityId: 1,
  childEntityTypeId: 152,
);

// Создание связей
// Создаст у Предложения с ID = 1 связь (ребёнка) со смарт-процессом с ID = 152 //
\B24\Devtools\Crm\Relation\Manager::create(
  parentEntityTypeId: \CCrmOwnerType::Quote,
  parentEntityId: 1,
  childEntityTypeId: 152,
  childEntityId: 1
);

Работа с денежными полями

Очень часто приходится работать с денежными сущностями в Битрикс24. Например, чтобы избежать такого кода:

$moneyField = '155|USD';
$rateToRub = 93.22;


[$price, $currency] = explode('|', $moneyField);
$newPrice = round($price * $rateToRub, 2);
$newMoneyField = $newPrice . '|RUB';
echo $newMoneyField; // 14449.1|RUB

Был придуман B24\Devtools\Data\MoneyField хэлпер для работы с денежными единицами. Код превращается в:

$moneyField = '155|USD';
$rateToRub = 93.22;
$money = B24\Devtools\Data\MoneyField::parse($moneyField)
   ->math(function (&$price) use ($rateToRub) {
    $price = $price * $rateToRub;
   })
   ->setCurrency('RUB')
   ->round(2);
echo (string) $money; // 14449.1|RUB

Работа с Пользовательскими полями для смарт-процессов и сущностей CRM

У смарт-процессов могут быть пользовательские поля с типом «Список», у этого списка есть поля ID, XML_ID, Значение
И чтобы задать смарт-процессу какое-то значение списка в это поле нужно передавать ID этого значения.
Хелпер B24\Devtools\Data\UserField упрощает получение значения пользовательских полей:

$smart = new B24\Devtools\Crm\Smart\SmartProcess('TEST');
$userField = new \B24\Devtools\Data\UserField($smart);
$userField->getUserFields('UF_TYPE_LIST');
$idPickup = $userField->getUserFieldsValue([
   'XML_ID' => 'PICKUP_PVZ'
])['ID'];


// getUserFields - Загружает пользовательское поле, эта функция бросает исключение если не нашлось такое поле у сущности
// и возвращает массив с данными этого пользовательского поля
// getUserFieldsValue - Вернёт массив значений по фильтру
// Кроме того, если задать объекту UserField $userField->isOne = false тогда вернутся несколько объектов по фильтру

Так же этот хелпер может учитывать не только сущности смарт-процессов, но и другие сущности CRM, например сделок:

$userField = new \B24\Devtools\Data\UserField('CRM_DEAL');

Преобразование массивов и строк в camelCase и обратно

В Битриксе есть стандарт написания ключей в массиве - SNAKE_KEYS, но иногда нужно чтобы ключи были в более привычном виде camelCase.
В пакете есть 2 хелпера, которые преобразуют ключи массивов или строк в camelCase или SNAKE_KEYS.
Например есть массив данных:

$arr = [
   'CONTACTS' => [
    [
      'NAME' => 'Имя',
      'LAST_NAME' => 'Фамилия'
    ]
   ]
];

Вызываем хелпер \B24\Devtools\Data\ArrayHelper с методом arrayToCamelCase

$arr = \B24\Devtools\Data\ArrayHelper::arrayToCamelCase($arr);

Массив превратится в такой:

Array
(
  [contacts] => Array
   (
    [0] => Array
     (
      [name] => Имя
      [lastName] => Фамилия
     )
   )
)

Чтобы вернуть обратно массив с прежними ключами, нужно вызвать метод arrayToUnderScore

$arr = \B24\Devtools\Data\ArrayHelper::arrayToUnderScore($arr);

Этот Хелпер работает вокруг хелпера \B24\Devtools\Data\StringHelper, который тоже самое делает со строками.

$string = 'LAST_NAME';
$string = \B24\Devtools\Data\StringHelper::stringToCamelCase($string); // lastName
$string = ' lastName ';
$string = \B24\Devtools\Data\StringHelper::stringToCamelCase($string); // LAST_NAME

Работа с инфоблоком через символьный код API

Битрикс позволяет работать с инфоблоком как с сущностью ORM D7. Если у инфоблока задан символьный код API, например, REGION, то его ORM класс будет называться:

\Bitrix\Iblock\Elements\Element\RegionTable

В библиотеке реализован хелпер, который соберёт сам название класса:

$iblock = new \B24\Devtools\Data\Iblock('REGION');
$class = $iblock->getClassName(); //Bitrix\Iblock\Elements\Element\RegionTable

И далее можно с ним работать как с ORM:

$class::getList();

Можно ещё короче:

$res = \B24\Devtools\Data\Iblock::generateClassName('REGION')::getList()->fetch();

Ссылка на репозиторий с данным пакетом
Поделитесь

Напишите нам. Мы поможем решить вашу задачу