Как вывести шаблон Twig из базы данных в symfony2 - PullRequest
29 голосов
/ 19 ноября 2011

Я работаю над приложением, написанным на symfony2, и хочу отправить электронное письмо после некоторого действия / события ... проблема в том, что пользователи могут определить что-то вроде "шаблонов электронной почты", которые хранятся в БД, например, в виде простой строки: "Это электронное письмо от {{user}}", и мне нужно отобразить тело для электронной почты из этого шаблона ... В документации Symfony по этой ссылке: http://symfony.com/doc/2.0/cookbook/email.html#sending-emails, методы для визуализации - это $ this-> renderViewи он ожидает ссылку на файл как «bundle: controller: file.html.twig», но мой шаблон представляет собой базу данных в виде простой строки ... Как я могу отрендерить его?

Ответы [ 11 ]

29 голосов
/ 17 марта 2016

Twig_Loader_String устарела и всегда была разработана для внутреннего использования. Использование этого загрузчика настоятельно не рекомендуется.

Из документа API:

Этот загрузчик НИКОГДА не должен использоваться. Он существует только для внутреннего 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' }
20 голосов
/ 10 августа 2012

Это должно работать.Замените "Hello {{name}}" на текст вашего шаблона и заполните массив, который передается в функцию рендеринга, любыми необходимыми вам переменными.

$env = new \Twig_Environment(new \Twig_Loader_String());
echo $env->render(
  "Hello {{ name }}",
  array("name" => "World")
);
10 голосов
/ 29 мая 2012

Клонируйте собственную службу 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'));
8 голосов
/ 19 ноября 2011

Twigengine не поддерживает рендеринг строк. Но есть доступный пакет, который добавляет это поведение под названием TwigstringBundle .

Добавляет сервис $this->get('twigstring'), который вы можете использовать для визуализации ваших строк.

6 голосов
/ 18 сентября 2018

Вот решение, которое работает с Symfony 4 (и, возможно, также с более старыми версиями, хотя я его не тестировал) и позволяет вам работать с шаблонами, хранящимися в базе данных, так же, как вы работали бы с шаблонами в файловой системе.

В этом ответе предполагается, что вы используете Doctrine, но его относительно легко адаптировать, если вы используете другую библиотеку базы данных.

Создайте сущность Template

Это примеркласс, который использует аннотации, но вы можете использовать любой метод конфигурации, который вы уже используете.

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;

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 будет проверятьбаза данных в первую очередь.Если getTemplate в вашем DatabaseLoader вернет ноль, Twig проверит файловую систему.Если шаблон недоступен в базе данных или файловой системы, Twig выдаст Twig_Error_Loader.

6 голосов
/ 30 мая 2014

Лучший способ сделать это - использовать template_from_string функцию ветки.

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

См. документацию template_from_string

Узнайте, почему это не хорошая идея использовать Twig_Loader_Chain или Twig_Loader_String для этой цели на этом выпуске github от stof .

1 голос
/ 26 марта 2013
0 голосов
/ 25 октября 2016

Мне недавно пришлось внедрить CMS, используемую несколькими сторонами, где каждая сторона могла полностью настроить свои шаблоны.Чтобы добиться этого, я реализовал пользовательский загрузчик Twig.

Самым сложным было придумать соглашение об именах для шаблонов, гарантированно не перекрывающихся с какими-либо существующими шаблонами, например <organisation_slug>!AppBundle:template.html.twig.Если шаблон не был настроен, шаблон AppBundle:template.html.twig должен быть загружен как запасной шаблон.

Однако это невозможно с загрузчиком цепей (AFAIK), поскольку там имя шаблона нельзя изменить.Поэтому мне пришлось вставить загрузчик по умолчанию (т. Е. Цепочку загрузчиков) в мой загрузчик и использовать его для загрузки резервного шаблона.

Другим решением было бы передать стек запросов или сеанс загрузчику шаблонов, сделаввозможно автоматически обнаружить организацию, но это сложно, потому что компонент безопасности зависит от шаблонной подсистемы, вызывая проблемы круговой зависимости.

0 голосов
/ 31 января 2013

Хороший пример вы можете найти здесь: http://twig.sensiolabs.org/doc/recipes.html#using-a-database-to-store-templates

0 голосов
/ 07 декабря 2012

FYI, эта функция была предложена , чтобы быть добавлена ​​ в ядре Twig с 1.11.0 , но ее необходимо будет активировать с помощью developper.

...