Using Batch API in Drupal 7 - Batch API Tutorial
Drupal's batch API processing is awesome and offers two great benefits: -
- You can break long tasks into smaller batches, preventing max_execution_time errors
- You can give the visitor (or more likely administration or developer) some feedback
Building a batch routine in a custom module requires a minimum of 4 function including the batch function call: -
- Call the batch API and tell it which function to build the batch instructions.
- The batch builder loop which builds a list of operations for the batch routine to perform. It doesn't call the operation code at this point but purely makes the list of operations.
- The function to perform 1 operation in the batch.
- A "Finished" function to notify the user when we're finished, or if there is an error.
Let's look through a working example, with some of the actual work code removed as it's not important here. This code was mostly adapted from the excellent "Examples" module collection.
Step 1 - Call the batch API function
This just passes a function name to the batch API. The referenced function should build the array which tells batch API what to do. In our example we have it on a form submit handler.
<?php
/*
* Form Submit handler
*/
function drupology_form_submit($form, $form_state) {
batch_set(drupology_associate_audio_tiles_to_songs());
}
?>
Step 2 - Create the list of operations to call when the batch starts
Here we essentially build an array of future function calls, with arguments, and a finished function.
The important thing here is the $operations array so look at that closely. When the batch starts it will loop through that array calling those function names with those arguments.
<?php
/**
* Batch operation: associate audio tiles to each song node.
*/
function drupology_associate_audio_tiles_to_songs() {
drupal_set_message('Updating Song Nodes');
// load all the "Song Story" nodes
$nodes = node_load_multiple(array(), array('type' => "story_song"));
$node_count = count($nodes);
// build the list of operation functions and function arguments
foreach($nodes as $nid=>$node) {
// $operations[] = array(<function name>, <array of arguments to pass to function>);
$operations[] = array('drupology_associate_audio_tiles_to_song', array($node));
}
// build the batch instructions
$batch = array(
'operations' => $operations,
'finished' => 'drupology_associate_audio_tiles_to_songs_finished',
);
return $batch;
}
?>
Step 3 - Create the operation code
This is the actual operation part - the thing that does the work. The arguments it receives will have come from the $operations[] loop in step 2 above. Note the additional $context argument. This is in additional to the ones we provided in step 2 and lets us converse with the batch. This is useful for passing back status messages, etc.
<?php
/**
* Batch operation: associate audio tiles to 1 node.
* This is the function that is called on each operation in the above.
*/
function drupology_associate_audio_tiles_to_song($node, &$context) {
$context['results'][] = $node->nid . ' : ' . check_plain($node->title);
// Optional message displayed under the progressbar.
$context['message'] = t('Processing song "@title"', array('@title' => $node->title));
$updated = FALSE;
// .... do the actual work - code removed in this example
if ($updated) {
node_save($node);
$path = drupal_lookup_path("alias", "node/" . $node->nid);
drupal_set_message("<a href='/$path'>" . $node->title . "</a> updated.");
}
}
?>
Step 4 - Create the "Finished" code
Here we let the user know when we've finished and also if there were any errors.
<?php
function drupology_associate_audio_tiles_to_songs_finished($success, $results, $operations) {
if ($success) {
// Here we could do something meaningful with the results.
// We just display the number of nodes we processed...
drupal_set_message(t('@count songs processed.', array('@count' => count($results))));
} else {
// An error occurred.
// $operations contains the operations that remained unprocessed.
$error_operation = reset($operations);
drupal_set_message(t('An error occurred while processing @operation with arguments : @args', array('@operation' => $error_operation[0], '@args' => print_r($error_operation[0], TRUE))));
}
}
?>