How can I dynamically set a parameter in Symfony2?

Gottlieb Notschnabel picture Gottlieb Notschnabel · Apr 18, 2014 · Viewed 17.5k times · Source

I'm trying to dynamically set a parameter in Symfony2 (that I cannot statically set in my parameters.yml file). My approach is to use an EventListener:

namespace Acme\AcmeBundle\EventListener;

use Symfony\Component\DependencyInjection\Container;

class AcmeListener
{
    private $container;

    public function __construct(Container $container)
    {
        $this->container = $container;
    }

    public function onKernelRequest()
    {
        // Dynamically fetch $bar
        $bar = fetch('foobar');

        // Set parameter
        $this->container->setParameter('foo', $bar);
    }
}

And my service definition in config.yml looks like this:

service:
    kernel.listener.acme_listener:
        class: Acme\AcmeBundle\EventListener\AcmeListener
        tags:
            - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
        arguments: [ '@service_container' ]

The problem is, I get an exception:

LogicException: Impossible to call set() on a frozen ParameterBag.

How can I work around this exception or do you see another way to dynamically set a parameter?

Answer

Marino Di Clemente picture Marino Di Clemente · Apr 18, 2014

The container parameters rule is that:

You can only set a parameter before the container is compiled

How to remedy the problem depends on your needs with the premise that the container is not thought to have dynamic parameters.

  1. create you custom dynamic "options" service and inject it in other services, in this way you can also manage your parameters in database (like wordpress wp_options), but i don't know a bundle that do this. For existing services (ex. mailer) you can use configurators.

  2. invalidate the cache when parameters changes here an easy method so when you reload the page the container is rebuilt. If the parameters change frequently risks to reload the cache frequently and this becomes a problem if you have large loads.

if you choose the second option you need to set the parameters before it is filled in the container, so you can:

  • export in a custom yaml file loaded in app/config/config.yml when parameters changes before you clear the cache, in this way you can get the data from other services
  • load parameters in a bundle extension here the cookbook, in this way you can't access to other services to get the data, the extension can access only to the containerbuilder

I suggest, however, option 1 (options service and configurators) because (I repeat) the container is not thought to have dynamic parameters but it offers the ability to have custom dynamic service configurators that use data from any source.