I am trying to expose a custom configuration defined for my bundle which is called DecisionBundle.
I have a Decision\DecisionBundle\Resources\config\custom.yaml that looks like this:
decision:
testValue: 11
anotherTest: 12
I also have an Extension that looks like this:
namespace Decision\DecisionBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;
class DecisionExtension 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('custom.yml');
}
}
The file custom.yml seems to be loaded ok. Next comes the configuration interface
namespace Decision\DecisionBundle\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('decision');
$rootNode
->children()
->scalarNode('testValue')->defaultValue('0')->end()
->scalarNode('anotherTest')->defaultValue('0')->end()
->end();
// Here you should define the parameters that are allowed to
// configure your bundle. See the documentation linked above for
// more information on that topic.
return $treeBuilder;
}
}
However, this results in the following:
InvalidArgumentException: There is no extension able to load the configuration for "decision" (in *****\src\Decision\DecisionBundle\DependencyInjection/../Resources/config\custom.yml). Looked for namespace "decision", found none
Can somebody point me as to what I am doing wrong? Do I need to manually register the Extension somehow? However, the Extension seems taken into account as it looks loaded from the debug info.
Thanks!
Currently you are setting parameters in your custom.yml file in the same way that you would set them in the `app/config/config.yml' file.
decision:
testValue: 11
anotherTest: 12
When you are loading a file through the configuration file then your service files (services.yml & custom.yml in your case) should have either services or parameters as the headers. To use your parameters in the custom.yml file you should lay it out like..
parameters:
decision.testValue: 11
decision.anotherTest: 12
Also, if you are putting the parameters in the app/config/config.yml file, you need to set them to actual parameters in your DecisionExtension. To do this you would need to get the data from your Configuration file and set them in the container like..
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
$loader = new Loader\YamlFileLoader($container, new
FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.yml');
$loader->load('custom.yml');
$container->setParameter('decision.testValue', $config['testValue']);
$container->setParameter('decision.anotherTest', $config['anotherTest']);
This, however, will overwrite the parameters set in your custom.yml and also negate any reason for it being there.
Related
I get following error:
There is no extension able to load the configuration for
"upload_images" (in
"/var/www/vhosts/diabetigraph-dev/vendor/verzeilberg/upload-images/src/Resources/services.yaml").
Looked for namespace "upload_images", found "none"
This are my files:
services.yaml
services:
verzeilberg\UploadImagesBundle\Service\Rotate:
autowire: true
upload_images:
version: 100
Configuration
namespace verzeilberg\UploadImagesBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
class Configuration implements ConfigurationInterface
{
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder('upload_images');
$treeBuilder->getRootNode()
->children()
->integerNode('version')->end()
->end();
return $treeBuilder;
}
}
UploadImagesExtension
namespace verzeilberg\UploadImagesBundle\DependencyInjection;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;
class UploadImagesExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container)
{
$loader = new YamlFileLoader($container, new FileLocator(dirname(__DIR__).'/Resources'));
$loader->load('services.yaml');
$config = $this->processConfiguration(new Configuration(), $configs);
$container->setParameter('version', $config['version']);
}
}
What am I doing wrong?
The basic answer is that your upload_images configuration needs to be moved to it's own application level config/packages file:
# app/config/packages/upload_images.yaml
upload_images:
version: 100
In your bundle, the Configuration object represents the bundle's configuration. There is no upload_images.yaml sort of file in your bundle. The object is quite powerful so you can add defaults and options and what not.
Your bundle's extension takes care of processing the final configuration and making information such as parameters available to the rest of the system:
class UploadImagesExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container)
{
// $configs is actually an array of array representing the contents of upload_files.yaml
$config = $this->processConfiguration(new Configuration(), $configs);
$container->setParameter('upload_files.version', $config['version']);
// Loading your bundle's services.yaml file is a different process which just happens to be kicked off by the loader
// Thanks to the setParameter above, %upload_files.version% can be used by services
$loader = new YamlFileLoader($container, new FileLocator(dirname(__DIR__).'/Resources'));
$loader->load('services.yaml');
}
}
It can be confusing and for me at least I had to read the docs multiple times and do quite a bit of experimenting to understand the overall process.
The confusion is one of the reasons that Symfony evolved from multiple bundles per application to one application bundle to no application bundle at all.
I might also add the Symfony uses composer recipes to simplify installing a bundle. The recipe not only adds the bundle to config/bundles.php but copies any default configuration file to config/packages as well. Unfortunately, there are additional steps required for this copy to happen (see the docs) so it's easiest to do things the old-fashion way and just tell the developer to create the config file via the bundle's README file.
I'm trying to convert a bundle to symfony 4 and need to update my ancient parameters.yml to the modern symfony 4 way of life. Basicall the bundle itself - shared across multiple apps - should have a configurable file under /config/packages/.
However I receive this error:
(1/1) InvalidArgumentException
There is no extension able to load the configuration for "ptmr" (in /var/www/html/ptmr/pws_ptmrio_dev/PtmrBundle/DependencyInjection/../../config/packages/ptmr.yaml). Looked for namespace "ptmr", found none
/PtmrBundle/DependencyInjection/PtmrExtension.php
<?php
namespace PtmrBundle\DependencyInjection;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class PtmrExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration(true);
$config = $this->processConfiguration($configuration, $configs);
$loader = new YamlFileLoader(
$container,
new FileLocator(__DIR__ . '/../../config/packages')
);
$loader->load('ptmr.yaml');
}
}
/PtmrBundle/DependencyInjection/Configuration.php
<?php
namespace PtmrBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
class Configuration implements ConfigurationInterface
{
private $debug;
public function __construct($debug = true)
{
$this->debug = (bool) $debug;
}
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder('ptmr');
$treeBuilder->getRootNode()
->children()
->arrayNode('twitter')
->children()
->integerNode('client_id')->end()
->scalarNode('client_secret')->end()
->end()
->end() // twitter
->end()
;
return $treeBuilder;
}
}
/config/packages/ptmr.yaml
ptmr:
twitter:
client_id: 123
client_secret: your_secret
--
Note:
The Bundle itself works.
I added this line to psr-4 in composer.json:
"PtmrBundle\\": "PtmrBundle/"
This lines to config/routes/annotations.yml
ptmr_bundle:
resource: ../PtmrBundle/Controller/
type: annotation
These lines to config/services.yaml
services:
...
PtmrBundle\:
resource: '../PtmrBundle/*'
exclude: '../PtmrBundle/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'
...
PtmrBundle\Controller\:
resource: '../PtmrBundle/Controller'
tags: ['controller.service_arguments']
And of course PtmrBundle/PtmrBundle.php
<?php
namespace PtmrBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class PtmrBundle extends Bundle
{
}
I'm following these instructions and I reaaaly do not see any errors. What am I missing? Symfony 4.2.
I found the answer after all. To make your own config/bundle.yaml parameters file, simply do:
Step 1: Create a file in your bundle DependencyInjection/{BundleNameWithoutBundle}Extension.php, e.g. for MyBundle > MyExtension.php
<?php
namespace MyBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\Extension;
class MyExtension extends \Symfony\Component\DependencyInjection\Extension\Extension
{
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
$container->setParameter('my', $config);
}
}
Also see How to Load Service Configuration inside a Bundle
Step 2: Make a configuration file that provides a schema for your .yaml file DependencyInjection/Configuration.php
<?php
namespace MyBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
class Configuration implements ConfigurationInterface
{
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder('my');
$treeBuilder->getRootNode()
->children()
->variableNode('project_name')->end()
->end()
;
return $treeBuilder;
}
}
Also see How to Create Friendly Configuration for a Bundle
Step 3: Mirror the Configuration.php in your config/my.yaml
my:
project_name: "My Name"
Now the parameter my is available (set in MyExtension Class). Simply get it in your Controller like:
class IndexController extends AbstractController
{
public function indexAction(ParameterBagInterface $parameterBag)
{
...
dump($parameterBag->get('ptmr'));
return $this->render('index.html.twig');
}
}
Note: Most bundles go further and manipulate the bundles config xml files, which is out of scope for a simple question like this. A simple example on how to do this is the KnpPaginatorBundle which is not overly complicated to understand for settings parameters.
Personal Note: It seems to me, the Symfony docs are overly complicated and should provide a simple example. You got to know a lot of nomenclature and it's hard to learn it, especially compared to other well documented symfony chapters.
You are attempting to load configuration for your bundle before Symfony is aware of your bundle. Your bundle class must be loaded and configured before you can parse configuration under the ptmr property.
Typically your bundle would load __DIR__.'/../Resources/config/services.yaml which contains service and parameters definitions. Outside of your bundle, in your application config/ dir, you would then load configuration under the ptmr property.
I'm running a issue here and I don't know why or where this is failing, perhaps I miss some configuration or so, anyway, I have this code at DependencyInjection\AppExtension.php file:
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;
class AppExtension 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');
}
}
Then at Resources/config/services.yml I have this:
services:
pdone.twig.extension:
class: GroupDCA\PDOneBundle\Extension\PDOneTwigExtension
tags:
- { name: twig.extension }
For some reason isn't working. This mean I got this error:
The filter "empty" does not exist in PDOneBundle::pdone.html.twig at line 1
Now if I move the services definition to config/config.yml I got this error instead:
Compile Error: Cannot use isset() on the result of an expression (you can use "null !== expression" instead)
Which makes me think the bundle is not going through DependecyInjection, what I am missing here? Why different errors?
1) Did you add your bundle to AppKernel?
2) I am not sure, but I think you must follow the naming convention of your Extension class:
Bundle's root directory should contain DependencyInjection directory
Within the DependencyInjection, Extension class should be named as <BUNDLE>Extension, without "Bundle" suffix. That would be PDOOneExtension in your case.
I have a bundle which was working quite well for some time. However, I had to add some custom configuration params to it, so I wrote some lines in the bundle's config.yml, something like this:
# ...
acme_my_bundle:
special_params: ['param_1', 'param_2']
The configuration is defined in the bundle's Configuration class:
namespace ACME\MyBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
/**
* This is the class that validates and merges configuration from your app/config files
*
* To learn more see {#link http://symfony.com/doc/current/cookbook/bundles/extension.html#cookbook-bundles-extension-config-class}
*/
class Configuration implements ConfigurationInterface {
/**
* {#inheritdoc}
*/
public function getConfigTreeBuilder() {
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('acme_my_bundle');
$rootNode
->children()
->arrayNode('special_params')
->end()
->end();
return $treeBuilder;
}
}
The bundle is properly registered in AppKernel.php:
public function registerBundles() {
$bundles = array(
// ...
new ACME\MyBundle(),
// ...
);
// ...
return $bundles;
}
However, when I try to use my app, I get an error:
There is no extension able to load the configuration for "acme_my_bundle" (in (path_to_bundle)/MyBundle/DependencyInjection/../Resources/config/config.yml). Looked for namespace "acme_my_bundle", found none
I looked it up, but most results found were unsatisfactory - I eliminated the problems that came up during searching:
improper configuration structure
bundle not registered in the app kernel
config root node name different than the one returned from ACMEMyBundleExtension::getAlias()
I tried debugging the cause of the exception being thrown and discovered that when the YAML file loader tries to validate my config file, the container has no extensions:
var_dump($container->getExtensions()); // prints empty array - array(0) { }
It causes the validation to fail and the none part of the message to be displayed - there a no available extensions.
I tried debugging $this->extensions in ContainerBuilder::hasExtension() and for some reason the list is complete when the method is launched for the vendor bundles, but is empty for my bundle. It looks like that something in my bundle is still defined or registered incorrectly.
I changed the names off classes, etc. not to expose company code, excuse me for that if it causes confusion.
EDIT: I didn't explicitly mention it, but the Extension class is defined and the exception occurs when it is loaded - just as I have written above:
when the YAML file loader tries to validate my config file
To be more clear, here is my Extension class:
namespace ACME\MyBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;
/**
* This is the class that loads and manages your bundle configuration
*
* To learn more see {#link http://symfony.com/doc/current/cookbook/bundles/extension.html}
*/
class ACMEMyBundleExtension 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'));
// The exception is thrown here
$loader->load('config.yml');
}
}
Check your configuration reader in ACME\MyBundle\DependencyInjection\Configuration for $rootNode = $treeBuilder->root('BUNDLE_CONFIG_KEY');.
BUNDLE_CONFIG_KEY should be:
valid (same in ACME\MyBundle\DependencyInjection\Configuration and your config.yml
unique for the app
Also please check you're defining bundle configuration in correct way - it should be added to app/config/*.yml (one of global config files). Maybe you have added acme_my_bundle config in other custom bundle config files?
You have missed bundle extension class (ACME\MyBundle\DependencyInjection\ACMEMyExtension) as explained here http://symfony.com/doc/current/cookbook/bundles/extension.html. Cookbook entry for bundle configuration is here. Key in config.yml should be named only as acme_my.
Creating the Configuration class alone is not enough. You need to register a dependency injection extension and use the Configuration class in there.
Read more about in the How to Create Friendly Configuration for a Bundle cookbook:
[The Configuration] class can now be used in your load() method to merge configurations and force validation (e.g. if an additional option was passed, an exception will be thrown)
namespace Acme\MyBundle\DependencyInjection;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class AcmeMyBundleExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
// ...
}
}
Naming your extension class according to the convention will make it's automatically loaded. More on creating DIC extension classes in Creating an Extension Class. You can also enable the extension manually, see Manually registering an extension class.
Iam trying to implement service for encoding password but it seems it doesn't work cause I get "You have requested a non-existent service user_service " error.
Here is my code :
Vendor/BundleNameBundle/Resources/config/services.yml
services:
user_service:
class: Morescreens\VideomanagerBundle\Service\UserService
arguments: ['#security.encoder_factory']
app/config/config.yml
imports:
- { resource: "#morescreensVideomanagerBundle/Resources/config/services.yml" }
code for my service
class UserService {
/**
* #var EncoderFactoryInterface
*/
private $encoderFactory;
public function __construct(EncoderFactoryInterface $encoderFactory)
{
$this->encoderFactory=$encoderFactory;
}
public function setEncodedPassword(User $user)
{
$encoder=$this->encoderFactory->getEncoder($user);
$password=$encoder->encodePassword($user->getPassword(),$user->getSalt());
$user->setPassword($password);
}
}
Inside mine controller:
$user=new User();
$user->setPassword('password');
$this->get('user_service')->setEncodedPassword($user);
EDIT:
I manually deleted cache folder and my service started to work.
Try looking at your src/{VendorName}/{BundleName}Bundle/DependencyInjection/{VendorName}{BundleName}Extension.php
This file should load your service definitions like this:
<?php
namespace Vendor\{BundleName}Bundle\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;
/**
* This is the class that loads and manages your bundle configuration
*
* To learn more see {#link http://symfony.com/doc/current/cookbook/bundles/extension.html}
*/
class Vendor{BundleName}Extension extends Extension
{
/**
* {#inheritDoc}
*/
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
//Load our YAML resources
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.yml');
}
}
This will load the service definition from src/{VendorName}/{BundleName}Bundle/Resources/config/services.yml into the container. If that still doesn't work try doing php app/console cache:clear. For speed reasons, symfony will basically aggregate all your service (and other configuration files) into a cached file so it doesn't have to read those files every time. The files that actually cache the service definitions are located:
app/cache/{envName}/ (The cache directory can be configured to be located wherever you want, this is just the default).
The files that have the relevent info for container service definitions are:
app{EnvName}DebugProjectContainer.php
app{EnvName}DebugProjectContainer.php.meta
app{EnvName}DebugProjectContainer.xml