1

I have a service (for sending error alerts) which is used by multiple other services.

The alert service is injected into those classes via the relevant services.yml files and through their constructors.

However, both classes share the same instance of the service (something I actually didn't realise until now). If I set a property from one class, it's available for the other class to use later.

In some cases this will be great, but in my use case I want each class to start with a fresh instantation of the alert service class.

Is there a way, through services.yml, the constructors, or any other means, to achieve this?

6
  • 4
    Services are by design singletons - they are only ever meant to be instantiated a single time. Services provide functionality, they aren't necessarily meant for data storage, so likely you need to rethink the architecture behind how you are storing the data, and store it relative to an object, or in the class calling the service.
    – Jaypan
    Oct 15 at 18:15
  • That makes sense. Is there a non-singleton equivalent of a service that can be injected into a class?
    – Geat
    Oct 15 at 18:19
  • 2
    No, but if it wasn't a singleton, it wouldn't need to be injected, you can just instantiate a new instance of your class as needed.
    – Jaypan
    Oct 15 at 18:49
  • I prefer the idea of using a factory service, especially for classes that have a host of constructor arguments.
    – Geat
    Oct 15 at 19:01
  • 2
    In Drupal services are by default singletons (shared). But you can define a non-shared service. See symfony.com/doc/current/service_container/shared.html
    – 4k4
    Oct 15 at 20:10
2

There are a couple options that come to mind:

  1. Create a factory service that provides instances of your non-service alert class
  2. Implement your alert class as a plugin

Which one you choose really depends on your architecture.

1
  • I refactored the code, and the factory service works beautifully.
    – Geat
    Oct 15 at 18:42
3

The direct answer is quoting the Symfony documentation:

In the service container, all services are shared by default. This means that each time you retrieve the service, you’ll get the same instance. This is usually the behavior you want, but in some cases, you might want to always get a new instance.

In order to always get a new instance, set the shared setting to false in your service definition:

# config/services.yaml
services:
    App\SomeNonSharedService:
        shared: false
        # ...

https://symfony.com/doc/current/service_container/shared.html

Discussing Drupal intentions is difficult because there aren't many references. When Drupal 8 was designed this service setting didn't exist and the predecessor was back then already disputed because of the now removed request scope. I couldn't find any references that Drupal is biased against using non-shared services. In the contrary, here a discussion where core finally switched from a factory to a non-shared service, which in fact is also a factory based solution, only that this is a factory already implemented in Symfony:

https://www.drupal.org/project/drupal/issues/2660124#comment-12318542

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.