Question

Configuration in Symfony is equal to valdiating the configuration. I want to validate my configuration with the treebuilder. In the yml-example, i give a quite example of how the config-tree will look like (in future, the tree will be even bigger than now). But to do this, i need a to create a structure.
Now Could you help me, to create the treebuilder? I've tried everything with arrayNode and prototypes, but it won't work. I get exceptions like

"FatalErrorException: Error: Call to undefined method Symfony\Component\Config\Definition\Builder\NodeBuilder::prototype() in /var/www/menu_bundle/src/my/MenuBundle/DependencyInjection/Configuration.php line 29 "


Base idea:

I want to generate a Symfony2-bundle, which creates a menuStructure in HTML. For generateing the HTML-Code, i need to pull the yaml-configuration in to an object-structure, this works, but the validating with symfony doesn't work...


Here is a quick example on how the menu.yml should look like:

my_menu_structure:
    menu:
        name: test
        cssClass: blubb
        children:
            child:
                name: item1
                route: route1
                position: 0
                  child:
                    name: item11
                    route: route11
                    position: 0
              child:
                  name: item2
                  route: route2
                  position: 1

Now i want to configure the Treebuilder in Symfony2, but it won't work..

After a few times of trying, this is my last version:

    $treeBuilder = new TreeBuilder();
    $rootNode = $treeBuilder->root('my_menu');
        $rootNode
            ->children()
            ->arrayNode('menu')
            ->scalarNode('cssClass')
            ->defaultValue(array())
            ->prototype('array')
                ->scalarNode('name')
                ->scalarNode('route')
                ->scalarNode('Position')
                ->prototype('array')

        ->end()
        ->end()
        ->end();

I have build the objects for Menu and MenuItems. Everything works so far, but i can't configure the treebuilder. What I'm searching for, is a way to reuse a part (menuItem-: name, route and children) in the treebuilder, but everything I found so far, couldn't help me... Everything else works, my only problem is, that I can't configure the Treebuilder and I can't get the config out of the yml with $this->container->get('menu.name'). This throws an exception: You have requested a non-existent service "menu.name".

So far, I've tried some Configuration with prototype('array'), but phpStorm says everytime, it can't find a scalarNode as a child of prototype or of ->prototype()->children()->scalarNode()..

Was it helpful?

Solution

I know this question dates from januari 2014, however when looking for a solution to the error message "Call to undefined method Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition::arrayNode()" this StackOverflow question came up in the search results.

The accepted answer doesn't actually mention the reason of the error message.

The example given by the original poster is as follows:

$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('my_menu');
$rootNode
        ->children()
        ->arrayNode('menu')
        ->scalarNode('cssClass')
        ->defaultValue(array())
        ->prototype('array')
            ->scalarNode('name')
            ->scalarNode('route')
            ->scalarNode('Position')
            ->prototype('array')

    ->end()
    ->end()
    ->end();

This is incorrect because each node must be ended with an end() call. As the [documentation][1] states:

In general: after defining a node, a call to end() takes you one step up in the hierarchy.

In my own case, what was unclear to me, was the fact that in an arrayNode defition both the arrayNode() definition as the underlying children() definition must be ended.

So the above example would be correct as follows:

$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('my_menu');
$rootNode
    ->children()
        ->scalarNode('cssClass')->end()
        ->arrayNode('menu')
            ->prototype('array')
                ->scalarNode('name')->end()
                ->scalarNode('route')->end()
                ->scalarNode('Position')->end()
            ->end()
        ->end()
    ->end()
->end();

Hopefully this will help others fix the problem quicker than I did.

OTHER TIPS

what you can do is to configure your rootNode this way:

    $rootNode
        ->children()
            ->arrayNode('menu')
            ->isRequired()
            ->prototype('array')
                ->children()
                ->scalarNode('label')->isRequired()->end()
                ->scalarNode('class')->end() // I added an optional class parameter
                //others parameters
                    ->arrayNode('sub_menu')
                        ->prototype('array')
                        ->children()
                            ->scalarNode('label')->isRequired()->end()
                            ->scalarNode('route')->end()
                            ->scalarNode('class')->end()
                            // add as many parameters as you want for your children
                        ->end()
                    ->end()
                ->end()
            ->end()
            ->end()
        ->end();

Then you want to pass this variable to a service. Very simple class :

class AdminMenu
{

    protected $menu;

    public function setMenu($menu)
    {
        $this->menu = $menu;
    }

    public function getMenu()
    {
        return $this->menu;
    }


}

In you services.xml

    <parameter key="wf.admin_menu.class">Acme\AdminBundle\Library\AdminMenu</parameter>

    <service id="wf.admin_menu" class="%wf.admin_menu.class%">
    </service>

In your bundleAdminExtension class

    $myServiceDefintion = $container->getDefinition('wf.admin_menu');
    $myServiceDefintion->addMethodCall('setMenu', array($config['menu']));

Finally, in any controller you want:

 $menu = $this->container->get('wf.admin_menu');

That's all :)

Edit : Here's what my config looks like

my_bundle_admin:
  menu:
    unique_key:
      label : 'Level1-1'
      class : ''
      sub_menu:
        sub_unique_key1:
          label: 'Level2-1'
          route: 'my_route'
        sub_unique_key2:
          label: 'Level2-2'
          route: 'my_route'
    unique_key_2:
      label : 'Level1-2'
      sub_menu :
        sub_unique_key1:
          label : 'Level2-1'
          route : 'my_route'
        sub_unique_key2:
          label : 'Level2-2'
          route : 'my_route'
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top