196

I have added a setting to my config.yml file as such:

app.config:
    contact_email: somebody@gmail.com
    ...

For the life of me, I can't figure out how to read it into a variable. I tried something like this in one of my controllers:

$recipient =
$this->container->getParameter('contact_email');

But I get an error saying:

The parameter "contact_email" must be defined.

I've cleared my cache, I also looked everywhere on the Symfony2 reloaded site documentation, but I can't find out how to do this.

Probably just too tired to figure this out now. Can anyone help with this?

nbro
  • 15,395
  • 32
  • 113
  • 196
josef.van.niekerk
  • 11,941
  • 20
  • 97
  • 157

7 Answers7

194

Rather than defining contact_email within app.config, define it in a parameters entry:

parameters:
    contact_email: somebody@gmail.com

You should find the call you are making within your controller now works.

Douglas Greenshields
  • 2,511
  • 1
  • 17
  • 8
  • 4
    How would this work with the Dev / Prod environments? So for testing I want the emails to send to a test email and production would get another email – Phill Pafford May 29 '12 at 17:07
  • 2
    @Phill: If you are using the standard swiftmailer in your symfony2, you can use the following setting in your config_dev.yml: `swiftmailer: delivery_address: dev@example.com` You can find more information in the [Symfony2 cookbook](http://symfony.com/doc/current/cookbook/email/dev_environment.html) – Pierre May 30 '12 at 13:02
  • 4
    Should I inject container class everywhere(controller, entity, class) when I use this statement **$this->container->getParameter('contact_email');**? or is there a simpler way to do so without injecting container class? – webblover Sep 24 '14 at 18:10
  • 1
    According to this solution how can I access to nested properties ? – Ousmane Dec 10 '14 at 15:13
  • 1
    @webblover Just insert the parameter itself using the `%parameter_name%` - notation (in YAML) – MauganRa Feb 02 '15 at 12:14
174

While the solution of moving the contact_email to parameters.yml is easy, as proposed in other answers, that can easily clutter your parameters file if you deal with many bundles or if you deal with nested blocks of configuration.

  • First, I'll answer strictly the question.
  • Later, I'll give an approach for getting those configs from services without ever passing via a common space as parameters.

FIRST APPROACH: Separated config block, getting it as a parameter

With an extension (more on extensions here) you can keep this easily "separated" into different blocks in the config.yml and then inject that as a parameter gettable from the controller.

Inside your Extension class inside the DependencyInjection directory write this:

class MyNiceProjectExtension extends Extension
{
    public function load( array $configs, ContainerBuilder $container )
    {
        // The next 2 lines are pretty common to all Extension templates.
        $configuration = new Configuration();
        $processedConfig = $this->processConfiguration( $configuration, $configs );
    
        // This is the KEY TO YOUR ANSWER
        $container->setParameter( 'my_nice_project.contact_email', $processedConfig[ 'contact_email' ] );

        // Other stuff like loading services.yml
    }

Then in your config.yml, config_dev.yml and so you can set

my_nice_project:
    contact_email: someone@example.com

To be able to process that config.yml inside your MyNiceBundleExtension you'll also need a Configuration class in the same namespace:

class Configuration implements ConfigurationInterface
{
    public function getConfigTreeBuilder()
    {
        $treeBuilder = new TreeBuilder();
        $rootNode = $treeBuilder->root( 'my_nice_project' );
        
        $rootNode->children()->scalarNode( 'contact_email' )->end();
        
        return $treeBuilder;
    }
}

Then you can get the config from your controller, as you desired in your original question, but keeping the parameters.yml clean, and setting it in the config.yml in separated sections:

$recipient = $this->container->getParameter( 'my_nice_project.contact_email' );

SECOND APPROACH: Separated config block, injecting the config into a service

For readers looking for something similar but for getting the config from a service, there is even a nicer way that never clutters the "paramaters" common space and does even not need the container to be passed to the service (passing the whole container is practice to avoid).

This trick above still "injects" into the parameters space your config.

Nevertheless, after loading your definition of the service, you could add a method-call like for example setConfig() that injects that block only to the service.

For example, in the Extension class:

class MyNiceProjectExtension extends Extension
{
    public function load( array $configs, ContainerBuilder $container )
    {
        $configuration = new Configuration();
        $processedConfig = $this->processConfiguration( $configuration, $configs );
        
        // Do not add a paramater now, just continue reading the services.
        $loader = new YamlFileLoader( $container, new FileLocator( __DIR__ . '/../Resources/config' ) );
        $loader->load( 'services.yml' );
        
        // Once the services definition are read, get your service and add a method call to setConfig()
        $sillyServiceDefintion = $container->getDefinition( 'my.niceproject.sillymanager' );
        $sillyServiceDefintion->addMethodCall( 'setConfig', array( $processedConfig[ 'contact_email' ] ) );
    }
}

Then in your services.yml you define your service as usual, without any absolute change:

services:
    my.niceproject.sillymanager:
        class: My\NiceProjectBundle\Model\SillyManager
        arguments: []

And then in your SillyManager class, just add the method:

class SillyManager
{
    private $contact_email;
    
    public function setConfig( $newConfigContactEmail )
    {
        $this->contact_email = $newConfigContactEmail;
    }
}

Note that this also works for arrays instead of scalar values! Imagine that you configure a rabbit queue and need host, user and password:

my_nice_project:
    amqp:
        host: 192.168.33.55
        user: guest
        password: guest

Of course you need to change your Tree, but then you can do:

$sillyServiceDefintion->addMethodCall( 'setConfig', array( $processedConfig[ 'amqp' ] ) );

and then in the service do:

class SillyManager
{
    private $host;
    private $user;
    private $password;
    
    public function setConfig( $config )
    {
        $this->host = $config[ 'host' ];
        $this->user = $config[ 'user' ];
        $this->password = $config[ 'password' ];
    }
}
starball
  • 20,030
  • 7
  • 43
  • 238
Xavi Montero
  • 9,239
  • 7
  • 57
  • 79
  • If you're wondering what's different between the first approach and the documentation, it's that the config values are converted into parameters in the `MyNiceProjectExtension->load()` method with this line: `$container->setParameter( 'my_nice_project.contact_email', $processedConfig[ 'contact_email' ]);`. Thanks Xavi! – jxmallett Jul 31 '15 at 04:55
  • Perfect answer, shame symfony doesn't let you access config the same way it does parameters. – Martin Lyne Dec 16 '15 at 14:04
  • This is a good answer, but it does expose Symfony's obtuse way of "configuring" an application. What's the point of having arbitrary environment config files when you have to write and invoke specific services to access them. Didn't somebody at Symfony sit there and realize, 'Maybe developers would actually want to provide environment specific values in their applications that they can access' Kind of the point of config files isn't it? They're following the "STKTFANREO" design pattern: "Set the knobs to F'd and rip 'em off" – eggmatters Oct 05 '16 at 22:32
  • It has several applications, specially in deploying parallelized automatic-testing, and specially when a team develops a bundle that is mainly model or logic that is consumed by several other teams in different applications, for example an application that is a user front-end, another one that is an admin-panel web front and another one that is a REST API. Each of those is a different application willing to configure diferetenly. That is multiplied by several environments (production, pre-production, testing, devel, etc.). This easily yields in 12 or 15 configurations in a single company. – Xavi Montero Oct 07 '16 at 13:28
  • @XaviMontero I followed your instruction SECOND APPROACH: and when var_dump the $this->contact_email or add an exit() in function setConfig() it doesn't exit. It looks like setConfig doesn't get called – user742736 Nov 08 '16 at 13:38
  • Some tips: 1) Did you invoke the `setConfig()` via the `addMethodCall()`? 2) `$this->contact_email` is defined by the private property. Did you set it up? 3) `$this->contact_email` does *not* have any value at the beginning of the `setConfig()` as you are, precisely, setting it from `$newConfigContactEmail` which in turn comes from `array( $processedConfig[ 'contact_email' ] )`in the `load()`. Can you `var_dump()` the `$newConfigContactEmail` in `setConfig()` and the `$processedConfig[]` in the `load()`?. Hope to help. If this gets longer let's open a chat instead of using the comments. – Xavi Montero Nov 08 '16 at 15:27
  • In fact, by yor comment "It looks like setConfig doesn't get called" maybe you should take a closer look to those two lines: `$sillyServiceDefintion = $container->getDefinition( 'my.niceproject.sillymanager' ); $sillyServiceDefintion->addMethodCall( 'setConfig', array( $processedConfig[ 'contact_email' ] ) );` and therefore trace that the `public function load( array $configs, ContainerBuilder $container )` is really being executed. Hope to help. – Xavi Montero Nov 08 '16 at 15:31
  • @XaviMontero, Thanks for the response. I have set and invoked everything. I var_dumped $sillyServiceDefintion and inside calls: there is the array with the 0 => 'setConfig', 1 => array ('value1') array( $processedConfig[ 'contact_email' ] has a value when var_dump and I have even used a array('string') to test it as well. – user742736 Nov 09 '16 at 09:17
  • Chat opened: http://chat.stackoverflow.com/rooms/127710/help-on-question-4821692 - "Avoid chatting in the comments" is a rule of the StackExchange community. Move to the chat, ping me there. – Xavi Montero Nov 09 '16 at 12:04
  • That's crazily complex for just wanting to access a config parameter. – RichieHH Aug 10 '17 at 09:07
  • This is the definition of "overengineered". – Karolis Aug 23 '20 at 09:02
36

I have to add to the answer of douglas, you can access the global config, but symfony translates some parameters, for example:

# config.yml
... 
framework:
    session:
        domain: 'localhost'
...

are

$this->container->parameters['session.storage.options']['domain'];

You can use var_dump to search an specified key or value.

Felipe Buccioni
  • 19,109
  • 2
  • 28
  • 28
16

In order to be able to expose some configuration parameters for your bundle you should consult the documentation for doing so. It's fairly easy to do :)

Here's the link: How to expose a Semantic Configuration for a Bundle

Nikola Petkanski
  • 4,724
  • 1
  • 33
  • 41
7

Like it was saying previously - you can access any parameters by using injection container and use its parameter property.

"Symfony - Working with Container Service Definitions" is a good article about it.

Michał Powaga
  • 22,561
  • 8
  • 51
  • 62
Paul Seleznev
  • 672
  • 1
  • 11
  • 24
3

I learnt a easy way from code example of http://tutorial.symblog.co.uk/

1) notice the ZendeskBlueFormBundle and file location

# myproject/app/config/config.yml

imports:
    - { resource: parameters.yml }
    - { resource: security.yml }
    - { resource: @ZendeskBlueFormBundle/Resources/config/config.yml }

framework:

2) notice Zendesk_BlueForm.emails.contact_email and file location

# myproject/src/Zendesk/BlueFormBundle/Resources/config/config.yml

parameters:
    # Zendesk contact email address
    Zendesk_BlueForm.emails.contact_email: dunnleaddress@gmail.com

3) notice how i get it in $client and file location of controller

# myproject/src/Zendesk/BlueFormBundle/Controller/PageController.php

    public function blueFormAction($name, $arg1, $arg2, $arg3, Request $request)
    {
    $client = new ZendeskAPI($this->container->getParameter("Zendesk_BlueForm.emails.contact_email"));
    ...
    }
ROMANIA_engineer
  • 54,432
  • 29
  • 203
  • 199
Dung
  • 19,199
  • 9
  • 59
  • 54
0

Inside a controller:

$this->container->getParameter('configname')

to get the config from config/config.yaml:

parameters:
   configname: configvalue