Drupal 8 - Simple example using Batch API to delete Article nodes

By xngo on March 12, 2019

Overview

Batching processing allows large amount of data to be processed without putting stress on your server. Also, there is a timeout limit per page load on your browser. Therefore, processing data that requires a lot of time in 1 go will not work.

In Drupal 8, the Batch API allows you to process in batches. What you need are:

  • $operationns: Breakdown your data into smaller chunks to be processed for each batch(operation).
  • batch_set($batch): Define and setup your batch.
  • batchDelete(): Implement the operation callback method. Define how to process the batch data.
  • batchFinished(): Implement the finish callback method. Define what to do after batch processing is complete.

In the tutorial below, I will show how to delete Article nodes in batches using node ID ranges.

Create routing to your batch delete form page

Create an URL for your batch delete form page by adding the following routing information in ./modules/tradesteps/tradesteps.routing.yml.

tradesteps.batch_delete_form:
  path: '/batch_delete_form'
  defaults:
    _form: 'Drupal\tradesteps\Form\BatchDeleteForm'
    _title: 'Batch delete nodes form'
  requirements:
    _permission: 'access content'

Implement your batch delete form class

In the code below, focus on

  • submitForm(): It is inside this function that you create and setup your batch processing using batch_set($batch);. The only required array element of $batch is the attribute $operations. It is an array of operations that your batch will process. The list of operations are in the form of an array containing two components. The first component is the function to call and the second is an array of parameters to pass to that function.
  • batchDelete(): The operation callback function. It will delete nodes listed in $smaller_batch_data parameter.
  • batchFinished(): The finish callback function. It will display success or error message after batch processing is complete.
<?php
namespace Drupal\tradesteps\Form;
 
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
 
 
class BatchDeleteForm extends FormBase {
 
    public function buildForm(array $form, FormStateInterface $form_state) {
 
        $form['from_nid'] = [
            '#type' => 'textfield',
            '#title' => $this->t('From node ID'),
            '#required' => TRUE,
        ];
 
        $form['to_nid'] = [
            '#type' => 'textfield',
            '#title' => $this->t('To node ID'),
            '#required' => TRUE,
        ];
 
        // Add a submit button that handles the submission of the form.
        $form['actions']['submit'] = [
          '#type' => 'submit',
          '#value' => $this->t('Delete nodes in batches'),
        ];
 
        return $form;
    }
 
    public function submitForm(array &$form, FormStateInterface $form_state) {
 
        // Get all data to be processed.
        $from_nid = $form_state->getValue('from_nid');
        $to_nid = $form_state->getValue('to_nid');
 
        $all_nids = \Drupal::entityQuery('node')
                              ->condition('type', 'article')
                              ->condition('nid', $from_nid, '>=')
                              ->condition('nid', $to_nid, '<=')
                            ->execute();
 
        // Breakdown your process into small batches(operations).
        //      Delete 50 nodes per batch.
        $operations = [];
        foreach (array_chunk($all_nids, 50) as $smaller_batch_data) {
            $operations[] = ['\Drupal\tradesteps\Form\BatchDeleteForm::batchDelete'
                                            , [$smaller_batch_data]];
        }
 
        // Setup and define batch informations.
        $batch = array(
            'title' => t('Deleting nodes in batch...'),
            'operations' => $operations,
            'finished' => '\Drupal\tradesteps\Form\BatchDeleteForm::batchFinished',
        );
        batch_set($batch);
    }
 
    // Implement the operation method.
    public static function batchDelete($smaller_batch_data, &$context) {
        // Deleting nodes.
        $storage_handler = \Drupal::entityTypeManager()->getStorage('node');
        $entities = $storage_handler->loadMultiple($smaller_batch_data);
        $storage_handler->delete($entities);
 
        // Display data while running batch.
        $batch_size=sizeof($smaller_batch_data);
        $batch_number=sizeof($context['results'])+1;
        $context['message'] = sprintf("Deleting %s nodes per batch. Batch #%s"
                                            , $batch_size, $batch_number);
        $context['results'][] = sizeof($smaller_batch_data);
    }
 
    // What to do after batch completed. Display success or error message.
    public static function batchFinished($success, $results, $operations) {
        if ($success)
            $message = count($results). ' batches processed.';
        else
            $message = 'Finished with an error.';
 
        drupal_set_message($message);
    }    
 
    public function getFormId() {
        return 'tradesteps_batch_delete_form';
    }
 
}

Test batch nodes delete form

  1. Clear your cache.
  2. Open http://your-domain.com/batch_delete_form
  3. Type in the node ID ranges and then run the batch.

Batch Delete Form

Screenshot of Batch Delete Form

Batch running: batchDelete()

Screenshot of batch running

Batch finished: batchFinished()

Screenshot of batch finished

Note

If you are using Batch API outside of form API, then you have to explicitly call batch_process('/node'); after batch_set($batch); to run the batch operation. Otherwise, batch operation will not run.

Github

  • https://github.com/xuanngo2001/drupal-batch-delete-nodes.git

About the author

Xuan Ngo is the founder of OpenWritings.net. He currently lives in Montreal, Canada. He loves to write about programming and open source subjects.