-
Notifications
You must be signed in to change notification settings - Fork 35
Tagging
Services configured in your container can also be tagged. In the service container, a tag implies that the service is meant to be used for a specific purpose.
import UserRepository from './Entity/UserRepository'
import {ContainerBuilder, Definition} from 'node-dependency-injection'
let container = new ContainerBuilder()
let definition = new Definition(UserRepository)
definition.addTag('repository')
container.setDefinition('app.entity.user_repository', definition)
Tags, then, are a way to tell to your app that your service should be registered or used in some special way by the app.
Tags on their own don't actually alter the functionality of your services in any way. But if you choose to, you can ask a container builder for a list of all services that were tagged with some specific tag. This is useful in compiler passes where you can find these services and use or modify them in some specific way.
For example:
// ./Entity/UserRepository
class UserRepository {
constructor (someManager) {
this._someManager = someManager
}
// ...
}
// ./Entity/AccountRepository
class AccountRepository {
constructor (someManager) {
this._someManager = someManager
}
// ...
}
So instead of injecting someManager for every repository we can use tags for this purpose.
services:
app.service.some_service:
class: ./Service/SomeService
app.entity.user_repository:
class: ./Entity/UserRepository
tags:
- { name: repository }
app.entity.account_repository:
class: ./Entity/AccountRepository
tags:
- { name: repository }
You can now use a compiler pass to ask the container for any services with the repository tag:
import {Reference} from 'node-dependency-injection'
class RepositoryPass {
/**
* @param {ContainerBuilder} container
*/
async process (container) {
let taggedServices = container.findTaggedServiceIds('repository')
for (let [id, definition] of taggedServices) {
definition.addArgument(new Reference('app.service.some_service'))
}
}
}
Sometimes you need additional information about each service that's tagged with your tag. For example, you might want to add an event to each listener.
To begin with, change the services configuration file:
services:
app.listener.user_listener:
class: ./Listener/UserListener
tags:
- {name: listener, attributes: {event: postUpdate}}
app.listener.account_listener:
class: ./Listener/AccountListener
tags:
- {name: listener, attributes: {event: prePersist}}
let definition = new Definition(SomeObject)
let attributes = new Map()
attributes.set('event', 'prePersist')
definition.addTag('listener', attributes)
container.setDefinition('app.listener', definition)
Notice that you've added a generic event key to the tag. To actually use this, update the compiler:
class ListenerPass {
/**
* @param {ContainerBuilder} container
*/
process (container) {
let taggedServices = container.findTaggedServiceIds('listener')
for (let definition of taggedServices) {
for (let tag of definition.tags) {
definition.addMethodCall(tag.attributes.get('event'))
}
}
}
}
The double loop may be confusing. This is because a service can have more than one tag. You tag a service twice or more with the listener
tag. The second for loop iterates over the listener
tags set for the current service and gives you one tag object.
You can define the arguments of the services using tags. This is very useful for situations like initializing server components, such as repositories, controllers, etc.
services:
repository-manager:
class: ./../RepositoryManager
arguments: ["!tagged repository"]
repository-foo:
class: ./../RepositoryFoo
tags:
- { name: repository }
repository-bar:
class: ./../RepositoryBar
tags:
- { name: repository }
Copyright © 2023-2024 Mauro Gadaleta