Estructura recursiva en yaml con Symfony2 Treebuilder
-
21-12-2019 - |
Pregunta
Configurar en Symfony es igual a validar la configuración.Quiero validar mi configuración con treebuilder.En el ejemplo de yml, doy un buen ejemplo de cómo se verá el árbol de configuración (en el futuro, el árbol será incluso más grande que ahora).Pero para hacer esto, necesito crear una estructura.
Ahora ¿podrías ayudarme a crear el constructor de árboles?Probé todo con arrayNode y prototipos, pero no funcionó.Recibo excepciones como
"Excepción de error fatal:Error:Llamada al método no definido Symfony\Component\Config\Definition\Builder odeBuilder::prototype() en /var/www/menu_bundle/src/my/MenuBundle/DependencyInjection/Configuration.php línea 29 "
Idea básica:
Quiero generar un paquete Symfony2, que crea una estructura de menú en HTML.Para generar el código HTML, necesito incorporar la configuración de yaml a una estructura de objeto, esto funciona, pero la validación con Symfony no funciona...
Aquí hay un ejemplo rápido de cómo debería verse menu.yml:
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
Ahora quiero configurar Treebuilder en Symfony2, pero no funciona.
Después de intentarlo varias veces, esta es mi última versión:
$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();
He creado los objetos para Menú y MenuItems.Todo funciona hasta ahora, pero no puedo configurar el generador de árboles.Lo que estoy buscando es una forma de reutilizar una pieza (menuItem-:nombre, ruta y niños) en el constructor de árboles, pero todo lo que encontré hasta ahora no pudo ayudarme...Todo lo demás funciona, mi único problema es que no puedo configurar Treebuilder y no puedo sacar la configuración del yml con $this->container->get('menu.name').Esto arroja una excepción:Ha solicitado un servicio "menú.nombre" inexistente.
Hasta ahora, he probado algo de configuración con el prototipo ('array'), pero phpStorm dice cada vez que no puede encontrar un scalarNode como hijo del prototipo o de ->prototype()->children()->scalarNode( )..
Solución
Sé que esta pregunta data de enero de 2014, sin embargo, cuando buscaba una solución al mensaje de error "Llamar a un método no definido Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition::arrayNode()", apareció esta pregunta de StackOverflow en los resultados de búsqueda. .
La respuesta aceptada en realidad no menciona el motivo del mensaje de error.
El ejemplo dado por el cartel original es el siguiente:
$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();
Esto es incorrecto porque cada nodo debe terminar con un end()
llamar.Como dice la [documentación][1]:
En general:Después de definir un nodo, una llamada a end() le lleva un paso hacia arriba en la jerarquía.
En mi propio caso, lo que no me quedó claro fue el hecho de que en una definición de arrayNode tanto el arrayNode()
definición como subyacente children()
Hay que poner fin a la definición.
Entonces el ejemplo anterior sería correcto de la siguiente manera:
$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();
Con suerte, esto ayudará a otros a solucionar el problema más rápido que yo.
Otros consejos
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'