Service Configuration: Autowiring and Tags
Service Configuration: Autowiring and Tags
~15 Min. Lesezeit Zuletzt aktualisiert am August 8, 2026
Autowiring works automatically for MOST cases – this chapter covers the cases where manual configuration becomes necessary, and the central services.yaml file behind it.
Understanding config/services.yaml
services:
_defaults:
autowire: true
autoconfigure: true
App\:
resource: '../src/'
exclude:
- '../src/DependencyInjection/'
- '../src/Entity/'
- '../src/Kernel.php'This default configuration (already set up by Symfony Flex at install time) AUTOMATICALLY registers EVERY class under src/ as a service – EXACTLY why ProjectStatsService from chapter 33 worked WITHOUT manual registration. exclude excludes directories that contain NO services (entities are pure data objects, not kernel bootstrap code).
autowire and autoconfigure in detail
| Option | Meaning |
|---|---|
| autowire: true | Constructor parameters get resolved AUTOMATICALLY by their type (EXACTLY what we've used since chapter 8). |
| autoconfigure: true | Certain interfaces/base classes get AUTOMATICALLY given matching "tags" – e.g. an event subscriber (chapter 37) gets automatically recognized as such, with no manual registration. |
When autowiring ISN'T enough: multiple implementations
If SEVERAL classes implement the SAME interface, Symfony CAN'T automatically decide which one is meant:
interface NotificationChannelInterface
{
public function send(string $message): void;
}
class EmailChannel implements NotificationChannelInterface { /* ... */ }
class SlackChannel implements NotificationChannelInterface { /* ... */ }If a service constructor requests NotificationChannelInterface $channel, Symfony throws an error: "ambiguous service". Solution: EXPLICIT binding in services.yaml, via a named argument:
services:
App\Service\AlertService:
arguments:
$channel: '@App\Service\Notification\EmailChannel'$channel MUST exactly match the constructor's parameter name, @ references a service by its ID (usually the full class name).
Injecting scalar values via bind
For configuration values (not objects), like a maximum number of tasks per project:
services:
_defaults:
autowire: true
autoconfigure: true
bind:
$maxTasksPerProject: '%env(int:MAX_TASKS_PER_PROJECT)%'class ProjectLimitService
{
public function __construct(
private readonly int $maxTasksPerProject,
) {
}
}%env(int:...)% reads the environment variable (EXACTLY like DATABASE_URL in chapter 4), int: automatically converts the string value to int. bind in _defaults applies to ALL services whose constructor has a parameter with EXACTLY this name – handy for widely used configuration values.
Tags: Symfony's plugin mechanism
Tags mark services as an "extension of a specific system" – with autoconfigure: true, this MOSTLY happens automatically via interface detection (event subscribers in chapter 37, voters from chapter 29 e.g. get automatically recognized via the security.voter tag), manual tagging is only needed for your own, more advanced extension points.
Tipp: Rule of thumb for this whole chapter: rely on automatic autowiring/autoconfigure AS MUCH AS POSSIBLE – manual services.yaml entries ONLY when Symfony actually throws an "ambiguous service" error or a scalar value needs to be injected.