Solving the LogicException: The database connection is not serializable

Drupal 10

Recently, working on altering the node form, I encountered an issue, which sounds like:

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.

It's clear from the error message that I'm trying to serialize an object that holds a reference - directly or indirectly - to the database connection or other services that depend on it. Since Drupal's database connection isn't serializable, this causes the error.

Understanding the Problem

Most Drupal services (e.g database connection, entity manager and so on) are complex objects with live resources, such as open DB connections or request contexts, and they can’t be safely serialized. And, if your custom class contains a such kind of services as a property, and this class is serialized, you'll get the errors:

The database connection is not serializable

Drupal serializes data in a few cases: Form API, Queue API, Cache API, Sessions.

That's why some Drupal classes, especially Form and certain Plugin or Queue items, will be serialized as part of Drupal’s infrastructure:

  • Form Classes: Stores the entire form structure and $form_state in cache.
  • Plugin Classes (PluginBase, BlockBase, FieldFormatter and so on): They are often cached or passed into subsystems that store them in state or config.
  • Queue Workers: Store tasks for later processing.

Temporary solution: DependencySerializationTrait

As the error message suggests, you can apply the temporary solution by using the DependencySerializationTrait. This trait helps manage the serialization of objects with dependencies.

use Drupal\Core\DependencyInjection\DependencySerializationTrait;
 
/**
 * Defines an example class.
 */
class ExampleClass {

  use DependencySerializationTrait;
  
}

This approach is widely used in Drupal core, e.g you can find it in Drupal\Core\Config\DatabaseStorage, Drupal\Core\Form\FormBase, Drupal\Core\Plugin\PluginBase, Drupal\Core\Queue\DatabaseQueue, etc.

DependencySerializationTrait excludes service dependencies from being serialized in __sleep() and reinstates them when the object is unserialized in __wakeup().

Store a service container reference and retrieve dependency on-demand

Another common approach is to store a reference to the service container and pull in dependencies only when they're needed.

Before:

use Drupal\Core\Database\Connection;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Defines an example class.
 */
class ExampleClass implements ContainerInjectionInterface {

  /**
   * The database connection.
   */
  protected Connection $database;
  
  /**
   * Constructs a ExampleClass.
   *
   * @param \Drupal\Core\Database\Connection $database
   *   A database connection.
   */
  public function __construct(Connection $database) {
    $this->database = $database;
  }

}

After:

use Drupal\Core\Database\Connection;

/**
 * Defines an example class.
 */
class ExampleClass {

  /**
   * The database connection.
   */
  protected ?Connection $database = NULL;
  
  /**
   * Gets the database connection.
   *
   * @return \Drupal\Core\Database\Connection
   *   The database connection object.
   */
  public function getDatabase(): Connection {
    if (!$this->database) {
      $this->database = \Drupal::database();
    }
    return $this->database;
  }

}

You can see this pattern used in class Drupal\Core\Controller\ControllerBase, for example.