root($name, 'variable'); $root->defaultValue([]); $root ->beforeNormalization() ->ifTrue(function ($v) { return !is_array($v); }) ->thenInvalid("The {$name} element must be an array.") ; $root ->beforeNormalization() ->always(function (iterable $children) use ($builder, $callback) { $config = []; foreach ($children as $name => $child) { $node = $builder->root($name); $callback($node, $callback); $config[$name] = $node->getNode(true)->finalize($child); } return $config; }) ; return $root; } /** * Usage of recursiveNode * This allows for configuration like: * * routing_bundle: * routes: * - name: route_1 * url: url_1 * children: * - name: route_2 * url: url_2 * - name: route_3 * url: url_3 * children: * - name: route_4 * - url: url_4 * - name: route_5 * url: url_5 */ class Configuration implements ConfigurationInterface { public function getConfigTreeBuilder() { $tree = new TreeBuilder(); $root = $tree->root('my_routing_bundle'); $root ->children() ->append( recursiveNode('routes', function ($node, $recursive) { return $this->routes($node, $recursive); }) ) ->end() ; return $tree; } private function routes(ArrayNodeDefinition $node, callable $recursive) { $node ->addDefaultsIfNotSet() ->children() ->scalarNode('description')->end() ->scalarNode('name') ->isRequired() ->cannotBeEmpty() ->end() ->scalarNode('url') ->defaultNull() ->end() ->append(recursiveNode('children', $recursive)) ; } }