Symfony中Twig加载数据库模板时无法include文件系统模板的问题
问题根源
手动实例化Twig时仅配置了ArrayLoader作为模板加载器,该加载器的检索范围只有初始化时传入数组内的模板,不会扫描项目文件系统下的模板目录,因此无法找到tmp/header.html.twig这类存放在磁盘上的模板文件,无论include语句写在入口模板还是数据库存储的字符串模板中,都会抛出模板不存在的报错。
解决方案
方案1:链式组合加载器(保留手动创建Twig的逻辑)
使用ChainLoader将数组加载器与文件系统加载器串联,让Twig优先查找内存中动态定义的模板,找不到时自动去磁盘模板目录检索,替换原有loader初始化代码即可:
- 先引入加载器对应的类(注意补全命名空间前缀
Twig\Loader\):
use Twig\Loader\ChainLoader; use Twig\Loader\FilesystemLoader; use Twig\Loader\ArrayLoader;
- 替换原来的$loader初始化逻辑:
// 初始化文件系统加载器,传入Symfony项目默认的模板目录路径 $filesystemLoader = new FilesystemLoader($this->getParameter('kernel.project_dir') . '/templates'); // 保留原有数组加载器,存放动态拼接的模板 $arrayLoader = new ArrayLoader([ 'test.html.twig' => '{{ include(template_from_string(html)) }}', ]); // 链式组合加载器,注意顺序:优先匹配动态数组模板,再匹配磁盘文件模板 $loader = new ChainLoader([$arrayLoader, $filesystemLoader]); // 后续创建Twig环境、注册扩展的逻辑完全不用改动 $twig = new \Twig\Environment($loader);
改完后字符串模板、入口模板里的include标签都能正常加载磁盘上的模板文件,之前注册的StringLoaderExtension、AssetExtension等扩展全部正常生效。
方案2:直接复用Symfony容器内置的Twig服务(更推荐)
不需要手动实例化Twig、重复注册各类扩展,直接用Symfony框架已经初始化好的Twig服务,仅追加动态模板即可,避免漏配扩展导致部分Twig功能失效:
/** * @Route("/test/for/conservatoire/of/music", name="test") */ public function test(PageRepository $pageRepo, SectionRepository $sectionRepo): Response { $page = $pageRepo->findOneByName('about'); $html = "<html><body><h1>Hello pal</h1></body></html>"; $html .= '{% include("tmp/header.html.twig") %}'; // 直接取框架内置的Twig服务,所有扩展、加载器、配置都已预配置完成 $twig = $this->get('twig'); // 追加动态模板到加载器 $twig->getLoader()->addLoader(new ArrayLoader([ 'test.html.twig' => '{{ include(template_from_string(html)) }}' ])); $response = new Response(); $response->setContent($twig->render('test.html.twig', ['html' => $html])); $response->setStatusCode(Response::HTTP_OK); $response->headers->set('Content-Type', 'text/html'); return $response; }
这种方式不需要手动配置asset、path、debug等扩展,Symfony原生提供的所有Twig能力(比如控制器渲染、表单渲染、全局变量等)都可以直接在数据库存储的模板中使用。
内容的提问来源于stack exchange,提问作者Josiah
相关产品推荐
相关产品推荐

