Installation:
composer require becklyn/route-tree-bundle
Register the bundle in config/bundles.php (Laravel 5.4+) or AppKernel.php (Symfony):
Becklyn\RouteTreeBundle\BecklynRouteTreeBundle::class => ['all' => true],
Define a Root Node:
Add a route with tree.parent set to null (or omit it for the root):
# config/routes.yaml
dashboard:
path: /dashboard
options:
tree:
parent: null
title: "Dashboard"
First Use Case: Generate the tree in a controller or Twig template:
use Becklyn\RouteTreeBundle\Tree\TreeBuilder;
$treeBuilder = $this->get('becklyn_route_tree.tree_builder');
$tree = $treeBuilder->build();
Render in Twig:
{% for node in tree %}
{{ node.title }} ({{ node.route.path }})
{% if node.children %}
<ul>
{% for child in node.children %}
<li>{{ loop(child) }}</li>
{% endfor %}
</ul>
{% endif %}
{% endfor %}
Dynamic Menu Generation: Use the tree to build admin panels or navigation menus:
$menuTree = $treeBuilder->build(['security' => 'is_granted("ROLE_ADMIN")']);
Parameterized Routes: Set default parameters for child routes:
products:
path: /products/{category}
options:
tree:
parent: dashboard
parameters: { category: "all" }
Security Filtering: Exclude routes based on security expressions:
admin_panel:
path: /admin
options:
tree:
security: "is_granted('ROLE_SUPER_ADMIN')"
Twig Extension: Register a custom Twig extension to access the tree globally:
// src/Twig/RouteTreeExtension.php
class RouteTreeExtension extends \Twig\Extension\AbstractExtension {
public function getFunctions() {
return [
new \Twig\TwigFunction('route_tree', [$this->container->get('becklyn_route_tree.tree_builder'), 'build']),
];
}
}
Use in Twig:
{{ route_tree()|render_tree }}
API Endpoints: Expose the tree as JSON for SPAs:
return response()->json($treeBuilder->build());
Caching: Cache the tree in a service provider:
$this->app->singleton('becklyn_route_tree.tree_builder', function () {
$builder = new TreeBuilder();
return Cache::remember('route_tree', 3600, function () use ($builder) {
return $builder->build();
});
});
Circular References:
Avoid setting parent to a child route (e.g., A.parent = B, B.parent = A). The builder throws a CircularReferenceException.
Missing Parents:
If a parent route is defined but doesn’t exist, the tree builder silently skips the node. Enable debug mode to catch this:
# config/packages/dev/becklyn_route_tree.yaml
becklyn_route_tree:
strict_parent_check: true
Priority Collisions:
Nodes with the same priority are sorted alphabetically by title. Explicitly set priorities for consistent ordering:
route1:
options:
tree:
priority: 1
route2:
options:
tree:
priority: 1
Dump the Tree:
Use the dump() method to inspect the raw tree structure:
$treeBuilder->build()->dump();
Symfony Profiler:
Install symfony/profiler-pack and enable the route_tree data collector to visualize the tree in the profiler toolbar.
Custom Node Classes:
Override the default Node class by binding your implementation:
# config/services.yaml
Becklyn\RouteTreeBundle\Tree\Node: '@app.custom_route_tree_node'
Post-Build Modifiers:
Use the TreeBuilder::addModifier() method to alter nodes after generation:
$treeBuilder->addModifier(function (Node $node) {
if ($node->getTitle() === 'Dashboard') {
$node->setTitle('Admin Dashboard');
}
});
Route Filtering:
Filter routes before tree generation by implementing RouteFilterInterface:
class CustomRouteFilter implements RouteFilterInterface {
public function filter(RouteCollection $collection) {
return $collection->filter(function (Route $route) {
return strpos($route->getPath(), 'api') === false;
});
}
}
Register it in services.yaml:
Becklyn\RouteTreeBundle\Tree\RouteFilterInterface: '@app.custom_route_filter'
Default Title:
If title is omitted, the builder uses the route’s name (e.g., homepage → "Homepage"). Override this in config/packages/becklyn_route_tree.yaml:
becklyn_route_tree:
default_title_generator: 'app.route_title_generator'
Parameters Handling:
Default parameters are merged with runtime parameters. To disable merging, set:
becklyn_route_tree:
merge_parameters: false
How can I help you explore Laravel packages today?