Projet de hack de Sylius : SyliusUiBundle

Table des matières
Suite du premier live
Ce projet a démarré un vendredi soir par un live sur Twitter. Le but est de monter un site utilisant un maximum de bundle Sylius sans la partie e-commerce. Je ne veux pas d'un Sylius amputer de ses fonctionnalités alors qu'elles sont dans le projet.
Si vous ne comprenez pas de quoi je parle, c'est que vous n'avez peut-être pas lu le premier billet sur l'initialisation du projet de hack de Sylius. Prenez le temps de le lire pour avant de continuer pour vous permettre de comprendre la suite. Nous allons rechercher le meilleur bundle à utiliser pour refaire l'apparence de l'administration Sylius rapidement, mais également tout le CSS et JavaScript nécessaire.
Outils utilisés
- Symfony Cli (Linux, Windows, Mac) https://symfony.com/download
- PHP 7.4
- PHP SQLite (base de données pour le projet Symfony)
- Composer https://getcomposer.org/download/
- Symfony 5 https://symfony.com
- Sylius 1.10 https://sylius.com/fr
- NodeJs 12
- Yarn 1
Déterminer le bundle à installer
Dans cette étape, je veux obtenir une page web sur mon projet qui ressemble à l'administration de Sylius.
En parcourant les bundles installés dans le dossier vendor/sylius du projet Sylius, je remarque AdminBundle. Cela semble une bonne piste, mais en regardant son contenu, je m'aperçois qu'il contient des éléments liés à la partie e-commerce de Sylius. Selon les règles de mon projet, je ne peux pas l'utiliser.
En regardant les dépendances de l'AdminBundle, je me rends compte qu'il y a un UiBundle. Il contient des éléments de base de l'interface utilisés par AdminBundle, mais rien de spécifique au e-commerce.
Ce bundle semble très bien, mais a un défaut majeur, la documentation est celle de Sylius. Il n'y a donc aucune information pour l'utiliser séparément. Ce n'est pas vraiment un problème, car en ayant le code source de Sylius, il est possible de le lire pour comprendre son comportement.
Installer SyliusUiBundle
Ce bundle fait partie intégrante de Sylius. Il dispose cependant de son dépôt de code source ce qui me permet de l'installer comme n'importe quelle dépendance dans mon projet grâce à la commande suivante : composer require sylius/ui-bundle
L'installation ajoute le bundle et les dépendances. Mais aucune configuration ni aide pour savoir quelle est la prochaine étape.
Comment l'utiliser ?
Intégration de l'UiBundle dans le projet
Dans tout projets Symfony utilisant Twig comme moteur de rendu, il existe un fichier de base Twig nommé base.html.twig
ou layout.html.twig
ou comme bon vous semble...
Pour comprendre, comment fonctionne l'UiBundle, la meilleure façon de faire est de regarder comment l'AdminBundle l'utilise. Le fichier de base de l'AdminBundle est donc vendor/sylius/sylius/src/Sylius/Bundle/AdminBundle/Resources/views/layout.html.twig
.
Je copie donc le contenu du fichier dans mon fichier themes/MyTheme/templates/base.html.twig
. Je replace les balises (pour les CSS et le JS) et blocs (body) que je souhaite conserver.

J'ajoute quelques balises pour afficher quelques informations par-ci par-là. Essentiellement pour repérer les emplacements.
Et je tente d'afficher la page après avoir démarré le serveur web avec la commande symfony serve -d
du client Symfony. La page s'affiche et c'est vraiment très moche. Ce n'est pas du tout le résultat attendu. Mais il y a un début de mise en page qui se dessine.

Il manque clairement les CSS pour la mise en page.
Ajout des assets
Depuis Symfony 5, il est nécessaire d'utiliser les outils de développement JS pour gérer les assets CSS et JS. La documentation de Symfony recommande d'utiliser le bundle WebpackEncore pour ajouter dans Symfony une passerelle entre les outils JS du front et Symfony (back).
Pour l'installer, c'est extrêmement simple grâce au plug-in Flex pour Composer. La commande composer req encore
installera tout le nécessaire et indiquera même la prochaine étape (ça change de l'UiBundle)
Suivons le guide et exécutons la commande yarn install
pour installer les dépendances puis yarn dev
pour compiler.
Maintenant que la compilation fonctionne, comment fait Sylius pour compiler les assets de l'administration ?
Retournons sur le projet Sylius et commençons à suivre la piste en partant du fichier de configuration webpack.config.js
présent à la racine du projet.
La configuration Webpack qui gère l'admin fait référence à un seul point d'entrée (EntryPoint) assets/admin/entry.js
et le contenu de ce fichier nous renvoie au fichier sylius/bundle/AdminBundle/Resources/private/entry
.

Je n'ai aucun dossier sylius/bundle
dans mon dossier asset. En retournant dans la configuration de Webpack, il y a une référence en haut et en bas vers une configuration d'un alias pour sylius/bundle
.

En suivant les chemins (de l'alias puis du fichier), je trouve le fichier souhaité. Je copie le contenu du fichier pour l'ajouter dans mon fichier assets/app.js
.

En regardant la correspondance dans la configuration Webpack, je supprime toutes les lignes qui font référence à AdminBundle.
Dans la configuration Webpak de mon projet, j'ajoute les alias pour sylius/bundle
et sylius/ui-resources
en me basant sur le projet Sylius et en personnalisant pour mon projet.
Lors des tentatives successives de compilation des assets, j'ai des erreurs liées au manque de librairies Sementic UI et au loader Sass pour gérer les fichiers css à compiler.
Pour ajouter le framework CSS Sementic UI, j'exécute la commande yarn add --dev semantic-ui-css
puis pour le loader Sass cette commande yarn add sass-loader@^12.0.0 sass --dev
.
Malgré l'ajout de Sass, la compilation ne fonctionne pas, car il est nécessaire de l'activer dans la configuration de WebPack. La compilation fonctionne.

Dans le fichier base.html.twig
de mon thème, j'active l'exécution des fonctions encore_entry_link_tags
et encore_entry_script_tags
pour ajouter les fichiers CSS et JS au chargement des pages.

Le menu de gauche
Il est maintenant temps d'ajouter des éléments dans le menu de gauche. Dans Sylius, le KnpMenuBundle est utilisé pour gérer tous les menus. Il est très pratique, car tout est géré dans PHP et une fonction Twig suffit pour obtenir le rendu du menu.
KnpMenuBundle dispose de sa propre documentation ce qui aide à l'ajouter dans le projet.
Nous avons besoin d'une classe qui définira le menu. Elle sera écrite dans le fichier src/Menu/AppMainMenu.php
il n'est pas nécessaire d'étendre une classe abstraite ou implémentée une interface. Nous avons besoin d'une méthode. Ça sera generateMenu
et elle retournera un objet ItemInterface
de Knp.
Voici le contenu de la classe:
<?php
/**
* @copyright Macintoshplus (c) 2021
* Added by : Macintoshplus at 08/10/2021 22:35
*/
declare(strict_types=1);
namespace AppMenu;
use KnpMenuItemInterface;
use KnpMenuMenuFactory;
use SyliusBundleUiBundleMenuEventMenuBuilderEvent;
use SymfonyContractsEventDispatcherEventDispatcherInterface;
final class AppMainMenu
{
public const EVENT_NAME = 'app.menu.main';
/**
* @var MenuFactory
*/
private $menuFactory;
/**
* @var EventDispatcherInterface
*/
private $eventDispatcher;
public function __construct(MenuFactory $menuFactory, EventDispatcherInterface $eventDispatcher)
{
$this->menuFactory = $menuFactory;
$this->eventDispatcher = $eventDispatcher;
}
public function generateMenu(array $options): ItemInterface
{
$menu = $this->menuFactory->createItem('app.main');
$admin = $menu->addChild('admin')->setLabel('Admin');
$admin->addChild('Item1')->setLabel('Item 1')->setLabelAttribute('icon', 'folder')
;
$admin->addChild('Item2')->setLabel('Item 2')->setLabelAttribute('icon', 'folder')
;
$admin->addChild('Item3')->setLabel('Item 3')->setLabelAttribute('icon', 'folder')
;
$this->eventDispatcher->dispatch(new MenuBuilderEvent($this->menuFactory, $menu), self::EVENT_NAME);
return $menu;
}
}
Avant de retourner le menu, la classe émet un évènement. Cela permet à quiconque de venir manipuler le menu avant qu'il ne soit affiché (grâce à un Listener ou Subscriber). J'ai repris ici le fonctionnement de Sylius, car je le trouve intéressant et que la classe nécessaire est présente dans l'UiBundle.
L'autoconfiguration et l'autowirring de Symfony ont besoin d'aide pour ce service. J'ajoute donc la configuration suivante dans le fichier config/services.yaml
:
AppMenuAppMainMenu:
arguments:
$menuFactory: '@knp_menu.factory'
tags:
- { name: knp_menu.menu_builder, method: 'generateMenu', alias: 'app.main'}
Le tag ajouté dans la configuration indique au MenuBundle comment se nomme le menu (alias) et quelle méthode appelés sur le service pour obtenir les items du menu.
Maintenant dans Twig, il reste à appeler la fonction knp_menu_render
. Ici aussi, je vais voir comment c'est réalisé dans l'AdminBundle.
Dans le bloc sidebar
, le contenu est maigre avec cette ligne {{ sylius_template_event('sylius.admin.layout.sidebar') }}
. Après quelques recherches, je comprends que c'est un système d'évènement lié au rendu des templates de Sylius. Ils permettent de personnaliser le contenu facilement grâce à des blocs.
Pour connaitre les blocs affichés lors d'un évènement de template, il est nécessaire de savoir comment en ajouter un. Le document de Sylius nous apprend donc que c'est sous la clé de configuration sylius_ui.events
que tous sont défini.
Pour afficher la configuration d'un bundle Symfony, j'utilise la commande de débug bin/console debug:configuration sylius_ui events
et il m'affiche toute la configuration. Je cherche donc dans le résultat le nom de l'événement sylius.admin.layout.sidebar
pour en suite aller chercher les fichiers.
Je finis par arriver dans le fichier vendor/sylius/sylius/src/Sylius/Bundle/AdminBundle/Resources/views/Layout/_menu.html.twig
qui contient le champ pour le moteur de recherche et l'appel de la fonction twig knp_menu_render
. Je copie le contenu du fichier dans le bloc sidebar
de mon template de base.html.twig
(je n'utilise pas les événements des templates dans mon projet pour l'instant).

Lors de l'actualisation de la page, le menu apparaît avec les icônes à droite du titre du menu.

Mais le rendu est déjà très bien. Comme je souhaite avoir une bonne ressemblance avec Sylius, j'ajoute le code HTML pour le logo au-dessus du champ de recherche.
Pour placer les icônes du menu à gauche, je retourne donc dans l'AdminBundle pour trouver les instructions CSS nécessaires que j'ajoute dans un nouveau fichier assets/styles/_ui.scss
. Je ne manque pas de l'ajouter dans le fichier assets/app.js
pour qu'il soit pris en compte lors de la compilation des assets.
Après compilation des assets et rechargement de la page, bingo ! L'apparence est conforme à ce que j'attendais.

Conclusion
L'objectif a été atteint en une heure de travail. C'est très rapide ! Ce que montre cette première étape est qu'il est important de savoir lire le code et surtout ne pas hésiter à aller dans le code source des outils utilisés pour les décortiquer.
Que faire maintenant ? Le projet n'est pas terminé, j'aimerais arriver à utiliser d'autre bundle dans ce projet.
Par exemple avec l'UiBundle est venu le SyliusGridBundle qui permet d'afficher des données sous forme de tableau, d'ajouter une pagination, un filtre, des actions globales, des actions de masse (bulk) et des actions pour chaque ligne de données.
Il y a également, le SyliusRessourceBundle qui permet de gérer des entités Doctrine comme des ressources Sylius et faciliter leurs utilisations dans l'administration de Sylius. Mais pourrais-je les utiliser indépendamment ? Ou uniquement ensemble ?
La suite de l'aventure est disponible avec l'utilisation du GridBundle sans le ResourceBundle (qui arrivera plus tard)
Le projet de Hack de Sylius sur GitHub