2

After upgrading to Drupal 10, running the coding standards check suggested that I could use DI in my validation constraints.

For example, this simple one:

<?php

declare(strict_types=1);

namespace Drupal\my_module\Plugin\Validation\Constraint;

use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;

/**
 * Validates the DateTimeMidnightOnly constraint.
 */
class DateTimeMidnightOnlyValidator extends ConstraintValidator {

  /**
   * {@inheritdoc}
   */
  public function validate(mixed $value, Constraint $constraint): void {
    foreach ($value as $item) {
      if (!$this->isMidnight($item->value)) {
        $item_value = $item->value;
        \Drupal::logger('my_custom_error_channel')->error("Invalid midnight timestamp! $item_value");
        $this->context->addViolation($constraint->notMidnight, ['%value' => $item_value]);
      }
    }
  }

  /**
   * Is the DateTime string midnight?
   *
   * @param string $value
   *   The DateTime string.
   *
   * @returns bool
   *   TRUE if it is a midnight string.
   */
  private function isMidnight(string $value): bool {
    return str_ends_with($value, 'T00:00:00');
  }

}

So I tried injecting the logger:

  /**
   * Logger factory.
   *
   * @var \Drupal\Core\Logger\LoggerChannelFactoryInterface
   */
  protected LoggerChannelFactoryInterface $loggerFactory;

  /**
   * Constructs DateTimeMidnightOnlyValidator.
   *
   * @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger_factory
   *   The logger factory.
   */
  public function __construct(LoggerChannelFactoryInterface $logger_factory) {
    $this->loggerFactory = $logger_factory;
  }

This results in an error:

ArgumentCountError: Too few arguments to function Drupal\custom_module\Plugin\Validation\Constraint\DateTimeMidnightOnlyValidator::__construct(), 0 passed in /var/www/html/web/core/lib/Drupal/Core/DependencyInjection/ClassResolver.php on line 31 and exactly 1 expected

So I need to pass the argument-- how do I do that? For custom services I know I can use mymodule.services.yml, and for totally overriding core services I can use MyModuleServiceProvider.php, but what about validation constraints?

1 Answer 1

5

To pass the argument implement ContainerInjectionInterface::create().

For example:

class TaxonomyTermHierarchyConstraintValidator extends ConstraintValidator implements ContainerInjectionInterface {

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

  /**
   * Creates a new TaxonomyTermHierarchyConstraintValidator instance.
   *
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   The entity type manager.
   */
  public function __construct(EntityTypeManagerInterface $entity_type_manager) {
    $this->entityTypeManager = $entity_type_manager;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('entity_type.manager')
    );
  }

...

Example from /core/modules/taxonomy/src/Plugin/Validation/Constraint/TaxonomyTermHierarchyConstraintValidator.php

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.