<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet href="https://jolicode.com/feed.xsl" type="text/xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" xml:lang="fr-FR">
    <id>https://jolicode.com/blog</id>
    <link type="text/html" rel="alternate" href="https://jolicode.com/blog"/>
    <link type="application/rss+xml" rel="self" href="https://jolicode.com/feed" />

            <title>JoliCode blog - les derniers articles</title>
        <updated>2026-04-12T13:06:25+02:00</updated>    <entry>
        <id>https://jolicode.com/blog/notre-retour-sur-le-symfonylive-paris-2026</id>
        <published>2026-04-08T15:42:00+02:00</published>
        <updated>2026-04-08T15:42:00+02:00</updated>
        <link type="text/html" rel="alternate" href="https://jolicode.com/blog/notre-retour-sur-le-symfonylive-paris-2026"/>
        <title>Notre retour sur le SymfonyLive Paris 2026</title>
        <author>
            <name>JoliCode Team</name>
            <uri>https://jolicode.com/</uri>
        </author>            <category term="conférence" />            <category term="symfony" />        <summary><![CDATA[Les années passent, mais certaines traditions restent immuables. Il y a quelques jours, la communauté s&#039;est de nouveau réunie à la Cité Universitaire pour l&#039;édition 2026 du Symfony Live Paris.
Si le monde…]]></summary>
        <content type="html">
            &lt;p&gt;Les années passent, mais certaines traditions restent immuables. Il y a quelques jours, la communauté s&#039;est de nouveau réunie à la Cité Universitaire pour l&#039;édition 2026 du Symfony Live Paris.&lt;/p&gt;
&lt;p&gt;Si le monde de la tech avance à toute vitesse, le cru 2026 conserve la recette qui a fait son succès. Nous étions, comme à notre habitude, présents au rendez-vous. Voici notre retour sur une édition qui prouve que Symfony reste à la pointe des évolutions.&lt;/p&gt;
&lt;h2&gt;Keynote de Fabien Potencier&lt;/h2&gt;
&lt;p&gt;&lt;picture class=&quot;js-dialog-target&quot; data-original-url=&quot;/media/original/2026/symfony-live-paris-2026/Fabien_TUI.jpg&quot; data-original-width=&quot;3143&quot; data-original-height=&quot;1922&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;/media/cache/content-webp/2026/symfony-live-paris-2026/Fabien_TUI.4a744ec6.webp&quot; /&gt;&lt;source type=&quot;image/jpeg&quot; srcset=&quot;/media/cache/content/2026/symfony-live-paris-2026/Fabien_TUI.jpg&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;width: 996px; ; aspect-ratio: calc(3143 / 1922)&quot; src=&quot;https://jolicode.com//media/cache/content/2026/symfony-live-paris-2026/Fabien_TUI.jpg&quot; alt=&quot;Fabien pendant la Keynote&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Fabien Potencier nous a présenté un composant Symfony que beaucoup de gens attendaient impatiemment depuis sa première annonce datant de la &lt;a href=&quot;https://jolicode.com/blog/du-code-des-gaufres-et-des-bds-nous-etions-a-la-symfonycon-a-bruxelles#keynote-d-ouverture-facon-fabpot&quot;&gt;SymfonyCon 2023&lt;/a&gt; : Symfony TUI (pour Terminal User Interface) !&lt;/p&gt;
&lt;p&gt;Avec l&#039;arrivée récente et massive de l’IA dans les habitudes de travail de beaucoup de développeurs, Fabien a trouvé une raison parfaite de relancer son travail sur le composant TUI pour permettre une utilisation plus ergonomique et avancée des LLMs directement depuis un terminal.&lt;/p&gt;
&lt;p&gt;On a d’ailleurs eu le droit à une démonstration de son propre coding agent pour voir en direct ce à quoi on pourrait s’attendre avec l&#039;adoption de ce composant pour discuter avec des LLMs. Et le rendu rivalise avec ce qui peut aujourd’hui être proposé directement dans nos IDEs.&lt;/p&gt;
&lt;p&gt;Mais au-delà de l’intégration évidente avec l’IA, le composant TUI, c’est aussi une évolution du composant Symfony Console que l’on utilise tous (pour rappel, il s’agit de l’un des  premiers composants de l’écosystème Symfony, sorti il y a 15 ans maintenant). L’objectif ici, c’est de laisser Console s’occuper des parties commandes/arguments/output, pour concentrer toute la partie affichage, interaction et interactivité dans TUI.&lt;/p&gt;
&lt;p&gt;Lors de ce talk assez orienté technique, Fabien a expliqué comment TUI fonctionne sous le capot. Sans entrer dans les détails ici, on pourra retenir :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Il existe trois manières de gérer le style: à la manière stylesheet avec des sélecteurs semblables au CSS ; avec des classes utilitaires, comme on le ferait en Tailwind ; ou directement in-line, pour prendre la main de manière ponctuelle comme c&#039;est possible en HTML. Les breakpoints sont aussi gérés avec des media-queries, pour un rendu nativement responsive ;&lt;/li&gt;
&lt;li&gt;Beaucoup de Widgets sont disponibles nativement pour gérer la majorité des cas d’usages (ex: TextWidget pour affichage de texte et ASCII, InputWidget pour les entrées textuelles, SelectListWidget pour des listes scrollables, etc.) mais il est évidemment possible de créer nos propres widgets pour des cas particuliers ;&lt;/li&gt;
&lt;li&gt;TUI utilise PHP Fibers et Revolt pour assurer un affichage et des animations complètement asynchrones et une gestion parallèle de l’affichage et de l’interactivité avec le développeur.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;La liste s’allonge évidemment, et toutes les informations sont disponibles sur le &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://symfony.com/blog/introducing-the-symfony-tui-component&quot;&gt;Blog Post dédié à l’arrivée de Symfony TUI&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Pour conclure cette première conférence, Fabien s&#039;est montré particulièrement enthousiaste. Ce nouveau composant Symfony ouvre des portes immenses, aussi bien pour l&#039;affichage dans la console que pour l&#039;intégration de l&#039;intelligence artificielle.&lt;/p&gt;
&lt;p&gt;Pour prouver la puissance de son outil, il a même fait une démonstration impressionnante : un jeu de &lt;strong&gt;Tetris&lt;/strong&gt; tournant en direct dans son terminal ! Le rendu est fluide et visuellement bluffant, montrant que l&#039;on peut désormais créer de véritables interfaces graphiques (TUI) modernes, directement en PHP.&lt;/p&gt;
&lt;p&gt;Pour terminer, Fabien a lancé une idée très originale pour le futur des contributions sur ce composant : plutôt que d&#039;envoyer une solution toute prête, les développeurs pourraient simplement partager un &lt;strong&gt;prompt&lt;/strong&gt; (une instruction pour l&#039;IA).&lt;/p&gt;
&lt;p&gt;Fabien utiliserait alors son propre assistant et ses propres ressources pour transformer ces instructions en code réel et donner vie aux futures améliorations du projet !&lt;/p&gt;
&lt;h2&gt;La communauté au rythme de l’IA&lt;/h2&gt;
&lt;p&gt;On se souvient du &lt;strong&gt;SymfonyLive 2024&lt;/strong&gt;, où l’IA générative d&#039;images s&#039;invitait déjà dans de nombreuses présentations. À l&#039;époque, c’était encore une nouveauté impressionnante, bien que limitée.
Cette année, le changement est radical : les &lt;strong&gt;agents IA&lt;/strong&gt; étaient omniprésents dans bon nombre de sujets. Cela reflète parfaitement l&#039;évolution de notre quotidien de développeur, qui s&#039;accélère de plus en plus dans cette direction.&lt;/p&gt;
&lt;p&gt;Aujourd&#039;hui, l&#039;IA n&#039;est plus juste un gadget visuel, elle est au cœur du développement de Symfony :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Les nouvelles versions de &lt;strong&gt;Twig&lt;/strong&gt; (le moteur de templates de Symfony) sont désormais gérées de manière quasi automatique par un agent ;&lt;/li&gt;
&lt;li&gt;Une grande partie du code source est également générée par des assistants intelligents.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Cependant, un point reste essentiel : l&#039;IA aide à produire, mais elle ne remplace pas l&#039;humain. La &lt;strong&gt;responsabilité&lt;/strong&gt; finale appartient toujours à la personne qui valide le code. C&#039;est le développeur qui vérifie et garantit la qualité de ce qui est produit avant de l&#039;envoyer en production.&lt;/p&gt;
&lt;h3&gt;L’IA au service des devs : Anatomie d&#039;un assistant de Code Review - Thomas Boileau&lt;/h3&gt;
&lt;p&gt;La volonté de Thomas est de normaliser l’utilisation de l’IA au sein de sa société. Son constat est qu’il est difficile d’intervenir dans le cycle de développement logiciel, au niveau individuel, car l’usage de l’IA est encore très inégal selon les développeurs. C’est pour ça qu’il a préféré intervenir sur la CI.&lt;/p&gt;
&lt;p&gt;Lors de sa présentation, il nous expose donc un cas pratique. Son but : construire un assistant IA qui intervient au moment des code reviews.&lt;/p&gt;
&lt;p&gt;Techniquement, dès qu&#039;une PR est labellisé Ready For Review (RFR) alors un webhook est lancé, et l’assistant IA est déclenché.&lt;/p&gt;
&lt;p&gt;Ici, ce que l&#039;on apprend n’est pas réellement comment utiliser l’IA, mais plutôt un rappel bienvenu sur l’adoption d’une fonctionnalité par des utilisateurs. Il nous a bien expliqué qu’après sa première itération, quasiment personne n&#039;interagissait avec son agent de revue de code.&lt;/p&gt;
&lt;p&gt;En effet, comme toutes les nouveautés, l’essentiel, c&#039;est de construire avec les utilisateurs finaux et d’éviter au maximum la friction. C’est donc après avoir mesuré l’usage de son bot et consulté les développeurs (utilisateurs) qu’il a proposé une nouvelle version, cette fois-ci plus utile pour tout le monde.&lt;/p&gt;
&lt;p&gt;Évidement, Thomas nous rappelle qu’il aurait pu utiliser une solution sur étagère, mais il souligne les contraintes réglementaires qui s&#039;appliquent à son domaine.&lt;/p&gt;
&lt;p&gt;Sa conclusion est que le plus important dans ce projet reste la DX pour avoir une bonne adoption, et surtout que ce bot IA ne remplace pas l&#039;humain, il est créé dans le but d’être seulement un plus dans la boucle.&lt;/p&gt;
&lt;h3&gt;Développer un Coding Agent en PHP : dans les coulisses du &amp;quot;Harness&amp;quot; - Fabien Potencier&lt;/h3&gt;
&lt;p&gt;&lt;picture class=&quot;js-dialog-target&quot; data-original-url=&quot;/media/original/2026/symfony-live-paris-2026/fabien-potencies-ia-niveau.jpg&quot; data-original-width=&quot;1600&quot; data-original-height=&quot;738&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;/media/cache/content-webp/2026/symfony-live-paris-2026/fabien-potencies-ia-niveau.d189aa63.webp&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;/media/cache/content/2026/symfony-live-paris-2026/fabien-potencies-ia-niveau.jpg&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;width: 996px; ; aspect-ratio: calc(1600 / 738)&quot; src=&quot;https://jolicode.com//media/cache/content/2026/symfony-live-paris-2026/fabien-potencies-ia-niveau.jpg&quot; alt=&quot;Les différents niveaux d&#039;utilisation de l&#039;IA selon Fabien&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Tout a commencé par un défi sur Twitter. Un utilisateur affirmait qu&#039;il était impossible de créer un &lt;strong&gt;coding agent&lt;/strong&gt; (un assistant capable de coder de façon autonome) performant en utilisant le langage PHP. Piqué au vif, Fabien Potencier, le créateur de Symfony, a décidé de prouver le contraire en développant son propre assistant.&lt;/p&gt;
&lt;p&gt;Plutôt que d&#039;utiliser des outils tout prêts comme GitHub Copilot ou Claude Code, fabriquer son propre agent permet de comprendre les coulisses de l’intelligence artificielle. On découvre alors comment se déroule réellement une &amp;quot;discussion&amp;quot; entre un développeur et un modèle de langage (LLM).&lt;/p&gt;
&lt;p&gt;Pour rendre l&#039;outil agréable à utiliser, il a utilisé le composant &lt;strong&gt;TUI de Symfony&lt;/strong&gt;. Cela permet d&#039;avoir une interface textuelle interactive directement dans sa console.&lt;/p&gt;
&lt;p&gt;Pour que l&#039;agent soit intelligent, il doit communiquer avec un modèle (comme Claude Opus, Claude Sonnet, GPT 5). Mais les modèles évoluent sans cesse. Fabien a donc créé un petit outil, &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://github.com/symfony/models-dev&quot;&gt;symfony/models-dev&lt;/a&gt;, qui répertorie de manière automatique tous les modèles disponibles afin de toujours utiliser la version la plus récente.&lt;/p&gt;
&lt;p&gt;Discuter avec un chat, c&#039;est facile. Mais pour qu&#039;un agent soit utile, il doit pouvoir agir sur votre ordinateur. C&#039;est là qu&#039;interviennent les &lt;em&gt;Tools&lt;/em&gt; (outils). De manière surprenante, Fabien n&#039;a eu besoin que de 4 outils de base :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Read&lt;/em&gt; : pour lire le contenu d&#039;un fichier.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Write&lt;/em&gt; : pour créer un nouveau fichier.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Update&lt;/em&gt; : pour modifier un fichier existant sans gaspiller de token.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Bash&lt;/em&gt; : pour exécuter n&#039;importe quelle commande (lancer des tests, installer un package, etc.).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Un agent classique oublie tout dès que la session se ferme. Pour corriger cela, chaque échange est enregistré dans une base de données.&lt;/p&gt;
&lt;p&gt;L’astuce géniale ? Cette mémoire est stockée sous forme d&#039;&lt;em&gt;arbre&lt;/em&gt;. Cela permet à l&#039;agent de revenir en arrière à un point précis pour tester une autre solution si la première n&#039;a pas fonctionné.&lt;/p&gt;
&lt;p&gt;Plus on discute avec l&#039;IA, plus le &amp;quot;contexte&amp;quot; (le nombre de mots envoyés) devient important. Si on dépasse la limite du modèle, il sature. Fabien a donc mis en place un système de compression intelligent :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Il garde toujours le tout premier message (les instructions de base).&lt;/li&gt;
&lt;li&gt;Il garde les derniers messages récents.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Il résume&lt;/em&gt; tout ce qui se trouve entre les deux.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Les conseils de Fabien&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Durant sa démonstration, il a partagé quelques astuces précieuses :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Les Skills&lt;/em&gt; : Dès qu&#039;il réalise qu&#039;il répète une tâche, ou une instruction, il crée un &amp;quot;skill&amp;quot; (une compétence) pour son agent. C&#039;est comme écrire des tests : on a l&#039;impression de perdre du temps au début, mais on en gagne énormément sur le long terme ;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Le regard neuf&lt;/em&gt; : Parfois, l&#039;IA s&#039;embrouille. Fabien conseille de lui demander d&#039;analyser la situation avec &amp;quot;un regard neuf&amp;quot; (&lt;em&gt;fresh eyes&lt;/em&gt;). Cela donne souvent des résultats spectaculaires pour débloquer un bug complexe.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Bien qu&#039;il n&#039;ait pas eu le temps de tout montrer, notamment son système d&#039;orchestration dans le cloud, la preuve est faite : &lt;strong&gt;le PHP est un langage de choix pour l&#039;intelligence artificielle !&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;Embeddings en PHP : Symfony AI en pratique - Grégoire Pineau&lt;/h3&gt;
&lt;p&gt;&lt;picture class=&quot;js-dialog-target&quot; data-original-url=&quot;/media/original/2026/symfony-live-paris-2026/gregoire-symfony-ai.jpg&quot; data-original-width=&quot;4080&quot; data-original-height=&quot;3072&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;/media/cache/content-webp/2026/symfony-live-paris-2026/gregoire-symfony-ai.c803838f.webp&quot; /&gt;&lt;source type=&quot;image/jpeg&quot; srcset=&quot;/media/cache/content/2026/symfony-live-paris-2026/gregoire-symfony-ai.jpg&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;width: 996px; ; aspect-ratio: calc(4080 / 3072)&quot; src=&quot;https://jolicode.com//media/cache/content/2026/symfony-live-paris-2026/gregoire-symfony-ai.jpg&quot; alt=&quot;Grégoire lors de sa conférence sur Symfony AI&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Le talk de &lt;a href=&quot;https://jolicode.com/qui-sommes-nous/equipe/gregoire-pineau&quot;&gt;Grégoire&lt;/a&gt; nous met tout de suite dans la pratique avec une vraie mise en situation autour des embeddings et de la similarité de contenu.&lt;/p&gt;
&lt;p&gt;Il part sur un cas d’usage qui parle à tout le monde : faire correspondre des URLs entre un ancien et un nouveau site. Là où on pourrait partir sur des règles compliquées ou du matching approximatif, les embeddings offrent une solution plus robuste : on compare directement le sens sémantique des pages.&lt;/p&gt;
&lt;p&gt;Ce qui marche particulièrement bien, c’est le fil rouge visuel de la conférence. Un schéma du processus est affiché, puis réutilisé à chaque étape. Ce qui rend la progression assez claire : on comprend où on en est, ce qu’on fait, et pourquoi on le fait.&lt;/p&gt;
&lt;p&gt;La conférence prend le temps de poser les bases :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ce qu’est un embedding (une représentation vectorielle d’un contenu) ;&lt;/li&gt;
&lt;li&gt;à quoi ça sert (mesurer de la similarité sémantique) ;&lt;/li&gt;
&lt;li&gt;et surtout dans quels cas ça devient utile.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ensuite, on rentre dans le concret :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;comment choisir un modèle selon son besoin ;&lt;/li&gt;
&lt;li&gt;comment vectoriser ses données depuis Symfony ;&lt;/li&gt;
&lt;li&gt;où stocker ces vecteurs (PostgreSQL, Redis, etc.) ;&lt;/li&gt;
&lt;li&gt;et comment les requêter efficacement.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;L&#039;intérêt de cette présentation, c&#039;est que Grégoire nous a montré que tout se fait sans quitter l’écosystème Symfony, grâce à Symfony AI. Cette initiative fournit ainsi toutes les abstractions nécessaires pour manipuler modèles, stores, agents et bien plus encore. N&#039;hésitez pas à consulter le site dédié à &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://ai.symfony.com/&quot;&gt;Symfony AI&lt;/a&gt; pour découvrir tout ça.
&lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://speakerdeck.com/lyrixx/embeddings-symfony-ai-en-pratique&quot;&gt;Vous pouvez retrouver ses slides en ligne&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Retours d’expériences et présentations techniques&lt;/h2&gt;
&lt;p&gt;Tous ces nouveaux outils basés sur l&#039;IA ne doivent pas éclipser l&#039;importance de la technicité en dehors de l&#039;intelligence artificielle ! Au contraire, nous apprécions également les outils éprouvés et bien établis, et les retours d&#039;expériences de manière générale.&lt;/p&gt;
&lt;h3&gt;Chiffrez vos données avec Doctrine, en restant recherchable - Jérôme Tamarelle&lt;/h3&gt;
&lt;p&gt;Lors de cette conférence, Jérôme Tamarelle a rappelé un point essentiel : la sécurité des données ne concerne pas uniquement les informations directement identifiantes (comme un email ou un nom), mais aussi les données indirectes. Croisées entre elles, ces dernières peuvent suffire à identifier une personne.&lt;/p&gt;
&lt;p&gt;Il est important de distinguer deux approches souvent confondues :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Le chiffrement&lt;/em&gt; : les données sont transformées de manière réversible. On peut les déchiffrer à l’aide d’une clé ;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Le hashage&lt;/em&gt; : il s’agit d’une empreinte unique et fixe d’une donnée. Cette opération est irréversible.
Plusieurs types de chiffrement existent, dont le chiffrement aléatoire et le chiffrement déterministe.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Avec le &lt;em&gt;chiffrement aléatoire&lt;/em&gt;, chaque valeur est chiffrée différemment, même si elle est identique à une autre.
Par exemple, une même adresse email enregistrée deux fois en base produira deux valeurs chiffrées différentes.&lt;/p&gt;
&lt;p&gt;Avec le &lt;em&gt;chiffrement déterministe&lt;/em&gt;, une même donnée produira toujours le même résultat chiffré.&lt;/p&gt;
&lt;p&gt;Avec Doctrine, on encapsule le chiffrement directement dans Doctrine via des types personnalisés.
Un champ devient &amp;quot;chiffré&amp;quot; simplement par sa définition.&lt;/p&gt;
&lt;p&gt;Mais du coup, on ne peut plus faire de recherche sur ces champs sans passer par Doctrine.&lt;/p&gt;
&lt;p&gt;Sécuriser ses données, c’est accepter de complexifier son application. Et surtout, le faire dès la conception.&lt;/p&gt;
&lt;h3&gt;Doctrine inheritance - Rémi JANOT&lt;/h3&gt;
&lt;p&gt;Rémi commence par présenter la différence entre héritage (mapper une hiérarchie de classe) et polymorphisme (une clef étrangère qui pointe vers une autre classe) car le vocabulaire utilisé par les différents ORM et framework peuvent parfois prêter à confusion.&lt;/p&gt;
&lt;p&gt;S&#039;ensuit un rappel bienvenu de l’héritage implémenté directement au niveau de Doctrine ; avec des exemples concrets, il passe en revue toutes les combinaisons possibles : soit avec des MappedSuperClass, soit des DiscriminatorMap.&lt;/p&gt;
&lt;p&gt;Fort de son expérience, il nous explique aussi qu’il est possible d’assez facilement passer d’une architecture Single Table Inheritance (STI) vers Class Table Inheritance (CTI). Donc les choix techniques ne sont pas forcément figés, les projets évoluent, et des solutions sont toujours possibles. Il nous rappelle aussi que lorsqu’on utilise les CTI, il est important de bien vérifier les index pour gagner en performance.&lt;/p&gt;
&lt;h3&gt;JSON + SQL : hérésie ou élégance ? Retour d&#039;expérience - Rémy Bonfils, Olivier FOURNY&lt;/h3&gt;
&lt;p&gt;On reste ici dans le thème de la modélisation de nos bases de données avec un retour d’expérience sur l’utilisation de JSON dans nos tables SQL, via l’exemple d’une application mobile offline permettant de configurer des maisons imprimées en 3D (oui oui).&lt;/p&gt;
&lt;p&gt;Étant donné la nature très flexible des paramètres d’impression (80000 configurations possibles, importées depuis un CSV), la question de comment les stocker se pose dès le départ.
Rémy et Olivier nous expliquent vouloir tout d’abord se diriger vers une modélisation de type Entity-Attribute-Valeur, où les paramètres ne sont pas représentés par des colonnes dans nos tables, mais par des lignes (ça doit parler aux personnes devant travailler sur des projets Magento ou Drupal) : beaucoup de flexibilité évidemment, mais aussi l’impossibilité d’avoir un minimum de structure dans nos données (tout est varchar). Et surtout des requêtes beaucoup plus complexes et donc des performances catastrophiques.&lt;/p&gt;
&lt;p&gt;L’équipe se penche donc sur une autre solution : le stockage des paramètres dans des colonnes de la base de données en format JSON.&lt;/p&gt;
&lt;p&gt;Et la surprise, les performances sont à peine inférieures à une modélisation classique de base de données (avec des tables liées par des clés étrangères), mais en gardant la flexibilité voulue ! Et grâce aux fonctions SQL permettant de manipuler du JSON, les requêtes restent simples et lisibles.&lt;/p&gt;
&lt;p&gt;Pour notre part, à JoliCode, nous avons l’habitude de profiter du type JSON dans nos bases de données relationnelles (PostgreSQL ou MySQL), et nous vous le recommandons lorsque le besoin s’en fait ressentir.&lt;/p&gt;
&lt;h3&gt;ClickHouse pour les développeurs Symfony - Romain Neutron&lt;/h3&gt;
&lt;p&gt;&lt;picture class=&quot;js-dialog-target&quot; data-original-url=&quot;/media/original/2026/symfony-live-paris-2026/clickhouse-romain-neutron.jpg&quot; data-original-width=&quot;1600&quot; data-original-height=&quot;901&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;/media/cache/content-webp/2026/symfony-live-paris-2026/clickhouse-romain-neutron.6afbb7d4.webp&quot; /&gt;&lt;source type=&quot;image/jpeg&quot; srcset=&quot;/media/cache/content/2026/symfony-live-paris-2026/clickhouse-romain-neutron.jpg&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;width: 996px; ; aspect-ratio: calc(1600 / 901)&quot; src=&quot;https://jolicode.com//media/cache/content/2026/symfony-live-paris-2026/clickhouse-romain-neutron.jpg&quot; alt=&quot;Romain Neutron lors de son talk sur ClickHouse&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Dans cette session, Romain Neutron a abordé la problématique de la gestion des données analytiques et des logs à grande échelle, des domaines où les bases relationnelles classiques comme MySQL ou PostgreSQL atteignent souvent leurs limites. Il a présenté &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://clickhouse.com/&quot;&gt;ClickHouse&lt;/a&gt;, une base de données orientée colonnes ultra-performante, comme la solution idéale pour traiter des volumes massifs de données en temps réel. L&#039;idée centrale n&#039;est pas de remplacer votre base de données habituelle, mais de l&#039;épauler pour des besoins spécifiques d&#039;agrégation et de dashboards instantanés.&lt;/p&gt;
&lt;p&gt;Côté technique, la conférence a mis en lumière la simplicité d&#039;intégration de ClickHouse dans l&#039;écosystème Symfony. Il a également partagé des benchmarks impressionnants comparant les temps de réponse sur des agrégations de plusieurs millions de lignes, montrant que ClickHouse peut transformer des requêtes de plusieurs secondes en résultats quasi instantanés.&lt;/p&gt;
&lt;p&gt;Enfin, Romain a insisté sur les bonnes pratiques et les pièges à éviter, notamment sur la structure des données et le choix des moteurs de table (comme MergeTree). C&#039;est un talk indispensable pour les développeurs cherchant à scaler leur stack analytique tout en restant dans un environnement PHP familier. De notre côté, on utilise ClickHouse dans plusieurs projets, surtout dans &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://redirection.io/&quot;&gt;redirection.io&lt;/a&gt; pour les parties logs, analytics et crawler. On ne peut donc que vous recommander de vous y intéresser.&lt;/p&gt;
&lt;p&gt;Ses slides sont &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://speakerdeck.com/romainneutron/clickhouse-for-symfony-developers-symfonylive-paris-2026&quot;&gt;disponibles en ligne&lt;/a&gt; pour retrouver toutes les informations importantes.&lt;/p&gt;
&lt;h2&gt;Symfony UX et la suite Hotwired&lt;/h2&gt;
&lt;p&gt;Cette année, on continue de parler de Symfony UX, et en particulier, deux retours d’expériences spécifiquement axés sur &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://hotwired.dev/&quot;&gt;hotwired.dev&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Du web au mobile avec Symfony &amp;amp; Hotwire Native - Imad ZAIRIG&lt;/h3&gt;
&lt;p&gt;Imad nous a préparé une conférence sur le nouveau bundle Symfony UX Native qui utilise Hotwire Native. Basé sur un exemple, on peut voir comment l’application est architecturée.&lt;/p&gt;
&lt;p&gt;On a eu l&#039;exemple d&#039;un bouton, rendu avec un composant bouton mobile, mais dont l&#039;événement click est écouté par stimulus du côté de l’application Symfony&lt;/p&gt;
&lt;p&gt;Cela fonctionne avec des webview pour que ce soit toujours Symfony qui gère le back-end et le front, mais avec des comportements mobiles gérés nativement (ex : la navigation et les transitions). On note qu’il y a quand même encore quelques fois ou il faut lancer le projet mobile avec Xcode par exemple pour iOS.&lt;/p&gt;
&lt;p&gt;Pour utiliser les capacités natives des applications mobiles (type appareil photo) il faut passer par des &amp;quot;Bridges Component&amp;quot;, ça demande malgré tout du code côté mobile.&lt;/p&gt;
&lt;p&gt;Selon la complexité de l’application et la taille de l’équipe, Symfony UX Native est une piste à explorer.&lt;/p&gt;
&lt;h3&gt;Édition simultanée : facile avec Symfony UX - David Buchmann&lt;/h3&gt;
&lt;p&gt;Au travers d’un exemple concret David nous a fait voir comment intégrer toute la suite d’outils de hotwired.dev. Il commence avec Turbo (et un peu de Turbo Frames aussi) et nous fait voir à quel point c’est bien intégré à Symfony UX. Puis sa conférence continue avec la mise en place de Mercure (grâce à son intégration dans FrankenPHP), et finalement le tout s’intègre parfaitement via des contrôleurs Stimulus qui écoutent les messages Mercure.&lt;/p&gt;
&lt;p&gt;Il résume les avantages de Hotwire comme ceci : la logique reste dans le backend, la sécurité est intégrée. La complexité du frontend s’en trouve réduite.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Si cette édition du SymfonyLive Paris 2026 nous a offert un aperçu saisissant de l&#039;intégration massive de l&#039;Intelligence Artificielle au cœur de l&#039;écosystème Symfony, elle prouve une chose essentielle : l&#039;importance des conférences n&#039;a jamais été aussi grande. Notre métier de développeur est en pleine mutation, et nous avons fort à faire pour rester à jour.&lt;/p&gt;
&lt;p&gt;Le fil conducteur de cette année reste cependant une évidence : quelle que soit la puissance des outils, &lt;strong&gt;l&#039;humain reste au centre de la boucle&lt;/strong&gt;. L&#039;IA est un assistant extraordinaire pour la production de code et les tâches répétitives, mais c&#039;est bien la communauté, la validation humaine et le partage de connaissances qui garantissent la qualité et la progression de notre écosystème.
Merci aux organisateurs, aux conférenciers, et à la communauté d&#039;avoir fait de cette édition 2026 un moment marquant, et rendez-vous l&#039;année prochaine pour continuer à naviguer ensemble dans le futur du développement web !&lt;/p&gt;

        </content>
    </entry>    <entry>
        <id>https://jolicode.com/blog/jane-now-supports-openapi-3-1-and-json-schema-2020-12</id>
        <published>2026-03-30T11:50:00+02:00</published>
        <updated>2026-03-30T11:50:00+02:00</updated>
        <link type="text/html" rel="alternate" href="https://jolicode.com/blog/jane-now-supports-openapi-3-1-and-json-schema-2020-12"/>
        <title>Jane now supports OpenAPI 3.1 and JSON Schema 2020-12</title>
        <author>
            <name>JoliCode Team</name>
            <uri>https://jolicode.com/</uri>
        </author>            <category term="symfony" />            <category term="jsonapi" />            <category term="jane" />            <category term="openapi" />        <summary><![CDATA[Version 7.11.0 of Jane PHP, the API client and Normalizer generator, is now available. This major update to the generation engine focuses on aligning with the latest industry standards through the support…]]></summary>
        <content type="html">
            &lt;p&gt;Version &lt;strong&gt;7.11.0&lt;/strong&gt; of Jane PHP, the API client and Normalizer generator, is now available. This major update to the generation engine focuses on aligning with the latest industry standards through the support of &lt;strong&gt;JSON Schema 2020-12&lt;/strong&gt; and &lt;strong&gt;OpenAPI 3.1&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;🚀 Major Updates&lt;/h2&gt;
&lt;h3&gt;JSON Schema 2020-12 Support&lt;/h3&gt;
&lt;p&gt;One of the most important changes introduced in this version is the integration of support for &lt;strong&gt;JSON Schema 2020-12&lt;/strong&gt; (PR &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://github.com/janephp/janephp/pull/918&quot;&gt;#918&lt;/a&gt;). This update allows Jane to process much more modern and complex data schemas, thus offering a richer semantics for describing your structures.&lt;/p&gt;
&lt;p&gt;It is important to note that this evolution was designed to be totally transparent: Jane retains &lt;strong&gt;backward compatibility with the 2019-09 draft&lt;/strong&gt;. You can therefore benefit from the latest advances without worrying about your existing schemas.&lt;/p&gt;
&lt;p&gt;This dual compatibility allows the tool to offer increased precision in two areas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;PHP model generation&lt;/strong&gt;: The generated classes more accurately reflect the constraints and relationships defined in your schemas, thus reducing the need for manual adjustments.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Data validation&lt;/strong&gt;: The support of these specifications guarantees that the built-in validation logic is perfectly aligned with modern API requirements.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;OpenAPI 3.1 Support&lt;/h3&gt;
&lt;p&gt;The other pillar of this version is the support for the OpenAPI 3.1 specification (PR &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://github.com/janephp/janephp/pull/904&quot;&gt;#904&lt;/a&gt;). This update is structural because it finally aligns the OpenAPI standard with JSON Schema, thus simplifying the management of data models.&lt;/p&gt;
&lt;p&gt;Thanks to this support, Jane natively leverages the new capabilities of the standard:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Convergence with JSON Schema&lt;/strong&gt;: OpenAPI 3.1 becomes a &amp;quot;superset&amp;quot; of JSON Schema, allowing Jane to interpret complex definitions without loss of information.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Native and nullable typing&lt;/strong&gt;: Jane uses the new syntax to generate PHP properties with exact typing (e.g &lt;code&gt;?string&lt;/code&gt;), ensuring consistency between your API contract and your code.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;📖 New Official Documentation&lt;/h2&gt;
&lt;p&gt;Alongside these technical developments, the project&#039;s documentation has been refreshed to provide a better user experience. It centralizes the installation, configuration, and usage guides for all components.&lt;/p&gt;
&lt;p&gt;It is available here: &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://jane.jolicode.com/latest/&quot;&gt;https://jane.jolicode.com/latest/&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;🛠️ Continuous Improvements and Maintenance&lt;/h2&gt;
&lt;p&gt;Beyond these major developments, this version is also the occasion for a big spring cleaning for the project. We have consolidated the codebase with a series of stability fixes, particularly regarding the handling of numeric types in query strings, and an overall update of the maintenance tools. These adjustments, although more discreet, guarantee better reliability of the generator and simplify the installation of dependencies for your projects.&lt;/p&gt;
&lt;h2&gt;📦 Get Started!&lt;/h2&gt;
&lt;p&gt;All these improvements are available now via the &lt;strong&gt;v7.11.0&lt;/strong&gt; tag. Whether you want to take advantage of the latest OpenAPI specifications or simply benefit from a more robust generation engine, we strongly encourage you to update your projects:&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;composer&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; update&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; jane-php/&lt;/span&gt;&lt;span class=&quot;syntax-11&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; -W&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Feel free to test these new features, explore the new documentation, and give us your feedback on GitHub. Jane continues to grow thanks to your usage and contributions, so enjoy!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;📚 Official documentation: &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://jane.jolicode.com/latest/&quot;&gt;jane.jolicode.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;💻 GitHub Repository: &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://github.com/janephp/janephp&quot;&gt;janephp/janephp&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;🚀 Release v7.11.0: &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://github.com/janephp/janephp/releases/tag/v7.11.0&quot;&gt;Check the changelog&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

        </content>
    </entry>    <entry>
        <id>https://jolicode.com/blog/jane-supporte-maintenant-json-schema-2020-12-et-openapi-3-1</id>
        <published>2026-03-30T11:34:00+02:00</published>
        <updated>2026-03-30T11:34:00+02:00</updated>
        <link type="text/html" rel="alternate" href="https://jolicode.com/blog/jane-supporte-maintenant-json-schema-2020-12-et-openapi-3-1"/>
        <title>Jane supporte maintenant JSON Schema 2020-12 et OpenAPI 3.1</title>
        <author>
            <name>JoliCode Team</name>
            <uri>https://jolicode.com/</uri>
        </author>            <category term="symfony" />            <category term="json" />            <category term="jsonapi" />            <category term="jane" />        <summary><![CDATA[La version v7.11.0 de Jane PHP, le générateur de client d&#039;API et de Normalizer, est désormais disponible. Cette mise à jour majeure du moteur de génération se concentre sur l&#039;alignement avec les derniers…]]></summary>
        <content type="html">
            &lt;p&gt;La version &lt;strong&gt;v7.11.0&lt;/strong&gt; de Jane PHP, le générateur de client d&#039;API et de Normalizer, est désormais disponible. Cette mise à jour majeure du moteur de génération se concentre sur l&#039;alignement avec les derniers standards de l&#039;industrie via le support de &lt;strong&gt;JSON Schema 2020-12&lt;/strong&gt; et d&#039;&lt;strong&gt;OpenAPI 3.1&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;🚀 Évolutions majeures&lt;/h2&gt;
&lt;h3&gt;Support de JSON Schema 2020-12&lt;/h3&gt;
&lt;p&gt;L&#039;un des changements les plus importants introduits dans cette version est l&#039;intégration du support pour &lt;strong&gt;JSON Schema 2020-12&lt;/strong&gt; (PR &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://github.com/janephp/janephp/pull/918&quot;&gt;#918&lt;/a&gt;). Cette mise à jour permet à Jane de traiter des schémas de données beaucoup plus modernes et complexes, offrant ainsi une sémantique plus riche pour la description de vos structures.&lt;/p&gt;
&lt;p&gt;Il est important de noter que cette évolution a été pensée pour être totalement transparente : Jane conserve une &lt;strong&gt;rétrocompatibilité avec la draft 2019-09&lt;/strong&gt;. Vous pouvez donc bénéficier des dernières avancées sans craindre pour vos schémas existants.&lt;/p&gt;
&lt;p&gt;Cette double compatibilité permet à l&#039;outil d&#039;offrir une précision accrue dans deux domaines :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;La génération des modèles PHP&lt;/strong&gt; : Les classes générées reflètent plus fidèlement les contraintes et relations définies dans vos schémas, réduisant ainsi le besoin d&#039;ajustements manuels.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;La validation des données&lt;/strong&gt; : Le support de ces spécifications garantit que la logique de validation intégrée est en parfaite adéquation avec les exigences modernes des API.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Support d&#039;OpenAPI 3.1&lt;/h3&gt;
&lt;p&gt;L&#039;autre pilier de cette version est le support de la spécification &lt;strong&gt;OpenAPI 3.1&lt;/strong&gt; (PR &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://github.com/janephp/janephp/pull/904&quot;&gt;#904&lt;/a&gt;). Cette mise à jour est structurante car elle aligne enfin le standard OpenAPI sur JSON Schema, simplifiant ainsi la gestion des modèles de données.&lt;/p&gt;
&lt;p&gt;Grâce à ce support, Jane exploite nativement les nouvelles capacités du standard :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Convergence avec JSON Schema&lt;/strong&gt; : OpenAPI 3.1 devient un &amp;quot;superset&amp;quot; de JSON Schema, permettant à Jane d&#039;interpréter des définitions complexes sans perte d&#039;information.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Typage natif et nullable&lt;/strong&gt; : Jane utilise la nouvelle syntaxe pour générer des propriétés PHP au typage exact (ex &lt;code&gt;?string&lt;/code&gt;), assurant une cohérence entre votre contrat d&#039;API et votre code.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;📖 Nouvelle documentation officielle&lt;/h2&gt;
&lt;p&gt;Parallèlement à ces évolutions techniques, la documentation du projet a reçu un coup de neuf pour offrir une meilleure expérience aux utilisateurs. Elle centralise les guides d&#039;installation, de configuration et d&#039;utilisation pour l&#039;ensemble des composants.&lt;/p&gt;
&lt;p&gt;Elle est disponible ici: &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://jane.jolicode.com/latest/&quot;&gt;jane.jolicode.com&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;🛠️ Améliorations continues et maintenance&lt;/h2&gt;
&lt;p&gt;Au-delà de ces évolutions majeures, cette version est aussi l&#039;occasion d&#039;un grand nettoyage de printemps pour le projet. Nous avons consolidé la base de code avec une série de correctifs de stabilité, notamment sur la gestion des types numériques dans les query strings, et une mise à jour globale des outils de maintenance. Ces ajustements, bien que plus discrets, garantissent une meilleure fiabilité du générateur et simplifient l&#039;installation des dépendances pour vos projets.&lt;/p&gt;
&lt;h2&gt;📦 Lancez-vous !&lt;/h2&gt;
&lt;p&gt;Toutes ces améliorations sont disponibles dès maintenant via le tag &lt;strong&gt;v7.11.0&lt;/strong&gt;. Que vous souhaitiez profiter des dernières spécifications OpenAPI ou simplement bénéficier d&#039;un moteur de génération plus robuste, nous vous encourageons vivement à mettre à jour vos projets :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;composer&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; update&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; jane-php/&lt;/span&gt;&lt;span class=&quot;syntax-11&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; -W&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;N&#039;hésitez pas à tester ces nouveautés, à explorer la nouvelle documentation et à nous faire part de vos retours sur GitHub. Jane continue de grandir grâce à vos usages et vos contributions, alors profitez-en bien !&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;📚 Documentation officielle : &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://jane.jolicode.com/latest/&quot;&gt;jane.jolicode.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;💻 Dépôt GitHub : &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://github.com/janephp/janephp&quot;&gt;janephp/janephp&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;🚀 Release v7.11.0 : &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://github.com/janephp/janephp/releases/tag/v7.11.0&quot;&gt;Consulter le changelog&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

        </content>
    </entry>    <entry>
        <id>https://jolicode.com/blog/deploiement-on-premise-partie-2-castor-a-la-rescousse</id>
        <published>2026-03-26T10:41:00+01:00</published>
        <updated>2026-03-26T10:41:00+01:00</updated>
        <link type="text/html" rel="alternate" href="https://jolicode.com/blog/deploiement-on-premise-partie-2-castor-a-la-rescousse"/>
        <title>Déploiement On-Premise - Partie 2 - Castor à la rescousse</title>
        <author>
            <name>JoliCode Team</name>
            <uri>https://jolicode.com/</uri>
        </author>            <category term="devops" />            <category term="docker" />            <category term="castor" />        <summary><![CDATA[Dans le précédent article, nous avons vu toutes les étapes nécessaires pour préparer les images Docker qui seront utilisées en production. Mais nous allons maintenant aller plus loin pour automatiser…]]></summary>
        <content type="html">
            &lt;p&gt;Dans le &lt;a href=&quot;https://jolicode.com/blog/deploiement-on-premise-le-socle-docker&quot;&gt;précédent article&lt;/a&gt;, nous avons vu toutes les étapes nécessaires pour préparer les images Docker qui seront utilisées en production. Mais nous allons maintenant aller plus loin pour automatiser et simplifier encore un peu plus cette étape grâce à Castor et les runners GitLab, le but étant de
faciliter la procédure de déploiement de nouvelles versions de l&#039;application afin que le client puisse
être autonome.&lt;/p&gt;
&lt;h2&gt;Création et publication des images&lt;/h2&gt;
&lt;p&gt;Comme souvent quand nos projets nécessitent de lancer des commandes, nous mettons en place des tâches Castor pour simplifier la DX. Nous avons donc créé une task &lt;code&gt;production:build&lt;/code&gt; qui applique tout ce que nous avons vu dans l&#039;article précédent :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;&amp;#x3C;?&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;php&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Castor\Attribute\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;AsOption&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Castor\Attribute\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;AsTask&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Castor\Exception\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;ProblemException&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Symfony\Component\Console\Input\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;InputOption&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Castor\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;capture&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Castor\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;check&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Castor\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Castor\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Castor\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Castor\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Castor\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;variable&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; REGISTRY&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;&amp;#x3C;url du registre&gt;:4567/plancq/arsol/&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; DEFAULT_BRANCH&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;main&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; BAKE_FILE&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; __DIR__&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; .&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;/../infrastructure/production/bake.hcl&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; IMAGES_TO_TAG&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;    &#039;postgres:16&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;postgres&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;    &#039;getmeili/meilisearch:v1.16&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;meilisearch&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;#[AsTask(description: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Build production docker images&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, namespace: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;production:docker&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; build&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    #[AsArgument(description: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Version of the images&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    ?string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $tagVersion &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; null&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    #[AsOption(description: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Force the build whatever the current branch state&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    bool&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $force &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; false&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    #[AsOption(description: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Push the images to the registry&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, mode: &lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;InputOption&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;VALUE_NEGATABLE&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    ?bool&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $push &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; null&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    #[AsOption(description: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Update docker-compose.yml with current tag&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, mode: &lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;InputOption&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;VALUE_NEGATABLE&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    ?bool&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $updateDockerCompose &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; null&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; void&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    $currentBranch &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; capture&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;git&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;branch&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;--show-current&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;    check&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Checking current branch:&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;You must be on the main branch to build the production images. Change the current branch or use --force to bypass this check.&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;static&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; fn&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; () =&gt; &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;DEFAULT_BRANCH&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; ===&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $currentBranch &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;||&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $force);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    $currentChanges &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; capture&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;git&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;status&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;--porcelain&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;    check&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Checking git working tree:&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;You have uncommitted changes. Git stash everything before building image or use --force to bypass this check.&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;static&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; fn&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; () =&gt; &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;$currentChanges &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;||&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $force);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    if&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;$force) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;        run&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;git&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;pull&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;origin&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;DEFAULT_BRANCH&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    $validateVersion &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; static&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $tagVersion)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        if&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;$tagVersion) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;            throw&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; new&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; \RuntimeException&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Version is required&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        if&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt;preg_match&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;/&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;^&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;\d&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;{2}&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;\.\d&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;{2}&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;\.\d&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;{2}&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;(-&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;\d&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;)?&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;/&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, $tagVersion)) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;            throw&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; new&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; \RuntimeException&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Version must be in the format YYYY.MM.DD (eventually with a revision like YYYY.MM.DD-1), got: &#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; .&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $tagVersion);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        return&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $tagVersion;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    if&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; ($tagVersion) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        $tagVersion &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $validateVersion($tagVersion);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    } &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        $tagVersion &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; io&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;ask&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Please provide the version of the production images to build:&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Y.m.d&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;), $validateVersion);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    $tags &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; [$tagVersion, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;lastest&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;    io&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;section&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Building the local development images...&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    \docker\&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    if&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;exists&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;workingDirectory &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;/.dockerignore&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;        fs&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;rename&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;workingDirectory &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;/.dockerignore&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;workingDirectory &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;/.dockerignore.tmp&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    try&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        $command &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;            &#039;docker&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;buildx&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;bake&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;            &#039;--file&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;BAKE_FILE&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;            &#039;--load&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        ];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        foreach&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;getImagesToBuild&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;() &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $targetName &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $config) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;            foreach&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; ($tags &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $tag) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;                $command[] &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;--set&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;                $command[] &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $targetName &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;.tags+=&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; .&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; REGISTRY&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; .&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $targetName &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;:&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; .&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $tag;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;            }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;            $command[] &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;--set&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;            $command[] &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $targetName &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;.args.PROJECT_NAME=&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; .&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; variable&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;project_name&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;        run&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($command);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    } &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;finally&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        if&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt;file_exists&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;workingDirectory &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;/.dockerignore.tmp&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;            fs&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;rename&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;workingDirectory &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;/.dockerignore.tmp&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;workingDirectory &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;/.dockerignore&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    foreach&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;IMAGES_TO_TAG&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; as&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $sourceImage &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $image) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        foreach&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; ($tags &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $tag) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;            run&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;docker&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;image&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;pull&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, $sourceImage]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;            run&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;docker&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;image&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;tag&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, $sourceImage, &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;REGISTRY&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; .&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $image &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;:&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; .&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $tag]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;    io&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;success&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Production images are now built.&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    if&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; ($updateDockerCompose &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;||&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; ===&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $updateDockerCompose &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;&amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; io&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;confirm&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Do you want to update the docker-compose with the current version?&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;))) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;        updateDockerCompose&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($tagVersion);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    } &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;        io&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;You can update the production docker-compose.yml file by running the following command:&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;        io&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;castor production:docker:update-docker-compose &#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; .&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $tagVersion);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    if&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; ($push &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;||&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; ===&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $push &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;&amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; io&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;confirm&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Do you want to push the images to the registry (you may want to play with the images before pushing)?&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;))) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;        push&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($tagVersion);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    } &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;        io&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;You can push the images to the registry by running the following command:&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;        io&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;castor production:docker:push&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;    io&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;success&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Great job! Everything is now done.&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;#[AsTask(description: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Push production docker images&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, namespace: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;production:docker&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; push&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;?string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $tag &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; null&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; void&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;    io&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Pushing image to the registry...&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    foreach&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; ([&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt;array_keys&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;getImagesToBuild&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()), &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;IMAGES_TO_TAG&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;] &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $image) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;        io&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(\&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt;sprintf&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Pushing %s image...&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, $image));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        if&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; ($tag) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;            run&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;docker&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;image&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;push&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;--disable-content-trust&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;REGISTRY&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; .&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $image &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;:&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; .&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $tag]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        } &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;            run&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;docker&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;image&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;push&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;--disable-content-trust&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;--all-tags&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;REGISTRY&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; .&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $image]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;    io&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;success&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(\&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt;sprintf&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Production images have been pushed to the registry %s&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;REGISTRY&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; getImagesToBuild&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; array&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    $bakeConfigOutput &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; capture&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;([&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;        &#039;docker&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;buildx&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;bake&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;        &#039;--file&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;BAKE_FILE&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;        &#039;--print&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    ]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    $bakeConfig &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt; json_decode&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($bakeConfigOutput, &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    if&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; ===&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $bakeConfig) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        throw&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; new&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; ProblemException&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Failed to parse bake.hcl output as JSON.&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    if&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt;is_array&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($bakeConfig) &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;||&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; !&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt;isset&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($bakeConfig[&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;target&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;]) &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;||&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; !&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt;is_array&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($bakeConfig[&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;target&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;])) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        throw&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; new&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; ProblemException&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Invalid bake.hcl structure or missing targets.&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    return&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $bakeConfig[&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;target&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;c-alert c-alert--note&quot;&gt;
    &lt;p class=&quot;c-alert__title&quot;&gt;
                    &lt;span class=&quot;c-icon c-icon--monospace&quot;&gt;
                &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; aria-hidden=&quot;true&quot; class=&quot;c-icon__svg&quot; focusable=&quot;false&quot; viewBox=&quot;0 0 70 71&quot;&gt;&lt;path fill-rule=&quot;nonzero&quot; d=&quot;M35 .9c19.3 0 35 15.7 35 35s-15.7 35-35 35-35-15.7-35-35S15.7.9 35 .9m0 5c-16.552 0-30 13.449-30 30s13.448 30 30 30c16.552.103 30-13.448 30-30 0-16.551-13.448-30-30-30m0 24.9c1.7 0 3 1.3 3 3v15.3c0 1.7-1.3 3-3 3s-3-1.3-3-3V33.8c0-1.7 1.3-3 3-3m0-11c.8 0 1.6.3 2.3.9.6.5.9 1.3.9 2.1 0 .2-.1.4-.1.6-.1.2-.1.4-.2.6s-.2.3-.3.5-.3.4-.4.5c-1.1 1.1-3.1 1.1-4.2 0-.2-.2-.3-.3-.4-.5s-.2-.3-.3-.5-.2-.4-.2-.6c-.1-.2-.1-.4-.1-.6 0-.8.3-1.6.9-2.1.5-.6 1.3-.9 2.1-.9&quot;/&gt;&lt;/svg&gt;
            &lt;/span&gt;
                        &lt;strong&gt;Info&lt;/strong&gt;
    &lt;/p&gt;
    &lt;div class=&quot;c-alert__content&quot;&gt;
                &lt;p&gt;
Nous recommandons généralement de placer les tâches et fonctions du projets dans des fichiers .php dans le dossier &lt;code&gt;.castor/&lt;/code&gt; à la racine du dépôt.&lt;/p&gt;
        &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Pour résumer, cette task tout-en-un va :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;s&#039;assurer que vous êtes sur la branche main, à jour et sans modification locale ;&lt;/li&gt;
&lt;li&gt;demander la version des tags à créer en prenant par défaut la date du jour ;&lt;/li&gt;
&lt;li&gt;construire la stack Docker de dev si nécessaire (pour avoir les images de base utilisées) ;&lt;/li&gt;
&lt;li&gt;exécuter la commande Bake pour construire/taguer les images PHP ;&lt;/li&gt;
&lt;li&gt;exécuter les commandes Docker pour construire/taguer les images publiques (Postgres et Meilisearch) ;&lt;/li&gt;
&lt;li&gt;demander si vous voulez mettre à jour la version des images dans le docker-compose.yml final (si jamais vous voulez tester l&#039;application en local avant de publier la version) ;&lt;/li&gt;
&lt;li&gt;demander si vous voulez publier les images sur le registry.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Chaque paramètre de cette task (la version à taguer, la confirmation de push ou de mise à jour du docker-compose.yml) est optionnel. S&#039;ils ne sont pas spécifiés, la task les demandera de manière intéractive.&lt;/p&gt;
&lt;p&gt;Il est maintenant très facile de publier une nouvelle version du projet pour n&#039;importe quel intervenant du projet (tant qu&#039;il a accès au registre Docker évidemment). Mais on peut aller encore un peu plus loin pour faciliter, cette fois, l&#039;exploitation de l&#039;infrastructure de production.&lt;/p&gt;
&lt;h2&gt;Pilotage de l&#039;infrastructure&lt;/h2&gt;
&lt;p&gt;Pour démarrer le projet (que ce soit en production, en pré-production ou sur du on-premise), nous avons besoin de 3 choses :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;créer le fichier docker-compose.yml avec tous les services définis ;&lt;/li&gt;
&lt;li&gt;créer un fichier .env pour configurer l&#039;application Symfony ;&lt;/li&gt;
&lt;li&gt;lancer la commande &lt;code&gt;docker compose up -d&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Nous allons tout d&#039;abord créer un nouvel ensemble de task Castor qui vont permettre de tout de piloter l&#039;infrastructure docker :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;&amp;#x3C;?&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;php&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;namespace&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;production&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Castor\Attribute\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;AsTask&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Castor\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Castor\Event\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;BeforeExecuteTaskEvent&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Castor\Exception\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;ProblemException&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Symfony\Component\Process\Exception\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;ProcessFailedException&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Castor\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Castor\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Castor\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Castor\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;load_dot_env&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Castor\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Castor\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;wait_for_docker_container&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;#[AsTask(description: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Start production infrastructure&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; start&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; void&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    try&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;        production_docker_compose&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;up&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;-d&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    } &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;ProcessFailedException&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $e) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        if&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt;preg_match&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;            &#039;/Bind for (.&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;):(?&amp;#x3C;port&gt;&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;\d&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;) failed: port is already allocated/mi&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;            $e&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;getProcess&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;getErrorOutput&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;            $matches&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        )) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;            throw&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; new&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; ProblemException&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(\&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt;sprintf&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;It seems that port %s is already used on your machine. Please free this port and try again.&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, $matches[&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;port&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;]), &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;previous&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: $e);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        throw&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $e;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;    io&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;success&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Production infrastructure has been started. It should be available in a few seconds.&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    $url &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;https://&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; .&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $_ENV[&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;SERVER_NAME&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;    wait_for_docker_container&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;arsol-&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; .&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $_ENV[&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;ARSOL_INSTANCE&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;] &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;-frontend-1&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, timeout: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, intervalMs: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;    io&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;success&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(\&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt;sprintf&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Production infrastructure is now ready at %s. Enjoy!&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, $url));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;#[AsTask(description: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Stop production infrastructure&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; stop&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; void&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;    io&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Stopping production infrastructure&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;    production_docker_compose&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;stop&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;    io&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;success&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Production infrastructure has been stopped.&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; production_docker_compose&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $arguments)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; void&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;    load_dot_env&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;workingDirectory &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;/.env&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;    run&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;docker&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;compose&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;-f&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;docker-compose.yml&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;-p&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;arsol-&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; .&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $_ENV[&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;ARSOL_INSTANCE&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;], &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;$arguments]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;c-alert c-alert--note&quot;&gt;
    &lt;p class=&quot;c-alert__title&quot;&gt;
                    &lt;span class=&quot;c-icon c-icon--monospace&quot;&gt;
                &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; aria-hidden=&quot;true&quot; class=&quot;c-icon__svg&quot; focusable=&quot;false&quot; viewBox=&quot;0 0 70 71&quot;&gt;&lt;path fill-rule=&quot;nonzero&quot; d=&quot;M35 .9c19.3 0 35 15.7 35 35s-15.7 35-35 35-35-15.7-35-35S15.7.9 35 .9m0 5c-16.552 0-30 13.449-30 30s13.448 30 30 30c16.552.103 30-13.448 30-30 0-16.551-13.448-30-30-30m0 24.9c1.7 0 3 1.3 3 3v15.3c0 1.7-1.3 3-3 3s-3-1.3-3-3V33.8c0-1.7 1.3-3 3-3m0-11c.8 0 1.6.3 2.3.9.6.5.9 1.3.9 2.1 0 .2-.1.4-.1.6-.1.2-.1.4-.2.6s-.2.3-.3.5-.3.4-.4.5c-1.1 1.1-3.1 1.1-4.2 0-.2-.2-.3-.3-.4-.5s-.2-.3-.3-.5-.2-.4-.2-.6c-.1-.2-.1-.4-.1-.6 0-.8.3-1.6.9-2.1.5-.6 1.3-.9 2.1-.9&quot;/&gt;&lt;/svg&gt;
            &lt;/span&gt;
                        &lt;strong&gt;Info&lt;/strong&gt;
    &lt;/p&gt;
    &lt;div class=&quot;c-alert__content&quot;&gt;
                &lt;p&gt;
Cette fois, nous plaçons les tasks et fonctions destinées à être utilisées en production dans un dossier &lt;code&gt;tools/production/castor.php&lt;/code&gt;. Nous expliquerons l&#039;intérêt de séparer ces tasks dans un dossier à part dans le chapitre suivant.&lt;/p&gt;
        &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Avec ces deux tasks, on peut maintenant lancer &lt;code&gt;castor production:start&lt;/code&gt; et &lt;code&gt;castor production:stop&lt;/code&gt;. Mais pour une meilleure DX, on va également créer automatiquement le fichier &lt;code&gt;.env&lt;/code&gt; de base ainsi que le fichier &lt;code&gt;docker-compose.yml&lt;/code&gt; s&#039;ils n&#039;existent pas encore. Cela se fait facilement avec le système de &lt;em&gt;Listener&lt;/em&gt; intégré dans Castor :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Castor\Attribute\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;AsListener&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Castor\Event\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;BeforeExecuteTaskEvent&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Castor\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Castor\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Castor\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; DOT_ENV_GENERABLES&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;    &#039;APP_SECRET&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;    &#039;POSTGRES_PASSWORD&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;    &#039;MEILI_MASTER_KEY&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;#[AsListener(&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;BeforeExecuteTaskEvent&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;::class&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; configure&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;BeforeExecuteTaskEvent&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $event)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; void&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    $context &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; context&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    $taskName &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;) $event&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    if&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;str_starts_with&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($taskName, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;production:&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        ||&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; str_starts_with&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($taskName, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;production:docker:&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        ||&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;production:repack&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; ===&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $taskName) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        return&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    if&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;exists&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($context&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;workingDirectory &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;/docker-compose.yml&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;        io&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;No docker-compose.yml file found in the current directory. Creating one from the default template.&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;        fs&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;copy&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;__DIR__&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; .&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;/docker-compose.yml&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, $context&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;workingDirectory &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;/docker-compose.yml&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    if&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;exists&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($context&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;workingDirectory &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;/.env&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;        io&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;No .env file found in the current directory. Creating one from the default template.&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;        fs&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;copy&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;__DIR__&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; .&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;/.env.production&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, $context&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;workingDirectory &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;/.env&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    foreach&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;DOT_ENV_GENERABLES&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; as&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $envVar) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        $dotEnvContent &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt; file_get_contents&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($context&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;workingDirectory &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;/.env&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        if&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt;preg_match&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;/^&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; .&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt; preg_quote&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($envVar, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;/&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;) &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;=.+$/m&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, $dotEnvContent)) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;            continue&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        $value &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt; bin2hex&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;random_bytes&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        $dotEnvContent &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt; preg_replace&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;/^&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; .&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt; preg_quote&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($envVar, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;/&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;) &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;=.*/m&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, $envVar &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;=&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; .&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $value, $dotEnvContent);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-9&quot;&gt;        file_put_contents&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($context&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;workingDirectory &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;/.env&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, $dotEnvContent);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nous avons également ajouté quelques tasks supplémentaires pour afficher les logs du projet Docker compose, pour afficher l&#039;état des conteneurs ou encore pour détruire complètement toute trace du projet (pratique quand on veut juste tester la stack).&lt;/p&gt;
&lt;h2&gt;Création d&#039;un exécutable&lt;/h2&gt;
&lt;p&gt;Castor propose une &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://castor.jolicode.com/docs/going-further/extending-castor/repack/&quot;&gt;fonctionnalité de repack&lt;/a&gt; qui permet de packager un projet Castor avec ses tasks dans un Phar autonome, ce qui permet de le partager et l&#039;utiliser sans avoir besoin d&#039;avoir le code du projet, ni même d&#039;avoir Castor installé. C&#039;est parfait pour notre environnement de production / On-Premise car hormis PHP, nous avons rien à installer à l&#039;avance pour pouvoir démarrer l&#039;application.&lt;/p&gt;
&lt;p&gt;Dans le dossier &lt;code&gt;tools/production/&lt;/code&gt;, nous allons donc placer plusieurs fichiers qui seront inclus dans le phar :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;un fichier .env.production avec les vars d&#039;env à définir obligatoirement ;&lt;/li&gt;
&lt;li&gt;le fichier docker-compose.yml vu dans le précédent article ;&lt;/li&gt;
&lt;li&gt;le fichier castor.php du chapitre précédent qui contient les tasks pour piloter la stack.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Nous allons donc maintenant pouvoir repacker le projet Castor situé dans ce dossier &lt;code&gt;tools/production/&lt;/code&gt;. La commande à lancer en dev pour repacker notre projet sera évidemment encapsulée dans une task Castor (donc placée dans le dossier habituel &lt;code&gt;.castor/&lt;/code&gt;) :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; PRODUCTION_DIRECTORY&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; __DIR__&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; .&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;/../tools/production/&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;#[AsTask(description: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Repack production application in a new phar&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; repack&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; void&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;    io&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Repacking production application.&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;    // Castorception \o/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;    run&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;castor repack --app-name=arsol --no-logo&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, context: &lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;withWorkingDirectory&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;PRODUCTION_DIRECTORY&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;    fs&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;mkdir&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;PRODUCTION_DIRECTORY&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; .&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;/build&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;    fs&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;finder&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;PRODUCTION_DIRECTORY&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; .&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;/build&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;ignoreDotFiles&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;    fs&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;rename&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;PRODUCTION_DIRECTORY&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; .&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;/arsol.linux.phar&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;PRODUCTION_DIRECTORY&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; .&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;/build/arsol.phar&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Il ne reste qu&#039;à installer le fichier &lt;code&gt;tools/production/build/arsol.phar&lt;/code&gt; sur le serveur, par exemple dans &lt;code&gt;/usr/bin/local/arsol&lt;/code&gt;. On peut dès maintenant lancer la commande &lt;code&gt;arsol production:start&lt;/code&gt; (notez le nom de l&#039;exécutable 😎) pour initialiser et démarrer complètement l&#039;infrastructure.&lt;/p&gt;

&lt;div class=&quot;c-alert c-alert--note&quot;&gt;
    &lt;p class=&quot;c-alert__title&quot;&gt;
                    &lt;span class=&quot;c-icon c-icon--monospace&quot;&gt;
                &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; aria-hidden=&quot;true&quot; class=&quot;c-icon__svg&quot; focusable=&quot;false&quot; viewBox=&quot;0 0 70 71&quot;&gt;&lt;path fill-rule=&quot;nonzero&quot; d=&quot;M35 .9c19.3 0 35 15.7 35 35s-15.7 35-35 35-35-15.7-35-35S15.7.9 35 .9m0 5c-16.552 0-30 13.449-30 30s13.448 30 30 30c16.552.103 30-13.448 30-30 0-16.551-13.448-30-30-30m0 24.9c1.7 0 3 1.3 3 3v15.3c0 1.7-1.3 3-3 3s-3-1.3-3-3V33.8c0-1.7 1.3-3 3-3m0-11c.8 0 1.6.3 2.3.9.6.5.9 1.3.9 2.1 0 .2-.1.4-.1.6-.1.2-.1.4-.2.6s-.2.3-.3.5-.3.4-.4.5c-1.1 1.1-3.1 1.1-4.2 0-.2-.2-.3-.3-.4-.5s-.2-.3-.3-.5-.2-.4-.2-.6c-.1-.2-.1-.4-.1-.6 0-.8.3-1.6.9-2.1.5-.6 1.3-.9 2.1-.9&quot;/&gt;&lt;/svg&gt;
            &lt;/span&gt;
                        &lt;strong&gt;Info&lt;/strong&gt;
    &lt;/p&gt;
    &lt;div class=&quot;c-alert__content&quot;&gt;
                &lt;p&gt;
Si nous le voulions, nous pourrions également compiler ce phar dans un binaire statique contenant PHP, ce qui aurait éliminé le besoin d&#039;avoir PHP installé sur le serveur pour pouvoir utiliser le projet repacké. N&#039;hésitez pas à jeter un œil à la &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://castor.jolicode.com/docs/going-further/extending-castor/compile/&quot;&gt;documentation Castor qui explique comment compiler son projet&lt;/a&gt;.&lt;/p&gt;
        &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;&lt;picture class=&quot;js-dialog-target&quot; data-original-url=&quot;/media/original/2026/on-premise-docker-castor/arsol-production-phar.png&quot; data-original-width=&quot;1242&quot; data-original-height=&quot;559&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;/media/cache/content-webp/2026/on-premise-docker-castor/arsol-production-phar.d2bfb0f1.webp&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;/media/cache/content/2026/on-premise-docker-castor/arsol-production-phar.png&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;width: 996px; ; aspect-ratio: calc(1242 / 559)&quot; src=&quot;https://jolicode.com//media/cache/content/2026/on-premise-docker-castor/arsol-production-phar.png&quot; alt=&quot;Les tasks disponibles dans l&#039;exécutable arsol&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;h2&gt;Automatiser le build depuis Gitlab&lt;/h2&gt;
&lt;p&gt;Dernière étape pour simplifier la vie du client, nous allons automatiser plusieurs choses directement depuis Gitlab :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Le build des images Docker de prod et le push sur le registre Docker ;&lt;/li&gt;
&lt;li&gt;Le repack du projet tools/production dans un phar autonome ;&lt;/li&gt;
&lt;li&gt;Ajout de ce phar dans les artefacts associés du job pour le rendre facilement accessible.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Voici un extrait du fichier &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; qui permet de faire cela :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;stages&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;  # ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;  - &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;release&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;variables&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;  CASTOR_CONTEXT&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;ci&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;  CASTOR_WORKING_DIR&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;/tmp/castor/${CI_PIPELINE_ID}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;# ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;release&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;  stage&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;release&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;  script&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    - &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;export TAG=$(date +%Y.%m.%d)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    - &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;export RELEASE_DIRECTORY=/tmp/castor-release/${TAG}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    - &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;rm -rf ${RELEASE_DIRECTORY}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    - &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;git clone . ${RELEASE_DIRECTORY}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    - &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;cd $RELEASE_DIRECTORY&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    - &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;echo &quot;$CI_REGISTRY_PASSWORD&quot; | docker login $CI_REGISTRY -u $CI_REGISTRY_USER --password-stdin&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    - &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;castor production:docker:build --push --update-docker-compose &quot;$TAG&quot; --force&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    - &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;castor production:repack&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;    # On déplace le phar à la racine, pour qu&#039;il soit exposé en tant qu&#039;artefact du job&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    - &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;cp tools/production/build/arsol.phar &quot;$CI_PROJECT_DIR/&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;    # On est sur un self-hosted runner donc on fait le ménage avant de terminer&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    - &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;castor docker:destroy --force&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    - &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;rm -rf $RELEASE_DIRECTORY&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;  # Cet artefact sera associé au job, et donc facilement récupérable depuis GitLab&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;  artifacts&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    paths&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;      - &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;arsol.phar&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    expire_in&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;1 week&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;  # Ce job est à lancer manuellement et n&#039;est disponible que sur la branche main&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;  when&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;manual&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;  allow_failure&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;  rules&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    - &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;$CI_COMMIT_BRANCH == &quot;main&quot;&#039;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;      when&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;manual&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    - &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;when&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;never&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Avant de finir cet article, je voudrais revenir sur un point vu précédemment. Vous vous souvenez de la variable &lt;code&gt;PROJECT_NAME&lt;/code&gt; qui était employée pour préfixer le nom de l&#039;image de la stack de dev ? C&#039;était nécessaire justement quand la stack de prod est construite dans la CI GitLab.&lt;/p&gt;
&lt;p&gt;En effet, comme nous utilisons un runner self-hosté, les jobs ne sont pas isolés et tournent sur la même machine en parallèle. Pour ne pas avoir de conflits dans les noms et avoir une stack Docker indépendante entre chaque build de la CI, nous avons donc besoin que les projets Docker compose utilisent un nom unique pour chaque build. Notre template docker-starter est parfaitement compatible avec ce use-case et expose une variable dans le contexte qui permet d&#039;adapter le nom du projet Docker compose à notre guise :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// castor.php à la racine du projet&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; create_default_variables&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; array&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    return&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;        // Nom du projet docker compose par défaut en local&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;        &#039;project_name&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;arsol&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;        // ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    ];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// .castor/context.php&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;#[AsContext(name: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;ci&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; create_ci_context&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; Context&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    $pipelineId &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $_SERVER[&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;CI_PIPELINE_ID&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;] &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;??&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; throw&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; new&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; \RuntimeException&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;CI_PIPELINE_ID is not set. This context should only be used in a CI environment.&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    $c &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; create_test_context&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    return&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $c&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        -&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;withData&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;([&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;            &#039;project_name&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;arsol-ci-&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; .&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $pipelineId,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;            &#039;docker_compose_files&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;                &#039;docker-compose.yml&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;                &#039;docker-compose.ci.yml&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;            ],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        ], recursive: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    ;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Comme nous forçons le context &lt;code&gt;CASTOR_CONTEXT=ci&lt;/code&gt; dans GitLab, toutes les stacks construites dans cet environnement sont automatiquement nommées avec un suffixe qui reprend l&#039;id de la pipeline actuelle.&lt;/p&gt;
&lt;p&gt;Au passage, on rajoute un &lt;code&gt;docker-compose.ci.yml&lt;/code&gt; au projet qui va rajouter quelques configurations spécifiques à la CI (comme des variables d&#039;environnement ou un routeur adaptés)&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Pour conclure, grâce au template docker-starter et à Castor, nous avons pu mettre en place un déploiement d&#039;&lt;em&gt;ArSol&lt;/em&gt; qui convient à la fois pour la production et le On-Premise mais qui reste simple à l&#039;usage :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Les images Docker pré-construites garantissent des déploiements fiables et déterministes ;&lt;/li&gt;
&lt;li&gt;Les images sont optimisées pour ne contenir que ce qui est nécessaire, sans les outils de build ;&lt;/li&gt;
&lt;li&gt;Tout passe par Castor, la complexité Docker/CI est masquée ;&lt;/li&gt;
&lt;li&gt;Le &lt;em&gt;repack&lt;/em&gt; Castor génère un seul exécutable (&lt;code&gt;arsol.phar&lt;/code&gt;) pour tout gérer (démarrer, arrêter, mettre à jour) sur site, même sans être un expert système.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;L&#039;automatisation complète via Castor et GitLab CI permet au client d&#039;être autonome pour préparer et déployer une mise à jour pour tous les environnements. Une preuve que Docker et Castor sont la bonne formule pour transformer des contraintes de terrain en solutions d&#039;infrastructure efficaces et élégantes.&lt;/p&gt;

        </content>
    </entry>    <entry>
        <id>https://jolicode.com/blog/deploiement-on-premise-partie-1-le-socle-docker</id>
        <published>2026-03-25T10:41:00+01:00</published>
        <updated>2026-03-25T10:41:00+01:00</updated>
        <link type="text/html" rel="alternate" href="https://jolicode.com/blog/deploiement-on-premise-partie-1-le-socle-docker"/>
        <title>Déploiement On-Premise - Partie 1 - Le socle Docker</title>
        <author>
            <name>JoliCode Team</name>
            <uri>https://jolicode.com/</uri>
        </author>            <category term="devops" />            <category term="docker" />            <category term="castor" />        <summary><![CDATA[Dans cet article, nous vous expliquons notre approche de déploiement hybride pour une application Symfony conteneurisée avec Docker. Ce système permet un déploiement à la fois sur des serveurs connectés…]]></summary>
        <content type="html">
            &lt;p&gt;Dans cet article, nous vous expliquons notre approche de déploiement hybride pour une application Symfony conteneurisée avec Docker. Ce système permet un déploiement à la fois sur des serveurs connectés à Internet et en mode local (on-premise) pour les zones de travail sans connectivité réseau.&lt;/p&gt;
&lt;h2&gt;Le contexte&lt;/h2&gt;
&lt;p&gt;Nous avons récemment entrepris la refonte complète de l&#039;application &lt;em&gt;ArSol&lt;/em&gt; pour l&#039;équipe archéologique de l&#039;université de Tours. Le logiciel original, une application desktop, était obsolète. Nous l&#039;avons entièrement modernisé en développant une application web sur mesure... avec une interface utilisateur considérablement rajeunie de plusieurs décennies propulsée avec Symfony 7.4, PHP 8.4 et FrankenPHP.&lt;/p&gt;
&lt;p&gt;Une des particularités du projet, c&#039;est que l&#039;application peut être utilisée sur des lieux de fouilles archéologiques ne disposant pas de réseau. Pour plusieurs raisons, l&#039;idée d&#039;une &lt;abbr title=&quot;Progressive Web App&quot;&gt;PWA&lt;/abbr&gt; a été écartée assez rapidement. Nous avons plutôt opté pour un mode de déploiement On-Premise : chaque site de fouille pourra ainsi faire tourner l&#039;infrastructure complète (serveur web, bases de données et application Symfony) sur une machine locale. Les contraintes métiers nous ont permis de développer, sans trop de complexité, un système de verrouillage partiel de l&#039;application (pour éviter tout conflit) et une synchronisation des données entre le &lt;abbr title=&quot;Software as a Service&quot;&gt;SaaS&lt;/abbr&gt; (c&#039;est-à-dire la production, le serveur central) et les instances On-Premise.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;/media/cache/content-webp/2026/on-premise-docker-castor/arsol-schema-onpremise.b3de4a68.webp&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;/media/cache/content/2026/on-premise-docker-castor/arsol-schema-onpremise.png&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;width: 777px; ; aspect-ratio: calc(777 / 757)&quot; src=&quot;https://jolicode.com//media/cache/content/2026/on-premise-docker-castor/arsol-schema-onpremise.png&quot; alt=&quot;Le schéma des différentes instances&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Je vais vous montrer aujourd&#039;hui comment nous avons mis en place ce déploiement On-Premise (que j&#039;abrégerai &lt;abbr title=&quot;On-Premise&quot;&gt;OP&lt;/abbr&gt; dans la suite de cet article). D&#039;abord, nous verrons comment se présente la stack Docker, puis comment nous avons automatisé la création des images et le déploiement.&lt;/p&gt;
&lt;h2&gt;La base Docker&lt;/h2&gt;
&lt;p&gt;Pour ce projet, nous avons choisi de déployer l&#039;application via des images Docker, aussi bien pour la production que pour les instances OP. Ainsi, ce sont les mêmes images Docker qui seront utilisées dans le mode SaaS et dans le mode OP. L’activation des différentes options et feature flags repose exclusivement sur des variables d’environnement.&lt;/p&gt;
&lt;h3&gt;La stack de dev&lt;/h3&gt;
&lt;p&gt;Sur tous nos projets, nous utilisons &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://github.com/jolicode/docker-starter&quot;&gt;docker-starter&lt;/a&gt; et &lt;em&gt;ArSol&lt;/em&gt; n&#039;y a pas fait exception. Ce squelette fournit une stack Docker complète, avec tout ce qu&#039;il faut dedans pour que chaque intervenant du projet puisse le faire tourner en local facilement.&lt;/p&gt;
&lt;p&gt;Docker-starter s&#039;est amélioré au fil des années pour offrir une excellente &lt;abbr title=&quot;Developer eXperience&quot;&gt;DX&lt;/abbr&gt; à tous nos développeurs, qu&#039;ils soient développeurs PHP ou intégrateurs, qu&#039;ils soient à l&#039;aise avec le fonctionnement de Docker ou pas du tout.&lt;/p&gt;
&lt;p&gt;Pour cela, toute la stack est pilotée par des tasks Castor 🦫 :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;castor start&lt;/code&gt; : construit les images si nécessaire, les démarre, installe les dépendances Composer/Yarn/npm, build les assets front, etc. Bref, cette seule commande suffit pour rendre le projet complètement fonctionnel en local, que ce soit au premier lancement ou aux lancements suivants ;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;castor migrate&lt;/code&gt; : joue les migrations Doctrine ;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;castor stop&lt;/code&gt; : stoppe toute la stack ;&lt;/li&gt;
&lt;li&gt;et plein d&#039;autres tasks pour les power users ou pour ceux qui veulent lancer une tâche particulière, par exemple ré-installer les dépendances, exécuter les tests, etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Cette infrastructure dockerisée est parfaite pour le développement&lt;/strong&gt; :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Les tâches castor fournies couvrent une bonne partie des besoins du quotidien ;&lt;/li&gt;
&lt;li&gt;Le code du projet n&#039;est pas copié/collé dans les conteneurs, mais &amp;quot;monté&amp;quot; dans des volumes pour chaque conteneur. Ainsi, les modifications dans le code sont directement disponibles dans les conteneurs, pas de build/restart à faire à chaque modification.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Comment passer en production ?&lt;/h3&gt;
&lt;p&gt;Si Docker-starter est parfait pour l&#039;environnement de développement, il ne doit toutefois pas être utilisé tel quel en production.&lt;/p&gt;
&lt;p&gt;En production, la priorité est la fiabilité, la sécurité, la rapidité de déploiement et la reproductibilité de l&#039;environnement. Contrairement à l&#039;environnement de développement où le montage de volume permet l&#039;itération rapide du code, en production, on cherche à minimiser les étapes au moment du déploiement.&lt;/p&gt;
&lt;p&gt;Avoir des images pré-construites garantit que l&#039;image qui a été testée et validée contient exactement le code, les dépendances (Composer, Yarn/npm) et les assets frontend nécessaires et compilées. Cela élimine le besoin d&#039;exécuter des commandes de &lt;em&gt;build&lt;/em&gt; ou d&#039;installation au moment du lancement des conteneurs (sur les serveurs de production ou sur les instances On-Premise). Cela réduit ainsi le risque d&#039;erreurs dues à des dépendances externes ou des configurations de l&#039;environnement hôte. C&#039;est la garantie d&#039;un déploiement &amp;quot;figé&amp;quot; et déterministe.
D&#039;ailleurs, comme les assets sont buildées en amont, nous n&#039;avons pas besoin de NodeJS dans le conteneur final, ce qui permet également d&#039;avoir des images plus légères in-fine.&lt;/p&gt;
&lt;p&gt;L&#039;objectif est d&#039;avoir des images Docker prêtes pour la production, mais aussi utilisables pour la pré-production et le déploiement on-premise.&lt;/p&gt;
&lt;h3&gt;L&#039;important, c&#039;est la santé&lt;/h3&gt;
&lt;p&gt;Dans l&#039;idéal, nous souhaitons également que chaque service définisse son propre &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://docs.docker.com/reference/dockerfile/#healthcheck&quot;&gt;healthcheck&lt;/a&gt;. Un healthcheck est une commande que Docker pourra exécuter pour déterminer si le conteneur est toujours en bon état. Si ce n&#039;est pas le cas, Docker tentera de redémarrer le conteneur. Voilà un exemple de healthcheck permettant de vérifier si un serveur MySQL est toujours opérationnel dans un conteneur :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;mysqladmin&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; ping&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; -h&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; localhost&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Il se configure de cette manière dans un docker-compose.yml classique:&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;  mysql&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    image&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;mysql&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    restart&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;unless-stopped&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    healthcheck&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;      test&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: [&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;CMD&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;mysqladmin&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; ,&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;ping&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;-h&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;localhost&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;      timeout&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;20s&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;      retries&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;10&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;En général, je préfère que les images &amp;quot;applicatives&amp;quot; déclarent elles-mêmes leur healthcheck plutôt que de laisser l&#039;utilisateur le définir lui-même dans son docker-compose.yml. Cela offre, selon moi, deux avantages :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;l&#039;image garde la responsabilité de tout configurer comme il faut : l&#039;utilisateur n&#039;a pas besoin de savoir ce qui tourne dans l&#039;image, comment vérifier que tout fonctionne (faut-il utiliser wget, curl ou un autre outil pour avoir l&#039;état du service), etc ;&lt;/li&gt;
&lt;li&gt;le docker-compose.yml reste le plus simple possible pour l&#039;utilisateur.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Cela se fait grâce à l&#039;instruction &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://docs.docker.com/reference/dockerfile/#healthcheck&quot;&gt;&lt;code&gt;HEALTHCHECK&lt;/code&gt;&lt;/a&gt; directement dans le dockerfile de l&#039;image en question. Voici un exemple d&#039;un healthcheck qu&#039;on pourrait définir dans le Dockerfile pour un conteneur faisant tourner un serveur web :&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;HEALTHCHECK --interval=5m --timeout=3s CMD curl -f http://localhost/ || exit 1
&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;c-alert c-alert--note&quot;&gt;
    &lt;p class=&quot;c-alert__title&quot;&gt;
                    &lt;span class=&quot;c-icon c-icon--monospace&quot;&gt;
                &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; aria-hidden=&quot;true&quot; class=&quot;c-icon__svg&quot; focusable=&quot;false&quot; viewBox=&quot;0 0 70 71&quot;&gt;&lt;path fill-rule=&quot;nonzero&quot; d=&quot;M35 .9c19.3 0 35 15.7 35 35s-15.7 35-35 35-35-15.7-35-35S15.7.9 35 .9m0 5c-16.552 0-30 13.449-30 30s13.448 30 30 30c16.552.103 30-13.448 30-30 0-16.551-13.448-30-30-30m0 24.9c1.7 0 3 1.3 3 3v15.3c0 1.7-1.3 3-3 3s-3-1.3-3-3V33.8c0-1.7 1.3-3 3-3m0-11c.8 0 1.6.3 2.3.9.6.5.9 1.3.9 2.1 0 .2-.1.4-.1.6-.1.2-.1.4-.2.6s-.2.3-.3.5-.3.4-.4.5c-1.1 1.1-3.1 1.1-4.2 0-.2-.2-.3-.3-.4-.5s-.2-.3-.3-.5-.2-.4-.2-.6c-.1-.2-.1-.4-.1-.6 0-.8.3-1.6.9-2.1.5-.6 1.3-.9 2.1-.9&quot;/&gt;&lt;/svg&gt;
            &lt;/span&gt;
                        &lt;strong&gt;Info&lt;/strong&gt;
    &lt;/p&gt;
    &lt;div class=&quot;c-alert__content&quot;&gt;
                &lt;p&gt;
Certaines images publiques ne fournissent volontairement pas de healthcheck. Il faudra donc le configurer nous-même, soit dans une image à vous, soit dans le docker-compose.yml.&lt;/p&gt;
        &lt;/div&gt;
&lt;/div&gt;

&lt;h3&gt;Publication et utilisation des images finales&lt;/h3&gt;
&lt;p&gt;Une fois les images construites, il faut les publier sur un registre Docker pour les mettre à disposition des différents environnements cibles (que ce soit votre serveur de production, dans un Kubernetes, etc). Docker fournit un registre par défaut qui s&#039;appelle Docker Hub. C&#039;est sur ce service que Docker va chercher les images qu&#039;il ne connaît pas encore localement (par exemple depuis un &lt;code&gt;FROM xxx&lt;/code&gt; dans un Dockerfile ou depuis un docker-compose.yml). La plupart des systèmes de gestion de code comme GitHub ou GitLab fournissent également un registre de conteneur pour stocker de manière privée vos images Docker. Ces images, dans le cadre d&#039;&lt;em&gt;ArSol&lt;/em&gt;, sont stockées dans le registre GitLab du client.&lt;/p&gt;
&lt;p&gt;Une fois que tout est prêt, nous obtenons le docker-compose.yml suivant (simplifié dans le cadre de cet article) :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;volumes&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;  postgres-data&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: {}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;  caddy_data&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: {}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;  caddy_config&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: {}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;  frontend&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    image&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;&amp;#x3C;url du registre&gt;/arsol/frontend:2025.10.23&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    restart&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;unless-stopped&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    env_file&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;.env&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    depends_on&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;      postgres&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        condition&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;service_healthy&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;      meilisearch&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        condition&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;service_healthy&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    ports&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;      - &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;80:80&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;      - &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;443:443&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;      - &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;443:443/udp&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    volumes&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;      - &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;caddy_data:/data&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;      - &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;caddy_config:/config&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;  postgres&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    image&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;&amp;#x3C;url du registre&gt;/arsol/postgres:2025.10.23&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    restart&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;unless-stopped&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    env_file&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;.env&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    volumes&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;      - &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;postgres-data:/var/lib/postgresql/data&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    healthcheck&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;      test&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: [&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;CMD-SHELL&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;pg_isready -U app&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;      interval&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;5s&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;      timeout&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;5s&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;      retries&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;5&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;  worker-messenger&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    image&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;&amp;#x3C;url du registre&gt;/arsol/worker-messenger:2025.10.23&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    restart&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;unless-stopped&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    env_file&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;.env&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    depends_on&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;      frontend&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;          condition&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;service_healthy&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;      postgres&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        condition&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;service_healthy&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;c-alert c-alert--note&quot;&gt;
    &lt;p class=&quot;c-alert__title&quot;&gt;
                    &lt;span class=&quot;c-icon c-icon--monospace&quot;&gt;
                &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; aria-hidden=&quot;true&quot; class=&quot;c-icon__svg&quot; focusable=&quot;false&quot; viewBox=&quot;0 0 70 71&quot;&gt;&lt;path fill-rule=&quot;nonzero&quot; d=&quot;M35 .9c19.3 0 35 15.7 35 35s-15.7 35-35 35-35-15.7-35-35S15.7.9 35 .9m0 5c-16.552 0-30 13.449-30 30s13.448 30 30 30c16.552.103 30-13.448 30-30 0-16.551-13.448-30-30-30m0 24.9c1.7 0 3 1.3 3 3v15.3c0 1.7-1.3 3-3 3s-3-1.3-3-3V33.8c0-1.7 1.3-3 3-3m0-11c.8 0 1.6.3 2.3.9.6.5.9 1.3.9 2.1 0 .2-.1.4-.1.6-.1.2-.1.4-.2.6s-.2.3-.3.5-.3.4-.4.5c-1.1 1.1-3.1 1.1-4.2 0-.2-.2-.3-.3-.4-.5s-.2-.3-.3-.5-.2-.4-.2-.6c-.1-.2-.1-.4-.1-.6 0-.8.3-1.6.9-2.1.5-.6 1.3-.9 2.1-.9&quot;/&gt;&lt;/svg&gt;
            &lt;/span&gt;
                        &lt;strong&gt;Info&lt;/strong&gt;
    &lt;/p&gt;
    &lt;div class=&quot;c-alert__content&quot;&gt;
                &lt;p&gt;
Les volumes pour caddy sont importants pour permettre la génération automatique des certificats SSL.&lt;/p&gt;
        &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Vous noterez que nous avons spécifié, pour chaque image, un tag basé sur une date (en l&#039;occurrence &lt;code&gt;2025.10.23&lt;/code&gt;). En effet, quand Docker a déjà récupéré un tag pour une image donnée, il ne cherchera pas à la mettre à jour à moins de nettoyer les images locales (ou à moins de forcer le &lt;em&gt;pull&lt;/em&gt; avec l&#039;option &lt;code&gt;docker run --pull=always&lt;/code&gt;). Si on utilisait un tag fixe (comme &lt;code&gt;latest&lt;/code&gt; par exemple), les images ne seraient jamais mises à jour sur la machine, quand bien même le tag en question aurait changé sur le registre pour cibler une nouvelle version.&lt;/p&gt;
&lt;p&gt;Une fois le docker-compose.yml configuré, et le fichier &lt;code&gt;.env&lt;/code&gt; créé, nous pouvons lancer &lt;code&gt;docker compose -f docker-compose.yml up -d&lt;/code&gt;, et voilà 🎉 : l&#039;infrastructure de production est opérationnelle en HTTP et HTTPS.&lt;/p&gt;
&lt;p&gt;La seule différence pour mettre en place une instance OP sera le contenu du &lt;code&gt;.env&lt;/code&gt; pour activer/désactiver certaines fonctionnalités spécifiques à ce mode (comme les écrans pour déclencher la synchronisation avec le SaaS, ou la désactivation des données des autres sites archéologiques). Et pour déployer une nouvelle version, il nous faut construire et taguer une nouvelle version de nos images, mettre à jour le docker-compose.yml pour utiliser la bonne version des images et relancer la commande &lt;code&gt;docker compose&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Maintenant que l&#039;objectif est clair, voyons en détails comment y parvenir.&lt;/p&gt;
&lt;h2&gt;Construction des images&lt;/h2&gt;
&lt;p&gt;Ici, nous voulons construire toutes les images Docker nécessaires pour faire tourner le projet. À cette étape, nous devons considérer deux cas, suivant si nous utilisons des images publiques telles quelles ou s&#039;il s&#039;agit de nos propres images spécifiques à &lt;em&gt;ArSol&lt;/em&gt;.&lt;/p&gt;
&lt;h3&gt;Images publiques&lt;/h3&gt;
&lt;p&gt;Dans un premier temps, parlons des images publiques que nous utilisons telles quelles (comme pour Postgres ou Meilisearch). Nous pourrions réutiliser ces images présentes sur le Docker Hub. Mais à la place, nous allons plutôt taguer ces images avec notre système de tag et les pousser sur notre propre registre Docker. Cela apporte plusieurs avantages :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;toutes les images du projet utilisent le même tag (plus simple pour s&#039;y retrouver) ;&lt;/li&gt;
&lt;li&gt;le projet n&#039;est dépendant que d&#039;un seul registre Docker, celui du client.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pour celles-ci, nous allons simplement créer un nouveau tag sur les images en question et les publier sur notre registre Docker privé :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;# Tag de l&#039;image&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;docker&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; image&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; tag&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;le&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; nom&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; de&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; l&#039;image&gt;:&amp;#x3C;la version de l&#039;imag&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; du&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; registr&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;/arsol/&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;le&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; nom&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; de&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; l&#039;image chez nous&gt;:&amp;#x3C;la version de l&#039;applicati&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;# Publication de l&#039;image sur le registre&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;docker&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; image&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; push&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; --disable-content-trust&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; du&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; registr&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;/arsol/&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;le&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; nom&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; de&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; l&#039;image chez nous&gt;:&amp;#x3C;la version de l&#039;applicati&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;c-alert c-alert--note&quot;&gt;
    &lt;p class=&quot;c-alert__title&quot;&gt;
                    &lt;span class=&quot;c-icon c-icon--monospace&quot;&gt;
                &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; aria-hidden=&quot;true&quot; class=&quot;c-icon__svg&quot; focusable=&quot;false&quot; viewBox=&quot;0 0 70 71&quot;&gt;&lt;path fill-rule=&quot;nonzero&quot; d=&quot;M35 .9c19.3 0 35 15.7 35 35s-15.7 35-35 35-35-15.7-35-35S15.7.9 35 .9m0 5c-16.552 0-30 13.449-30 30s13.448 30 30 30c16.552.103 30-13.448 30-30 0-16.551-13.448-30-30-30m0 24.9c1.7 0 3 1.3 3 3v15.3c0 1.7-1.3 3-3 3s-3-1.3-3-3V33.8c0-1.7 1.3-3 3-3m0-11c.8 0 1.6.3 2.3.9.6.5.9 1.3.9 2.1 0 .2-.1.4-.1.6-.1.2-.1.4-.2.6s-.2.3-.3.5-.3.4-.4.5c-1.1 1.1-3.1 1.1-4.2 0-.2-.2-.3-.3-.4-.5s-.2-.3-.3-.5-.2-.4-.2-.6c-.1-.2-.1-.4-.1-.6 0-.8.3-1.6.9-2.1.5-.6 1.3-.9 2.1-.9&quot;/&gt;&lt;/svg&gt;
            &lt;/span&gt;
                        &lt;strong&gt;Info&lt;/strong&gt;
    &lt;/p&gt;
    &lt;div class=&quot;c-alert__content&quot;&gt;
                &lt;p&gt;
Ici, nous n&#039;allons pas utiliser le système de signature des images, donc nous désactivons la validation des images côté registre en utilisant l&#039;option &lt;code&gt;--disable-content-trust&lt;/code&gt;.&lt;/p&gt;
        &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Par exemple, pour meilisearch, cela nous donnerait quelque comme cela pour publier le tag &amp;quot;daté&amp;quot; ainsi que le tag &lt;code&gt;latest&lt;/code&gt; :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;docker&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; image&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; tag&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; getmeili/meilisearch:v1.16&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; du&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; registr&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;:4567/plancq/arsol/meilisearch:2025.10.23&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;docker&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; image&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; tag&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; getmeili/meilisearch:v1.16&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; du&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; registr&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;:4567/plancq/arsol/meilisearch:latest&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;docker&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; image&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; push&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; --disable-content-trust&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; du&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; registr&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;:4567/plancq/arsol/meilisearch:2025.10.23&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;docker&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; image&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; push&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; --disable-content-trust&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; du&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; registr&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;:4567/plancq/arsol/meilisearch:latest&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nous pouvons maintenant utiliser cette image directement dans notre docker-compose.yml.&lt;/p&gt;
&lt;h3&gt;Images applicatives&lt;/h3&gt;
&lt;p&gt;En revanche, pour les images avec PHP (frontend et worker) contenant le code de l&#039;application, les dépendances &amp;amp; cie, c&#039;est un peu plus compliqué comme nous allons le voir.&lt;/p&gt;
&lt;h4&gt;La stack de dev comme base&lt;/h4&gt;
&lt;p&gt;Nous allons nous servir des images de la stack de dev comme base pour nos images de production. En effet, dans Docker-starter, nos images customs suivent déjà plusieurs bonnes pratiques, notamment pour optimiser leur poids :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;les instructions RUN sont regroupées le plus possible pour réduire le nombre de couches de l&#039;image (layer squashing) ;&lt;/li&gt;
&lt;li&gt;un nettoyage immédiat du cache et des fichiers temporaires est fait pour chaque commande afin de diminuer la taille de chaque layer, et donc le poids final de l&#039;image ;&lt;/li&gt;
&lt;li&gt;le build &amp;quot;multi-stage&amp;quot; est utilisé pour ne pas inclure les outils de dev dans l&#039;image finale (composer, nodejs, yarn, etc) mais uniquement dans une image &lt;em&gt;builder&lt;/em&gt; utilisée quand il y a besoin de ces outils.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pour la production, nous allons appliquer la même logique. Nous créerons donc un &lt;code&gt;Dockerfile&lt;/code&gt; unique avec plusieurs stages qui nous donnera les différentes images finales à construire, mais en partant des différentes images de dev pour éviter d&#039;avoir à dupliquer et maintenir une autre installation de PHP dans la bonne version, avec les bonnes extensions, configurer FrankenPHP et caddy, etc.&lt;/p&gt;
&lt;p&gt;Voyons à quoi ressemble ce Dockerfile dans les grandes lignes.&lt;/p&gt;
&lt;h4&gt;Le builder et la préparation de l&#039;application&lt;/h4&gt;
&lt;p&gt;En premier, nous définissons un stage &amp;quot;builder&amp;quot;, qui se base sur notre builder de dev et va faire toutes les étapes nécessaires pour installer le projet (installations composer et yarn, construction des assets, etc) :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;ARG&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; PROJECT_NAME&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; ${PROJECT_NAME}-builder &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;AS&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; production-builder&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; APP_ENV=prod&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; COMPOSER_MIRROR_PATH_REPOS=1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; NODE_ENV=production&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;# C&#039;est dans ce dossier que nous allons travailler&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;WORKDIR&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; /var/www&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;# On récupère les fichiers composer.json et composer.lock et on lance Composer&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; composer.* /var/www/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;RUN&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; composer install \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    --no-dev \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    --prefer-dist \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    --no-scripts \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    --no-interaction \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    &amp;#x26;&amp;#x26; composer clear-cache&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;# Pareil pour Yarn&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; package.json yarn.lock .yarnrc.yml /var/www/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;RUN&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; yarn install --immutable&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;# On récupère le reste des fichiers&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; . /var/www/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;# Et enfin, on lance toutes les tasks nécessaires pour avoir l&#039;application opérationnelle &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;RUN&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; composer dump-autoload --optimize \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    &amp;#x26;&amp;#x26; yarn run build \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    &amp;#x26;&amp;#x26; bin/console cache:warmup \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    &amp;#x26;&amp;#x26; bin/console assets:install public --relative \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    &amp;#x26;&amp;#x26; rm -rf node_modules&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Plusieurs choses sont à noter ici. Déjà, vous remarquez que nous mentionnons un argument de build &lt;code&gt;PROJECT_NAME&lt;/code&gt; pour préfixer le nom de l&#039;image de base. Nous verrons dans le prochain article pourquoi c&#039;est nécessaire. Dites vous dans un premier temps que la variable a comme valeur &lt;code&gt;arsol&lt;/code&gt;, ce qui correspond au nom du projet Docker compose de la stack de dev en local.&lt;/p&gt;

&lt;div class=&quot;c-alert c-alert--note&quot;&gt;
    &lt;p class=&quot;c-alert__title&quot;&gt;
                    &lt;span class=&quot;c-icon c-icon--monospace&quot;&gt;
                &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; aria-hidden=&quot;true&quot; class=&quot;c-icon__svg&quot; focusable=&quot;false&quot; viewBox=&quot;0 0 70 71&quot;&gt;&lt;path fill-rule=&quot;nonzero&quot; d=&quot;M35 .9c19.3 0 35 15.7 35 35s-15.7 35-35 35-35-15.7-35-35S15.7.9 35 .9m0 5c-16.552 0-30 13.449-30 30s13.448 30 30 30c16.552.103 30-13.448 30-30 0-16.551-13.448-30-30-30m0 24.9c1.7 0 3 1.3 3 3v15.3c0 1.7-1.3 3-3 3s-3-1.3-3-3V33.8c0-1.7 1.3-3 3-3m0-11c.8 0 1.6.3 2.3.9.6.5.9 1.3.9 2.1 0 .2-.1.4-.1.6-.1.2-.1.4-.2.6s-.2.3-.3.5-.3.4-.4.5c-1.1 1.1-3.1 1.1-4.2 0-.2-.2-.3-.3-.4-.5s-.2-.3-.3-.5-.2-.4-.2-.6c-.1-.2-.1-.4-.1-.6 0-.8.3-1.6.9-2.1.5-.6 1.3-.9 2.1-.9&quot;/&gt;&lt;/svg&gt;
            &lt;/span&gt;
                        &lt;strong&gt;Info&lt;/strong&gt;
    &lt;/p&gt;
    &lt;div class=&quot;c-alert__content&quot;&gt;
                &lt;p&gt;
J&#039;ai voulu dissocier le Dockerfile de la stack de dev du Dockerfile de la stack de prod, d&#039;où la nécessité de pouvoir référencer l&#039;image de la stack de dev. Si nous avions mergé les deux Dockerfile, nous n&#039;aurions pas eu besoin de cette variable &lt;code&gt;PROJECT_NAME&lt;/code&gt;.&lt;/p&gt;
        &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Ensuite, vous vous demandez peut-être la raison pour laquelle nous effectuons l&#039;installation de Composer et Yarn &lt;strong&gt;avant&lt;/strong&gt; de &lt;code&gt;COPY&lt;/code&gt; le code et l&#039;ensemble des fichiers de l&#039;application. Cette approche permet de tirer parti du cache de couches : Docker commence la construction en vérifiant le cache pour la première instruction et, en cas de correspondance, il réutilise la couche et passe à l&#039;instruction suivante. Mais si une instruction invalide le cache (par exemple, un &lt;code&gt;COPY&lt;/code&gt; avec un fichier modifié), toutes les instructions suivantes seront exécutées sans utiliser le cache, ce qui ralentit la construction. Étant donné que les dépendances sont moins sujettes à changement que le code, il est plus probable que les étapes d&#039;installation de Composer et Yarn soient réutilisées depuis le cache, car elles précèdent l&#039;opération de &lt;code&gt;COPY&lt;/code&gt; du code.&lt;/p&gt;
&lt;p&gt;On peut maintenant préparer le stage docker qui contiendra uniquement php et les outils nécessaires pour le runtime (FrankenPHP, Caddy, etc), en se basant sur l&#039;image du conteneur frontend :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;ARG&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; PROJECT_NAME&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; ${PROJECT_NAME}-frontend &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;AS&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; production-php-base&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;WORKDIR&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; /var/www&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;# Symfony tournera dans son env prod&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; APP_ENV=prod&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;# On récupère le code de l&#039;application, ses dépendances et assets depuis le stage builder vu précédemment&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; --from=production-builder /var/www /var/www&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Il faut bien faire attention à ce qui est inclus dans les conteneurs. C&#039;est pour cette raison que nous allons créer un &lt;code&gt;Dockerfile.gitignore&lt;/code&gt; au même niveau que le &lt;code&gt;Dockerfile&lt;/code&gt; du projet. On retire tout ce qui n&#039;est pas nécessaire, comme les dépendances ou les assets qui auraient été installées/construites dans votre stack de dev :&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.castor/
.castor.stub.php
.env.local
.env.ci
*.cache
.git/
.idea/
.home/
.yarn/
doc/
infrastructure/
!infrastructure/docker/services/php-production/
tests/
tools/
node_modules/
var/
vendor/
public/assets/
public/build/
public/bundles/
public/media/
!public/media/.gitkeep
&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;c-alert c-alert--note&quot;&gt;
    &lt;p class=&quot;c-alert__title&quot;&gt;
                    &lt;span class=&quot;c-icon c-icon--monospace&quot;&gt;
                &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; aria-hidden=&quot;true&quot; class=&quot;c-icon__svg&quot; focusable=&quot;false&quot; viewBox=&quot;0 0 70 71&quot;&gt;&lt;path fill-rule=&quot;nonzero&quot; d=&quot;M35 .9c19.3 0 35 15.7 35 35s-15.7 35-35 35-35-15.7-35-35S15.7.9 35 .9m0 5c-16.552 0-30 13.449-30 30s13.448 30 30 30c16.552.103 30-13.448 30-30 0-16.551-13.448-30-30-30m0 24.9c1.7 0 3 1.3 3 3v15.3c0 1.7-1.3 3-3 3s-3-1.3-3-3V33.8c0-1.7 1.3-3 3-3m0-11c.8 0 1.6.3 2.3.9.6.5.9 1.3.9 2.1 0 .2-.1.4-.1.6-.1.2-.1.4-.2.6s-.2.3-.3.5-.3.4-.4.5c-1.1 1.1-3.1 1.1-4.2 0-.2-.2-.3-.3-.4-.5s-.2-.3-.3-.5-.2-.4-.2-.6c-.1-.2-.1-.4-.1-.6 0-.8.3-1.6.9-2.1.5-.6 1.3-.9 2.1-.9&quot;/&gt;&lt;/svg&gt;
            &lt;/span&gt;
                        &lt;strong&gt;Info&lt;/strong&gt;
    &lt;/p&gt;
    &lt;div class=&quot;c-alert__content&quot;&gt;
                &lt;p&gt;
Après réflexion, on aurait peut-être mieux fait de tout bloquer par défaut et ne lister que ce qu&#039;il fallait garder (src, config, public, etc). 😝&lt;/p&gt;
        &lt;/div&gt;
&lt;/div&gt;

&lt;h4&gt;Frontend et worker&lt;/h4&gt;
&lt;p&gt;Maintenant que nous avons un stage avec l&#039;application opérationnelle, nous allons pouvoir construire les images finales pour notre projet. Voilà l&#039;image qui se base sur le stage construit ci-dessus :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; production-php-base &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;AS&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; production-frontend&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;# Mise en place d&#039;un entrypoint qui s&#039;occupera d&#039;initialiser l&#039;application&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; infrastructure/docker/services/php-production/entrypoint-frontend.sh /entrypoint.sh&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;# Configuration de Caddy&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; infrastructure/docker/services/php-production/etc/. /etc/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;RUN&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; [&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;chmod&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;+x&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;/entrypoint.sh&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;ENTRYPOINT&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; [&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;/entrypoint.sh&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;# Indique les ports utilisés par ce conteneur &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;EXPOSE&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; 80&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;EXPOSE&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; 443&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;# Commande qui sera exécutée dans le conteneur&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;CMD&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; [ &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;frankenphp&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;run&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;--config&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;/etc/caddy/Caddyfile&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;--adapter&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;caddyfile&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nous avons défini un entrypoint pour ce conteneur. Lors du démarrage de ce dernier, il permet d&#039;initialiser toute la partie data (schéma SQL, migrations Doctrine à jouer, configuration du transport pour Messenger, indexation des données dans Meilisearch). Cela est rendu possible car nous n&#039;aurons toujours qu&#039;une seule instance de ce conteneur en production (aucun pic de trafic à gérer). Si ce n&#039;est pas votre cas, il faudra peut-être adapter cette technique.&lt;/p&gt;
&lt;p&gt;Voilà à quoi ressemble cet entrypoint :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;#!/bin/bash&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-9&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; -e&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-9&quot;&gt;echo&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &quot;Updating all databases...&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;php&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; bin/console&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; doctrine:database:create&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; --if-not-exists&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; --env=prod&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; --no-interaction&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;php&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; bin/console&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; doctrine:migrations:sync-metadata-storage&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; --env=prod&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; --no-interaction&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;php&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; bin/console&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; doctrine:migrations:migrate&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; --env=prod&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; --no-interaction&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;php&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; bin/console&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; messenger:setup-transports&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; --env=prod&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; --no-interaction&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;php&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; bin/console&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; app:meilisearch:index&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; --env=prod&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-9&quot;&gt;echo&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &quot;Ready to start the frontend service.&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-9&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &quot;&lt;/span&gt;&lt;span class=&quot;syntax-12&quot;&gt;$@&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Enfin, il ne nous manque plus que le stage qui permettra de construire l&#039;image faisant tourner le conteneur pour le worker Messenger :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; production-php-base &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;AS&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; production-worker-messenger&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;RUN&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; apt-get update \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    &amp;#x26;&amp;#x26; apt-get install -y --no-install-recommends \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        procps \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    &amp;#x26;&amp;#x26; apt-get clean \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    &amp;#x26;&amp;#x26; rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /usr/share/doc/*&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;CMD&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; [&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;php&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;-d&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;memory_limit=-1&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;bin/console&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;messenger:consume&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;-vv&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;async&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ici, on note l&#039;installation de &lt;code&gt;procps&lt;/code&gt; qui fournit le binaire pgrep, utile pour vérifier si le processus qui fait tourner le worker est toujours actif. Il sera employé dans le healthcheck de ce service :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;  worker-messenger&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;    # ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    healthcheck&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;      test&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: [&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;CMD-SHELL&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;pgrep -f &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;messenger:consume&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; || exit 1&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;      interval&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;5s&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;      timeout&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;5s&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;      retries&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;5&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Grâce au multi-stage de Docker, nous avons ainsi pu construire toutes nos images utilisant PHP dans un même Dockerfile.&lt;/p&gt;
&lt;h4&gt;A table !&lt;/h4&gt;
&lt;p&gt;Maintenant que nos images peuvent être construites, il va nous falloir les taguer puis les envoyer sur le registre. On pourrait lancer les mêmes commandes &lt;code&gt;docker image tag|push&lt;/code&gt; vues précédemment. Mais à la place, on va simplifier et automatiser le processus grâce à &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://docs.docker.com/build/bake/&quot;&gt;Bake&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Cet outil permet de définir l&#039;ensemble des cibles de build dans un fichier de configuration (&lt;code&gt;bake.hcl&lt;/code&gt; par exemple) et de les construire/pousser en une seule commande. Voici un extrait de ce à quoi ressemble notre fichier bake :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-6&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; &quot;default&quot; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;  targets &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;    &quot;frontend&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;    &quot;worker-messenger&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;  ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-6&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; &quot;frontend&quot; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;  context    &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &quot;.&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;  dockerfile &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &quot;./infrastructure/docker/services/php-production/Dockerfile&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;  target     &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &quot;production-frontend&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-6&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; &quot;worker-messenger&quot; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;  context    &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &quot;.&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;  dockerfile &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &quot;./infrastructure/docker/services/php-production/Dockerfile&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;  target     &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &quot;production-worker-messenger&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Avec ce fichier, il ne nous reste qu&#039;à lancer la commande suivant pour construire les images, les tagger et les envoyer au registre docker :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;docker&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; buildx&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; bake&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; --file&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; bake.hcl&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; --push&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;c-alert c-alert--note&quot;&gt;
    &lt;p class=&quot;c-alert__title&quot;&gt;
                    &lt;span class=&quot;c-icon c-icon--monospace&quot;&gt;
                &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; aria-hidden=&quot;true&quot; class=&quot;c-icon__svg&quot; focusable=&quot;false&quot; viewBox=&quot;0 0 70 71&quot;&gt;&lt;path fill-rule=&quot;nonzero&quot; d=&quot;M35 .9c19.3 0 35 15.7 35 35s-15.7 35-35 35-35-15.7-35-35S15.7.9 35 .9m0 5c-16.552 0-30 13.449-30 30s13.448 30 30 30c16.552.103 30-13.448 30-30 0-16.551-13.448-30-30-30m0 24.9c1.7 0 3 1.3 3 3v15.3c0 1.7-1.3 3-3 3s-3-1.3-3-3V33.8c0-1.7 1.3-3 3-3m0-11c.8 0 1.6.3 2.3.9.6.5.9 1.3.9 2.1 0 .2-.1.4-.1.6-.1.2-.1.4-.2.6s-.2.3-.3.5-.3.4-.4.5c-1.1 1.1-3.1 1.1-4.2 0-.2-.2-.3-.3-.4-.5s-.2-.3-.3-.5-.2-.4-.2-.6c-.1-.2-.1-.4-.1-.6 0-.8.3-1.6.9-2.1.5-.6 1.3-.9 2.1-.9&quot;/&gt;&lt;/svg&gt;
            &lt;/span&gt;
                        &lt;strong&gt;Info&lt;/strong&gt;
    &lt;/p&gt;
    &lt;div class=&quot;c-alert__content&quot;&gt;
                &lt;p&gt;
Malheureusement, Bake n&#039;a pas l&#039;air de permettre de juste taguer des images existantes, donc nous ne pourrons pas l&#039;utiliser pour remplacer les &lt;code&gt;docker image tag|push&lt;/code&gt; des images publiques.&lt;/p&gt;
        &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Nos images sont maintenant disponibles sur le registre du client :&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;/media/cache/content-webp/2026/on-premise-docker-castor/arsol-docker-registry.f99c2888.webp&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;/media/cache/content/2026/on-premise-docker-castor/arsol-docker-registry.png&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;width: 869px; ; aspect-ratio: calc(869 / 550)&quot; src=&quot;https://jolicode.com//media/cache/content/2026/on-premise-docker-castor/arsol-docker-registry.png&quot; alt=&quot;Les images disponibles sur le registre GitLab&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Nous avons vu dans cet article une bonne partie des étapes qui nous permettent de construire les images Docker dont nous aurons besoin pour faire tourner l&#039;application dans tous nos environnements.&lt;/p&gt;
&lt;p&gt;Je n&#039;en ai pas parlé jusque-là mais il reste quelques points de sécurité à garder en tête avant de mettre en production cette infrastructure (comme les capabilities Docker ou encore les logs générés par Docker Compose qui peuvent vite remplir le disque par défaut en cas de fort trafic).&lt;/p&gt;
&lt;p&gt;Le client devant être autonome pour déployer les prochaines évolutions de l&#039;application, nous ne nous sommes pas arrêtés là. Nous avons donc mis en place tout un ensemble de task Castor permettant d&#039;automatiser la création et la publication des images, ainsi que de simplifier le pilotage de la stack dans les différents environnements. Mais nous verrons tout cela dans le prochain article.&lt;/p&gt;

        </content>
    </entry>    <entry>
        <id>https://jolicode.com/blog/automapper-10-0-high-performance-mapping-ready-for-the-future</id>
        <published>2026-02-10T14:42:00+01:00</published>
        <updated>2026-02-10T14:42:00+01:00</updated>
        <link type="text/html" rel="alternate" href="https://jolicode.com/blog/automapper-10-0-high-performance-mapping-ready-for-the-future"/>
        <title>AutoMapper 10.0: High-performance mapping ready for the future</title>
        <author>
            <name>JoliCode Team</name>
            <uri>https://jolicode.com/</uri>
        </author>            <category term="php" />            <category term="symfony" />        <summary><![CDATA[This is a major milestone for the library: AutoMapper has reached version 10.0.
While our promise remains unchanged — transforming your data from one format to another as fast as possible — this version…]]></summary>
        <content type="html">
            &lt;p&gt;This is a major milestone for the library: AutoMapper has reached version 10.0.&lt;/p&gt;
&lt;p&gt;While our promise remains unchanged — transforming your data from one format to another as fast as possible — this version marks a technological breakthrough. We took advantage of this major release to modernize the core engine and align with the latest Symfony standards: and much more!&lt;/p&gt;
&lt;h2&gt;Under the hood: The arrival of symfony/type-info&lt;/h2&gt;
&lt;p&gt;This is the most impactful invisible change. I have been working on Jane and AutoMapper for a long time, and I started from a simple observation: a tool capable of retrieving metadata in an advanced way was missing. At the time, Symfony&#039;s &lt;code&gt;PropertyInfo&lt;/code&gt; component had limited support for unions and handled neither intersections nor generics.&lt;/p&gt;
&lt;p&gt;I initially &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://github.com/symfony/symfony/pull/40457&quot;&gt;implemented the PHPStan extractor&lt;/a&gt; in &lt;code&gt;PropertyInfo&lt;/code&gt; to try to fill these gaps, but I quickly realized that the system remained limited by the component&#039;s very design. It was following discussions with Mathias Arlaud that we decided to team up to create and &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://github.com/symfony/symfony/pull/52510&quot;&gt;release the TypeInfo component&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;That is why &lt;code&gt;TypeInfo&lt;/code&gt; (introduced in Symfony 7.1) is now the new standard for obtaining even more detailed type definitions. With v10, AutoMapper switches to this component. The more precise the type definition, the more reliable and optimal the mapping process can be. This is an essential step to guarantee a much more faithful extraction of your data.&lt;/p&gt;
&lt;h3&gt;The new feature: Forced Typing&lt;/h3&gt;
&lt;p&gt;Sometimes, automatic inference isn&#039;t enough, or you want to transform data into a specific format that differs from the source property&#039;s PHP type. v10 allows you to force the target type directly via the &lt;code&gt;#[MapTo]&lt;/code&gt; attribute.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Here, we explicitly ask the mapper to transform the $number property into an array of integers in the target, even if the source is a string.&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;Entity&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    #[MapTo(target: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;array&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, targetPropertyType: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;int&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $number;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Interoperability: Symfony ObjectMapper Support&lt;/h2&gt;
&lt;p&gt;Much like AutoMapper&#039;s native interface, the &lt;code&gt;ObjectMapperInterface&lt;/code&gt; allows transforming a source object into a target object via a single method. The strength of this signature lies in the &lt;code&gt;$target&lt;/code&gt; parameter, which accepts either a &lt;strong&gt;class name&lt;/strong&gt; (to create a new instance) or an &lt;strong&gt;existing object&lt;/strong&gt; (to hydrate or update it).&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;interface&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;ObjectMapperInterface&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; map&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $source, &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $target &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; null&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; object&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is where AutoMapper makes a difference: whereas Symfony&#039;s native implementation relies mainly on the Reflection API (which can be costly), AutoMapper generates optimized PHP code. By activating this support, you replace the default mechanism with a solution tailored for performance, which is much faster at runtime.
To activate this feature and replace Symfony&#039;s default ObjectMapper with the AutoMapper one, a single line in the bundle configuration is enough:&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;automapper&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    object_mapper&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;true&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To make this possible and efficient, we had to implement a highly anticipated feature: &lt;strong&gt;Nesting&lt;/strong&gt;. This allows reading or writing deeply nested values in your objects, drastically simplifying the transition from &amp;quot;flat&amp;quot; DTOs to rich entities.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;
Here we map a flat &lt;code&gt;UserDto&lt;/code&gt; to a &lt;code&gt;User&lt;/code&gt; containing an &lt;code&gt;Address&lt;/code&gt; object. Thanks to the &lt;code&gt;address.street&lt;/code&gt; notation, AutoMapper knows it must hydrate the sub-object.&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;UserDto&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    #[MapTo(property: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;address.street&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $streetAddress;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    #[MapTo(property: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;address.city&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $cityAddress;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $name;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; Address&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $address;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $name;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt; __construct&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-11&quot;&gt;        $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;address &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; new&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; Address&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;Address&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $street;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $city;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// In use, the Address sub-object is automatically populated&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$mapper&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; UserDto&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    streetAddress: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;123 Main St&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    cityAddress: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Springfield&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    name: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;John Doe&#039;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;), &lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;::class&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Polymorphism: Rethinking discriminator management&lt;/h2&gt;
&lt;p&gt;Until now, handling polymorphism relied on a dependency on the Symfony Serializer. It was limited by the necessity to specify a discrimination property. v10 improves this by allowing you to define the discrimination logic directly at the class level via the &lt;code&gt;#[Mapper]&lt;/code&gt; attribute, enabling object mapping without having to define a property to discriminate entities.
This allows for seamless mapping from DTO hierarchies to Entity hierarchies.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;
Here you define how to map the children of the abstract Pet class based on the incoming DTO type.&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;#[Mapper(discriminator: &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; Discriminator&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    mapping: [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;        DogDto&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;::class&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; Dog&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;::class&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;        CatDto&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;::class&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; Cat&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;::class&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;))]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;abstract&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;Pet&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;    /** &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;@var&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; string&lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt; */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $name;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;    /** &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;@var&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; PetOwner&lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt; */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $owner;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;A new Profiler to see clearly&lt;/h2&gt;
&lt;p&gt;Mapping is often a &amp;quot;black box&amp;quot;. When data comes out malformed, understanding why can be complex.&lt;/p&gt;
&lt;p&gt;We have revamped the integration into the &lt;strong&gt;Symfony Profiler&lt;/strong&gt;. Gone are the days when you didn&#039;t know if AutoMapper had actually worked. Now, the dedicated panel allows you to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;List all mappings performed during the request.&lt;/li&gt;
&lt;li&gt;See the Source class and the Target class.&lt;/li&gt;
&lt;li&gt;Inspect the context passed to the mapper.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It is an indispensable tool for debugging your complex transformations without having to place breakpoints everywhere.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://jolicode.com//media/original/2026/automapper-v10/profiler.png&quot;&gt;&lt;picture class=&quot;js-dialog-target&quot; data-original-url=&quot;/media/original/2026/automapper-v10/profiler.png&quot; data-original-width=&quot;1289&quot; data-original-height=&quot;2259&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;/media/cache/content-webp/2026/automapper-v10/profiler.e93155e3.webp&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;/media/cache/content/2026/automapper-v10/profiler.png&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;width: 996px; ; aspect-ratio: calc(1289 / 2259)&quot; src=&quot;https://jolicode.com//media/cache/content/2026/automapper-v10/profiler.png&quot; alt=&quot;Profiler&quot; /&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Final Word&lt;/h2&gt;
&lt;p&gt;Version 10 is the culmination of a lot of work to modernize and sustain the library.
A huge thank you to all the contributors who participated in this release, with a special mention to &lt;a href=&quot;https://jolicode.com/qui-sommes-nous/equipe/joel-wurtz&quot;&gt;Joel Wurtz&lt;/a&gt; for his colossal investment in this major version.&lt;/p&gt;
&lt;p&gt;To go further, everything happens here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;📖 &lt;strong&gt;Documentation&lt;/strong&gt;: &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://automapper.jolicode.com/&quot;&gt;https://automapper.jolicode.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;🐙 &lt;strong&gt;GitHub repository&lt;/strong&gt;: &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://github.com/jolicode/automapper&quot;&gt;https://github.com/jolicode/automapper&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Make sure your environment is running PHP 8.4 or higher and run:&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;composer&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; require&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; jolicode/automapper&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; ^10.0&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

        </content>
    </entry>    <entry>
        <id>https://jolicode.com/blog/automapper-10-0-le-mapping-haute-performance-pret-pour-le-futur</id>
        <published>2026-02-10T14:41:00+01:00</published>
        <updated>2026-02-10T14:41:00+01:00</updated>
        <link type="text/html" rel="alternate" href="https://jolicode.com/blog/automapper-10-0-le-mapping-haute-performance-pret-pour-le-futur"/>
        <title>AutoMapper 10.0 : Le mapping haute performance prêt pour le futur</title>
        <author>
            <name>JoliCode Team</name>
            <uri>https://jolicode.com/</uri>
        </author>            <category term="php" />            <category term="symfony" />        <summary><![CDATA[C’est une étape majeure pour la librairie : AutoMapper passe en version 10.0.
Si notre promesse reste inchangée — transformer vos données d&#039;un format à un autre le plus vite possible — cette version marque…]]></summary>
        <content type="html">
            &lt;p&gt;C’est une étape majeure pour la librairie : AutoMapper passe en version 10.0.&lt;/p&gt;
&lt;p&gt;Si notre promesse reste inchangée — transformer vos données d&#039;un format à un autre le plus vite possible — cette version marque une rupture technologique. Nous avons profité de cette version majeure pour moderniser le cœur du réacteur et s&#039;aligner sur les derniers standards de Symfony et bien plus encore !&lt;/p&gt;
&lt;h2&gt;Sous le capot : L&#039;arrivée de symfony/type-info&lt;/h2&gt;
&lt;p&gt;C&#039;est le changement invisible le plus impactant. Je travaille sur Jane et AutoMapper depuis longtemps, et je suis parti d&#039;un constat simple : il manquait un outil capable de récupérer les métadonnées de façon avancée. À l&#039;époque, le composant &lt;code&gt;PropertyInfo&lt;/code&gt; de Symfony avait un support limité des unions et ne gérait ni les intersections, ni les génériques.&lt;/p&gt;
&lt;p&gt;J&#039;avais initialement &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://github.com/symfony/symfony/pull/40457&quot;&gt;mis en place l&#039;extracteur PHPStan&lt;/a&gt; dans &lt;code&gt;PropertyInfo&lt;/code&gt; pour tenter de combler ces manques, mais je me suis vite rendu compte que le système restait limité par la conception même du composant. C&#039;est suite à des discussions avec Mathias Arlaud que nous avons décidé de nous mettre ensemble pour créer et &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://github.com/symfony/symfony/pull/52510&quot;&gt;sortir le composant TypeInfo&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;C&#039;est pourquoi &lt;code&gt;TypeInfo&lt;/code&gt; (introduit dans Symfony 7.1) est aujourd&#039;hui le nouveau standard pour obtenir des définitions de types encore plus détaillées. Avec la v10, AutoMapper bascule sur ce composant. Plus la définition des types est précise, plus le processus de mapping peut être effectué de manière fiable et optimale. C&#039;est une étape essentielle pour garantir une extraction beaucoup plus fidèle de vos données.&lt;/p&gt;
&lt;h3&gt;La nouveauté : Le typage forcé&lt;/h3&gt;
&lt;p&gt;Parfois, l&#039;inférence automatique ne suffit pas ou vous souhaitez transformer une donnée vers un format précis qui diffère du type PHP de la propriété source. La v10 permet de forcer le type cible directement via l&#039;attribut &lt;code&gt;#[MapTo]&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Exemple :&lt;/strong&gt;
Ici, on demande explicitement au mapper de transformer la propriété &lt;code&gt;$number&lt;/code&gt; en un entier dans la cible, même si la source est une string.&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;Entity&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    #[MapTo(targetPropertyType: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;int&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $number;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Interopérabilité : Support de Symfony ObjectMapper&lt;/h2&gt;
&lt;p&gt;Un peu comme l&#039;interface native d&#039;AutoMapper, l&#039;&lt;code&gt;ObjectMapperInterface&lt;/code&gt; permet de transformer un objet source en un objet cible via une méthode unique. La force de cette signature réside dans le paramètre &lt;code&gt;$target&lt;/code&gt;, qui accepte soit le &lt;strong&gt;nom d&#039;une classe&lt;/strong&gt; (pour créer une nouvelle instance), soit un &lt;strong&gt;objet existant&lt;/strong&gt; (pour l&#039;hydrater ou le mettre à jour).&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;interface&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;ObjectMapperInterface&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; map&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $source, &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $target &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; null&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; object&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et c&#039;est ici que AutoMapper fait la différence : là où l&#039;implémentation native de Symfony s&#039;appuie principalement sur l’API Reflection qui peut être coûteuse, AutoMapper génère du code PHP optimisé. En activant ce support, vous remplacez le mécanisme par défaut par une solution taillée pour la performance, beaucoup plus rapide à l&#039;exécution.&lt;/p&gt;
&lt;p&gt;Pour activer cette fonctionnalité et remplacer l&#039;ObjectMapper par défaut de Symfony par celui de l’AutoMapper, il suffit d&#039;une ligne dans la configuration du bundle :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;automapper&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    object_mapper&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;true&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pour rendre cela possible et efficace, nous avons dû implémenter une fonctionnalité très attendue : le &lt;strong&gt;Nesting&lt;/strong&gt;. Cela permet d&#039;aller lire ou écrire des valeurs profondément imbriquées dans vos objets, simplifiant drastiquement le passage de DTOs &amp;quot;à plat&amp;quot; vers des entités riches.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Exemple :&lt;/strong&gt;
On mappe ici un &lt;code&gt;UserDto&lt;/code&gt; plat vers un &lt;code&gt;User&lt;/code&gt; qui contient un objet &lt;code&gt;Address&lt;/code&gt;. Grâce à la notation &lt;code&gt;address.street&lt;/code&gt;, AutoMapper sait qu&#039;il doit hydrater le sous-objet.&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;UserDto&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    #[MapTo(property: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;address.street&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $streetAddress;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    #[MapTo(property: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;address.city&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $cityAddress;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $name;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; Address&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $address;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $name;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt; __construct&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-11&quot;&gt;        $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;address &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; new&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; Address&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;Address&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $street;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $city;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// À l&#039;utilisation, le sous-objet Address est automatiquement peuplé&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$mapper&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; UserDto&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    streetAddress: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;123 Main St&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    cityAddress: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Springfield&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    name: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;John Doe&#039;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;), &lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;::class&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Polymorphisme : Une gestion des discriminants repensée&lt;/h2&gt;
&lt;p&gt;Jusqu&#039;ici, gérer le polymorphisme reposait sur une dépendance au Serializer de Symfony. Il était limité par la nécessité de spécifier une propriété de discrimination. La v10 améliore cela en permettant de définir la logique de discrimination directement au niveau de la classe via l&#039;attribut &lt;code&gt;#[Mapper]&lt;/code&gt; et permet de définir un mapping d&#039;objets sans avoir à définir une propriété pour discriminer les entités.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Exemple :&lt;/strong&gt;
Vous définissez ici comment mapper les enfants de la classe abstraite &lt;code&gt;Pet&lt;/code&gt; en fonction du type de DTO entrant.&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;#[Mapper(discriminator: &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; Discriminator&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    mapping: [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;        DogDto&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;::class&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; Dog&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;::class&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;        CatDto&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;::class&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; Cat&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;::class&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;))]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;abstract&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;Pet&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;    /** &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;@var&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; string&lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt; */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $name;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;    /** &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;@var&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; PetOwner&lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt; */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $owner;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Un nouveau Profiler pour y voir clair&lt;/h2&gt;
&lt;p&gt;Le mapping est souvent une &amp;quot;boîte noire&amp;quot;. Quand une donnée ressort mal formée, comprendre pourquoi peut être complexe.
Nous avons revu l&#039;intégration dans le &lt;strong&gt;Profiler Symfony&lt;/strong&gt;. Fini le temps où vous ne saviez pas si AutoMapper avait réellement travaillé. Désormais, le panneau dédié vous permet de :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Lister tous les mappings effectués durant la requête ;&lt;/li&gt;
&lt;li&gt;Voir la classe Source et la classe Cible ;&lt;/li&gt;
&lt;li&gt;Inspecter le contexte passé au mapper.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;C&#039;est un outil indispensable pour debugger vos transformations complexes sans devoir placer des points d&#039;arrêt partout.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://jolicode.com//media/original/2026/automapper-v10/profiler.png&quot;&gt;&lt;picture class=&quot;js-dialog-target&quot; data-original-url=&quot;/media/original/2026/automapper-v10/profiler.png&quot; data-original-width=&quot;1289&quot; data-original-height=&quot;2259&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;/media/cache/content-webp/2026/automapper-v10/profiler.e93155e3.webp&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;/media/cache/content/2026/automapper-v10/profiler.png&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;width: 996px; ; aspect-ratio: calc(1289 / 2259)&quot; src=&quot;https://jolicode.com//media/cache/content/2026/automapper-v10/profiler.png&quot; alt=&quot;Profiler&quot; /&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Le mot de la fin&lt;/h2&gt;
&lt;p&gt;Cette version 10 est l&#039;aboutissement de beaucoup de travail pour moderniser et pérenniser la librairie.
Un immense merci à tous les contributeurs qui ont participé à cette release, avec une mention spéciale à &lt;a href=&quot;https://jolicode.com/qui-sommes-nous/equipe/joel-wurtz&quot;&gt;Joel Wurtz&lt;/a&gt; pour son investissement colossal sur cette version majeure.&lt;/p&gt;
&lt;p&gt;Pour aller plus loin, tout se passe ici :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;📖 &lt;strong&gt;La documentation&lt;/strong&gt; : &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://automapper.jolicode.com/&quot;&gt;https://automapper.jolicode.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;🐙 &lt;strong&gt;Le dépôt GitHub&lt;/strong&gt; : &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://github.com/jolicode/automapper&quot;&gt;https://github.com/jolicode/automapper&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Assurez-vous que votre environnement tourne sous PHP 8.4 ou supérieur et lancez :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;composer&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; require&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; jolicode/automapper&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; ^10.0&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

        </content>
    </entry>    <entry>
        <id>https://jolicode.com/blog/que-devient-elasticsearch-en-2026</id>
        <published>2026-01-28T14:30:00+01:00</published>
        <updated>2026-01-28T14:30:00+01:00</updated>
        <link type="text/html" rel="alternate" href="https://jolicode.com/blog/que-devient-elasticsearch-en-2026"/>
        <title>Que devient Elasticsearch en 2026 ?</title>
        <author>
            <name>JoliCode Team</name>
            <uri>https://jolicode.com/</uri>
        </author>            <category term="conférence" />            <category term="elasticsearch" />        <summary><![CDATA[J&#039;étais hier à Elastic{On} Paris - la conférence de l&#039;éditeur d&#039;Elasticsearch 🔎. Dans cet article, je vais vous partager mon point de vue de développeur et consultant Elasticsearch sur les nouveautés,…]]></summary>
        <content type="html">
            &lt;p&gt;J&#039;étais hier à Elastic{On} Paris - la conférence de l&#039;éditeur d&#039;Elasticsearch 🔎. Dans cet article, je vais vous partager mon point de vue de développeur et &lt;a href=&quot;https://jolicode.com/blog/tag/elasticsearch&quot;&gt;consultant Elasticsearch&lt;/a&gt; sur les nouveautés, la direction, la transformation des usages du moteur et son écosystème.&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;js-dialog-target&quot; data-original-url=&quot;/media/original/2026/elasticon/elasticon-2026.jpg&quot; data-original-width=&quot;1500&quot; data-original-height=&quot;1048&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;/media/cache/content-webp/2026/elasticon/elasticon-2026.d1a2696a.webp&quot; /&gt;&lt;source type=&quot;image/jpeg&quot; srcset=&quot;/media/cache/content/2026/elasticon/elasticon-2026.jpg&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;width: 996px; ; aspect-ratio: calc(1500 / 1048)&quot; src=&quot;https://jolicode.com//media/cache/content/2026/elasticon/elasticon-2026.jpg&quot; alt=&quot;La scène de ElasticOn&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;h2&gt;L&#039;IA est la priorité 🤖&lt;/h2&gt;
&lt;p&gt;Elastic fait tout pour asseoir sa place dans le monde des LLM, avec beaucoup de nouveautés pour améliorer la recherche par vecteurs, leur stockage et les performances générales ; et il faut le dire : la majorité des talks de la journée en parlaient ! Il est loin le temps où nous parlions des nouveautés de Lucene 🤣&lt;/p&gt;
&lt;p&gt;L&#039;IA générative s&#039;invite partout, et les premiers retours d&#039;expérience concrets arrivent également, au fur et à mesure que les solutions s&#039;améliorent. Nous allons de plus en plus souvent intégrer des LLM, développer des serveurs MCP, mettre de l&#039;IA dans les workflows de nos clients, et Elastic compte bien avoir sa part du gâteau dans cette révolution technique.&lt;/p&gt;
&lt;h2&gt;La recherche hybride s&#039;améliore 👬&lt;/h2&gt;
&lt;p&gt;La recherche hybride est le fait de combiner des résultats de recherche vectorielle (&lt;abbr title=&quot;k-nearest neighbors, méthode des k plus proches voisins&quot;&gt;KNN&lt;/abbr&gt;) avec des résultats lexicaux (&lt;abbr title=&quot;Modèle de scoring par pertinence d&#039;Elasticsearch&quot;&gt;BM25&lt;/abbr&gt;), afin de proposer des résultats exploitant le meilleur des deux mondes.&lt;/p&gt;
&lt;p&gt;Un des challenges est la fusion des résultats ; et dans Elasticsearch, elle se fait avec différents &amp;quot;retriever&amp;quot; :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;RRF (Reciprocal Rank Fusion) : bon choix si on n&#039;a pas trop de règles métier ;&lt;/li&gt;
&lt;li&gt;Weighted RRF : permet de donner de la pondération ;&lt;/li&gt;
&lt;li&gt;Linear : utilise une somme pondérée des scores originaux...&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;La RRF ne nécessite aucun réglage préalable, et les différents indicateurs de pertinence n&#039;ont pas besoin d&#039;être liés entre eux pour obtenir des résultats de haute qualité. La syntaxe vient de changer et elle ressemble maintenant à cela :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;GET /retrievers_example/_search&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;    &quot;retriever&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;        &quot;linear&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;            &quot;retrievers&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;                {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;                    &quot;retriever&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;                        &quot;standard&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;                            &quot;query&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;                                &quot;match&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;                                    &quot;content&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-15&quot;&gt;&quot;raccord coudé en laiton&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;                                }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;                            }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;                        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;                    },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;                    &quot;weight&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;                    &quot;normalizer&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-15&quot;&gt;&quot;minmax&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;                },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;                {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;                    &quot;retriever&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;                        &quot;knn&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;                            &quot;field&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-15&quot;&gt;&quot;vector&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;                            &quot;query_vector&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: [ &lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt;// Des vecteurs de recherche&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-3&quot;&gt;                                0.23&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-3&quot;&gt;                                0.67&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-3&quot;&gt;                                0.89&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;                            ],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;                            &quot;k&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;                            &quot;num_candidates&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;5&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;                        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;                    },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;                    &quot;weight&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;1.5&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;                    &quot;normalizer&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-15&quot;&gt;&quot;minmax&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;                }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;            ],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;            &quot;rank_window_size&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;10&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;    &quot;_source&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;false&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Elastic 9.2 apporte aussi une grosse amélioration de la performance dont ils ont beaucoup parlé : &lt;strong&gt;DiskBBQ&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Aujourd&#039;hui, les vecteurs sont indexés via &lt;strong&gt;HNSW&lt;/strong&gt; (Hierarchical Navigable Small Worlds), qui est un genre de graphe à plusieurs niveaux. Ce graphe est parcouru, une similarité entre chaque nœud est calculée, et chaque niveau est visité l&#039;un après l&#039;autre. À chaque niveau, on a de plus en plus de nœuds à parcourir, mais à la fin nous obtenons les plus &amp;quot;justes&amp;quot;.&lt;/p&gt;
&lt;p&gt;Sur le disque, ces vecteurs ne sont pas au même endroit et donc les lire coûte cher 💸. Pour indexer dans ce graphe, il faut aussi faire des recherches, et pour merger des segments, il faut monter le graphe complet en mémoire... et ça coûte cher aussi 💸&lt;/p&gt;
&lt;p&gt;Dans DiskBBQ, les vecteurs sont groupés par similarité. Ces vecteurs proches vont donc être localisés près les uns des autres, et la lecture sera donc séquentielle lors des recherches. Pour l&#039;indexation, c&#039;est aussi beaucoup mieux, car tout se passe dans le CPU.&lt;/p&gt;
&lt;p&gt;DiskBBQ va être automatiquement appliqué à vos index si vous avez plus de 384 dimensions 👍 et la promesse est qu&#039;il offre 95 % de réduction d&#039;espace disque/mémoire 🚀&lt;/p&gt;
&lt;p&gt;Un autre point d&#039;attention est l&#039;utilisation d&#039;ACORN, un nouvel algorithme de recherche qui permet de filtrer efficacement les nœuds traversés dans un graphe HNSW, avec des gains annoncés jusqu&#039;à cinq fois plus rapides sur des recherches avec beaucoup de filtres.&lt;/p&gt;
&lt;p&gt;💡 Tout va très vite en ce moment, et chaque version du moteur apporte son lot d&#039;optimisations ; &lt;strong&gt;alors, si vous utilisez des vecteurs, mettez à jour Elasticsearch&lt;/strong&gt; !&lt;/p&gt;
&lt;h2&gt;Le nouvel Agent Builder 🔧&lt;/h2&gt;
&lt;p&gt;Ils passent la seconde avec &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://www.elastic.co/search-labs/blog/elastic-ai-agent-builder-context-engineering-introduction&quot;&gt;la release de l&#039;Agent Builder&lt;/a&gt;, qui va mettre à disposition des API et des interfaces graphiques pour la création d&#039;agents intelligents complets.&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;js-dialog-target&quot; data-original-url=&quot;/media/original/2026/elasticon/elastic-agent.jpg&quot; data-original-width=&quot;1500&quot; data-original-height=&quot;1131&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;/media/cache/content-webp/2026/elasticon/elastic-agent.ce4df1b8.webp&quot; /&gt;&lt;source type=&quot;image/jpeg&quot; srcset=&quot;/media/cache/content/2026/elasticon/elastic-agent.jpg&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;width: 996px; ; aspect-ratio: calc(1500 / 1131)&quot; src=&quot;https://jolicode.com//media/cache/content/2026/elasticon/elastic-agent.jpg&quot; alt=&quot;Elastic Agent Builder&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Cela va permettre l&#039;utilisation directe d&#039;outils en mode conversationnel : par exemple, combiner des données météo en temps réel avec un catalogue de produits indexé pour proposer le produit le mieux adapté... et c&#039;est clé en main dans le moteur !&lt;/p&gt;
&lt;p&gt;L&#039;outil se veut super complet et va permettre aux clients &amp;quot;Entreprise&amp;quot; de créer des solutions agentiques très simplement : tout se fait dans une interface Kibana. On y combine des outils, comme la recherche ES|QL, la récupération de documents par ID, nos outils custom... dans des workflows, et on les utilise soit via le chat inclus, soit par l&#039;API. Les outils de l&#039;agent sont aussi mis à disposition dans un serveur MCP Elasticsearch.&lt;/p&gt;
&lt;p&gt;Beaucoup de ces features reposent sur Jina.ai, qui est maintenant la propriété d&#039;Elastic.&lt;/p&gt;
&lt;p&gt;La démo des Streams était aussi sympa : l&#039;idée est d&#039;envoyer tous les logs en vrac dans Elastic. Puis un LLM est utilisé pour les partitionner (par type, par exemple « log_apache » et « log_mysql »). Enfin, toujours via un LLM et via l&#039;interface graphique, nous pouvons créer le pipeline ingest qui va parser/analyser notre log et le rendre exploitable - la fin des galères avec nos patterns GROK ?!&lt;/p&gt;
&lt;h2&gt;Des retours d&#039;expérience ⭐&lt;/h2&gt;
&lt;h3&gt;La mémoire des agents&lt;/h3&gt;
&lt;p&gt;Exploiter un agent conversationnel nécessite de conserver l&#039;historique des conversations afin de le servir comme contexte à chaque nouvelle question.&lt;/p&gt;
&lt;p&gt;Cet historique est problématique car il peut vite être très volumineux et donc dépasser la limite de tokens autorisée. C&#039;est là qu&#039;il faut ruser.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Dans LangChain, ce n&#039;est pas terrible : ils ont un tampon, disons 30k tokens, et le reste est oublié ;&lt;/li&gt;
&lt;li&gt;Utiliser un LLM pour résumer la conversation, ce n&#039;est pas terrible non plus, car on y perd en précision.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Le papier CoALA (Cognitive Architectures for Language Agents) essaie de résoudre ce problème. Il expose les différents types de mémoire : sémantique (ce que l&#039;on sait, le RAG), épisodique (ce qu&#039;on a fait, les actions), procédurale (système prompt)... La solution implémentée chez le client est donc de garder tout l&#039;historique mais de le simplifier via cette logique et de le stocker dans Elasticsearch.&lt;/p&gt;
&lt;h3&gt;La recherche par image&lt;/h3&gt;
&lt;p&gt;L&#039;exemple de Leroy Merlin (Adeo) était très sympa. Ils veulent proposer la recherche par image dans leur catalogue de 10 000 000 de produits. Il suffirait de prendre en photo l&#039;objet pour le trouver - pas besoin de connaître son nom précis (&amp;quot;raccord de bonde en laiton coudé&amp;quot; 🤣).&lt;/p&gt;
&lt;p&gt;Vous connaissez la chanson : on indexe des embeddings de nos images et on recherche en KNN. Rien de plus simple, le POC fonctionne, ça part en prod... MAIS !&lt;/p&gt;
&lt;p&gt;Il y a des challenges :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;l&#039;usage de la mémoire explose ;&lt;/li&gt;
&lt;li&gt;l&#039;espace disque n&#039;est pas prévisible ;&lt;/li&gt;
&lt;li&gt;les recherches sont lentes ;&lt;/li&gt;
&lt;li&gt;la pertinence n&#039;est pas toujours au rendez-vous.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Répondre avec le top K ne suffit pas, la pertinence visuelle ne suffit pas... Il faut aussi qualifier les résultats selon des critères métier (qualité, marge, stock).&lt;/p&gt;
&lt;p&gt;Leur solution a été de montrer l&#039;image du client à un LLM pour créer une recherche naturelle ; au final, ils font la recherche avec l&#039;image et avec les informations que le LLM a extrait (marque, catégorie de produit...), ce qui affine leurs résultats.&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;js-dialog-target&quot; data-original-url=&quot;/media/original/2026/elasticon/adeo.jpg&quot; data-original-width=&quot;1500&quot; data-original-height=&quot;1004&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;/media/cache/content-webp/2026/elasticon/adeo.a9b5266d.webp&quot; /&gt;&lt;source type=&quot;image/jpeg&quot; srcset=&quot;/media/cache/content/2026/elasticon/adeo.jpg&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;width: 996px; ; aspect-ratio: calc(1500 / 1004)&quot; src=&quot;https://jolicode.com//media/cache/content/2026/elasticon/adeo.jpg&quot; alt=&quot;Adeo sur scène&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;h2&gt;ES|QL en recherche applicative ? ⚡&lt;/h2&gt;
&lt;p&gt;ES|QL est partout dans les nouveaux produits, et on se pose donc la question : est-ce mature et pouvons-nous abandonner le QueryDSL ? Personnellement, je pense que c&#039;est un peu tôt, et les quelques personnes avec qui j&#039;en ai parlé le pensent aussi. Cependant, il faut bien l&#039;admettre : ES|QL permet des choses qui ne sont pas possibles en QueryDSL et il est bien plus lisible ! L&#039;autocomplétion est aussi vraiment complète et puissante.&lt;/p&gt;
&lt;p&gt;J&#039;y ai découvert la syntaxe FORK, qui permet d&#039;exploiter la recherche hybride :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; index&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; METADATA _score&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;| &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;WHERE&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; match&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(content, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;JoliCode&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;| &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;WHERE&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; year&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; &gt;&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; 2010&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;-- hybrid&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;| FORK (&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;WHERE&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; semantic_content: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;JoliCode&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;          | SORT _score &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;DESC&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;       (&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;WHERE&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; match&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(content, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;JoliCode&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;          | SORT _score &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;DESC&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;| FUSE RRF&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;| SORT _score &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;DESC&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;| &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;LIMIT&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; 100&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;-- rerank&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;| RERANK &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;JoliCode&quot;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; ON&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; content&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;| SORT _score &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;DESC&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;| &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;LIMIT&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; 10&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ES|QL est aussi capable de fournir des réponses augmentées par LLM - faire un résumé des résultats, par exemple :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;-- passage du résultat dans un LLM&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;| &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;LIMIT&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; 6&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;| COMPLETION &lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt;CONCAT&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;Summarize : &quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, content) &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;WITH&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &quot;openai&quot;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; AS&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; summary&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Avec un FORK en plus, la même requête va permettre d&#039;avoir des documents, un résumé, des agrégations...&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;js-dialog-target&quot; data-original-url=&quot;/media/original/2026/elasticon/esql.jpg&quot; data-original-width=&quot;1500&quot; data-original-height=&quot;1082&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;/media/cache/content-webp/2026/elasticon/esql.7a7e8108.webp&quot; /&gt;&lt;source type=&quot;image/jpeg&quot; srcset=&quot;/media/cache/content/2026/elasticon/esql.jpg&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;width: 996px; ; aspect-ratio: calc(1500 / 1082)&quot; src=&quot;https://jolicode.com//media/cache/content/2026/elasticon/esql.jpg&quot; alt=&quot;Exemple de ES QL&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;J&#039;aime beaucoup aussi l&#039;usage de CASE pour se passer de scripts Painless, souvent catastrophiques pour les performances.&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; employees&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;| EVAL &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; CASE&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    languages &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;&amp;#x3C;=&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; 1&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;monolingual&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    languages &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;&amp;#x3C;=&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; 2&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;bilingual&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;     &quot;polyglot&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;| &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;KEEP&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; emp_no, languages, &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;type&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Quelques take-away rapides 📔&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Cela fait un moment qu&#039;il existe le type natif &lt;code&gt;semantic_text&lt;/code&gt;, et je vous le recommande pour vos premiers essais. Il permet de s&#039;affranchir du chunking, des dimensions, du stockage de vecteurs... C&#039;est le moteur qui se charge de tout, et c&#039;est aussi simple à utiliser qu&#039;un champ &lt;code&gt;text&lt;/code&gt; ;&lt;/li&gt;
&lt;li&gt;Elastic Inference Service, c&#039;est uniquement dans Elastic Cloud ;&lt;/li&gt;
&lt;li&gt;Il y aura bientôt un mode &amp;quot;Elastic Cloud Connect&amp;quot; qui permettra de bénéficier de certaines fonctionnalités Cloud tout en ayant nos data nodes on-premise ;&lt;/li&gt;
&lt;li&gt;Les vecteurs vont passer d&#039;un stockage en float 32 bits à 16 bits en 9.3 sans perte de recall.&lt;/li&gt;
&lt;li&gt;Le Serverless va bientôt (cette année) arriver en self-hosted !&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Merci Elastic{On} 👏&lt;/h2&gt;
&lt;p&gt;J&#039;ai passé une journée très instructive ; j&#039;y ai trouvé du contenu technique, j&#039;ai échangé avec des pairs, croisé de vieilles connaissances... merci Elastic !&lt;/p&gt;
&lt;p&gt;Il est devenu difficile de suivre l&#039;évolution du moteur depuis la version 8, les nouvelles fonctionnalités liées à l&#039;IA sont nombreuses, et deviennent aussi un fort point de divergence avec OpenSearch, le fork d&#039;Amazon. En terme de recherche hybride et de confort d&#039;utilisation (ES|QL), il y a enfin de véritables impacts techniques dans le choix de l&#039;un ou de l&#039;autre.&lt;/p&gt;
&lt;script type=&quot;application/ld+json&quot;&gt;
{
  &quot;@context&quot;: &quot;https://schema.org&quot;,
  &quot;@type&quot;: &quot;FAQPage&quot;,
  &quot;mainEntity&quot;: [
    {
      &quot;@type&quot;: &quot;Question&quot;,
      &quot;name&quot;: &quot;Comment faire de la recherche hybride avec ES|QL dans Elasticsearch ?&quot;,
      &quot;acceptedAnswer&quot;: {
        &quot;@type&quot;: &quot;Answer&quot;,
        &quot;text&quot;: &quot;La recherche hybride dans ES|QL s&#039;effectue via la syntaxe FORK. Elle permet d&#039;exécuter parallèlement une recherche textuelle classique et une recherche sémantique, puis de combiner les scores grâce à l&#039;opérateur FUSE (utilisant par exemple l&#039;algorithme RRF).&quot;
      }
    },
    {
      &quot;@type&quot;: &quot;Question&quot;,
      &quot;name&quot;: &quot;Qu&#039;est-ce que DiskBBQ et quel est son impact sur les performances ?&quot;,
      &quot;acceptedAnswer&quot;: {
        &quot;@type&quot;: &quot;Answer&quot;,
        &quot;text&quot;: &quot;DiskBBQ est une optimisation introduite dans Elasticsearch 9.2 qui regroupe les vecteurs par similarité sur le disque pour permettre une lecture séquentielle plutôt qu&#039;aléatoire.&quot;
      }
    },
    {
      &quot;@type&quot;: &quot;Question&quot;,
      &quot;name&quot;: &quot;Pourquoi recommander l&#039;utilisation du type natif semantic_text ?&quot;,
      &quot;acceptedAnswer&quot;: {
        &quot;@type&quot;: &quot;Answer&quot;,
        &quot;text&quot;: &quot;Le type &#039;semantic_text&#039; est recommandé car il simplifie drastiquement l&#039;usage de l&#039;IA dans Elasticsearch. Il permet de s&#039;affranchir de la gestion complexe du chunking, du calcul des dimensions et du stockage manuel des vecteurs. Le moteur gère l&#039;ensemble du processus de manière transparente, rendant son utilisation aussi simple qu&#039;un champ texte standard.&quot;
      }
    }
  ]
}
&lt;/script&gt;

        </content>
    </entry>    <entry>
        <id>https://jolicode.com/blog/microservices-et-contrats-d-api-jane-comme-source-de-verite</id>
        <published>2026-01-13T14:42:00+01:00</published>
        <updated>2026-01-13T14:42:00+01:00</updated>
        <link type="text/html" rel="alternate" href="https://jolicode.com/blog/microservices-et-contrats-d-api-jane-comme-source-de-verite"/>
        <title>Microservices et contrats d&#039;API : Jane comme source de vérité</title>
        <author>
            <name>JoliCode Team</name>
            <uri>https://jolicode.com/</uri>
        </author>            <category term="php" />            <category term="symfony" />            <category term="json" />            <category term="api" />            <category term="jane" />            <category term="openapi" />            <category term="validator" />        <summary><![CDATA[Dans le développement d&#039;une API, nous sommes tous confrontés au même défi : maintenir la cohérence entre la documentation et le code.
Qui n&#039;a jamais perdu des heures à débugger une erreur parce que le…]]></summary>
        <content type="html">
            &lt;p&gt;Dans le développement d&#039;une API, nous sommes tous confrontés au même défi : maintenir la cohérence entre la documentation et le code.&lt;/p&gt;
&lt;p&gt;Qui n&#039;a jamais perdu des heures à débugger une erreur parce que le champ &lt;code&gt;user_id&lt;/code&gt; était devenu &lt;code&gt;userId&lt;/code&gt; dans le code, mais pas dans la documentation ? C&#039;est ce qu&#039;on appelle le &amp;quot;drift&amp;quot;. À mesure que le projet évolue, le code change, mais la documentation (OpenAPI, Wiki, Postman) traîne souvent la patte, devenant une source d&#039;erreurs plutôt qu&#039;une aide.&lt;/p&gt;
&lt;p&gt;Et si la solution n&#039;était pas de mettre à jour manuellement notre code pour coller à la doc, mais de générer automatiquement notre code &lt;em&gt;à partir&lt;/em&gt; de la doc ? C&#039;est ici qu&#039;intervient &lt;strong&gt;Jane&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;Découvrir Jane&lt;/h2&gt;
&lt;p&gt;En deux mots : Jane est une suite de librairies PHP dont la mission est de générer du code de qualité à partir de vos spécifications &lt;strong&gt;&lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://json-schema.org/&quot;&gt;JSON Schema&lt;/a&gt;&lt;/strong&gt; ou &lt;strong&gt;&lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://www.openapis.org/&quot;&gt;OpenAPI&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Si l&#039;on devait résumer son rôle dans une API moderne, Jane agit comme le &amp;quot;traducteur automatique&amp;quot; entre vos contrats d&#039;interface (vos spécifications &lt;code&gt;.yaml&lt;/code&gt; ou &lt;code&gt;.json&lt;/code&gt;) et votre code PHP. Au lieu d&#039;écrire manuellement vos classes, vos validateurs et vos clients HTTP — une tâche répétitive et sujette à l&#039;erreur humaine — Jane les fabrique pour vous.&lt;/p&gt;
&lt;p&gt;Concrètement, Jane analyse votre schéma et produit :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Des Modèles (DTO) :&lt;/strong&gt; Des classes PHP simples (POPO - &lt;em&gt;Plain Old PHP Objects&lt;/em&gt;) strictement typées qui représentent vos données ;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Des Normalizers :&lt;/strong&gt; Toute la logique nécessaire pour transformer ces objets en JSON et inversement, en s&#039;appuyant sur le composant &lt;code&gt;symfony/serializer&lt;/code&gt; ;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Un Client HTTP complet :&lt;/strong&gt; (dans le cas du composant OpenAPI) Une implémentation prête à l&#039;emploi (compatible PSR-18) pour consommer l&#039;API, gérant les requêtes, les réponses et même les exceptions définies dans votre spec.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;L&#039;atout majeur de Jane n&#039;est pas seulement le gain de temps, c&#039;est la garantie de &lt;strong&gt;conformité&lt;/strong&gt;. Puisque le code est généré directement depuis la source de vérité (le schéma), il est impossible d&#039;avoir une divergence (&amp;quot;drift&amp;quot;) entre ce que votre documentation prétend faire et ce que votre code fait réellement. Si le schéma change, vous régénérez le code, et le tour est joué.&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Voyons un projet d’exemple&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Pour passer de la théorie à la pratique, nous allons construire un cas d&#039;usage classique : un &lt;strong&gt;tunnel d&#039;achat e-commerce&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Nous allons simuler la transformation d&#039;un panier en une commande validée. Pour cela, nous découpons la logique en deux micro-services distincts :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Le service Panier&lt;/strong&gt; : Il matérialise l&#039;état d&#039;attente avant la commande. C&#039;est lui qui mémorise les articles choisis au fur et à mesure que le client parcourt le site, avant qu&#039;il ne décide (ou non) de passer à l&#039;achat ;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Le service Commande&lt;/strong&gt; : Il gère la finalisation de la vente et la collecte des informations client.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Ce scénario nous permettra d&#039;explorer des cas concrets :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Côté Panier&lt;/strong&gt;, nous utiliserons des UUID pour récupérer le contenu du panier ;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Côté Commande&lt;/strong&gt;, nous mettrons en place une validation stricte des données (regex pour le téléphone, énumération pour le pays) lors de la transformation du panier en commande, ainsi qu&#039;un endpoint pour les codes promo.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Voyons maintenant comment formaliser tout cela dans nos contrats d&#039;API.&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Créer nos micro-services (1 / 2) : Panier&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Commençons par le service le plus simple : le &lt;strong&gt;Panier&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Ici, nous prenons le parti du &lt;em&gt;Design First&lt;/em&gt; : avant d&#039;écrire la moindre ligne de PHP, nous allons figer la structure de nos échanges dans un fichier &lt;code&gt;cart.yaml&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Pour ce service, le besoin est basique : nous voulons pouvoir récupérer un panier via son identifiant unique. Voici notre définition en OpenAPI 3.0.3 :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;openapi&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;3.0.3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;  title&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Service Panier&#039;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;  version&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;1.0.0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;paths&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;  /carts/{id}&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    get&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;      summary&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Récupérer un panier&#039;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;      parameters&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        - &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;id&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;          in&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;path&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;          required&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;          schema&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;            type&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;string&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;            format&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;uuid&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;      responses&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;        &#039;200&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;          description&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Le panier trouvé&#039;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;          content&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;            application/json&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;              schema&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;                $ref&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;#/components/schemas/Cart&#039;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;components&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;  schemas&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    Cart&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;      type&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;object&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;      properties&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        id&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;          type&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;string&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;          format&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;uuid&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        items&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;          type&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;array&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;          items&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;            type&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt; # simplifié pour l&#039;exemple&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;strong&gt;Ce qu&#039;il faut retenir ici&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;L&#039;utilisation du format &lt;code&gt;uuid&lt;/code&gt; sur l&#039;identifiant (&lt;code&gt;id&lt;/code&gt;) n&#039;est pas anodine. Jane exploite cette information pour enrichir le code généré avec des règles de validation strictes, garantissant que la donnée n&#039;est pas une simple chaîne de caractères mais bien un UUID valide.&lt;/p&gt;
&lt;p&gt;Nous avons posé les bases du Panier. Attaquons-nous maintenant au second morceau du puzzle, qui va nous demander un peu plus de rigueur : le service Commande.&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Créer nos micro-services (2 / 2) : Commande&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Passons maintenant aux choses sérieuses avec le service &lt;strong&gt;Commande&lt;/strong&gt;. Si le Panier était une simple lecture, la Commande implique de recevoir et valider des données utilisateur critiques.&lt;/p&gt;
&lt;p&gt;C&#039;est l&#039;occasion idéale pour définir des règles strictes directement dans notre fichier &lt;code&gt;order.yaml&lt;/code&gt;. Nous allons déclarer deux routes :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Une route de création de commande, qui valide l&#039;adresse et le téléphone, données indispensables pour assurer la livraison ;&lt;/li&gt;
&lt;li&gt;Une route pour appliquer un code de réduction à une commande (qui impactera le prix).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Voici à quoi ressemble notre contrat :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;openapi&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;3.0.3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;  title&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Service Commande&#039;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;  version&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;1.0.0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;paths&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;  /orders&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    post&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;      summary&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Créer une commande à partir d un panier&#039;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;      requestBody&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        required&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        content&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;          application/json&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;            schema&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;              $ref&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;#/components/schemas/OrderInput&#039;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;      responses&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;        &#039;201&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;          description&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Commande créée&#039;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;          content&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;            application/json&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;              schema&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;                $ref&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;#/components/schemas/Order&#039;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;/orders/{id}/discount&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    post&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;      summary&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Ajouter un code promo&#039;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;      parameters&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        - &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;id&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;          in&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;path&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;          required&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;          schema&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;            type&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;string&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;            format&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;uuid&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;      requestBody&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        required&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        content&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;          application/json&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;            schema&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;              type&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;object&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;              properties&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;                code&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;                  type&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;string&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;      responses&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;        &#039;200&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;          description&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Code promo appliqué&#039;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;components&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;  schemas&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    OrderInput&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;      type&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;object&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;      required&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: [&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;cartId&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;customer&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;      properties&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        cartId&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;          type&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;string&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;          format&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;uuid&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        customer&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;          $ref&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;#/components/schemas/CustomerAddress&#039;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    CustomerAddress&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;      type&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;object&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;      required&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: [&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;firstName&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;lastName&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;phoneNumber&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;countryCode&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;      properties&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        firstName&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;          type&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;string&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        lastName&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;          type&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;string&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        phoneNumber&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;          type&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;string&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;          pattern&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;^\+33[1-9]\d{8}$&#039;&lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt; # Validation Regex pour numéros français&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;          description&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Format: +33xxxxxxxxx&#039;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        countryCode&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;          type&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;string&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;          enum&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: [&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;FR&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;BE&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;LU&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;] &lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt;# Limitation par énumération&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    Order&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;      type&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;object&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;      properties&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        id&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;          type&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;string&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;          format&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;uuid&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        status&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;          type&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;string&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;          enum&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: [&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;pending&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;paid&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;shipped&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        price&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;          type&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;number&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;          description&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Prix final après réduction éventuelle&#039;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;strong&gt;Pourquoi aller aussi loin dans la spec ?&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Regardez bien le schéma &lt;code&gt;CustomerAddress&lt;/code&gt;. Nous n&#039;avons pas seulement défini des chaînes de caractères, nous avons imposé des contraintes métiers :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;pattern&lt;/code&gt; : Le champ &lt;code&gt;phoneNumber&lt;/code&gt; doit correspondre à une regex précise (+33...).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;enum&lt;/code&gt; : Le champ &lt;code&gt;countryCode&lt;/code&gt; ne peut être qu&#039;une valeur parmi une liste définie (France, Belgique, Luxembourg).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Au lieu de coder ces validations &amp;quot;à la main&amp;quot; dans chaque contrôleur PHP, nous les déclarons une seule fois dans le contrat. Jane se chargera de traduire ces contraintes dans le code généré, garantissant que si une donnée ne respecte pas la spec, elle ne passera pas.&lt;/p&gt;
&lt;p&gt;Nos contrats sont prêts. Il est temps de passer au PHP.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Configuration de Jane pour le projet exemple&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Nos spécifications OpenAPI sont prêtes (&lt;code&gt;cart.yaml&lt;/code&gt; et &lt;code&gt;order.yaml&lt;/code&gt;). Il faut maintenant expliquer à Jane comment les transformer en code PHP.&lt;/p&gt;
&lt;p&gt;Cela se passe via un fichier de configuration, généralement nommé &lt;code&gt;.jane-openapi.php&lt;/code&gt; à la racine du projet.&lt;/p&gt;
&lt;p&gt;Pour gérer nos deux micro-services (Panier et Commande) sans mélanger leur code, nous utilisons l&#039;option &lt;code&gt;mapping&lt;/code&gt;. Elle permet de définir des règles de génération spécifiques pour chaque fichier OpenAPI au sein d&#039;une seule et même configuration.&lt;/p&gt;
&lt;p&gt;Voici à quoi ressemble notre fichier &lt;code&gt;.jane-openapi.php&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;&amp;#x3C;?&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;php&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;    &#039;mapping&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-3&quot;&gt;        __DIR__&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; .&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;/cart.yaml&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;            &#039;namespace&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;App\Generated\Cart&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;            &#039;directory&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; __DIR__&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; .&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;/generated/Cart&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        ],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-3&quot;&gt;        __DIR__&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; .&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;/order.yaml&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;            &#039;namespace&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;App\Generated\Order&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;            &#039;directory&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; __DIR__&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; .&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;/generated/Order&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        ],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    ],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-3&quot;&gt;    ‘validation’&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; true&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;];&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;strong&gt;Les options disponibles&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Déchiffrons ensemble cette configuration :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;mapping&lt;/code&gt; : C&#039;est la clé maîtresse qui nous permet d&#039;associer chaque fichier de spécification (la clé du sous-tableau, ici le chemin vers &lt;code&gt;cart.yaml&lt;/code&gt; ou &lt;code&gt;order.yaml&lt;/code&gt;) à sa propre configuration.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;namespace&lt;/code&gt; : C&#039;est ici que l&#039;organisation se joue. En donnant des namespaces différents (&lt;code&gt;App\Generated\Cart&lt;/code&gt; vs &lt;code&gt;App\Generated\Order&lt;/code&gt;), nous isolons complètement les domaines. Si un modèle s&#039;appelle &lt;code&gt;Error&lt;/code&gt; dans les deux services, il n&#039;y aura aucun conflit de nom de classe dans notre projet.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;directory&lt;/code&gt; : Le dossier de destination où le code PHP généré pour chaque service va être écrit.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;validation&lt;/code&gt; : &lt;strong&gt;C&#039;est une option capitale.&lt;/strong&gt; En la passant à &lt;code&gt;true&lt;/code&gt;, nous demandons à Jane de traduire les contraintes du schéma OpenAPI (comme &lt;code&gt;minimum: 1&lt;/code&gt;, &lt;code&gt;required&lt;/code&gt;, &lt;code&gt;email&lt;/code&gt;) en métadonnées de &lt;strong&gt;Symfony Validator&lt;/strong&gt;. C&#039;est le socle qui nous permettra de garantir la robustesse des données sans avoir à réécrire manuellement les règles métiers en PHP. Nous verrons plus loin à quel point cela nous simplifie la vie.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Avec cette structure, Jane va itérer sur chaque entrée du mapping et produire automatiquement deux clients HTTP (compatibles PSR-18) distincts et autonomes.&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Et les erreurs ? Standardisation et gestion avec Jane&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Gérer le &amp;quot;happy path&amp;quot; (code 200), c&#039;est facile. Mais dans la vraie vie, une API échoue : validation invalide (400), accès refusé (403) ou crash serveur (500).&lt;/p&gt;
&lt;p&gt;Si nous laissons Jane deviner, elle lèvera des exceptions génériques HTTP. Mais nous pouvons faire mieux : nous pouvons uniformiser le format de nos erreurs pour que notre SDK PHP renvoie des objets structurés, faciles à manipuler dans un &lt;code&gt;try/catch&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Définir un schéma d&#039;erreur standard&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Dans nos fichiers OpenAPI (&lt;code&gt;cart.yaml&lt;/code&gt; et &lt;code&gt;order.yaml&lt;/code&gt;), nous allons ajouter une définition réutilisable dans la section &lt;code&gt;components&lt;/code&gt;. Cela permet d&#039;avoir un format unique (par exemple : un message et un code) pour toutes nos erreurs.&lt;/p&gt;
&lt;p&gt;Ajoutons ceci à la fin de nos fichiers YAML :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;components&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;  schemas&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;    # ... nos autres modèles (Cart, Product...)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;    # Notre modèle d&#039;erreur standardisé&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    Error&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;      type&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;object&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;      required&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        - &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;message&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        - &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;code&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;      properties&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        message&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;          type&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;string&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;          description&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;Le message d&#039;erreur pour le développeur&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        code&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;          type&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;integer&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;          description&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;Un code d&#039;erreur interne spécifique&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;strong&gt;Référencer ce schéma dans les endpoints&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Maintenant que le modèle &lt;code&gt;Error&lt;/code&gt; existe, nous devons dire à chaque endpoint : &amp;quot;Si nous renvoyons une 400, 403 ou 500, le corps de la réponse respectera ce schéma&amp;quot;.&lt;/p&gt;
&lt;p&gt;Reprenons l&#039;exemple de l&#039;ajout d&#039;un code de réduction sur une commande:&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;paths&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;  /orders/{id}/discount&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    post&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;      summary&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Ajouter un code promo&#039;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;      # ... (requestBody, etc)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;      responses&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;        &#039;200&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;          description&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Code promo appliqué&#039;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;          content&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;            application/json&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;              schema&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;                $ref&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;#/components/schemas/Order&#039;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;        # Gestion des erreurs standardisée&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;        &#039;400&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;          description&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Données invalides (ex: code non trouvé)&#039;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;          content&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;            application/json&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;              schema&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;                $ref&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;#/components/schemas/Error&#039;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;        &#039;403&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;          description&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;Action non autorisée&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;          content&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;            application/json&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;              schema&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;                $ref&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;#/components/schemas/Error&#039;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;        &#039;404&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;          description&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;Commande introuvable&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;          content&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;            application/json&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;              schema&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;                $ref&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;#/components/schemas/Error&#039;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;        &#039;500&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;          description&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;Erreur serveur&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;          content&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;            application/json&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;              schema&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;                $ref&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;#/components/schemas/Error&#039;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;strong&gt;Ce que Jane va générer pour nous&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;C&#039;est ici que la magie opère. Lors de la génération, Jane va détecter ces configurations et produire deux choses très utiles :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Un &lt;abbr title=&quot;Plain Old PHP Objects&quot;&gt;POPO&lt;/abbr&gt;&lt;/strong&gt; &lt;code&gt;Error&lt;/code&gt; : Une classe PHP classique (&lt;code&gt;App\Generated\Order\Model\Error&lt;/code&gt;) avec ses getters et setters.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Des Exceptions spécifiques&lt;/strong&gt; : Pour l&#039;endpoint ci-dessus, Jane va générer des exceptions comme &lt;code&gt;AddDiscountBadRequestException&lt;/code&gt; ou &lt;code&gt;AddDiscountNotFoundException&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Ces exceptions auront une méthode &lt;code&gt;getError()&lt;/code&gt; qui retournera... notre instance du modèle &lt;code&gt;Error&lt;/code&gt; remplie avec les données de l&#039;API !&lt;/p&gt;
&lt;p&gt;Cela permet d&#039;écrire un code client très propre :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    $api&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;addDiscount&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;C17891&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; OrdersIdDiscountPostBody&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;NOEL2025&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;} &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;AddDiscountBadRequestException&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;AddDiscountNotFoundException&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $e) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;    // On récupère notre objet Error proprement&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    $errorModel &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $e&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;getResponse&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-9&quot;&gt;    echo&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &quot;Erreur fonctionnelle : &quot;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; .&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $errorModel&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;getMessage&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;} &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;ClientExceptionInterface&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $e) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;    // Erreur réseau ou autre&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;En standardisant vos retours d&#039;erreurs dans l&#039;OpenAPI, vous garantissez que le code PHP généré est prévisible et facile à utiliser pour les développeurs qui consommeront votre SDK.&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Lancer la génération du code !&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;C&#039;est ici que la magie opère. Fini d&#039;écrire des DTOs, des clients HTTP et des exceptions à la main. Jane va faire tout ce travail fastidieux pour nous.&lt;/p&gt;
&lt;p&gt;Dans votre terminal, à la racine du projet, lancez simplement :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;vendor/bin/jane-openapi&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; generate&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; --config-file&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; .jane-openapi.php&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Jane va lire le fichier &lt;code&gt;.jane-openapi.php&lt;/code&gt;, détecter notre mapping et générer les fichiers correspondants. Si vous allez voir dans votre dossier &lt;code&gt;generated/&lt;/code&gt;, nous avons deux nouveaux dossiers qui sont apparus : &lt;code&gt;Cart&lt;/code&gt; et &lt;code&gt;Order&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Une étape indispensable : l&#039;autoloader&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Avoir les fichiers PHP, c&#039;est bien, mais pouvoir les utiliser, c&#039;est mieux ! Comme ce sont de nouvelles classes dans un nouveau dossier, Composer ne les connaît pas encore.&lt;/p&gt;
&lt;p&gt;Pour que Composer puisse charger ces nouvelles classes, nous devons mettre à jour le &lt;code&gt;composer.json&lt;/code&gt;. Plutôt que de déclarer chaque service un par un, allons au plus simple : nous allons faire pointer le namespace parent &lt;code&gt;App\Generated\&lt;/code&gt; directement vers le dossier &lt;code&gt;generated/&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Ouvrez votre &lt;code&gt;composer.json&lt;/code&gt; et ajoutez ces lignes dans la section &lt;code&gt;autoload&lt;/code&gt; :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;autoload&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;    &quot;psr-4&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;        &quot;App&lt;/span&gt;&lt;span class=&quot;syntax-17&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-15&quot;&gt;&quot;src/&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;        &quot;App&lt;/span&gt;&lt;span class=&quot;syntax-17&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;Generated&lt;/span&gt;&lt;span class=&quot;syntax-17&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-15&quot;&gt;&quot;generated/&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;},&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ensuite, pour que cette modification soit prise en compte, régénérez l&#039;autoloader :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;composer&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; dump-autoload&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;C&#039;est tout ! Votre projet est maintenant prêt à utiliser les librairies générées.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Que contient ce dossier generated ?&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Si vous êtes curieux (et vous devriez l&#039;être), jetez un œil à l&#039;intérieur de &lt;code&gt;generated/Cart&lt;/code&gt; ou &lt;code&gt;generated/Order&lt;/code&gt;. Vous y trouverez une structure très organisée :&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;generated/
├── Order
│   ├── Client.php
│   ├── Endpoint
│   ├── Exception
│   ├── Model
│   ├── Normalizer
│   ├── Runtime
│   │   ├── Client
│   │   └── Normalizer
│   └── Validator
└── Cart
    ├── Same file structure as Order
    └── ...
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Client.php&lt;/code&gt; : Votre &lt;strong&gt;point d&#039;entrée&lt;/strong&gt; principal. C&#039;est la classe qui contient toutes les méthodes comme &lt;code&gt;createOrder()&lt;/code&gt;, &lt;code&gt;addDiscount()&lt;/code&gt;, etc.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Endpoint/&lt;/code&gt; :  Chaque opération définie dans votre schéma OpenAPI (comme &lt;code&gt;GET /carts/{id}&lt;/code&gt;) possède ici sa propre classe dédiée. C&#039;est elle qui orchestre l&#039;appel API : elle sait quelle méthode HTTP utiliser, quel corps de requête envoyer, comment transformer la réponse brute en un objet de votre &lt;code&gt;Model&lt;/code&gt; et comment gérer les exceptions selon le code de retour.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Exception/&lt;/code&gt; : C&#039;est ici que se trouvent nos &lt;strong&gt;erreurs typées&lt;/strong&gt; (&lt;code&gt;AddDiscountBadRequestException&lt;/code&gt;, etc.) basées sur les codes HTTP et nos schémas d&#039;erreurs.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Model/&lt;/code&gt; : Vos &lt;strong&gt;POPO&lt;/strong&gt; (Plain Old PHP Objects). Ce sont les classes &lt;code&gt;Cart&lt;/code&gt;, &lt;code&gt;Order&lt;/code&gt;, &lt;code&gt;OrderInput&lt;/code&gt;, etc. Elles contiennent simplement des propriétés, des getters et des setters.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Normalizer/&lt;/code&gt; : C&#039;est le cœur de Jane (basé sur le composant &lt;strong&gt;Serializer&lt;/strong&gt; de Symfony). Ces classes savent comment transformer vos objets en JSON et inversement.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Runtime/&lt;/code&gt; : C&#039;est la salle des machines. Ce dossier contient le code &amp;quot;support&amp;quot; nécessaire au fonctionnement global du SDK généré (configuration du client, normalizers de base). Ce sont des composants techniques qui font le lien entre votre code généré et les librairies tierces (comme le client HTTP PSR-18).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Validator/&lt;/code&gt; : C&#039;est la particularité liée à notre option &lt;code&gt;&amp;quot;validation&amp;quot;: true&lt;/code&gt;. Jane génère des classes de contraintes spécifiques (ex: &lt;code&gt;OrderInputConstraint&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Détail technique :&lt;/em&gt; Ces classes étendent &lt;code&gt;Compound&lt;/code&gt; de Symfony Validator. Elles regroupent toutes les règles définies dans votre OpenAPI (&lt;code&gt;Required&lt;/code&gt;, &lt;code&gt;Email&lt;/code&gt;, &lt;code&gt;Regex&lt;/code&gt; etc.) en une seule classe appelable. Cela garde vos modèles propres tout en garantissant une validation stricte.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;&lt;/h2&gt;
&lt;h2&gt;&lt;strong&gt;Utilisation pratique : intégrer les modèles Jane dans votre code&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Maintenant que nos classes sont générées, comment les utiliser concrètement ?&lt;/p&gt;
&lt;p&gt;Jane brille par sa rigueur : elle sécurise les échanges via ses Normalizers et force l&#039;utilisation d&#039;objets typés.&lt;/p&gt;
&lt;h3&gt;La validation automatique&lt;/h3&gt;
&lt;p&gt;L&#039;un des atouts de Jane est qu&#039;elle intègre la validation directement dans le processus de &lt;strong&gt;sérialisation et de désérialisation&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Cela signifie que le contrôle des données est actif dans les deux sens :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;En entrée (Désérialisation)&lt;/strong&gt; : Vous ne pouvez pas créer un objet PHP invalide à partir d&#039;un JSON reçu.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;En sortie (Sérialisation)&lt;/strong&gt; : Vous ne pouvez pas générer un JSON invalide à partir d&#039;un objet PHP (par exemple, si vous avez oublié de remplir un champ obligatoire avant de renvoyer la réponse).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Vous n&#039;avez donc plus besoin d&#039;appeler le validateur manuellement. Regardez le code généré dans le &lt;code&gt;Normalizer&lt;/code&gt; (ici &lt;code&gt;OrdersIdDiscountPostBodyNormalizer&lt;/code&gt;) : il vérifie les contraintes &lt;strong&gt;pendant&lt;/strong&gt; la transformation.&lt;/p&gt;
&lt;p&gt;Si vous utilisez le Serializer de Symfony dans votre contrôleur, la validation est implicite :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;namespace&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;App\Controller&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; App\Generated\Order\Model\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;OrdersIdDiscountPostBody&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;; &lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt;// Le DTO généré&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Symfony\Component\HttpFoundation\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Symfony\Component\HttpFoundation\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Symfony\Component\Serializer\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;SerializerInterface&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Symfony\Component\Serializer\Exception\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;ValidationFailedException&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;OrderController&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-7&quot;&gt;AbstractController&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; addDiscount&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $request, &lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;SerializerInterface&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $serializer)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        try&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;            // C&#039;est ici que la magie opère :&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;            // Jane désérialise ET valide en même temps.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;            // Si le JSON est invalide (code manquant, trop court...), une exception est levée.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;            /** &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;@var&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; OrdersIdDiscountPostBody&lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt; $body */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;            $body &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $serializer&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;deserialize&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;                $request&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;getContent&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;                OrdersIdDiscountPostBody&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;::class&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;                &#039;json&#039;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;            );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        } &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;ValidationFailedException&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $e) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;            // On récupère les violations pour répondre une 400 propre via la constante Symfony&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;            return&lt;/span&gt;&lt;span class=&quot;syntax-11&quot;&gt; $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($e&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;getViolations&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(), &lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;HTTP_BAD_REQUEST&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;        // Si on arrive ici, $body est un objet valide et typé !&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        $code &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $body&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;getCode&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;        // ... traitement métier&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Le gain ?&lt;/strong&gt; Votre code métier ne manipule jamais de données &amp;quot;sales&amp;quot;. Et inversement, vous avez la certitude absolue que vos réponses API respectent toujours le contrat défini dans votre fichier YAML.&lt;/p&gt;
&lt;h3&gt;Gérer les erreurs (Exceptions typées)&lt;/h3&gt;
&lt;p&gt;C&#039;est ici que notre travail sur les schémas d&#039;erreurs &lt;code&gt;400&lt;/code&gt; / &lt;code&gt;404&lt;/code&gt; / &lt;code&gt;500&lt;/code&gt; porte ses fruits. Jane a généré des exceptions spécifiques pour chaque cas d&#039;erreur documenté.&lt;/p&gt;
&lt;p&gt;Fini les vérifications manuelles du status code, place aux &lt;code&gt;try/catch&lt;/code&gt; explicites :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; App\Generated\Order\Exception\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;AddDiscountBadRequestException&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; App\Generated\Order\Exception\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;AddDiscountNotFoundException&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; App\Generated\Order\Model\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;OrdersIdDiscountPostBody&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    $payload &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; new&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; OrdersIdDiscountPostBody&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    $payload&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;setCode&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;INVALID&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    $apiClient&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;addDiscount&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;order-12345&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, $payload);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;} &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;AddDiscountBadRequestException&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $e) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;    // C&#039;est une 400 spécifique à cette route (Response::HTTP_BAD_REQUEST)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;    // On récupère notre objet Error structuré défini dans l&#039;OpenAPI&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    $errorModel &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $e&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;getError&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;    // Affiche : &quot;Code promo invalide ou expiré&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    $logger&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;warning&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($errorModel&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;getMessage&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;());&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;} &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;AddDiscountNotFoundException&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $e) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;    // C&#039;est une 404 spécifique (Commande ou code de réduction introuvable)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;    // ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;} &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;Exception&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $e) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;    // Erreur réseau ou 500 générique&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Communication inter-services : Quand le service Commande appelle le service Panier&lt;/h2&gt;
&lt;p&gt;Dans une architecture microservices, la communication synchrone (HTTP) est souvent le talon d&#039;Achille. On se retrouve vite avec des appels &lt;code&gt;curl&lt;/code&gt; ou &lt;code&gt;Guzzle&lt;/code&gt; éparpillés, des tableaux associatifs non typés et, surtout, une confiance aveugle envers le service appelé.&lt;/p&gt;
&lt;p&gt;Maintenant, nous allons voir comment Jane sécurise l&#039;échange entre notre &lt;strong&gt;service Commande&lt;/strong&gt; (le consommateur) et le &lt;strong&gt;service Panier&lt;/strong&gt; (le producteur).&lt;/p&gt;
&lt;h3&gt;Le contrat comme dépendance : Réutiliser le schéma OpenAPI&lt;/h3&gt;
&lt;p&gt;Au lieu de redéfinir manuellement une classe &lt;code&gt;PanierDTO&lt;/code&gt; dans le service Commande (qui finirait inévitablement par diverger de la réalité), nous utilisons directement la définition OpenAPI du service Panier.&lt;/p&gt;
&lt;p&gt;Concrètement, cela signifie que le fichier &lt;code&gt;openapi.yaml&lt;/code&gt; du service Panier devient une &amp;quot;dépendance&amp;quot; du service Commande.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Configuration Jane&lt;/strong&gt; : Dans le service Commande, nous configurons Jane pour pointer vers ce fichier (via une URL, un submodule git, …).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Single Source of Truth&lt;/strong&gt; : Si l&#039;équipe Panier met à jour son modèle (par exemple, en ajoutant un champ &lt;code&gt;promoCode&lt;/code&gt;), le service Commande le saura dès la prochaine régénération du code.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Génération d&#039;un Client HTTP typé (SDK interne)&lt;/h3&gt;
&lt;p&gt;C&#039;est une des fonctionnalités clé de Jane pour la communication inter-services. En plus des modèles (DTOs), Jane a généré un Client HTTP complet prêt à l&#039;emploi (compatible PSR-18).&lt;/p&gt;
&lt;p&gt;Pour l&#039;utiliser, on va instancier le client via sa méthode statique &lt;code&gt;create()&lt;/code&gt; :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; App\Generated\Panier\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;Client&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$panierClient &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; Client&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$panier &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $panierClient&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;getPanier&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($uuid);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// $panier est une instance de la classe \App\Generated\Panier\Model\Panier&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// L&#039;IDE connaît toutes les propriétés :&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$total &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $panier&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;getTotal&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;foreach&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; ($panier&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;getItems&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;() &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $item) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;    // ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nous manipulons des objets PHP natifs, typés, générés spécifiquement pour cette interaction.&lt;/p&gt;
&lt;h3&gt;Validation automatique de la réponse&lt;/h3&gt;
&lt;p&gt;Que se passe-t-il si le service Panier a un bug et renvoie un prix sous forme de chaîne de caractères &lt;code&gt;10,50€&lt;/code&gt; au lieu d&#039;un nombre &lt;code&gt;10.50&lt;/code&gt;, ou s&#039;il manque un champ obligatoire ?&lt;/p&gt;
&lt;p&gt;Avec un client HTTP manuel, votre code planterait probablement plus loin, de manière obscure (&lt;code&gt;Call to member function on null&lt;/code&gt; ou erreur de calcul), ou pire, corromprait vos données silencieusement.&lt;/p&gt;
&lt;p&gt;Avec le client généré par Jane :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Le client valide &lt;strong&gt;automatiquement&lt;/strong&gt; la réponse HTTP reçue par rapport au schéma OpenAPI ;&lt;/li&gt;
&lt;li&gt;Si la réponse ne respecte pas le contrat (type incorrect, champ manquant), Jane lève immédiatement une &lt;code&gt;UnexpectedValueException&lt;/code&gt; ou une &lt;code&gt;InvalidResponseException&lt;/code&gt; ;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fail Fast&lt;/strong&gt; : L&#039;application s&#039;arrête net à la frontière du service, empêchant des données invalides de polluer votre logique métier de commande.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;En résumé, Jane transforme un appel HTTP incertain en un appel de méthode PHP sûr et typé.&lt;/p&gt;
&lt;h2&gt;Validation stricte dans les tests&lt;/h2&gt;
&lt;p&gt;Au lieu de charger vos données de test et ne jamais vérifier leur intégrité, passez-les dans la moulinette de Jane.&lt;/p&gt;
&lt;p&gt;Si votre donnée ne correspond plus à la réalité de l&#039;API (changement de type, champ manquant), Jane le détecte immédiatement.&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// 1. On charge la donnée brute (ici on prends un JSON en exemple&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// mais un endpoint API fonctionnera pareil&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$data &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt; json_decode&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt;file_get_contents&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;panier_fixture.json&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;), &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// 2. On demande à Jane de créer l&#039;objet.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// C&#039;est ici que la magie opère : Jane vérifie TOUT le contrat via la validation&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// dans le Normalizer généré&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    $panier &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $serializer&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;denormalize&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($data, &lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;Panier&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;::class&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;} &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;Exception&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $e) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;    // Le test échoue immédiatement si le JSON est périmé !&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-11&quot;&gt;    $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;fail&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Vos données de test ne respectent plus le contrat OpenAPI : &#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; .&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $e&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;getMessage&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;());&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// 3. On utilise un objet PHP valide pour configurer le mock&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// ou autre test que vous voudriez faire&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$mockService&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;getPanier&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;willReturn&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($panier);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pourquoi c&#039;est puissant ?&lt;/p&gt;
&lt;p&gt;Cela transforme vos tests unitaires en tests de contrat légers. Vous n&#039;avez plus peur que vos tests &amp;quot;passent au vert&amp;quot; alors qu&#039;ils utilisent des données obsolètes qui feront planter la production.&lt;/p&gt;
&lt;h2&gt;Évolution et versioning : Faire évoluer votre API sans tout casser&lt;/h2&gt;
&lt;p&gt;Une API n&#039;est jamais figée. Elle vit, elle change, elle s&#039;étend. Le cauchemar de tout développeur est de déployer une mise à jour qui casse la communication entre les services. Avec Jane, l&#039;impact d&#039;une modification du contrat OpenAPI devient immédiatement visible dans votre code PHP.&lt;/p&gt;
&lt;h3&gt;Ajout d&#039;un champ optionnel (Non-breaking change)&lt;/h3&gt;
&lt;p&gt;C&#039;est le scénario idéal. Vous ajoutez, par exemple, une &lt;code&gt;description&lt;/code&gt; facultative à votre objet &lt;code&gt;Commande&lt;/code&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Dans l&#039;OpenAPI :&lt;/strong&gt; Vous ajoutez le champ sans le marquer comme &lt;code&gt;required&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Après régénération Jane :&lt;/strong&gt; Votre classe &lt;code&gt;Commande&lt;/code&gt; gagne une propriété &lt;code&gt;protected ?string $description = null;&lt;/code&gt; ainsi que ses getters et setters.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Impact :&lt;/strong&gt; &lt;strong&gt;Nul.&lt;/strong&gt; Votre code existant continue de fonctionner parfaitement car le constructeur reste compatible (le nouveau champ est initialisé à &lt;code&gt;null&lt;/code&gt; par défaut). C&#039;est une évolution en douceur.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Ajout d&#039;un champ obligatoire (Breaking change)&lt;/h3&gt;
&lt;p&gt;C&#039;est ici que Jane vous sauve la mise. Imaginons que le service Panier exige désormais un &lt;code&gt;customerId&lt;/code&gt; pour chaque création de panier.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Dans l&#039;OpenAPI :&lt;/strong&gt; Le champ &lt;code&gt;customerId&lt;/code&gt; est ajouté à la liste &lt;code&gt;required&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Après régénération Jane :&lt;/strong&gt; La signature du constructeur de la classe &lt;code&gt;Panier&lt;/code&gt; change radicalement.
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Avant :&lt;/em&gt; &lt;code&gt;public function __construct(string $uuid)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Après :&lt;/em&gt; &lt;code&gt;public function __construct(string $uuid, string $customerId)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Impact :&lt;/strong&gt; &lt;strong&gt;Immédiat et visible.&lt;/strong&gt; Partout dans votre code où vous instanciez un &lt;code&gt;Panier&lt;/code&gt; sans ce nouvel argument, &lt;strong&gt;votre code ne fonctionne plus&lt;/strong&gt;. Votre IDE (PhpStorm, VSCode) surligne les erreurs en rouge avant même que vous ne lanciez le moindre test.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Note clé :&lt;/strong&gt; Jane transforme une erreur de &amp;quot;runtime&amp;quot; (qui arriverait en production) en une erreur de &amp;quot;compile-time&amp;quot; (qui arrive sur votre machine).&lt;/p&gt;
&lt;h3&gt;Régénération et impact sur le code : La boucle de sécurité&lt;/h3&gt;
&lt;p&gt;Le workflow de mise à jour devient alors très sécurisant :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Mise à jour du contrat :&lt;/strong&gt; Vous récupérez la nouvelle version de &lt;code&gt;openapi.yaml&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Régénération :&lt;/strong&gt; Vous lancez la génération du code Jane.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Analyse d&#039;impact :&lt;/strong&gt; Vous lancez l&#039;analyse statique (PHPStan, Psalm) ou simplement vos tests.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Correction :&lt;/strong&gt; Jane vous a forcé à voir tous les endroits du code impactés par le changement. Vous ne pouvez pas &amp;quot;oublier&amp;quot; de traiter le nouveau champ obligatoire.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;En résumé, Jane agit comme un &lt;strong&gt;révélateur de dette technique&lt;/strong&gt; immédiat lors des mises à jour d&#039;API.&lt;/p&gt;
&lt;h2&gt;Conclusion : Pourquoi passer à l&#039;approche &amp;quot;Contract-First&amp;quot; avec Jane ?&lt;/h2&gt;
&lt;p&gt;Au fil de cet article, nous avons vu que Jane n&#039;est pas simplement un générateur de code, c&#039;est un changement de paradigme dans la façon de concevoir la communication entre vos services. En plaçant le schéma OpenAPI au centre du jeu, vous gagnez sur quatre tableaux majeurs :&lt;/p&gt;
&lt;h3&gt;Type Safety : L&#039;armure de votre code&lt;/h3&gt;
&lt;p&gt;Fini les array mystérieux qui circulent d&#039;un service à l&#039;autre. Avec Jane, chaque donnée entrante ou sortante est encapsulée dans un objet PHP fortement typé.&lt;/p&gt;
&lt;p&gt;Vous savez exactement ce que vous manipulez : des entiers sont des int, des dates sont des \DateTime. PHPStan et votre IDE vous remercient, et votre code devient auto-documenté par sa structure même.&lt;/p&gt;
&lt;h3&gt;Documentation Vivante&lt;/h3&gt;
&lt;p&gt;Souvent, la documentation est le parent pauvre du projet : écrite au début, jamais mise à jour, et finalement trompeuse.&lt;/p&gt;
&lt;p&gt;Ici, la spécification OpenAPI est votre Source de Vérité. Puisque c&#039;est elle qui génère le code qui tourne en production, elle est de facto toujours à jour.&lt;/p&gt;
&lt;p&gt;Couplée à des outils de visualisation comme &lt;strong&gt;SwaggerUI&lt;/strong&gt; ou &lt;strong&gt;ReDoc&lt;/strong&gt;, cette documentation devient interactive et fiable pour toutes les équipes (frontend, mobile, partenaires). Ce que vous voyez dans la doc est exactement ce que le code attend.&lt;/p&gt;
&lt;h3&gt;Moins de Bugs en Production&lt;/h3&gt;
&lt;p&gt;En transformant les erreurs de runtime (données mal formées, champs manquants) en erreurs de &amp;quot;compile-time&amp;quot; (le code généré change, l&#039;IDE signale l&#039;erreur), vous capturez les bugs le plus tôt possible.&lt;/p&gt;
&lt;p&gt;La validation automatique des réponses agit comme un filet de sécurité : aucune donnée corrompue ne peut pénétrer silencieusement dans votre système pour causer des erreurs en cascade plus loin.&lt;/p&gt;
&lt;h3&gt;Une DX retrouvée&lt;/h3&gt;
&lt;p&gt;C&#039;est peut-être le point le plus important au quotidien. Plus besoin de faire des allers-retours incessants entre le code et une documentation PDF externe.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;L&#039;autocomplétion de l&#039;IDE fonctionne instantanément.&lt;/li&gt;
&lt;li&gt;Les refactorings sont sûrs.&lt;/li&gt;
&lt;li&gt;Les tests sont plus simples à écrire et plus robustes.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Performance accrue&lt;/h3&gt;
&lt;p&gt;Au-delà de la sécurité et du confort, le gain de performance est significatif. Le code généré par Jane étant spécifique à vos modèles, il est beaucoup plus performant que l&#039;utilisation générique de l&#039;&lt;code&gt;ObjectNormalizer&lt;/code&gt; du Serializer de Symfony. En évitant d&#039;utiliser la Reflection lors de l&#039;exécution de votre code, vos applications consomment moins de ressources et répondent plus vite.&lt;/p&gt;
&lt;p&gt;&lt;u&gt;Le mot de la fin :&lt;/u&gt;
Arrêtez d&#039;écrire vos clients HTTP et vos DTOs à la main. Laissez Jane faire, de manière optimisée, le travail répétitif et concentrez-vous sur ce qui a de la valeur : votre logique métier.&lt;/p&gt;

        </content>
    </entry>    <entry>
        <id>https://jolicode.com/blog/arretez-de-deviner-interceptez-vos-flux-http-s-avec-mitmproxy</id>
        <published>2025-12-04T13:42:00+01:00</published>
        <updated>2025-12-04T13:42:00+01:00</updated>
        <link type="text/html" rel="alternate" href="https://jolicode.com/blog/arretez-de-deviner-interceptez-vos-flux-http-s-avec-mitmproxy"/>
        <title>Arrêtez de deviner : Interceptez vos flux HTTP(s) avec MITMProxy</title>
        <author>
            <name>JoliCode Team</name>
            <uri>https://jolicode.com/</uri>
        </author>            <category term="proxy" />            <category term="ssl" />            <category term="ops" />        <summary><![CDATA[Vous récupérez une codebase inconnue, vous savez qu’elle effectue des requêtes HTTP(s), mais vous ne savez pas vers où, ni ce qu&#039;elle envoie ? C&#039;est une situation classique. Nous allons voir comment intercepter…]]></summary>
        <content type="html">
            &lt;p&gt;Vous récupérez une codebase inconnue, vous savez qu’elle effectue des requêtes HTTP(s), mais vous ne savez pas vers où, ni ce qu&#039;elle envoie ? C&#039;est une situation classique. Nous allons voir comment intercepter et analyser ce trafic simplement.&lt;/p&gt;
&lt;p&gt;Pour cela, nous utiliserons un &lt;strong&gt;proxy&lt;/strong&gt;. C&#039;est un intermédiaire qui se place entre votre code et le monde extérieur. Nous parlons de &amp;quot;Man In The Middle&amp;quot; (MITM). Non, aucun rapport avec Malcolm. Quoique... c&#039;est exactement le principe : nous nous mettons au milieu pour écouter.&lt;/p&gt;
&lt;p&gt;L&#039;outil du jour s&#039;appelle &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://www.mitmproxy.org/&quot;&gt;&lt;strong&gt;mitmproxy&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Installation de mitmproxy&lt;/h2&gt;
&lt;p&gt;C&#039;est très simple. On récupère l&#039;archive, on décompresse et on déplace les binaires.&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;# Download the binary (check https://www.mitmproxy.org/downloads/ for latest version)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;wget&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; https://downloads.mitmproxy.org/12.2.1/mitmproxy-12.2.1-linux-x86_64.tar.gz&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; -O&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; mitm.tgz&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;# Extract and cleanup&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;tar&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; xzf&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; mitm.tgz&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;rm&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; mitm.tgz&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;# Move binaries to a global path&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;sudo&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; mv&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; mitm&lt;/span&gt;&lt;span class=&quot;syntax-11&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; /usr/local/bin&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;L&#039;archive contient trois binaires. Nous utiliserons ici &lt;code&gt;mitmweb&lt;/code&gt;, car il fournit une interface graphique web idéale pour débuter.&lt;/p&gt;
&lt;h2&gt;Exécution de mitmweb&lt;/h2&gt;
&lt;p&gt;Démarrons le proxy avec deux options explicites :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;--web-port&lt;/code&gt; : le port de l’interface d&#039;administration web.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--listen-port&lt;/code&gt; : le port d&#039;écoute du proxy (par où transitent les requêtes).&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;mitmweb&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; --web-port&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; 9999&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; --listen-port&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; 8888&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ouvrez votre navigateur sur : &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;http://127.0.0.1:9999/&quot;&gt;http://127.0.0.1:9999/&lt;/a&gt;.
&lt;em&gt;Note : Jetez un œil aux logs de &lt;code&gt;mitmweb&lt;/code&gt;, un token de sécurité peut être requis.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Premier test en HTTP&lt;/h2&gt;
&lt;p&gt;La plupart des clients HTTP (curl, symfony/http-client, clients Go/Rust...) respectent une convention : si la variable d&#039;environnement &lt;code&gt;http_proxy&lt;/code&gt; existe, le trafic transite par celle-ci.&lt;/p&gt;
&lt;p&gt;Testons avec curl :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;# Send a request through the proxy&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;http_proxy&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;http://localhost:8888&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; curl&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; http://perdu.com&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;html&gt;&amp;#x3C;head&gt;&amp;#x3C;title&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;Vous Etes Perdu &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;?&amp;#x3C;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;/title&gt;&amp;#x3C;/head&gt;&amp;#x3C;body&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;[...]&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;/body&gt;&amp;#x3C;/html&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;picture class=&quot;js-dialog-target&quot; data-original-url=&quot;/media/original/2025/mitmproxy/mitm-proxy-http.png&quot; data-original-width=&quot;1713&quot; data-original-height=&quot;1141&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;/media/cache/content-webp/2025/mitmproxy/mitm-proxy-http.18f67d25.webp&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;/media/cache/content/2025/mitmproxy/mitm-proxy-http.png&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;width: 996px; ; aspect-ratio: calc(1713 / 1141)&quot; src=&quot;https://jolicode.com//media/cache/content/2025/mitmproxy/mitm-proxy-http.png&quot; alt=&quot;mitmproxy en HTTP&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Si nous regardons l&#039;interface web, nous verrons la requête et sa réponse. C’est propre, efficace. Mais le HTTP clair, c&#039;est de l&#039;histoire ancienne. Passons aux choses sérieuses.&lt;/p&gt;
&lt;h2&gt;Comment proxifier du HTTPS ?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;mitmproxy&lt;/code&gt; gère le HTTPS nativement. Essayons la même méthode :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;https_proxy&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;http://127.0.0.1:8888&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; curl&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; https://perdu.com&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;curl:&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (60) SSL certificate problem: unable to get &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;local&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; issuer certificate&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;[...]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Échec.&lt;/strong&gt; curl refuse la connexion. Pourquoi ?&lt;/p&gt;
&lt;p&gt;&lt;code&gt;mitmproxy&lt;/code&gt; n&#039;est pas un simple passe-plat. Pour pouvoir nous montrer le contenu chiffré, il doit le déchiffrer. Pour ce faire, il &amp;quot;termine&amp;quot; la connexion SSL en se faisant passer pour le serveur final (&lt;code&gt;perdu.com&lt;/code&gt;), puis initie une nouvelle requête vers le vrai serveur.&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;js-dialog-target&quot; data-original-url=&quot;/media/original/2025/mitmproxy/proxy.png&quot; data-original-width=&quot;2537&quot; data-original-height=&quot;1378&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;/media/cache/content-webp/2025/mitmproxy/proxy.29649d4f.webp&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;/media/cache/content/2025/mitmproxy/proxy.png&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;width: 996px; ; aspect-ratio: calc(2537 / 1378)&quot; src=&quot;https://jolicode.com//media/cache/content/2025/mitmproxy/proxy.png&quot; alt=&quot;schema d’un proxy HTTP&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;C&#039;est une véritable attaque &amp;quot;Man In The Middle&amp;quot;. Sauf que curl est bien élevé : il vérifie le certificat du serveur. Comme &lt;code&gt;mitmproxy&lt;/code&gt; signe les réponses avec son propre certificat (inconnu de votre système), curl panique et coupe tout. Heureusement !&lt;/p&gt;
&lt;h3&gt;Solution 1 : Le mode &amp;quot;Bourrin&amp;quot; (Insecure)&lt;/h3&gt;
&lt;p&gt;Nous pouvons demander au client d&#039;ignorer la sécurité.&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;# -k or --insecure allows insecure server connections&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;curl&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; -k&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; --proxy&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; http://127.0.0.1:8888&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; https://perdu.com&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;C&#039;est utile pour un test rapide, mais &lt;strong&gt;dangereux&lt;/strong&gt;. De plus, ce n&#039;est pas toujours possible (binaire compilé, code tiers sans option de configuration facile, etc.).&lt;/p&gt;
&lt;p&gt;Exemple pratique avec Git :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;# Check remote tags without cloning, bypassing SSL verification is risky but possible&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;git&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; -c&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &quot;http.proxy=127.0.0.1:8888&quot;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; ls-remote&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; --tags&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; https://github.com/twigphp/twig&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;fatal:&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; unable&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; to&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; access&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;https://github.com/twigphp/twig/&#039;:&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; server&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; certificate&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; verification&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; failed.&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; CAfile:&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; none&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; CRLfile:&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; none&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;Note : nous aurions pu passer l’option &lt;code&gt;-c &amp;quot;http.sslVerify=false&amp;quot;&lt;/code&gt;, mais ca n’aide pas l’article 🙈&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Solution 2 : La méthode propre (Trust the CA)&lt;/h3&gt;
&lt;p&gt;Pour que votre système accepte &lt;code&gt;mitmproxy&lt;/code&gt; comme une autorité légitime, nous devons ajouter son certificat racine (CA) à votre magasin de certificats local.&lt;/p&gt;
&lt;p&gt;Au premier lancement, &lt;code&gt;mitmproxy&lt;/code&gt; a généré ses certificats dans &lt;code&gt;~/.mitmproxy/&lt;/code&gt;. Sous Debian/Ubuntu, voici la procédure pour les installer proprement :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Convertir et copier le certificat&lt;/strong&gt; :&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;# Convert PEM to CRT format (if needed by your distro, usually for ca-certificates)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;openssl&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; x509&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; -in&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; ~/.mitmproxy/mitmproxy-ca-cert.pem&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; -inform&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; PEM&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; -out&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; ~/.mitmproxy/mitmproxy.crt&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;# Copy to the CA store directory&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;sudo&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; cp&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; ~/.mitmproxy/mitmproxy.crt&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; /usr/share/ca-certificates/extra&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;# Or for some systems&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;sudo&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; cp&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; ~/.mitmproxy/mitmproxy.crt&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; /usr/local/share/ca-certificates/&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;&lt;strong&gt;Mettre à jour le store&lt;/strong&gt; :&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;# Update the system&#039;s CA bundle&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;sudo&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; update-ca-certificates&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Si nous voyons &lt;code&gt;1 added, 0 removed; done.&lt;/code&gt;, c&#039;est gagné.&lt;/p&gt;
&lt;p&gt;Nous pouvons maintenant lancer vos requêtes HTTPS sans le flag &lt;code&gt;-k&lt;/code&gt;, elles apparaîtront en clair dans &lt;code&gt;mitmweb&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;https_proxy=http://127.0.0.1:8888 curl https://perdu.com
&amp;lt;html&amp;gt;&amp;lt;head&amp;gt;&amp;lt;title&amp;gt;Vous Etes Perdu ?&amp;lt;/title&amp;gt;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;[...]&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;picture class=&quot;js-dialog-target&quot; data-original-url=&quot;/media/original/2025/mitmproxy/mitm-proxy-https.png&quot; data-original-width=&quot;1717&quot; data-original-height=&quot;812&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;/media/cache/content-webp/2025/mitmproxy/mitm-proxy-https.fc79c20c.webp&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;/media/cache/content/2025/mitmproxy/mitm-proxy-https.png&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;width: 996px; ; aspect-ratio: calc(1717 / 812)&quot; src=&quot;https://jolicode.com//media/cache/content/2025/mitmproxy/mitm-proxy-https.png&quot; alt=&quot;mitmproxy en HTTPS&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Vous voilà équipés pour inspecter le trafic sortant de vos applications, même chiffré. Que ce soit pour débugger une API capricieuse, reverse-engineer un SDK tiers mal documenté ou comprendre pourquoi votre CI échoue, mitmproxy lève le voile sur la &amp;quot;boîte noire&amp;quot;.&lt;/p&gt;
&lt;p&gt;Gardez cet outil à portée de main, mais un dernier conseil : n&#039;oubliez pas de nettoyer vos variables d&#039;environnement (&lt;code&gt;unset https_proxy&lt;/code&gt;) une fois le debug terminé. Sinon, vous passerez votre matinée de demain à vous demander pourquoi votre terminal n&#039;a plus accès à Internet !&lt;/p&gt;

        </content>
    </entry></feed>
