Menu Accueil Trucs et astuces Réalisations Contact

phpBlocNote

Trucs et astuces

Un bundle pour Symfony 6.1 (et plus)

Pour la petite histoire...

En bonne feignasse, quand j'ai besoin d'un nouvel outil, je cherche d'abord si quelque chose existe.
Puis, en bonne développeuse exigeante, si rien n'existe ou si l'existant ne correspond pas à mes attentes, je développe mon propre outil.

C'est ainsi qu'est né phpBlocNote il y a des années.

Bien sûr, au fil du temps, il a (beaucoup) évolué. Mon instance perso porte même un autre nom (non, je ne vous le dirai pas, bande de petits curieux ^^).
Cet outil m'a aussi beaucoup servi de "base d'essai" pour tester des frameworks (oui, plusieurs en plus de 15 ans) puis pour éprouver les montées de versions du framework gagnant (Symfony, en version 6.1 à l'heure où j'écris).

Puis, d'autres outils se sont ajoutés... j'ai aujourd'hui 7 web apps sur la même instance de Symfony !
Et depuis un certain temps, je voulais en faire des bundles, histoire de séparer, organiser proprement tout mon petit bazar (car aucune app n'a vraiment de lien avec ses copines).

Et je me suis cassé les dents plusieurs fois

Depuis que j'ai migré en Symfony 5, la volonté de "bundliser" mes apps (qui n'étaient que 3 à l'époque) revient régulièrement...
Mais faute de temps (ou de motivation), je ne m'y suis réellement collée que ces derniers jours. Et comme j'en ai ch*é, je me suis dit que, tant qu'à faire, j'allais partager le résultat. Qui sait, ça peut servir à d'autres ;)

Créer un bundle Symfony 6.2 complet

Dans cet exemple, le bundle est relativement simple mais comprend notamment des templates, quelques assets, des traductions et forcément, le routing.

Documentation Symfony

J'ai consulté la documentation officielle Symfony concernant les bundles.
Mais cette documentation ne m'a pas permis, à elle seule, de créer et rendre mon bundle fonctionnel.

Arborescence et organisation des fichiers

Après avoir pas mal tâtonné pour trouver une structure de fichiers fonctionnelle, voici à quoi je suis arrivée : arborescence
Vous reconnaîtrez sans doute l'arborescence standard d'un projet Symfony.

Comme j'aurai, à terme, plusieurs bundles, j'ai choisi d'ajouter un niveau "organisation" ou "vendor" (DelPlop), mais je pense qu'on peut s'en passer.
Ce répertoire principal se place à la racine du projet.
Le code du bundle se place dans un sous-répertoire du nom du bundle avec le suffixe (WishListBundle).
Enfin, la structure est celle d'un projet Symfony classique, telle que mentionnée dans la documentation officielle.

Chargement des services

Pour cette partie, je n'ai pas eu vraiment de souci (à part avoir voulu faire un truc bien compliqué en premier lieu ><), la documentation officielle présente une solution simple et efficace.

Le code de la classe principale du bundle :
<?php
declare(strict_types=1);

namespace DelPlop\WishListBundle;

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Symfony\Component\HttpKernel\Bundle\AbstractBundle;

class DelPlopWishListBundle extends AbstractBundle
{
    public function prependExtension(ContainerConfigurator $container, ContainerBuilder $builder): void
    {
        $container->import('../config/services.yaml');
    }
}
            
Et le contenu de config/services.yaml :
services:
    _defaults:
        autowire: true
        autoconfigure: true

    DelPlop\WishListBundle\:
        resource: '../src/'
        exclude:
            - '../src/Entity/'

    DelPlop\WishListBundle\Controller\:
        resource: '../src/Controller'
        tags: [ 'controller.service_arguments' ]
            

Activer mon bundle

C'est probablement la partie qui m'a le plus bloquée et pourtant, ça semble si logique une fois fait ><
Composer sera votre ami. Il faut éditer le fichier composer.json à la racine du projet puis en créer un dans le bundle (à la racine également).

Dans composer.json du projet, ajouter :
[...]
"repositories": [
    {
        "type": "path", "url": "DelPlop/WishListBundle"
    }
],
"require": {
    "delplop/wishlist-bundle": "dev-master"
}
            
Le fichier composer.json du bundle :
{
    "name": "delplop/wishlist-bundle",
    "description": "My Wish-list Symfony bundle",
    "type": "symfony-bundle",
    "require": {
        "php": ">=8.1",
        "symfony/framework-bundle": "6.1.*"
    },
    "autoload": {
        "psr-4": {
            "DelPlop\\WishListBundle\\": "src/"
        }
    },
    "license": "proprietary",
    "authors": [
        {
            "name": "Del Plop",
            "email": "<votre email>"
        }
    ]
}
            
Bien sûr, ajustez selon vos goûts (licence, nom du ou des auteurs, description, etc) ; veillez simplement à ce que le nom corresponde à ce que vous ajoutez dans le fichier composer.json du projet.
composer update
Et on est presque prêt ;)

Exemple de contrôleur et template

Le contrôleur
<?php

declare(strict_types=1);

namespace DelPlop\WishListBundle\Controller;

use DelPlop\WishListBundle\Repository\WishListUserRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;

class UserController extends AbstractController
{
    public function index(WishListUserRepository $wishListUserRepository): Response
    {
        return $this->render('@DelPlopWishList/user/list.html.twig', [
            'users' => $wishListUserRepository->findAll()
        ]);
    }
}

            
Le template associé :
{% extends '@DelPlopWishList/wishlist.html.twig' %}

{% trans_default_domain 'wishlist' %}

{% block title %}{{ ('users.list_long'|trans) }}{% endblock %}

{% block body %}
    <h1 class="w3-card-4 w3-padding-16">{{ ('users.list_long'|trans) }}</h1>

    <table class="w3-table w3-striped w3-bordered">
        <tr class="w3-theme">
            <th>{{ ('users.name'|trans) }}</th>
        </tr>
        {% for user in users %}
            <tr>
                <td>
                    <a href="{{ path('wishlist_user_articles', {id: user.id}) }}" title="{{ ('articles.see_user_articles'|trans({username: user.user.username})) }}">{{ user.user.username }}</a>
                </td>
            </tr>
        {% endfor %}
    </table>
{% endblock %}

            
Rien d'extraordinaire là-dedans.
Le trans_default_domain (wishlist) correspond au nom des fichiers de trad (wishlist.fr.yaml / wishlist.en.yaml).

Routing

Toujours dans l'idée d'avoir plusieurs outils qui cohabitent, j'ai choisi de préfixer mes routes par bundle (plus lisible avec bin/console debug:router), voici donc un extrait du fichier config/routes.yaml général :
wishlist_routes:
    name_prefix: 'wishlist_'
    resource: '@DelPlopWishListBundle/config/routes.yaml'
            

Assets

Après avoir lancé bin/console assets:install, les assets du bundle se trouvent dans public/bundles/delplopwishlist/.

© phpBlocNote (2006 - 2024)