Advertising sustains the DA. Ads are hidden for members. Join today

Batch API overview

Last updated on
29 January 2023

Drupal 7 will no longer be supported after January 5, 2025. Learn more and find resources for Drupal 7 sites

Here's an example of how to use the Batch API, originally introduced 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.

Here is a sketch to understand the mechanism of Batch API :

PS! This is only an example. Don't forget to actually read the API documentation.

/**
 * 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. (Use an empty array if you want to pass 
 * no arguments.)
 *
 * @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. The default is 'Processing'.
 *
 * @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.
 *
 * @param file
 * Path to file containing the callbacks declared above. Always needed when
 * the callbacks are not in a .module file.
 *
 */
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.'),
 'file' => drupal_get_path('module', 'batch_example') . '/batch_example.inc',
 );
 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.
 // IMPORTANT: 
 // If you set a blank parameter, the batch_process() will cause an infinite loop
 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.
 * Note that $context['finished'] is set to 1 on entry - a single pass 
 * operation is assumed by default.
 *
 * 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 arguments 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 to
 * the current operation. If you need to pass values to the next operation
 * use $context['results'].
 *
 * 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. This
 * array is highly useful for passing data between operations. After all
 * operations have finished, these results may be referenced to display
 * information to the end-user, such as how many total items were
 * processed.
 *
 * @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.
 *
 * Batch API resets this to 1 each time the operation callback is called.
 */
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_query('SELECT COUNT(DISTINCT nid) FROM {node}')->fetchField();
 }
 // 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'], $limit);
 foreach ($result as $row) {
 // 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 Batch API overview', array('Batch API overview' => $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 = t('@count items successfully processed:', array('@count' => count($results)));
 // $message .= theme('item_list', $results); // D6 syntax
 $message .= theme('item_list', array('items' => $results));
 drupal_set_message($message);
 }
 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, 'error');
 }
 
}

Another example that might be of interest is GiantRobot / csvimport which uses file upload and Batch API to parse a CSV file line by line.

Tags

Help improve this page

Page status: Not set

You can: