PHP Slim 4 + Twig:如何在所有页面加载相同动态数据?
在Slim 4中全局注入动态菜单数据到Twig模板的方案
以下几种方案都能避免在每个控制器重复代码,让header.twig能直接加载菜单数据:
方案1:通过Twig全局变量注入(推荐)
直接把菜单数据注册为Twig全局变量,所有模板无需额外处理就能访问,最适合静态或低动态性的菜单。
- 抽离菜单逻辑到服务类
把HomeController里的菜单查询、分类逻辑单独封装成服务,方便复用:
// src/Service/MenuService.php namespace App\Service; use PDO; class MenuService { private $db; public function __construct(PDO $db) { $this->db = $db; } public function getMegaMenuData(): array { // 你的菜单查询逻辑 $stmt = $this->db->query('SELECT * FROM menu_items ORDER BY parent_id, sort_order'); $items = $stmt->fetchAll(PDO::FETCH_ASSOC); // 处理成mega menu需要的结构(比如树形、分组) return $this->buildMenuStructure($items); } private function buildMenuStructure(array $items): array { $structuredMenu = []; // 这里写你的菜单分类逻辑 return $structuredMenu; } }
- 在容器中注册服务
用PHP-DI的话,在容器配置文件里添加:
// config/container.php return [ App\Service\MenuService::class => function (\Psr\Container\ContainerInterface $c) { return new App\Service\MenuService($c->get(PDO::class)); }, ];
- 注册Twig全局变量
在Twig初始化的地方,把菜单数据注入为全局变量:
// src/Factory/TwigFactory.php $twig = \Twig\Twig::create($settings['paths'], $settings['options']); $menuService = $container->get(App\Service\MenuService::class); $twig->addGlobal('mega_menu', $menuService->getMegaMenuData()); return $twig;
- 在header.twig中直接使用
{# templates/partials/header.twig #} <div class="mega-menu"> {% for column in mega_menu %} <div class="menu-column"> <h3>{{ column.title }}</h3> <ul> {% for item in column.items %} <li><a href="{{ item.url }}">{{ item.name }}</a></li> {% endfor %} </ul> </div> {% endfor %} </div>
方案2:用路由中间件全局注入
如果菜单需要根据请求动态调整(比如用户权限、当前路由),用中间件在每个请求前注入数据。
- 创建菜单中间件
// src/Middleware/MenuMiddleware.php namespace App\Middleware; use App\Service\MenuService; use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ServerRequestInterface as Request; use Psr\Http\Server\RequestHandlerInterface as RequestHandler; use Slim\Views\Twig; class MenuMiddleware { private $twig; private $menuService; public function __construct(Twig $twig, MenuService $menuService) { $this->twig = $twig; $this->menuService = $menuService; } public function process(Request $request, RequestHandler $handler): Response { // 可以根据request参数调整菜单数据 $menuData = $this->menuService->getMegaMenuData(); $this->twig->getEnvironment()->addGlobal('mega_menu', $menuData); return $handler->handle($request); } }
- 绑定中间件到全局或路由组
// src/routes.php // 全局所有路由都生效 $app->add(App\Middleware\MenuMiddleware::class); // 或者只给特定路由组绑定 $app->group('/', function ($group) { $group->get('', \App\Controller\HomeController::class . ':index'); $group->get('/about', \App\Controller\AboutController::class . ':index'); })->add(App\Middleware\MenuMiddleware::class);
- header.twig中直接使用
mega_menu变量即可
方案3:Twig函数延迟加载
如果菜单不是所有页面都需要,用Twig函数实现延迟加载,只有调用时才查询数据。
- 注册Twig函数
// 在Twig初始化文件中 $twig->addFunction(new \Twig\TwigFunction('get_mega_menu', function () use ($container) { $menuService = $container->get(App\Service\MenuService::class); return $menuService->getMegaMenuData(); }));
- 在header.twig中调用
{% set mega_menu = get_mega_menu() %} <div class="mega-menu"> {% for item in mega_menu %} <!-- 渲染逻辑 --> {% endfor %} </div>
额外优化
如果菜单数据很少变动,可以在MenuService的getMegaMenuData方法中加入缓存逻辑(比如文件缓存、Redis),减少数据库查询次数。
内容的提问来源于stack exchange,提问作者cesarcarlos
相关产品推荐
相关产品推荐

