I want to add a new configuration file in Bundle/Resources/config. I've tried following http://symfony.com/doc/current/cookbook/bundles/extension.html , but it doesn't work as it should and I get
There is no extension able to load the configuration for
"mailbroker_mail_details"
My files:
MailbrokerMailDetailsExtension.php
<?php
namespace Mailbroker\MailDetailsBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;
class MailbrokerMailDetailsExtension extends Extension
{
/**
* {#inheritDoc}
*/
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.yml');
$loader->load('canonisers.yml');
}
public function getAlias()
{
return 'mailbroker_mail_details';
}
}
Configuration.php
<?php
namespace Mailbroker\MailDetailsBundle\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('mailbroker_mail_details');
$rootNode
->children()
->scalarNode('abc')->end()
->end()
;
return $treeBuilder;
}
}
canonisers.yml
mailbroker_mail_details:
abc: 123
The Configuration is correct (when placed in app/config/config.yml it loads as it should), canonisers.yml is loaded correctly, but for some reason I can't make it work together. Thanks for your help!
Well, I have not tried it but you should be able to use the Yaml extension to load in the canonisers.yml file directly and add it to configs. Not recommended (bypasses the application caching stuff) but it might work:
use Symfony\Component\Yaml\Yaml;
class MailbrokerMailDetailsExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container)
{
$file = __DIR__.'/../Resources/config/canonisers.yml';
$configs = array_merge($configs,Yaml::parse(file_get_contents($file));
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
....
Completely untested. You might need to add to app/config/config.yml
mailbroker_mail_details: ~
Just to get past the error message. Not sure.
Let me know if it works.
Ok, so #Iltar from #symfony irc channel pointed me to cookbook: http://symfony.com/doc/current/cookbook/bundles/prepend_extension.html
Long story short, PrependExtensionInterface with prepend method.
It was added since I last read through symfony book and cookbook, and it wasn't exactly googlable in this case, so I'll just leave the link here for other people.
Related
I am developing a bundle JohnCorpFormsBundle for my own symfony projects to share extended form logic.
However, when implementing the bundle in a project myproject I always get an error when I load a page in myproject that relies on bundle logic.
Error Message:
Too few arguments to function
JohnCorp\FormsBundle\Form\Builder\Core\ListFilterType::__construct(),
0 passed in /var/www/myproject/vendor/symfony/form/FormRegistry.php on line 81 and exactly 1 expected
It seems, the parameter $requestStack is not being passsed to ListFilterType::__construct() but shouldn't this work automatically using autowire?
MyProject: Controller-Code that causes the error:
// UserListController.php
use JohnCorps\FormsBundle\Form\Builder\Core\ListFilterType;
// ...
// this line runs into the error:
$listFilterForm = $this->createForm(ListFilterType::class);
Bundle: Form class:
// ListFilterType::class
namespace JohnCorp\FormsBundle\Form\Builder\Core;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\HttpFoundation\RequestStack;
class ListFilterType extends AbstractType
{
protected RequestStack $requestStack;
public function __construct(RequestStack $requestStack)
{
$this->requestStack = $requestStack;
}
//...
Bundle: Extension
// src/DependencyInjection/JohnCorpFormsExtension.php
namespace JohnCorp\FormsBundle\DependencyInjection;
class JohnCorpFormsExtension extends Extension
{
/**
* {#inheritdoc}
*/
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.yaml');
}
}
Bundle: composer.json autoload
{
"name": "johncorp/forms-bundle",
"type": "symfony-bundle",
...
"autoload": {
"psr-4": {
"JohnCorp\\FormsBundle\\": "src/"
}
}
}
I tried adding the arguments explicitly in the bundles services.yaml to ensure autowire is not a problem, but that does not change a thing.
Bundle: src/Resources/config/services.yaml:
services:
JohnCorp\FormsBundle\Form\Builder\Core\ListFilterType:
arguments:
$requestStack: '#request_stack'
I am not sure if this is required or if it is loaded. (I include this in a src/DependencyInjection/JohnCorpFormsExtension.php)
I also tried adding the yaml-Code from above directly inside the projects services.yaml without any effect.
I also tried explicitly using autowire-Attribute in ListFilterType.php
// ListFilterType::class
public function __construct(
#[Autowire(service: 'request_stack')] RequestStack $requestStack
)
If you use Symfony 6 , you just have to set the autowire and autoconfigure as true in your Bundle like this :
# yourBundle/src/Ressources/config/services.yaml
services:
JohnCorp\FormsBundle\Form\Builder\Core\ListFilterType:
autowire: true
autoconfigure: true
here is the doc : service-container-services-load-example
then after that you have to inject your service in your BundleExtension:
<?php
//yourBundle/src/DepencencyInjection/YourBundleExtension
namespace App\YourBundle\DependencyInjection;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\Extension;
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
class YourBundleExtension extends Extension implements PrependExtensionInterface
{
public function load(array $configs, ContainerBuilder $container)
{
$loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.yaml');
}
...
}
Since I'm running out of ideas (and stackoverflow threads): I need an additional app configuration, located under /etc/appname/config.yml (client requirement).
My configuration file:
foo: 'bar'
I'm loading this file within my `app/config/config.yml:
imports:
- { resource: /etc/appname/config.yml }
The error:
There is no extension able to load the configuration for "foo"
I've already implemented a Configuration class:
<?php
namespace AppBundle\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('');
$rootNode
->children()
->scalarNode('foo')->defaultValue('')->end()
->end()
;
return $treeBuilder;
}
}
But it still doesn't work. If I load the config via the load method
public function load(array $configs, ContainerBuilder $container)
{
$loader = new YamlFileLoader(
$container,
new FileLocator('/etc/frsereportergui')
);
$loader->load('config.yml');
}
I get the same error.
Anyone got an idea?
Introduction
I know. There are a thousands of post on this exact topic, but somehow I still can't manage to fix this issue. I'm always getting the error
There is no extension able to load the configuration for "first" (in /var/www/app/src/tzfrs/SpotifyBundle/DependencyInjection/../Resources/config/settings.yml). Looked for namespace "first", found none
Purpose
I created a bundle and want to have custom configuration options, but since they are bigger than a usual file, I don't want to put it in the config.yml, but in my own file.
Description
My project structure for the bundle looks like this /src/tzfrs/SpotifyBundle
Inside the bundle I have created the files
./TzfrsSpotifyBundle.php
./DependencyInjection/Configuration.php
./DependencyInjection/TzfrsSpotifyExtension.php
./Resources/config/settings.yml
I have, of course, registered the Bundle in the AppKernel, and everything works fine so far with the bundle, except for the new config I want to add
Contents of TzfrsSpotifyBundle
<?php
namespace tzfrs\SpotifyBundle;
use tzfrs\SpotifyBundle\DependencyInjection\TzfrsSpotifyExtension;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class TzfrsSpotifyBundle extends Bundle
{
}
Contents of /DependencyInjection/Configuration.php (minimized to relevant info only)
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('first');
$rootNode
->children()
->booleanNode('secondary')->defaultTrue()->end()
->end();
return $treeBuilder;
}
Contents of ./DependencyInjection/TzfrsSpotifyExtension.php (minimized to relevant info only)
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('settings.yml');
}
Contents of ./Resources/config/settings.yml
first:
secondary: false
Ideas
If any more questions come up or suggestions that won't work, I'll update this section with the info
What I tried was the same this post here suggested https://stackoverflow.com/a/36051719/2823419 but it didn't work. I got the same error.
Then I tried this answer https://stackoverflow.com/a/35505189 meaning naming the root element like my bundle. Still the same error
Make sure you have everything spelled correctly and that your extension class is actually being called. If you don't name the extension just right then it won't be loaded. I just tested this and it works fine:
cerad_spotify:
first:
secondary: false
class CeradSpotifyBundle extends Bundle
{
}
namespace Cerad\SpotifyBundle\DependencyInjection;
class Configuration implements ConfigurationInterface
{
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('cerad_spotify');
$rootNode
->children()
->arrayNode('first')
->children()
->booleanNode('secondary')->end()
->end()
->end() // first
->end()
;
return $treeBuilder;
}
}
namespace Cerad\SpotifyBundle\DependencyInjection;
class CeradSpotifyExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
VarDumper::dump($config);
}
}
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.
Details
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").
Update
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?
I have solved my own issue but not without trouble. I'm not at all pleased with Symfony's configuration system.
Step one - Create your config file
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
Step two - Importing your configuration file
Go to app/config/config.yml and add:
#app/config/config.yml
imports:
- { resource: "#YourBundle/Resources/config/config.yml" }
Step three - Create a configuration class
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;
}
}
Step four - Creating an extension
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:
you're almost done, you just need to configure your Bundle to use your config parameters, take a look at this answer.
I'm trying to write a semantic configuration of the bundle in Symfony2. I have prepared a configuration file using YAML (spu.yml):
spu:
modules:
spu-module:
reuqires: []
path: modules/spu-module.js
Then I'm loading it in Extension file (DependencyInjection/spuExtension.php):
namespace Gig\SpuBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;
class spuExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
$config = $this->processConfiguration( $configuration, $configs );
$loader = new Loader\YamlFileLoader( $container, new FileLocator( __DIR__ . '/../Resources/config' ) );
$loader->load( 'services.yml' );
$loader->load( 'spu.yml' );
}
}
And configuration class ()DependencyInjection/Configuration.php) for this extension is:
namespace Gig\SpuBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
class Configuration implements ConfigurationInterface
{
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root( 'spu' );
$rootNode
->children()
->arrayNode('modules')
->isRequired()
->prototype('array')
->end()
->end();
return $treeBuilder;
}
}
When validating the config I get an error:
[Symfony\Component\Config\Definition\Exception\InvalidConfigurationException]
The child node "modules" at path "spu" must be configured.
What am I doing wrong?
You must include this in your main app/config/config.yml file
imports:
- { resource: parameters.yml }
- { resource: security.yml }
- { resource: spu.yml }
If you want to have default parameters, set them in DependencyInjection/Configuration.php
This can be something like this.
$rootNode
->children()
->arrayNode('modules')
->isRequired()
->addDefaultsIfNotSet()
->prototype('array')
->children()
->arrayNode('reuqires')->end()
->scalarNode('path')->defaultValue('modules/spu-module.js')->end()
->end()
->end()
->end()
->end();
The best way to do this is to let the users define the configuration for your bundle in the config.yml file. There is no need for a special file.
You can keep the configuration which you have in your Configuration class but you will have to modify your spuExtension class.
The way Symfony works you will have to read the values from the config.yml for your bundle in the spuExtension class and then set them as parameters. Then you will have easy access to these values by using the container's getParameter method.
This is how your config.yml will look
spu:
modules:
spu-module:
reuqires: []
path: modules/spu-module.js
Then in your spuExtension class you can get these values specified by doing:
class spuExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
$config = $this->processConfiguration( $configuration, $configs );
$loader = new Loader\YamlFileLoader( $container, new FileLocator( __DIR__ . '/../Resources/config' ) );
$loader->load( 'services.yml' );
$container->setParameter('modules', $configs[0]);
}
}
This will make it easy for you to use the container in an controller to get a parameter.
The $configs variable in the spuExtension will contain all the configuration that is listed under your root. Which in this case is spu.
The way Symfony works you first have to load the configuration in your bundle's extension class and then set this as a parameter, this seems redundant but it's a best practice and it was intended to work like this.
Ok, I did it different way.
public function load(array $configs, ContainerBuilder $container)
{
$loader = new Loader\YamlFileLoader( $container, new FileLocator( __DIR__ . '/../Resources/config' ) );
$loader->load( 'services.yml' );
$loader->load( 'spu.yml' );
$spuConfig = Yaml::parse(__DIR__ . '/../Resources/config/spu.yml');
$configs = array_merge($configs, $spuConfig);
$configuration = new Configuration();
$config = $this->processConfiguration( $configuration, $configs );
}
And now I'm getting what I wanted. I can validate params in Configuration class.