Use of Drupal module uninstall validators and lazy services

Sometimes we do not want users to uninstall a Drupal module directly, but instead want to perform some checks first.

Let's take a scenario where installing a custom module creates node types along with some fields. Later on If a user wishes to uninstall that module (assuming some content has already been created on the site using the node types that are created by this module), then uninstalling that module can cause a serious problem. It should ask the user to delete the content first that has been created on the site using these node types before uninstalling the module.

To solve such a problem, we can use module uninstall validators' lazy services and ask the user to take action before uninstalling the module.

In this example, I am going to use a module named lorem_ipsum, assuming it is already there.

First, we will define lorem_ipsum.service.yml and add the below code.

lorem_ipsum.uninstall_validator:
  class: Drupal\lorem_ipsum\LoremIpsumUninstallValidator
  tags:
  - { name: module_install.uninstall_validator }
  arguments: ['@string_translation', '@entity_type.manager']
  lazy: true

Now, create LoremIpsumUninstallValidator.php under'src' and add the below code.

namespace Drupal\lorem_ipsum;

use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Extension\ModuleUninstallValidatorInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslationInterface;

/**
 * Module uninstall validator service.
 */
class LoremIpsumUninstallValidator implements ModuleUninstallValidatorInterface {

  use StringTranslationTrait;

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

  /**
   * Constructs a new LoremIpsumUninstallValidator.
   *
   * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
   *   The string translation service.
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   The entity type manager.
   */
  public function __construct(TranslationInterface $string_translation, EntityTypeManagerInterface $entity_type_manager) {
    $this->stringTranslation = $string_translation;
    $this->entityTypeManager = $entity_type_manager;
  }

  /**
   * {@inheritdoc}
   */
  public function validate($module) {
    $reasons = [];
    if ($module == 'lorem_ipsum' && $this->doAnotherAction()) {
      $reasons[] = $this->t('There are content available for node type [@type], please delete before uninstallation.',[
        '@type' => 'your_node_type'
      ]);
    }
    return $reasons;
  }

  /**
   * Check if content available for given node_types.
   *
   * @return bool
   *   TRUE if there are content of specified node types.
   */
  protected function doAnotherAction() {
    $nodes = $this->entityTypeManager->getStorage('node')
    ->getQuery()->condition('type','your_node_type');
    retun (bool) nodes;
  }

}

Now, create a proxy service class under'src/ProxyClass' and blow the code.

namespace Drupal\lorem_ipsum\ProxyClass;

use Drupal\Core\Extension\ModuleUninstallValidatorInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\StringTranslation\TranslationInterface;

/**
 * Module uninstall validator proxy service class.
 */
class LoremIpsumUninstallValidator implements ModuleUninstallValidatorInterface  {

  use \Drupal\Core\DependencyInjection\DependencySerializationTrait;

  /**
   * The proxy service.
   *
   * @var string
   */
  protected $drupalProxyService;

  /**
   * The lazy loaded proxy service.
   *
   * @var \Drupal\lorem_ipsum\LoremIpsumUninstallValidator
   */
  protected $service;

  /**
   * The service container.
   *
   * @var \Symfony\Component\DependencyInjection\ContainerInterface
   */
  protected $container;

  /**
   * Constructs a ProxyClass Drupal proxy object.
   *
   * @param \Symfony\Component\DependencyInjection\ContainerInterface $container
   *   The container.
   * @param string $drupal_proxy_service
   *   The service ID of the original service.
   */
  public function __construct(ContainerInterface $container, $drupal_proxy_service) {
    $this->container = $container;
    $this->drupalProxyService = $drupal_proxy_service;
  }

  /**
   * Lazy loads the service from the container.
   *
   * @return object
   *   Returns the constructed service.
   */
  protected function lazyLoad() {
    if (!isset($this->service)) {
      $this->service = $this->container->get($this->drupalProxyService);
    }
    return $this->service;
  }

  /**
   * {@inheritdoc}
   */
  public function validate($module) {
    return $this->lazyLoad()->validate($module);
  }

  /**
   * {@inheritdoc}
   */
  public function setStringTranslation(TranslationInterface $translation) {
    return $this->lazyLoad()->setStringTranslation($translation);
  }

}

Clear the cache and try to uninstall the module lorem_ipsum. You should see warnings.

0/5