如何在 symfony2 中从数据库中渲染 Twig 模板
Posted
技术标签:
【中文标题】如何在 symfony2 中从数据库中渲染 Twig 模板【英文标题】:How to render Twig template from database in symfony2 【发布时间】:2012-01-01 14:04:05 【问题描述】:我正在开发用 symfony2 编写的应用程序,我想在一些操作/事件之后发送电子邮件......问题是,用户可以定义像“电子邮件模板”这样的东西,它们像简单的字符串一样存储在 db 中,例如:“这是来自 user 的一些电子邮件”,我需要为应该使用该模板的电子邮件呈现正文...
在来自此链接的 symfony 文档中:https://symfony.com/doc/2.0/cookbook/email/email.html#sending-emails 渲染视图的方法是 $this->renderView,它需要文件路径,例如“bundle:controller:file.html.twig”,但我的模板很简单来自数据库的字符串...
如何渲染它?
【问题讨论】:
$this->renderView()
应该返回一个简单的字符串。你试过return "some_string"
,而不是return $this->renderView()
,只是想看看会发生什么?
你应该看看这个包github.com/Remixjobs/RjEmailBundle,它完全可以处理你想要的。
自 2011 年以来情况发生了变化。对于现在遇到这个问题的任何人 - 请阅读 Twig 文档中的相关食谱:twig.sensiolabs.org/doc/…twig.sensiolabs.org/doc/…
【参考方案1】:
Twig_Loader_String 已被弃用,并且始终是为内部使用而设计的。强烈建议不要使用此加载器。
来自 API 文档:
永远不要使用这个加载器。它只存在于 Twig 内部 目的。当将此加载器与缓存机制一起使用时,您应该 知道每次模板内容都会生成一个新的缓存键 “更改”(缓存键是模板的源代码)。如果 你不想看到你的缓存失去控制,你需要 自己清理旧缓存文件。
也可以看看这个问题:https://github.com/symfony/symfony/issues/10865
我知道从字符串源加载模板的最佳方法是:
来自控制器:
$template = $this->get('twig')->createTemplate('Hello name ');
$template->render(array('name'=>'World'));
如此处所述:http://twig.sensiolabs.org/doc/recipes.html#loading-a-template-from-a-string
来自树枝模板:
include(template_from_string("Hello name ", 'name' : 'Peter'))
如此处所述:http://twig.sensiolabs.org/doc/functions/template_from_string.html
注意,'template_from_string' - 函数默认不可用,需要加载。在 symfony 中,您可以通过添加一个新服务来做到这一点:
# services.yml
services:
appbundle.twig.extension.string:
class: Twig_Extension_StringLoader
tags:
- name: 'twig.extension'
【讨论】:
在我的情况下,我无法在 template_from_string 中发送变量(可能它具有相同的范围?),所以我在 inlude 之前使用了 % set name = 'Peter' %【参考方案2】:这应该可行。将“Hello name ”替换为您的模板文本,并使用您需要的任何变量填充传递给渲染函数的数组。
$env = new \Twig_Environment(new \Twig_Loader_String());
echo $env->render(
"Hello name ",
array("name" => "World")
);
【讨论】:
我同意,一个简洁的解决方案,不使用侧包。 我认为这是最好的解决方案和最好的答案:) 这行得通。但是,我注意到默认情况下未启用自定义树枝扩展。所以这里的问题:***.com/questions/16383522/… 此解决方案并不适合所有人:它不加载 twig 扩展,不缓存已编译的模板(较慢),并且不允许任何类型的模板引用:% extends %或 % include %。 我在 cmets 中读到 Twig_Loader_String(已弃用)它会填满缓存...【参考方案3】:这是一个适用于 Symfony 4(也可能是旧版本,虽然我没有测试过)的解决方案,它允许您使用存储在数据库中的模板,就像使用文件系统中的模板一样。
此答案假定您使用的是 Doctrine,但如果您使用的是其他数据库库,则相对容易适应。
创建模板实体
这是一个使用注解的示例类,但您可以使用您已经在使用的任何配置方法。
src/Entity/Template.php
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Table(name="templates")
* @ORM\Entity
*/
class Template
/**
* @var int
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* @var string
*
* @ORM\Column(type="string", nullable=false)
*/
private $filename;
/**
* @var string
*
* @ORM\Column(type="text", nullable=false)
*/
private $source;
/**
* @var \DateTime
*
* @ORM\Column(type="datetime", nullable=false)
*/
private $last_updated;
最少的字段是filename
和source
,但最好包含last_updated
,否则您将失去缓存的好处。
创建一个 DatabaseLoader 类
src/Twig/Loader/DatabaseLoader.php
<?php
namespace App\Twig\Loader;
use App\Entity\Template;
use Doctrine\ORM\EntityManagerInterface;
use Twig_Error_Loader;
use Twig_LoaderInterface;
use Twig_Source;
class DatabaseLoader implements Twig_LoaderInterface
protected $repo;
public function __construct(EntityManagerInterface $em)
$this->repo = $em->getRepository(Template::class);
public function getSourceContext($name)
if (false === $template = $this->getTemplate($name))
throw new Twig_Error_Loader(sprintf('Template "%s" does not exist.', $name));
return new Twig_Source($template->getSource(), $name);
public function exists($name)
return (bool)$this->getTemplate($name);
public function getCacheKey($name)
return $name;
public function isFresh($name, $time)
if (false === $template = $this->getTemplate($name))
return false;
return $template->getLastUpdated()->getTimestamp() <= $time;
/**
* @param $name
* @return Template|null
*/
protected function getTemplate($name)
return $this->repo->findOneBy(['filename' => $name]);
类比较简单。 getTemplate
从数据库中查找模板文件名,其余方法使用getTemplate
实现Twig需要的接口。
将 DatabaseLoader 添加到您的服务配置中
config/services.yaml
services:
App\Twig\Loader\DatabaseLoader:
tags:
- name: twig.loader
现在您可以像使用文件系统模板一样使用数据库模板。
从控制器渲染:
return $this->render('home.html.twig');
从另一个 Twig 模板中包含(可以在数据库或文件系统中):
include('welcome.html.twig')
呈现为字符串(其中$twig
是Twig\Environment
的一个实例)
$html = $twig->render('email.html.twig')
在每种情况下,Twig 都会首先检查数据库。如果您的DatabaseLoader
中的getTemplate
返回null,则Twig 将检查文件系统。如果模板在数据库或文件系统中不可用,Twig 将抛出Twig_Error_Loader
。
【讨论】:
对于相同但使用 PDO,您可以看到 twig.symfony.com/doc/2.x/… 对于那些想要实现这一点的人,请注意,这将使cache:clear
和 cache:warmup
依赖于数据库...如果您通过 docker 等进行部署,这可能会破坏您的映像构建过程。 ..
您可以随时将安装后脚本更改为cache:clear --no-warmup
,但需要注意这一点...
你现在必须控制缓存【参考方案4】:
克隆原生 twig
服务并将文件系统加载器替换为原生 twig 字符串加载器:
<service id="my.twigstring" class="%twig.class%">
<argument type="service" id="my.twigstring.loader" />
<argument>%twig.options%</argument>
</service>
<service id="my.twigstring.loader" class="Twig_Loader_String"></service>
控制器内的使用示例:
$this->get('my.twigstring')->render('Hello name ', array('name' => 'Fabien'));
【讨论】:
从 Symfony2.2 开始,您可以简单地添加一个新的加载程序来定义服务并使用 twig.loader 标记对其进行标记。这样,twig 将使用 Twig_Loader_Chain 尝试使用每个定义的加载器加载模板。看看symfony.com/doc/current/reference/dic_tags.html#twig-loader 如果你需要定义树枝加载器的优先级,看看DifaneTwigDatabaseBundle,我在github.com/webgriffe/DifaneTwigDatabaseBundle 的fork 中做了一些更改(没有推回原始仓库,因为我仍然需要查看代码)【参考方案5】:最好的方法是使用template_from_string
twig 函数。
include(template_from_string("Hello name "))
include(template_from_string(page.template))
见documentation of template_from_string
了解为什么在this github issue by stof 上使用Twig_Loader_Chain
或Twig_Loader_String
是不是一个好主意。
【讨论】:
甜蜜而简单。省时间。救生员。 +1【参考方案6】:从 Twig 1.10 开始,Twig 引擎不支持渲染字符串。但是有一个可用的捆绑包添加了这种行为,称为TwigstringBundle。
它添加了$this->get('twigstring')
服务,您可以使用它来呈现您的字符串。
(截至 19 年 9 月,Twig 的当前版本是 2.X,版本 3 即将发布;所以这只适用于非常旧的 Twig 版本)。
【讨论】:
【参考方案7】:这对我有用:
$loader = new \Twig\Loader\ArrayLoader([
'Temp_File.html' => 'Hello name !',
]);
$twig = new \Twig\Environment($loader);
echo $twig->render('Temp_File.html', ['name' => 'Fabien']);
https://twig.symfony.com/doc/2.x/api.html
【讨论】:
还在 3.x 文档中 https://twig.symfony.com/doc/3.x/api.html.【参考方案8】:仅供参考,从 1.11.0 开始,此功能在 Twig 核心中的 suggested 将变为 added,但需要由开发人员激活。
【讨论】:
【参考方案9】:在 Symfony 2.2 中,您可以使用 Twig_Chain_Loader How to register another (custom) Twig loader in Symfony2 environment?
【讨论】:
请将所有信息添加到您的答案中,而不是仅链接到外部网站【参考方案10】:我最近不得不实施一个供多方使用的 CMS,每一方都可以完全自定义他们的模板。为了实现这一点,我实现了一个自定义的 Twig Loader。
最困难的部分是为模板制定一个命名约定,保证不与任何现有模板重叠,例如<organisation_slug>!AppBundle:template.html.twig
。
如果模板未自定义,则必须将模板 AppBundle:template.html.twig
作为备用模板加载。
但是,对于 Chain Loader (AFAIK),这是不可能的,因为无法修改模板名称。因此我不得不将默认加载器(即加载器链)注入我的加载器并使用它来加载回退模板。
另一种解决方案是将请求堆栈或会话传递给模板加载器,从而可以自动检测组织,但这很困难,因为安全组件依赖于模板子系统,导致循环依赖问题。
【讨论】:
【参考方案11】: $message = \Swift_Message::newInstance()
->setSubject('Hello Email')
->setFrom('send@example.com')
->setTo('recipient@example.com')
->setBody('hai its a sample mail')
;
$this->get('mailer')->send($message);
【讨论】:
这与问题无关。请包含一个使用 Twig 的示例。以上是关于如何在 symfony2 中从数据库中渲染 Twig 模板的主要内容,如果未能解决你的问题,请参考以下文章