Сегодня у нас де-факто существует 2 версии API: старый CCrm* с событиями и новый подход с фабрикой и операциями. Я не являюсь сторонником старого подхода, однако в новом подходе партнеры разрабатывающие тиражные приложения получили существенный недостаток - их лишили возможности динамически подписываться на события.
В новом подходе архитектурно у нас нет событий в их текущем виде и для того чтобы добавить свое поведение в какой-нибудь СП необходимо перекрывать фабрику этого СП и добавлять Action, что в свою очередь пораждает проблемы в тиражном приложении: невозможно поставить 2 тиражных приложения каждое из которых перекрывает одну и ту же фабрику.
Предлагаю свое компромисное решение: мы будем использовать событие, которое будет добавлять Action's к Operation.
Мне совершенно не нравится идея когда мы будем тащить события для обратной совместимости как это было со сделками, однако сам механизм событий показал себя вполне хорошо.
Предлагаю расширить методы получения операций `get*Operation` в `Bitrix\Crm\Service\Factory` чем-то вроде такого:
public function get*Operation(Item $item, Context $context = null): Operation\Add
{
$operation = new Operation\Add($item, $this->getOperationSettings($context), $this->getFieldsCollection());
// ....
$event = new Event("crm", "OnBuildOperation", [
'factory' => $this,
'actionClass' => get_class($operation)
]);
$event->send();
if ($event->getResults())
{
foreach($event->getResults() as $eventResult)
{
if ( $eventResult->getResultType() !== EventResult::SUCCESS )
{
continue;
}
$eventParameters = $eventResult->getParameters();
if ( isset($eventParameters['ACTIONS']) && is_array($eventParameters['ACTIONS']) )
{
foreach ($eventParameters['ACTIONS'] as $action)
{
if ( !is_object($action['OBJ']) || !($action['OBJ'] instanceof Operation\Action) )
{
continue;
}
$operation->addAction(
$action['PLACE'],
$action['OBJECT'],
);
}
}
}
}
return $operation;
} |
Таким образом можно получить следующие преимущества:
1. Возможность динамического расширения операций без кастомизации фабрик.
2. Как следствие пункта 1 - возможность использовать это в модулях для маркетплейса
Почему в `OnBuildOperation` не передается операция?
Сама операция является высокоуровневым действием и ее настройки, а так же опции должны конфигурироваться вызываемой строной, в то время как прямая передача ее в событие позволит значительно менять поведение системы, что сильно усложнит отладку.
К тому же - событие является глобальным, а значит что для определения нужного смарт-процесса в таком случае необходимо все равно было бы тащить туда данные текущей фабрики.
Для определения типа операции предлагается использовать название класса операции или возможно Enum из значений доступных операций (предпочтительнее).