Tous les articles par jbnahan

A propos jbnahan

Consultant dans un cabinet d'expertise technique, j'aide les entreprises en difficulté avec leur projet Web.

Utilisation de GridBundle hors de Sylius

Le résultat final

Dans le premier article, j’ai démarré un projet Symfony avec comme but d’utiliser des bundles Sylius sans la partie e-commerce. J’y ai installé le SyliusThemeBundle.

Dans le second article, j’ai continué le projet en installant le SyliusUIBundle. Ce bundle n’est pas prévu pour être utilisé hors de Sylius et m’a demandé un peu de rétro ingénierie pour qu’il fonctionne correctement.

Dans ce troisième article, je vais utiliser SyliusGridBundle. Ce bundle est prévu pour être utilisé en dehors de Sylius mais conjointement avec le SyliusResourceBundle. SyliusGridBundle permet la gestion simple (via de la configuration) de grille de données avec pagination, tri, filtre, actions globales, actions groupées ou action pour une ligne de données.

L’absence du SyliusResourceBundle, m’a posé de nombreux de problème et j’ai encore une fois réalisé de l’ingénierie inverse pour comprendre comment cela fonctionne.

Dans le métier de développeur, il est très important de savoir décortiquer le fonctionnement d’un logiciel pour pouvoir le modifier. Avoir accès au code source des logiciels PHP est un ajout majeur qui nous évite de devoir utiliser d’autre outils parfois plus complexe à prendre en main.

Comme pour la première étape, j’ai réalisé un thread twitter pour vous partager mon avancé. Si les thread vous intéressent, suivez moi sur Twitter.

Dans cet article, je vais reprendre les grandes étapes et les erreurs les plus difficiles à résoudre. Le but étant d’afficher une grille de données, il est nécessaire d’avoir une base de données et des données. Pour cela, le projet sera un Blog.

Ajouter une entité doctrine

Pour aller vite, j’ai utilisé la commande bin/console make:entity BlogPost pour initialiser mon entité.

L’entité générée par le Maker

Pour gérer les dates de création et de mise à jour, j’utiliser les fonctions de rappel (callback) lié a la vie de l’entité. A savoir PrePersist et PreUpdate.

J’ai transformé les setter en fonction de rappel Doctrine.

Pour terminé, il est obligatoire de générer une migration Doctrine avec la commande bin/console doctrine:migrations:diff et de l’exécuter avec la commande bin/console doctrine:migrations:migrate

La classe de migration de la base SQLite

Le projet est maintenant prêt pour afficher la liste des blog posts.

Installation du bundle

Comme pour toute dépendance directe d’un projet, l’installation est réalisé par la commande composer req sylius/grid-bundle

Il n’y a rien a installé car c’est une dépendance obligatoire de SyliusUiBundle.

Cependant, j’ai eu plus tard une erreur lié a une dépendance manquante : PagerFanta.

L’erreur fut double car j’avais installé en premier lieu la version 3 alors que le Sylius utilise encore la version 2.

Pour finir voici les dépendances obligatoires non installées avec le Bundle :

composer req babdev/pagerfanta-bundle ^2.0 pagerfanta/pagerfanta ^2.0 pagerfanta/twig ^2.0

Configuration de la grille

La configuration d’une grille se fait exclusivement dans un fichier de configuration YAML. En se basant sur la documentation, la configuration est aisée.

Définition de la grille

Etant donné que le ResourceBundle n’est pas installé, je dois définir tous les détails de chaque action : nom de la route, paramètres…

Configuration des actions

Définition des routes

En l’absence du ResourceBundle (oui il facilite énormément la vie), je doit donc définir manuellement chaque route nécessaire pour ma ressource.

Définition des routes sans contrôleur

Les contrôleurs ne sont pas défini car je ne vais pas les utiliser. Le GridBundle a besoin de générer les URL.

Le contrôleur pour l’index

La page d’affichage de la grille correspond à l’index (le listing) et j’ai besoin d’un contrôleur pour la route app_blog_post_index.

J’ajoute donc la classe BlogPostIndexController avec un affichage d’un template vide pour l’instant.

Pour ajouter l’affichage de la grille, je me suis basé sur le projet Sylius 1.10 pour remonter jusqu’à l’appel de la fonction Twig « sylius_grid_render ».

L’appel de la fonction Twig « sylius_grid_render » dans le projet Sylius.

J’ai ensuite eu de nombreuses erreurs sur les données fournis, la méthode du repository qui ne retournait pas un QueryBuilder, et enfin, j’ai obtenu ce contrôleur:

<?php

namespace App\Controller;

use Sylius\Component\Grid\Parameters;
use Sylius\Component\Grid\Provider\GridProviderInterface;
use Sylius\Component\Grid\View\GridViewFactoryInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;

class BlogPostIndexController extends AbstractController
{
    public function __invoke(
        Request               $request,
        GridProviderInterface $gridProvider,
        GridViewFactoryInterface $gridViewFactory
    )
    {
        $gridView = $gridViewFactory->create(
            $gridProvider->get('app_blog_post'),
            new Parameters($request->query->all())
        ) ;


        return $this->render('blogpost/index.html.twig', [
            'resources' => $gridView,
        ]);
    }
}

La configuration de la grille et le repository ont également changé:

Configuration de la grille correcte pour l’obtention du QueryBuilder.
La fonction dans le Respository.

Le menu

Pour permettre un accès aisé à la page de listing des blog post, j’ai modifié le menu principal pour y mettre un lien directe vers la page.

La configuration du lien dans le menu princial.

Lors de l’ajout de l’UiBundle dans le projet, j’avais également configuré un menu principal pour que l’apparence du site soit similaire à celle de Sylius.

Les templates

Le template de la page de listing des blog post est réduite au maximum. Lors de l’affichage des première tentative d’affichage j’ai eu des problèmes de template pour les actions. Aucune d’elles n’est définie.

J’ai dû copier et modifié les templates de Sylius présents dans l’AdminBundle :

{% import '@SyliusUi/Macro/buttons.html.twig' as buttons %}
{% set options = action.options %}
{% set path = options.link.url|default(path(options.link.route, options.link.parameters|default({'id': data.id}))) %}

{{ buttons.delete(path, action.label, true, data.id) }}

puis les configurer:

sylius_grid:
  templates:
    action:
      update: "Grid/Action/update.html.twig"
      delete: "Grid/Action/delete.html.twig"
      show: "Grid/Action/show.html.twig"

L’affichage de cette première page fut satisfaisante:

Aucune données a afficher.

L’ajout des données

Maintenant que la page sans données s’affiche, il est nécessaire d’ajouter des données dans la base de données.

Pour n’importe quel projet, j’aurais utilisé des Fixtures pour charger les données. Mais par manque de temps, j’ai préférer utiliser le contrôleur Home (ajouté lors du démarrage du projet) pour initialiser des BlogPost et les sauvegarder en base de données.

Ajout de données dans la base de données grâce à un contrôleur.

Conclusion

Pour conclure, cette étape s’annonçait plus simple de par la connaissance que j’en avais. Mais l’absence du ResourceBundle m’a fait comprendre que ces deux Bundles sont très puissants ensemble.

J’aurai sûrement gagné du temps en les utilisant conjointement, mais je n’aurais pas appris son fonctionnement interne.

Tous le travail que j’ai réalisé et que je vous ai décris au cours de ces 3 articles avait déjà été réalisé. Tout ce travail est rassembler dans le projet Monofony. Je pourrais en faire un article de présentation si cela vous intéressent (dite le sur Twitter)

La liste des blogs posts terminés.

Le code source du projet est disponible sur le dépôt GitHub du Hack de Sylius.

Projet de hack de Sylius : SyliusUiBundle

La page à la fin de la première étape.

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

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.

Mon nouveau template de base

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.

La page Home avec le nouveau template.

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.

La définition des chemins des dossiers contenant les assets de Sylius.

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.

L’ajout des alias dans la configuration Webpack.

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.

Mon fichier 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.

Activation de Sass dans la configuration de WebPack

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 rendu de la page est bien plus agréable.

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

Le code de la sidebar

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

Le résultat est déjà satisfaisant mais les icones des items ne sont pas au bon endroit.

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.

Résultat final

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 ?

En attendant le prochain live Twitter sur le sujet, j’ai besoin de vous ! Quelles entités Doctrine ajouter pour avoir de la matière lors du prochain live ? Donnez-moi vos idées via les réseaux sociaux ou le slack Symfony.

Le projet de Hack de Sylius sur GitHub

Démarrage d’un projet de hack de Sylius

Le projet

Après avoir lu un article sur la bonne utilisation des thèmes dans Sylius d’Alexandre Balmes sur la gestion des thèmes dans Sylius, je me suis lancé un petit défi : détourner certains Bundles de Sylius pour en faire un site. Cela changera des tests logiciels et des conseils sur la sécurité.

La première étape du défi a pris la forme d’un live coding sur Twitter.

Sylius

Sylius est un framework basé sur le framework Symfony pour réaliser un site de e-commerce. L’un des principaux intérêts que je lui trouve c’est la rapidité de mise à jour. Contrairement à beaucoup d’autres outils se basant sur Symfony, Sylius reste toujours sur la dernière version de Symfony. Il y a toujours un peu plus de temps de développement lors du passage à une version majeure de Symfony.

Dans le cadre du projet de hack, je veux utiliser que certaines parties de Sylius. Voici les premiers éléments utilisés:

  • Le SyliusThemeBundle: pour gérer plusieurs apparences sur le site. Potentiellement, plusieurs sites avec la même base de code.
  • Le SyliusUiBundle : pour avoir l’apparence de l’admin Sylius.

D’autres éléments utiles sont dans le SyliusAdminBundle, mais comme il contient les éléments liés à l’e-commerce, je ne veux pas l’ajouter dans le projet.

Environnement de développement du projet

Pour la réalisation de ce projet, j’ai besoin d’un projet Symfony fraichement installé et d’une installation fraiche de la dernière version de Sylius.

Les deux projets seront donc utilisés : l’un pour coder ; l’autre pour décortiquer le fonctionnement de ce que je souhaite reproduire.

Avertissement

Ce projet est une preuve de concept sans aucun but d’en faire un projet à distribuer. Vous pouvez en faire ce que vous souhaitez, car il reste libre sous licence MIT.
Il existe sûrement d’autre façon de faire pour obtenir une application ressemblant à Sylius. Le but ici est démonstratif et éducatif.

Lors des Lives Twitter, dans les vidéos et dans les articles, je vous montre que les succès pour vous éviter les fausses pistes.

Outils utilisés

Le déroulé de la première étape pas à pas.

Installation des deux projets

Je commence par initialiser un projet Sylius 1.10 avec la commande suivante :

composer create-project sylius/sylius-standard test_sylius_110

Et le nouveau projet avec le client Symfony :

symfony new --full test_bundle_sylius

Les commandes initialisent un nouveau projet et téléchargent les dépendances PHP avec Composer.

Pour le projet Sylius, je n’ai pas besoin d’installer les dépendances JS pour l’instant.

Ajout d’une page dans le projet Symfony

Pour ajouter une page rapidement dans le projet, rien de tel que le MakerBundle (je l’utilise que pendant les formations Symfony en temps normal).

En exécutant la commande bin/console make:controller HomeController, j’ai une page par défaut qui est généré (contrôleur PHP et template Twig).

Par défaut l’URL de la page est donc /home. Pour pouvoir y accéder, j’ai besoin d’un serveur Web sur ma machine.

J’utilise pour cela le client Symfony en exécutant cette commande symfony serve -d dans le terminal après m’être placé dans le dossier du projet Symfony.

La commande donne l’URL de la page d’accueil. En cliquant dessus vous obtenez la page d’accueil.

Pour ma part, j’ai eu une erreur de driver SQL introuvable.

An exception occired in driver: could not find driver

Cette erreur est liée à la configuration par défaut de Symfony pour la base de données. Symfony est configuré pour utiliser une base de données Postgres. Ne l’ayant pas utilisé sur cet ordinateur, PHP n’a pas le pilote.

Pour corriger l’erreur, je modifie la configuration par défaut de la base de données en activant SQLite et en désactivant Postgres.

fichier .env changement de la configuration de la base de données.

Maintenant, j’ai bien les pages par défaut de Symfony et ma page /home.

Ajout du thème bundle

Comme indiqué plus haut ce bundle permet d’avoir pour une même application plusieurs thèmes. Ces thèmes surchargent les fichiers présents dans le dossier templates présent à la racine du projet, mais également ce qui est présent dans les bundles tiers.

Pour l’ajouter au projet, j’utilise Composer en exécutant la commande suivante : composer require sylius/theme-bundle.

J’ajoute en suite la configuration pour le Bundle (il ne dispose pas de recette Flex) dans le fichier config/packages/sylius_theme.yaml avec le contenu suivant:

sylius_theme:
  sources:
    filesystem: ~
  context: AppContextThemeContext

Dans la dernière ligne, je définis un service qui permet d’indiquer au ThemeBundle quel thème utiliser pour la requête en cours.

Ce service n’existe pas, il faut donc ajouter le fichier src/Context/ThemeContext.php avec le contenu suivant :

<?php
declare(strict_types=1);

namespace AppContext;

use SyliusBundleThemeBundleContextThemeContextInterface;
use SyliusBundleThemeBundleModelThemeInterface;
use SyliusBundleThemeBundleRepositoryThemeRepositoryInterface;

final class ThemeContext implements ThemeContextInterface
{

    /**
     * @var ThemeRepositoryInterface
     */
    private $themeRepository;

    public function __construct(ThemeRepositoryInterface $themeRepository)
    {
        $this->themeRepository = $themeRepository;
    }

    /**
     * @inheritDoc
     */
    public function getTheme(): ?ThemeInterface
    {
        return $this->themeRepository->findOneByName('my/theme');
    }
}

C’est dans ce fichier qu’est déterminé le thème à utiliser. C’est très pratique pour les migrations de thèmes ou les A/B testing.

J’ajoute maintenant le thème dans le dossier themes que j’ai ajouté à la racine du projet. Pour cela je copie le contenu du dossier templates dans le dossier themes/MyTheme/templates.

J’ajoute le fichier themes/MyTheme/composer.json (qui permet de configurer le thème en définissant son nom) avec le contenu suivant:

{
  "name": "my/theme"
}

Je modifie quelques petites choses pour m’assurer que ce qui est affiché provient bien du thème.

Conclusion

L’ajout de plusieurs thèmes est vraiment très facile dans une application Symfony avec le SyliusThemeBundle.

La documentation du bundle est suffisante pour pouvoir l’utiliser sans trop de problèmes et pouvoir entrevoir le potentiel qu’il offre.

Pour ceux qui ont suivi le live sur Twitter, vous savez qu’il contenait plus de choses. La suite fera l’objet d’un autre article sur l’utilisation de l’UIBundle avec les assets.

Le projet de Hack de Sylius sur GitHub

Pourquoi les auto-incréments sont dangereux ?

Serveurs
Source https://www.pxfuel.com/en/free-photo-ojibu

Lors du développement des sites web ou des applications web, nous utilisons une fonctionnalité courante dans les bases de données : l’auto-incrément.

L’auto-incrément est une colonne spéciale dans la base de données qui est alimentée par le moteur de base de données. Il est unique pour la table dans laquelle les données sont ajoutées. Il sert généralement de clé primaire (principale) pour charger des données précises.

La confidentialité

Le premier problème est la confidentialités. Ces clés primaires auto incrément sont utilisé par la suite dans les URL.

Prenons un exemple, votre entreprise propose un service dans lequel l’utilisateur peux enregistrer des données. Ces données sont stocké dans une base de données avec un auto incrément pour les retrouver plus tard. Cet auto incrément est utilisé dans l’URL pour afficher les données.

Je suis l’un de vos concurents et j’aimerai avoir une idée du volume d’utilisateur. Bien sûr vous ne donnez aucune indication sur les volumes que vous traitez. Et bien malgrè tout je peux en avoir une idée.

Pour cela, je m’inscrit comme un utilisateur, j’ajoute des données, puis j’attends un mois ou plus. Je reviens sur votre service et j’enregistre de nouvelles données.

Je récupère maintenant l’auto incrément présent dans les URL pour connaitre le volume de données enregistré sur la période sur votre service.

La sécurité

Comme je l’ai déjà dit dans la vidéo « Comment mon site a été piraté 2 fois (injection SQL)« , une URL avec un auto-incrément est dangereuse.

Continuons avec notre exemple données pour la confidentialité. Si je vais sur la page d’affichage des données. Et que je modifie l’auto-incrément présent dans l’URL pour mettre le précédent. Que ce passe-t-il ?

Si le site est sécurisé, j’ai droit à un message d’erreur m’indiquant un refus d’accès.

Et malheureusement dans de trop nombreux cas, je vois les données ! C’est une fuite de données qu’il faut déclarer à la CNIL et dont-il faut assumer les conséquences !

L’article de la CNIL sur le sujet « Sécurité des sites web : les 5 problèmes les plus souvent constatés » (point 3)

Mais ce n’est pas tout, il y a encore un autre risque. Dans le cas d’une faille de sécurité de type « Injection SQL » la présence d’auto incrément simplifie la tache des malveillants.

En effet, la colonne se nomme dans 90% (pourcentage arbitraire pour dire presque tout le temps) des cas id. Et pour la table des utilisateurs nommé « users » dans 80% des cas (toujours arbitraire mais c’est moins souvent) il est très simple d’exfiltrer la liste des utilisateurs.

Par quoi les remplacer ? Par un identifiant généré aléatoirement grâce à des fonctions de hachages fiables. Utilisez les mêmes que pour la cryptographie.

Je vous conseil les GUID ou UUID qui sont disponible dans tous les langages de programmation. C’est un texte de 36 caractères qu’il est possible d’utiliser en tant que clé primaire.

Malheureusement il n’est pas toujours possible de les utiliser en tant que clé primaire. Certaine logiciel de base de données ont des limites qui empêche de réaliser des liaisons entre table si les tables utilise des clés primaire trop longue.

Pour palier à ce défaut, vous pouvez modifier la configuration du logiciel de base de données. Mais dans la pire des situations qu’est l’hébergement mutualisé, vous n’aurez pas le choix d’utiliser un auto incrément pour réaliser la liaison.

Conclusion

La confidentialité (secret des affaires) est en jeu lors de l’utilisation d’un auto incrément, mais le plus grave reste la sécurité.

La sécurité permet de garantir l’intégrité des données, leur sécurités et leur confidentialité et ces 3 points sont abordés dans le RGPD. Tout manquement est donc punissable par la CNIL.

Vidéo

Vidéo Youtube « Pourquoi les auto-incréments sont dangereux ? »

Lancement de ma chaîne Youtube

Chaîne Youtube JB Dev Labs

Voilà maintenant quelques temps que je travaille sur le projet de chaîne Youtube. Abonnez-vous à ma chaîne JB Dev Labs ! Vous y trouverez mes réalisations personnelles et professionnelles (celles dont je peux parler).

Pour commencer, voici la présentation de Rymfony.

https://youtu.be/y0BEkzZZCmM

Malgré de très nombreux tests, tout n’est pas encore au point. Faites-moi vos commentaires sous la vidéo. Je les lirai tous !

Bon visionnage !

Cas pratique d’écriture d’un test automatique en français

Source: https://www.pxfuel.com/en/free-photo-jepee

Dans mon précédent article sur les tests automatisés, je parlais d’écrire les tests en français. Je vous ai présenté un test et parlé de son anatomie. Vous connaissez maintenant la base de la syntaxe Gherkin.

Des outils comme Cucumber et Behat permettent d’interpréter les fichiers de fonctionnalité (feature) contenant les scénarii de test.

Terminologie

Avant d’aller plus loin, un petit point terminologie, nous réalisons ici des tests d’acceptation. Il est courant d’entendre parler de tests fonctionnels mais ce terme peut s’appliquer à d’autres types de test.

Voyons maintenant comment écrire le scénario pour tester la connexion d’un utilisateur sur notre application web.

Déterminer les cas d’usage de la fonctionnalité à tester

Nous allons donc tester la connexion d’un utilisateur. Quelles sont les différentes situations qu’un utilisateur peut rencontrer lors de la connexion ?

Le premier cas est une connexion réussie avec son identifiant et son mot de passe.

Le deuxième cas est une connexion échouée en raison d’un mauvais mot de passe.

Le troisième cas est une connexion échouée en raison d’un identifiant inconnu.

Et c’est tout. Notre application n’utilise pas de double authentification et ne dispose pas de la mémorisation de l’utilisateur (remember me).

Déterminer les pré-requis, l’état initial du test

Maintenant que nous avons tous les cas de figure métier de la connexion d’un utilisateur, quel doit être l’état du système avant de commencer un cas de test ?

Le premier pré-requis est que nous devons ouvrir un navigateur Internet et charger la page de connexion d’un utilisateur sur notre application web.

Le second est qu’un utilisateur doit être présent en base de données pour permettre la réalisation de deux des scénarii.

Et c’est tout.

Déterminer ce qui sera vérifié

Maintenant, qu’allons-nous vérifier ?

Dans le cas d’une connexion réussie, nous devons arriver sur la page du compte de l’utilisateur.

Dans le cas d’une connexion échouée, nous devons être sur la page de connexion et le message « Identifiant ou mot de passe invalide ».

Pour des raisons de sécurité, la distinction entre un compte inconnu et un mot de passe erroné n’est pas affichée.

Écrire les scénarii

Maintenant, nous pouvons passer à l’écriture d’un fichier de fonctionnalité (feature).

#language fr
Fonctionnalité: Connexion d'un utilisateur

  Contexte:
    Etant donné que l'utilisateur "test" est enregistré avec le mot de passe "test123456!"
    Et que je suis sur la page de connexion

  Scénario: Connexion réussie
    Lorsque je me connecte en tant que "test" avec le mot de passe "test123456!"
    Et que je valide la connexion de l'utilisateur
    Alors je dois être sur la page du compte de l'utilisateur "test"

  Plan du Scénario: Identifiant incorrect
    Lorsque je me connecte en tant que "<identifiant>" avec le mot de passe "<mot_de_passe>"
    Alors je dois être sur la page de connexion
    Et le message d'erreur "Identifiant ou mot de passe incorrect" est visible

  Exemples:
    | identifiant | mot_de_passe |
    | test        | abracadabra  |
    | toto        | abracadabra  |

Avez-vous remarqué que j’ai regroupé les deux scénarii d’échec ? C’est une fonctionnalité de la syntaxe Gherkin. Il est possible de jouer plusieurs fois le même scénario avec des informations différentes.

Explication du fichier

Mais revenons au début du fichier. Si vous comparez avec ce que je vous ai montré dans mon précédent article, il y a beaucoup de chose en plus !

Contexte

A la ligne 1, nous trouvons la définition de la langue utilisée dans le fichier.

A la ligne 2, le terme « Fonctionnalité: » est un mot clé Gherkin qui lui indique le début du test d’une fonctionnalité. Le texte qui suit le mot clé est un commentaire (il peut être sur plusieurs lignes).

A la ligne 4, le terme « Contexte » indique que les phrases (ou étapes) qui suivent (ligne 5 et 6) doivent être réalisées avant chaque scénario de la fonctionnalité. Le contexte est très utile pour alléger les scénarii. Cela permet de disposer le système dans un état donné commun à toute la fonctionnalité.

Scénario simple

Le premier scénario débute à la ligne 6 et ressemble plus à ce dont j’ai parlé dans le précédent article.

Cependant, il y deux points que je souhaite vous montrer :

Les textes entre guillemets est celui qui sera saisi sur la page de connexion par l’outil de tests. Il est possible de récupérer ainsi des informations depuis les phrases permettant leur réutilisation.

A la ligne 10 (comme à la ligne 6 et 16), la phrase commence par le mot clé « Et que » (ou « Et »). Ce mot clé est utilisé en remplacement des autres mots clés pour éviter la répétition et obtenir un texte compréhensible.

Plan de scénario

A la ligne 13 nous découvrons le mot clé « Plan du Scénario ». Celui-ci indique à l’outil qu’il doit exécuter le scénario plusieurs fois en fonction d’un tableau de données.

Dans les phrases du plan, les éléments qui changent entre chaque exécution sont placés entre les signes « inférieur » et « supérieur ». Le texte présent entre ces deux signes désigne le nom de la colonne où trouver la donnée.

Les données en elles-mêmes sont présentées sou forme de tableau après le mot clé « Exemples » (ligne 18 et suivantes).

Récapitulons

Dans cet article, nous avons vu comment analyser une fonctionnalité et écrire un fichier de scénario contenant tous les cas que nous souhaitons tester.

Nous avons également découvert quelques mots clés supplémentaires nous permettant d’écrire des scénarii simples et concis.

La simplicité des phrases et la concision des tests permet à tous les intervenants de comprendre facilement et rapidement le fonctionnement de l’application.

Cadeau

Après avoir bien travaillé, je vous offre un cadeau. En plus d’avoir écrit un test qui sera exécuté avant chaque livraison de votre application, nous venons d’écrire de la documentation.

Oui, les tests d’acceptation (comme les autres d’ailleurs) font partie de la documentation de votre application.

La documentation fournie par les tests a la particularité d’être maintenue à jour. Il serait dommage de s’en priver.

Intéressé par le sujet ?

Merci d’avoir lu jusqu’à la fin ! Contactez-moi pour en discuter et restez connecté !

Quand les tests vous sauvent

Test
https://www.pxfuel.com/en/free-photo-ogmwi

Quand on parle de développement logiciel, nous pensons à du code, à des choses incompréhensibles, abstraites, à un homme barbu qui reste toute la journée devant son ordinateur.

Loin des clichés, qu’est-il possible de comprendre quand on n’a pas les connaissances ?

En un mot : les tests.

Quels sont les tests qu’un chef de produit peut comprendre sans entrer dans la technique ?

Il existe plusieurs façons de réaliser des tests. La méthode que vous connaissez sûrement et que vous pratiquez, c’est l’utilisation du produit.

Réaliser des tests manuels sera toujours nécessaire. Ils ne doivent pas être sous-évalués.

Mais vous est-il arrivé de retomber sur un bug déjà corrigé quelque temps plus tôt ? Vous est-il déjà arrivé d’être lassé de réaliser toujours les mêmes tests ?

Sûrement ! Et c’est normal ! L’être humain n’est pas une machine. Il a même inventé des machines pour se débarrasser des tâches répétitives.

C’est la même chose avec les projets informatiques. Il est possible d’automatiser la réalisation des tests tout en vous permettant de comprendre.

Écrire des tests en langage naturel

Oui, il est possible d’écrire des tests en langage naturel moyennant un certain formalisme.

Ainsi le scénario suivant est correct:

Étant donné que je suis sur la page d’accueil du blog de Jean-Baptiste Nahan
Lorsque je clique sur « PHP 8 » dans le menu
Alors je suis sur la liste des articles disposant du tag « PHP 8 »

Comprenez-vous ce qui se passe lors de l’exécution de ce scénario ?

Il est possible de formaliser tous vos tests ainsi !

Anatomie du test

L’exemple est très court, mais est composé des éléments clés :

La phrase commençant par « Étant donné que » indique un prérequis, une situation préétablie. Elle ne se trouve en général qu’au début d’un scénario de test. Dans l’exemple, le prérequis est que le navigateur soit ouvert sur la page d’accueil de mon site.

La phrase commençant par « Lorsque », indique l’action que vous réalisez. Le résultat de cette action sera vérifié plus tard. Dans l’exemple, le testeur clique sur le lien nommé « PHP 8 » situé dans le menu du site.

Enfin la phrase commençant par « Alors » indique ce que l’on attend, ce qui est vérifié. Dans l’exemple, tous les tags des articles présents sur la page seront récupérés, puis la présence du tag « PHP 8 » sera vérifiée sur chacun d’eux. Si un article n’a pas le tag, alors une erreur sera levée et le test échouera.

Transcription

Bien que vous écriviez en langage naturel et en français (oui, ça fonctionne dans toutes les langues), il n’y a rien de magique.

Un développeur devra écrire du code pour chaque phrase permettant la réalisation des actions, et la vérification des attendus.

Ils vous sauveront

La mise en place de ces tests prend du temps, ce serait une erreur de le négliger. Mais ils vous sauveront !

Après la phase de mise en place et d’écriture des scénarii, vous pourrez demander à votre équipe de développeurs d’exécuter les tests périodiquement. Avant une mise en production par exemple, ou avant chaque modification (c’est utile pour certains projets). Quand vous le souhaitez.

À plusieurs reprises les tests m’ont épargné de très grosses galères, et je trouve dommage que vous ne puissiez pas en profiter pour vous économiser du temps et de nombreuses galères, et surtout gagner en qualité.

Intéressé par le sujet ?

Contactez-moi pour en discuter et restez connecté !

Bonne année 2021 !

Développement d'un logiciel (Tous droits réservé 2021 - Jean-Baptiste Nahan)
Développement d’un logiciel (Tous droits réservés 2021 – Jean-Baptiste Nahan)

Après une année 2020 très particulière sur de nombreux points, voici que l’année 2021 arrive avec son lot d’espoirs et de défis.

Que s’est-il passé pour moi en 2020 ?

Cela fait maintenant 4 ans et demi (oui c’est important à cet âge-là) que je maintiens l’extension Win32Service pour PHP. La version 1.0 est sortie peu de temps après PHP 8.0. N’hésitez pas à me contacter pour venir en discuter.

C’est également en 2020 que j’ai commencé à utiliser le langage Rust pour écrire des logiciels. Après quelques contributions au logiciel Rymfony (clone libre de Symfony Cli), j’ai entamé un projet écrit en Rust (plus de détails en 2021).

En 2020, j’ai découvert un nouveau concept pour améliorer l’écriture de test Behat et particulièrement les contextes. Je l’utilise régulièrement et c’est un vrai plaisir.

Côté sécurité, j’ai utilisé ma première clé de sécurité physique SoloKeys. Elles ont la particularité d’être Open Source.

La carte MicroSD du RaspBerry Pi qui me permet de faire le télérelevé client de mon compteur Linky a décidé de ne plus fonctionner.

A quoi s’attendre en 2021 ?

N’étant pas coutumier de la prédiction, je me limiterai à des événements fortement probables.

Je peux prédire la version 8.1 de PHP et avec elle sûrement une nouvelle version de l’extension Win32Service.

Il y aura sûrement des informations sur mon projet écrit en Rust. Je peux vous dire qu’il est dans un domaine que j’aime particulièrement dans le développement logiciel.

Bien sûr il y aura des projets dont j’ignore encore l’existence et qui me passionneront !

Et vous quelles sont vos prédictions pour 2021 ?

Win32Service 1.0.0 is available

Win32Service Logo
Win32Service Logo

After the last 1.0.0Alpha2 version, the new version 1.0.0 of Win32Service extension for PHP is available.

Download the new version

Or

Win32Service is a PHP extension. Following the planned change for Win32Service, the version 0.4.x is for PHP7 and the version 1.0.x is for PHP8. This extension provides a simple way to run your PHP script like a service into the Windows Service Manager. Win32Service is same as supervisorD or SystemD for Linux.

The PHP documentation for win32service is up to date.

The v1.0.0 change log

  • Add Win32ServiceException (extends Exception)
  • The return value is no longer false when call method with wrong parameters
    • win32_start_service_ctrl_dispatcher
    • win32_set_service_exit_mode
    • win32_set_service_exit_code
    • win32_set_service_status
    • win32_create_service
    • win32_delete_service
    • win32_get_last_control_message
    • win32_query_service_status
    • win32_start_service
    • win32_stop_service
    • win32_pause_service
    • win32_continue_service
    • win32_send_custom_control
  • Add check in array information provided to win32_create_service. On error, this method throw a ValueError.
  • For function win32_send_custom_control, the PHP error The control argument value is not between 128 and 255. is now throw on ValueError.
  • For all function needed the service name, a ValueError is throw if the string is empty.
  • The PHP Error This function work only when using the CLI SAPI and called into the service code. has been converted to Win32ServiceException for this method
    • win32_start_service_ctrl_dispatcher
    • win32_set_service_exit_mode
    • win32_set_service_exit_code
    • win32_set_service_status
    • win32_get_last_control_message
  • The PHP Warning Service ctrl dispatcher already running has been converted to Win32ServiceException.
    This exception is throw if function win32_start_service_ctrl_dispatcher is called when the control dispatcher is already started.
  • The PHP Warning Failed to start dispatcher thread has been converted to Win32ServiceException.

Edit on December 1, 2020 : I have updated this blog post to add download pre-built DLL links.

More blogpost about Win32Service

Win32Service 1.0.0-alpha2 is available

Win32Service Logo
Win32Service Logo

The new version 1.0.0-alpha2 of Win32Service extension for PHP is available for test.

Win32Service is a PHP extension. The version 0.4.x is for PHP7 and the version 1.0.x is for PHP8. This extension provides a simple way to run your PHP script like a service into the Windows Service Manager. Win32Service is same as supervisorD or SystemD for Linux.

The v1.0.0-alpha2 change log

  • Add Win32ServiceException (extends Exception)
  • The return value is no longer false when call method with wrong parameters
    • win32_start_service_ctrl_dispatcher
    • win32_set_service_exit_mode
    • win32_set_service_exit_code
    • win32_set_service_status
    • win32_create_service
    • win32_delete_service
    • win32_get_last_control_message
    • win32_query_service_status
    • win32_start_service
    • win32_stop_service
    • win32_pause_service
    • win32_continue_service
    • win32_send_custom_control
  • Add check in array information provided to win32_create_service. On error, this method throw a ValueError.
  • For function win32_send_custom_control, the PHP error The control argument value is not between 128 and 255. is now throw on ValueError.
  • For all function needed the service name, a ValueError is throw if the string is empty.
  • The PHP Error This function work only when using the CLI SAPI and called into the service code. has been converted to Win32ServiceException for this method
    • win32_start_service_ctrl_dispatcher
    • win32_set_service_exit_mode
    • win32_set_service_exit_code
    • win32_set_service_status
    • win32_get_last_control_message
  • The PHP Warning Service ctrl dispatcher already running has been converted to Win32ServiceException.
    This exception is throw if function win32_start_service_ctrl_dispatcher is called when the control dispatcher is already started.
  • The PHP Warning Failed to start dispatcher thread has been converted to Win32ServiceException.

The release 1.0.0-alpha2 on the GitHub project

See Also: