Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Injecting parameter based service into other service

I have a service which takes a driver to do the actual work. The driver itself is within the context of Symfony 2 is just another service.

To illustrate a simplified version:

services:
  # The driver services.
  my_scope.mailer_driver_smtp:
    class: \My\Scope\Service\Driver\SmtpDriver

  my_scope.mailer_driver_mock:
    class: \My\Scope\Service\Driver\MockDriver

  # The actual service.
  my_scope.mailer:
    class: \My\Scope\Service\Mailer
    calls:
      - [setDriver, [@my_scope.mailer_driver_smtp]]

As the above illustrates, I can inject any of the two driver services into the Mailer service. The problem is of course that the driver service being injected is hard coded. So, I want to parameterize the @my_scope.mailer_driver_smtp.

I do this by adding an entry to my parameters.yml

my_scope_mailer_driver: my_scope.mailer_driver_smtp

I can then use this in my config.yml and assign the parameter to the semantic exposed configuration [1]:

my_scope:
  mailer:
    driver: %my_scope_mailer_driver%

In the end, in the Configuration class of my bundle I set a parameter onto the container:

$container->setParameter('my_scope.mailer.driver', $config['mailer']['driver'] );

The value for the container parameter my_scope.mailer.driver now equals the my_scope.mailer_driver_smtp that I set in the parameters.yml, which is, as my understanding of it is correct, just a string.

If I now use the parameter name from the container I get an error complaining that there is no such service. E.g:

services:
  my_scope.mailer:
    class: \My\Scope\Service\Mailer
    calls:
      - [setDriver, [@my_scope.mailer.driver]]

The above will result in an error:

[Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException]                                          
The service "my_scope.mailer" has a dependency on a non-existent service "my_scope.mailer.driver"

The question now is, what is the correct syntax to inject this container parameter based service?

[1] http://symfony.com/doc/current/cookbook/bundles/extension.html

like image 332
Luke Avatar asked Mar 13 '14 00:03

Luke


People also ask

Can you inject a service into another service?

Yes, the first thing is to add the @Injectable decorator on each services you want to inject. In fact, the Injectable name is a bit insidious. It doesn't mean that the class will be "injectable" but it will decorate so the constructor parameters can be injected.

Can you inject a service into a service Angular?

Angular provides the ability for you to inject a service into a component to give that component access to the service. The @Injectable() decorator defines a class as a service in Angular and allows Angular to inject it into a component as a dependency.

Can we use service without injectable?

If you want to use angular services (and Http is an angular service) you must inject them as I told above as a constructor attribute to another service / component , which means if you want to use Http you need to have your service injectable. So the answer is no, you can't do it in a nice way.


1 Answers

This question has a similar answer here

I think the best way to use this kind of definition is to use service aliasing.
This may look like this

Acme\FooBundle\DependencyInjection\AcmeFooExtension

public function load(array $configs, ContainerBuilder $container)
{
    $configuration = new Configuration;
    $config = $this->processConfiguration($configuration, $configs);

    $loader = new Loader\YamlFileLoader(
        $container,
        new FileLocator(__DIR__.'/../Resources/config')
    );
    $loader->load('services.yml');

    $alias = $config['mailer']['driver'];
    $container->setAlias('my_scope.mailer_driver', $alias);
}

This will alias the service you've defined in my_scope.mailer.driver with my_scope.mailer_driver, which you can use as any other service

services.yml

services:
    my_scope.mailer_driver:
        alias: my_scope.mailer_driver_smtp # Fallback

    my_scope.mailer_driver_smtp:
        class: My\Scope\Driver\Smtp

    my_scope.mailer_driver_mock:
        class: My\Scope\Driver\Mock

    my_scope.mailer:
        class: My\Scope\Mailer
        arguments:
            - @my_scope.mailer_driver

With such a design, the service will change whenever you change the my_scope.mailer_driver parameter in your config.yml.
Note that the extension will throw an exception if the service doesn't exist.

like image 56
Touki Avatar answered Nov 09 '22 18:11

Touki