Как можно отследить событие изменения или добавления "OnAfter" в Битрикс24 после завершения всех бизнес-процессов?

Я столкнулся с проблемой при использовании событий OnAfter для объектов CRM и IBlocks. Когда создается или изменяется документ, и в это время запускается один или несколько бизнес-процессов (БП), событие OnAfter срабатывает столько раз, сколько раз изменяется документ в этих БП. С одной стороны, это корректное поведение, но мне необходимо поймать только последнее событие, которое происходит после завершения всех БП.

У меня есть два варианта решения, но ни один из них меня не устраивает:
1. Вызывать необходимые функции с помощью блока PHP в самом конце цепочки БП для документа.
2. Запускать цепочку БП из PHP по событиям OnAfter и в конце выполнять нужный код.

Подскажите, как можно переписать класс, который вызывает события OnAfter, чтобы он срабатывал только после завершения всех бизнес-процессов? Возможно, стоит использовать наследование для этого?
  • 29 сентября 2024 г. 16:53
Ответы на вопрос 2
Чтобы отслеживать завершение всех бизнес-процессов и вызывать обработчик только один раз в конце, вы можете использовать несколько подходов.

### Подход 1: Использование статического флага

Добавьте статический флаг, который будет указывать, что событие уже было обработано. Этот флаг необходимо сбрасывать, когда все бизнес-процессы завершены.

```php
class MyEntityHandler
{
    private static $eventHandled = false;

    public static function onAfterAdd($fields)
    {
        if (self::$eventHandled) {
            return;
        }

        // Логика обработки события
        // ...

        // Устанавливаем флаг о том, что событие было обработано
        self::$eventHandled = true;

        // После завершения всех БП сбрасываем флаг
        register_shutdown_function(function() {
            self::$eventHandled = false;
        });
    }

    public static function onAfterUpdate($fields)
    {
        return self::onAfterAdd($fields);
    }
}
```

### Подход 2: Регистрация завершающего обработчика

Вы можете ожидать завершение всех бизнес-процессов по их сигналам:

1. При каждом запуске бизнес-процесса установить таймер.
2. Обработать финальное событие после проверки, завершены ли все бизнес-процессы.

### Подход 3: Наследование и переопределение

Если у вас есть возможность изменить обработку событий, то вы можете создать свой класс, который наследует существующий и переопределяет метод, отвечающий за срабатывание ожиданий.

```php
class CustomCRMEntity extends OriginalCRMEntity
{
    public function onAfterAdd($fields)
    {
        // Ваша логика проверки завершенности всех бизнес-процессов
        if ($this->areBusinessProcessesFinished()) {
            parent::onAfterAdd($fields);
        }
    }

    private function areBusinessProcessesFinished()
    {
        // Логика проверки завершенности (например, через API БП)
        // ...
    }
}
```

### Подход 4: Использование очередей сообщений

Можно создать систему сообщений между бизнес-процессами и вашими обработчиками, чтобы уведомлять их о завершении. Это, конечно, более сложный подход, но он более надежен в больших системах.

### Итог

Какой бы метод вы ни выбрали, важно контролировать, что ваш обработчик не выполнялся по несколько раз в результате изменений от разных бизнес-процессов. Обычно наиболее простым и изящным решением является использование статического флага, сохраняя при этом единообразие кода, избегая сложных структур.
Во-первых, вам скорее следует поправить логику БП и вашего обработчика событий, чтобы он делал то что нужно только в определенный момент и не делал ее лишний раз. 
Во-вторых, если вам нужен момент ИМЕННО после определенных действий, а обычные изменения в БП вам в принципе не нужно, то стоит рассмотреть момент создания собственного действия в БП.
Похожие вопросы