Every service you've built so far in this section is something you ask for by name. A tag flips that relationship around: it lets your service announce itself to some other part of Drupal core — "notify me when an event fires," "collect me into your list of plugins" — without that other system ever needing to know your service's name in advance.
What you'll learn in this lesson
- What a service tag actually does, using the real
event_subscribertag as the example - How Drupal core's compiler passes discover every tagged service automatically
- Why tagging avoids a whole category of hardcoded service-name lists
A real tagged service
Here's the complete events_example.services.yml from Drupal's Examples module:
services:
events_example_subscriber:
class: Drupal\events_example\EventSubscriber\EventsExampleSubscriber
tags:
- {name: event_subscriber}
That's the entire registration. No controller, no form, nothing else in the whole module ever references events_example_subscriber by name — and yet its getSubscribedEvents() method starts firing the moment the tagged events it's listening for occur, anywhere in Drupal.
How the tag actually gets discovered
Behind the scenes, Drupal's event dispatcher service runs a "compiler pass" during container building: it asks the container for every single service tagged event_subscriber, and registers each one with itself automatically. Your module doesn't dispatch events to your subscriber directly — you just tag it, and the dispatcher's own compiler pass does the connecting for you.
Tags aren't only for events
The exact same mechanism powers plenty of other core systems: {name: cache.bin} registers a service as a cache backend, {name: breadcrumb_builder} registers a breadcrumb builder that gets consulted on every page, {name: access_check, applies_to: _my_custom_requirement} hooks a service into route access checking. In every case, the pattern is identical: a compiler pass collects everything tagged with a given name and does something useful with the whole collection.
priority key — {name: event_subscriber, priority: 100} — that determines the order tagged services get invoked in when several are tagged the same way. Higher numbers generally run earlier. Without an explicit priority, order isn't guaranteed, which matters the moment two subscribers both react to the same event.Quick check: if you wrote a second, completely separate module with its own service tagged
event_subscriber, would you need to modifyevents_example's code to make both subscribers fire? (Answer: no — that's the entire point of tagging. The event dispatcher's compiler pass collects every tagged service from every enabled module automatically; neither module needs to know the other exists.)
Key takeaways
- A tag lets a service register itself with a core subsystem without either side hardcoding the other's name.
- A compiler pass is what actually collects tagged services — you never write that discovery logic yourself.
event_subscriberis the most common tag you'll use, but the same pattern (cache.bin,breadcrumb_builder,access_check, and more) shows up throughout Drupal core.- Some tags accept a
prioritykey to control invocation order when multiple services share the same tag.
Coming up next
You've now covered the complete arc of services and dependency injection — defining, wiring, building the class, and injecting into controllers, plugins, and tag-based systems. Next, we shift from code-level architecture to configuration: how to define a schema that describes exactly what shape your module's settings take.