Skip to content

Tagging

Mauro edited this page May 27, 2022 · 2 revisions

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'))
       }
    }
}

Adding Additional Attributes on Tags

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:

YAML
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}}
JS
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.

Defining service's arguments using tags

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.

YAML
services:
  repository-manager:
    class: ./../RepositoryManager
    arguments: ["!tagged repository"]

  repository-foo:
    class: ./../RepositoryFoo
    tags:
      - { name: repository }
  
  repository-bar:
    class: ./../RepositoryBar
    tags:
      - { name: repository }