Drupal Service Container Deep Dive (Part 2): Aliases, Autowiring, and Named Arguments

This article is the second part of a series exploring the Drupal service container, focusing on aliases, autowiring, and named arguments.
Drupal Service Container Deep Dive (Part 2): Aliases, Autowiring, and Named Arguments

In the first part of this series, we explored the basics of the Drupal service container, including how services are defined and how dependencies are injected. We discussed concepts such as tags, compiler passes, service providers, and autoconfiguration. In this second part, we delve deeper into more advanced concepts such as aliases, autowiring, and named arguments.

This article was originally published on the SparkFabrik engineering blog and is republished here with permission.

Aliases

An alias in the Drupal service container is a way to create an alternative name for an existing service. This is particularly useful when you want to refer to a service by a different name without changing the original service definition.

request_stack:
  class: Symfony\Component\HttpFoundation\RequestStack
  tags:
    - { name: persist }

Symfony\Component\HttpFoundation\RequestStack: '@request_stack'

In this example, we define a service request_stack and then create an alias Symfony\Component\HttpFoundation\RequestStack that points to the same service. This allows the RequestStack service to be referenced using either name.

A service name is just a string: config.factory, entity_type.manager, Symfony\Component\HttpFoundation\RequestStack are all valid service names.

Starting from Drupal 10.1, most core services are aliased using the Fully Qualified Class Name (FQCN).

Change record: https://www.drupal.org/node/3323122

You can also define your own aliases in custom modules:

webprofiler.profiler:
  class: Drupal\webprofiler\Profiler\Profiler
  arguments:
    - '@webprofiler.file_storage'
    - '@logger.channel.webprofiler'
    - '@config.factory'

Drupal\webprofiler\Profiler\Profiler: '@webprofiler.profiler'

When a service name matches the service class FQCN, the container can automatically resolve dependencies. This capability is known as autowiring.

Autowiring

Autowiring allows automatic dependency injection based on constructor type hints. The service is instantiated normally, but required arguments are inferred from type declarations.

Autowiring can be enabled per service:

services:
  _defaults:
    autoconfigure: true

  webprofiler.profiler_listener:
    class: Drupal\webprofiler\EventListener\ProfilerListener
    autowire: true

Or globally:

services:
  _defaults:
    autoconfigure: true
    autowire: true

  webprofiler.profiler_listener:
    class: Drupal\webprofiler\EventListener\ProfilerListener

Unlike Symfony, Drupal does not automatically create aliases for single implementations. Aliases must be explicitly defined.

Multiple services for the same interface

Many services implement the same interface, such as loggers implementing Psr\Log\LoggerInterface.

use Psr\Log\LoggerInterface;

class MyService {
  public function __construct(
    private readonly LoggerInterface $logger,
  ) {}
}

In such cases, the container cannot determine which service to inject. The #[Autowire] attribute resolves this ambiguity.

use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\Attribute\Autowire;

class MyService {
  public function __construct(
    #[Autowire(service: 'logger.channel.module_name')]
    private readonly LoggerInterface $logger,
  ) {}
}

Autowiring in Controllers and Hooks

Controllers are not services. Prior to Drupal 10.2, dependency injection relied on a create() method, an example of the service locator anti-pattern.

class DashboardController extends ControllerBase {

  public function __construct(
    private readonly Profiler $profiler,
    private readonly TemplateManager $templateManager,
  ) {}

  public static function create(ContainerInterface $container): self {
    return new static(
      $container->get('webprofiler.profiler'),
      $container->get('webprofiler.template_manager'),
    );
  }
}

From Drupal 10.2 onward, ControllerBase uses AutowireTrait, enabling constructor injection.

use Drupal\Core\DependencyInjection\AutowireTrait;

class DashboardController extends ControllerBase {
  use AutowireTrait;

  public function __construct(
    private readonly Profiler $profiler,
    private readonly TemplateManager $templateManager,
  ) {}
}

Named Arguments

Named arguments allow explicit dependency mapping in YAML without modifying PHP code.

class SendMessageMiddleware {
  public function __construct(
    SendersLocatorInterface $sendersLocator,
    EventDispatcherInterface $eventDispatcher = null,
  ) {}
}
messenger.middleware.send_message:
  class: Symfony\Component\Messenger\Middleware\SendMessageMiddleware
  arguments:
    $eventDispatcher: '@event_dispatcher'

Named arguments work alongside autowiring to provide precise control when needed.

Conclusion

Aliases, autowiring, and named arguments together modernize dependency injection in Drupal, reducing boilerplate while improving maintainability.

  • Aliases improve clarity and refactoring safety
  • Autowiring resolves dependencies automatically
  • Named arguments provide explicit overrides

Note: The vision of this web portal is to help promote news and stories around the Drupal community and promote and celebrate the people and organizations in the community. We strive to create and distribute our content based on these content policy. If you see any omission/variation on this please reach out to us at #thedroptimes channel on Drupal Slack and we will try to address the issue as best we can.

Related Organizations

Upcoming Events

Latest Opportunities