Back to Blog
May 2nd, 2015

Symfony Service Expressions: Do things you thought Impossible

Written by weaverryan, and Leannapelham

Edit
Symfony Service Expressions: Do things you thought Impossible

Did you know you can do this with Symfony's service container?

# app/config/services.yml
services:
    app_user_manager:
        class: AppBundle\Service\UserManager
        arguments:
            - "@=service('doctrine').getRepository('AppBundle:User')"
            # ... other arguments

The @= means that you're using Symfony's Expression Language, which let's you mix dynamic logic into your normally-static service definitions.

Normally, if you want to inject a repository, you need to register it as a service first, using a factory. And while that's fine (and probably better if you're injecting the factory a lot), using the expression language is well, kinda cool.

In the compiled container, it beautifully generates with exactly the code you would've used directly::

// app/cache/appDevDebugProjectContainer.php
protected function getAppUserManagerService()
{
    return $this->services['app_user_manager'] = new AppBundle\Service\UserManager(
        $this->get('doctrine')->getRepository('AppBundle:User')
    );
}

And from an object-oriented, dependency-injection perspective, that feels good, and makes up for the slightly-wonky service expression syntax. The expression language isn't new, but I think a lot of people don't really know its power.

Passing Database Configuration as a Service Argument

How about reading configuration from the database and passing those as arguments? Normally, you'd need create a service that reads the configuration and inject the whole thing in:

services:
    # some service that can read configuration values from the database
    app_configuration_reader:
        class: AppBundle\Config\ConfigurationReader
        arguments: ["@doctrine.orm_entity_manager"]

    # some service that helps email things
    app_mailer:
        class: AppBundle\Service\Mailer:
        arguments:
            - "@mailer"
            - "@app_configuration_reader"

Suppose our app_mailer service needs some database configuration value called email_from_username, which it uses as the from address when sending emails. To accomplish this, you'd usually need inject the entire app_configuration_reader service (the service you create to read config values). But with expressions, you can inject only what you need:

    services:
        # some service that can read configuration values from the database
        app_configuration_reader:
            class: AppBundle\Config\ConfigurationReader
            arguments: ["@doctrine.orm_entity_manager"]

        # some service that helps email things
        app_mailer:
            class: AppBundle\Service\Mailer:
            arguments:
                - "@mailer"
                - "@=service('app_configuration_reader').get('email_from_username')"

And in addition to the service() function you also have a parameter function, and all the normal syntax (including if statement logic) from the Expression Language. See Using the Expression Language for a few more details about using it with services.

Now, go do something cool with this :).

9 Comments

Sort By
Login or Register to join the conversation
Default user avatar Edison 5 years ago

Interesting! That's Powerful.

41 | Reply |
Default user avatar ivorobioff 5 years ago

stupid symfony with its stupid yaml... why making life harder by limiting configuration with yml??? what's so bad in plain php arrays???

| Reply |

Hey ivorobioff ,

I'm sorry that you upset. But what do you mean about "limiting configuration with yml"? If we're talking about plain PHP arrays - there's no limitation, you can do the same in YAML, but YAML is read clearer because you don't need to worry about opening and closing brackets, semi-colons at the end, arrow operators or reserved words like 'array()'. I don't think YAML makes our life harder, maybe just in some odd cases, but there's always a trade-off. Anyway, it IS possible to use XML or even plain PHP files instead of YAML for your configuration, and you can find these alternative configuration examples in Symfony docs.

Cheers!

1 | Reply |
Default user avatar Александр Ерин ivorobioff 4 years ago

Arrays are an old approach, no limites with yml

| Reply |
tomchkkk avatar tomchkkk 5 years ago

The Symfony documentation doesn't go into great detail about how to use Expression Language as an argument to a service. This article was helpful, but it could perhaps go further into the topic and explain it in more detail.

| Reply |

Hey Tom!

Sure :). What would you like to know - what parts are still not so clear?

Cheers!

| Reply |

When I stumbled across this article, I had been trying to find a way to conditionally pass an argument into a service, depending on the current environment. I knew I could have used "%kernel.environment%" as the argument and then tested it inside the service class, but the test wouldn't have been relevant to the class.

Helpfully for me, you mention that using `@=` invokes Symfony's Expression Language; from pouring over the Symfony docs, I'd had the impression that you could only use `@=parameter()`, `@=service()`, or `@=container`. But I found that you could write something (essentially useless) like `@=true ? 'yes' : 'no'`. That led me to a better understanding of this concept and I ended up doing something like `@=parameter('kernel.environment') === 'prod' ? true : false`.

So your article really helped me, thanks. But still I just thought it could cover this topic a bit more than the actual Symfony docs do, and give a few more examples of what is possible using each of the methods provided.

2 | Reply |

Thanks for sharing Tom - this makes great sense, and I bet your comment (at the very least) will help some other people - the expression language indeed is more flexible than I'm really showing here!

| Reply |
Default user avatar Alexander Volochnev 5 years ago

Cool!

| Reply |

Delete comment?

Share this comment

astronaut with balloons in space

"Houston: no signs of life"
Start the conversation!