Email 如何在symfony2中从数据库中呈现细枝模板

Email 如何在symfony2中从数据库中呈现细枝模板,email,symfony,render,twig,Email,Symfony,Render,Twig,我正在处理用symfony2编写的应用程序,我想在一些行动/活动之后发送电子邮件。。。问题是,用户可以定义类似于“电子邮件模板”的东西,它存储在db中,类似于简单字符串,例如:“这是来自{{user}}的某封电子邮件”,我需要为该电子邮件呈现正文,该邮件应该使用该模板 在这个链接的symfony文档中:render view的方法是$this->renderView,它需要文件的路径,比如“bundle:controller:file.html.twig”,但我的模板是数据库中的简单字符串 如何

我正在处理用symfony2编写的应用程序,我想在一些行动/活动之后发送电子邮件。。。问题是,用户可以定义类似于“电子邮件模板”的东西,它存储在db中,类似于简单字符串,例如:“这是来自{{user}}的某封电子邮件”,我需要为该电子邮件呈现正文,该邮件应该使用该模板

在这个链接的symfony文档中:render view的方法是$this->renderView,它需要文件的路径,比如“bundle:controller:file.html.twig”,但我的模板是数据库中的简单字符串


如何渲染它?

从Twig 1.10开始,Twig引擎不支持渲染字符串。但是有一个包可以添加这种行为,称为

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


(截至19年9月,Twig的当前版本为2.X,版本3即将发布;因此这只适用于Twig的非常旧的版本)。

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

这应该行得通。将“Hello{{name}}”替换为模板文本,并使用所需的任何变量填充传递到呈现函数的数组

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

仅供参考,此功能将在的Twig核心中,但需要由开发人员激活。

使用Symfony 2.2,您可以使用

最好的方法是使用
template\u from\u string
twig函数

{{ include(template_from_string("Hello {{ name }}")) }}
{{ include(template_from_string(page.template)) }}


查看为什么使用
细枝加载器链
细枝加载器字符串
用于此目的不是一个好主意。

细枝加载器字符串已被弃用,并且始终是为内部使用而设计的。强烈反对使用此加载程序

从API文档:

不得使用此加载程序。它只存在于细枝内部 目的。当将此加载程序与缓存机制一起使用时,您应该 知道每次模板内容更新时都会生成一个新的缓存密钥 “更改”(缓存键是模板的源代码)。如果 你不想看到你的缓存失控,你需要 请自行清除旧的缓存文件

另请查看此问题:


我所知道的从字符串源加载模板的最佳方法是:

从控制器: 如下所述:

从细枝模板: 如下所述:

请注意,“template_from_string”函数在默认情况下不可用,需要加载。在symfony中,您可以通过添加新服务来实现这一点:

# services.yml
services:
    appbundle.twig.extension.string:
        class: Twig_Extension_StringLoader
        tags:
            - { name: 'twig.extension' }

我最近不得不实现一个由多方使用的CMS,其中各方可以完全定制他们的模板。为了实现这一点,我实现了一个定制的细枝加载器

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

但是,这在链装入器(AFAIK)中是不可能的,因为无法修改模板名称。因此,我必须将默认加载程序(即加载程序链)注入到我的加载程序中,并使用它加载回退模板


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

这里有一个与Symfony 4一起使用的解决方案(可能还有更老的版本,尽管我还没有测试过),它允许您使用存储在数据库中的模板,就像使用文件系统中的模板一样

这个答案假设您使用的是条令,但如果您使用的是另一个数据库库,则相对容易适应

创建模板实体 这是一个使用注释的示例类,但是您可以使用已经使用的任何配置方法

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

包括来自另一个细枝模板的内容(可以在数据库或文件系统中):

{{include('welcome.html.twig')}

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

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

在每种情况下,Twig都会首先检查数据库。如果
DatabaseLoader
中的
getTemplate
返回null,Twig会检查文件系统。如果数据库或文件系统中没有模板,Twig会抛出
Twig\u错误\u加载程序
这对我有用:

$loader = new \Twig\Loader\ArrayLoader([
    'Temp_File.html' => 'Hello {{ name }}!',
]);
$twig = new \Twig\Environment($loader);

echo $twig->render('Temp_File.html', ['name' => 'Fabien']);

$this->renderView()
应该返回一个简单的字符串。您是否尝试过
返回“some_string”
,而不是
返回$this->renderView()
,看看会发生什么?你应该看看这个捆绑包,它能准确地处理你想要的东西。自2011年以来,情况发生了变化。对于现在遇到这个问题的人,请阅读细枝文档中的相关配方:我同意,一个不使用边捆绑包的简洁解决方案。我认为这是最好的解决方案,也是最好的解决方案
<?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;
}
<?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]);  
    }
}
services:
    App\Twig\Loader\DatabaseLoader:
        tags:
        - { name: twig.loader }
$loader = new \Twig\Loader\ArrayLoader([
    'Temp_File.html' => 'Hello {{ name }}!',
]);
$twig = new \Twig\Environment($loader);

echo $twig->render('Temp_File.html', ['name' => 'Fabien']);