Use Drush command with Batch to update Node

28 / Nov / 2022 by anurag.nagar 0 comments

In this tutorial, we are going learn how to create/update nodes with the help of the Drush command using  Batch API in Drupal. For that, we need to create a custom module. The file structure will be like below:

Example: In the below example, we are updating the article content type Title field with a dummy value by dividing it into chunks of 100, similarly can add your own logic.

To create a custom module, follow the below steps, which will help create a custom Drush Command update:node article.

  1. Create BatchDrushCommand.php file under the custom module/Commands folder.
    <?php
    
    namespace Drupal\module_name\Commands;
    
    use Drupal\Core\Entity\EntityTypeManagerInterface;
    
    use Drupal\Core\Logger\LoggerChannelFactoryInterface;
    
    use Drush\Commands\DrushCommands;
    
    /**
    
    * A Drush commandfile for defining the command and callback method to it.
    
    */
    
    class BatchDrushCommand extends DrushCommands {
    
    /**
    
    * Entity type service.
    
    *
    
    * @var \Drupal\Core\Entity\EntityTypeManagerInterface
    
    */
    
    private $entityTypeManager;
    
    /**
    
    * Logger service.
    
    *
    
    * @var \Drupal\Core\Logger\LoggerChannelFactoryInterface
    
    */
    
    private $loggerChannelFactory;
    
    /**
    
    *
    
    * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
    
    * Entity type service.
    
    * @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $loggerChannelFactory
    
    * Logger service.
    
    */
    
    public function__construct(EntityTypeManagerInterface$entityTypeManager, LoggerChannelFactoryInterface$loggerChannelFactory) {
    
    $this->entityTypeManager = $entityTypeManager;
    
    $this->loggerChannelFactory = $loggerChannelFactory;
    
    }
    
    /**
    
    * Update Node.
    
    *
    
    * @param string $type
    
    * Type of node to update
    
    * Argument provided to the drush command.
    
    *
    
    * @command update:node
    
    * @aliases update-node
    
    *
    
    * @usage update:node article
    
    */
    
    public functionupdateNode($type = '') {
    
    // 1. Log the start of the script.
    
    $this->loggerChannelFactory->get('module_name')->info('node update started');
    
    // Check the type of node given as argument, if not, set article as default.
    
    if (strlen($type) == 0) {
    
    $type = 'article';
    
    }
    
    // 2. Retrieve all nodes of this type.
    
    try {
    
    $storage = $this->entityTypeManager->getStorage('node');
    
    $query = $storage->getQuery()
    
    ->condition('type', $type)
    
    ->condition('status', '1');
    
    $nidsCount = $query->execute();
    
    $nidsCount = count($nidsCount);
    
    }
    
    catch (\Exception$e) {
    
    $this->output()->writeln($e);
    
    $this->loggerChannelFactory->get('module_name')->warning('Error found @e', ['@e' => $e]);
    
    }
    
    // 3. Create the operations array for the batch.
    
    $operations = [];
    
    $numOperations = 0;
    
    $batchId = 1;
    
    if ($nidsCount > 0) {
    
    for ($i = 0; $i < $nidsCount;) {
    
    $query = $storage->getQuery()
    
    ->condition('type', $type)
    
    ->range($i, 100)
    
    ->condition('status', '1');
    
    $nids = $query->execute();
    
    $articleId = array_keys($nids);
    
    $operations[] = [
    
    '\Drupal\module_name\UpdateNode::articleUpdate',
    
    [
    
    $batchId, $articleId,
    
    ],
    
    ];
    
    $batchId++;
    
    $numOperations++;
    
    $i = $i + 100;
    
    }
    
    }
    
    else {
    
    $this->logger()->warning('No nodes of this type @type', ['@type' => $type]);
    
    }
    
    // 4. Create the batch.
    
    $batch = [
    
    'title' => t('Updating @num node(s)', ['@num' => $numOperations]),
    
    'operations' => $operations,
    
    'finished' => '\Drupal\module_name\UpdateNode::articleUpdateFinished',
    
    ];
    
    // 5. Add batch operations as new batch sets.
    
    batch_set($batch);
    
    // 6. Process the batch sets.
    
    drush_backend_batch_process();
    
    }
    
    }
    
    
  2.  Create a Helper File named UpdateNode.php in which we will write the articleUpdate and articleUpdateFinished function 
    <?php
    
    namespace Drupal\module_name;
    
    /**
    
    * Class RevisionBatchProcess.
    
    */
    
    class UpdateNode {
    
    /**
    
    * articleUpdate callback.
    
    */
    
    public function articleUpdate($id, $articleId, &$context) {
    
    foreach ($articleId as $nid) {
    
    // Simulate long process by waiting 100 microseconds.
    
    usleep(100);
    
    $entityService = \Drupal::entityTypeManager();
    
    $articleObject = $entityService->getStorage('node')->loadRevision($nid);
    
    $articleObject->set('title', 'update-title');
    
    $articleObject->save();
    
    $context['results'][] = $id;
    
    $context['message'] = t('processing "@id" @response',
    
    ['@id' => $id, '@response' => $nid]
    
    );
    
    }
    
    }
    
    /**
    
    * articleUpdateFinished callback.
    
    */
    
    public function articleUpdateFinished($success, array $results, array $operations) {
    
    $messenger = \Drupal::messenger();
    
    if ($success) {
    
    $messenger->addMessage(t('@count results processed.', ['@count' => count($results)]));
    
    }
    
    }
    
    }

    Once the code is added to the module, enable the module or if you are using the existing module, then clear the drupal cache and run the drush update:node article
    command.

    Please add comments if you have any queries or doubts.

FOUND THIS USEFUL? SHARE IT

Leave a Reply

Your email address will not be published. Required fields are marked *