Перейти к содержанию

Итератор

Итератор выполняет длительные операции по частям и сохраняет состояние между запусками.

Создать итератор

Итератор реализован в базовом классе \Bitrix\Main\Update\Stepper. Чтобы настроить свой итератор, создайте новый класс-наследник.

  1. Укажите идентификатор модуля.

  2. Реализуйте метод execute(array &$option).

  3. Укажите, когда продолжить или завершить выполнение итератора с помощью констант self::CONTINUE_EXECUTION и self::FINISH_EXECUTION.

Один вызов execute() обрабатывает порцию элементов и увеличивает steps на количество обработанных элементов.

<?php
namespace MyCompany\MyModule\Stepper;

use Bitrix\Main\Update\Stepper;

class ExampleStepper extends Stepper
{
    protected static $moduleId = 'mycompany.mymodule';

    // За один запуск обработаем один шаг и увеличим счетчик прогресса.
    public function execute(array &$option)
    {
        if (empty($option))
        {
            $option['steps'] = 0; // сколько обработано
            $option['count'] = 100; // всего шагов
        }

        $option["steps"]++;

        // Используем константы для указания статуса выполнения
        return ($option['steps'] < $option['count'])
            ? self::CONTINUE_EXECUTION
            : self::FINISH_EXECUTION;
    }

    // Переопределяем метод для отображения своего заголовка
    public static function getTitle()
    {
        return 'Пример обработки';
    }
}

Переопределяйте заголовок методом getTitle(), если нужно показывать свое название в интерфейсе.

Для одноразовых задач в класс можно добавить свойство $deleteFile со значением true. После завершения работы система удалит файл класса.

protected $deleteFile = true;

Запустить итератор

Итератор запускается как отложенный агент. Система выполняет его при каждом запросе к сайту, но не чаще одного раза в секунду.

Совет

Подробнее в статье Агенты и фоновые задачи

Для создания агента можно использовать один из двух методов.

  1. bind($delay, $withArguments) — создает агента для текущего класса. Используйте, когда модуль уже загружен, например, в обработчике события или в компоненте.

  2. $delay — задержка в секундах перед первым запуском. Значение по умолчанию — 300.

  3. $withArguments — массив параметров для передачи в итератор.

// Запустить через 120 секунд
if (\Bitrix\Main\Loader::includeModule('mycompany.mymodule'))
{
    \MyCompany\MyModule\Stepper\ExampleStepper::bind(120);
}
  1. bindClass($className, $moduleId, $delay, $withArguments) — создает агента для указанного класса и модуля. Используйте в файлах установки модуля или когда модуль может быть не загружен.

  2. $className — полное имя класса итератора.

  3. $moduleId — идентификатор модуля, к которому привязан итератор.

  4. $delay — задержка в секундах перед первым запуском. Значение по умолчанию — 300.

  5. $withArguments — массив параметров для передачи в итератор.

\Bitrix\Main\Update\Stepper::bindClass(
    'MyCompany\MyModule\Stepper\ExampleStepper',
    'mycompany.mymodule',
    60
);

Передать параметры в итератор

Передавайте параметры через $withArguments, если хотите запускать один и тот же Stepper с разными входными данными.

В массиве используйте простые типы: строку или целое число.

// Запуск с параметрами
\Bitrix\Main\Update\Stepper::bindClass(
    'MyCompany\MyModule\Stepper\DataProcessor',
    'mycompany.mymodule',
    120,
    [
        'category_id' => 5,
        'action' => 'export',
        'limit' => 1000,
    ]
);

Получить параметры можно методом getOuterParams().

// Получение параметров в итераторе
public function execute(array &$option)
{
    $params = $this->getOuterParams();

    $categoryId = (int)($params['category_id'] ?? 0);
    $action = $params['action'] ?? '';

    // Используем параметры в логике
}

Показать прогресс выполнения

Прогресс выполнения итераторов отображается с помощью элемента Градусник. Он показывает соотношение пройденных шагов к общему количеству шагов.

  1. Конкретный итератор — показывает прогресс только одного итератора. Используйте, чтобы отобразить статус конкретной операции.
echo \MyCompany\MyModule\Stepper\ExampleStepper::getHtml();
  1. Все итераторы модуля — показывает общий прогресс всех итераторов модуля. Можно использовать для административной страницы модуля.
echo \Bitrix\Main\Update\Stepper::getHtml(
    'mycompany.mymodule',
    'Обработка данных модуля'
);
  1. Выборочные итераторы — показывает прогресс только указанных итераторов. Используйте, когда нужно сгруппировать связанные операции.
echo \Bitrix\Main\Update\Stepper::getHtml([
    'mycompany.mymodule' => [
        'MyCompany\MyModule\Stepper\DataMigrator',
        'MyCompany\MyModule\Stepper\CacheClear',
    ],
    'iblock' => [
        'Bitrix\Iblock\Stepper\SomeProcessor',
    ],
], 'Фоновая обработка');

Градусник опрашивает сервер о состоянии каждые пять минут, когда отображается на странице.

Сохранить прогресс выполнения

Прогресс выполнения итератора между запусками можно хранить в массиве $option.

  • steps — количество выполненных шагов.

  • count — общее количество шагов.

  • другие данные для продолжения работы, например, последний обработанный ID.

Данные о шагах steps и count нужны для отображения прогресса в Градуснике.

public function execute(array &$option)
{
    if (empty($option))
    {
        // Инициализация при первом запуске
        $option['steps'] = 0; // количество выполненных шагов
        $option['count'] = $this->calculateTotalItems(); // общее количество шагов
        $option['last_processed_id'] = 0;
        $option['processed_ids'] = [];
    }

    // Используем сохраненные данные
    $lastId = (int)$option['last_processed_id'];

    // Обрабатываем следующую порцию
    $processed = $this->processNextBatch($lastId, 50);

    // Сохраняем состояние для следующего запуска
    $option['last_processed_id'] = $processed['last_id'];
    $option['steps'] += $processed['count'];
    $option['processed_ids'] = array_merge(
        $option['processed_ids'],
        $processed['ids']
    );

    // Проверяем завершение
    return ($option['steps'] < $option['count'])
        ? self::CONTINUE_EXECUTION
        : self::FINISH_EXECUTION;
}

Внимание

Не рекомендуется хранить большие объемы данных в массиве $option, так как массив сериализуется при каждом сохранении.