Tutoriel Vidéo Symfony Créer un bloc partagé

Télécharger la vidéo

Aujourd'hui je vous propose de répondre à une question qui m'est posé assez souvent sur Symfony : Comment gérer un élément commun entre plusieurs pages (sidebar, footer) qui utilise des éléments dynamique.

La mauvaise approche : Variable globale

La première approche, qui peut sembler la plus évidente au premier abord, est d'utiliser une variable globale que l'on va injecter dans toutes les vues Twig.

# config/packages/twig.yaml
twig:
    globals:
        sidebar: '@App\Twig\SidebarService'

Cette approche n'est pas idéale car la variable va être chargée avec twig (même si la variable n'est finalement pas utilisée par une de vos pages).

La bonne approche : Créer une fonction twig

Une meilleure solution est de créer une fonction twig que l'on pourra ensuite utiliser seulement si on en a besoin.

On peut se contenter de renvoyer des variables à utiliser dans la vue :

<!-- partial/sidebar.twig -->
{% set data = sidebar() %}
<h1>Dernier articles</h1>

<ul>
{% for post in data.posts %}
  <li>{{ post.title }}</li>
{% endfor %}
</ul>

Mais on peut aussi utiliser l'injection de dépendance pour utiliser twig dans notre fonction.

<?php

namespace App\Twig;

use App\Repository\CommentRepository;
use App\Repository\PostRepository;
use Twig\Environment;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;

class SidebarExtension extends AbstractExtension
{

    /**
     * @var PostRepository
     */
    private $postRepository;
    /**
     * @var Environment
     */
    private $twig;
    /**
     * @var CommentRepository
     */
    private $commentRepository;

    public function __construct(
        PostRepository $postRepository,
        CommentRepository $commentRepository,
        Environment $twig
    ) {
        $this->postRepository = $postRepository;
        $this->twig = $twig;
        $this->commentRepository = $commentRepository;
    }

    public function getFunctions(): array
    {
        return [
            new TwigFunction('sidebar', [$this, 'renderSidebar'], ['is_safe' => ['html']])
        ];
    }

Ce qui permet de directement rendre le contenu HTML.

    private function renderSidebar(): string
    {
        $posts = $this->postRepository->findForSidebar();
        $comments = $this->commentRepository->findForSidebar();
        return $this->twig->render('partials/sidebar.html.twig', [
            'posts'    => $posts,
            'comments' => $comments
        ]);
    }

Cache cache !

Si le contenu de votre bloc est peu dynamique il peut être intéréssant d'utiliser du cache afin de ne pas répéter des appels externes inutilement.

Il faudra utiliser un système de cache qui implémente l'interface TagAwareInterface afin d'associer des tags à notre cache pour une invalidation plus simple par la suite.

    public function getSidebar (): string {
        return $this->cache->get('sidebar', function (ItemInterface $item) {
            $item->tag(['comments', 'posts']);
            return $this->renderSidebar();
        });
    }