Разбираемся с пакетными операциями (Batch API)

С помощью Batch API можно разбивать длительные операции на части и выполнять каждую часть в отдельном http запросе. Такой механизм позволяет избежать ограничение в 30 секунд (по умолчанию) на выполнение php скриптов.

С работой Batch API сталкивались все, например эта штука используется при установке друпала или обновлении переводов:

Простейший пример работы с Batch API, в котором мы изменим дату создания всех нод на текущую:

function mymodule_batch_form($form, &$form_state) {
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => 'Начать',
  );
  return $form;
}
 
function mymodule_batch_form_submit($form, &$form_state) {
  // Подготавливаем данные для операций
  $result = db_select('node', 'n')->fields('n', array('nid'))->execute();
 
  // Создаём массив с операциями.
  // Каждая операция будет выполнена в отдельном http запросе.
  foreach ($result as $row) {
    $operations[] = array('mymodule_change_date', array($row->nid));
  }
 
  $batch = array(
    // Массив операций и их параметров
    'operations' => $operations,
    // Функция, которая будет выполнена после окончания всех операций
    'finished' => 'mymodule_batch_finished',
    // Заголовок страницы с прогресс баром.
    // Опционально, по умолчанию t('Processing')
    'title' => 'Обновление дат',
    // Сообщение, показываемое при инициализации.
    // Опционально, по умолчанию t('Initializing.')
    'init_message' => 'Подготовка данных',
    // Сообщение, показывемое при выполнении операций.
    // Опционально, по умолчанию t('Completed @current of @total.')
    'progress_message' => 'Выполнено @current из @total.',
    // Сообщение показываемое при ошибке выполнения операции.
    // Опционально, по умолчанию t('An error has occurred.')
    'error_message' => 'Произошла ошибка.',
  );
 
  batch_set($batch);
 
  // Если Batch API используется не из _submit функции,
  // то дополнительно нужно вызвать batch_process();
}
 
function mymodule_change_date($nid, &$context) {
  // Производим манипуляции над нодами
  $node = node_load($nid);
  $node->created = time();
  node_save($node);
 
  // Эта информация будет доступна в mymodule_batch_finished
  $context['results'][] = $node->title;
  // Сообщение выводимое под прогресс баром после окончания текущей операции
  $context['message'] = 'Обновлена дата у материала <em>' . $node->title . '</em>';
}
 
function mymodule_batch_finished($success, $results, $operations) {
  if ($success) {
    drupal_set_message('Обновлена дата у ' . count($results) . ' материалов:' . theme('item_list', array('items' => $results)));
  }
  else {
    drupal_set_message('Завершено с ошибками.', 'error');
  }
}

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

function mymodule_batch_form_submit($form, &$form_state) {
  $batch = array(
    'operations' => array(
      array('mymodule_change_date', array()),
      array('mymodule_send_notify', array()),
    ),
    'finished' => 'mymodule_batch_finished',
  );
  batch_set($batch);
}
 
function mymodule_change_date(&$context) {
  // Переменная $context['sandbox'] используется для хранения данных между итерациями
  if (empty($context['sandbox'])) {
    $context['sandbox']['progress'] = 0;
    $context['sandbox']['prev_nid'] = 0;
    $context['sandbox']['max'] = db_select('node')->countQuery()->execute()->fetchField();
  }
 
  $nid = db_select('node', 'n')
    ->fields('n', array('nid'))
    ->condition('nid', $context['sandbox']['prev_nid'], '>')
    ->execute()
    ->fetchField();
  $node = node_load($nid);
  $node->created = time();
  node_save($node);
 
  $context['sandbox']['progress']++;
  $context['sandbox']['prev_nid'] = $nid;
  $context['results'][] = $node->title;
  $context['message'] = 'Обновлена дата у материала <em>' . $node->title . '</em>';
 
  // Если операция не завершена, то показываем на каком она этапе
  if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
    $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
  }
}
 
function mymodule_send_notify(&$context) {
  // Код по аналогии
}

Написанное актуально для
Drupal 7
Теги:
Batch API
Добавлено: 29 Июля 2018 22:08:59 Добавил: Андрей Ковальчук Нравится 0
Добавить
Комментарии:
Нету комментариев для вывода...