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.
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 have an app with a MicroKernel and would like to use Symfony's TreeBuilder with a custom config because the app will be quite generic and I would like to have as much as possible configured externally. My problem is, I can't get my custom namespace registered for symfony:
There is no extension able to load the configuration for "research"
(in /var/www/html/src/app/config/config.yml). Looked for
namespace "research", found "framework", "twig",
"sensio_framework_extra", "web_profiler"
I have this Configuration class:
<?php
namespace App\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('research');
// #formatter:off
$rootNode
->children()
->arrayNode('questions')
->children()
->integerNode('client_id')->end()
->scalarNode('client_secret')->end()
->end()
->end()// twitter
->end();
// #formatter:on
return $treeBuilder;
}
}
and my AppExtension:
<?php
namespace App\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\DependencyInjection\ConfigurableExtension;
class AppExtension extends ConfigurableExtension
{
// note that this method is called loadInternal and not load
protected function loadInternal(array $mergedConfig, ContainerBuilder $container)
{
dump($mergedConfig);
}
public function getAlias()
{
return 'research';
}
}
In my AppKernel class, I'm initializing the extension like this and as a result I also get an output from the dump($mergedConfig) call in my AppExtension from my original research.yml
<?php
namespace App;
use App\DependencyInjection\AppExtension;
use Doctrine\Common\Annotations\AnnotationRegistry;
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\Routing\RouteCollectionBuilder;
use Symfony\Component\Yaml\Exception\ParseException;
use Symfony\Component\Yaml\Yaml;
$loader = require __DIR__ . '/../vendor/autoload.php';
// auto-load annotations
AnnotationRegistry::registerLoader(array($loader, 'loadClass'));
/**
* Class AppKernel
*/
class AppKernel extends Kernel
{
use MicroKernelTrait;
...
protected function configureContainer(ContainerBuilder $c, LoaderInterface $loader)
{
$researchConfig = Yaml::parse(file_get_contents(__DIR__ . '/config/research.yml'));
$appExtension = new AppExtension();
$appExtension->load(researchConfig, $c);
$loader->load(__DIR__ . '/config/config.yml');
}
...
}
nevertheless, when my config.yml looks like this, I get the error mentioned above:
framework:
secret: S0ME_SUPER_SECRET
profiler: { only_exceptions: false }
research:
questions:
client_id: 2342323423
clent_secret: mysecret
so what do I need to do, to actually register my namespace research so I can properly overwrite it? Or does it have to an external vendor bundle?
You get the error because you dont register a bundle and so symfony doesnt know the bundle config namespace.
I cant say how you could fix this with your approach but i would suggest to make a AppBundle instead of the App and register AppBundle in public function registerBundles().
See f.E. in some boilerplates like https://github.com/ikoene/symfony-micro.
Alternativly you could try to set up your project with the new symfony Flex.
This already uses MicroKernel, is bundleless and also supports latest stable symfony version (sf3.3).
https://symfony.com/doc/current/setup/flex.html#using-symfony-flex-in-new-applications
So no need to build something off the track yourself.
Since Symfony does not recommend the usage of Bundles anymore (), to create a Bundle is NOT the way to solve this.
#see [https://symfony.com/doc/current/bundles.html][1]
In Symfony versions prior to 4.0, it was recommended to organize your own application code using bundles. This is no longer recommended and bundles should only be used to share code and features between multiple applications.
[1]: Symfony Bundle Documentation
I noticed a strange thing with managing common dependencies with parent services in symfony 3.1.6.
http://symfony.com/doc/current/service_container/parent_services.html
I try to define a user_manager service which extends fos user manager service.
// AppBundle/Service/UserManager.php
namespace AppBundle\Service;
use FOS\UserBundle\Doctrine\UserManager as BaseUserManager;
class UserManager extends BaseUserManager
{
}
// AppBundle/DependencyInjection/AppExtension.php
namespace AppBundle\DependencyInjection;
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)
{
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.yml');
}
}
# AppBundle/Resources/config/services.yml
services:
app.user_manager:
class: AppBundle\Service\UserManager
parent: fos_user.user_manager.default
When trying to get service in contoller
$this->get('app.user_manager');
Then the exception is thrown:
'You have requested a non-existent service "app.user_manager".'
However, if I define my custom service in a different way:
# AppBundle/Resources/config/services.yml
services:
app.user_manager:
class: AppBundle\Service\UserManager
arguments:
- '#fos_user.util.password_updater'
- '#fos_user.util.canonical_fields_updater'
- '#fos_user.object_manager'
- '%fos_user.model.user.class%'
Then it works fine.
Moreover, it used to work like this with a "parent" before, in symfony 2.8
What could be wrong here and how it should be done properly with symfony 3.1?
p.s. I tried to clear cache, and also remove var/cache* directory, but that doesn't help
I develop a little bundle, that provides tag cloud functionality. It should be easy, to include it in other Symfony projects and therefore it needs to be configurable. I discovered 3 pages:
How to Create Friendly Configuration for a Bundle
Defining and Processing Configuration Values
How to Load Service Configuration inside a Bundle
I worked along the examples, but it's obvious, that I miss something, because I get the following error message when I use php app/console config:dump-reference:
[Symfony\Component\Config\Exception\FileLoaderLoadException]
There is no extension able to load the configuration for "loew_tag" (in somePath/blog/app/config/../../src/Loew/TagBundle/Resources/config/config.yml). Looked for namespace "loew_tag", found " ... " in somePath/blog/app/config/../../src/Loew/TagBundle/Resources/config/config.yml (which is being imported from "somePath/blog/app/config/config.yml").
and
[Symfony\Component\DependencyInjection\Exception\InvalidArgumentException]
There is no extension able to load the configuration for "loew_tag" (in /home/somePath/blog/app/config/../../src/Loew/TagBundle/Resources/config/config.yml). Looked for namespace "loew_tag", found "framework", "security", "twig", "monolog", "swiftmailer", "assetic", "doctrine", "sensio_framework_extra", "blog", "fos_user", "debug", "web_profiler", "sensio_distribution"
I work inside a 'blog bundle' and try to access the config data for the 'tag bundle'.
Top of my 'app/config/config.yml':
imports:
- { resource: parameters.yml }
- { resource: security.yml }
- { resource: services.yml }
- { resource: ../../src/Loew/TagBundle/Resources/config/services.yml }
- { resource: ../../src/Loew/TagBundle/Resources/config/config.yml }
LoewTagExtension.php:
<?php
// Loew/TagBundle/DependencyInjection/LoewTagExtension.php
namespace Loew\TagBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;
class LoewTagExtension extends Extension
{
/**
* {#inheritdoc}
*/
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
//$container->setParameter('food_entities', $config['food_entities']);
$container->setParameter('split_match', $config['split_match']);
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('config.yml');
$loader->load('services.yml');
}
}
config.yml:
loew_tag:
# food_entities:
# - "BlogBundle:Article"
# - "BlogBundle:Comment"
split_match: "/[^0-9a-zA-ZöÖüÜäÄß]/"
Configuration.php:
<?php
// Loew/TagBundle/DependencyInjection/Configuration.php
namespace Loew\TagBundle\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('loew_tag');
$rootNode
->children()
->scalarNode('split_match')->end()
// ->arrayNode('food_entities')
// ->prototype('scalar')->end()
->end();
return $treeBuilder;
}
}
Entries for the node food_entities are commented in all files to keep it as simple as possible.
I noticed, that similar questions has been asked and the regarding problems has been solved but I cannot transfer that solutions to this issue.
Any idea, what I do miss?
Finally solved this by
keeping naming conventions in mind, especially the part, pointed out by Jakub Zalas
removing entry: $loader->load('config.yml'); from the extension file.
Obviously, the config file will be loaded automatically as soon, as the service has been loaded.
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.