For a few hours now I've been struggling to do the most simple thing you can imagine and it just won't work. I've read tons of stackoverflow questions, read the complete Symfony documentation on configuration files and with every article or other piece of information I read, it gets harder and harder to understand.
I've created my own Bundle. Lets call it HappyBundle
. I've put this Bundle in my company's folder. So naturally I've got CompanyHappyBundle
.
I want to make a configuration file specifically for this bundle as I want it to be reusable.
As I test i created the following:
# src/Company/HappyBundle/Resources/config/config.yml
company_happy:
import:
path: /tmp
Now, what I want is to be able to use this value in my Controller. I just don't know how. It throws me the following error:
[Symfony\Component\Config\Exception\FileLoaderLoadException]
There is no extension able to load the configuration for "company_happy" (in /home/user/symfony/src/Company/HappyBundle/Resources/config/config.yml).
Looked for namespace "company_happy", found "framework", "security", "twig", "monolog", "swiftmailer", "assetic", "doctrine", "sensio_framework_extra", "debug", "web_profiler", "sensio_distribution" in /home/user/symfony/src/Company/HappyBundle/Resources/config/config.yml (which is being imported from "/home/user/symfony/app/config/config.yml").
In the config.yml I added the following:
#app/config/config.yml
imports:
- { resource: "@CompanyHappyBundle/Resources/config/config.yml" }
I've also made a Configuration class because I read somewhere this was required. I really do think this is alot of work to make just one config file.
namespace Company\HappyBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
class Configuration implements ConfigurationInterface
{
/**
* {@inheritDoc}
*/
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('company_happy');
$rootNode
->children()
->arrayNode('import')
->children()
->scalarNode('attachments_path')->defaultValue('/tmp')->end()
->scalarNode('method')->defaultValue('ALL')->end()
->booleanNode('move_mail')->defaultValue(true)->end()
->booleanNode('mark_read')->defaultValue(true)->end()
->end()
->end()
;
return $treeBuilder;
}
}
What I am actually looking for are the steps and requirements needed to get this working. The thing with symfony is that it has a million ways to do this. The documentation doesn't just give a workflow.
Can someone please help me out?
The service configurator is a feature of the service container that allows you to use a callable to configure a service after its instantiation. A service configurator can be used, for example, when you have a service that requires complex setup based on configuration settings coming from different sources/services.
Instead of defining env vars in your shell or your web server, Symfony provides a convenient way to define them inside a . env (with a leading dot) file located at the root of your project. The . env file is read and parsed on every request and its env vars are added to the $_ENV & $_SERVER PHP variables.
A Symfony bundle is a collection of files and folders organized in a specific structure. The bundles are modeled in such a way that it can be reused in multiple applications. The main application itself is packaged as a bundle and it is generally called AppBundle.
In Symfony, these useful objects are called services and each service lives inside a very special object called the service container. The container allows you to centralize the way objects are constructed. It makes your life easier, promotes a strong architecture and is super fast!
I have solved my own issue but not without trouble. I'm not at all pleased with Symfony's configuration system.
Create a file named config.yml
in src/<bundle name>/Resources/config/
yourbundle:
param_one: value_one
param_two: value_two
param_three: value_three
param_four: value_four
param_five:
subparam_one: subvalue_one
subparam_two: subvalue_two
subparam_three: subvalue_three
subparam_four: subvalue_four
Go to app/config/config.yml
and add:
#app/config/config.yml
imports:
- { resource: "@YourBundle/Resources/config/config.yml" }
Create a file named Configuration.php
in src/<bundle name>/DependencyInjection/
namespace YourBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
class Configuration implements ConfigurationInterface
{
/**
* {@inheritDoc}
*/
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('yourbundle');
$rootNode
->children()
->scalarNode('param_one')->defaultValue('value_one')->end()
->scalarNode('param_two')->defaultValue('value_two')->end()
->scalarNode('param_three')->defaultValue('value_three')->end()
->scalarNode('param_four')->defaultValue('value_four')->end()
->arrayNode('param_five')
->children()
->scalarNode('subparam_one')->defaultValue('subvalue_one')->end()
->scalarNode('subparam_two')->defaultValue('subvalue_two')->end()
->scalarNode('subparam_three')->defaultValue('subvalue_three')->end()
->scalarNode('subparam_four')->defaultValue('subvalue_four')->end()
->end()
->end()
;
return $treeBuilder;
}
}
Last but not least, you'll have to create an extension. Create a file <yourbundle>Extension.php
in src/<your bundle>/DependencyInjection/
namespace YourBundle\DependencyInjection;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
class YourbundleExtension extends Extension
{
/**
* @var ContainerBuilder
*/
protected $container;
/**
* {@inheritDoc}
*/
public function load(array $configs, ContainerBuilder $container)
{
$this->container = $container;
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
foreach ($config as $key => $value) {
$this->parseNode('yourbundle.'.$key, $value);
}
$container->setParameter('yourbundle', $config);
}
/**
* @param string $name
* @param mixed $value
*
* @throws \Exception
*/
protected function parseNode($name, $value)
{
if (is_string($value)) {
$this->set($name, $value);
return;
}
if (is_integer($value)) {
$this->set($name, $value);
return;
}
if (is_array($value)) {
foreach ($value as $newKey => $newValue) {
$this->parseNode($name.'.'.$newKey, $newValue);
}
return;
}
if (is_bool($value)) {
$this->set($name, $value);
return;
}
throw new \Exception(gettype($value).' not supported');
}
/**
* @param string $key
* @param mixed $value
*/
protected function set($key, $value)
{
$this->container->setParameter($key, $value);
}
}
All these steps are required just to be able to call a configuration parameter specific for your bundle.
If any of you know any way to do this easier, feel free to post an answer or comment.
A few notices:
In config.yml, you are trying to define import
as array. It seems like symfony doesn't allow creating array elements in the root of your config, meaning that you have to nest arrays deeper down the tree. So you can not do:
company_happy:
import:
path: /tmp
another_import:
...
I am not sure this is exactly what you were trying to do, but you defined import
as array, which makes me assume so.
On the other hand, you can do:
company_happy:
imports:
import:
path: /tmp
another_import:
...
Regarding no extension able to load the configuration error: Make sure your extension file is following naming convetions.It should be called CompanyHappyExtension.php
with CompanyHappyExtension
class defined inside.
I have created a sample CompanyHappyBundle bundle which is working fine on Symofny 3 (probably works on S2 as well). Feel free to clone/download it :)
The services.yml file is an added bonus, as you will most likely need it anyway.
src/Company/Bundle/HappyBundle/CompanyHappyBundle.php:
<?php
namespace Company\Bundle\HappyBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class CompanyHappyBundle extends Bundle
{
}
src/Company/Bundle/HappyBundle/DependencyInjection/CompanyHappyExtension.php
<?php
namespace Company\Bundle\HappyBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;
use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
class CompanyHappyExtension extends Extension implements ExtensionInterface
{
public function load(array $configs, ContainerBuilder $container)
{
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.yml');
$configuration = new Configuration();
$options = $this->processConfiguration($configuration, $configs);
// Do something with your options here
}
}
src/Company/Bundle/HappyBundle/DependencyInjection/Configuration.php
<?
namespace Company\Bundle\HappyBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
class Configuration implements ConfigurationInterface
{
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('company_happy');
$rootNode
->children()
->arrayNode('imports')
->prototype('array')
->children()
->scalarNode('path')->defaultValue('/tmp')->end()
->scalarNode('method')->defaultValue('ALL')->end()
->booleanNode('move_mail')->defaultValue(true)->end()
->booleanNode('mark_read')->defaultValue(true)->end()
->end()
->end()
->end()
;
return $treeBuilder;
}
}
src/Company/Bundle/HappyBundle/Resources/config/config.yml
company_happy:
imports:
import:
path: /tmp
src/Company/Bundle/HappyBundle/Resources/config/services.yml
# Define your services here
services:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With