Batch API

Last modified: October 1, 2009 - 02:22

Here's an example of how to use the new Batch API in Drupal 6. In this example, you would probably call batch_example() from a form submit handler, where the form submission provided the $options you want to use to update the nodes.

<?php
/**
* The $batch can include the following values. Only 'operations'
* and 'finished' are required, all others will be set to default values.
*
* @param operations
*   An array of callbacks and arguments for the callbacks.
*   There can be one callback called one time, one callback
*   called repeatedly with different arguments, different
*   callbacks with the same arguments, one callback with no
*   arguments, etc.
*
* @param finished
*   A callback to be used when the batch finishes.
*
* @param title
*   A title to be displayed to the end user when the batch starts.
*
* @param init_message
*   An initial message to be displayed to the end user when the batch starts.
*
* @param progress_message
*   A progress message for the end user. Placeholders are available.
*   Placeholders note the progression by operation, i.e. if there are
*   2 operations, the message will look like:
*    'Processed 1 out of 2.'
*    'Processed 2 out of 2.'
*   Placeholders include:
*     @current, @remaining, @total and @percentage
*
* @param error_message
*   The error message that will be displayed to the end user if the batch
*   fails.
*
*/
function batch_example($options1, $options2, $options3, $options4) {
 
$batch = array(
   
'operations' => array(
      array(
'batch_example_process', array($options1, $options2)),
      array(
'batch_example_process', array($options3, $options4)),
      ),
   
'finished' => 'batch_example_finished',
   
'title' => t('Processing Example Batch'),
   
'init_message' => t('Example Batch is starting.'),
   
'progress_message' => t('Processed @current out of @total.'),
   
'error_message' => t('Example Batch has encountered an error.'),
  );
 
batch_set($batch);

 
// If this function was called from a form submit handler, stop here,
  // FAPI will handle calling batch_process().

  // If not called from a submit handler, add the following,
  // noting the url the user should be sent to once the batch
  // is finished.
 
batch_process('node/1');
}

/**
* Batch Operation Callback
*
* Each batch operation callback will iterate over and over until
* $context['finished'] is set to 1. After each pass, batch.inc will
* check its timer and see if it is time for a new http request,
* i.e. when more than 1 minute has elapsed since the last request.
*
* An entire batch that processes very quickly might only need a single
* http request even if it iterates through the callback several times,
* while slower processes might initiate a new http request on every
* iteration of the callback.
*
* This means you should set your processing up to do in each iteration
* only as much as you can do without a php timeout, then let batch.inc
* decide if it needs to make a fresh http request.
*
* @param options1, options2
*   If any arguments were sent to the operations callback, they
*   will be the first argments available to the callback.
*
* @param context
*   $context is an array that will contain information about the
*   status of the batch. The values in $context will retain their
*   values as the batch progresses.
*
* @param $context['sandbox']
*   Use the $context['sandbox'] rather than $_SESSION to store the
*   information needed to track information between successive calls.
*   The values in the sandbox will be stored and updated in the database
*   between http requests until the batch finishes processing. This will
*   avoid problems if the user navigates away from the page before the
*   batch finishes.
*
* @param $context['results']
*   The array of results gathered so far by the batch processing.
*   The current operation can append its own.
*
* @param $context['message']
*   A text message displayed in the progress page.
*
* @param $context['finished']
*   A float number between 0 and 1 informing the processing engine
*   of the completion level for the operation.
*
*   1 (or no value explicitly set) means the operation is finished
*   and the batch processing can continue to the next operation.
*/
function batch_example_process($options1, $options2, &$context) {
  if (!isset(
$context['sandbox']['progress'])) {
   
$context['sandbox']['progress'] = 0;
   
$context['sandbox']['current_node'] = 0;
   
$context['sandbox']['max'] = db_result(db_query('SELECT COUNT(DISTINCT nid) FROM {node}'));
  }

 
// For this example, we decide that we can safely process
  // 5 nodes at a time without a timeout.
 
$limit = 5;

 
// With each pass through the callback, retrieve the next group of nids.
 
$result = db_query_range("SELECT nid FROM {node} WHERE nid > %d ORDER BY nid ASC", $context['sandbox']['current_node'], 0, $limit);
  while (
$row = db_fetch_array($result)) {

   
// Here we actually perform our processing on the current node.
   
$node = node_load($row['nid'], NULL, TRUE);
   
$node->value1 = $options1;
   
$node->value2 = $options2;
   
node_save($node);

   
// Store some result for post-processing in the finished callback.
   
$context['results'][] = check_plain($node->title);

   
// Update our progress information.
   
$context['sandbox']['progress']++;
   
$context['sandbox']['current_node'] = $node->nid;
   
$context['message'] = t('Now processing %node', array('%node' => $node->title));
  }

 
// Inform the batch engine that we are not finished,
  // and provide an estimation of the completion level we reached.
 
if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
   
$context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
  }
}

/**
* Batch 'finished' callback
*/
function batch_example_finished($success, $results, $operations) {
  if (
$success) {
   
// Here we do something meaningful with the results.
   
$message = count($results) .' processed.';
   
$message .= theme('item_list', $results);
  }
  else {
   
// An error occurred.
    // $operations contains the operations that remained unprocessed.
   
$error_operation = reset($operations);
   
$message = t('An error occurred while processing %error_operation with arguments: @arguments', array('%error_operation' => $error_operation[0], '@arguments' => print_r($error_operation[1], TRUE)));
  }
 
drupal_set_message($message);
}
?>

Sandbox is not persistent

JirkaRybka - May 8, 2009 - 11:02

Note that $context['sandbox'] is only useful inside a multi-pass operation iterations, and may not be used for persistent data shared between more operations in a batch. This took me a while to figure out - in batch.inc, the sandbox is explicitly cleared after every single operation in the batch is finished. We can use $context['results'] instead, when different operations in a batch need to share some data.

I modified this example for

lefnire - May 18, 2009 - 08:08

I modified this example for my situation, and running the batch operation from a _submit() handler just showed the "Processing page" with "Processed 2 out of 2", and does nothing in the end. I put a variable_set('test','test'); at the beginning of the batch operation, and it's never set. Then I tried just copy/pasting this code as-is, and it does the exact same thing.

It seems like the operations aren't being called -- like batch_set is doing the setup properly except registering the the operation

Hi!

joehudson - May 21, 2009 - 20:47

Hi!

I had the same problem creating, setting and processing my batch in an .inc file for a module I'm working on. Reading through the batch.inc file I saw the $batch_set['file'] variable. When I set $batch['file'] = \< path to the file containing the batch operation function \>
(along with $batch['operations'] and all the rest)
the batch was processed correctly!

I hope this helps :)

Thanx joe, It saved me the

m4manas - July 11, 2009 - 03:03

Thanx joe,
It saved me the trouble.

THANK YOU!

janusman - August 28, 2009 - 20:25

Thanks @joehudson! Never would've figured it out otherwise!

yup, that did it Joe! Thanks

lefnire - July 28, 2009 - 04:58

yup, that did it Joe! Thanks

you're welcome!

joehudson - September 22, 2009 - 01:22

glad to be a help.

 
 

Drupal is a registered trademark of Dries Buytaert.