Retour sur le SymfonyLive Online French Edition 2021

Retour sur le SymfonyLive Online French Edition 2021

Cette année encore, nous étions plusieurs JoliCodeuses et JoliCodeurs à assister à cette nouvelle édition du SymfonyLive. Avec la crise sanitaire toujours d’actualité, cette conférence s’est déroulée complètement en ligne, prenant, au passage, le nom de « French Edition 2021 ». Deux tracks étaient proposées en parallèle sur une seule journée, pour un total de 12 conférences. Voici un résumé de ce que nous retenons de cette édition.

Boring is the new hype

Lors de la conférence d’ouverture de ce SymfonyLive, Fabien Potencier est revenu sur l’importance, pour la plupart des projets, de choisir des technologies « boring » plutôt que de céder à la tentation de la hype. Ici, « boring » fait référence à des technologies stables, éprouvées et surtout maîtrisées correctement par l’équipe qui les met en place. Au passage, il souligne qu’un bon choix de technologie pour une grosse société des nouvelles technologies (type GAFAM) est presque toujours un mauvais choix pour nos projets.

Selon lui, utiliser une technologie récente peut être fun sur du court-terme, mais représente un risque non négligeable pour votre projet sur le long terme. Vous et votre équipe allez devoir maîtriser ce nouvel outil (risquant au passage d’introduire de la dette technique dès le début – par manque de connaissance et/ou d’expérience), et apprendre à développer avec, à le déployer, à le monitorer, à gérer les montées en charge, etc. Et ce, sans l’aide d’une communauté déjà bien établie.

Fabien considère Symfony comme une technologie boring, grâce à la stabilité de ses interfaces qui n’ont quasiment pas changé depuis la version 2, ainsi qu’à ses promesses fortes de Backward Compatibility qui facilite les montées de version. Il recommande d’ailleurs de ne plus rester sur les versions LTS de Symfony mais de mettre à jour à minima tous les 6 mois.

Selon lui, la stack minimale idéale pour un projet Symfony est la suivante :

  • PHP ;
  • PostgreSQL ;
  • JavaScript ;
  • SaaS.

Cela fait plus de 10 ans qu’il utilise PostgreSQL à la place de MySQL. Outre la partie base de données relationnelle, ce SGBD est également utilisable avec plusieurs composants de Symfony :

  • Messenger ;
  • Lock ;
  • Sessions ;
  • Cache.

Il recommande enfin d’utiliser au maximum des SaaS quand c’est possible. Que ce soit pour monitorer les logs, envoyer les mails, et bientôt même pour gérer ses traductions, de nombreuses intégrations sont disponibles nativement dans Symfony pour nous simplifier ces tâches.

La puissance de l’écosystème Symfony

Que ce soit à travers la présentation de composants et outils fournis par Symfony directement ou de projets tirant parti de ses composants, plusieurs conférences ont mis en avant des points forts de cet écosystème.

Une sérialisation adaptée avec API Platform et Symfony

Tout d’abord, Mathias Arlaud nous a rappelé le but du composant Serializer de Symfony (traduire une structure de données vers un format spécifique) et ses 2 grandes parties que sont la normalisation et l’encodage.

Nous pouvons noter que dans sa conférence, tout comme dans la documentation de API Platform, les attributs de PHP 8 sont utilisés à la place des annotations classiques (fournies par doctrine/annotations).

Mathias a enchaîné sur la gestion des ressources dans API Platform, avec les différents attributs (fournis soit par le Serializer, soit par APIP) qui nous permettent de contrôler la façon dont nos données vont être représentées dans l’API. L’utilisation de #[Ignore] ou de #[ApiProperty(readable: false, writable: false)] permet de définir si une propriété sera exposée ou non. Il est également possible de définir des groupes de sérialisation (avec l’attribut #[Groups(['group1', 'group2'])]) pour paramétrer dynamiquement quelles seront les données exposées.

Il est également revenu sur quelques nouveautés de la version 2.6 d’API Platform, comme par exemple, la possibilité de configurer les propriétés des ressources en fonction de l’utilisateur actuel :

#[ApiProperty(security: 'is_granted("ROLE_ADMIN")')]
public string $foo; // seuls les utilisateurs avec ce rôle auront accès à cette propriété

#[ApiProperty(security: 'object.creator == user')]
public string $bar; // où user est l’utilisateur connecté, et $creator une propriété de la ressource

Enfin, Mathias nous a montré qu’API Platform reste très souple et qu’il est possible de modifier son comportement à notre guise, par exemple en modifiant le ContextBuilder pour configurer le contexte du Serializer Symfony, ou encore en mettant en place une ResourceMetadataFactory pour adapter la configuration pour une ApiResource.

Comment nous avons tiré parti de symfony/http-client pour construire un nouveau SDK AWS

Jérémy Derussé nous a raconté l’origine de la création d’un nouveau SDK pour AWS, asynchrone et basé sur le client HTTP de Symfony.

Un SDK AWS existe déjà, fourni par Amazon. Dans celui-ci, tout n’est pas à jeter. Il est complet, testé, mais sa DX est une catastrophe : paramètres et retours de fonction opaques, pas d’autocomplétion, code peu lisible et l’API du SDK n’est pas simple à mémoriser. En outre, il est assez lourd et possède beaucoup de dépendances.

Plusieurs composants de Symfony (comme Messenger, Mailer ou encore Notifier) fournissent des bridges pour s’interfacer avec les services d’Amazon. Mais jusqu’à maintenant, Symfony ne s’appuyait pas sur le SDK officiel, ce qui a engendré plusieurs bugs. Plutôt que de chercher à utiliser celui-ci, Jérémy a fait le choix, avec Tobias Nyholm, de construire un nouveau SDK AWS.

Un des avantages d’utiliser le client HTTP de Symfony, c’est sa DX :

  • le debug est grandement facilité grâce à la Web Debug Toolbar dans une application Symfony ;
  • l’asynchrone est natif et complètement transparent (pas de promise ou de callback) ;
  • gestion des requêtes parallèles ;
  • gestion du retry en cas d’erreur (même si la stratégie de retry doit être adaptée à Amazon qui n’utilise que le code d’erreur 400 et force à vérifier le message d’erreur plutôt que les codes d’erreurs adéquats).

En revanche, AWS, c’est presque 300 services et 10 000 opérations disponibles. Impossible de maintenir à jour un SDK complet manuellement. Heureusement, Amazon fournit une spécification en JSON (dans un format non standard, si cela avait été du OpenAPI, il aurait été possible d’utiliser notre générateur Jane) pour décrire toutes ses APIs. Cela a permis à Jérémy et Tobias d’automatiser la génération et la mise à jour du SDK, et ainsi de se concentrer uniquement sur la DX du SDK.

À l’heure actuelle, le SDK est stable et est utilisé en production dans Symfony Messenger et Mailer par exemple. En revanche, seuls les services essentiels sont implémentés. Mais toutes les contributions pour ajouter le support de nouvelles opérations seront acceptées, le gros du travail étant fait par le robot de génération du SDK.

Des trésors cachés dans Symfony

Pour sa conférence, Nicolas Grekas a remonté l’historique Git de Symfony pour en extraire une longue – mais intéressante – liste d’astuces et de fonctionnalités de Symfony, souvent méconnues et parfois présentes depuis sa version 2.0. Il nous a également présenté quelques améliorations apportées récemment, notamment avec l’utilisation de PHP 8 dans la release 5.3 et l’ajout de nouveaux attributs.

Il est difficile de résumer tout ce dont nous a parlé Nicolas mais voici tout de même quelques points abordés :

  • il est possible de coder une application complète dans le Kernel. Ce dernier peut être utilisé en tant que CompilerPass, EventListener, ou pour définir des contrôleurs et des routes, etc ;
  • la configuration du DIC a encore été améliorée : l’autowiring peut maintenant utiliser le type de la variable mais aussi son nom, les services taggués ou un service locator peuvent être passés directement depuis la configuration YAML, voire directement dans le code grâce à des attributs PHP 8 (donc sans aucune config YAML) ;
  • la Console a également eu le droit à ses attributes PHP 8 pour définir le nom et la description des commandes, la commande spéciale bin/console list va pouvoir user du lazy-load et ainsi corriger sa lenteur actuelle ;
  • le Validator peut désormais déduire des contraintes depuis les annotations de Doctrine ORM ;
  • le script ./link à la racine de symfony/symfony permet de remplacer les vendors d’une application par les sources de Symfony que l’on est en train de modifier, pratique pour tester une PR dans une application Symfony réelle.

Nicolas nous a rappelé qu’il n’y a pas de roadmap définie à l’avance pour Symfony. Toutes les améliorations apportées ont pour source le besoin d’un contributeur. Nous ne pouvons que vous conseiller de jeter un œil au replay de cette conférence, quand il sera disponible.

De plus en plus présent sur le Front

Depuis quelque temps, Symfony s’invite de plus en plus sur la partie frontend de nos projets. Cela avait commencé avec Webpack Encore pour apporter la même vision DX de Symfony autour de Webpack. Et récemment, l’initiative Symfony UX a été lancée pour aller encore plus loin.

Démarrer avec Symfony UX

À l’origine de cette initiative, Titouan Galopin nous a montré comment profiter de Symfony UX. Son but ? Apporter aux applications traditionnelles (Symfony / Twig / HTML), l’expérience utilisateur et la réactivité des SPA :

  • sans leur complexité ;
  • sans avoir à réimplémenter des comportements natifs du navigateur (historique, sessions, formulaires, etc) ;
  • sans risquer (paradoxalement) de nuire à l’UX si elle est mal faite.

En premier lieu, installer le WebpackEncoreBundle et sa recette Flex permet :

  • de préparer la mise en place des dépendances via un package.json ;
  • de configurer Webpack Encore, Webpack et les entrypoints ;
  • de préparer l’arborescence JS et CSS dans le dossier assets.

Symfony UX est principalement basé sur Stimulus. Il s’agit d’un framework JS qui se veut « modeste » et qui fonctionne à partir du HTML que vous avez déjà fabriqué côté serveur, plutôt que de chercher à refabriquer du HTML dans le navigateur. Tout fonctionne avec des contrôleurs. Un contrôleur Stimulus est une classe JS (dans assets/controllers/) associée à un nœud du DOM qui va contenir du code pour réagir quand ce nœud est affiché, cliqué, supprimé, etc. On note que Stimulus n’est pas une alternative à React ou VueJS et peut tout à fait être utilisé avec eux (ce sont alors les contrôleurs Stimulus qui vont instancier les composants React/VueJS).

Les contrôleurs Stimulus fonctionnent ensuite avec 3 types d’éléments : les targets, les actions et les values :

  • les targets permettent de référencer et d’accéder à des éléments enfants du DOM node du contrôleur ;
  • les actions nous aident à réagir à des événements avec très peu de code et directement depuis le HTML ;
  • les values permettent à la fois d’exposer des variables entre le template Twig et le JS à exécuter et également de stocker un état dans un data attribute du DOM node pour éviter d’avoir à gérer un état interne dans le contrôleur.

Finalement, Symfony UX se veut être un bridge entre Symfony, Stimulus et Wepack. Il devient ainsi possible de créer et réutiliser des packages qui contiennent à la fois le code backend ainsi que le code frontend. Symfony UX propose d’ores et déjà plusieurs packages prêts à l’emploi, tels qu’un drag and drop, un système de lazyload d’image ou encore Turbo.

Pied au plancher avec Symfony Turbo

Durant cette conférence, Kévin Dunglas nous a parlé de Symfony Turbo, une intégration de la librairie Hotwire Turbo dans Symfony UX.

Turbo est une bibliothèque légère publiée récemment par le créateur de Ruby On Rails. Le but de Turbo est là aussi de venir apporter la puissance des SPA mais cette fois-ci sans avoir à écrire de JavaScript. Turbo va venir se brancher automatiquement dans notre page HTML. Au moment du clic sur un lien ou au moment de soumettre un formulaire, Turbo va faire la requête correspondante en arrière plan via fetch(), récupérer la réponse HTML et l’injecter dans la page courante (en remplaçant les éléments dans le BODY et fusionnant le HEAD). Cela va permettre d’éviter le flash blanc que l’on a en temps normal en changeant de page.

Quelques petites améliorations ont été introduites dans Symfony 5.3 pour faciliter l’utilisation de Turbo. Par exemple, une méthode helper $this->renderForm() a été ajoutée dans les contrôleurs pour que la soumission d’un formulaire invalide puisse retourner une erreur 400.

Un système de frame est également présent dans Turbo. Celui-ci permet de gérer des blocs, de gérer du cache, et est assez similaire aux ESI, mais côté client cette fois-ci. D’ailleurs, dans Symfony, les frames vont pouvoir réutiliser le système de fragment ESI. Kévin nous assure d’ailleurs que les 2 ne sont pas exclusifs et peuvent êtres utilisés ensemble.

Enfin, Turbo fournit un système de stream pour apporter des mises à jour en temps réel à notre page. Le bridge de Symfony UX offre d’ailleurs une intégration avec Mercure. Il va être ainsi possible de forcer une mise à jour de l’interface sur tous les clients connectés, par exemple lorsqu’une entité Doctrine est mise à jour, le tout sans avoir à écrire de JS.

Les tests

Un autre domaine abordé lors de ce SymfonyLive concernait le monde des tests, que ce soit une présentation de l’état de l’art des tests dans l’écosystème PHP / Symfony ou à travers la présentation de Cypress, un outils JS assez puissant pour faire des tests E2E.

The New Testing Landscape: Panther, Foundry & More

Ryan Weaver nous a déjà beaucoup appris grâce à ses nombreux cours sur la plateforme SymfonyCasts. Comme à son habitude, il commence par nous fournir le code qu’il utilise pour ses démonstrations, disponible sur son GitHub.

Petit rappel de sa part : il existe trois types de tests :

  • Les tests unitaires (TestCase) : Comme leur nom l’indique, ils permettent de tester une petite unité du code. Symfony n’est pas utilisé pour ces tests, on mock les services ;
  • Les tests d’intégration (KernelTestCase) : on utilise les vrais services, on fait des requêtes en BDD ;
  • Les tests fonctionnels (ou E2E pour End-to-End) (WebTestCase) qui eux, testent une expérience utilisateur du début à la fin, avec les appels aux APIs etc : on utilise tout Symfony, les controllers, et on teste ce qui est rendu dans le navigateur.

Dans le contexte de Symfony, PHPUnit n’est jamais exécuté directement, mais à via le PHPUnit Bridge. En réalité, PHPUnit est installé dans le dossier bin, comme un projet à part entière, et c’est le Bridge que nous exécutons qui sera chargé de trouver et exécuter PHPUnit. Ce Bridge nous apporte :

  • le rapport de « deprecations » à la fin de l’exécution ;
  • le Clock Mocking et DNS Mocking ;
  • la possibilité d’exécuter nos tests avec plusieurs versions de PHPUnit ;
  • la séparation complète entre les dépendances de notre application et celles de PHPUnit lui même.

Après un rapide exemple de test unitaire, Ryan se lance dans le test d’intégration d’une méthode d’un Repository Doctrine. Pour cela, il a besoin de générer, à l’aide du MakerBundle, un KernelTestCase. Dans ce type de classe de test, nous avons accès à un container via self::$container et ce dernier expose tous les services (à condition qu’ils ne soient ni « inlinés », ni supprimés) de notre application comme publics, peu importe qu’ils soient déclarés comme public ou privé dans la configuration. Parfait pour tester réellement un Repository.

Mais le problème suivant se pose : comment peupler la base de données avant d’exécuter notre test de Repository ? Découvrons le package zenstruck/foundry !

Ce package va nous permettre de créer des factories pour générer des fixtures, rapidement et facilement pour n’importe quelle entité de notre application. Le support de Faker est natif pour remplir les propriétés de nos objets. Ces factories sont bien sûr utilisables dans Doctrine Fixtures pour instancier nos objets.

Nous vous avions déjà parlé de la génération de fixtures dans un précédent article . Foundry semble aller dans le même sens, mais avec une DX encore plus poussée, et plus agréable à utiliser.

final class ProductFactory extends ModelFactory
{
    protected function getDefaults(): array
    {
        return [
            'name' => self::faker()->words(3, true),
            'description' => self::faker()->paragraph,
            'brand' => self::faker()->company,
            'price' => self::faker()->numberBetween(1000, 10000),
            'imageFilename' => 'floppy-disc.png',
            // will create a random category
            'category' => CategoryFactory::new(),
        ];
    }

    protected static function getClass(): string
    {
        return Product::class;
    }
}

Ryan nous a également listé quelques astuces :

  • l’utilisation du trait ResetDatabase mis à disposition par Foundry pour supprimer et recréer le schéma de notre base de données entre chaque test ;
  • l’utilisation de la méthode enableAutoRefresh sur l’objet retourné par la factory de Foundry pour rafraîchir automatiquement cet objet lorsqu’il subit une mise à jour en base de données après un appel HTTP. De cette façon, cet objet peut être utilisable dans nos assertions.

Enfin, Ryan atteint le Graal des tests, les tests E2E avec du JavaScript. Pour cela, il installe symfony/panther. Panther lance notre application dans un vrai navigateur. Seul bémol, l’exécution des tests enchaîne les assertions de manière instantanée, sans attendre les éventuels retours et changement de DOM à la suite d’appels AJAX. Ryan ajoute donc un appel à $client->waitForElementToContain($selector, $content) pour forcer l’attente du changement de l’élément de DOM qu’il veut tester.

Il a également mentionné plusieurs outils très pratiques comme zenstruck/browser (une surcouche par dessus symfony/browser-kit et symfony/panther) ou brianium/paratest (pour lancer des processus PHPUnit en parallèle).

À l’instar de sa présentation lors du SymfonyWorld sur la nouvelle sécurité de Symfony, Ryan nous fait profiter de son expérience sur la mise en place d’une stack de tests robuste, à la DX quasiment irréprochable !

Cypress, le E2E moderne doit encore apprendre du passé

Guillaume Loulier a commencé sa conférence par un état des lieux des tests dans l’écosystème PHP et dans celui de JavaScript en évoquant notamment les frameworks et outils les plus utilisés actuellement. Il a ensuite rappelé ce qu’étaient les tests End to End et leur principale caractéristique : se forcer à tester le vrai parcours utilisateur en testant l’intégralité de la stack. Cela mène par définitions à des tests souvent plus lourds (couches testées plus nombreuses : UI, API, BDD, etc.), plus compliqués à mettre en place et également plus compliqué à maintenir.

Cypress est un framework orienté E2E qui propose, pour écrire nos tests, une syntaxe JS connue (celle de Mocha et Chai), simple et intuitive. Il permet de simplifier la mise en place des tests E2E grâce à plusieurs fonctionnalités :

  • compatible avec plusieurs navigateurs ;
  • permet de faire des tests d’API Rest, des audits via Lighthouse, de surveiller les performances, etc ;
  • propose des screenshots, des vidéos et un replay des étapes qui ont échouées ;
  • permet d’intercepter / mocker / décorer les requêtes (XHR, fetch, etc) ;
  • beaucoup d’intégrations disponibles (GitHub, Gitlab, notification Slack, avec des IDE, etc).

Guillaume nous a ensuite montré des exemples de tests avec Cypress, des astuces pour simplifier les tests ainsi que quelques limitations de l’outil, comme :

  • le debug sur une application Symfony pas toujours très évident ;
  • la version Docker pas très qualitative ;
  • l’API qui manque encore un peu de maturité et qui change de temps en temps au fil des releases.

Conclusion

Cette édition 2021 du SymfonyLive a pu se dérouler sans accroc, les vidéos des conférences étant enregistrées à l’avance pour éviter tous les aléas du direct.

La plateforme utilisée, Hopin, a quelques axes d’améliorations possibles concernant la visualisation des conférences (passage en mode plein écran pas évident, sous-titres forcés sur la langue du navigateur sans possibilité de changer, etc). Même si une conférence en ligne n’apporte jamais autant d’interactions qu’une conférence en présentiel, Hopin ajoute également des fonctionnalités qui vont dans ce sens, certaines (commentaires live) avec plus de succès que d’autres (matching aléatoire entre participants). Espérons qu’ils continueront ces efforts !

Bravo à l’équipe Symfony pour cette nouvelle édition en ligne et bravo à tous les speakers. Nous avons hâte de vous retrouver lors de la prochaine conférence, en espérant que celle-ci puisse se dérouler en physique.

Nos formations sur le sujet

  • Logo Symfony

    Symfony

    Formez-vous à Symfony, l’un des frameworks web PHP les plus connus au monde

  • Logo Symfony avancée

    Symfony avancée

    Décou­vrez les fonc­tion­na­li­tés et concepts avan­cés de Symfo­ny

blog comments powered by Disqus