How to render Twig template from database in symfony2

前端 未结 11 959
闹比i
闹比i 2020-12-08 04:21

I\'m working on application written in symfony2 and I want to send email after some action/event... the problem is, that the users can define something like \"email template

相关标签:
11条回答
  • 2020-12-08 05:02

    Here's a solution that works with Symfony 4 (and possibly older versions as well, although I haven't tested it) and allows you to work with templates stored in the database the same way you would work with templates in the filesystem.

    This answer assumes you're using Doctrine, but is relatively easy to adapt if you're using another database library.

    Create the Template entity

    This is an example class that uses annotations, but you can use whatever configuration method you're already using.

    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;
    }
    

    The bare minimum fields are filename and source, but it's a very good idea to include last_updated or you'll lose the benefits of caching.

    Create a DatabaseLoader class

    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]);  
        }
    }
    

    The class is relatively simple. getTemplate looks up the template filename from the database, and the rest of the methods use getTemplate to implement the interface that Twig needs.

    Add the DatabaseLoader to your service config

    config/services.yaml

    services:
        App\Twig\Loader\DatabaseLoader:
            tags:
            - { name: twig.loader }
    

    Now you can use your database templates in the same way as filesystem templates.

    Rendering from a controller:

    return $this->render('home.html.twig');

    Including from another Twig template (which can be in the database or filesystem):

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

    Rendering to a string (where $twig is an instance of Twig\Environment)

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

    In each of these cases, Twig will check the database first. If getTemplate in your DatabaseLoader returns null, Twig will then check the filesystem. If the template isn't available in the database or the filesystem, Twig will throw a Twig_Error_Loader.

    0 讨论(0)
  • 2020-12-08 05:02

    This should work. Replace "Hello {{ name }}" with your template text, and fill the array that is passed into the render function with any variables that you need.

    $env = new \Twig_Environment(new \Twig_Loader_String());
    echo $env->render(
      "Hello {{ name }}",
      array("name" => "World")
    );
    
    0 讨论(0)
  • 2020-12-08 05:03

    The best way to do it is to use template_from_string twig function.

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

    See documentation of template_from_string

    See why it is not a good idea to use Twig_Loader_Chain or Twig_Loader_String for that purpose on this github issue by stof.

    0 讨论(0)
  • 2020-12-08 05:04

    I recently had to implement a CMS used by multiple parties where each party could completely customize their templates. To achieve this I implemented a custom Twig Loader.

    The most difficult part was coming up with a naming convention for the templates guaranteed not to overlap with any existing templates, for example <organisation_slug>!AppBundle:template.html.twig. In case the template was not customised, the template AppBundle:template.html.twig would have to be loaded as fallback template.

    However, this is not possible with the Chain Loader (AFAIK) because there the template name cannot be modified. Therefore I had to inject the default loader (i.e. the loader chain) into my loader and use it to load the fallback template.

    Another solution would be to pass the request stack or the session to the template loader, making it possible to automatically detect the organisation, but this is difficult because the security component depends on the templating subsystem, causing circular dependency issues.

    0 讨论(0)
  • 2020-12-08 05:08

    As of Twig 1.10, the Twig Engine doesn't support rendering strings. But there is a bundle available which adds this behavior called TwigstringBundle.

    It adds the $this->get('twigstring') service wich you can use to render your strings.

    (As September '19, the current version of Twig is 2.X, and version 3 is around the corner; so this is only applies to very old versions of Twig).

    0 讨论(0)
  • 2020-12-08 05:08

    This work for me:

    $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

    0 讨论(0)
提交回复
热议问题