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\RequestStackare 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: trueOr globally:
services:
_defaults:
autoconfigure: true
autowire: true
webprofiler.profiler_listener:
class: Drupal\webprofiler\EventListener\ProfilerListenerUnlike 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


