Drupal batch operations example using Domain Access

The code below illustrates how to use Drupal's batch operations to process domains created with the Domain Access module. Batch functions allow forms processing to be spread out over several page requests, avoiding PHP timeouts. Batch operations also provide visual feedback on the progress of the ongoing operations.

This example is for Drupal 7.x.

The function below generates the submission form that triggers the batch process. 

function MYMODULE_example_form($form, &$form_state) {
  $form['description'] = array(
    '#markup' => '<p>' . t('Click the button below to begin.') . '</p>',
  );
  $form['actions']['process'] = array(
    '#type' => 'submit',
    '#value' => t('Process'),
  );
  return $form;
}
 
The function triggered when the form is submitted:
 
function MYMODULE_example_form_submit(&$form, &$form_state) {

  // Specify a few values we will need during the operation.
  $placeholders = array(
    'placeholder_data1',
    'placeholder_data2',
    'placeholder_data3',
  );  

  $batch = array(
    'operations' => array(
      array('MYMODULE_batch', array($placeholders)), // The name of the batch function plus optional values.
    ),
    'title' => t('Example'),
    'finished' => 'MYMODULE_batch_finished', // The function to call at end of operation.
    'init_message' => t('Process is starting.'),
    'progress_message' => t('Processed @current out of @total.'),
    'error_message' => t('Sorry, we have encountered an error.'),
    // Add this if batch code is in a separate file.
    // 'file' => drupal_get_path('module', 'MYMODULE') . '/MYMODULE.admin.inc', 
  );
  batch_set($batch);
  batch_process('admin/'); // Where to send the user when finished.
}
 
Where the magic happens:
 
function MYMODULE_batch($supported, &$context) {
  if (!isset($context['sandbox']['progress'])) {
    $context['sandbox']['progress'] = 0;
    $context['sandbox']['current_domain'] = 0; // Set this to -1 to also retrieve the primary domain.
    $context['sandbox']['max'] = db_query('SELECT COUNT(DISTINCT domain_id) FROM {domain} WHERE domain_id <> 1')->fetchField();
  }

  $limit = 5;

  // Get all affiliate domains.
  $result = MYMODULE_get_domains($context['sandbox']['current_domain'], $limit);

  foreach ($result as $row) {
    $domain = domain_lookup($row->domain_id);
    
    // .. do something here

    $context['results'][] = check_plain($domain['sitename']);
    $context['sandbox']['progress']++;
    $context['sandbox']['current_domain'] = $domain['domain_id'];
    $context['message'] = t('Now processing %domain', array('%domain' => $domain['sitename']));
  }
  if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
    $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
  }
}
 
Helper function to retrieve domains:
 
function MYMODULE_get_domains($current_domain, $limit) {
  return db_select('domain')
    ->fields('domain', array('domain_id'))
    ->condition('domain_id', $current_domain, '>')
    ->orderBy('domain_id')
    ->range(0, $limit)
    ->execute();
}
 
A pretty generic example of a batch callback function for when operations are complete:
 
function MYMODULE_batch_finished($success, $results, $operations) {
  if ($success) {
    drupal_set_message(t('%results processed.', array('%results' => count($results))));
  }
  else {
    $error_operation = reset($operations);
    drupal_set_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))), 'error');
  }
}