Comment rendre le modèle de brindille à partir de la base de données en symfony2

je travaille sur la demande écrite en symfony2 et je veux envoyer un e-mail après une action/événement... le problème est que les utilisateurs peuvent définir quelque chose comme "modèles de courrier électronique" qui stocke dans la base de données comme une chaîne simple, par exemple: "Ceci est un courrier électronique de {{ user }}" et j'ai besoin de rendre le corps pour le courrier électronique à partir de ce modèle... Dans la documentation de symfony à partir de ce lien: http://symfony.com/doc/2.0/cookbook/email.html#sending-emails le methos pour rendre est $this->renderView et il attend de référence de fichier comme "bundle:contrôleur:fichier.HTML.twig", mais mon modèle est Base de données comme chaîne simple... Comment puis-je le rendre?

25
demandé sur Karol Fiturski 2011-11-19 00:25:34

11 réponses

Voici une solution qui fonctionne avec Symfony 4 (et peut-être aussi avec des versions plus anciennes, bien que je ne l'ai pas testé) et qui vous permet de travailler avec des gabarits stockés dans la base de données de la même manière que vous travailleriez avec des gabarits dans le système de fichiers.

cette réponse suppose que vous utilisez la Doctrine, mais est relativement facile à adapter si vous utilisez une autre bibliothèque de base de données.

créer le modèle d'entité

C'est un exemple de classe qui utilise des annotations, mais vous pouvez utiliser n'importe quelle méthode de configuration que vous utilisez déjà.

src/Entité/Modèle.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;
}

les champs minimaux sont filename et source , mais c'est une très bonne idée d'inclure last_updated ou vous perdrez les avantages de la mise en cache.

créer une classe 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)]);  
    }
}

la classe est relativement simple. getTemplate recherche le nom du fichier de modèle à partir de la base de données, et le reste des méthodes utilisent getTemplate pour mettre en œuvre l'interface dont Twig a besoin.

ajouter le DatabaseLoader à votre configuration de service

config/services.yaml

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

Maintenant vous pouvez utiliser votre modèles de base de données de la même manière que les modèles de système de fichiers.

Rendu d'un contrôleur:

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

y compris d'un autre modèle de brindille (qui peut être dans la base de données ou le système de fichiers):

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

Rendu à une chaîne (où $twig est une instance de Twig\Environment )

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

dans chacun de ces cas, Twig vérifiera d'abord la base de données. Si getTemplate dans votre DatabaseLoader retourne nul, Twig vérifiera alors le système de fichiers. Si le modèle n'est pas disponible dans la base de données ou le système de fichiers, Twig lancera un Twig_Error_Loader .

1
répondu Clamburger 2018-09-18 13:22:37

Twig_Loader_String est déprécié et a toujours été conçu pour un usage interne de toute façon. L'utilisation de ce chargeur est fortement déconseillée.

de L'API doc:

ce chargeur ne doit jamais être utilisé. Il n'existe que pour les brindilles internes but. Lorsque vous utilisez ce chargeur avec un mécanisme de cache, vous devriez sachez qu'une nouvelle clé de cache est générée à chaque fois qu'un contenu de template "change" (la clé de cache étant le code source du modèle.) Si vous ne voulez pas voir votre cache se développe hors de contrôle, vous devez prenez soin de vider vous-même l'ancien fichier cache.

consultez aussi ce numéro: https://github.com/symfony/symfony/issues/10865


le meilleur moyen que je connaisse pour charger un modèle à partir d'une source de chaîne de caractères sont:

D'un contrôleur:

$template = $this->get('twig')->createTemplate('Hello {{ name }}');
$template->render(array('name'=>'World'));

comme décrit ici: http://twig.sensiolabs.org/doc/recipes.html#loading-a-template-from-a-string

D'un modèle de brindille:

{{ include(template_from_string("Hello {{ name }}", {'name' : 'Peter'})) }}

comme décrit ici: http://twig.sensiolabs.org/doc/functions/template_from_string.html

notez que la fonction' template_from_string ' n'est pas disponible par défaut et doit être chargée. En symfony vous feriez ceci en ajoutant un nouveau service:

# services.yml
services:
    appbundle.twig.extension.string:
        class: Twig_Extension_StringLoader
        tags:
            - { name: 'twig.extension' }
22
répondu Atan 2016-04-06 15:31:19

ça devrait marcher. Remplacez "Hello {{ name }}" par votre texte de modèle, et remplissez le tableau qui est passé dans la fonction de rendu avec toutes les variables dont vous avez besoin.

$env = new \Twig_Environment(new \Twig_Loader_String());
echo $env->render(
  "Hello {{ name }}",
  array("name" => "World")
);
17
répondu adavea 2012-08-10 17:39:52

clonez le service natif twig et remplacez le chargeur du système de fichiers par le chargeur à ficelles natif:

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

exemple D'utilisation dans un contrôleur:

$this->get('my.twigstring')->render('Hello {{ name }}', array('name' => 'Fabien'));
10
répondu Philipp Rieber 2012-05-29 12:18:14

le Twigengine ne supporte pas les chaînes de rendu. Mais il y a un paquet disponible qui ajoute ce comportement appelé TwigstringBundle .

il ajoute le service $this->get('twigstring') que vous pouvez utiliser pour rendre vos chaînes.

8
répondu Kristian Zondervan 2011-11-19 09:36:07

La meilleure façon de le faire est d'utiliser template_from_string brindille de fonction.

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

voir documentation de template_from_string

Voir pourquoi il est pas une bonne idée d'utiliser Twig_Loader_Chain ou Twig_Loader_String pour cette fin sur cette github question par stof .

6
répondu Quentin 2014-05-30 14:23:15

pour information , Cette caractéristique était suggéré pour être ajouté dans le noyau de la brindille à partir de 1.11.0 , mais il sera nécessaire d'être activé par le développeur.

0
répondu yvoyer 2012-12-06 20:30:03
0
répondu me987654323 2013-01-31 11:47:02

j'ai récemment dû mettre en œuvre un SGC utilisé par plusieurs parties où chaque partie pourrait complètement personnaliser leurs modèles. Pour ce faire, j'ai mis en place un chargeur de brindilles personnalisé.

la partie la plus difficile a été de trouver une convention d'appellation pour les gabarits garantis ne pas chevaucher avec les gabarits existants, par exemple <organisation_slug>!AppBundle:template.html.twig . Dans le cas où le modèle n'était pas personnalisé, le modèle AppBundle:template.html.twig devait être chargé comme modèle de rechange.

cependant, ce N'est pas possible avec le chargeur à chaîne (AFAIK) parce que là le nom du modèle ne peut pas être modifié. Par conséquent, j'ai dû injecter le chargeur par défaut (c'est-à-dire la chaîne du chargeur) dans mon chargeur et l'utiliser pour charger le modèle de rechange.

une autre solution serait de passer la pile de requêtes ou la session au chargeur de gabarits, ce qui permettrait de détecter automatiquement l'organisation, mais cela est difficile car le composant de sécurité dépend du sous-système templating, provoquant des problèmes de dépendance circulaire.

0
répondu MauganRa 2016-10-25 08:51:45
  $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);
-7
répondu kiran 2012-06-14 06:55:14