Как отобразить шаблон Twig из базы данных в symfony2
Я работаю над приложением, написанным в symfony2, и я хочу отправить электронное письмо после некоторого действия/события... проблема в том, что пользователи могут определить что-то вроде "шаблонов электронной почты", которые хранятся в БД как простая строка, например: "это некоторая электронная почта от {{ user}}", и мне нужно отобразить тело для электронной почты из этого шаблона... В документации symfony по этой ссылке:http://symfony.com/doc/2.0/cookbook/email.html#sending-emails methos для рендеринга - $this - >renderView и он ожидает ссылку на файл как " bundle:controller: file.формат html.twig", но мой шаблон-это база данных как простая строка... Как я могу это передать?
11 ответов
вот решение, которое работает с Symfony 4 (и, возможно, более старыми версиями, хотя я его не тестировал) и позволяет работать с шаблонами, хранящимися в базе данных, так же, как вы работали бы с шаблонами в файловой системе.
этот ответ предполагает, что вы используете доктрину, но относительно легко адаптироваться, если вы используете другую библиотеку баз данных.
создайте сущность шаблона
это пример класса, который использует аннотации, но вы можете использовать любой метод конфигурации, который вы уже используете.
src / сущность / шаблон.в 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');
в том числе от еще один шаблон веточки (который может быть в базе данных или файловой системы):
{{ include('welcome.html.twig') }}
рендеринг в строку (где $twig
пример Twig\Environment
)
$html = $twig->render('email.html.twig')
в каждом из этих случаев Twig сначала проверит базу данных. Если getTemplate
в своем DatabaseLoader
возвращает null, затем Twig проверяет файловую систему. Если шаблон недоступен в базе данных или файловая система, Twig будет бросьте Twig_Error_Loader
.
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' }
Это должно работать. Замените" Hello {{ name}} " текстом шаблона и заполните массив, переданный в функцию рендеринга, любыми переменными, которые вам нужны.
$env = new \Twig_Environment(new \Twig_Loader_String());
echo $env->render(
"Hello {{ name }}",
array("name" => "World")
);
клонировать уроженца 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'));
Twigengine не поддерживает строки рендеринга. Но есть доступный пакет, который добавляет Это поведение под названием TwigstringBundle.
добавляет $this->get('twigstring')
сервис, который вы можете использовать для рендеринга строк.
лучший способ сделать это-использовать 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.
с Symfony 2.2 вы можете использовать Twig_Chain_Loader
как зарегистрировать другой (пользовательский) загрузчик Twig в среде Symfony2?
вы можете найти хороший пример здесь : http://twig.sensiolabs.org/doc/recipes.html#using-a-database-to-store-templates
недавно мне пришлось реализовать CMS, используемую несколькими сторонами, где каждая сторона могла полностью настроить свои шаблоны. Для этого я реализовал пользовательский загрузчик Twig.
самой сложной частью было придумать соглашение об именах для шаблонов, гарантированных не перекрываться с любыми существующими шаблонами, например <organisation_slug>!AppBundle:template.html.twig
.
В случае, если шаблон не был настроен, шаблон AppBundle:template.html.twig
должен быть загружен как резервный шаблон.
однако, это невозможно с цепным загрузчиком (AFAIK), потому что там имя шаблона не может быть изменено. Поэтому мне пришлось ввести загрузчик по умолчанию (т. е. цепочку загрузчиков) в мой загрузчик и использовать его для загрузки резервного шаблона.
другим решением было бы передать стек запросов или сеанс загрузчику шаблонов, что позволяет автоматически обнаруживать организацию, но это сложно, потому что компонент безопасности зависит от подсистемы шаблонов, вызывая круговые проблемы зависимости.
$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);