如何在symfony2中从数据库中呈现Twig模板

时间:2011-11-18 20:25:35

标签: email symfony render twig

我正在研究用symfony2编写的应用程序,我想在一些动作/事件之后发送电子邮件...问题是,用户可以定义类似“电子邮件模板”的东西,例如存储在db中的简单字符串,例如:“这是来自{{user}}的一些电子邮件”,我需要为该模板的电子邮件呈现正文... 在这个链接的symfony文档中:http://symfony.com/doc/2.0/cookbook/email.html#sending-emails用于渲染的方法是$ this-> renderView,它希望引用文件为“bundle:controller:file.html.twig”,但我的模板是数据库为简单的字符串... 我该如何呈现它?

12 个答案:

答案 0 :(得分:29)

Twig_Loader_String已弃用,无论如何总是设计用于内部使用。强烈建议不要使用此加载程序。

来自API doc:

  

不应使用此装载程序。它仅适用于Twig内部   目的。使用带有缓存机制的加载器时,您应该这样做   知道每次模板内容时都会生成一个新的缓存键   "变更" (缓存键是模板的源代码)。如果   你不需要看到你的缓存失去控制   请自己清理旧的缓存文件。

另请查看此问题:https://github.com/symfony/symfony/issues/10865

我知道从String源加载模板的最佳方法是:

来自控制器:

$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' }

答案 1 :(得分:19)

这应该有效。将“Hello {{name}}”替换为模板文本,并使用您需要的任何变量填充传递给render函数的数组。

$env = new \Twig_Environment(new \Twig_Loader_String());
echo $env->render(
  "Hello {{ name }}",
  array("name" => "World")
);

答案 2 :(得分:10)

克隆本机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'));

答案 3 :(得分:8)

Twigengine不支持渲染字符串。但是有一个捆绑可用,它添加了这种称为TwigstringBundle的行为。

它添加了$this->get('twigstring')服务,您可以用它来渲染字符串。

答案 4 :(得分:6)

最好的方法是使用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_ChainTwig_Loader_String

答案 5 :(得分:6)

这是一个可与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;
}

最起码的字段是filenamesource,但是最好包含last_updated,否则您将失去缓存的好处。

创建一个DatabaseLoader类

src / Twig / Loader / DatabaseLoader.php

<?php
namespace App\Twig;

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') }}

呈现为字符串(其中$twigTwig\Environment的实例)

$html = $twig->render('email.html.twig')

在每种情况下,Twig都会首先检查数据库。如果getTemplate中的DatabaseLoader返回null,则Twig将检查文件系统。如果模板在数据库或文件系统中不可用,则Twig将抛出Twig_Error_Loader

答案 6 :(得分:1)

答案 7 :(得分:1)

为我工作:

$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

答案 8 :(得分:0)

仅供参考,自suggested起,此功能在Twig核心中added1.11.0,但需要由开发人员激活。

答案 9 :(得分:0)

答案 10 :(得分:0)

我最近不得不实施多方使用的CMS,每个方都可以完全自定义他们的模板。为此,我实现了一个自定义的Twig Loader。

最困难的部分是提出模板的命名约定,保证不与任何现有模板重叠,例如<organisation_slug>!AppBundle:template.html.twig。 如果未自定义模板,则必须将模板AppBundle:template.html.twig作为后备模板加载。

但是,使用Chain Loader(AFAIK)无法实现这一点,因为无法修改模板名称。因此,我必须将默认加载器(即加载器链)注入我的加载器并使用它来加载回退模板。

另一种解决方案是将请求堆栈或会话传递给模板加载器,从而可以自动检测组织,但这很困难,因为安全组件依赖于模板子系统,导致循环依赖问题。

答案 11 :(得分:-7)

  $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);