Calling batchBuilder in custom service throws error when DependencySerializationTrait is not used.

Created on 24 June 2020, over 4 years ago
Updated 9 November 2023, about 1 year ago

When calling a batchBuilder in custom service without using DependencySerializationTrait it thows an error.

LogicException: The database connection is not serializable. This probably means you are serializing an object that has an indirect reference to the database connection. Adjust your code so that is not necessary. Alternatively, look at DependencySerializationTrait as a temporary solution. in Drupal\Core\Database\Connection->__sleep() (line 1573 of core/lib/Drupal/Core/Database/Connection.php). 

In order to solve that we need to use `use Drupal\Core\DependencyInjection\DependencySerializationTrait;` .

For example I created a custom service :


<?php

namespace Drupal\book_content\Batch;

use Drupal\Core\Session\AccountProxyInterface;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\Core\Datetime\DateFormatter;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Entity\EntityTypeManager;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Batch\BatchBuilder;
use Drupal\node\NodeInterface;
use Drupal\node\Entity\Node;
use Drupal\Core\DependencyInjection\DependencySerializationTrait;

/**
 * Book Content Batch Class.
 */
class BookContentBatch {

  use StringTranslationTrait;
  // use DependencySerializationTrait;

  /**
   * Drupal\Core\Session\AccountProxyInterface definition.
   *
   * @var \Drupal\Core\Session\AccountProxyInterface
   */
  protected $currentUser;

  /**
   * Date formatter.
   *
   * @var \Drupal\Core\Datetime\DateFormatterInterface
   */
  protected $dateFormatter;

  /**
   * The Messenger service.
   *
   * @var \Drupal\Core\Messenger\MessengerInterface
   */
  protected $messenger;

  /**
   * Logger service.
   *
   * @var \Drupal\Core\Logger\LoggerChannelFactoryInterface
   */
  protected $logger;

  /**
   * Language manager.
   *
   * @var \Drupal\Core\Logger\LoggerChannelFactoryInterface
   */
  protected $languageManager;

  /**
   * The entity type manager.
   *
   * @var \Drupal\Core\Entity\EntityTypeManager
   */
  protected $entityTypeManager;

  /**
   * Batch Builder.
   *
   * @var \Drupal\Core\Batch\BatchBuilder
   */
  protected $batchBuilder;

  /**
   * Booksubjects constructor.
   *
   * @param \Drupal\Core\Session\AccountProxyInterface $current_user
   *   The user service.
   * @param \Drupal\Core\Messenger\MessengerInterface $messenger
   *   The messenger service.
   * @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger
   *   The logger service.
   * @param \Drupal\Core\Datetime\DateFormatter $date_formatter
   *   The date formatter service.
   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
   *   The language manager.
   * @param \Drupal\Core\Entity\EntityTypeManager $entity_type_manager
   *   The entity type manager.
   */
  public function __construct(AccountProxyInterface $current_user, MessengerInterface $messenger, LoggerChannelFactoryInterface $logger, DateFormatter $date_formatter, LanguageManagerInterface $language_manager, EntityTypeManager $entity_type_manager) {
    $this->currentUser = $current_user;
    $this->messenger = $messenger;
    $this->logger = $logger->get('book_content');
    $this->dateFormatter = $date_formatter;
    $this->languageManager = $language_manager;
    $this->entityTypeManager = $entity_type_manager;
    $this->batchBuilder = new BatchBuilder();

  }

  /**
   * Method to Delete Book Content.
   */
  public function deleteBooks() {

    $language = $this->languageManager->getCurrentLanguage()->getId();

    $nids = [];
    $storage = $this->entityTypeManager->getStorage('node');
    $query = $storage->getQuery();
    $query->condition('status', NodeInterface::PUBLISHED);
    $query->condition('type', 'book');
    $nids = $query->execute();

    try {

      $this->batchBuilder
        ->setTitle($this->t('Processing Batch...'))
        ->setInitMessage($this->t('Initializing Batch...'))
        ->setProgressMessage($this->t('Completed @current of @total. Estimated remaining time: @estimate. Elapsed time : @elapsed.'))
        ->setErrorMessage($this->t('An error has occurred.'));
      $this->batchBuilder->addOperation([$this, 'processItems'], [$nids]);
      $this->batchBuilder->setFinishCallback([$this, 'finished']);
      batch_set($this->batchBuilder->toArray());

      return $this->t('Books Deleted Successfully.');
    }
    catch (Exception $e) {

      $this->logger->error('Encountered the following error while trying to delete the book content: @error',
        [
          '@error' => $e,
        ]
      );
      return $this->t('Books Deletion Failed.');

    }
  }

  /**
   * Processor for batch operations.
   */
  public function processItems($nids, array &$context) {

    // Elements per operation.
    $limit = 50;

    // Set default progress values.
    if (empty($context['sandbox']['progress'])) {
      $context['sandbox']['progress'] = 0;
      $context['sandbox']['max'] = count($nids);
    }

    // Save items to array which will be changed during processing.
    if (empty($context['sandbox']['items'])) {
      $context['sandbox']['items'] = $nids;
    }

    $counter = 0;
    if (!empty($context['sandbox']['items'])) {
      // Remove already processed items.
      if ($context['sandbox']['progress'] != 0) {
        array_splice($context['sandbox']['items'], 0, $limit);
      }

      foreach ($context['sandbox']['items'] as $nid) {
        if ($counter != $limit) {
          $this->processItem($nid);
          $counter++;
          $context['sandbox']['progress']++;

          $context['message'] = $this->t('Now Processing Data Item :progress of :count', [
            ':progress' => $context['sandbox']['progress'],
            ':count' => $context['sandbox']['max'],
          ]);

          // Increment total processed item values. Will be used in finished
          // callback.
          $context['results']['processed'] = $context['sandbox']['progress'];
        }
      }
    }

    // If not finished all tasks, we count percentage of process. 1 = 100%.
    if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
      $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
    }
  }

  /**
   * Process single item.
   *
   * @param int $nid
   *   Node ID Book Content.
   */
  public function processItem(int $nid) {

    if (!empty($nid)) {

      $node = Node::load($nid);
      if (!empty($node)) {
        // $node->delete();
      }

      $this->messenger->addMessage($this->t('Book Content with nid : @nid deleted.',
          [
            '@nid' => $nid,
          ])
        );

      $this->logger->notice($this->t('Book Content with nid : @nid deleted.',
          [
            '@nid' => $nid,
          ])
        );

    }

  }

  /**
   * Finished callback for batch.
   */
  public function finished($success, $results, $operations) {
    $message = $this->t('Number of Items processed by batch: @count', [
      '@count' => $results['processed'],
    ]);

    $this->messenger->addStatus($message);
    $this->logger->info($message);
  }

}

calling the above custom service throws error when `DependencySerializationTrait` is not used.


// Delete all nodes of given content type.
  $storage_handler = \Drupal::service('book_content.batch');

  $storage_handler->deleteBooks();

🐛 Bug report
Status

Active

Version

9.5

Component
Batch 

Last updated 4 days ago

Created by

🇮🇳India bhanu951

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

Production build 0.71.5 2024