Using Batch API in Drupal 7 tutorial – in simple code

Using Batch API in Drupal 7 tutorial – in simple code

Finding a good tutorial about the batch API can be a little difficult. When you do, it may be a little difficult to understand. So here is my shot to try a simple tutorial. 4 simple functions you will need as a minimum:

  • hook_menu to create the page
  • callback function to run when page is hit (sets up the batch jobs)
  • actual function to run multiple times during the batch
  • function to run after job is complete (catches success and error messages)

That’s it, here we go:

// mymodule_import.module

// menu_hook to create a page
function mymodule_import_menu() {
  $items['admin/mymodule-import'] = array(
    'title' => 'Import stuff',
    'page callback' => 'mymodule_import_stuff',
    'access arguments' => array('access administration pages'),
    'type' => MENU_NORMAL_ITEM,
  );
  return $items;
}

// menu callback function - sets up the batch
function mymodule_import_stuff() {
  $batch = array(
    'operations' => array(),
    'finished' => 'mymodule_import_batch_finished', // runs after batch is finished
    'title' => t('Processing Import'),
    'init_message' => t('Import is starting.'),
    'progress_message' => t('Processed @current out of @total.'),
    'error_message' => t('Import has encountered an error.'),
  );
  $progress = 0; // where to start
  $limit = 100; // how many to process for each run
  $max = 1000; // how many records to process until stop - can do query here to get max times to run
  while ($progress <= $max) {
    $batch['operations'][] = array('mymodule_import_stuff_process', array($progress, $limit));
    $progress = $progress + $limit;
  }
  batch_set($batch);
  batch_process('admin/mymodule-import'); // page to return to after complete
}

// this is what runs multiple times per batch
// progress and limit and is updated during each run
function mymodule_import_stuff_process($progress, $limit, &$context) {
  // use $progress and $limit to only run that amount of records
  // example using db_select
  $query = db_select('node', 'n');
  $query->fields('n.nid');
  $query->orderBy('n.nid', 'ASC');
  $query->range($progress, $limit);
  $nodes = $query->execute()->fetchAll();
  foreach ($nodes as $node) {
    // processing
  }  

  // update progress for message
  $progress = $progress + $limit; 
  // update message during each run so you know where you are in the process
  $context['message'] = 'Now processing ' . $progress . ' - ' . $context['results'][0] . ' imported';
}

function mymodule_import_batch_finished($success, $results, $operations) {
  if ($success) {
    drupal_set_message('Import is complete');
  }
  else {
    $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');
  }
}

Little tip: if you want to keep track of records updated or inserted (or any other kind of persistent value), you can latch onto $context[‘results’]. So that would look like:


// add to bottom of mymodule_import_stuff_process()
$context['results']['updated'] = $context['results']['updated'] + $updated_count;
$context['results']['inserted'] = $context['results']['inserted'] + $new_count;

//retrieve final number in mymodule_import_batch_finished()
if ($success) {
  drupal_set_message($results['updated'] . ' updated');
  drupal_set_message($results['inserted'] . ' inserted');
}

Comments are closed.