New in Symfony 4.1: Getting container parameters as a service
February 1, 2018 • Published by Javier Eguiluz
Warning: This post is about an unsupported Symfony version. Some of this information may be out of date. Read the most recent Symfony Docs.
Contributed by
Nicolas Grekas
in #25288.
Symfony applications using autowiring can remove most of their service
configuration and rely on the injection of services based on argument type-hinting.
The main exception to this feature are scalar arguments, such as a service
requiring the value of the kernel.project_dir
parameter in its constructor.
The solution is simple thanks to argument binding, which lets you define scalar arguments once and apply them to all the services defined in that same config file:
1 2 3 4 5 6
# config/services.yaml
services:
# ...
_defaults:
bind:
$projectDir: '%kernel.project_dir%'
However, if some particular service needs lots of container parameters (or even
all of them, for some edge case feature) using argument binding is cumbersome.
In Symfony 4.1 we added a feature to get all container parameters as a service.
You just need to type-hint the argument with the ParameterBagInterface
class
or the new ContainerBagInterface
class (which is compatible with the PSR-11 standard):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
// src/Service/MessageGenerator.php
// ...
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
class MessageGenerator
{
private $params;
public function __construct(ParameterBagInterface $params)
{
$this->params = $params;
}
public function someMethod()
{
$parameterValue = $this->params->get('parameter_name');
// ...
}
}
Help the Symfony project!
As with any Open-Source project, contributing code or documentation is the most common way to help, but we also have a wide range of sponsoring opportunities.
Comments are closed.
To ensure that comments stay relevant, they are closed for old posts.
You guys are right - certainly the best option is to use the already-existing functionality and inject the *specific* parameter you need :). To give a bit more background, this was needed internally so that $this->getParameter() could work in the new AbstractController. That base controller class is not passed the entire container - just a smaller "locator" with the services it needs. So, you couldn't access the parameters. This allows $this->getParameter() to work.
Another example usage is a service "configurator" - a class whose sole purpose is to help configure *other* services. The configurator may need to be able to dynamically select certain parameters so that it can just inject the one it needs into the service it's configuring :).
Cheers!
As long as it will be optional I am fine with it, but please please do not force it and keep symfony optionanted ;)
Or even allow the ServiceLocater to locate parameters as well. Never did understand why you could not just Container::get('some_parameter'); I'm sure there are reasons.
Like a lot of people, I try to write code that stays "framework agnostic" and injecting the whole ParameterBag makes me feel like my service become dependent on the whole container.
But like said before, that's a great feature for the people who need it.
Its not the perfect solution because you can always get into the "service locator" trap as mentioned already by others, BUT its definitively a much elegant solution than having to inject this: "@=service('kernel').getContainer().getParameterBag()" or other similar workarounds...
We have to appreciate that there's an ongoing effort from the core contributors to improve the general architecture and to promote decoupling.
As always we all can contribute to symfony and push it in the right direction, keep that in mind!
your_service_params:
class: 'Symfony\Component\DependencyInjection\ParameterBag\ParameterBag'
calls:
- ['add', [{parameter_name: '%kernel.project_dir%'}]]
and inject @your_service_params. Not as elegant though!