Search code examples
phpsymfonysymfony4

Symfony 4 Custom config yaml file for Bundle


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.


Solution

  • 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.