<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Jérémy DECOOL (@jdecool), Ingénieur Etudes et Développement à Lyon</title>
        <description></description>
        <link>https://www.jdecool.fr</link>
        <atom:link href="https://www.jdecool.fr/feed/feed.xml" rel="self" type="application/rss+xml" />
        
        
            
                6
                <item>
                    <title>Qu&apos;est-ce qu&apos;un « bon code » à l&apos;ère de l&apos;IA ?</title>
                    <description>&lt;p&gt;La semaine dernière, je faisais référence au mantra “code is cheap”. Depuis, j’ai trouvé le projet “&lt;a href=&quot;https://simonwillison.net/guides/agentic-engineering-patterns/ &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Agentic Engineering Patterns&lt;/a&gt;” de Simon Willison qui documente les patterns pratiques pour travailler et obtenir les meilleurs résultats possibles avec l’IA.&lt;/p&gt;

&lt;p&gt;On y retrouve une section “Writing code is cheap now” avec toute fois une nuance que je partage: “Good code still has a cost”. Ce qu’il y explique a pour moi toujours était vrai même avant l’IA. Écrire du code est facile, mais écrire du bon code, ça l’est moins et c’est ce point qui représente un coût.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Pour lui, un “bon code” a les caractéristiques suivantes:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;il fonctionne réellement: il fait ce qu’il est censé faire, sans bugs majeurs.&lt;/li&gt;
  &lt;li&gt;on sait qu’il fonctionne: il existe des preuves (tests, validation, vérifications) qui inspirent confiance.&lt;/li&gt;
  &lt;li&gt;il résout le bon problème: produire une solution techniquement correcte, mais inutile reste un échec.&lt;/li&gt;
  &lt;li&gt;il gère les erreurs proprement: pas seulement le scénario idéal, mais aussi les cas d’échec, avec des messages exploitables pour le futur debugging.&lt;/li&gt;
  &lt;li&gt;il reste simple et minimal: éviter la complexité inutile et l’“over-engineering”.&lt;/li&gt;
  &lt;li&gt;il est protégé par des tests: pour éviter les régressions quand le code évolue.&lt;/li&gt;
  &lt;li&gt;il est documenté: et la documentation doit rester synchronisée avec le comportement réel du système.&lt;/li&gt;
  &lt;li&gt;il reste évolutif: il ne doit pas compliquer inutilement les changements futurs, tout en évitant d’anticiper des besoins hypothétiques (principe YAGNI).&lt;/li&gt;
  &lt;li&gt;il respecte les qualités non fonctionnelles: sécurité, maintenabilité, observabilité, accessibilité, fiabilité, scalabilité, etc., selon le contexte du projet.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Je vous recommande vivement d’aller jeter un coup d’oeil au projet: &lt;a href=&quot;https://simonwillison.net/guides/agentic-engineering-patterns/ &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;https://simonwillison.net/guides/agentic-engineering-patterns/&lt;/a&gt;&lt;/p&gt;
</description>
                    <pubDate>Sat, 06 Jun 2026 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2026/06/06/qu-est-ce-qu-un-bon-code-a-l-ere-de-l-ia.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2026/06/06/qu-est-ce-qu-un-bon-code-a-l-ere-de-l-ia.html</guid>
                </item>
            
        
            
                7
                <item>
                    <title>Profilez vos tests PHPUnit avec OpenTelemetry</title>
                    <description>&lt;p&gt;Un projet de développement informatique qui grossit, c’est une base de code qui grossit par la même occasion et la batterie de tests qui évolue en conséquence. On ajoute des tests semaine après semaine, les temps d’exécution s’allongent. Et puis, à un moment, la CI s’arrête net: &lt;code&gt;Allowed memory size exhausted&lt;/code&gt;. Le premier réflexe est alors d’augmenter la mémoire allouée à PHP. Et ce cycle peut continuer un certain temps, jusqu’au moment où l’on atteint des seuils critiques.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Je l’ai rencontré dans plusieurs expériences professionnelles. Corriger des problèmes de mémoire est compliqué et l’outillage restreint. On est donc aveugle, on ne sait pas quels tests consomment de la mémoire, lesquels prennent du temps à s’exécuter. Monitorer une application en production est une bonne pratique, pourquoi n’en serait-il pas de même pour nos tests ?&lt;/p&gt;

&lt;p&gt;Pour l’observabilité de nos applications de production, un standard est en train de s’imposer: &lt;a href=&quot;https://opentelemetry.io &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;OpenTelemetry&lt;/a&gt;. Pourquoi, alors, ne pas utiliser ce dernier pour également suivre ce qu’il se passe sur nos tests: produire des traces et des métriques que nous pourrions analyser ?&lt;/p&gt;

&lt;p&gt;Il existe d’ailleurs une bibliothèque pour intégrer de l’observabilité dans nos tests PHPUnit avec OpenTelemetry: &lt;a href=&quot;https://packagist.org/packages/flow-php/phpunit-telemetry-bridge &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;flow-php/phpunit-telemetry-bridge&lt;/a&gt;. Il s’agit d’une extension PHPUnit qui collecte la télémétrie de votre suite de tests et l’exporte vers n’importe quel &lt;em&gt;backend&lt;/em&gt; compatible OTLP.&lt;/p&gt;

&lt;p&gt;Pour l’installer, rien de plus simple:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;composer require --dev flow-php/phpunit-telemetry-bridge&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Il restera ensuite à ajouter la configuration nécessaire dans PHPUnit:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-xml&quot; data-lang=&quot;xml&quot;&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;
&amp;lt;phpunit&amp;gt;
    &amp;lt;!-- ... --&amp;gt;
    &amp;lt;extensions&amp;gt;
        &amp;lt;bootstrap class=&amp;quot;Flow\Bridge\PHPUnit\Telemetry\TelemetryExtension&amp;quot;&amp;gt;
            &amp;lt;parameter name=&amp;quot;service_name&amp;quot; value=&amp;quot;phpunit-opentelemetry&amp;quot;/&amp;gt;
            &amp;lt;parameter name=&amp;quot;transport&amp;quot; value=&amp;quot;curl&amp;quot;/&amp;gt;
            &amp;lt;parameter name=&amp;quot;endpoint&amp;quot; value=&amp;quot;http://localhost:4318&amp;quot;/&amp;gt;
            &amp;lt;parameter name=&amp;quot;emit_traces&amp;quot; value=&amp;quot;true&amp;quot;/&amp;gt;
            &amp;lt;parameter name=&amp;quot;emit_metrics&amp;quot; value=&amp;quot;true&amp;quot;/&amp;gt;
            &amp;lt;parameter name=&amp;quot;emit_test_spans&amp;quot; value=&amp;quot;true&amp;quot;/&amp;gt;
            &amp;lt;parameter name=&amp;quot;emit_test_case_spans&amp;quot; value=&amp;quot;true&amp;quot;/&amp;gt;
            &amp;lt;parameter name=&amp;quot;curl_connect_timeout_ms&amp;quot; value=&amp;quot;1000&amp;quot;/&amp;gt;
            &amp;lt;parameter name=&amp;quot;curl_timeout_ms&amp;quot; value=&amp;quot;2000&amp;quot;/&amp;gt;
        &amp;lt;/bootstrap&amp;gt;
    &amp;lt;/extensions&amp;gt;
&amp;lt;/phpunit&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Une fois configuré, il ne reste plus qu’à lancer vos tests, les données de télémétrie seront automatiquement envoyées, vous permettant ensuite de les visualiser dans des tableaux de bord.&lt;/p&gt;

&lt;center&gt;
    &lt;img src=&quot;/img/blog/20260601-profilez-vos-tests-phpunit-avec-opentelemetry/phpunit-otel-overview.png&quot; alt=&quot;Vue d&apos;ensemble de la télémétrie d&apos;une suite de tests PHPUnit dans un tableau de bord&quot; /&gt;
&lt;/center&gt;

&lt;p&gt;On y retrouvera chaque suite de tests avec le détail de chaque test: sa durée, son empreinte mémoire, son statut.&lt;/p&gt;

&lt;center&gt;
    &lt;img src=&quot;/img/blog/20260601-profilez-vos-tests-phpunit-avec-opentelemetry/phpunit-otel-memory.png&quot; alt=&quot;Consommation mémoire des tests PHPUnit visualisée via OpenTelemetry&quot; /&gt;
&lt;/center&gt;

&lt;p&gt;Quelques points d’attention néanmoins. Instrumenter chaque test a un coût. Cela ne sera pas forcément intéressant sur une petite suite de test. C’est surtout lorsque le projet commence à grossir que ce type de donnée a de la valeur et que vous souhaitez arrêter de naviguer à l’aveugle.&lt;/p&gt;

&lt;p&gt;Et si vous désirez en savoir plus, n’hésitez pas à consulter &lt;a href=&quot;https://flow-php.com/documentation/components/bridges/phpunit-telemetry-bridge/ &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;la documentation de l’outil&lt;/a&gt;.&lt;/p&gt;
</description>
                    <pubDate>Mon, 01 Jun 2026 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2026/06/01/profilez-vos-tests-phpunit-avec-opentelemetry.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2026/06/01/profilez-vos-tests-phpunit-avec-opentelemetry.html</guid>
                </item>
            
        
            
        
            
                8
                <item>
                    <title>L&apos;IA accélère l&apos;écriture du code, pas sa criticité</title>
                    <description>&lt;p&gt;L’une des phrases que j’entends le plus ces derniers temps est: « code is cheap », à savoir que le code ne coûte plus grand-chose à produire et qu’il n’a plus de valeur. Je ne suis que partiellement d’accord avec ça.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;On fait également beaucoup l’analogie du prompt qui génère du code, tout comme le code est transformé en assembleur. Au final, c’est une nouvelle couche d’abstraction qui permet de se concentrer sur l’essentiel, ce que l’on souhaite « produire ».&lt;/p&gt;

&lt;p&gt;Là où je ne suis pas tout à fait d’accord, c’est qu’un compilateur est déterministe. Le même code source produira toujours la même sortie. Ce n’est pas forcément le cas des prompts avec les LLMs. Ces derniers ne sont pas déterministes et, un même prompt peut générer des résultats différents d’une exécution à l’autre. Et cela change fondamentalement la donne.&lt;/p&gt;

&lt;p&gt;Le code généré reste l’artefact final, la source de vérité, ce qui est exécuté, déployée et maintenu. Réduire le code à quelque chose qui n’a pas (ou plus) de valeur, c’est faire un raccourci un peu rapide. L’IA accélère son écriture, mais ne change pas la criticité de ce dernier.&lt;/p&gt;
</description>
                    <pubDate>Wed, 27 May 2026 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2026/05/27/l-ia-accelere-l-ecriture-du-code-pas-sa-criticite.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2026/05/27/l-ia-accelere-l-ecriture-du-code-pas-sa-criticite.html</guid>
                </item>
            
        
            
                9
                <item>
                    <title>La meilleure technologie, c&apos;est celle que l&apos;équipe maîtrise</title>
                    <description>&lt;p&gt;Lorsque l’on doit résoudre un problème technique, il est tentant de chercher la solution la plus adaptée. Celle qui répond le mieux aux contraintes identifiées. Pourtant, il peut arriver que la meilleure technologie ne soit pas celle qui colle parfaitement au problème.&lt;/p&gt;

&lt;p&gt;Choisir une technologie, c’est prendre en compte plusieurs facteurs tels que: les compétences de l’équipe, le temps disponible et la tolérance au risque du projet.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Quand les délais sont contraints, le choix doit l’être aussi. La meilleure technologie devient celle que l’équipe maîtrise. C’est celle avec laquelle on saura avancer rapidement, déboguer efficacement et qui ne nécessitera pas plusieurs semaines ou mois de montée en compétence.&lt;/p&gt;

&lt;p&gt;Introduire une nouvelle technologie, c’est ajouter du risque: temps d’apprentissage, erreurs de jeunesse, mauvaises pratiques liées à la méconnaissance de l’outil. Pour que cela se passe dans de bonnes conditions, il faut du temps pour assimiler les nouveaux concepts, ou un périmètre réduit pour limiter la casse.&lt;/p&gt;

&lt;p&gt;Et ce constat est également vrai à l’ère de l’IA. L’IA ne permet pas de compenser le manque de maîtrise ni le manque de connaissance. Sans expertise, impossible de guider, challenger ou valider ce qui est produit. C’est accumuler du code que personne ne comprend réellement.&lt;/p&gt;

&lt;p&gt;Choisir une techno, ce n’est pas uniquement répondre à un problème technique. C’est prendre en compte l’équipe, son expérience et le contexte du projet. La technologie idéale sur le papier ne vaut rien si personne ne sait la dompter dans les temps impartis.&lt;/p&gt;
</description>
                    <pubDate>Sat, 23 May 2026 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2026/05/23/la-meilleure-technologie-c-est-celle-que-l-equipe-maitrise.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2026/05/23/la-meilleure-technologie-c-est-celle-que-l-equipe-maitrise.html</guid>
                </item>
            
        
            
                10
                <item>
                    <title>Utiliser PHP avec Airflow</title>
                    <description>&lt;p&gt;Je travaille actuellement sur un projet orienté BI pour lequel des scripts d’analyse et de visualisation de données sont écrits en Python et sont orchestrés par &lt;a href=&quot;https://airflow.apache.org &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Apache Airflow&lt;/a&gt;. Airflow est un outil Python, initialement conçu et pensé pour l’écosystème Python. Mais travaillant essentiellement en PHP, je me suis demandé s’il était possible d’y faire tourner des scripts PHP.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;En parcourant la documentation d’Airflow, on peut se rendre compte que l’outil permet bien plus que d’exécuter des scripts Python. Si Python y est fortement intégré, en réalité, Airflow est capable d’exécuter n’importe quelle commande d’un terminal. Il est donc possible d’orchestrer tout type de script, et donc du PHP.&lt;/p&gt;

&lt;p&gt;Les tâches (appelées DAG pour &lt;em&gt;Directed Acyclic Graph&lt;/em&gt;) Airflow sont lancées au travers d’opérateur. Parmi ceux disponibles, l’opérateur &lt;code&gt;BashOperator&lt;/code&gt; permet d’exécuter une commande dans un &lt;em&gt;worker&lt;/em&gt; Airflow sur lequel serait présent PHP et le script à lancer.&lt;/p&gt;

&lt;p&gt;Prenons par exemple le script PHP suivant:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&amp;lt;?php

echo &amp;quot;Hello from PHP script!\n&amp;quot;;
echo &amp;quot;Execution time: &amp;quot; . date(&amp;#39;Y-m-d H:i:s&amp;#39;) . &amp;quot;\n&amp;quot;;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;La création d’un DAG passe par l’écriture du script Python (c’est le seul moment où ce denier est nécessaire). La mise en place de notre tâche PHP pourrait se faire ainsi:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;from airflow import DAG
from airflow.providers.standard.operators.bash import BashOperator
from datetime import datetime

with DAG(
    dag_id=&amp;quot;run_php_script&amp;quot;,
    description=&amp;quot;A DAG that runs a PHP script&amp;quot;,
    schedule=None,
    start_date=datetime(2024, 1, 1),
    catchup=False,
    tags=[&amp;quot;php&amp;quot;],
) as dag:

    run_php_script = BashOperator(
        task_id=&amp;quot;run_php_script&amp;quot;,
        bash_command=&amp;quot;php /path/to/scripts/hello.php&amp;quot;,
    )&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Avec ce simple code Python, Airflow déclenche notre script PHP (qui lui peut être bien plus complexe). Les sorties standard et d’erreur sont automatiquement capturées dans les logs Airflow. On peut également bénéficier de toute la mécanique de &lt;em&gt;retry&lt;/em&gt;, de monitoring et d’alerting Airflow.&lt;/p&gt;

&lt;p&gt;Si vous désirez tester par vous même, vous trouverez tout le nécessaire dans &lt;a href=&quot;https://github.com/jdecool/airflow-php-demo &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;ce dépôt&lt;/a&gt; pour démarrer un environnement Airflow avec tout le nécessaire.&lt;/p&gt;
</description>
                    <pubDate>Mon, 18 May 2026 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2026/05/18/utiliser-php-avec-airflow.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2026/05/18/utiliser-php-avec-airflow.html</guid>
                </item>
            
        
            
        
            
                11
                <item>
                    <title>L&apos;outil n&apos;est pas la compétence</title>
                    <description>&lt;p&gt;L’IA a changé (comme pour beaucoup, je pense) ma manière de travailler. Pas seulement dans ma façon d’écrire du code, mais aussi dans les outils que j’utilise au quotidien.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Je passe aujourd’hui beaucoup plus de temps dans mon terminal et dans des éditeurs comme Zed ou VS Code. Les outils, tels que Claude Code, Codex ou Mistral Vibe ont modifié mes habitudes. L’IDE que j’utilisais de manière systématique est (presque) devenu un outil parmi d’autres.&lt;/p&gt;

&lt;p&gt;J’ai toujours cherché à utiliser l’outil le plus adapté au contexte plus que l’outil qui m’est familier. Sur des sessions de code où l’IA fait l’essentiel du travail, inutile de sortir l’artillerie lourde, un éditeur léger suffit amplement. Par contre, pour des sessions de refactoring ou de debug, l’IDE reste imbattable.&lt;/p&gt;

&lt;p&gt;Ne pas utiliser un IDE est souvent perçu négativement. Pourtant, parmi les meilleurs développeurs que j’ai côtoyés, certains n’utilisaient pas et n’utilisent toujours pas un IDE. Certains utilisent Zed, d’autres VS Code et même Vim.&lt;/p&gt;

&lt;p&gt;Et alors ? Ce n’est pas l’outil qui façonne le développeur.&lt;/p&gt;
</description>
                    <pubDate>Tue, 12 May 2026 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2026/05/12/l-outil-n-est-pas-la-competence.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2026/05/12/l-outil-n-est-pas-la-competence.html</guid>
                </item>
            
        
            
                12
                <item>
                    <title>Faire des requêtes CTE avec Doctrine ORM en PHP</title>
                    <description>&lt;p&gt;J’ai déjà évoqué sur ce blog que &lt;a href=&quot;/blog/2026/02/18/boring-technology-les-bases-de-donnees-relationnelles.html&quot;&gt;le développement “moderne” avec les ORM masque les fonctionnalités avancées des SGBD&lt;/a&gt; au point que dorénavant les développeurs ne maitrisent et ne connaissent guère plus que le classique &lt;code&gt;SELECT ... FROM ... WHERE ...&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Dans les mécanismes méconnus et qui pourtant, pourrait permettre de soulager certains traitements applicatifs, on retrouve les CTE (&lt;em&gt;Common Table Expressions&lt;/em&gt;). Voyons comment les utiliser avec Doctrine ORM en PHP.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Commençons par expliquer ce qu’est une CTE. Il s’agit d’une sous-requête utilisable comme une table temporaire. C’est une technique utile pour décomposer des requêtes complexes, éviter la duplication de sous-sélections ou écrire des requêtes récursives.&lt;/p&gt;

&lt;p&gt;En presque 20 ans d’expérience professionnelle, j’ai très rarement eu l’occasion d’en voir dans le code que je peux être amené à parcourir quotidiennement. Prenons par exemple, un blog où les articles sont rattachés à des catégories, ces dernières organisées sous forme arborescente sous la forme &lt;code&gt;Category(id, label, parent_id)&lt;/code&gt;. Je suis certain que, si l’on demande à un développeur de récupérer toutes les catégories parentes d’une catégorie précise, ce dernier fera le traitement en PHP avec un code ressemblant au suivant:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;// src/Repository/CategoryRepository.php
class CategoryRepository extends ServiceEntityRepository
{
    // ...

    /**
     * @return list&amp;lt;Category&amp;gt;
     */
    function getCatagoriesWithParents(int $categoryId): array
    {
        $category = $this-&amp;gt;categoryRepository-&amp;gt;find($categoryId);

        $categories = [
            $category,
        ];

        while ($parent = $category-&amp;gt;getParent()) {
            $categories[] = $parent;
        }

        return $categories;
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;L’inconvénient majeur ici est qu’en interne, Doctrine va faire une requête à chaque tour de boucle. C’est ce que l’on appelle &lt;a href=&quot;/blog/2015/12/17/le-probleme-n+1.html&quot;&gt;le problème N+1&lt;/a&gt;. Pour résoudre ce problème, via une requête SQL, il n’est pas possible de faire un simple &lt;code&gt;SELECT ... FROM ... WHERE&lt;/code&gt;, c’est exactement dans ce cas qu’une CTE récursive est utile. Cette dernière peut être écrite de la manière suivante:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;WITH RECURSIVE cte_category AS (
    -- point de départ
    SELECT id, label, parent_id, 0 AS depth
    FROM category
    WHERE id = :id

    UNION ALL

    -- récursivité
    SELECT c.id, c.label, c.parent_id, cp.depth + 1
    FROM category c
    INNER JOIN cte_category cp ON c.id = cp.parent_id
)
SELECT id, label, parent_id FROM cte_category
ORDER BY depth DESC&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;La requête commence par récupérer la catégorie identifiée par &lt;code&gt;:id&lt;/code&gt;, puis se joint récursivement à elle-même en remontant via &lt;code&gt;parent_id&lt;/code&gt; jusqu’à ce qu’il n’y ait plus de parent. La colonne &lt;code&gt;depth&lt;/code&gt; permet ensuite de trier du plus ancien ancêtre jusqu’à la catégorie cible.&lt;/p&gt;

&lt;p&gt;Le problème pour faire cette requête avec un ORM tel que Doctrine, c’est que ce dernier ne permet pas de gérer les CTE nativement au travers de son &lt;code&gt;QueryBuilder&lt;/code&gt; ni même en &lt;code&gt;DQL&lt;/code&gt;. Il est alors nécessaire de passer par une requête native où le résultat final sera mappé sur un objet.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;// src/Repository/CategoryRepository.php
class CategoryRepository extends ServiceEntityRepository
{
    // ...

    public function findAllWithParents(int $categoryId)
    {
        $rsm = new ResultSetMappingBuilder($this-&amp;gt;getEntityManager());
        $rsm-&amp;gt;addRootEntityFromClassMetadata(Category::class, &amp;#39;c&amp;#39;);

        $sql = &amp;lt;&amp;lt;&amp;lt;SQL
            WITH RECURSIVE with_categories AS (
                SELECT id, label, parent_id, 0 AS depth
                FROM category
                WHERE id = :id

                UNION ALL

                SELECT c.id, c.label, c.parent_id, cp.depth + 1
                FROM category c
                INNER JOIN with_categories cp ON c.id = cp.parent_id
            )
            SELECT id, label, parent_id FROM with_categories
            ORDER BY depth DESC
        SQL;

        return $this-&amp;gt;getEntityManager()
            -&amp;gt;createNativeQuery($sql, $rsm)
            -&amp;gt;setParameter(&amp;#39;id&amp;#39;, $categoryId)
            -&amp;gt;getResult();
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Dans le code précédent, le &lt;code&gt;ResultSetMappingBuilder&lt;/code&gt; se charge de faire correspondre les colonnes retournées avec les propriétés de l’entité &lt;code&gt;Category&lt;/code&gt;. On obtient alors en sortie une liste d’instances de &lt;code&gt;Category&lt;/code&gt; ordonnés de la racine jusqu’à la catégorie demandée.&lt;/p&gt;

&lt;p&gt;Avec ce type de requête, on peut faire énormément de choses, il est par exemple, possible de récupérer tous les articles appartenant à une catégorie enfant en réutilisant la requête précédente:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;WITH RECURSIVE category_path AS (
    SELECT id, label, parent_id
    FROM category
    WHERE id = :id

    UNION ALL

    SELECT c.id, c.label, c.parent_id
    FROM category c
    INNER JOIN category_path cp ON c.id = cp.parent_id
)
SELECT a.id, a.title, a.category_id
FROM article a
INNER JOIN category_path cp ON a.category_id = cp.id
ORDER BY a.published_date DESC&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;En une seule requête, on récupère tous les articles liés à la catégorie cible ou à n’importe lequel de ses ancêtres. Sans CTE récursive, il faudrait soit faire plusieurs requêtes successives pour parcourir la hiérarchie une approche bien moins efficace. Un point d’attention néanmoins, étant donné que l’on exécute une requête SQL native, il faudra faire attention à la portabilité de cette dernière, les CTE pouvant ne pas être disponible sur toutes les bases de données supportées par Doctrine.&lt;/p&gt;

&lt;p&gt;De plus, l’utilisation du &lt;code&gt;ResultSetMappingBuilder&lt;/code&gt; nécessite que le &lt;code&gt;SELECT&lt;/code&gt; de la requête contienne l’ensemble des colonnes nécessaire à l’alimentation de l’objet. À défaut, l’entité sera hydratée de manière incomplète sans qu’aucune erreur explicite ne soit levée.&lt;/p&gt;
</description>
                    <pubDate>Sat, 09 May 2026 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2026/05/09/faire-des-requetes-cte-avec-doctrine-orm-en-php.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2026/05/09/faire-des-requetes-cte-avec-doctrine-orm-en-php.html</guid>
                </item>
            
        
            
        
            
                13
                <item>
                    <title>Être développeur, c&apos;est résoudre des problèmes, pas écrire du code</title>
                    <description>&lt;p&gt;Je constate que de plus en plus de développeurs s’interrogent sur leur avenir face à l’IA. Je ne parle pas de simplement s’inquiéter de la perte de leur emploi, mais une remise en question qui va jusqu’à changer de métier.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Ce n’est pas mon cas. Ce qui m’a toujours attiré, c’est de résoudre des problèmes, de réfléchir à une solution, de faire émerger des idées et aller jusqu’à sa concrétisation. Et c’est dans ce cheminement que l’IA prend place, elle permet d’implémenter et de challenger les solutions pensées.&lt;/p&gt;

&lt;p&gt;C’est cette réflexion et le processus lié qui m’intéressent. Écrire le code était un des moyens d’y arriver, mais pas une fin en soi (même si j’aimais bien cette partie). Et je pense que c’est ce qui change tout.&lt;/p&gt;

&lt;p&gt;Si, en tant que développeur, la seule chose qui vous animait, c’était le code en lui-même, alors je comprends que l’IA remet en question un bon nombre de choses.&lt;/p&gt;

&lt;p&gt;L’IA (tel qu’elle existe aujourd’hui tout du moins) ne remplace pas les développeurs. Elle ne remplace pas le jugement, la capacité à comprendre un problème de fond, à choisir la bonne approche et à concevoir une architecture qui tient dans le temps. Ce sont ces compétences qui ont toujours compté et qui comptent toujours autant, voire même plus qu’auparavant.&lt;/p&gt;
</description>
                    <pubDate>Wed, 06 May 2026 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2026/05/06/etre-developpeur-c-est-resoudre-des-problemes-pas-ecrire-du-code.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2026/05/06/etre-developpeur-c-est-resoudre-des-problemes-pas-ecrire-du-code.html</guid>
                </item>
            
        
            
                14
                <item>
                    <title>Se former à un nouveau langage à l&apos;ère de l&apos;IA</title>
                    <description>&lt;p&gt;C’est une habitude que j’ai depuis longtemps, je m’intéresse de manière plus ou moins assidue, chaque année à un langage de programmation autre que mon langage principal que j’utilise professionnellement. &lt;a href=&quot;https://www.ruby-lang.org &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Ruby&lt;/a&gt;, &lt;a href=&quot;https://elixir-lang.org &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Elixir&lt;/a&gt;, &lt;a href=&quot;https://rust-lang.org &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Rust&lt;/a&gt;, &lt;a href=&quot;https://go.dev &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Go&lt;/a&gt;, &lt;a href=&quot;https://www.python.org &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Python&lt;/a&gt; ou encore &lt;a href=&quot;https://www.typescriptlang.org &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Typescript&lt;/a&gt;. Cette année, ça sera &lt;a href=&quot;https://learn.microsoft.com/dotnet/csharp/ &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;C#&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Mais à une période où l’intelligence artificielle écrit de plus en plus de code à notre place, est-ce qu’apprendre et explorer un nouveau langage a-t-il encore du sens ?&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Pour moi, la réponse est clairement oui. Oui l’IA est omniprésente dans notre quotidien aussi bien personnel que professionnel. Je l’utilise pour explorer et générer du code, me challenger sur des solutions et/ou architectures techniques, accélérer certaines implémentations de code ou de prototype. Mais le problème que l’on constate de plus en plus, c’est la perte de connaissance, de maitrise des concepts de bases de notre métier, autrement dit, au plus on utilise l’IA et au plus nos compétences/connaissances se réduisent.&lt;/p&gt;

&lt;p&gt;Il est donc essentiel de rester actif, d’entretenir notre savoir-faire et d’apprendre un nouveau langage est un des moyens pour que les bases restent ancrées de mon esprit. Même si écrire des lignes de code manuellement tend à disparaître, c’est quelque chose que j’ai toujours aimé. Réfléchir à ce que je souhaite mettre en place, modéliser un problème et mettre en place une solution. Cela me permet de conserver ma capacité de jugement de la qualité de code produit par l’IA.&lt;/p&gt;

&lt;p&gt;De plus, apprendre un nouveau langage est l’occasion de voir ce qui se fait ailleurs, de découvrir d’autres principes, d’autres pratiques. Chaque langage a une philosophie et ça permet parfois d’aborder des problèmes différemment.&lt;/p&gt;

&lt;p&gt;J’ai choisi C# cette année, car c’est un langage qui a beaucoup évolué et qui, malgré le fait qu’il ne soit plus cantonné à l’environnement Microsoft et Windows, souffre toujours de cette image. Ce sera l’occasion de me faire ma propre idée. Au delà du langage lui-même, je fais beaucoup de veille et je vois régulièrement des développeurs autour de cet écosystème partager un tas de choses intéressantes. J’ai l’impression qu’il y a une culture très “craft” qui se dégage de la communauté et que j’apprécie.&lt;/p&gt;

&lt;p&gt;Alors oui, l’IA peut écrire du code à ma place, elle peut l’écrire en C#, elle peut générer une application dans n’importe quel langage. Mais comment conserver un esprit critique, comment prendre du recul sur ce qu’elle a produit, si on n’est pas capable ou si l’on a oublié nos bases fondamentales. C’est aussi ce que je recherche quand j’apprends ou que je pars à la découverte d’un nouvel écosystème. Parce que je pense que je ne le répéterai jamais assez, mais connaitre les fondamentaux à l’ère de l’IA, c’est primordial pour ne pas décrocher.&lt;/p&gt;
</description>
                    <pubDate>Sat, 02 May 2026 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2026/05/02/se-former-a-un-nouveau-langage-a-l-ere-de-l-ia.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2026/05/02/se-former-a-un-nouveau-langage-a-l-ere-de-l-ia.html</guid>
                </item>
            
        
            
        
            
                15
                <item>
                    <title>Quand personne ne répare la CI, ce n&apos;est pas un bug. C&apos;est un choix.</title>
                    <description>&lt;p&gt;J’ai connu des équipes dans lesquels, quand on arrivait le matin, on pouvait constater que la branche principale était cassée depuis plusieurs jours. La CI (eg. l’intégration continue) échouait en continu, et personne dans l’équipe ne semblait s’en inquiéter.&lt;/p&gt;

&lt;p&gt;Quand on vit cette situation, ce n’est plus un problème technique, c’est un problème de culture.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Quand une CI cassée devient la norme (et ça arrive plus souvent qu’on ne le croit), c’est que l’équipe a progressivement appris à l’ignorer. J’en ai déjà parlé plusieurs fois, mais ce que l’on tolère devient un standard.&lt;/p&gt;

&lt;p&gt;Une pipleine rouge que personne ne corrige, c’est un signal silencieux envoyé à toute l’équipe: “ce n’est pas grave”. Et ce signal s’installe durablement.&lt;/p&gt;

&lt;p&gt;Pourtant, une intégration continue est le filet de sécurité collectif. C’est ce qui garantit que le code partagé est dans un état stable et déployable à tout moment. Laisser ce filet trouvé, c’est avancer sans protection en espérant que rien ne tombe.&lt;/p&gt;

&lt;p&gt;Le problème n’est pas la CI qui casse, cela peut arriver et c’est presque normal et rassurant. Ce qui est inquiétant, c’est l’absence de réaction. Une branche principale cassée devrait déclencher une mobilisation immédiate, pas une résignation silencieuse.&lt;/p&gt;

&lt;p&gt;Restaurer l’état de la branche principale devrait être LA priorité numéro une de toute équipe. C’est une règle simple, mais elle en dit beaucoup sur la manière dont une équipe traite la qualité au quotidien.&lt;/p&gt;

&lt;p&gt;Une CI verte en permanence, ce n’est pas du perfectionnisme. C’est l’assurance de travailler sereinement et de livrer avec un minimum de confiance.&lt;/p&gt;
</description>
                    <pubDate>Wed, 29 Apr 2026 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2026/04/29/quand-personne-ne-repare-la-ci-ce-n-est-pas-un-bug-c-est-un-choix.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2026/04/29/quand-personne-ne-repare-la-ci-ce-n-est-pas-un-bug-c-est-un-choix.html</guid>
                </item>
            
        
            
                16
                <item>
                    <title>L&apos;IA accélère la valeur et les erreurs</title>
                    <description>&lt;p&gt;L’IA permet de livrer plus vite, même parfois beaucoup plus vite. Mais livrer vite, ce n’est pas sans conséquences. Car si l’IA permet d’aller plus vite, elle accentue aussi les erreurs et les problèmes qui vont avec.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Un mauvais choix d’architecture, une logique métier mal comprise, une dette technique ignorée: tout cela va arrive désormais plus vite, à plus grande échelle et donc avec des impacts potentiellement plus importants.&lt;/p&gt;

&lt;p&gt;L’IA est un accélérateur, elle amplifie le bon comme le mauvais. C’est pour cela qu’il est important d’avoir des équipes avec des bases solides, une vision claire et une organisation bien structurée. Une équipe sans fondation, c’est aller dans le mur plus vite.&lt;/p&gt;

&lt;p&gt;L’avènement de l’IA ne rend pas les fondamentaux obsolètes, elle les rend indispensables. Organisation, vision, qualité et clarté sont les éléments à faire émerger dans les organisations.&lt;/p&gt;
</description>
                    <pubDate>Wed, 22 Apr 2026 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2026/04/22/l-ia-accelere-la-valeur-et-les-erreurs.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2026/04/22/l-ia-accelere-la-valeur-et-les-erreurs.html</guid>
                </item>
            
        
            
                17
                <item>
                    <title>La qualité est une responsabilité collective</title>
                    <description>&lt;p&gt;Dans la plupart des équipes où je suis passé, les responsabilités étaient souvent fortement découpées. Le product owner définit, le développeur code, la Q/A valide. Sur le papier, ça semble fonctionner, chacun à son rôle et ses responsabilités sont bien définies. Ce que l’on constate dans la pratique, c’est exactement l’inverse.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Dans cette chaîne de valeur, la qualité est alors l’étape finale. Elle devient un filet de sécurité plutôt qu’une culture d’équipe. Pourquoi le développeur va-t-il s’assurer dans les moindres détails de l’impact de son travail, puisque la validation est la responsabilité du testeur ?&lt;/p&gt;

&lt;p&gt;Les bugs remontent alors tardivement et coutent alors plus cher à corriger. Les uns et les autres vont se renvoyer la responsabilité. Le testeur renvoie la balle au développeur, qui va potentiellement remettre en cause la spécification.&lt;/p&gt;

&lt;p&gt;Pourtant, j’ai eu la chance de travailler dans des équipes où la qualité est ancrée dans la culture de l’équipe. Et je parle bien de l’équipe entière, pas seulement de quelques membres. Et ça change tout !&lt;/p&gt;

&lt;p&gt;Tout le monde travaille main dans la main. Toute l’équipe est intégrée dans le processus de définition et de validation des spécifications produit. Le développeur va intégrer les tests (qu’ils soient automatisés ou manuels) dans son travail en coordination avec les PO et les testeurs. La QA n’intervient pas uniquement en phase finale, mais est intégré en amont pour challenger la conception et les cas à prendre en compte.&lt;/p&gt;

&lt;p&gt;La qualité, ce n’est pas une phase du cycle de développement, c’est une responsabilité partagée. La qualité, ça commence avant même l’écrire des premières lignes de spécifications et ça ne s’arrête qu’une fois la fonctionnalité dans les mains des utilisateurs satisfaits.&lt;/p&gt;
</description>
                    <pubDate>Mon, 20 Apr 2026 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2026/04/20/la-qualite-est-une-responsabilite-collective.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2026/04/20/la-qualite-est-une-responsabilite-collective.html</guid>
                </item>
            
        
            
                18
                <item>
                    <title>Déployer et livrer: et si c&apos;était deux choses différentes ?</title>
                    <description>&lt;p&gt;Dans de nombreuses équipes de développement, déployer c’est livrer des fonctionnalités aux utilisateurs. Pourtant, il est tout à fait possible de dissocier déploiement et livraison. Cela peut même avoir de nombreux avantages.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Déployer du code est un acte technique: on dépose du code sur un serveur. Livrer une fonctionnalité est un acte produit: on ajoute ou modifie le fonctionnement de l’application pour les utilisateurs.&lt;/p&gt;

&lt;p&gt;Dissocier les deux, c’est permettre de déployer fréquemment sans impact utilisateur (ou de manière limitée). C’est pouvoir choisir quelles fonctionnalités livrées et à quel moment. C’est aussi activer une fonctionnalité de manière progressive afin d’observer son impact, ses conséquences en utilisation réelle et ainsi pouvoir relever les éventuels problèmes sur un panel restreint.&lt;/p&gt;

&lt;p&gt;Pour que cela se fasse dans de bonnes conditions, il est essentiel de ne pas basculer l’ensemble des utilisateurs d’un coup, mais d’exposer les changements progressivement. On mesure et on ajuste.&lt;/p&gt;

&lt;p&gt;Cela permet aux équipes de développement de limiter les développements longs en mode tunnel. On peut alors mettre en place des itérations courtes et déployer des changements incomplets. La fonctionnalité finale ne sera activée qu’une fois complètement terminée ou dans un état réellement utilisable par les utilisateurs finaux.&lt;/p&gt;

&lt;p&gt;C’est ainsi que se met en place une stratégie de “Continuous Delivery”. Avoir un code qui soit toujours livrable en production sans accumuler plusieurs semaines de développement. Le déploiement ne doit pas un événement redouté: c’est une opération contrôlée, dont le risque est modéré et facilement réversible ou corrigible.&lt;/p&gt;
</description>
                    <pubDate>Wed, 08 Apr 2026 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2026/04/08/deployer-et-livrer-et-si-c-etait-deux-choses-differentes.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2026/04/08/deployer-et-livrer-et-si-c-etait-deux-choses-differentes.html</guid>
                </item>
            
        
            
                19
                <item>
                    <title>Le biais de nos choix techniques</title>
                    <description>&lt;p&gt;À mon sens, la compétence essentielle d’un développeur, ce n’est pas d’écrire du code (surtout à l’ère de l’IA), mais c’est de savoir choisir la bonne solution au bon problème et de savoir défendre ce choix.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;J’en parle régulièrement, je suis un défenseur des technologies “ennuyantes” (les « boring technologies ») et j’observe un schéma qui se répète régulièrement: lorsqu’une équipe choisit une technologie classique, elle sera remise en cause au premier problème venu. À l’inverse, une solution plus moderne sera rarement questionnée de la même manière.&lt;/p&gt;

&lt;p&gt;Le choix initial biaise le regard que l’on porte sur les difficultés qui suivent. Tout d’abord parce que utiliser une technologie en vogue est intellectuellement stimulant. Mais aussi parce que beaucoup ont l’impression de maitriser sur le bout des doigts les technologies que l’on utilise « tous les jours ». Pourtant, la réalité c’est que nous avons beaucoup à apprendre.&lt;/p&gt;

&lt;p&gt;Ce qui devrait guider une décision technique, ce n’est pas l’attrait d’une technologie ni l’envie qu’elle soit valorisante sur un CV. Ce devrait être le problème à résoudre, les compétences de l’équipe en place et la question de savoir si les outils actuels ont réellement été explorés dans leur profondeur.&lt;/p&gt;
</description>
                    <pubDate>Wed, 01 Apr 2026 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2026/04/01/le-biais-de-nos-choix-techniques.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2026/04/01/le-biais-de-nos-choix-techniques.html</guid>
                </item>
            
        
            
                20
                <item>
                    <title>Déploiement continu et cycles de développement longs</title>
                    <description>&lt;p&gt;Lorsque l’on travaille sur des applications web orientées SaaS, on a la chance de pouvoir livrer rapidement et régulièrement de nouvelles fonctionnalités aux utilisateurs. C’est ce qui se cache derrière les concepts de livraison ou de déploiement continu. Pourtant, beaucoup d’équipes font des mises en production qui concentrent des semaines de développement. Et c’est à mon sens un problème.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Plus un déploiement est rare, plus il embarque de changements et plus le risque d’incidents augmente. Le volume de modifications impliqué peut drastiquement complexifier l’identification et la correction des bugs, car les développements liés peuvent parfois remonter à plusieurs semaines ou mois.&lt;/p&gt;

&lt;p&gt;Les utilisateurs en sont les premiers impactés: apparition de nombreux bugs du fait de l’accumulation des développements et nouvelles fonctionnalités qui arrivent tardivement.&lt;/p&gt;

&lt;p&gt;Côté développeur, les cycles longs sont souvent liés à des branches de développement avec une longue durée de vie. Si le travail est isolé le temps de conception, c’est lorsqu’il faut tout mettre en commun que les choses se compliquent: conflits de code à gérer et effets de bord potentiellement détectés à la dernière minute.&lt;/p&gt;

&lt;p&gt;La réponse à ce problème est peut-être contre-intuitive, mais elle réside dans le fait de déployer plus souvent, des changements progressifs et incrémentaux. Les petits changements sont alors plus faciles à surveiller et à corriger. De plus, l’impact d’un bug se fait sur une surface réduite.&lt;/p&gt;

&lt;p&gt;Déployer plus souvent, c’est également se poser moins de questions sur les potentiels impacts de ce que l’on va livrer. J’ai, par exemple, vu des équipes devoir « patcher » la version d’un outil datant de plusieurs mois et être effrayées à l’idée de devoir déployer les nouveaux changements.&lt;/p&gt;

&lt;p&gt;Il est pourtant possible de limiter les erreurs. Il existe de nombreuses techniques permettant de livrer du code progressivement. On peut, par exemple, évoquer le mécanisme de « features flags » qui permet d’activer (ou non), de manière progressive (ou non), des fonctionnalités pour un périmètre d’utilisateur défini.&lt;/p&gt;

&lt;p&gt;Il ne faut pas oublier que moins on déploie et plus on a peut de déployer. Et plus on a peur de déployer, moins on déploie. Il est donc essentiel de livrer petit et souvent.&lt;/p&gt;
</description>
                    <pubDate>Wed, 25 Mar 2026 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2026/03/25/deploiement-continu-et-cycles-de-developpement-longs.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2026/03/25/deploiement-continu-et-cycles-de-developpement-longs.html</guid>
                </item>
            
        
            
                21
                <item>
                    <title>Période difficile pour les conférences développeur</title>
                    <description>&lt;p&gt;Avec l’avènement de l’IA, notre métier de développement logiciel doit se réinventer et avec lui, son écosystème. Je pense notamment aux conférences pour développeurs.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Cela fait quelque temps que de nombreuses conférences, qui sont essentielles à notre métier, étaient en difficulté. Avec une économie compliquée, le sponsoring en baisse, difficile de rentrer dans les frais nécessaires à leurs survies.&lt;/p&gt;

&lt;p&gt;Si l’on commence à voir les emplois de « développeurs » chuter, je constate de plus en plus l’arrêt des conférences du fait d’un manque de financement et de participation.&lt;/p&gt;

&lt;p&gt;Les conférences sont des espaces où se transmettre le savoir et les retours d’expérience. C’est également un moment social de rencontre et d’échange. Il est à mon sens essentiel que ce genre d’événement puisse perdurer.&lt;/p&gt;
</description>
                    <pubDate>Wed, 18 Mar 2026 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2026/03/18/periode-difficile-pour-les-conferences-developpeur.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2026/03/18/periode-difficile-pour-les-conferences-developpeur.html</guid>
                </item>
            
        
            
                22
                <item>
                    <title>Les profils T-Shaped</title>
                    <description>&lt;p&gt;Il y a un type de profil qui performe particulièrement bien dans les équipes projet. Des profils spécialisés, mais qui ont un truc en plus: des compétences transverses complémentaires. C’est ce que l’on retrouve derrière la notion de profil “T-Shaped”.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Ce type de profil possède une expertise poussée dans un domaine, à laquelle s’ajoute un large base de connaissances et de compétences transverses.&lt;/p&gt;

&lt;p&gt;Vous en avez sûrement déjà croisé. C’est par exemple un développeur backend capable de comprendre les aspects de l’expérience utilisateur, tout en prenant en compte les contraintes d’infrastructures et les besoins produit. Il ne maîtrise pas tout, mais il a conscience de ce qui l’entoure et des enjeux liés.&lt;/p&gt;

&lt;p&gt;C’est le genre de profil qui limite les frictions et les malentendus lorsque des métiers différents collaborent. Il traduit, facilite et aligne, aidant ainsi l’équipe à prendre des décisions collectives éclairées.&lt;/p&gt;

&lt;p&gt;Cultiver ce type de profil dans une équipe est essentiel. Une équipe trop spécialisée tend à créer des silos par compétence et limite les interactions transverses. Les profils T-Shaped sont ce qui permet de collaborer efficacement sur le long terme.&lt;/p&gt;
</description>
                    <pubDate>Thu, 12 Mar 2026 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2026/03/12/les-profils-t-shaped.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2026/03/12/les-profils-t-shaped.html</guid>
                </item>
            
        
            
                23
                <item>
                    <title>Arrêtez de blâmer vos utilisateurs</title>
                    <description>&lt;p&gt;On a tous des phrases qui nous font réagir intérieurement. Parmi celles qui me font certainement le plus réagir, il y a les réactions du style « la plateforme est plantée, c’est la faute du client, il a fait n’importe quoi ». Parce que dans bien des cas, si le client a pu faire n’importe quoi, c’est parce que l’outil le lui a permis.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Ce n’est pas l’utilisateur le problème, c’est l’absence de contraintes, de validations et de contrôles qui auraient dû empêcher l’applicatif d’atteindre cet état. Un outil ne se contente pas de fonctionner dans les cas nominaux, il « ne fait pas confiance » à l’utilisateur. Il anticipe les erreurs et les mauvaises manipulations. Il doit guider l’utilisateur et l’empêcher de se retrouver dans une situation difficile.&lt;/p&gt;

&lt;p&gt;Je suis convaincu que nombreux sont ceux qui ont des exemples en tête. Un formulaire qui accepte une valeur « non autorisée », un clic sur un bouton de suppression sans confirmation ou encore une API sans restriction d’usage. Autant d’exemples qui autorisent les données à être dans un état incohérent.&lt;/p&gt;

&lt;p&gt;Rejeter la faute sur le client, c’est passer à côté de l’essentiel: l’expérience utilisateur. Les outils que l’on conçoit doivent nous faciliter le quotidien, mais aussi nous guider dans les actions que nous réalisons pour que tout se passe au mieux.&lt;/p&gt;
</description>
                    <pubDate>Fri, 06 Mar 2026 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2026/03/06/arretez-de-blamer-vos-utilisateurs.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2026/03/06/arretez-de-blamer-vos-utilisateurs.html</guid>
                </item>
            
        
            
                24
                <item>
                    <title>Ne confondez pas dette technique et préférence personnelle</title>
                    <description>&lt;p&gt;Quand on évolue dans la tech, on entend souvent parler de « dette technique ». Je trouve ce terme assez galvaudé, car souvent, lorsque l’on me parle de dette technique, ce que l’on m’explique c’est que le code ne correspond pas au « standard » du développeur qui en parle.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Qui n’a jamais rencontré cette situation ? Un développeur arrive sur un projet et découvre une architecture qu’il ne connaît pas, des conventions qui diffèrent de celles qu’il a l’habitude d’utiliser. Bien souvent, on va qualifier ça de « dette technique ». Pourtant le code est fonctionnel, correctement maintenu et répond au besoin utilisateur.&lt;/p&gt;

&lt;p&gt;En informatique et en développement, il n’y a pas de recette universelle. Une méthode ou façon que l’on peut appliquer partout de la même manière. Tout est une question de contexte et d’équipe. Ce n’est pas parce qu’un choix technique diffère de nos habitudes qu’il est mauvais.&lt;/p&gt;

&lt;p&gt;Mais alors la dette technique c’est quoi ? C’est un compromis conscient et assumé entre vitesse et qualité. Autrement dit, c’est un choix délibéré, une solution imparfaite pour livrer du code plus vite avec l’intention d’y revenir plus tard.&lt;/p&gt;

&lt;p&gt;Le problème survient lorsque la dette n’est pas identifiée, ni suivie. Lorsque les compromis établis s’accumulent sans que personne n’en ait conscience. C’est à ce moment que la dette devient toxique, lorsqu’elle devient durable et ralentit les équipes.&lt;/p&gt;

&lt;p&gt;La dette technique, il faut l’identifier et la suivre, mais il ne faut pas confondre un choix intentionnel d’un choix qui diffère de nos pratiques personnelles.&lt;/p&gt;
</description>
                    <pubDate>Wed, 25 Feb 2026 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2026/02/25/ne-confondez-pas-dette-technique-et-preference-personnelle.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2026/02/25/ne-confondez-pas-dette-technique-et-preference-personnelle.html</guid>
                </item>
            
        
            
                25
                <item>
                    <title>Boring technology: les bases de données relationnelles</title>
                    <description>&lt;p&gt;&lt;a href=&quot;/blog/2026/02/12/savoir-jusqu-ou-utiliser-les-boring-technologies.html&quot;&gt;La semaine dernière, j’évoquais les « boring technologies » et le fait qu’elles soient sous-cotées&lt;/a&gt; dans l’esprit collectif. Les bases de données relationnelles et le langage SQL en sont probablement l’exemple le plus courant. Très largement utilisées dans les projets nécessitant de stocker des informations, elles sont pourtant régulièrement boudées par les développeurs au premier problème.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;C’est le constat que je fais depuis plusieurs années et la cause est que de moins en moins de développeurs en maîtrisent le fonctionnement ou ne connaissent réellement le langage SQL. Je ne pense pas que cela soit réellement dû à un manque d’intérêt, je pense surtout que les frameworks et les ORM que nous utilisons au quotidien ont leur part de responsabilité.&lt;/p&gt;

&lt;p&gt;De nombreuses couches d’abstraction sont conçues de manière générique pour permettre d’interchanger rapidement et simplement le système relationnel. Cela se fait au détriment des fonctionnalités avancées, des optimisations spécifiques ou des mécanismes évolués permettant d’améliorer drastiquement les performances de nos applications (colonnes générées, gestion des vues, index partiels, CTE, requêtes récursives, partitionnement, …).&lt;/p&gt;

&lt;p&gt;Cela ne se remarque pas à charge ou volumétrie réduite. Mais dès que l’on change un peu d’échelle, c’est là que les problèmes commencent. Les temps de réponse se dégradent, les performances chutent et si l’on ne s’intéresse pas à ce qu’il se passe sous le capot du SGBD, cela devient très compliqué de s’en sortir.&lt;/p&gt;

&lt;p&gt;Les ORM ne sont pas le problème. Ce sont des outils très pratiques qui permettent de gagner du temps. Mais eux aussi, il convient de les maîtriser pour optimiser les requêtes générées et savoir comment tirer parti des fonctionnalités avancées des bases de données. La plupart d’entre eux ont des couches « bas niveau » et permettent généralement de faire des requêtes SQL directement et éventuellement de « mapper » la réponse dans des objets.&lt;/p&gt;

&lt;p&gt;Comme je le dis régulièrement, il est essentiel, voire indispensable, de s’attacher aux fondamentaux, de comprendre et maîtriser les outils que l’on utilise. Le langage SQL et le fonctionnement des bases de données en font partie. C’est ainsi que l’on peut se donner les moyens de construire des applications qui tiennent la charge sur le long terme sans multiplier les technologies.&lt;/p&gt;
</description>
                    <pubDate>Wed, 18 Feb 2026 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2026/02/18/boring-technology-les-bases-de-donnees-relationnelles.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2026/02/18/boring-technology-les-bases-de-donnees-relationnelles.html</guid>
                </item>
            
        
            
                26
                <item>
                    <title>Simplifier vos objets immuables avec PHP 8.5</title>
                    <description>&lt;p&gt;PHP 8.5 a été publié le 20 novembre 2025 et, dans les nouvelles fonctionnalités proposées par cette version, on trouve notamment la possibilité de mettre à jour des propriétés lors du clonage d’objets. Une amélioration qui va permettre de simplifier nos objets immuables.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;En programmation orientée objet, un objet immuable est un objet dont l’état ne peut pas être modifié après sa création. Ainsi, toute “modification” visant à changer l’état de ce dernier conduit à la création d’une nouvelle instance avec les valeurs mises à jour. Cela représente un réel avantage, car leur état ne changeant jamais, les effets de bord et bugs liés à des modifications inattendues de l’objet sont ainsi limités.&lt;/p&gt;

&lt;p&gt;Avant PHP 8.5, la modification d’un objet immuable pouvait s’avérer fastidieuse, car il était nécessaire de créer une nouvelle instance avec les nouvelles valeurs. Cela pouvait être d’autant plus contraignant si l’objet avait de nombreuses propriétés.&lt;/p&gt;

&lt;p&gt;Par exemple:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;readonly class GPSLocation
{
    public function __construct(
        public float $latitude,
        public float $longitude,
        public float $altitude,
        public DateTimeImmutable $timestamp,
    ) {
    }

    public function withCoordinates(float $latitude, float $longitude): GPSLocation
    {
        return new GPSLocation(
            $latitude,
            $longitude,
            $this-&amp;gt;altitude,
            $this-&amp;gt;timestamp,
        );
    }

    public function withAltitude(float $altitude): GPSLocation
    {
        return new GPSLocation(
            $this-&amp;gt;latitude,
            $this-&amp;gt;longitude,
            $altitude,
            $this-&amp;gt;timestamp,
        );
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;PHP 8.5 nous permet de simplifier cette opération en permettant de cloner l’objet tout en autorisant de mettre à jour certaines propriétés lors du clonage. Seules les propriétés devant être modifiées doivent être précisées:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;readonly class GPSLocation
{
    public function __construct(
        public float $latitude,
        public float $longitude,
        public float $altitude,
        public DateTimeImmutable $timestamp,
    ) {
    }

    public function withCoordinates(float $latitude, float $longitude): GPSLocation
    {
        return clone($this, [
            &amp;#39;latitude&amp;#39; =&amp;gt; $latitude,
            &amp;#39;longitude&amp;#39; =&amp;gt; $longitude,
        ]);
    }

    public function withAltitude(float $altitude): GPSLocation
    {
        return clone($this, [
            &amp;#39;altitude&amp;#39; =&amp;gt; $altitude,
        ]);
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

</description>
                    <pubDate>Mon, 16 Feb 2026 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2026/02/16/simplifier-vos-objets-immuables-avec-php-8-5.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2026/02/16/simplifier-vos-objets-immuables-avec-php-8-5.html</guid>
                </item>
            
        
            
                27
                <item>
                    <title>Ce site est de retour en France</title>
                    <description>&lt;p&gt;Cela fait plusieurs années que je m’intéresse beaucoup à la souveraineté numérique et la dépendance technologique que l’on peut avoir (principalement concentré sur des acteurs américains). J’en parlais dans un précédent article, mais à ce titre, je souhaite réduire ma dépendance aux GAFAM au profit d’acteurs français (ou européens).&lt;/p&gt;

&lt;p&gt;J’ai l’année dernière quitté la galaxie Google pour basculer chez &lt;a href=&quot;https://proton.me/fr &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Proton&lt;/a&gt;. Toutes mes applications et serveurs sont actuellement hébergés chez &lt;a href=&quot;https://www.scaleway.com/fr/ &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Scaleway&lt;/a&gt; avec les noms de domaines gérés par &lt;a href=&quot;https://www.ovhcloud.com/fr/ &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;OVH&lt;/a&gt;.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Mais ce site et quelques autres contenus statiques étaient hébergés depuis plusieurs années sur Github Pages. Pratique et gratuit, mais cela faisait “tâche” pour quelqu’un qui souhaite défendre la souveraineté numérique française.&lt;/p&gt;

&lt;p&gt;Après quelques recherches et recommandations, j’ai trouvé la solution qui me convient parfaitement. Ce site est donc de retour en France, hébergé par &lt;a href=&quot;https://www.alwaysdata.com &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Alwaysdata&lt;/a&gt;, un hébergeur français qui m’a grandement été recommandé. Et je dois dire que je valide complètement ce choix aussi bien par la facilité d’utilisation, de migration que par la qualité et l’offre de services proposés.&lt;/p&gt;

&lt;p&gt;Une migration faite sans difficulté et j’ai maintenant un sentiment de cohérence entre ma volonté de défendre la souveraineté numérique française et les choix que je fais moi-même pour les solutions informatiques que je mets en œuvre.&lt;/p&gt;
</description>
                    <pubDate>Sat, 14 Feb 2026 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2026/02/14/ce-site-est-de-retour-en-france.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2026/02/14/ce-site-est-de-retour-en-france.html</guid>
                </item>
            
        
            
                28
                <item>
                    <title>Savoir jusqu&apos;où utiliser les &quot;boring technologies&quot;</title>
                    <description>&lt;p&gt;Je milite activement pour l’utilisation des « boring technologies », les technologies ennuyeuses, mais maîtrisées par rapport aux solutions « tendances ». Le plus dur étant de savoir où s’arrêter et de déterminer à quel moment il est temps de changer de solutions.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Choisir une technologie « ennuyante », c’est par exemple pousser une base de données relationnelle dans ses retranchements avant de vouloir passer sur une technologie NoSQL. C’est aussi concevoir un monolithe modulaire bien structuré au maximum de ses capacités avant de basculer sur des microservices.&lt;/p&gt;

&lt;p&gt;L’idée dans tout ça, c’est de pousser les technologies jusqu’à leurs limites avant de changer de solution technique. Cela signifie qu’il est essentiel de se former et de se documenter sur ces dernières pour connaitre l’ensemble de leurs capacités et pas forcément s’arrêter à notre cercle de connaissance.&lt;/p&gt;

&lt;p&gt;Le piège, c’est l’anticipation et le marketing. Pour prendre l’exemple de la base de données, bien souvent on démarre avec une base de donnéee relationnelle. Le produit grandit, les données s’accumulent et aux premiers signes de charge, on souhaite passer à l’échelle et introduire une nouvelle technologie.&lt;/p&gt;

&lt;p&gt;Le problème que je vois, c’est qu’on a souvent exploré un minimum de ce que les SGBD peuvent offrir. En tant que développeur, les ORM nous éloignent du monde des bases de données, la couche d’abstraction tue nos compétences SQL et les performances de nos applications.&lt;/p&gt;

&lt;p&gt;Maîtriser les ORM, optimiser des requêtes, ajuster les index, utiliser des fonctionnalités SQL avancées (telle que les CTE, les index fulltext, la mise en cache…). Poncer une technologie jusqu’au bout, c’est accepter de ne pas toujours choisir la solution la plus élégante, mais la plus pragmatique. C’est repousser le moment où l’on va devoir introduire de la complexité supplémentaire.&lt;/p&gt;

&lt;p&gt;Le plus dur dans tout ça, c’est d’identifier le moment où l’on atteint la limite. Quand optimiser coûte plus cher que de changer une partie du système.&lt;/p&gt;

&lt;p&gt;Bien souvent dans ces cas-là, c’est l’expérience et la connaissance approfondie des outils qui font la différence.&lt;/p&gt;
</description>
                    <pubDate>Thu, 12 Feb 2026 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2026/02/12/savoir-jusqu-ou-utiliser-les-boring-technologies.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2026/02/12/savoir-jusqu-ou-utiliser-les-boring-technologies.html</guid>
                </item>
            
        
            
                29
                <item>
                    <title>Siloter les équipes, c&apos;est effriter la cohésion</title>
                    <description>&lt;p&gt;J’évoque régulièrement la thématique du silotage des équipes. Aujourd’hui, j’aimerais revenir sur les barrières invisibles que cela crée pour la collaboration et le partage des connaissances.&lt;/p&gt;

&lt;p&gt;Découper les équipes, les isoler, c’est faire en sorte que ces dernières puissent se concentrer sur leur périmètre. Mais c’est aussi complexifier et réduire les interactions avec les autres équipes. Moins de communication, c’est plus d’incompréhensions. On perd ainsi progressivement la vision globale où les objectifs directs vont primer.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Le fonctionnement en silos freine les opportunités de développement, aussi bien pour l’entreprise que les collaborateurs eux-mêmes. Moins d’interactions, ce sont des connaissances qui ne sont plus partagées, c’est moins de découvertes de compétences différentes.&lt;/p&gt;

&lt;p&gt;Et au final: une cohésion qui s’effrite.&lt;/p&gt;

&lt;p&gt;Pour atténuer ces effets, il est possible d’activer plusieurs leviers. Organiser et favoriser la rotation des membres entre équipes pour casser les habitudes et ouvrir de nouvelles perspectives. Mettre en place des projets transverses nécessitant la collaboration de différentes équipes pour renforcer l’esprit d’équipe et avoir des enjeux partagés.&lt;/p&gt;

&lt;p&gt;Mais surtout, il est indispensable de créer et maintenir des canaux de communication ouverts à tous. Des espaces où chaque équipe peut partager ses idées, ses problématiques et ses apprentissages avec les autres. Et c’est ainsi que pourra se construire une culture de la collaboration et du partage.&lt;/p&gt;
</description>
                    <pubDate>Wed, 04 Feb 2026 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2026/02/04/siloter-les-equipes-c-est-effriter-la-cohesion.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2026/02/04/siloter-les-equipes-c-est-effriter-la-cohesion.html</guid>
                </item>
            
        
            
                30
                <item>
                    <title>La réunion d&apos;équipe la plus importante</title>
                    <description>&lt;p&gt;La réunion d’équipe que je considère comme indispensable est certainement la rétrospective. C’est un point essentiel pour favoriser l’amélioration continue et renforcer la dynamique d’équipe.&lt;/p&gt;

&lt;p&gt;La rétrospective permet de mettre en place un espace dédié pour réfléchir et prendre du recul sur le travail récent. Identifier ce qui a bien ou mal fonctionné. Décider et
mettre en place des pistes d’amélioration pour la suite.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Pour qu’elle se déroule au mieux, il est important de faire des rétrospectives de manière régulière, généralement à la fin de chaque itération. Ne pas faire ce point, c’est manquer l’opportunité de s’améliorer et de corriger les problèmes qui retardent un projet.&lt;/p&gt;

&lt;p&gt;Si ces dernières sont trop espacées les unes des autres, certains aspects ne seront plus frais dans la tête des participants, ce qui occultera des éléments qui pourraient être essentiels.&lt;/p&gt;

&lt;p&gt;Il est également fondamental de prévoir le temps nécessaire pour avoir des discussions significatives et permettre d’approfondir les points abordés. Cela ne pourra se faire efficacement que via la mise en place d’un cadre et d’une structure telle que « Démarrer, Arrêter, Continuer » dans lequel chaque participant pourra donner son point de vue.&lt;/p&gt;

&lt;p&gt;Et bien entendu, la rétrospective ne s’arrête pas une fois la réunion terminée. Dans le cadre de l’amélioration continue, il sera nécessaire de suivre les actions décidées d’une rétrospective à une autre.&lt;/p&gt;

&lt;p&gt;La rétrospective est donc pour moi le rituel qui, bien mené, permet de transformer les difficultés rencontrées en axes d’amélioration. Ne pas prendre le temps de regarder en arrière, c’est avancer avec des œillères et potentiellement aller dans le mur.&lt;/p&gt;
</description>
                    <pubDate>Fri, 30 Jan 2026 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2026/01/30/la-reunion-d-equipe-la-plus-importante.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2026/01/30/la-reunion-d-equipe-la-plus-importante.html</guid>
                </item>
            
        
            
                31
                <item>
                    <title>Documentation vivante</title>
                    <description>&lt;p&gt;N’importe qui ayant déjà écrit de la documentation sait que le principal problème, c’est qu’elle devient rapidement obsolète une fois écrite.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Il n’y a qu’à parcourir les README des projets sur lesquels vous travaillez pour probablement y trouver des informations qui ne correspondent plus à la réalité du projet. Il y également fort à parier que vous ayez déjà vu des schémas d’architecture qui décrivent un système qui n’existe plus. Ou alors des procédures d’installation qui échouent, car plus à jour.&lt;/p&gt;

&lt;p&gt;Pour éviter ces décalages, il est intéressant de créer une documentation capable de vivre et d’évoluer avec le projet. C’est le concept de “Living Documentation”. L’idée est simple: plutôt que de maintenir manuellement des documents décorrélés du code, la documentation est directement générée à partir de ce dernier.&lt;/p&gt;

&lt;p&gt;C’est moins compliqué qu’il n’y paraît, et vous avez probablement les premières briques nécessaires à sa mise en place. Les tests peuvent décrire les spécifications implémentées. Des annotations peuvent servir à générer de la documentation technique. Les diagrammes d’architecture peuvent être générés à partir de l’analyse du code source. Il est possible d’automatiser énormément de choses, encore plus aujourd’hui à l’ère des IA génératives.&lt;/p&gt;

&lt;p&gt;L’approche de “Living Documentation” permet ainsi d’avoir une documentation toujours à jour, une source de vérité fiable au cours du temps. Pour y parvenir, il est essentiel que la gestion de la documentation soit intégrée dans le processus de développement, que cela fasse partie de la culture d’équipe.&lt;/p&gt;

&lt;p&gt;Une documentation qui ne suit pas le rythme du code est vouée à mourir. Investir dans une documentation vivante, c’est s’assurer de sa pérennité et d’améliorer durablement la maintenabilité de vos projets.&lt;/p&gt;
</description>
                    <pubDate>Wed, 21 Jan 2026 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2026/01/21/une-documentation-vivante.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2026/01/21/une-documentation-vivante.html</guid>
                </item>
            
        
            
                32
                <item>
                    <title>La formulation d&apos;un message n&apos;est pas un détail</title>
                    <description>&lt;p&gt;La manière dont on transmet un message est tout aussi importante que le fond du message lui-même.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Les équipes techniques discutent et débattent régulièrement d’idées et de concepts, de différentes solutions pour répondre à un problème. Une proposition d’architecture mal formulée, un retour de revue de code maladroit ou un feedback technique confus et le message est atténué. Le fond s’efface au profit de la forme.&lt;/p&gt;

&lt;p&gt;Le résultat: des personnes qui se braquent ou se ferment. Le sens du message passe au second plan. On oublie l’idée, on ne retient que la manière dont elle a été exprimée. C’est également quelque chose que j’observe lors de conférences avec des speakers qui ne sont pas à l’aise à l’oral. Le contenu peut être excellent, mais la transmission est difficile et l’audience décroche.&lt;/p&gt;

&lt;p&gt;La communication n’est pas uniquement une question de contenu, c’est aussi une question de perception et réception. La manière dont on dit les choses est tout aussi importante que ce que l’on dit.&lt;/p&gt;

&lt;p&gt;Que l’on soit développeur, product owner ou manager, la communication est une compétence essentielle. Un message mal transmis perd de sa valeur, aussi pertinent puisse-t-il être.&lt;/p&gt;

&lt;p&gt;La forme n’est pas un détail, c’est ce qui permet à votre message d’être compris et entendu.&lt;/p&gt;
</description>
                    <pubDate>Wed, 14 Jan 2026 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2026/01/14/la-formulation-d-un-message-n-est-pas-un-detail.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2026/01/14/la-formulation-d-un-message-n-est-pas-un-detail.html</guid>
                </item>
            
        
            
                33
                <item>
                    <title>Les bonnes pratiques sont contextuelles</title>
                    <description>&lt;p&gt;Ce qui fonctionne pour une équipe et un projet donné peut ne pas fonctionner ailleurs. L’architecture, la méthodologie, les pratiques en place qui ont fonctionné dans un contexte précis ne sont pas nécessairement transposables telles quelles d’une équipe à une autre ou d’un projet à un autre.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Il n’est pourtant pas rare de vouloir tout uniformiser en entreprise, parfois à l’extrême. Je vois et j’entends régulièrement des développeurs dire « il ne faut pas faire comme ça » ou « ça c’est une bêtise » en voyant telle ou telle pratique de code. Pourtant, ce sont des pratiques qui fonctionnent (parfois très bien) dans le contexte dans lequel elles sont utilisées. Simplement, cela sort des standards habituels connus des personnes qui font la remarque.&lt;/p&gt;

&lt;p&gt;Il n’y a pas de recette universelle, pas de vérité absolue en informatique. « There’s no silver bullet » comme disent nos amis anglo-saxons. Une équipe de quelques développeurs sur un projet qui démarre n’a pas les mêmes contraintes qu’une équipe de dizaines de développeurs sur un produit mature.&lt;/p&gt;

&lt;p&gt;Il est essentiel de comprendre le pourquoi derrière chaque pratique avant de la juger. C’est en comprenant les éléments qui ont conduit à sa mise en place qu’il sera possible de déterminer si elle est pertinente dans un autre contexte.&lt;/p&gt;

&lt;p&gt;Les équipes qui fonctionnent le mieux ne cherchent pas à appliquer aveuglément ce qu’elles ont appris ailleurs. Elles questionnent, s’adaptent et font évoluer leurs pratiques en fonction de la maturité du projet et des personnes qui le composent.&lt;/p&gt;
</description>
                    <pubDate>Wed, 07 Jan 2026 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2026/01/07/les-bonnes-pratiques-sont-contextuelles.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2026/01/07/les-bonnes-pratiques-sont-contextuelles.html</guid>
                </item>
            
        
            
                34
                <item>
                    <title>Récapitulatif 2025</title>
                    <description>&lt;p&gt;Je ne suis pas coutumier du traditionnel récapitulatif de l’année écoulée, néanmoins cela ne fait pas de mal de regarder un peu en arrière de temps en temps.&lt;/p&gt;

&lt;div class=&quot;noexcerpt-content&quot;&gt;
&lt;h2 id=&quot;partage-de-contenu&quot;&gt;Partage de contenu&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;Je m’étais fixé comme objectif de relancer un peu ce blog et de publier de manière plus régulière. J’avais en tête un objectif de faire une publication par semaine. Avec 45 publications sur l’année, je ne suis pas loin de mon objectif. 2025 est l’année où j’ai le plus partagé sur le blog.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Depuis la création de ce blog, je publie avant tout pour moi. Il s’agit de partager mes réflexions, de poser des notes et de creuser certains sujets. Voici les trois articles qui ont été le plus consultés sur l’année:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/2025/04/07/structurez-votre-code-explicitement-avec-la-screaming-architecture.html&quot;&gt;Structurez votre code explicitement avec la “Screaming Architecture”&lt;/a&gt; (2025)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/2020/10/18/la-notion-d-agregat-en-ddd.html&quot;&gt;La notion d’agrégat en DDD&lt;/a&gt; (2020)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/2025/02/10/le-design-pattern-unit-of-work.html&quot;&gt;Le design pattern “Unit of Work”&lt;/a&gt; (2025)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Je partage régulièrement du contenu depuis 2009. J’ai eu différents blogs avant celui-là où j’ai pris l’habitude de diffuser ma veille technique. Cette dernière est disponible sur différents réseaux sociaux (&lt;a href=&quot;https://bsky.app/profile/jdecool.bsky.social &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Bluesky&lt;/a&gt;, &lt;a href=&quot;https://phpc.social/@jdecool &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Mastodon&lt;/a&gt; et &lt;a href=&quot;https://twitter.com/jdecool &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;X&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Je partage également depuis peu et chaque semaine sur &lt;a href=&quot;https://www.linkedin.com/in/jdecool &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;LinkedIn&lt;/a&gt;. La ligne éditoriale est un peu différente, puisque j’y partage un contenu moins technique. Il s’agit généralement d’une idée ou un concept sur une thématique de gestion et structuration d’équipe. Comme pour le contenu technique, l’idée est de partager, d’échanger, me forcer à creuser et structurer mes pensées sur des sujets humains et/ou organisationnels. J’ai de plus en plus envie de comprendre et progresser sur les mécanismes humains et organisationnels qui permettent d’améliorer la performance et la cohésion des équipes.&lt;/p&gt;

&lt;p&gt;À ce titre, voici le top 3 des publications &lt;a href=&quot;https://www.linkedin.com &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;LinkedIn&lt;/a&gt; 2025:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.linkedin.com/feed/update/urn:li:activity:7356290826307551234/?updateEntityUrn=urn:li:fs_updateV2:(urn:li:activity:7356290826307551234,FEED_DETAIL,EMPTY,DEFAULT,false) &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Les tests techniques pour les développeurs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.linkedin.com/feed/update/urn:li:activity:7384197009844649984/?updateEntityUrn=urn:li:fs_updateV2:(urn:li:activity:7384197009844649984,FEED_DETAIL,EMPTY,DEFAULT,false) &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Expliquer les concepts avant de les nommer&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.linkedin.com/feed/update/urn:li:activity:7366440906628972544/?updateEntityUrn=urn:li:fs_updateV2:(urn:li:activity:7366440906628972544,FEED_DETAIL,EMPTY,DEFAULT,false) &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Le travail d’un développeur ne s’arrête pas au code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;la-révolution-de-lia-dans-le-développement-logiciel&quot;&gt;La révolution de l’IA dans le développement logiciel&lt;/h2&gt;

&lt;p&gt;L’IA est certainement le plus grand changement que j’ai connu depuis le début de ma carrière. Je pense que c’est une révolution majeure qui est en train de s’opérer. La manière dont nous travaillons est en train de radicalement évoluer.&lt;/p&gt;

&lt;p&gt;Comme de nombreuses personnes, je ne souhaite pas passer à côté. Je suis utilisateur régulier d’outils basés sur l’IA, de LLM. C’est une technologie qui est maintenant intégrée dans mon quotidien, que ce soit personnel ou professionnel.&lt;/p&gt;

&lt;p&gt;La courbe d’apprentissage et le changement d’habitude ne sont pas simples. Mais comme souvent, une fois la technologie et les concepts sous-jacents assimilés, c’est un vrai gain de qualité et de productivité qui en découle.&lt;/p&gt;

&lt;p&gt;Je vois difficilement comment l’avenir ne peut continuer dans cette direction. Je pense qu’il est important de s’y mettre avant de rater le train et qu’il ne soit trop tard. Je pense aussi, qu’il est plus que jamais important, d’acquérir les bases techniques et de revenir aux fondamentaux dans un monde où l’on va de plus en plus déléguer de responsabilités à l’IA. Il est essentiel de conserver notre capacité à suivre et comprendre ce qui est fait par les machines.&lt;/p&gt;

&lt;h2 id=&quot;participations-aux-conférences&quot;&gt;Participations aux conférences&lt;/h2&gt;

&lt;p&gt;J’apprécie beaucoup de participer à des conférences. Ce sont des moments essentiels pour découvrir de nouveaux sujets, assister à des retours d’expérience d’autres professionnels et entreprises. C’est un moment qui permet de rencontrer et d’échanger des idées, des expériences et des connaissances avec d’autres personnes.&lt;/p&gt;

&lt;p&gt;Ces derniers temps il est devenu plus difficile de trouver du temps en semaine pour participer à des meetups en fin de journée. Heureusement, mon employeur me permet d’assister à des salons professionnels.&lt;/p&gt;

&lt;p&gt;Comme depuis plusieurs années maintenant, j’ai pu assister cette année à la grande messe des développeurs PHP: le Forum PHP organisé par l’&lt;a href=&quot;https://afup.org &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;AFUP&lt;/a&gt;. C’est pour moi l’événement le plus important de la communauté PHP que je ne souhaite pas louper.&lt;/p&gt;

&lt;p&gt;J’ai d’ailleurs eu l’occasion cette année avec &lt;a href=&quot;https://www.linkedin.com/in/marion-escure/ &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Marion&lt;/a&gt; de présenter la société où je travaille actuellement: &lt;a href=&quot;https://www.activinnov.com &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Activinnov&lt;/a&gt; qui fait partie des sponsors de l’événement (merci à eux, le sponsoring étant essentiel pour faire vivre ce genre d’événement).&lt;/p&gt;

&lt;center&gt;
    &lt;img src=&quot;/img/blog/20260104-recapitulatif-2025/forumphp.jpeg&quot; alt=&quot;ForumPHP 2025 - Présentation Activinnov par Jérémy et Marion&quot; style=&quot;width: 65%; height: 65%;&quot; /&gt;
&lt;/center&gt;

&lt;p&gt;L’ensemble des conférences du ForumPHP est maintenant &lt;a href=&quot;https://www.youtube.com/playlist?list=PL9zDdgiGjkIdQwiNb-1evZqGmDfmOwIHv &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;disponible en &lt;em&gt;replay&lt;/em&gt; sur YouTube&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;À côté de ça, j’ai également eu le plaisir et l’occasion d’assister au premier &lt;a href=&quot;https://devfest.gdglyon.com &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;DevFest de Lyon&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;un-pas-de-plus-vers-une-tech-plus-européenne&quot;&gt;Un pas de plus vers une tech plus européenne&lt;/h2&gt;

&lt;p&gt;Cela fait maintenant quelques années que je m’intéresse de plus en plus à la souveraineté numérique. J’ai l’envie et la volonté de moins dépendre des services techs américains. Je souhaite réduire ma dépendance à ces derniers et migrer le plus possible vers des solutions françaises ou à défaut européennes.&lt;/p&gt;

&lt;p&gt;C’est un sujet qui me tient à cœur, d’autant plus qu’on a quelques sujets d’actualités qui peuvent faire réfléchir.&lt;/p&gt;

&lt;p&gt;Même si ce blog est un mauvais élève, je fais héberger autant que possible mes services et sites Web sur des infrastructures françaises (notamment sur &lt;a href=&quot;https://www.scaleway.com/fr/ &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Scaleway&lt;/a&gt;). J’ai arrêté d’utiliser Google Analytics il y a quelques années maintenant au profit de &lt;a href=&quot;https://pirsch.io/ &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Pirsch Analytics&lt;/a&gt; pour me détacher de Google.&lt;/p&gt;

&lt;p&gt;J’ai cette année franchi un nouveau cap en quittant GMail et la galaxie d’applications liées. J’utilise dorénavant la suite proposée par &lt;a href=&quot;https://proton.me/fr &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Proton&lt;/a&gt; qui est une solution créée et hébergée en Suisse.&lt;/p&gt;

&lt;hr style=&quot;margin-bottom: 24px;&quot; /&gt;

&lt;p&gt;Pour conclure, il ne me reste qu’à vous souhaiter une très bonne année 2026.&lt;/p&gt;
</description>
                    <pubDate>Sun, 04 Jan 2026 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2026/01/04/recapitulatif-2025.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2026/01/04/recapitulatif-2025.html</guid>
                </item>
            
        
            
                35
                <item>
                    <title>Ce que l’on tolère devient un nouveau standard d’entreprise</title>
                    <description>&lt;p&gt;Ce que l’on tolère devient un nouveau standard d’entreprise.&lt;/p&gt;

&lt;p&gt;Du code sans tests qui est accepté un jour deviendra la norme de demain. Un raccourci technique validé une fois parce que cela arrange s’éparpillera dans le projet au cours du temps.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Ce qui est toléré devient une règle acceptée, c’est un signal qui dit à l’équipe que c’est « acceptable ». Et progressivement, les exceptions deviennent la norme.&lt;/p&gt;

&lt;p&gt;Cela se fait au fil du temps qui passe sans prise de décision consciente parce que l’on aura tendance à reproduire ce qui est fait. Et c’est l’accumulation des petits raccourcis faits dans l’urgence ou par manque d’inattention qui au final, conduit à la perte de qualité.&lt;/p&gt;

&lt;p&gt;Il est donc essentiel de bien faire attention et d’accepter que chaque situation où l’on ferme les yeux équivaut à une décision: « oui on peut fonctionner ainsi ». Dans le cas d’un test manquant dans du code, c’est dire que les tests sont optionnels.&lt;/p&gt;

&lt;p&gt;Bien entendu, il ne faut pas être rigide. Il y a parfois des compromis à faire. Ces compromis doivent avant tout être explicites et temporaires. Un écart ponctuel est bien entendu possible, le problème c’est si cet écart perdure dans le temps.&lt;/p&gt;

&lt;p&gt;C’est donc pour cela qu’il est indispensable de documenter ses standards, conventions et fonctionnement d’équipe. Plus important, de les maintenir au cours du temps. Si l’effort de maintenance est constant, la douleur est moindre que de devoir le traiter ponctuellement. C’est ainsi que pourra se mettre en place une culture d’équipe solide et pérenne.&lt;/p&gt;
</description>
                    <pubDate>Wed, 17 Dec 2025 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2025/12/17/ce-que-l-on-tolere-devient-un-nouveau-standard-d-entreprise.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2025/12/17/ce-que-l-on-tolere-devient-un-nouveau-standard-d-entreprise.html</guid>
                </item>
            
        
            
                36
                <item>
                    <title>L&apos;onboarding développeur en 1 étape</title>
                    <description>&lt;p&gt;L’onboarding d’un nouveau développeur sur un projet est une étape cruciale. Rien de plus frustrant que de devoir passer plusieurs heures à configurer un environnement de développement avant de pouvoir écrire la moindre ligne de code.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Pour résoudre ce problème, j’aime mettre en place une commande unique qui installe, configure et lance l’ensemble du projet. Une simple commande &lt;code&gt;make install&lt;/code&gt; (ou un équivalent) et le projet est opérationnel avec la configuration de développement adéquate et les dépendances systèmes requises.&lt;/p&gt;

&lt;p&gt;On pourrait penser que l’investissement demandé est superflu. Après tout c’est une commande que l’on va exécuter une seule fois lorsque l’on arrive sur le projet. Mais cette dernière peut également être utilisée au quotidien pour réinitialiser l’environnement de travail, dans le cas d’un changement de machine ou tout simplement pour maintenir une configuration à jour entre l’ensemble des intervenants du projet.&lt;/p&gt;

&lt;p&gt;De plus, ce script d’installation et de configuration peut servir de documentation du projet. Et puisqu’automatisé restera toujours à jour.&lt;/p&gt;

&lt;p&gt;Démarrer un projet en une seule commande, c’est faciliter l’intégration des développeurs en maximisant le temps entre l’installation du projet et la première contribution. C’est également fournir un onboarding de qualité sur le projet qui donnera une très bonne première impression.&lt;/p&gt;
</description>
                    <pubDate>Fri, 12 Dec 2025 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2025/12/12/l-onboarding-developpeur-en-1-etape.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2025/12/12/l-onboarding-developpeur-en-1-etape.html</guid>
                </item>
            
        
            
                37
                <item>
                    <title>Les réunions hybrides (présentiel/distanciel)</title>
                    <description>&lt;p&gt;Quand on commence à faire du télétravail, on se rend rapidement compte que les réunions « hybrides » (à la fois en présentiel et distanciel) sont compliquées à gérer: les personnes à distance peuvent rapidement être oubliées et devenir invisibles si certaines règles ne sont pas mises en place.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;J’ai souvent observé des réunions où les personnes en distanciel étaient effacées par celles en présentiel. Il n’est pas rare de ne même pas les voir, l’écran utilisé pour la réunion étant généralement monopolisé pour projeter du contenu. Et cela s’accentue si le matériel (son, micro, image) n’est pas de bonne qualité. Tout cela combiné entraîne une mauvaise dynamique de groupe.&lt;/p&gt;

&lt;p&gt;Au-delà du matériel, pour qu’une réunion hybride puisse se passer dans les meilleures conditions, j’ai remarqué qu’il était essentiel de maintenir un lien visuel permanent avec l’ensemble des participants. Cela passe par « couper » un écran en deux pour avoir d’un côté les participants à distance et de l’autre le contenu présenté. Il m’est même arrivé, dans certaines expériences, de positionner des avatars dans les salles de réunions représentant physiquement les personnes absentes.&lt;/p&gt;

&lt;p&gt;L’objectif est de faire en sorte que chacun reste visible, et donc présent dans la conscience collective. Quand on voit quelqu’un, il est plus facile de capter ses réactions, de l’inclure dans les échanges et de lui donner la parole.&lt;/p&gt;

&lt;p&gt;Une réunion hybride n’est pas une simple réunion présentielle auquel des personnes se connectent à distance. Il est essentiel que chaque participant puisse être « physiquement » présent pour prendre place dans la discussion.&lt;/p&gt;
</description>
                    <pubDate>Wed, 03 Dec 2025 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2025/12/03/les-reunions-hybrides-presentiel-distanciel.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2025/12/03/les-reunions-hybrides-presentiel-distanciel.html</guid>
                </item>
            
        
            
                38
                <item>
                    <title>Les &quot;boring technologies&quot;, un choix sous-estimé</title>
                    <description>&lt;p&gt;Les “boring technologies” (comprendre les technologies ennuyantes) sont clairement les technologies les plus sous-cotées actuellement.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;D’un côté, il y a les technologies “à la mode”, celles que l’on voit partout, dont tout le monde parle, récentes, innovantes et modernes. Celles avec lesquelles la plupart des développeurs aimeraient travailler. Et il y a les autres, les technologies « standard » sans hype, matures, éprouvées et bien comprises. Celles pour lesquelles il n’y a plus de surprises.&lt;/p&gt;

&lt;p&gt;Ce que je vois le plus souvent, c’est que les technologies éprouvées sont souvent délaissées au profit de solutions plus récentes alors qu’elles résolvent efficacement la grande majorité des problèmes rencontrés. La mise en place d’un serveur Elasticsearch, là où une requête SQL optimisée pourrait faire l’affaire. Mais cela ne fait pas rêver, ni forcément vendre sur un CV ou intellectuellement.&lt;/p&gt;

&lt;p&gt;Le problème avec les technologies récentes, ce n’est pas qu’elles sont mauvaises ou qu’elles ne répondent pas à un besoin. C’est surtout qu’elles ajoutent un niveau de complexité et de risque supplémentaire, qui n’est pas toujours justifié. Elles impliquent également des temps d’apprentissage et manquent parfois de prise de recul.&lt;/p&gt;

&lt;p&gt;Choisir une technologie éprouvée et connue, c’est privilégier la fiabilité et la maintenabilité plutôt que la nouveauté. C’est pouvoir tirer parti de nombreuses ressources et connaissances sur le sujet. Et ainsi minimiser les coûts à long terme.&lt;/p&gt;

&lt;p&gt;Lorsque l’on souhaite résoudre un problème, il est essentiel de se poser la question de la nécessité d’introduire une nouvelle brique technologique. C’est ainsi que l’on peut construire une architecture et un environnement robustes et pérennes plutôt que de courir après la nouveauté.&lt;/p&gt;
</description>
                    <pubDate>Wed, 26 Nov 2025 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2025/11/26/les-boring-technologies-un-choix-sous-estime.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2025/11/26/les-boring-technologies-un-choix-sous-estime.html</guid>
                </item>
            
        
            
                39
                <item>
                    <title>La taille d&apos;une équipe est importante</title>
                    <description>&lt;p&gt;Pour être efficiente, la taille d’une équipe ne devrait pas dépasser 12 personnes. Elle devrait même idéalement se situer entre 5 et 9 personnes. C’est l’idée que l’on retrouve derrière la règle de la « pizza team » popularisée par Jeff Bezos (le fondateur d’Amazon) et également un concept au cœur du mouvement agile.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Cette règle prend tout son sens, car plus une équipe est grande, plus la communication devient difficile. Par exemple, dans une équipe de 5 personnes, il y a 10 canaux de communication possibles. À 10 personnes, cela monte à 45 et, dans une équipe de 20, il y en a 190.&lt;/p&gt;

&lt;p&gt;Avoir des équipes de taille réduite permet ainsi de fluidifier la communication. Cela accélère les prises de décision, facilite le partage d’une vision commune et favorise la responsabilité collective. Les petites équipes sont également plus agiles, car elles conservent leur capacité à décider et réagir rapidement.&lt;/p&gt;

&lt;p&gt;Il est néanmoins nécessaire de bien positionner le curseur pour allier efficacité et performance. Les équipes doivent rester autonomes et pluridisciplinaires. Elles doivent être dimensionnées et structurées de manière à maîtriser leur périmètre de travail et disposer de l’ensemble des compétences nécessaires à sa bonne réalisation.&lt;/p&gt;
</description>
                    <pubDate>Wed, 19 Nov 2025 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2025/11/19/la-taille-d-une-equipe-est-importante.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2025/11/19/la-taille-d-une-equipe-est-importante.html</guid>
                </item>
            
        
            
                40
                <item>
                    <title>Équipe et individualité</title>
                    <description>&lt;p&gt;Une équipe n’est pas uniquement la somme d’individualité qui travaille conjointement. Une équipe, c’est un ensemble de personnes qui avance à un but commun et qui partage les responsabilités de leur travail.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;C’est ce qui différencie une équipe d’un groupe de personnes. Un groupe de personnes est une collection d’individus qui coordonnent leurs efforts en travaillant chacun de leur côté.&lt;/p&gt;

&lt;p&gt;Dans les équipes les plus efficaces, chaque membre comprend et partage l’objectif à atteindre, pourquoi cet objectif existe et est également aligné sur la manière d’y parvenir.&lt;/p&gt;

&lt;p&gt;Le rôle du management est clé dans cette dynamique, car c’est lui qui a la responsabilité de la mise en place de l’environnement nécessaire à l’atteinte de l’objectif de l’équipe. Il doit créer les conditions indispensables à la cohésion en définissant et partageant la vision et la direction technique.&lt;/p&gt;

&lt;p&gt;Car c’est l’alignement de tous sur cette vision partagée et les moyens d’y parvenir qui permettra de mettre en place l’environnement favorable au partage de la responsabilité qui incombe à l’équipe et la différencie de simples personnes qui collaborent ensemble.&lt;/p&gt;
</description>
                    <pubDate>Thu, 13 Nov 2025 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2025/11/13/equipe-et-individualite.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2025/11/13/equipe-et-individualite.html</guid>
                </item>
            
        
            
                41
                <item>
                    <title>Les équipes de développement ne sont pas immuables</title>
                    <description>&lt;p&gt;Les équipes de développement ne sont pas immuables. Lorsqu’elles se retrouvent modifiées par l’arrivée ou le départ d’un nouveau membre, c’est une nouvelle équipe qui se crée.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Modifier la composition d’une équipe n’est pas anodin. Lors d’un départ, c’est une partie des connaissances du groupe qui s’en va. Des connaissances potentiellement non documentées et des décisions implicites qui sont perdues. À l’inverse, lors d’une nouvelle arrivée, c’est une nouvelle vision des choses qui émerge, mais aussi la nécessité de reconstruire une compréhension et vision commune du projet.&lt;/p&gt;

&lt;p&gt;C’est dans cette situation qu’avoir une documentation, une vision et des standards de développement clairement établis devient essentiel. Ils ne sont pas là pour contraindre le fonctionnement, mais pour constituer une connaissance commune et partagée permettant d’avancer ensemble dans la même direction.&lt;/p&gt;

&lt;p&gt;Documenter d’où vient et là où l’on veut aller. Décrire l’architecture, les choix effectués et pourquoi les choses ont été réalisées de telle ou telle manière. Expliciter la façon de travailler, les patrons de conceptions privilégiés, le processus de revue de code, la stratégie de tests. Autant d’éléments qui définissent le fonctionnement de l’équipe et permettent de limiter les frictions lors des changements de composition du groupe.&lt;/p&gt;

&lt;p&gt;Sans standards clairement établis et partagés, chacun avance dans sa propre direction. Les décisions sont régulièrement discutées et remises en question. Les approches divergent et la cohérence de l’ensemble s’effrite. Le projet devient alors le reflet du passage de chaque équipe.&lt;/p&gt;

&lt;p&gt;Définir et maintenir ces standards peut-être long. Cela devrait être un processus continu qui doit être mis à jour au fil des évolutions du projet. L’idéal (et le Graal à atteindre) serait que la documentation et les règles du projet soient automatisées, permettant que personne ne puisse passer à côté.&lt;/p&gt;

&lt;p&gt;Définir et documenter permet ainsi d’assurer la stabilité de l’équipe projet au fil du temps.&lt;/p&gt;
</description>
                    <pubDate>Wed, 05 Nov 2025 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2025/11/05/les-equipes-de-developpement-ne-sont-pas-immuables.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2025/11/05/les-equipes-de-developpement-ne-sont-pas-immuables.html</guid>
                </item>
            
        
            
                42
                <item>
                    <title>Le problème n&apos;est pas humain, il est organisationnel</title>
                    <description>&lt;p&gt;Une des phrases que j’ai le plus entendue depuis le début de ma carrière est « l’équipe X n’a pas conscience des problèmes de l’équipe Y ». Et dans la plupart des cas où elle est prononcée, le problème est rarement humain. Il est plutôt organisationnel.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Cette situation apparaît généralement lorsque les équipes évoluent en silos, chacune avec ses propres objectifs, sans alignement ni vision commune. Des équipes de développement qui n’ont pas conscience des problématiques du support client parce que leurs objectifs et les indicateurs de performance associés sont uniquement basés sur le développement de nouvelles fonctionnalités.&lt;/p&gt;

&lt;p&gt;Ou alors, des équipes de développement qui n’ont pas conscience des problèmes de l’infrastructure (et vice versa) parce qu’elles n’ont pas dans leur périmètre le déploiement et le suivi opérationnel des mises en production. Des exemples comme cela, il en existe une multitude.&lt;/p&gt;

&lt;p&gt;Un découpage par expertise technique conduit inévitablement à ces silos. Chaque équipe défend ses intérêts, optimise ses indicateurs, mais perd de vue l’objectif qui devrait être commun aux différentes équipes: la valeur pour l’utilisateur.&lt;/p&gt;

&lt;p&gt;C’est là qu’une organisation en équipe produit est intéressante. Une équipe produit s’organise autour de compétences pluridisciplinaires (PO, UX, développeurs, ops, QA…) qui possèdent toutes les compétences nécessaires: analyser, concevoir, développer, tester et déployer. Une équipe capable de gérer un cycle produit dans sa globalité, de la création jusqu’à la mise à disposition aux utilisateurs tout en assurant son suivi opérationnel. Elle incarne le mantra « You build it, You run it ».&lt;/p&gt;

&lt;p&gt;À la fois, autonome, auto-organisée et responsable d’un périmètre fonctionnel entier. Chaque membre de l’équipe aura ainsi conscience des problématiques des autres. Tout le monde avance à un objectif commun où la satisfaction de l’utilisateur final est la clé du succès.&lt;/p&gt;
</description>
                    <pubDate>Wed, 29 Oct 2025 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2025/10/29/le-probleme-n-est-pas-humain-il-est-organisationnel.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2025/10/29/le-probleme-n-est-pas-humain-il-est-organisationnel.html</guid>
                </item>
            
        
            
                43
                <item>
                    <title>Expliquer un concept avant de le nommer</title>
                    <description>&lt;p&gt;Au fil des années, j’ai appris à expliquer un concept avant de le nommer.&lt;/p&gt;

&lt;p&gt;Partir d’un problème, décrire les différentes solutions envisagées, leurs avantages et inconvénients. Ce n’est qu’une fois que l’équipe a compris et adhéré à l’approche que l’on peut nommer cette dernière. Mais pourquoi fonctionner ainsi ?&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Car derrière chaque pratique se cache des « buzzwords ». De mon expérience, ces derniers sont généralement perçus négativement, indépendamment de ce que les pratiques associées peuvent apporter (ou non). Entendre des termes « marketing » ou popularisés peut entrainer biais et préjugés, mais peuvent aussi générer des prises de décisions précipitées et sans réelles réflexion.&lt;/p&gt;

&lt;p&gt;Par exemple, dans une équipe précédente, nous avions des problèmes de couplage fort entre les différentes couches de code d’un projet. Nous avons donc réfléchi aux différentes solutions possibles. Nous avons commencé à modifier l’architecture du projet. Séparer le fonctionnement de l’application, des règles à appliquer sans dépendre du framework que nous utilisions.&lt;/p&gt;

&lt;p&gt;Le résultat ? Une architecture projet qui avait une adhésion naturelle de l’équipe. Chacun avait le « pourquoi » avant de connaitre le « comment ». Le nom de cette architecture: l’architecture hexagonale. Pourtant, nombreux sont les membres de l’équipe qui voyaient cette dernière comme inutilement complexe et sans intérêt.&lt;/p&gt;

&lt;p&gt;Les buzzwords ne sont pas forcément le problème. Ils le deviennent quand ils biaisent la réflexion et qu’ils servent d’arguments d’autorité plutôt que de support à la conversation. Expliquer avant de nommer c’est comprendre ce qu’on souhaite mettre en place plutôt que de réagir sur la forme.&lt;/p&gt;

&lt;p&gt;Et, comme j’aime souvent à le dire: « il est essentiel de comprendre les différents outils et pratiques, savoir pourquoi ils ont été créés » afin d’être en mesure de les challenger. C’est ainsi que l’on peut juger de leur bien-fondé dans une situation donnée.&lt;/p&gt;
</description>
                    <pubDate>Wed, 22 Oct 2025 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2025/10/22/expliquer-un-concept-avant-de-le-nommer.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2025/10/22/expliquer-un-concept-avant-de-le-nommer.html</guid>
                </item>
            
        
            
                44
                <item>
                    <title>Comment arbitrer les développements prioritaires ?</title>
                    <description>&lt;p&gt;Comment arbitrer les développements prioritaires ? Quelles fonctionnalités prioriser ? La réponse à ces questions peut être trouvée dans le développement piloté par le métier (Domain Driven Design), qui nous propose des outils utiles permettant de classer les domaines métier selon trois catégories: Core, Support et Generic.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Le “core” correspond au cœur du métier. C’est ce qui différencie l’entreprise de ses concurrents. C’est l’aspect le plus important d’une entreprise. Il devrait idéalement être développé en interne avec une attention particulière à la qualité, l’innovation et la maintenabilité.&lt;/p&gt;

&lt;p&gt;La catégorie support regroupe les fonctionnalités nécessaires au business, mais non différenciantes. On y retrouve des développements qui peuvent être externalisés ou mis en place avec des solutions clés en main.&lt;/p&gt;

&lt;p&gt;Pour finir, la catégorie générique représente toutes les fonctionnalités standardisées, sans valeur stratégique et bien souvent semblable d’un outil à l’autre. Il est ici primordial d’utiliser des solutions existantes et des modules génériques. L’investissement doit être minime.&lt;/p&gt;

&lt;p&gt;À chaque typologie de domaine métier doit correspondre un niveau d’effort et d’investissement à y consacrer. Prenez le recul nécessaire pour identifier où se situe réellement votre valeur ajoutée et allouez vos ressources en conséquence. C’est ainsi que se construira la cohérence de votre stratégie de développement.&lt;/p&gt;
</description>
                    <pubDate>Wed, 15 Oct 2025 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2025/10/15/comment-arbitrer-les-developpements-prioritaires.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2025/10/15/comment-arbitrer-les-developpements-prioritaires.html</guid>
                </item>
            
        
            
                45
                <item>
                    <title>Build vs Buy</title>
                    <description>&lt;p&gt;Dans &lt;a href=&quot;/blog/2025/09/23/la-loi-de-conway.html&quot;&gt;ma publication de la semaine dernière&lt;/a&gt;, j’évoquais le fait que la solution à un problème logiciel ne provient pas nécessairement du code. Mais concrètement, que cela signifie-t-il ?&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Pour illustrer le propos, prenons un exemple concret: un logiciel de gestion pour lequel nous souhaitons mettre en place des tableaux de bord métier pour les utilisateurs. Création de graphiques, filtres dynamiques, export de données, gestion des droits d’accès, etc. Une charge de travail conséquente.&lt;/p&gt;

&lt;p&gt;Pourtant, est-ce réellement nécessaire ? N’existe-t-il pas de solution existante et intégrable répondant à ce besoin ? C’est le genre de questions et de réflexions qui peut transformer des mois de développement en quelques semaines d’intégration.&lt;/p&gt;

&lt;p&gt;C’est le concept du “build vs buy”, construire ou acheter. Et la réponse est moins simple qu’il n’y paraît puisqu’il y a de nombreuses variables à prendre en compte.&lt;/p&gt;

&lt;p&gt;Si une équipe décide de construire une solution sur-mesure, il faut que cela représente un réel avantage concurrentiel, une différenciation forte. Idéalement, il faut que le développement se rapproche du cœur de métier de la solution. Si ce n’est pas le cas, c’est de l’investissement qui ne serait pas optimisé pour apporter une réelle valeur à l’utilisateur finale.&lt;/p&gt;

&lt;p&gt;C’est une vraie décision stratégique à arbitrer et il est pourtant fort probable que le réflexe de toute équipe de développement sera de développer sa propre solution. C’est là que la prise de recul et une vision business suffisante sont indispensable pour faire la différence.&lt;/p&gt;
</description>
                    <pubDate>Tue, 07 Oct 2025 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2025/10/07/build-vs-buy.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2025/10/07/build-vs-buy.html</guid>
                </item>
            
        
            
                46
                <item>
                    <title>Les meilleures solutions ne proviennent parfois pas du code</title>
                    <description>&lt;p&gt;Dans les équipes de développement, les meilleures solutions ne proviennent parfois pas du code.&lt;/p&gt;

&lt;p&gt;Pour que cela soit possible, il faut pouvoir prendre le recul nécessaire. Il faut avoir la vision globale qui permet de comprendre le problème de fond et d’aller plus loin que ce qui est spécifié dans un ticket.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Comprendre la véritable douleur de l’utilisateur est clé. C’est aller chercher le “pourquoi” derrière le “quoi”. Cela s’avère difficile lorsque l’on court après la livraison de nouvelles fonctionnalités. Difficile de trouver la solution qui aura un impact maximum avec le moins de compromis.&lt;/p&gt;

&lt;p&gt;Pour y parvenir, il faut des équipes avec un ownership élevé. Des équipes qui ne se contentent pas d’exécuter, mais qui questionnent et proposent. Des développeurs suffisamment proches du métier et du business pour en saisir les véritables enjeux.&lt;/p&gt;

&lt;p&gt;C’est ainsi qu’on peut se rendre compte que la meilleure solution ne passe pas forcément par le code. L’impact d’un produit ne se traduit pas uniquement à travers les lignes de code qui sont nécessaires à son fonctionnement.&lt;/p&gt;

&lt;p&gt;Et pour être proche du métier et du business, pas besoin d’assister à toutes les réunions orientées produit. Mais il est indispensable que les développeurs soient assez proches des product owners et des utilisateurs pour comprendre les douleurs rencontrées, ce qui permettra de proposer les meilleures solutions.&lt;/p&gt;
</description>
                    <pubDate>Tue, 30 Sep 2025 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2025/09/30/les-meilleures-solutions-ne-proviennent-parfois-pas-du-code.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2025/09/30/les-meilleures-solutions-ne-proviennent-parfois-pas-du-code.html</guid>
                </item>
            
        
            
                47
                <item>
                    <title>Le langage de programmation n&apos;est qu&apos;un outil pour arriver à ses fins</title>
                    <description>&lt;p&gt;Cela fait plusieurs années que j’entends régulièrement “je suis développeur X” (insérer n’importe quel langage de programmation ou framework). Cette tendance à se définir par rapport à un langage ou framework spécifique est de plus en plus commune et n’est à mon sens pas sans conséquence.&lt;/p&gt;

&lt;p&gt;Pour ma part, je considère que les langages de programmation et les frameworks ne sont que des outils permettant de résoudre des problèmes. Et comme tout outil, ils doivent être choisis en fonction de la tâche à accomplir. S’enfermer dans un écosystème et ne pas en sortir peut être un obstacle à notre croissance professionnelle.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Pourtant, cela me semble de plus en plus à contre-courant de “l’époque actuelle”. On retrouve aujourd’hui de nombreuses équipes projet travaillant avec les mêmes outils, indépendamment des spécificités du problème à résoudre. Toujours le même framework, toujours les mêmes outils, même quand ce dernier n’est pas le plus adapté au contexte.&lt;/p&gt;

&lt;p&gt;Et tout cela n’est pas sans conséquence. Le risque étant de créer des solutions complexes ou inadaptées. J’ai vu des équipes refuser d’utiliser des librairies parce qu’elles attendaient des solutions proposées par les frameworks ne voulant pas dépendre d’autres composants. J’ai vu des développeurs mettre en place une usine à gaz  pour utiliser des solutions types &lt;a href=&quot;https://ux.symfony.com/live-component &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Symfony LiveComponents&lt;/a&gt; ou &lt;a href=&quot;https://livewire.laravel.com &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Laravel Livewire&lt;/a&gt; là où quelques lignes de JavaScript auraient suffi, uniquement pour rester le plus possible dans du code backend. Techniquement, ça fonctionne, mais ce n’est pas optimal. Et puis quand un problème survient, il est plus difficile à résoudre.&lt;/p&gt;

&lt;p&gt;L’effet pervers de cette approche “framework-centrique” c’est aussi qu’actuellement de nombreux développeurs ne connaissent pas les fondamentaux du développement. Je ne parle pas de savoir faire du C (et jouer avec les pointeurs) mais, lors d’entretien de recrutement, je vois de nombreux développeurs exceller dans l’utilisation d’un framework et puis complètement perdus une fois que “l’on sort du cadre”.&lt;/p&gt;

&lt;p&gt;Personnellement, je préfère privilégier une approche consistant à partir du problème pour aller vers une solution technique. On commence par identifier le besoin et quelles sont les contraintes du projet. C’est seulement une fois la réponse à ces questions que l’on peut commencer à se questionner sur quel langage et quel framework est le plus adapté. Et dans ce choix, bien entendu que les compétences de l’équipe qui va travailler dessus ont leur importance et peuvent influencer le choix final.&lt;/p&gt;

&lt;p&gt;Cette philosophie rejoint d’ailleurs le principe que &lt;a href=&quot;/blog/2024/03/22/concentrez-vous-sur-les-principes-pas-la-technologie.html&quot;&gt;j’ai déjà évoqué dans ce blog&lt;/a&gt;: se concentrer sur les principes plutôt que sur la technologie. Les concepts fondamentaux du développement logiciel (architecture, patterns, bonnes pratiques) sont transversaux et peuvent s’appliquer indépendamment du langage utilisé.&lt;/p&gt;

&lt;p&gt;Il est également important de noter que cette approche pragmatique ne doit pas conduire à l’anarchie technologique. Dans le contexte d’une équipe ou d’une entreprise, il est nécessaire de trouver un équilibre entre la diversité des outils et la cohérence de l’écosystème technique. L’objectif n’est pas d’utiliser un langage différent pour chaque projet, mais de faire des choix éclairés et justifiés.&lt;/p&gt;

&lt;p&gt;Au-delà de tout ça, un développeur capable de s’adapter à différents langages et frameworks saura également s’adapter à tout type de situations, ce qui est un avantage non négligeable pour l’évolution de carrière.&lt;/p&gt;

&lt;p&gt;Si vous n’aviez qu’une seule chose à retenir, c’est que notre métier consiste avant tout à résoudre des problèmes et à créer de la valeur pour les utilisateurs. Pour arriver à nos fins, nous avons une multitude de solutions pouvant être mises en place. Il est essentiel de choisir celle qui sera la plus adaptée à notre situation, que ce soit en choisissant un langage, un framework. Et pour cela, il est primordial de rester curieux, ouverts aux nouvelles technologies et surtout, de ne pas s’enfermer dans une seule approche technique.&lt;/p&gt;

&lt;p&gt;Le meilleur outil est celui qui permet de résoudre efficacement le problème posé, pas celui que nous maîtrisons le mieux.&lt;/p&gt;
</description>
                    <pubDate>Sun, 28 Sep 2025 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2025/09/28/le-langage-de-programmation-n-est-qu-un-outil-pour-arriver-a-ses-fins.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2025/09/28/le-langage-de-programmation-n-est-qu-un-outil-pour-arriver-a-ses-fins.html</guid>
                </item>
            
        
            
                48
                <item>
                    <title>La loi de Conway</title>
                    <description>&lt;p&gt;L’organisation interne d’une entreprise a un impact direct sur la manière dont y sont développés les outils. L’organisation et les interactions au sein des équipes influencent l’architecture des projets. C’est l’illustration de la loi de Conway.&lt;/p&gt;

&lt;p&gt;La loi de Melvin Conway stipule que “toute organisation qui conçoit un système produira une conception qui est la copie de la structure de communication de cette organisation”.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;J’ai déjà évoqué les problèmes qui peuvent être liés au découpage des équipes centrées sur des compétences techniques et qui à mon sens, tendent à générer des problématiques de communication et de performance de l’entreprise du fait d’une perte de vision d’ensemble.&lt;/p&gt;

&lt;p&gt;Un exemple concret que l’on rencontre fréquemment est une organisation dans laquelle les équipes responsables du développement d’un projet sont séparées de celles qui en assurent le déploiement. Dans ce contexte, il est probable que l’on retrouve une scission entre le code du projet et le code associé à son déploiement (deux équipes = deux dépôts de code la plupart du temps).&lt;/p&gt;

&lt;p&gt;Dans ce genre de situation, gérer la cohérence des deux dépôts demande beaucoup d’organisation, de rigueur et de communication. Cela peut s’avérer difficile, car les équipes étant séparées, chacun peut ne pas avoir conscience des problématiques des autres.&lt;/p&gt;

&lt;p&gt;De plus, chaque équipe ayant ses propres objectifs, il est possible qu’elles ne partagent pas la même vision, ce qui peut nuire à la qualité du logiciel.&lt;/p&gt;

&lt;p&gt;Dans ce cas, la loi de Conway nous enseigne que le développement logiciel n’est pas uniquement une histoire de code. Que pour améliorer la qualité et la déliverabilité d’un outil, il est essentiel d’aligner la structure organisationnelle de l’entreprise avec l’architecture souhaitée pour le produit.&lt;/p&gt;
</description>
                    <pubDate>Tue, 23 Sep 2025 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2025/09/23/la-loi-de-conway.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2025/09/23/la-loi-de-conway.html</guid>
                </item>
            
        
            
        
            
                49
                <item>
                    <title>You run it, You build it</title>
                    <description>&lt;p&gt;Mon expérience a montré que les équipes qui assurent l’ensemble du cycle de vie d’un produit (de la conception au déploiement en passant par sa maintenance) obtiennent de meilleurs résultats et conçoivent des solutions de meilleure qualité.&lt;/p&gt;

&lt;p&gt;&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;C’est l’approche que l’on retrouve derrière le mantra “You build it, You run it”, central dans la culture DevOps. L’équipe qui développe un projet en assure son maintien opérationnel. Elle fait ainsi directement face aux différents problèmes d’exploitation, ceux rencontrés par les utilisateurs ainsi qu’aux incidents de production. Cette proximité incite naturellement chacun à s’engager dans une démarche d’amélioration continue.&lt;/p&gt;

&lt;p&gt;Ce type de fonctionnement nécessite des équipes de développeurs pluridisciplinaires, autonomes et ayant de nombreuses compétences techniques et opérationnelles (conception, développement front &amp;amp; back, monitoring, gestion d’incident, sécurité …). Mais cette organisation responsabilise énormément les équipes techniques, améliore la qualité produit ainsi que la satisfaction client.&lt;/p&gt;

&lt;p&gt;Cela permet de créer un cercle vertueux où l’équipe de développement est au plus proche de l’utilisation de son produit. Cette proximité permet d’obtenir au plus vite le résultat de son travail, accélérant ainsi la boucle de feedback permettant de se rendre compte plus rapidement de ses erreurs et d’itérer rapidement sur les solutions à mettre en place.&lt;/p&gt;

&lt;p&gt;Dans ce mode de fonctionnement, il n’y a plus de retranscription des problèmes, ces derniers sont directement vécus par l’équipe. De ce fait, la qualité augmente non pas à cause des pratiques mises en place, mais parce que l’équipe vit avec ce qu’elle livre.&lt;/p&gt;
</description>
                    <pubDate>Tue, 16 Sep 2025 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2025/09/16/you-run-it-you-build-it.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2025/09/16/you-run-it-you-build-it.html</guid>
                </item>
            
        
            
                50
                <item>
                    <title>On ne peut pas améliorer ce que l&apos;on ne mesure pas</title>
                    <description>&lt;p&gt;Au cours de ma carrière, j’ai vu de nombreuses équipes de développement avancer en “mode tunnel”, à l’aveugle, avec comme seule vision la liste des tâches à accomplir. Pourtant, il est essentiel de regarder en arrière pour visualiser l’impact de notre travail.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Il est commun de dire que “l’on ne peut pas améliorer ce que l’on ne mesure pas” (Lord William Thomson KELVIN). On a parfois le sentiment d’avancer en réalisant de nombreuses tâches, mais sans mesurer l’impact de ces dernières, il est impossible de démontrer l’impact de notre travail.&lt;/p&gt;

&lt;p&gt;C’est un des fondements du Lean Startup qui repose sur une boucle de feedback “Build - Measure - Learn”. Un processus itératif, où l’on expérimente en construisant notre produit, on collecte les données et/ou retours d’utilisation afin de persévérer dans la direction choisie ou au contraire effectuer un pivot.&lt;/p&gt;

&lt;p&gt;Ne pas mesurer, ne pas avoir de données, c’est naviguer à l’aveugle. C’est supposer que le travail que l’on réalise apporte la valeur escomptée. C’est ne pas apprendre de ces erreurs et passer à côté du principe d’amélioration continue.&lt;/p&gt;

&lt;p&gt;Idéalement, les outils de mesure nécessaire doivent être intégrés dès la conception. Il est primordial de définir les indicateurs de succès avant de partir tête baissée. Collecter les données, quantifier les objectifs et prendre le temps d’analyser les résultats une fois livrés.&lt;/p&gt;

&lt;p&gt;En somme, nous parlons de comment travailler sur de l’amélioration continue de manière quantifiable, c’est remplacer l’intuition par des données chiffrées.&lt;/p&gt;
</description>
                    <pubDate>Tue, 09 Sep 2025 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2025/09/09/on-ne-peut-pas-ameliorer-ce-que-l-on-ne-mesure-pas.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2025/09/09/on-ne-peut-pas-ameliorer-ce-que-l-on-ne-mesure-pas.html</guid>
                </item>
            
        
            
        
            
        
            
                51
                <item>
                    <title>Partager sa vision</title>
                    <description>&lt;p&gt;Après les congés d’été et avec la rentrée de septembre qui approche, c’est le bon moment pour définir ou repartager sa vision stratégique.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Trop souvent, les équipes se focalisent dans l’exécution et l’opérationnel sans avoir d’objectif ou de but à atteindre (autre que le développement de la fonctionnalité sur laquelle elles travaillent) perdant de vue la direction à suivre. La vision permet de (re)donner du sens aux équipes, elle permet de savoir pourquoi on travaille.&lt;/p&gt;

&lt;p&gt;J’ai longtemps réfléchi à comment en mettre une en place, et je dirai que cela s’articule autour de 3 axes:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Décrire&lt;/strong&gt; où l’on va afin de pouvoir prendre les bonnes décisions et de faire les choix techniques nécessaires et correspondant à l’objectif.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Planifier&lt;/strong&gt; comment on y va afin de tracer le chemin pour y arriver et matérialiser des étapes concrètes.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Mesurer&lt;/strong&gt;, suivre et ajuster si nécessaire. Aucune vision et planification n’est parfaite. Pouvoir suivre sa mise en place et rectifier en cours de route est indispensable.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ces éléments sont d’autant plus importants quand une entreprise grandit afin de pouvoir aligner tout le monde et aller dans la même direction avec le même objectif.&lt;/p&gt;

&lt;p&gt;La rentrée, c’est l’occasion de partager ses ambitions.&lt;/p&gt;
</description>
                    <pubDate>Tue, 19 Aug 2025 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2025/08/19/partager-sa-vision.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2025/08/19/partager-sa-vision.html</guid>
                </item>
            
        
            
        
            
        
            
        
            
        
            
        
            
                52
                <item>
                    <title>Testez votre documentation OpenAPI (avec PHP)</title>
                    <description>&lt;p&gt;Il n’y a rien de plus frustrant que d’utiliser une API qui ne se comporte pas de la manière qui est décrite dans sa documentation. Nombreux sont les développeurs ayant déjà vécu cette situation. Et pour cause, maintenir une documentation précise et à jour peut s’avérer être une tâche complexe et laborieuse (et ce, même si cette dernière est générée automatiquement). Pour éviter ces désagréments, il est primordial de pouvoir comparer ce qui est décrit dans la documentation avec le comportement réel du code.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Dans le cadre d’une API, une partie de ce travail consiste à s’assurer de la conformité de la &lt;a href=&quot;https://swagger.io/specification &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;documentation OpenAPI&lt;/a&gt;. Une solution pouvant être mise en place consiste à intégrer la validation et la vérification de la documentation au sein de tests fonctionnels. L’objectif de cette approche est de vérifier que la requête HTTP effectuée, ainsi que la réponse retournée lors du test correspondent à ce qui est spécifié. Ainsi, si les tests s’exécutent à chaque modification du code, cela permettra de garantir que l’API se comporte bien tel que décrit dans la documentation OpenAPI tout au cours de la vie du projet.&lt;/p&gt;

&lt;p&gt;Pour mettre en place ces tests dans un projet PHP, j’utilise généralement la bibliothèque  &lt;a href=&quot;https://packagist.org/packages/league/openapi-psr7-validator &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;league/openapi-psr7-validator&lt;/code&gt;&lt;/a&gt; qui permet de valider des requêtes et réponses HTTP par rapport à une spécification OpenAPI.&lt;/p&gt;

&lt;p&gt;Pour faciliter leur intégration dans vos bases de code, je recommande généralement d’utiliser une classe abstraitre pour mutualiser le code de vérification nécessaire et qui sera dédié aux tests de l’API. Cela pourrait ressembler au code suivant:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;// ...
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;

abstract class ApiTestCase extends WebTestCase
{
    protected static KernelBrowser $client;

    protected function setUp(): void
    {
        parent::setUp();

        static::$client = static::createClient();
    }

    protected function makeRequest(
        string $method,
        string $uri,
        array $parameters = [],
        array $files = [],
        array $server = [],
        ?string $content = null,
        bool $changeHistory = true,
    ): Response {
        static::$client-&amp;gt;request($method, $uri, $parameters, $files, $server, $content, $changeHistory);
        $response = static::$client-&amp;gt;getResponse();

        // TODO: insérer le code de validation de la requête
        // TODO: insérer le code de validation de la réponse

        return $response;
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Le code précédent est basé sur l’utilisation du &lt;em&gt;framework&lt;/em&gt; Symfony, mais sa logique est universelle. L’idée étant d’avoir une méthode dans laquelle on puisse faire un appel HTTP, récupérer la requête HTTP effectuée ainsi que la réponse retournée afin de pouvoir effectuer des contrôles sur ces dernières.&lt;/p&gt;

&lt;p&gt;Ajoutons maintenant le code permettant d’effectuer la vérification des données avec la spécification OpenAPI.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;// ...
use League\OpenAPIValidation\PSR7\OperationAddress;
use League\OpenAPIValidation\PSR7\ValidatorBuilder;
use Nyholm\Psr7\Request as Psr7Request;
use Nyholm\Psr7\Response as Psr7Response;

abstract class ApiTestCase extends WebTestCase
{
    private const OPENAPI_SPECIFICATION_FILE = &amp;#39;/path/to/specification.json&amp;#39;;

    private static ?ValidatorBuilder $validatorBuilder = null;

    // ...

    protected function makeRequest(
        // ...
        bool $enableOpenApiSpecificationRequestCheck = true,
        bool $enableOpenApiSpecificationResponseCheck = true,
    ): Response {
        // ...

        if ($enableOpenApiSpecificationRequestCheck) {
            $this-&amp;gt;validateOpenApiRequestSpecification($method, $uri, static::$client-&amp;gt;getRequest());
        }

        if ($enableOpenApiSpecificationResponseCheck) {
            $this-&amp;gt;validateOpenApiResponseSpecification($method, $uri, static::$client-&amp;gt;getResponse());
        }

        return $response;
    }

    private function validateOpenApiRequestSpecification(string $method, string $endpoint, Request $request): void
    {
        $validator = $this-&amp;gt;getValidatorBuilder()-&amp;gt;getRequestValidator();
        $validator-&amp;gt;validate(
            new Psr7Request(strtolower($method), $endpoint, $request-&amp;gt;headers-&amp;gt;all(), $request-&amp;gt;getContent()),
        );
    }

    private function validateOpenApiResponseSpecification(string $method, string $endpoint, Response $response): void
    {
        $validator = $this-&amp;gt;getValidatorBuilder()-&amp;gt;getResponseValidator();
        $validator-&amp;gt;validate(
            new OperationAddress($endpoint, strtolower($method)),
            new Psr7Response($response-&amp;gt;getStatusCode(), $response-&amp;gt;headers-&amp;gt;all(), (string) $response-&amp;gt;getContent()),
        );
    }

    private function getValidatorBuilder(): ValidatorBuilder
    {
        return self::$validatorBuilder ??= new ValidatorBuilder()-&amp;gt;fromJsonFile(self::OPENAPI_SPECIFICATION_FILE);
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;De cette manière, à chaque appel à votre API, vous avez la possibilité de vérifier la documentation qui lui est associée. Vous remarquerez au passage qu’il est possible de désactiver la vérification soit de la requête soit de la réponse. Si la désactivation de la vérification de la réponse peut-être discutable, pouvoir désactiver la vérification de la requête est &lt;strong&gt;indispensable&lt;/strong&gt; pour pouvoir tester les cas d’erreurs avec des appels HTTP invalides (afin de pouvoir tester le comportement d’un client qui effectuerait un mauvais appel).&lt;/p&gt;

&lt;p&gt;L’intégration de la validation OpenAPI dans vos tests fonctionnels vous permettra de faire “d’une pierre deux coups”: vous aurez la possibilité d’écrire des tests fonctionnels pour tester le comportement de votre code tout en permettant d’assurer sa cohérence avec votre documentation. Cette approche renforcera la qualité de votre API et évitera des désagréments auprès de vos utilisateurs.  N’hésitez pas à adapter cette méthode à votre contexte et les différents &lt;em&gt;frameworks&lt;/em&gt; que vous utilisez.&lt;/p&gt;
</description>
                    <pubDate>Mon, 28 Jul 2025 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2025/07/28/testez-votre-documentation-openapi-avec-php.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2025/07/28/testez-votre-documentation-openapi-avec-php.html</guid>
                </item>
            
        
            
                53
                <item>
                    <title>C&apos;est quoi un test unitaire ?</title>
                    <description>&lt;p&gt;Depuis que je fais de l’informatique, lorsque l’on parle de tests automatisés, je remarque que tout le monde a une définition différente de ce qu’est un test. Qu’ils soient dits unitaires, d’intégration, fonctionnels ou de bout en bout, je n’ai quasiment jamais vu deux équipes en avoir la même définition. J’aimerais ici donner une définition, la définition que j’adopte dorénavant et qui est actuellement partagée par les équipes avec lesquelles je travaille.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Les tests unitaires sont aujourd’hui considérés comme le Graal par les développeurs. Ce sont les tests qu’il faut absolument écrire. Considérés comme simples à écrire et rapides à s’exécuter, ils doivent permettre de s’assurer du bon fonctionnement d’une partie d’un projet. On les retrouve à la base de la fameuse pyramide des tests:&lt;/p&gt;

&lt;center&gt;
    &lt;img src=&quot;/img/blog/20250522-c-est-quoi-un-test-unitaire/pyramide.png&quot; alt=&quot;Pyramide des tests&quot; style=&quot;width: 50%; height: 50%;&quot; /&gt;
&lt;/center&gt;

&lt;p&gt;Un test unitaire est souvent défini comme une procédure automatisée qui &lt;strong&gt;vérifie le bon fonctionnement d’une unité de code&lt;/strong&gt;. L’unité de code présente la plus petite partie testable d’une application. Cela est, la plupart du temps, une fonction, une méthode ou une classe. L’objectif étant de vérifier le comportement de chaque composant indépendamment du reste du système.&lt;/p&gt;

&lt;p&gt;De mon point de vue et d’expérience, la définition précédente est ambigüe. Personne n’arrive réellement à définir ce qu’est une &lt;strong&gt;unité de code&lt;/strong&gt;. Tout le monde donne sa propre définition et le résultat est de voir des débats interminables au sein des équipes de développeurs.&lt;/p&gt;

&lt;p&gt;Parmi les questions que l’on se pose et qui ne trouvent pas de réponse universelle, doit-on bouchonner (&lt;em&gt;mocker&lt;/em&gt;) tout élément extérieur au code que l’on teste ? Un test peut-il être considéré comme unitaire s’il utilise une instance d’une autre classe ? Cette question pourtant simple ne trouve pas de réponse unniverselle. Certains défendront l’idée qu’il faut isoler au maximum le code testé. D’autres pourront considérer qu’on peut voir l’ensemble comme une unité de code.&lt;/p&gt;

&lt;p&gt;Dans les équipes avec lesquelles je travaille, nous nous sommes accordés sur une définition n’ayant que très peu d’ambigüité et qui mets l’ensemble des développeurs d’accord. Cette définition n’est pas unique et je l’ai également rencontré dans la formation de &lt;a href=&quot;https://www.charlesdesneuf.com &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Charles DESNEUF&lt;/a&gt; sur &lt;a href=&quot;https://formation.charlesdesneuf.com &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;les tests automatisés&lt;/a&gt;. Ainsi, actuellement, je considére qu’un test est unitaire s’il respecte les conditions suivantes:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;s’il ne communique pas avec la base de données,&lt;/li&gt;
  &lt;li&gt;n’interagit pas avec le réseau,&lt;/li&gt;
  &lt;li&gt;ne touche pas au système de fichier,&lt;/li&gt;
  &lt;li&gt;peut s’exécuter en même temps que n’importe quel autre test unitaire,&lt;/li&gt;
  &lt;li&gt;ne nécessite pas de modification de l’environnement pour fonctionner.&lt;/li&gt;
&lt;/ul&gt;
</description>
                    <pubDate>Thu, 22 May 2025 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2025/05/22/c-est-quoi-un-test-unitaire.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2025/05/22/c-est-quoi-un-test-unitaire.html</guid>
                </item>
            
        
            
                54
                <item>
                    <title>L&apos;IA comme catalyseur de création des side-projects</title>
                    <description>&lt;p&gt;C’est une réalité indéniable, l’intelligence artificielle est désormais présente dans notre quotidien professionnel. Nous (développeurs) utilisons dorénavant de nombreux outils toujours plus puissants (ChatGPT, Claude, Cursor, Windsurf, …). Il en résulte que la création de projet devient désormais plus simple, plus rapide. En tant que développeurs, nous aimons explorer de nouvelles idées, tester de nouvelles technologies, expérimenter au travers de projets personnels (&lt;em&gt;side project&lt;/em&gt;). L’arrivée des assistants intelligents nous permet désormais d’itérer et de créer de manière plus ou moins efficace.&lt;/p&gt;

&lt;p&gt;Ce billet est là pour partager mon expérience, expliquer comment je tente de tirer profit de ces outils, que je considère aujourd’hui comme incontournables, pour créer les idées qui me passent par la tête.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Des idées de création d’outils, de sites Web, d’application, j’en ai régulièrement. Pour résoudre une problématique que moi ou des proches avons, pour expérimenter sur des outils ou technologies. Ce ne sont pas les sujets qui manquent, mais le temps (et parfois la motivation). J’ai aujourd’hui trouvé en l’IA, l’aide qui me permet de tester et itérer plus rapidement.&lt;/p&gt;

&lt;p&gt;Généralement, je commence par mettre mon idée par écrit et je commence à itérer sur ce que j’aimerai mettre en place, les technologies que je souhaite utiliser. Pour commencer à lui faire prendre vie, je vais généralement démarrer un prototype avec &lt;a href=&quot;https://v0.dev &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;V0 de Vercel&lt;/a&gt;. Si vous ne connaissez pas V0, il s’agit d’une plateforme de développement boosté à l’IA. Elle est conçue pour générer rapidement des interfaces utilisateurs (en React) à partir de descriptions en langage naturel.&lt;/p&gt;

&lt;p&gt;À cette étape, je m’en sers vraiment comme prototype, l’objectif étant de commencer à matérialiser ce que j’ai en tête. Une fois suffisamment abouti et que je suis satisfait du résultat, je vais généralement demander à l’IA de dumper l’ensemble des écrans de l’application dans de purs fichiers HTML. Je ne fais pas de React, et je ne connais (aujourd’hui) pas suffisamment la technologie pour pouvoir travailler sereinement dessus. Pour moi, il est indispensable de comprendre ce que produit l’IA et les maquettes générées en HTML me permettent de créer l’outil dans une technologie que je vais maîtriser.&lt;/p&gt;

&lt;p&gt;Pour la suite du développement du projet, je bascule sur &lt;a href=&quot;https://cursor.sh &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Cursor&lt;/a&gt;. Ce dernier est l’un des éditeurs de code nouvelle génération les plus en vogue. C’est certainement l’un des outils que j’utilise et que j’explore le plus. Aujourd’hui, au travers de règles personnalisées (règle de développement, architecture), j’ai réussi à trouver un bon équilibre entre écriture de code et génération de code assisté. Habitué aux IDE de Jetbrains, revenir sur un éditeur type Visual Studio Code a demandé un peu d’adaptation, mais le gain de temps est aujourd’hui palpable. Cela nécessite de mettre en place quelques extensions pour avoir un éditeur aux petits oignons, mais ce fut également l’occasion de découvrir de nouveaux &lt;em&gt;plugins&lt;/em&gt;, tels que &lt;a href=&quot;https://www.devsense.com/en/features#vscode &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;cette extension “PHP Tools for Visual Studio Code”&lt;/a&gt; dédiée au langage PHP (qui reste aujourd’hui mon langage principal).&lt;/p&gt;

&lt;p&gt;À titre d’information, j’aimerais sur mes prochains projets tenter de retourner sur un IDE Jetbrains et de tester &lt;a href=&quot;https://www.jetbrains.com/fr-fr/junie/ &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Junie&lt;/a&gt; l’agent IA Jetbrains et/ou l’extension &lt;a href=&quot;https://windsurf.com/plugins &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Windsurf&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;En plus des deux outils dont je viens de parler, j’utilise aussi régulièrement &lt;a href=&quot;https://claude.ai &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Claude&lt;/a&gt; dans de nombreuses tâches. J’utilise beaucoup Claude comme un collègue avec qui je peux échanger, réfléchir ou être challengé sur des tâches de code ou des tâches/questions annexes. Je fais également de nombreuses recherches au travers de &lt;a href=&quot;https://www.perplexity.ai &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Perplexity&lt;/a&gt;, un moteur de recherche basé sur l’intelligence artificielle et qui, contrairement à des moteurs “classiques” ne va pas me retourner une &lt;em&gt;simple liste&lt;/em&gt; de liens, mais fournit des réponses directes, sourcées et conversationnelles.&lt;/p&gt;

&lt;p&gt;Et tout cela à quel coût ? Pas besoin de sortir son porte-monnaie pour profiter de la plupart des outils cités ici. La plupart, voire la totalité sont accessibles gratuitement si vous en avez une utilisation basique et peu intensive. Vous aurez la possibilité de sortir la carte bancaire pour les services que vous utilisez le plus et pour lesquels le plan gratuit ne vous suffirait pas.&lt;/p&gt;

&lt;p&gt;Si au final, s’y mettre n’aura pas été une tâche facile, il n’est pas simple de changer ses habitudes, l’utilisation d’outils boostés à l’IA est en train de transformer de manière radicale la façon de développer. Concevoir des projets personnels n’a jamais autant été à la portée de tous. Moins de barrière à l’entrée, des temps de développement et d’idéation réduits permettent d’explorer de nombreuses idées.&lt;/p&gt;

&lt;p&gt;Pour moi, l’IA est la révolution technologique incontournable. Si vous n’avez pas encore intégré ces outils dans votre quotidien, je vous encourage vivement à les explorer. D’autant plus, que je considère que ce sont des compétences qui vont devenir indispensables à l’avenir et que cela va directement influer sur votre employabilité.&lt;/p&gt;

&lt;p&gt;Il est également important de noter que si l’IA va générer de plus en plus de code, qu’elle va résoudre des bugs et problèmes à notre place, il n’aura jamais été aussi important de &lt;a href=&quot;/blog/2015/01/23/comprenez-les-outils-que-vous-utilisez.html&quot;&gt;comprendre les outils que l’on utilise&lt;/a&gt;, de comprendre ce qui est fait ou généré. Le risque étant à terme de perdre les compétences et la compréhension du système, rendant le fonctionnement, le débogage ou toute autre tâche impossible sans aide d’un outil tiers.&lt;/p&gt;
</description>
                    <pubDate>Tue, 20 May 2025 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2025/05/20/l-ia-comme-catalyseur-de-creation-des-side-projects.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2025/05/20/l-ia-comme-catalyseur-de-creation-des-side-projects.html</guid>
                </item>
            
        
            
                55
                <item>
                    <title>Évaluez la qualité de vos tests avec les tests de mutation</title>
                    <description>&lt;p&gt;Très souvent, lorsque l’on souhaite mettre en place des indicateurs sur les tests d’un projet, la première mesure que l’on va mettre en place est celle de la couverture de code. Et pour beaucoup, il s’agit de la métrique principale permettant d’avoir une idée de qualité des tests d’un projet. Cette dernière sous-entend que plus la couverture est élevée et plus les tests assurent la sécurité du fonctionnement de notre code. Il s’agit pourtant d’un indicateur qui peut être trompeur.&lt;/p&gt;

&lt;p&gt;Il s’agit d’un &lt;a href=&quot;/blog/2023/01/03/a-propos-de-la-couverture-de-code.html&quot;&gt;sujet que j’ai déjà abordé dans ce blog&lt;/a&gt;, expliquant que ce n’est pas parce qu’un test exécute une ligne de code que la vérification du comportement associé est efficace et pertinente. C’est dans ce cadre-là, qu’il est possible de mettre en place des tests de mutation (du &lt;em&gt;mutation testing&lt;/em&gt; en anglais).&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Les tests de mutation introduisent volontairement des défauts dans le code source d’un projet dans le but de vérifier si les tests permettent de détecter les changements de comportement introduits. Ces “défauts” sont appelés des “mutants” (d’où le nom de tests de mutation) et correspondent à des modifications mineures du code tentant de simuler des erreurs de programmation courantes. À la suite de l’exécution de ces tests, un &lt;strong&gt;score de mutation&lt;/strong&gt; est retourné. Ce dernier représente le pourcentage de mutants tués. Plus le score est élevé, plus les tests sont efficaces pour détecter les erreurs.&lt;/p&gt;

&lt;p&gt;Parmi les mutations les plus courantes, on retrouve:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;le remplacement des opérateurs mathématiques: un &lt;code&gt;+&lt;/code&gt; qui devient un &lt;code&gt;-&lt;/code&gt;,&lt;/li&gt;
  &lt;li&gt;la modification des opérateurs de comparaison: une condition d’égalité &lt;code&gt;===&lt;/code&gt; qui devient une négation &lt;code&gt;!==&lt;/code&gt;,&lt;/li&gt;
  &lt;li&gt;la suppression de lignes de code,&lt;/li&gt;
  &lt;li&gt;la modification des valeurs booléennes: un &lt;code&gt;true&lt;/code&gt; qui devient &lt;code&gt;false&lt;/code&gt;,&lt;/li&gt;
  &lt;li&gt;la modification de valeurs de retour dans les fonctions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Les tests de mutation permettent alors d’évaluer la capacité des tests à détecter des erreurs et pas simplement à exécuter du code. Ils mettent en évidence les parties du code où les tests sont insuffisants ou inefficaces. Cela permet ainsi d’ajouter des tests aux endroits les plus pertinents. Au final, ils permettent d’améliorer la confiance envers la suite de tests.&lt;/p&gt;

&lt;p&gt;En PHP, l’outil le plus connu pour cela est certainement &lt;a href=&quot;https://infection.github.io &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Infection&lt;/a&gt;. Ce dernier supporte PHPUnit, PhpSpec et Codeception, nécessite PHP 7.4 au minimum avec au moins une des extensions suivantes: &lt;code&gt;Xdebug&lt;/code&gt;, &lt;code&gt;phpdbg&lt;/code&gt; ou &lt;code&gt;pcov&lt;/code&gt; installée. Son installation est extrêmement simple, puisqu’il suffira de faire un &lt;code&gt;composer require --dev infection/infection&lt;/code&gt;. Une configuration de base sera automatiquement créée au premier lancement d’Infection permettant un démarrage extrêmement rapide.&lt;/p&gt;

&lt;p&gt;Prenons un exemple simple pour illustrer le principe. Une classe &lt;code&gt;Calculator&lt;/code&gt; qui permet d’effectuer des opérations de calcul:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;class Calculator
{
    public function add(int $x, int $y): int
    {
        return $x + $y;
    }

    public function divide(int $x, int $y): float
    {
        if ($y === 0) {
            throw new \InvalidArgumentException(&amp;#39;Division by zero is not allowed.&amp;#39;);
        }

        return $x / $y;
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Cette classe est associée aux tests suivants:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;use PHPUnit\Framework\TestCase;

final class CalculatorTest extends TestCase
{
    public function testAdd(): void
    {
        $addition = new Calculator();

        $result = $addition-&amp;gt;add(2, 3);

        $this-&amp;gt;assertEquals(5, $result);
    }

    public function testDivide(): void
    {
        $addition = new Calculator();

        $result = $addition-&amp;gt;divide(6, 2);

        $this-&amp;gt;assertEquals(3, $result);
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;En lançant les tests de mutation (via la commande &lt;code&gt;vendor/bin/infection&lt;/code&gt;), nous obtenons le rapport suivant:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;Metrics:
   Mutation Score Indicator (MSI): 71%
   Mutation Code Coverage: 85%
   Covered Code MSI: 83%&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Le &lt;code&gt;Mutation Score Indicator&lt;/code&gt; représente le pourcentage de mutants tués par rapport au nombre total de mutants générés. Un mutant tué correspond à au moins un test qui échoue après l’application d’un mutant. Elle constitue la métrique principale des tests de mutation. Elle qui permet de mesurer l’efficacité des tests à détecter des erreurs. Le score doit alors être le plus élevé possible.&lt;/p&gt;

&lt;p&gt;Le &lt;code&gt;Mutation Code Coverage&lt;/code&gt; correspond au pourcentage de code parcouru par les tests de mutation. Cette métrique est similaire à la couverture de code “classique”, mais du point de vue des mutations.&lt;/p&gt;

&lt;p&gt;La dernière métrique, le &lt;code&gt;Covered code MSI&lt;/code&gt; est un calcul effectué uniquement sur le code couvert par les tests. Elle permet notamment d’isoler l’efficacité des tests existants. Cet indicateur répond à la question: “Parmi le code que mes tests exécutent, quelle est leur efficacité à détecter des erreurs”. Cet indicateur permet de détecter si vos tests sont suffisamment présents (indicateur élevé) ou si ces derniers ne permettent pas de détecter les problèmes de manière efficace (indicateur bas).&lt;/p&gt;

&lt;p&gt;Pour améliorer le score des tests de mutation et par rebond la qualité de vos tests en général, il faudra &lt;strong&gt;tester tous les chemins d’exécution&lt;/strong&gt; de votre code (incluant les cas limites et les exceptions). De plus, il ne faut pas se contenter de tester les cas nominaux, il est important de &lt;strong&gt;tester les cas d’erreurs&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Par exemple dans les tests que nous avons écrit précédemment, nous n’avons pas testé le cas d’erreur de la division par zéro. Modifions le code précédent, pour ajouter ce dernier:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;use PHPUnit\Framework\TestCase;

final class CalculatorTest extends TestCase
{
    public function testAdd(): void
    {
        $addition = new Calculator();

        $result = $addition-&amp;gt;add(2, 3);

        $this-&amp;gt;assertEquals(5, $result);
    }

    public function testDivide(): void
    {
        $addition = new Calculator();

        $result = $addition-&amp;gt;divide(6, 2);

        $this-&amp;gt;assertEquals(3, $result);
    }

    public function testDivideByZero(): void
    {
        $addition = new Calculator();

        $this-&amp;gt;expectException(\InvalidArgumentException::class);
        $this-&amp;gt;expectExceptionMessage(&amp;#39;Division by zero is not allowed.&amp;#39;);

        $addition-&amp;gt;divide(6, 0);
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Nous obtenons alors le résultat ci-dessous:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;Metrics:
   Mutation Score Indicator (MSI): 100%
   Mutation Code Coverage: 100%
   Covered Code MSI: 100%&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Si mettre en place un calcul de la couverture de code de vos tests est un bon premier point, si vous souhaitez aller plus loin et avoir une métrique pertinente pour avoir une idée de la qualité de vos tests, les tests de mutation sont une très bonne approche. Vous aurez alors les outils pour identifier et combler les “trous” de vos tests. Et comme toute solution, il sera nécessaire de prêter attention à quelques points.&lt;/p&gt;

&lt;p&gt;Tout d’abord, il est important de noter que les tests de mutation sont des tests qui sont coûteux en termes de performance. La création et la mise en place de mutants sont consommatrices de ressources de calcul et de temps d’exécution. La génération de mutants peut ne pas être parfaite et peut ne pas changer le comportement observable du code. Dans ce dernier cas de figure, les mutants ne peuvent être tués, faussant ainsi le calcul final. On parle alors de “mutants équivalents”. Ces derniers entraînent alors de faux positifs, car ils ne changent pas réellement le comportement attendu.&lt;/p&gt;
</description>
                    <pubDate>Sun, 11 May 2025 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2025/05/11/evaluez-la-qualite-de-vos-tests-avec-les-tests-de-mutation.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2025/05/11/evaluez-la-qualite-de-vos-tests-avec-les-tests-de-mutation.html</guid>
                </item>
            
        
            
                56
                <item>
                    <title>La &quot;Clean Architecture&quot;, bien plus qu&apos;une arborescence de fichier</title>
                    <description>&lt;p&gt;Une grande partie des projets informatique, que j’ai pu voir au cours de ma carrière, conçue autour d’une &lt;a href=&quot;https://fr.wikipedia.org/wiki/Architecture_hexagonale &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;architecture hexagonale&lt;/a&gt;, était bien souvent plus proche d’une &lt;a href=&quot;https://www.elao.com/blog/dev/ddd-practice &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;em&gt;clean architecture&lt;/em&gt;&lt;/a&gt; en pratique. Cette dernière, est souvent découpée en plusieurs couches qui sont &lt;em&gt;Domain&lt;/em&gt;, &lt;em&gt;Application&lt;/em&gt; ou &lt;em&gt;UseCase&lt;/em&gt;, &lt;em&gt;Infrastructure&lt;/em&gt; et plus rarement &lt;em&gt;UserInterface&lt;/em&gt;, sont une représentation physique des couches évoquées par &lt;a href=&quot;http://cleancoder.com &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Robert C. Martin&lt;/a&gt; qui a théorisé cette architecture.&lt;/p&gt;

&lt;p&gt;Beaucoup sont attachés à la représentation physique de ces couches. Pour autant, la &lt;em&gt;clean architecture&lt;/em&gt; ne se résume pas à un simple découpage du code. Le principe fondamental de cette architecture repose en réalité sur le sens des dépendances et non sur la matérialisation des couches du code.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Preuve en est, si vous prenez le livre de référence “&lt;a href=&quot;https://www.amazon.fr/Clean-Architecture-Craftsmans-Software-Structure/dp/0134494164/ref=sr_1_1?__mk_fr_FR=%C3%85M%C3%85%C5%BD%C3%95%C3%91&amp;amp;crid=3BHSE1ILPTKUK&amp;amp;dib=eyJ2IjoiMSJ9.dgexBldN7wHzFBoMGMrczOhCvs3z3IOhmYt46k7wKZOaE3oopaN5ZRkbLI2iEDImO2fA5scawkDLpJHRxTBxc6OBQ12LVyllPPtO1v4k4czJCc70sKJVDD_4Y3E820NDdSy28fuYkzGmjGOD-wsmh9a-MCPHuj0VuZrik3TSoa2l8_bioD51BwiF0UMn8w0u8d74E4zDAGPZM1SQo7yQsOzAVUHFOfJFbNnLhP0yWUyZg3yMB_2VN11WIajB8lE6yMFBK1FPIUqyCPM1EjdMg00tuBTNURCHhGgF-hFhgT0.wBdcYwWyY19KIlR2Jf6qRxJhx9KS4CIJIpAybh4WpTQ&amp;amp;dib_tag=se&amp;amp;keywords=clean+architecture&amp;amp;qid=1744920293&amp;amp;sprefix=clean+architecture,aps,90&amp;amp;sr=8-1&amp;amp;ufe=app_do:amzn1.fos.bb6ea7e0-0ef7-4fc8-9adc-4bf1477881dd &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;em&gt;Clean Architecture: A Craftsman’s Guide to Software Structure and Design: A Craftsman’s Guide to Software Structure and Design&lt;/em&gt;&lt;/a&gt;” (lien non affilié), sur les un peu plus de 370 pages du livre, 8 pages sont consacrées au découpage des couches représenté par le diagramme ci-dessous et qui pourtant sert à résumé ce qu’est la &lt;em&gt;clean architecture&lt;/em&gt; dans 90% des articles du Web.&lt;/p&gt;

&lt;center&gt;
    &lt;img src=&quot;/img/blog/20250418-la-clean-architecture-bien-plus-qu-une-arborescence-de-fichiers/clean-architecture.png&quot; alt=&quot;Représentation schématique de la clean architecture&quot; width=&quot;100%&quot; height=&quot;100%&quot; style=&quot;max-width: 1204px; max-height: 864px;&quot; /&gt;
&lt;/center&gt;

&lt;p&gt;Les couches présentes sont généralement représentées par l’arborescence suivante:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;.
└── src
    ├── ContexteMetier1
    │   ├── Application
    │   ├── Domain
    │   ├── Infrastructure
    │   └── UserInterface
    └── ContexteMetier2
        ├── Application
        ├── Domain
        ├── Infrastructure
        └── UserInterface
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Et pour beaucoup, la &lt;em&gt;clean architecture&lt;/em&gt; se résume à ce découpage.&lt;/p&gt;

&lt;p&gt;Pourtant, comme je l’ai rapidement indiqué en introduction de ce billet, le principe fondamental de la &lt;em&gt;clean architecture&lt;/em&gt; est &lt;strong&gt;le sens des dépendances&lt;/strong&gt;, ces dernières devant pointer vers l’intérieur du cercle. Dis autrement, les couches métiers (&lt;em&gt;Domain&lt;/em&gt;) contenant les règles métier ne doivent pas dépendre du code qui implémente les détails techniques (les couches extérieures). Concrètement, cela signifie que vos objets métiers (tel qu’une &lt;code&gt;Commande&lt;/code&gt; ou une &lt;code&gt;Facture&lt;/code&gt;) ne devraient avoir connaissance ni de l’&lt;em&gt;ORM&lt;/em&gt;, ni du &lt;em&gt;Framework&lt;/em&gt; utilisé. La couche &lt;em&gt;Application&lt;/em&gt; qui orchestre les cas d’utilisation peut connaître les objets métier, mais reste indépendante des éléments d’infrastructure, tels que le client HTTP utilisé. Seule cette dernière couche, la couche se trouvant à l’extrémité des dépendances peut dépendre de &lt;em&gt;frameworks&lt;/em&gt; et autres outils techniques (tel que Guzzle ou Symfony HTTP Client dans le cas d’un client HTTP).&lt;/p&gt;

&lt;p&gt;Ce sens des dépendances est notamment illustré par le &lt;a href=&quot;https://fr.wikipedia.org/wiki/SOLID_(informatique) &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;principe SOLID&lt;/a&gt; donc les différentes notions sont présentées de manière détaillée dans le livre. C’est le cas du principe d’&lt;strong&gt;inversion des dépendances&lt;/strong&gt; qui contribue à la flexibilité et à la modularité du code face aux changements et aux évolutions du code.&lt;/p&gt;

&lt;p&gt;Respecter les principes précédents permet de mieux découper et isoler la logique métier permettant de &lt;strong&gt;faciliter l’écriture des tests&lt;/strong&gt;. Les éléments techniques étant isolés, il devient facile de remplacer une implémentation d’une base de données ou d’un composant technique offrant une &lt;strong&gt;résistance au changement&lt;/strong&gt;. La &lt;strong&gt;compréhension du code est facilitée&lt;/strong&gt; du fait que le cœur de l’application est mis en valeur par l’absence de bruit technique et se concentre sur ce qui a de la valeur pour le métier.&lt;/p&gt;

&lt;p&gt;Si la &lt;em&gt;clean architecture&lt;/em&gt; ne repose finalement pas sur une arborescence de dossier, cela reste néanmoins un outil pratique pour matérialiser, visualiser et se repérer dans les différents éléments du code. L’arborescence permet de rendre visible la séparation des différentes couches qui composent l’architecture. Elle permet de créer une convention d’équipe qui simplifie la revue du code, la collaboration entre développeurs et participe à l’uniformisation des pratiques. Elle permet également de faciliter la mise en place &lt;a href=&quot;/blog/2025/04/15/un-projet-coherent-sur-le-long-terme-grace-aux-tests-d-architecture.html&quot;&gt;des contrôles de cohérence d’architecture&lt;/a&gt; avec des outils tels que &lt;a href=&quot;https://deptrac.github.io/deptrac/ &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Deptrac&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Ne résumez pas la &lt;em&gt;clean architecture&lt;/em&gt; a une arborescence de fichier. Cherchez avant tout à isoler le métier des dépendances techniques. Créez une architecture et un code résilient face au changement et qui soit capable d’évoluer sans friction au cours du temps. Néanmoins, je reste convaincu qu’il est important que votre architecture puisse se rattacher à des principes et concepts de base et connu d’un grand nombre, permettant de trouver facilement de l’information dans la littérature.&lt;/p&gt;
</description>
                    <pubDate>Fri, 18 Apr 2025 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2025/04/18/la-clean-architecture-bien-plus-qu-une-arborescence-de-fichiers.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2025/04/18/la-clean-architecture-bien-plus-qu-une-arborescence-de-fichiers.html</guid>
                </item>
            
        
            
                57
                <item>
                    <title>Un projet cohérent sur le long terme grâce aux tests d&apos;architecture</title>
                    <description>&lt;p&gt;L’architecture d’un projet joue un rôle crucial dans sa maintenabilité et son évolution. Si généralement au début d’un projet, il est facile de bien structurer son code, l’ajout successif de nouvelles fonctionnalités, l’arrivée ou le passage de nouveaux développeurs peuvent dégrader la qualité des fondations du projet au cours du temps. C’est dans ce cadre que les tests d’architecture vont nous aider à documenter et à garantir la cohérence de notre architecture sur le long terme.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Les tests d’architecture permettent de vérifier que le code d’un projet respecte les règles et contraintes architecturales qui ont été définies. Ils sont là pour s’assurer que le code et ses interactions sont correctement structurés. Ils permettent de &lt;strong&gt;maintenir la cohérence du code&lt;/strong&gt; au fil des évolutions. Si des règles sont enfreintes, les tests permettront de relever les erreurs.&lt;/p&gt;

&lt;p&gt;De plus, ces tests pourront également servir de documentation, puisqu’il sera nécessaire de matérialiser explicitement les règles applicables. Cela permettra ainsi de &lt;strong&gt;faciliter l’intégration des nouveaux développeurs&lt;/strong&gt; qui pourront prendre connaissance des règles d’architecture du projet tout en ayant un filet de sécurité qui les empêcheront de faire des erreurs.&lt;/p&gt;

&lt;p&gt;Pour mettre en place ce type de tests, il existe une multitude d’outils. Je vais dans ce billet en présenter quelques-uns provenant de l’écosystème PHP.&lt;/p&gt;

&lt;p&gt;Parmi les outils existants, l’un des plus anciens (et peut-être l’un des plus connus) est certainement &lt;a href=&quot;https://github.com/deptrac/deptrac &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Deptrac&lt;/a&gt;. Il s’agit d’un outil d’analyse statique qui permet de définir des règles de dépendance entre les différentes couches de code qui sont ensuite vérifiées par l’outil. La configuration s’effectue au travers d’un fichier YAML (ou PHP), comme celui-ci :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;paths:
  - ./src
exclude_files:
  - &amp;#39;#.*test.*#&amp;#39;
layers:
  - name: Controller
    collectors:
      - type: className
        regex: .*\\Controller\\.*
  - name: Service
    collectors:
      - type: className
        regex: .*\\Service\\.*
  - name: Repository
    collectors:
      - type: className
        regex: .*\\Repository\\.*
ruleset:
  Controller:
    - Service
  Service:
    - Repository
  Repository: ~&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;On peut également présenter &lt;a href=&quot;https://github.com/phparkitect/arkitect &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;PHPArkitect&lt;/a&gt; comme alternative à Deptrac. Tout comme Deptrac, PHPArkitect permet de faire respecter les règles d’architecture. Ce dernier se distingue par son approche de configuration qui se veut expressive via du code PHP.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;use Arkitect\ClassSet;
use Arkitect\CLI\Config;
use Arkitect\Expression\ForClasses\HaveNameMatching;
use Arkitect\Expression\ForClasses\NotHaveDependencyOutsideNamespace;
use Arkitect\Expression\ForClasses\ResideInOneOfTheseNamespaces;
use Arkitect\Rules\Rule;

return static function (Config $config): void {
    $mvcClassSet = ClassSet::fromDir(__DIR__.&amp;#39;/mvc&amp;#39;, __DIR__.&amp;#39;/lib/my-lib/src&amp;#39;);

    $rules = [];

    $rules[] = Rule::allClasses()
        -&amp;gt;that(new ResideInOneOfTheseNamespaces(&amp;#39;App\Controller&amp;#39;))
        -&amp;gt;should(new HaveNameMatching(&amp;#39;*Controller&amp;#39;))
        -&amp;gt;because(&amp;#39;we want uniform naming&amp;#39;);

    $rules[] = Rule::allClasses()
        -&amp;gt;that(new ResideInOneOfTheseNamespaces(&amp;#39;App\Domain&amp;#39;))
        -&amp;gt;should(new NotHaveDependencyOutsideNamespace(&amp;#39;App\Domain&amp;#39;))
        -&amp;gt;because(&amp;#39;we want protect our domain&amp;#39;);

    $config
        -&amp;gt;add($mvcClassSet, ...$rules);
};&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Il existe également &lt;a href=&quot;https://github.com/carlosas/phpat/ &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;PHPat&lt;/a&gt; (alias PHP Architecture Tester) qui est un outil basé sur &lt;a href=&quot;https://phpstan.org &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;PHPStan&lt;/a&gt;. L’approche de configuration est très similaire à l’outil précédent, la différence essentielle étant que PHPArkitect peut fonctionner de manière autonome, alors que pour utiliser PHPat, il sera nécessaire d’installer PHPStan.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;use PHPat\Selector\Selector;
use PHPat\Test\Builder\Rule;
use PHPat\Test\PHPat;
use App\Domain\SuperForbiddenClass;

final class MyFirstTest
{
    public function test_domain_does_not_depend_on_other_layers(): Rule
    {
        return PHPat::rule()
            -&amp;gt;classes(Selector::inNamespace(&amp;#39;App\Domain&amp;#39;))
            -&amp;gt;shouldNotDependOn()
            -&amp;gt;classes(
                Selector::inNamespace(&amp;#39;App\Application&amp;#39;),
                Selector::inNamespace(&amp;#39;App\Infrastructure&amp;#39;),
                Selector::classname(SuperForbiddenClass::class),
                Selector::classname(&amp;#39;/^SomeVendor\\\.*\\\ForbiddenSubfolder\\\.*/&amp;#39;, true)
            )
            -&amp;gt;because(&amp;#39;this will break our architecture, implement it another way! see /docs/howto.md&amp;#39;);
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Le dernier outil que je vais présenter pour faire des tests d’architecture est &lt;a href=&quot;https://pestphp.com &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Pest&lt;/a&gt;. Pest est un outil largement utilisé dans l’écosystème Laravel. S’il a été initialement conçu pour écrire des tests unitaires/fonctionnels, il permet également de vérifier les règles architecturales d’un projet. L’avantage d’utiliser ce dernier est d’avoir un outil qui permet à la fois de gérer vos tests unitaires et d’architecture en utilisant une syntaxe similaire.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;arch()
    -&amp;gt;expect(&amp;#39;App&amp;#39;)
    -&amp;gt;toUseStrictTypes()
    -&amp;gt;not-&amp;gt;toUse([&amp;#39;die&amp;#39;, &amp;#39;dd&amp;#39;, &amp;#39;dump&amp;#39;]);
arch()
    -&amp;gt;expect(&amp;#39;App\Models&amp;#39;)
    -&amp;gt;toBeClasses()
    -&amp;gt;toExtend(&amp;#39;Illuminate\Database\Eloquent\Model&amp;#39;)
    -&amp;gt;toOnlyBeUsedIn(&amp;#39;App\Repositories&amp;#39;)
    -&amp;gt;ignoring(&amp;#39;App\Models\User&amp;#39;);
arch()
    -&amp;gt;expect(&amp;#39;App\Http&amp;#39;)
    -&amp;gt;toOnlyBeUsedIn(&amp;#39;App\Http&amp;#39;);
arch()
    -&amp;gt;expect(&amp;#39;App\*\Traits&amp;#39;)
    -&amp;gt;toBeTraits();
arch()-&amp;gt;preset()-&amp;gt;php();
arch()-&amp;gt;preset()-&amp;gt;security()-&amp;gt;ignoring(&amp;#39;md5&amp;#39;);&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Peu importe l’outil que vous utiliserez (ce n’est finalement qu’un détail d’implémentation), les tests d’architecture représentent une étape cruciale de vos pratiques de développement. Grâce aux outils présentés dans cet article, vous aurez la possibilité de garantir le respect de vos règles d’architecture, limitant ainsi les dérives naturelles qui arrivent au cours de la vie d’un projet.&lt;/p&gt;

&lt;p&gt;En plus de la garantie que vos règles soient respectées, la mise en place de ce genre de tests vous permettra de rendre les règles explicites, pouvant ainsi servir de documentation. Le Graal étant bien entendu que ces tests soient lancés dans un environnement d’intégration continue.&lt;/p&gt;
</description>
                    <pubDate>Tue, 15 Apr 2025 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2025/04/15/un-projet-coherent-sur-le-long-terme-grace-aux-tests-d-architecture.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2025/04/15/un-projet-coherent-sur-le-long-terme-grace-aux-tests-d-architecture.html</guid>
                </item>
            
        
            
                58
                <item>
                    <title>Structurez votre code explicitement avec la &quot;Screaming Architecture&quot;</title>
                    <description>&lt;p&gt;Vous est-il déjà arrivé d’ouvrir un projet, de regarder la structure de son code, l’organisation des différents répertoires et de n’avoir aucune information sur les fonctionnalités, les cas d’utilisation ou encore le métier géré par ce dernier ? En ce qui me concerne, c’est le cas de nombreux projets que j’ai pu voir, car bien souvent, la structure des projets est organisée par couches techniques orientées dont la structure dépendra généralement du &lt;em&gt;framework&lt;/em&gt; utilisé. C’est exactement pour éviter ce genre de déconvenues qu’est né le concept de Screaming Architecture.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;La &lt;code&gt;Screaming Architecture&lt;/code&gt; (que l’on pourrait traduire par l’&lt;em&gt;Architecture Criante&lt;/em&gt; en français) est un principe d’architecture logicielle théorisé par &lt;a href=&quot;https://fr.wikipedia.org/wiki/Robert_C._Martin&quot;&gt;Robert C. Martin&lt;/a&gt; (alias Uncle Bob) et qui stipule que la structure de code d’un projet devrait révéler clairement l’intention et la fonction d’un logiciel sans avoir à entrer dans les détails d’implémentation.&lt;/p&gt;

&lt;p&gt;Or, bien souvent, lorsque l’on démarre un nouveau projet avec son &lt;em&gt;framework&lt;/em&gt; préféré, nous utilisons le squelette par défaut proposé par ce dernier. Par exemple, la structure d’un projet Symfony se présente généralement sous la forme suivante:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;src
├── Command
├── Controller
├── DataFixtures
├── Entity
├── Event
├── EventSubscriber
├── Form
├── Pagination
├── Repository
├── Security
├── Twig
├── Utils
└── Kernel.php
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Il serait préférable de regrouper les différents éléments au sein de dossiers représentant des contextes métier et fonctionnels, ce qui permettra de rendre plus facile la compréhension globale d’une fonctionnalité en évitant de devoir naviguer dans le code pour comprendre ou modifier un comportement.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;src
├── Authentication
├── Inventory
├── Order
├── Reporting
├── Shipping
├── User
└── Kernel.php
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Libre à vous ensuite, de choisir le découpage de chacun des contextes. Vous pouvez utiliser une structure orientée &lt;em&gt;Clean Architecture&lt;/em&gt; ou architecture hexagonale, mais aussi du &lt;em&gt;Vertical Slice&lt;/em&gt; ou un découpage plus courant en CRUD ou RAD. Le choix pouvant être fait selon les besoins et la complexité de chacun des contextes.&lt;/p&gt;

&lt;p&gt;Ce mode de fonctionnement possède de nombreux avantages:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;la &lt;strong&gt;structure révèle l’intention métier&lt;/strong&gt; rendant l’organisation du projet plus expressive et facilitant la compréhension de ce dernier par un nouveau développeur,&lt;/li&gt;
  &lt;li&gt;le code devient un peu plus &lt;strong&gt;indépendant du &lt;em&gt;framework&lt;/em&gt;&lt;/strong&gt; car vous organisez votre code en fonction de votre métier et non pas en fonction de la couche technique que vous utilisez,&lt;/li&gt;
  &lt;li&gt;les &lt;strong&gt;détails d’implémentation sont relégués au second plan&lt;/strong&gt; mettant en avant la logique métier.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Lorsque vous tentez de mettre en place une &lt;em&gt;Screaming Architecture&lt;/em&gt;, le plus important est de commencer par vous focaliser sur les cas d’utilisation que vous allez résoudre. Séparez ces derniers autour de concepts métier sans réfléchir aux détails techniques.&lt;/p&gt;

&lt;p&gt;La &lt;em&gt;Screaming Architecture&lt;/em&gt; n’est pas seulement une façon d’organiser du code, c’est une philosophie qui place l’intention du logiciel au premier plan. Avec ce principe, créez des projets plus compréhensibles et maintenables. Attention cependant à ne pas tomber dans l’&lt;em&gt;over engineering&lt;/em&gt;, ne multipliez pas les couches ou classes. Trouvez le bon équilibre entre principe de base et pragmatisme, c’est le secret de tous les projets qui durent.&lt;/p&gt;
</description>
                    <pubDate>Mon, 07 Apr 2025 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2025/04/07/structurez-votre-code-explicitement-avec-la-screaming-architecture.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2025/04/07/structurez-votre-code-explicitement-avec-la-screaming-architecture.html</guid>
                </item>
            
        
            
                59
                <item>
                    <title>Le principe d&apos;Exponential Backoff</title>
                    <description>&lt;p&gt;La majorité des applications modernes sont amenées à communiquer avec des systèmes tiers, tels que des services Web, des bases de données, des systèmes de messagerie ou de files d’attente, etc. Ces derniers peuvent rencontrer des pannes temporaires liées à des facteurs externes, perte de connexion réseau, pic d’activité ou panne entraînant une interruption temporaire de service. Un des moyens possibles permettant d’améliorer la résilience de nos projets est d’appliquer le principe d’&lt;em&gt;exponential backoff&lt;/em&gt;.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;L’&lt;em&gt;exponential backoff&lt;/em&gt; est un algorithme qui est utilisé pour gérer les tentatives de reconnexion ou de réexécution d’une opération après un échec. Le principe consiste à effectuer plusieurs tentatives d’une opération en appliquant un temps d’attente entre chaque nouvelle tentative. Ce temps d’attente sera de plus en plus important après chacun des essais infructueux. Cette approche permet notamment de réduire la pression sur le système en défaillance et d’augmenter les chances de succès lors de défaillances temporaires.&lt;/p&gt;

&lt;p&gt;Le principe de fonctionnement de base est très simple à mettre en place, puisqu’après chaque échec, on attend un délai qui va augmenter selon une fonction exponentielle avant de retenter l’opération. Cela peut se traduire par le code suivant: &lt;code&gt;$delay = $initialDelay * ($factor ^ $attempt)&lt;/code&gt;. Le délai augmentera ainsi jusqu’au nombre maximal de tentatives que vous souhaitez effectuer. Il peut également être intéressant de définir un nombre maximal de tentatives autorisées.&lt;/p&gt;

&lt;p&gt;Les algorithmes d’&lt;em&gt;exponential backoff&lt;/em&gt; permettent généralement de définir un paramètre nommé &lt;code&gt;jitter&lt;/code&gt;. Il s’agit d’un élément aléatoire qui va permettre de faire varier le délai calculé afin d’éviter que plusieurs tentatives d’opérations simultanées n’aient lieu au même moment dans le cas de traitements effectués en parallèle. Incluant ce paramètre, la formule de calcul du délai d’attente pourrait être la suivante: &lt;code&gt;$delayToApply = $delay * 1 + (mt_rand(-$jitter, $jitter))&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Une implémentation complète en PHP pourrait se traduire ainsi:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;function executeExponentialBackoff(
    callable $fn,
    int $maximumAttempt = 5,
    int $initialDelay = 100,
    float $factor = 2,
    float $jitter = 0.25,
    int $maximumDelay = 30_000
) {
    $attempts = 0;

    while ($attempts &amp;lt; $maximumAttempt) {
        try {
            return $fn();
        } catch (Exception $e) {
            $attempts++;

            if ($attempts == $maximumAttempt) {
                throw new Exception(&amp;quot;Failed after {$maximumAttempt} attempts: &amp;quot; . $e-&amp;gt;getMessage());
            }

            $delay = min($initialDelay * pow($factor, $attempts - 1), $maximumDelay);

            $delayWithJitter = $delay * (1 + mt_rand(-$jitter * 100, $jitter * 100) / 100);

            $delayToApply = (int)($delayWithJitter * 1000);

            echo &amp;quot;Attempt {$attempts} failed. New attempt in &amp;quot; . round($delayWithJitter / 1000, 2) . &amp;quot;s\n&amp;quot;;

            usleep($delayToApply);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Afin de pouvoir mettre en place ce mécanisme dans les meilleures conditions, il sera essentiel de veiller à ce que les opérations effectuées soient &lt;strong&gt;idempotentes&lt;/strong&gt; et qu’elles puissent être exécutées plusieurs fois tout en ayant le même résultat à chaque appel. Il faudra également veiller à bien configurer les paramètres qui vont influer sur les temps d’attente.&lt;/p&gt;

&lt;p&gt;L’&lt;em&gt;exponential backoff&lt;/em&gt; est une technique à la fois simple et efficace. Elle est aujourd’hui mise en place dans de nombreux projets et est implémentée dans la plupart des frameworks. On retrouve par exemple ce principe dans &lt;a href=&quot;https://symfony.com/doc/current/http_client.html#retry-failed-requests &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Symfony&lt;/a&gt; ou encore &lt;a href=&quot;https://laravel.com/docs/12.x/http-client#retries &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Laravel&lt;/a&gt;. Mais il existe également des bibliothèques indépendantes, telles que &lt;a href=&quot;https://github.com/EventSaucePHP/BackOff &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;eventsauce/backoff&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
</description>
                    <pubDate>Tue, 18 Mar 2025 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2025/03/18/le-principe-d-exponential-backoff.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2025/03/18/le-principe-d-exponential-backoff.html</guid>
                </item>
            
        
            
                60
                <item>
                    <title>Les tests automatisés à eux seuls ne garantissent pas le bon fonctionnement de vos applications</title>
                    <description>&lt;p&gt;Lorsque l’on commence à écrire des tests, nous avons souvent un sentiment de sécurité vis-à-vis du bon fonctionnement de nos applications. Une fonctionnalité a été conçue, une série de tests automatisés (qu’ils soient unitaires, fonctionnels, d’intégrations, parfois même en parallèle de tests manuels) a été mis en place. Tous les voyants sont au vert, vous êtes confiants, et pourtant, une fois en production… tout ne se passe pas comme attendu.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Il est indéniable qu’écrire des tests automatisés est essentiel lorsque l’on développe des projets informatiques. Ces derniers peuvent être joués à chaque modification d’un projet, participent à l’amélioration de la qualité et permettent de détecter des changements de comportement d’une application lors de modification dans le code.&lt;/p&gt;

&lt;p&gt;Néanmoins, il est faux de penser qu’un projet avec des tests, indépendamment de leurs qualités, fonctionnera sans faille. La mise en place de tests ayant des limites.&lt;/p&gt;

&lt;p&gt;Tout d’abord, le &lt;strong&gt;taux de couverture&lt;/strong&gt; du code est un élément à prendre en compte lorsque l’on souhaite mesurer la qualité de nos tests. J’ai déjà évoqué &lt;a href=&quot;/blog/2023/01/03/a-propos-de-la-couverture-de-code.html&quot;&gt;la couverture de code&lt;/a&gt; dans ce blog, expliquant que c’est un indicateur à prendre avec précaution. Mais même avec la meilleure volonté du monde, il sera impossible de tester toutes les combinaisons possibles d’état de votre système.&lt;/p&gt;

&lt;p&gt;Ensuite, quand on écrit des tests, de la même manière que lorsque l’on écrit du code applicatif, il est possible de faire des erreurs. Un test mal écrit, des jeux de données irréalistes, incomplets ou tout simplement biaisés peuvent aussi rendre nos &lt;strong&gt;tests imparfaits&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;De plus, les tests sont généralement exécutés dans un environnement contrôlé ayant peu de dépendance avec l’extérieur. Les interactions avec des systèmes tiers sont généralement bouchonnées (&lt;em&gt;mock&lt;/em&gt; en anglais). Cela crée un &lt;strong&gt;écart entre l’environnement de test et l’environnement réel&lt;/strong&gt; d’exécution. Bien qu’il soit possible de créer des environnements au plus proches de la production, l’environnement et les conditions identiques de production ne pourront jamais être totalement rassemblés.&lt;/p&gt;

&lt;p&gt;Et pour finir, si l’on considère que le développement d’une fonctionnalité résulte de la compréhension de cette dernière par le ou les développeurs qui l’implémente, alors une &lt;strong&gt;fonctionnalité mal comprise&lt;/strong&gt; pourra entrainer l’écriture d’un test qui validera le fonctionnement compris sans pour autant répondre au besoin utilisateur.&lt;/p&gt;

&lt;p&gt;Si les tests automatisés constituent un outil puissant, ils ne suffisent pas à garantir le bon fonctionnement d’une application. J’entends pourtant de nombreux développeurs garantir que leur développement est OK “parce qu’il est testé”. Certes les tests permettent de limiter les erreurs et les régressions, comme nous avons pu le voir, de nombreux facteurs sont à prendre en compte.&lt;/p&gt;
</description>
                    <pubDate>Wed, 12 Mar 2025 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2025/03/12/les-tests-automatises-a-eux-seuls-ne-garantissent-pas-le-bon-fonctionnement-de-vos-applications.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2025/03/12/les-tests-automatises-a-eux-seuls-ne-garantissent-pas-le-bon-fonctionnement-de-vos-applications.html</guid>
                </item>
            
        
            
                61
                <item>
                    <title>Des objets autovalidants avec le &quot;Self-Validating Object&quot; pattern</title>
                    <description>&lt;p&gt;J’ai déjà évoqué dans ce blog &lt;a href=&quot;/blog/2020/04/04/coherence-des-donnees-dans-un-modele-oriente-objet.html&quot;&gt;la notion de cohérence des données&lt;/a&gt; au sein d’un modèle orienté objet.
Ce principe est connu sous le nom de &lt;em&gt;Self-Validating Object&lt;/em&gt; (objet autovalidant). On le retrouve également sous le nom de &lt;em&gt;Validated Domain Object&lt;/em&gt; ou &lt;em&gt;Invariant-Enforcing Object&lt;/em&gt; dans la littérature proche du &lt;em&gt;Domain Driven Design&lt;/em&gt;. Il s’agit d’un modèle de conception consistant à ajouter une logique de validation directement dans les objets.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Cette approche possède de nombreux avantages. Tout d’abord, elle permet de garantir la cohérence des données, tout en renforçant l’encapsulation de nos objets, puisque ce sont ces derniers qui sont responsables de leurs propres validations. De plus, en cas d’erreur d’une donnée ou d’une règle de validation précise, il est possible de générer des erreurs précises.&lt;/p&gt;

&lt;p&gt;Prenons un exemple. Le plus évident et commun est certainement celui du constructeur d’un objet. Nous allons donc modéliser un horaire que l’on va construire autour de 3 propriétés que sont: l’heure, la minute et la seconde.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;readonly class Time
{
    public function __construct(
        public int $hour,
        public int $minute,
        public int $second,
    ) {
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Pour que l’objet soit autovalidant, nous allons y ajouter les contrôles nécessaires sur nos propriétés:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;readonly class Time
{
    public function Time(
        public int $hour,
        public int $minute,
        public int $second,
    ) {
        if ($this-&amp;gt;hour &amp;lt; 0 || $this-&amp;gt;hour &amp;gt; 23) {
            throw new InvalidArgumentException(&amp;quot;Hour value should be between 0 and 23: {$this-&amp;gt;hour} given.&amp;quot;);
        }

        if ($this-&amp;gt;minute &amp;lt; 0 || $this-&amp;gt;minute &amp;gt; 59) {
            throw new InvalidArgumentException(&amp;quot;Minute value should be between 0 and 59: {$this-&amp;gt;minute} given.&amp;quot;);
        }

        if ($this-&amp;gt;second &amp;lt; 0 || $this-&amp;gt;second &amp;gt; 59) {
            throw new InvalidArgumentException(&amp;quot;Second value should be between 0 and 59: {$this-&amp;gt;second} given.&amp;quot;);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;La classe ci-dessus est autovalidate. Elle vérifie que les données qu’on lui donne correspondent effectivement à un horaire conforme. Dans le cas contraire, une exception est levée avec un message d’erreur explicite. Nous avons ici pris l’exemple d’un constructeur, mais le principe serait exactement le même sur une méthode de notre objet.&lt;/p&gt;

&lt;p&gt;Il s’agit de l’implémentation la plus courante que l’on retrouve dans les bases de code. Elle est simple et rapide à mettre en place. Néanmoins, elle présente un inconvénient majeur: chaque donnée est validée individuellement, et la vérification s’arrête à la première erreur détectée. Ainsi, si on tente de créer l’instance &lt;code&gt;new Time(25, 120, 0)&lt;/code&gt;, on obtiendra d’abord l’erreur &lt;code&gt;Hour value should be between 0 and 23: 25 given.&lt;/code&gt;, mais sans aucune information sur les minutes, qui sont pourtant invalide. Ce n’est qu’après la correction de cette première erreur qu’on remarquera celle concernant les minutes.&lt;/p&gt;

&lt;p&gt;Sur un objet simple comme celui que nous utilisons ici, ce n’est pas très grave, car il contient peu de propriétés. Cependant, sur des applications métiers plus complexes, cela peut s’avérer plus contraignant. L’idéal serait donc de mettre en place un mécanisme de validation permettant de vérifier l’intégrité de l’ensemble des données et de remonter l’ensemble des erreurs d’un seul coup. Nous pouvons alors imaginer un nouveau &lt;em&gt;pattern&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Commençons par créer un conteneur pouvant stocker plusieurs erreurs:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;class ValidationException extends InvalidArgumentException
{
    /**
     * @param list&amp;lt;string&amp;gt; $errors
     */
    public function __construct(
        string $message,
        public private(set) array $errors,
    ) {
        parent::__construct($message);
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;L’idée est ensuite d’effectuer chacune des vérifications de nos propriétés en ajoutant les erreurs dans un tableau et lever l’exception précédente si ce dernier n’est pas vide:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;readonly class Time
{
    public function __construct(
        public int $hour,
        public int $minute,
        public int $second,
    ) {
        $errors = [];
        if ($this-&amp;gt;hour &amp;lt; 0 || $this-&amp;gt;hour &amp;gt; 23) {
            $errors[] = &amp;quot;Hour value should be between 0 and 23: {$this-&amp;gt;hour} given.&amp;quot;;
        }

        if ($this-&amp;gt;minute &amp;lt; 0 || $this-&amp;gt;minute &amp;gt; 59) {
            $errors[] = &amp;quot;Minute value should be between 0 and 59: {$this-&amp;gt;minute} given.&amp;quot;;
        }

        if ($this-&amp;gt;second &amp;lt; 0 || $this-&amp;gt;second &amp;gt; 59) {
            $errors[] = &amp;quot;Second value should be between 0 and 59: {$this-&amp;gt;second} given.&amp;quot;;
        }

        if (!empty($errors)) {
            throw new ValidationException(&amp;#39;Invalid time data.&amp;#39;, $errors);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Maintenant, lorsque nous tentons d’instancier notre objet avec plusieurs propriétés invalides, nous obtenons une exception qui contient &lt;strong&gt;toutes&lt;/strong&gt; les erreurs:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;$time = new Time(25, 120, 0);

// class ValidationException#2 (8) {
//   protected $message =&amp;gt;
//   string(18) &amp;quot;Invalid time data.&amp;quot;
//   public array $errors =&amp;gt;
//   array(2) {
//     [0] =&amp;gt;
//     string(38) &amp;quot;Hour value should be between 0 and 23: 25 given.&amp;quot;
//     [1] =&amp;gt;
//     string(40) &amp;quot;Minute value should be between 0 and 59: 120 given.&amp;quot;
//   }
// }&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Ce mécanisme de validation peut être très intéressant même s’il est un peu plus complexe à mettre en place. Il fonctionne pour des objets ou structures de données relativement simples. Si un objet nécessite une validation contextuelle ou dépendante de plusieurs objets extérieurs, il sera peut-être préférable de sortir la logique de validation de l’objet lui-même.&lt;/p&gt;
</description>
                    <pubDate>Sat, 01 Mar 2025 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2025/03/01/des-objets-autovalidants-avec-le-self-validating-object-pattern.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2025/03/01/des-objets-autovalidants-avec-le-self-validating-object-pattern.html</guid>
                </item>
            
        
            
                62
                <item>
                    <title>Tester les implémentations d&apos;une interface avec le &quot;Behavioral Contract Testing&quot;</title>
                    <description>&lt;p&gt;Lorsque l’on doit écrire plusieurs implémentations d’une interface, il est crucial de veiller à ce qu’elles aient le même comportement. Par exemple, une interface &lt;code&gt;PaymentProcessor&lt;/code&gt; permettant de s’interfacer avec plusieurs plateformes de paiements possède trois implémentations, il est important que chacune d’entre elles se comporte de la même manière. Pour cela, il est possible d’utiliser le principe du &lt;strong&gt;Behavioral Contract Testing&lt;/strong&gt;.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Le &lt;strong&gt;Behavioral Contract Testing&lt;/strong&gt; (qui est également appelé &lt;strong&gt;Interface Contract Testing&lt;/strong&gt;) est un sous-ensemble du &lt;strong&gt;Contract Testing&lt;/strong&gt;. C’est une technique de test qui permet de vérifier les contrats (ou interfaces dans notre cas) entre différents composants d’un système. Cette approche va nous permettre de garantir que le comportement de chaque implémentation va être identique, tout en mutualisant le code nécessaire à la mise en place des tests. Comme souvent avec les tests,  le &lt;em&gt;Contract Testing&lt;/em&gt; peut servir de documentation vivante, chacun des tests décrivant un comportement attendu.&lt;/p&gt;

&lt;p&gt;Pennons maintenant un exemple concret: une abstraction permettant de gérer un système de cache. Commençons par définir notre contrat d’interface:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;interface Cache
{
    public function get(string $key): mixed;

    public function set(string $key, mixed $value, ?int $ttl = null): void;

    public function delete(string $key): void;
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Cette dernière pourrait être déclinée en de multiples implémentations, telles que: un cache mémoire, fichier ou encore en base de données…&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;final class InMemoryCache implements Cache
{
    // ...
}

final class FileCache implements Cache
{
    // ...
}

final class RedisCache implements Cache
{
    // ...
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Mettre en place du &lt;strong&gt;Behavioral Contract Testing&lt;/strong&gt;, c’est créer des tests qui vont être exécutés sur les différentes instances d’implémentations de notre interface. Nous allons donc commencer par définir la structure de nos tests au sein d’une classe de test abstraite:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;abstract class CacheContractTest extends TestCase
{
    abstract protected function createCacheInstance(): Cache;

    public function testGetItem(): void
    {
        $cache = $this-&amp;gt;createCacheInstance();

        // test implementation [...]
    }

    public function testCacheExpiration(): void
    {
        $cache = $this-&amp;gt;createCacheInstance();

        // test implementation [...]
    }

    #[Test]
    public function testCacheDeletion(): void
    {
        $cache = $this-&amp;gt;createCacheInstance();

        // test implementation [...]
    }

    // ...
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Une fois notre classe abstraite définie, il faut créer les classes de tests correspondant à chacune des implémentations:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;class InMemoryCacheTest extends CacheContractTest
{
    protected function createCacheInstance(): Cache
    {
        return new InMemoryCache();
    }
}

class FileCacheTest extends CacheContractTest
{
    protected function createCacheInstance(): Cache
    {
        return new FileCache(sys_get_temp_dir());
    }
}

class RedisCacheTest extends CacheContractTest
{
    protected function createCacheInstance(): Cache
    {
        return new RedisCache(/* ... */);
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Il ne vous restera plus qu’à exécuter les tests pour vous assurer que tout est OK.&lt;/p&gt;

&lt;p&gt;Comme nous venons de le voir, mettre en place du &lt;strong&gt;Behavioral Contract Testing&lt;/strong&gt; dans sa version la plus simple (et qui couvre un large cas d’utilisation) n’est pas quelque chose de compliqué. Et pourtant, c’est un mécanisme d’une grande puissante. Nous vérifions que chaque implémentation respecte et se comporte de manière identique pour un même contrat. Si cela s’avère nécessaire, il est également possible de gérer les cas particuliers de chaque type de cache dans sa classe de test respective.&lt;/p&gt;

&lt;p&gt;La force de cette approche est également que, si vous avez besoin de créer une nouvelle implémentation de cache, cette dernière bénéficiera automatiquement de tous les tests que vous avez pu écrire en amonts.&lt;/p&gt;
</description>
                    <pubDate>Sun, 16 Feb 2025 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2025/02/16/tester-les-implementations-d-une-interface-avec-le-behavioral-contract-testing.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2025/02/16/tester-les-implementations-d-une-interface-avec-le-behavioral-contract-testing.html</guid>
                </item>
            
        
            
                63
                <item>
                    <title>Gérer des raccourcis clavier dans des projets Web</title>
                    <description>&lt;p&gt;Il peut parfois être utile d’ajouter des raccourcis clavier dans nos applications Web afin de simplifier ou d’accélérer l’accès à certaines fonctionnalités. Mettre en place une gestion des raccourcis dans son projet est très facile : cela ne nécessite que quelques lignes de code JavaScript.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Il suffit pour cela de se brancher sur l’événement &lt;code&gt;keydown&lt;/code&gt; qui est déclenché lorsque l’utilisateur appuie sur une touche du clavier. Ensuite, il faut vérifier les touches qui ont été enfoncées et lancer la fonction que vous souhaitez exécuter.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;document.addEventListener(&amp;#39;keydown&amp;#39;, (event) =&amp;gt; {
    if (
        event.shiftKey
        &amp;amp;&amp;amp; event.key.toLowerCase() === &amp;#39;u&amp;#39;
    ) {
        event.preventDefault();

        myFunc();
    }
});&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Le code d’exemple ci-dessus vous permettra ainsi d’exécuter l’action de la fonction &lt;code&gt;myFunc&lt;/code&gt; lorsque l’utilisateur presse à la fois la touche &lt;em&gt;Shift&lt;/em&gt; et &lt;code&gt;U&lt;/code&gt;. L’exécution de votre code se fera en désactivant le fonctionnement standard prévue par le navigateur (au travers de l’instruction &lt;code&gt;event.preventDefault();&lt;/code&gt; effectué en amont).&lt;/p&gt;
</description>
                    <pubDate>Wed, 12 Feb 2025 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2025/02/12/gerer-des-raccourcis-clavier-dans-des-projets-web.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2025/02/12/gerer-des-raccourcis-clavier-dans-des-projets-web.html</guid>
                </item>
            
        
            
                64
                <item>
                    <title>Le design pattern &quot;Unit of Work&quot;</title>
                    <description>&lt;p&gt;Le &lt;em&gt;design pattern&lt;/em&gt; &lt;strong&gt;Unit of Work&lt;/strong&gt; est au cœur de nombreux projets du fait qu’il s’agit d’un modèle utilisé par de nombreux ORM (parmi lesquels on peut citer &lt;a href=&quot;https://hibernate.org &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Hibernate&lt;/a&gt; pour Java, &lt;a href=&quot;https://learn.microsoft.com/ef/ &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;EntityFramework&lt;/a&gt; pour l’environnement Dotnet ou encore &lt;a href=&quot;https://www.doctrine-project.org/projects/orm.html &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Doctrine&lt;/a&gt; pour PHP). Ce dernier a pour objectif de tenir à jour une liste d’objets et coordonne l’écriture des modifications en gérant les problèmes de concurrence.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Le cas des ORM (&lt;em&gt;Object-Relational Mapping&lt;/em&gt;) est un cas d’école de l’utilisation de ce &lt;em&gt;pattern&lt;/em&gt;. Lorsque l’on récupère des données (en base de données), qu’on les modifie, qu’on les supprime ou que l’on en insère des nouvelles, il est important de conserver une trace de ce qu’il s’est passé pour pouvoir ensuite les retranscrire dans la base de données.&lt;/p&gt;

&lt;p&gt;L’avantage de l’&lt;em&gt;Unit of Work&lt;/em&gt; est qu’il travaille par transaction. C’est-à-dire qu’il accumule les modifications pour ensuite les exécuter au moment voulu. De ce fait, si une erreur survient durant la transaction, toutes les modifications peuvent être annulées. Un autre avantage qui découle de la gestion transactionnelle est qu’il est alors possible de regrouper plusieurs opérations, réduisant ainsi le nombre d’accès à la base de données.&lt;/p&gt;

&lt;p&gt;Dans sa version la plus simple, on pourrait définir une interface de travail comme celle-ci:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;interface UnitOfWork
{
    public function registerNew(object $object): void;

    public function registerDirty(object $object): void;

    public function registerDeleted(object $object): void;

    public function commit(): void;
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Et dont l’implémentation la plus simpliste pourrait ressembler à:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;class UnitOfWorkImplementation implements UnitOfWork
{
    private array $newObjects = [];
    private array $dirtyObjects = [];
    private array $deletedObjects = [];

    public function __construct(
        private readonly Connection $db,
    ) {}

    public function registerNew(object $object): void
    {
        $this-&amp;gt;newObjects[] = $object;
    }

    public function registerDirty(object $object): void
    {
        $this-&amp;gt;dirtyObjects[] = $object;
    }

    public function registerDeleted(object $object): void
    {
        $this-&amp;gt;deletedObjects[] = $object;
    }

    public function commit(): void
    {
        $this-&amp;gt;db-&amp;gt;beginTransaction();

        try {
            foreach ($this-&amp;gt;newObjects as $object) {
                $this-&amp;gt;insertIntoDb($object);
            }

            foreach ($this-&amp;gt;dirtyObjects as $object) {
                $this-&amp;gt;updateDbData($object);
            }

            foreach ($this-&amp;gt;deletedObjects as $object) {
                $this-&amp;gt;deleteFromDb($object);
            }

            $this-&amp;gt;db-&amp;gt;commitTransaction();

            $this-&amp;gt;newObjects = [];
            $this-&amp;gt;dirtyObjects = [];
            $this-&amp;gt;deletedObjects = [];
        } catch (Exception $e) {
            $this-&amp;gt;db-&amp;gt;rollbackTransaction();

            throw $e;
        }
    }

    private function insertIntoDb(object $object): void
    {
        // ...
    }

    private function updateDbData(object $object): void
    {
        // ...
    }

    private function deleteFromDb(object $object): void
    {
        // ...
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;C’est un des &lt;em&gt;pattern&lt;/em&gt; décrit dans l’excellent livre de &lt;a href=&quot;https://martinfowler.com &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Martin Fowler&lt;/a&gt; &lt;em&gt;&lt;a href=&quot;https://www.amazon.fr/Patterns-Enterprise-Application-Architecture-Martin/dp/0321127420/ref=sr_1_1?__mk_fr_FR=%C3%85M%C3%85%C5%BD%C3%95%C3%91&amp;amp;sr=8-1 &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Patterns of Enterprise Application Architecture&lt;/a&gt;&lt;/em&gt; (lien non affilié) qui date de 2002 mais qui contient l’essentiel des &lt;em&gt;design patterns&lt;/em&gt; les plus populaire de nos jours.&lt;/p&gt;
</description>
                    <pubDate>Mon, 10 Feb 2025 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2025/02/10/le-design-pattern-unit-of-work.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2025/02/10/le-design-pattern-unit-of-work.html</guid>
                </item>
            
        
            
                65
                <item>
                    <title>Quelle différence entre le pattern CQS et CQRS ?</title>
                    <description>&lt;p&gt;Ces dernières années, on a entendu de plus en plus souvent parler du &lt;em&gt;pattern&lt;/em&gt; CQRS (pour &lt;em&gt;Command Query Responsibility Segregation&lt;/em&gt;) du fait de sa démocratisation croissante. Mais on entend beaucoup moins parler du pattern &lt;em&gt;CQS&lt;/em&gt; (pour &lt;em&gt;Command Query Separation&lt;/em&gt;). Et pour cause, les deux concepts sont souvent assimilés à la même chose. Bien qu’ils partagent une même idée, il existe quelques différences fondamentales que nous allons voir dans ce billet.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Commençons par le &lt;em&gt;pattern&lt;/em&gt; &lt;em&gt;Command Query Separation&lt;/em&gt; (CQS). Ce dernier repose sur l’idée que chaque méthode/fonction de notre code doit soit modifier l’état du système (on parlera alors d’une commande), soit retourner une information (on parlera de requête), mais qu’aucune action ne devrait faire les deux à la fois.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;J’avais déjà un peu évoqué cette notion dans un article précédent sur &lt;a href=&quot;/blog/2025/01/27/la-notion-de-message-en-programmation.html&quot;&gt;le nommage des messages dans le code&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Voici un exemple d’implémentation:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;class OrderService
{
    // Command
    public function createOrder(string $customer, array $items): void
    {
        // ...
    }

    // Command
    public function addItemToOrder(string $orderId, string $productId, int $quantity, float $unitPrice): void
    {
        // ...
    }

    // Query
    public function getOrder(string $orderId): ?Order
    {
        // ...
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Même si d’un point de vue purement théorique, une commande ne doit pas retourner de données, il peut être pratique dans des cas précis d’enfreindre cette règle. C’est le cas des commandes qui vont créer une information en base de données et pour laquelle il est utile de pouvoir récupérer l’identifiant de la donnée qui a été insérée.&lt;/p&gt;

&lt;p&gt;Si on reprend l’exemple précédent, la commande &lt;code&gt;OrderService::createOrder&lt;/code&gt; pourrait être adaptée de cette manière:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;class OrderService
{
    // Command
    public function createOrder(string $customer, array $items): int
    {
        $order = new Order(/* ... */);

        // ...

        return $order-&amp;gt;getId();
    }

    // ....
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Ce principe peut paraître simple et insignifiant, il est pourtant d’une efficacité redoutable et possède de nombreux avantages. Il permet de se concentrer sur la responsabilité unique de vos méthodes (le fameux &lt;code&gt;S&lt;/code&gt; du principe &lt;code&gt;S.O.L.I.D.&lt;/code&gt;). Ce qui permet de tendre à un code plus compréhensible (parce qu’il fait moins de choses) et facilitant ainsi la mise en place de tests.&lt;/p&gt;

&lt;p&gt;De plus c’est une technique facile à mettre en place, il serait dommage de s’en priver.&lt;/p&gt;

&lt;p&gt;Le &lt;em&gt;pattern&lt;/em&gt; &lt;em&gt;Command Query Responsibility Segregation&lt;/em&gt; (CQRS) peut quant à lui, être considéré comme une évolution du CQS puisque ce dernier reprend la même idée, mais en poussant le concept de séparation des lectures et des écritures au sein d’un système.&lt;/p&gt;

&lt;p&gt;Reprenons notre exemple précédent de création de commande, cela reviendrait à avoir deux services distincts pour créer la commande et la récupérer:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;// Command
class CreateOrderCommandService
{
    public function create(string $customer, array $items): int
    {
        $order = new Order(/* ... */);

        // ...

        return $order-&amp;gt;getId();
    }
}

// Query
class GetOrderQueryService
{
    public function getOrderById(string $orderId): ?Order
    {
        // ...
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Comme on peut l’apercevoir dans l’exemple précédent, c’est un &lt;em&gt;pattern&lt;/em&gt; plus lourd à mettre en place, puisqu’il va falloir découper le code plus finement. Néanmoins cette approche présente certains avantages très intéressants. Tout d’abord, le découpage supplémentaire permet de structurer son code tout en continuant de respecter le principe de responsabilité unique. Mais, la plus grande force du &lt;em&gt;pattern CQRS&lt;/em&gt; est l’optimisation des performances qu’il est capable de mettre en place sur le projet.&lt;/p&gt;

&lt;p&gt;En permettant de séparer lecture et écriture, il est possible de gérer chacun des modèles dans des tables, des bases de données, voire des moteurs de bases de données distincts. Si au démarrage d’un projet (le pragmatisme étant de rigueur), il est fort probable (et même conseillé pour éviter l’optimisation prématurée) que les commandes et les requêtes (au sens &lt;em&gt;CQRS&lt;/em&gt;) tapent la même table de votre base de données, lorsque vous allez avoir de la volumétrie et/ou un trafic important, il sera intéressant de pouvoir mettre en place une version optimisée de chacun des modèles. Les écritures pouvant se faire dans une table relationnelle et les lectures sur une table dénormalisée par exemple. Suivant le niveau de performance requis, il est même possible de sortir un modèle dans une base de données distincte qui sera optimisé pour de la lecture. Faire ce travail de découpage est facilité avec ce &lt;em&gt;pattern&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Cela permettra également par rebond d’améliorer votre &lt;em&gt;scalabilité&lt;/em&gt; du fait que les opérations sont effectuées de manières séparées. Vous aurez ainsi la possibilité de faire évoluer les choses de manières indépendantes.&lt;/p&gt;

&lt;p&gt;En résumé, &lt;em&gt;CQS&lt;/em&gt; et &lt;em&gt;CQRS&lt;/em&gt; sont des approches similaires qui vous permettent de tendre vers un code simple, découpé, tout en facilitant l’écriture de test. La mise en place des principes sous-jacents n’est pas complexe à mettre en place et peut être effectuée au démarrage d’un projet. Dans le cas du &lt;em&gt;CQRS&lt;/em&gt;, on peut séparer les opérations de lecture et d’écriture dans le code, tout en partageant dans un premier temps les mêmes &lt;em&gt;repositories&lt;/em&gt; qui manipulent une même table dans une base de données unique. Le découpage des modèles dans des tables ou des bases de données distinctes ne doit se faire qu’au fil du temps et des besoins d’un projet.&lt;/p&gt;

&lt;p&gt;Chacune des approches a sa place et j’ai presque envie de dire que le CQS est un &lt;em&gt;must have&lt;/em&gt;. Il y a juste une chose essentielle à retenir, c’est que la complexité du découpage de vos modèles doit se faire en fonction de vos besoins et non pas parce que vous souhaitez utiliser un principe jusqu’au bout.&lt;/p&gt;

&lt;p&gt;Soyez pragmatique et ne soyez pas dogmatique!&lt;/p&gt;
</description>
                    <pubDate>Mon, 03 Feb 2025 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2025/02/03/quelle-difference-entre-le-pattern-cqs-et-cqrs.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2025/02/03/quelle-difference-entre-le-pattern-cqs-et-cqrs.html</guid>
                </item>
            
        
            
        
            
                66
                <item>
                    <title>La notion de message en programmation</title>
                    <description>&lt;p&gt;Au début de l’informatique, le concept de message était assez large, désignant une transmission de données entre des objets (appels de fonctions, système événementiel, etc.). De nos jours, un message se définit plutôt comme une communication émise dans un bus (généralement asynchrone, mais pas exclusivement).&lt;/p&gt;

&lt;p&gt;Bien qu’un message émis sur un bus soit un concept générique, il est important de le nommer correctement, comme tout élément du code. Tout comme il est préférable d’éviter de nommer des services de manière générique, tels que &lt;code&gt;UserService&lt;/code&gt; ou &lt;code&gt;ProductManager&lt;/code&gt;, il est important de nommer un message en fonction de sa responsabilité.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Partant de ce principe, on peut distinguer trois grandes familles de messages ayant chacune leur raison d’être :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Les &lt;code&gt;commandes&lt;/code&gt; (&lt;em&gt;Command&lt;/em&gt;): il s’agit de messages qui vont permettre de modifier l’état du système. Ces derniers sont généralement traités par un seul récepteur. Ce type de message est généralement nommé en commençant par un verbe permettant d’indiquer l’action qui va être effectuée (exemple: &lt;code&gt;CreateClientAccount&lt;/code&gt;).&lt;/li&gt;
  &lt;li&gt;Les &lt;code&gt;requêtes&lt;/code&gt; (&lt;em&gt;Query&lt;/em&gt;): les requêtes permettent de lire et récupérer de la donnée. Tout comme les commandes, elles sont généralement également traitées par un seul récepteur et vont également commencer par un verbe (exemple: &lt;code&gt;GetUsersListing&lt;/code&gt;).&lt;/li&gt;
  &lt;li&gt;Les &lt;code&gt;événements&lt;/code&gt; (&lt;em&gt;Event&lt;/em&gt;): ce dernier type de message permet d’indiquer que “quelque chose” s’est passé dans le système. Ils sont généralement nommés au participe passé et peuvent être gérés par plusieurs écouteurs (exemple: &lt;code&gt;OrderShipped&lt;/code&gt;). Les événements peuvent également servir à la communication entre plusieurs systèmes informatiques.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Accordez une attention particulière au nommage de vos messages. Nommez ces derniers en fonction de leur intention et responsabilité et non pas en fonction de l’état de votre système. Cela permettra de rendre votre code plus explicite et donc plus facilement compréhensible.&lt;/p&gt;
</description>
                    <pubDate>Mon, 27 Jan 2025 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2025/01/27/la-notion-de-message-en-programmation.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2025/01/27/la-notion-de-message-en-programmation.html</guid>
                </item>
            
        
            
                67
                <item>
                    <title>Intégrer un moteur de recherche dans un site statique</title>
                    <description>&lt;p&gt;Un moteur de recherche est disponible depuis quelques mois sur la page des articles de ce blog. Ce dernier étant généré par &lt;a href=&quot;https://jekyllrb.com &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Jekyll&lt;/a&gt;, un générateur de sites statiques, mettre en place un moteur de recherche sur un site non dynamique n’est pas chose aisée. Heureusement, il existe &lt;a href=&quot;https://lunrjs.com &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Lunr.js&lt;/a&gt;: une bibliothèque JavaScript permettant de créer un moteur de recherche côté client.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;L’utilisation de Lunr est très simple, puisqu’une fois l’avoir installé &lt;a href=&quot;https://www.npmjs.com/package/lunr &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;via NPM&lt;/a&gt; son utilisation va se passer en deux temps.&lt;/p&gt;

&lt;p&gt;Dans un premier temps, il sera nécessaire de lui indiquer quels sont les éléments qui peuvent apparaître dans la recherche. C’est la phase d’indexation. Ensuite, nous allons utiliser la fonction &lt;code&gt;lunr&lt;/code&gt; pour construire notre index en spécifiant sur quels champs de notre document la recherche va s’effectuer.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;var idx = lunr(function () {
  // définition des champs qui seront utlisés lors de la recherche
  this.field(&amp;#39;title&amp;#39;)
  this.field(&amp;#39;body&amp;#39;)

  // ajout d&amp;#39;un document dans l&amp;#39;index
  this.add({
    &amp;quot;id&amp;quot;: &amp;quot;1&amp;quot;
    &amp;quot;title&amp;quot;: &amp;quot;Intégrer un moteur de recherche dans un site statique&amp;quot;,
    &amp;quot;body&amp;quot;: &amp;quot;Un moteur de recherche est disponible depuis quelques mois[...]&amp;quot;,
    &amp;quot;url&amp;quot;: &amp;quot;https://jdecool.fr/blog/2025/01/25/integrer-un-moteur-de-recherche-dans-un-site-statique.html&amp;quot;,
  })
})&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Après avoir créé l’index de notre moteur de recherche construit, il est alors possible d’appeler la fonction &lt;code&gt;search&lt;/code&gt; lui transmettant les termes de la recherche. Le résultat contiendra la liste des documents correspondants avec un score correspondant à leur degré de pertinence.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;[
  {
    &amp;quot;ref&amp;quot;: &amp;quot;1&amp;quot;,
    &amp;quot;score&amp;quot;: 0.3535533905932737,
    &amp;quot;matchData&amp;quot;: {
      &amp;quot;metadata&amp;quot;: {
        &amp;quot;termes de la recherche&amp;quot;: {
          &amp;quot;body&amp;quot;: {}
        }
      }
    }
  },
  [...]
]&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Un peu de HTML, de mise en forme et du JavaScript pour intégrer les données dans la page affichée et vous avez un moteur de recherche sur un site statique.&lt;/p&gt;

&lt;center&gt;
    &lt;img src=&quot;/img/blog/20250125-integrer-un-moteur-de-recherche-dans-un-site-statique/lunrjs.png&quot; alt=&quot;Image d&apos;exemple de résultats suite à une recherche&quot; width=&quot;100%&quot; height=&quot;100%&quot; style=&quot;border: 1px solid black;&quot; /&gt;
&lt;/center&gt;
</description>
                    <pubDate>Sat, 25 Jan 2025 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2025/01/25/integrer-un-moteur-de-recherche-dans-un-site-statique.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2025/01/25/integrer-un-moteur-de-recherche-dans-un-site-statique.html</guid>
                </item>
            
        
            
                68
                <item>
                    <title>Les constructeurs nommés comme alternative aux constructeurs multiples en PHP</title>
                    <description>&lt;p&gt;En PHP et, contrairement à d’autre langage, il n’est pas possible d’avoir plusieurs constructeurs dans une classe. Pouvoir définir plusieurs constructeurs peut-être intéressants dans de nombreux cas, comme par exemple, pouvoir construire un objet à partir de différents types de données. Si cela n’est pas possible en PHP, il est possible d’utiliser des &lt;em&gt;constructeurs nommés&lt;/em&gt; pour avoir un fonctionnement similaire.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Mais qu’est-ce qu’un constructeur nommé? Il s’agit d’une méthode statique que l’on va pouvoir appeler afin de construire une instance de classe. Ces méthodes ont l’avantage d’être potentiellement plus explicites que le constructeur de base parce qu’elles vont pouvoir ajouter une sémantique lors de la construction de notre objet.&lt;/p&gt;

&lt;p&gt;Prenons un premier exemple:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;readonly class Color
{
    public function __construct(
        public int $red,
        public int $blue,
        public int $green,
    ) {}
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Le code précédent définit un objet permettant de stocker une couleur. Cette dernière est composant d’un niveau de rouge, de bleu et de vert. Le constructeur principal permet ainsi de renseigner les valeurs correspondantes. Mais dans certains cas d’utilisation, nous pourrions avoir envie de créer une couleur depuis sa valeur hexadécimale. Pour cela, nous pouvons imaginer introduire un constructeur nommé &lt;code&gt;fromHexCode&lt;/code&gt; pour réaliser cette tâche:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;readonly class Color
{
    public static function fromHexCode(string $code): self
    {
        $code = ltrim($code, &amp;#39;#&amp;#39;);

        $red = hexdec(substr($code, 0, 2));
        $blue = hexdec(substr($code, 2, 2));
        $green = hexdec(substr($code, 4, 2));

        return new self($red, $blue, $green);
    }

    public function __construct(
        public int $red,
        public int $blue,
        public int $green,
    ) {}
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Les utilisations de constructeur nommé sont multiples et permettent de simplifier la création d’objets, de fournir des valeurs par défaut en fonction d’un contexte d’utilisation tout en encapsulant la logique de création potentiellement complexe au sein de l’objet lui-même. Cela améliore ainsi la lisibilité et la compréhension du code.&lt;/p&gt;

&lt;p&gt;C’est également une technique appréciée en &lt;em&gt;Domain Driven Design&lt;/em&gt; pour expliciter le processus métier qui a conduit à la création de la donnée. Prenons par exemple la création d’un utilisateur dans une application, nous pourrions imaginer la modélisation suivante:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;class User
{
    public static function fromRegistration(string $name, string $email, string $password): self
    {
        $user = new self($name, $email, $password);

        // additionnal business logic related to registration

        return $user;
    }

    public static function fromSocialLogin(string $name, string $email): self
    {
        $user = new self($name, $email);

        // additionnal business logic related to social login

        return $user;
    }

    private function __construct(
        public string $name,
        public string $email,
        public ?string $password = null,
    ) {}
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Ce dernier exemple définit une classe &lt;code&gt;User&lt;/code&gt; avec un constructeur privé. Pour pouvoir créer une instance de notre objet, il sera nécessaire d’utiliser un des constructeurs nommés définis dans cette même classe. Le constructeur nommé à utiliser se fera en fonction du contexte métier dans lequel le traitement est effectué. Il y a ici un réel avantage à utiliser cette méthodologie, car elle permet de voir les éléments nécessaires à la création de notre utilisateur en fonction de l’action effectuée dans l’application.&lt;/p&gt;

&lt;p&gt;Car si un utilisateur se doit d’avoir un nom, un email et potentiellement un mot de passe, le code précédent rend visible le fait qu’il est obligatoire de renseigner toutes ces informations dans le cas d’un enregistrement de l’utilisateur depuis notre projet, alors que le mot de passe n’est pas nécessaire dans le cas d’une connexion d’un réseau social.&lt;/p&gt;

&lt;p&gt;Le concept de constructeur nommé est très puissant et peut se révéler extrêmement utile pour la maintenance et la compréhension d’une base de code. Le principe a été très brièvement introduit dans cet article et je vous recommande vivement de vous y intéresser plus en profondeur, ces derniers étant bien souvent sous-exploités.&lt;/p&gt;
</description>
                    <pubDate>Mon, 20 Jan 2025 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2025/01/20/les-constructeurs-nommes-comme-alternative-aux-constructeurs-multiples-en-php.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2025/01/20/les-constructeurs-nommes-comme-alternative-aux-constructeurs-multiples-en-php.html</guid>
                </item>
            
        
            
                69
                <item>
                    <title>Écrire une API idempotente (exemple en PHP avec Symfony)</title>
                    <description>&lt;p&gt;D’après la &lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc9110 &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;RFC 9110&lt;/a&gt; qui spécifie la sémantique d’une requête HTTP, une requête est considérée comme &lt;em&gt;idempotente&lt;/em&gt; si elle peut être effectuée plusieurs fois et obtenir un résultat identique lors de chaque appel. Par exemple, si l’on tente d’accéder à une ressource REST &lt;code&gt;GET /resource/42&lt;/code&gt;, deux appels à cet endpoint retourneront toujours le même résultat. Les appels &lt;code&gt;GET&lt;/code&gt; à une API REST sont alors considérés comme idempotents (il en va de même pour des appels &lt;code&gt;PUT&lt;/code&gt; ou &lt;code&gt;DELETE&lt;/code&gt; par exemple). Mais ce n’est pas le cas d’un appel &lt;code&gt;POST /resource&lt;/code&gt; qui créera alors une nouvelle ressource à chaque appel. Nous allons voir dans ce billet comment rendre un appel &lt;code&gt;POST&lt;/code&gt; idempotent.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Pour rendre une API idempotente, il va falloir être en mesure d’identifier de manière unique un appel à une ressource. Généralement, le client API va générer une clé unique et l’ajouter à l’appel effectué. La clé est ensuite vérifiée par le serveur pour qu’il puisse savoir si la requête reçue a déjà été traitée ou non:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Si la clé n’est pas connue, le traitement va être effectué et le résultat sauvegardé avant de retourner la réponse HTTP.&lt;/li&gt;
  &lt;li&gt;Dans le cas contraire, le serveur va retourner la réponse précédemment enregistrée.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;La plupart du temps, la clé d’idempotence est envoyée dans les en-têtes de l’appel HTTP afin d’éviter de polluer le corps de la requête.&lt;/p&gt;

&lt;p&gt;Prenons par exemple, le code source d’une API permettant de créer un billet de blog:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;class BlogController extends AbstractController
{
    #[Route(&amp;#39;/api/articles&amp;#39;, methods: [&amp;#39;POST&amp;#39;])]
    public function create(Request $request): JsonResponse
    {
        $post = new stdClass();
        $post-&amp;gt;id = Uuid::v7();
        $post-&amp;gt;title = $request-&amp;gt;request-&amp;gt;get(&amp;#39;title&amp;#39;, &amp;#39;Default title&amp;#39;);
        $post-&amp;gt;createdAt = new DateTimeImmutable();
        // [...] enregistrement en base de données

        return new JsonResponse($post, JsonResponse::HTTP_CREATED);
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Comme expliqué précédemment, cette API n’est pas idempotente. Car si une même requête est effectuée deux fois de suite, un même article &lt;code&gt;My awesome article&lt;/code&gt; sera ajouté en base de données.&lt;/p&gt;

&lt;p&gt;Nous allons maintenant ajouter à notre API la gestion d’une clé permettant de s’assurer de l’unicité du traitement d’une requête. Cette clé doit être envoyée par le client dans une en-tête que nous nommerons &lt;code&gt;X-Idempotent-Key&lt;/code&gt;. Cette clé sera stockée dans un système de cache (via le composant &lt;code&gt;symfony/cache&lt;/code&gt;) pendant une durée d’une heure. Si la clé n’existe pas, elle sera créée et dans le cas contraire, la réponse du traitement de la requête sera renvoyée une nouvelle fois depuis les données mises en cache.&lt;/p&gt;

&lt;p&gt;Cela pourrait conduire à l’implémentation suivante:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;class BlogController extends AbstractController
{
    public function __construct(
        private readonly Psr\Cache\CacheItemPoolInterface $cache,
    ) {}

    #[Route(&amp;#39;/api/blog&amp;#39;, methods: [&amp;#39;POST&amp;#39;])]
    public function create(Request $request): JsonResponse
    {
        if (
            // si l&amp;#39;en-tête X-Idempotent-Key est présente
            ($idempotentKey = $request-&amp;gt;headers-&amp;gt;get(&amp;#39;X-Idempotent-Key&amp;#39;)) !== null

            // et que la donnée est en cache
            &amp;amp;&amp;amp; ($postCache = $cache-&amp;gt;getItem($idempotentKey))-&amp;gt;isHit()
        ) {
            // on retourne la réponse présente dans le cache
            return new JsonResponse($postCache-&amp;gt;get(), JsonResponse::HTTP_CREATED);
        }

        // dans le cas contraire, on effectue le traitement

        $post = new stdClass();
        $post-&amp;gt;id = Uuid::v7();
        $post-&amp;gt;title = $request-&amp;gt;request-&amp;gt;get(&amp;#39;title&amp;#39;, &amp;#39;Default title&amp;#39;);
        $post-&amp;gt;createdAt = new DateTimeImmutable();
        // [...] enregistrement en base de données

        // dans le cas où la clé d&amp;#39;idempotence est définie
        if ($idempotentKey !== null) {
            // on enregistre le résultat du traitement en cache
            $item = $cache-&amp;gt;getItem($idempotentKey);
            $item-&amp;gt;set($post);
            $item-&amp;gt;expiresAfter(3600)
            $cache-&amp;gt;save($item);
        }

        return new JsonResponse($post, JsonResponse::HTTP_CREATED);
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;De cette manière notre API pourra être idempotente, améliorant ainsi sa fiabilité ainsi que la cohérence des données du service. L’idempotence permet de garantir que les requêtes identiques produisent toujours le même résultat, évitant ainsi les doublons involontaires qui pourraient survenir en cas de problème réseau par exemple.&lt;/p&gt;

&lt;p&gt;Faut-il mettre en place ce mécanisme partout ? Pas nécessairement. Focalisez les opérations les plus critiques de votre système et en particulier les opérations de modification de l’état de ce dernier.&lt;/p&gt;
</description>
                    <pubDate>Mon, 13 Jan 2025 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2025/01/13/ecrire-une-api-idempotente-en-php-avec-symfony.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2025/01/13/ecrire-une-api-idempotente-en-php-avec-symfony.html</guid>
                </item>
            
        
            
                70
                <item>
                    <title>Copier une image Docker d&apos;une registry à une autre</title>
                    <description>&lt;p&gt;J’ai récemment eu le besoin de copier plusieurs images d’une &lt;em&gt;registry&lt;/em&gt; Docker à une autre. Bien que cette opération puisse sembler simple au premier abord, il y a quelques points auxquels il faut prêter attention.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;La première solution à laquelle on pourrait penser serait de récupérer l’image sur sa machine afin de créer un nouveau tag pointant vers la &lt;em&gt;registry&lt;/em&gt; de destination. Par exemple, si je souhaite copier l’image MySQL vers la &lt;em&gt;registry&lt;/em&gt; Gitlab, je pourrais m’y prendre comme cela:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;docker pull mysql:9.1.0-oracle
docker tag mysql:9.1.0-oracle registry.gitlab.com/jdecool/docker/mysql:9.1.0-oracle
docker push registry.gitlab.com/jdecool/docker/mysql:9.1.0-oracle&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Le problème de cette approche est qu’elle va copier uniquement l’image récupérée par la machine qui effectue l’opération et qui est propre à son architecture. Dans le cas de l’image MySQL, si j’effectue l’opération depuis mon MacBook je ne copierai que la version ARM alors que, si je le fais depuis mon PC, je ne copierai que la version &lt;code&gt;amd64&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Or, dans le cadre d’une migration, il est indispensable de prendre en compte les images multiarchitectures. Une solution envisageable serait de construire les images en mode multiarchitecture en se basant sur l’image d’origine. La commande &lt;code&gt;docker buildx build&lt;/code&gt; permet par exemple de spécifier dynamiquement le contenu d’un Dockerfile. On peut alors modifier les commandes précédentes de la sorte:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;docker buildx build --platform linux/amd64,linux/arm64/v8 \
  --tag registry.gitlab.com/jdecool/docker/mysql:9.1.0-oracle \
  --push \
  --build-context base=docker-image://mysql:9.1.0-oracle \
  - &amp;lt;&amp;lt;EOF
FROM base
EOF&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Bien que verbeuse, cette solution est parfaitement fonctionnelle. Mais il est possible de se simplifier un peu la vie. Depuis peu, Docker a introduit une nouvelle fonctionnalité dans &lt;code&gt;buildx&lt;/code&gt; permettant de copier directement les images entre Registry. L’opération peut alors être réalisée via une seule ligne de commande:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;docker buildx imagetools create \
  --tag &amp;quot;registry.gitlab.com/jdecool/docker/mysql:9.1.0-oracle&amp;quot; &amp;quot;mysql:9.1.0-oracle&amp;quot;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Je n’ai évoqué dans ce billet que les outils officiels proposés par Docker, mais il existe également des outils créés par d’autres éditeurs et qui permettent également d’effectuer cette tâche.&lt;/p&gt;
</description>
                    <pubDate>Tue, 07 Jan 2025 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2025/01/07/copier-une-image-docker-d-une-registry-a-une-autre.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2025/01/07/copier-une-image-docker-d-une-registry-a-une-autre.html</guid>
                </item>
            
        
            
                71
                <item>
                    <title>Un environnement local Dockerisé en deux lignes de commande avec ddev</title>
                    <description>&lt;p&gt;Construire un environnement de développement en local peut être fastidieux. Si l’on souhaite un environnement “natif”, il faudra installer tout le nécessaire en local. L’utilisation de machines virtuelles est pratique, mais gourmand en ressources matérielles. L’alternative à tout cela c’est bien évidemment les conteneurs qui ont été popularisés par Docker.&lt;/p&gt;

&lt;p&gt;Néanmoins, tout n’est pas rose. Lorsqu’on travail sur de multiples projets, on peut se retrouver rapidement à copier/coller des configurations &lt;em&gt;Docker Compose&lt;/em&gt; à droite et à gauche, ce qui peut ne pas être pratique. De plus, la configuration peut-être légèrement différente que l’on travaille sur Linux, Mac ou Windows ce qui ne facilite pas forcément le partage de configuration.&lt;/p&gt;

&lt;p&gt;Mais c’était sans compter &lt;a href=&quot;https://ddev.com &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;DDEV&lt;/a&gt; dont l’objectif est de simplifier la configuration des environnements locaux fonctionnant via des conteneurs.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Ddev offre une solution permettant de gérer des environnements locaux de développement entièrement conteneurisés de manière à ce qu’il ne soit pas nécessaire d’installer quoi que ce soit sur votre machine (mis à part Docker et Ddev lui-même). Disponible pour Linux, Mac &amp;amp; Windows, il est également capable de fonctionner dans des environnements cloud tels que &lt;a href=&quot;https://www.gitpod.io &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Gitpod&lt;/a&gt; ou &lt;a href=&quot;https://github.com/features/codespaces &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Github Codespaces&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Aussi simple que puissant, Ddev vous permettra en quelques lignes de commande d’installer, configurer et lancer tout le nécessaire au bon fonctionnement de votre projet PHP (de type CMS tel que Drupal ou WordPress, mais aussi vos projets Laravel, Symfony ou même CakePHP) ou Python (expérimental). Bien entendu, il ne se contente pas de fournir des images pour votre langage favori, au travers d’un système d’extension, vous aurez la possibilité d’ajouter à votre environnement un service préexistant (comme des CRONtab, de l’Elasticsearch, du Minio, des bases MongoDB ou Redis et même des systèmes de messages comme Beanstalk ou RabbitMQ) ou bien d’ajouter le vôtre.&lt;/p&gt;

&lt;p&gt;En plus de toutes ces fonctionnalités, une fois le projet démarré, Ddev fournit une URL vous permettant d’accéder à ce dernier. L’expérience avec Docker, montre que bien souvent, nous autres développeurs avons des difficultés à gérer le mapping des droits entre notre conteneur et notre système de fichier. Là encore, tout est géré à notre place.&lt;/p&gt;

&lt;p&gt;Pour ma part, Ddev commence à remplacer petit à petit l’environnement hybride que j’avais l’habitude de mettre en place à savoir un PHP natif installé sur machine et fonctionnement au travers de &lt;a href=&quot;https://github.com/symfony-cli/symfony-cli &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Symfony CLI&lt;/a&gt; qui était couplé avec des conteneurs Docker pour toutes les autres dépendances (base de données, RabbitMQ, gestion des emails…).&lt;/p&gt;

&lt;p&gt;Vous n’êtes pas encore convaincu ? Je vous propose un petit test. Prenez 5 minutes pour &lt;a href=&quot;https://ddev.readthedocs.io/en/stable/users/install/docker-installation/ &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;installer Ddev&lt;/a&gt;. Une fois fait, je vous propose de créer le projet de démo de Symfony: &lt;code&gt;symfony new ddev-demo --demo&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Puis pour configurer notre environnement, nous allons utiliser la commande &lt;code&gt;ddev config&lt;/code&gt; et renseigner les différentes informations demandées:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;$ cd ddev-demo
$ ddev config

Creating a new DDEV project config in the current directory (ddev-demo)
Once completed, your configuration will be written to ddev-demo/.ddev/config.yaml

Project name (ddev-demo): &amp;#39;ddev-demo&amp;#39;

The docroot is the directory from which your site is served.
This is a relative path from your project root at ddev-demo
You may leave this value blank if your site files are in the project root

Docroot Location (public): &amp;#39;public&amp;#39;
Found a php codebase at ddev-demo/public.

Project Type [backdrop, cakephp, craftcms, django4, drupal, drupal6, drupal7, laravel, magento, magento2, php, python, shopware6, silverstripe, typo3, wordpress] (php): &amp;#39;php&amp;#39;

Configuration complete. You may now run &amp;#39;ddev start&amp;#39;.&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Il n’y a pas de projet type Symfony, nous nous contentons alors de spécifier le dossier &lt;code&gt;public&lt;/code&gt; et d’indiquer qu’il s’agit d’un projet PHP.&lt;/p&gt;

&lt;p&gt;Il ne reste maintenant plus qu’à démarrer notre environnement via la commande &lt;code&gt;ddev start&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;Starting ddev-demo...
 Container ddev-ssh-agent  Recreate
 Container ddev-ssh-agent  Recreated
 Container ddev-ssh-agent  Started
ssh-agent container is running: If you want to add authentication to the ssh-agent container, run &amp;#39;ddev auth ssh&amp;#39; to enable your keys.
Building project images...
......Project images built in 6s.
 Network ddev-demo_default  Created
 Container ddev-demo-ddev-web  Created
 Container ddev-demo-ddev-db  Created
 Container ddev-demo-ddev-db  Started
 Container ddev-demo-ddev-web  Started
Waiting for containers to become ready: [web db]
Starting ddev-router if necessary...
 Container ddev-router  Created
 Container ddev-router  Started
Successfully started demo-ddev
Your project can be reached at https://demo-ddev.ddev.site
See &amp;#39;ddev describe&amp;#39; for alternate URLs.&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Vous avez maintenant un projet de démo fonctionnel au travers de l’URL https://demo-ddev.ddev.site&lt;/p&gt;

&lt;p&gt;Bluffant non ?&lt;/p&gt;
</description>
                    <pubDate>Wed, 06 Nov 2024 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2024/11/06/un-environnement-local-dockerise-en-deux-lignes-de-commande-avec-ddev.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2024/11/06/un-environnement-local-dockerise-en-deux-lignes-de-commande-avec-ddev.html</guid>
                </item>
            
        
            
        
            
                72
                <item>
                    <title>Testez vos images Docker</title>
                    <description>&lt;p&gt;Vous construisez des images Docker régulièrement ? Vous savez alors certainement que ce n’est pas parce que la construction de votre image s’est réalisée sans erreurs que cette dernière va se comporter comme vous vous y attendez. La construction d’une image Docker, tout comme n’importe quel code écris, doit être vérifiée et validée et pour cela, il est possible d’utiliser un outil qui va nous permettre de nous assurer du bon fonctionnement de notre image.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Pour ma part, l’outil que j’utilise se nomme &lt;a href=&quot;https://github.com/GoogleContainerTools/container-structure-test &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Container Structure Test&lt;/a&gt;. Conçu par Google, il permet d’écrire des tests unitaires pour nos images Docker. Développé en Go, il est disponible sous la forme d’un binaire compatible sur la plupart des systèmes d’exploitation.&lt;/p&gt;

&lt;p&gt;Grâce à &lt;em&gt;Container Structure Test&lt;/em&gt;, vous aurez la possibilité de:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Exécuter des commandes à l’intérieur du conteneur et de vérifier la sortie ou l’erreur produite,&lt;/li&gt;
  &lt;li&gt;Tester l’existence d’un fichier&lt;/li&gt;
  &lt;li&gt;Vérifier le contenu d’un fichier (y compris la métadonnée qui lui est associée),&lt;/li&gt;
  &lt;li&gt;Et de vérifier la configuration du conteneur lui-même.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Une fois téléchargée, la mise en place des tests se fait via l’écriture de règle dans un fichier YAML. Voici par exemple, un exemple de configuration que j’utilise pour tester la construction d’image Docker faisant tourner des projets PHP:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;schemaVersion: &amp;quot;2.0.0&amp;quot;

# Vérification de la présence de certaines variables d&amp;#39;environnement
globalEnvVars:
    - key: GITHUB_TOKEN
      value: github-token

commandTests:
    - name: &amp;quot;Symfony CLI installed&amp;quot;
      command: &amp;quot;which&amp;quot;
      args: [&amp;quot;symfony&amp;quot;]
      exitCode: 0
    - name: &amp;quot;Check PHP extensions&amp;quot;
      command: &amp;quot;php&amp;quot;
      args: [&amp;quot;-m&amp;quot;]
      expectedOutput:
          - &amp;quot;amqp&amp;quot;
          # ...


fileExistenceTests:
- name: &amp;#39;Configuration PHP&amp;#39;
  path: &amp;#39;etc/php/8.3/php.ini&amp;#39;
  shouldExist: false

fileContentTests:
- name: &amp;#39;Linux Version&amp;#39;
  path: &amp;#39;/etc/os-release&amp;#39;
  expectedContents: [&amp;quot;VERSION_ID=3.14.2&amp;quot;,&amp;quot;NAME=\&amp;quot;Alpine Linux\&amp;quot;&amp;quot;]&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Une fois la configuration terminée, il ne vous reste plus qu’à exécuter les tests en spécifiant l’image à tester et la ou les configurations à prendre en compte:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;container-structure-test test --image my-registry.jdecool.fr/php:8.3 --config php-tests.yaml

=======================================
====== Test file: php-tests.yaml ======
=======================================
=== RUN: Command Test: Symfony CLI installed
--- PASS
duration: 306.265278ms
stdout: [...]

=== RUN: Command Test: Check PHP extensions
--- PASS
duration: 303.302806ms
stdout: [...]

=== RUN: Command Test: Configuration PHP
--- PASS
duration: 276.503651ms
stdout: [...]

=== RUN: Command Test: Linux Version
duration: 276.503651ms
stdout: [...]

=======================================
=============== RESULTS ===============
=======================================
Passes:      4
Failures:    0
Duration:    789.707605ms
Total tests: 4

PASS
&lt;/code&gt;&lt;/pre&gt;
</description>
                    <pubDate>Mon, 28 Oct 2024 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2024/10/28/testez-vos-images-docker.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2024/10/28/testez-vos-images-docker.html</guid>
                </item>
            
        
            
                73
                <item>
                    <title>Le pattern Optional, le conteneur de valeur qui va remplacer vos données nullables</title>
                    <description>&lt;p&gt;Nous manipulons tous au quotidien des données &lt;em&gt;nullables&lt;/em&gt;, or manipuler des données qui peuvent être &lt;code&gt;null&lt;/code&gt; implique de devoir effectuer de nombreuses vérifications afin de savoir si la donnée que l’on manipule contient bien une valeur avant de l’utiliser. Auquel cas, nous aurons une erreur de type:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;PHP Warning:  Uncaught Error: Call to a member function method() on null in php shell code:1
Stack trace:
#0 {main}
  thrown in php shell code on line 1
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Pour éviter l’utilisation de conditions de type &lt;code&gt;if ($var !== null)&lt;/code&gt;, il est possible d’utiliser le &lt;em&gt;pattern&lt;/em&gt; &lt;code&gt;Optional&lt;/code&gt; qui peut s’apparenter à la monade &lt;code&gt;Maybe&lt;/code&gt;.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Un &lt;code&gt;Optional&lt;/code&gt; est un conteneur permettant d’encapsuler une valeur qui peut être présente ou non. Ainsi, plutôt que d’utiliser une variable &lt;code&gt;null&lt;/code&gt; pour représenter une valeur absente, nous allons pouvoir manipuler systématiquement un objet et appeler des méthodes sur ce dernier qui effectuera des opérations en fonction que la valeur soit présente ou non.&lt;/p&gt;

&lt;p&gt;Prenons un exemple concret pour illustrer ce concept. Sur un site d’e-commerce, un événement est émis une fois un achat effectué par un utilisateur afin de lui envoyer un récapitulatif de sa commande. La gestion de l’envoi de l’email pourrait ressembler au code suivant:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;class Customer
{
    public ?string $email;
}

readonly class OrderConfirmed
{
    public Customer $customer;
}

readonly class OrderNotification
{
    public function __construct(private EmailSender $email) {}

    public function sendConfirmatinEmailOnOrderConfirmed(OrderConfirmed $event): void
    {
        if ($event-&amp;gt;getCustomer() == null) { // invite account
            return;
        }


        if ($event-&amp;gt;getCustomer()-&amp;gt;getEmail() == null) { // no email filled
            return;
        }

        $this-&amp;gt;email-&amp;gt;send($event-&amp;gt;getCustomer()-&amp;gt;getEmail(), &amp;#39;Email content&amp;#39;);
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Nous voyons dans ce cas d’exemple, qu’il est nécessaire de faire deux vérifications avant de pouvoir envoyer notre e-mail de confirmation:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Dans un premier temps, il est nécessaire de vérifier que la commande n’a pas été effectuée en “mode invité”, ce qui impliquerait d’avoir une commande sans notion d’acheteur,&lt;/li&gt;
  &lt;li&gt;Il est ensuite nécessaire de vérifier que l’on a bien une adresse de renseignée pour pouvoir envoyer l’e-mail.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Voyons maintenant ce que pourrait donner ce même code avec l’utilisation du pattern &lt;code&gt;Optional&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;class Customer
{
    /** @var Optional&amp;lt;string&amp;gt; */
    public Optional $email;
}

readonly class OrderConfirmed
{
    /** @var Optional&amp;lt;Customer&amp;gt; */
    public Optional $customer;
}

readonly class OrderNotification
{
    public function __construct(private EmailSender $email) {}

    public function sendConfirmatinEmailOnOrderConfirmed(OrderConfirmed $event): void
    {
        $event-&amp;gt;customer
            -&amp;gt;flatMap(static fn (Customer $customer) =&amp;gt; $customer-&amp;gt;email)
            -&amp;gt;ifPresent(static fn (string $email) =&amp;gt; $this-&amp;gt;email-&amp;gt;send($event-&amp;gt;getCustomer()-&amp;gt;getEmail(), &amp;#39;Email content&amp;#39;));
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Dans cette seconde version, nous constatons que toutes nos vérifications (les &lt;code&gt;if&lt;/code&gt;) ont été supprimées. Le code s’en retrouve plus concis et surtout se concentre sur les opérations utiles au traitement de l’envoi de notre e-mail, ce qui le rend plus expressif. Il n’est plus question de devoir vérifier si la donnée est présente ou non, la structure de données &lt;code&gt;Optional&lt;/code&gt; va appliquer les transformations demandées uniquement si la valeur est présente et ne fait rien dans le cas contraire.&lt;/p&gt;

&lt;p&gt;Les monades gagnant en popularité ces dernières années, on commence à voir des implémentations du pattern &lt;code&gt;Optional&lt;/code&gt; directement dans les langages de programmation. Ce n’est (malheureusement) pas le cas en PHP. Mais on retrouve quelques implémentations sur &lt;a href=&quot;https://packagist.org &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Packagist&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Bien que son utilisation dans l’écosystème PHP reste marginale, à la vue des nombreux avantages qu’il représente, vous avez tout intérêt à regarder ça de plus près. Pour ma part, j’ai commencé à introduire cette notion en construisant ma propre implémentation qui se résume à quelques lignes de code.&lt;/p&gt;
</description>
                    <pubDate>Fri, 25 Oct 2024 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2024/10/25/le-pattern-optional-le-conteneur-de-valeur-qui-va-remplacer-vos-donnees-nullables.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2024/10/25/le-pattern-optional-le-conteneur-de-valeur-qui-va-remplacer-vos-donnees-nullables.html</guid>
                </item>
            
        
            
                74
                <item>
                    <title>Installer des extensions PHP facilement dans une image Docker</title>
                    <description>&lt;p&gt;Si vous avez déjà construit des images Docker pour des applications ou projet PHP, vous avez certainement utilisé l’outil &lt;code&gt;docker-php-ext-install&lt;/code&gt;. Ce dernier permet d’installer et configurer simplement des extensions PHP qui seront disponibles dans le conteneur. Néanmoins, cet outil se limite malheureusement aux extensions officielles fournies avec le langage.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Pour résoudre ce problème et permettre d’installer de nombreuses extensions PHP dans un conteneur Docker, il existe un outil disponible librement sur Github: &lt;a href=&quot;https://github.com/mlocati/docker-php-extension-installer &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;mlocati/docker-php-extension-installer&lt;/code&gt;&lt;/a&gt;. Ce dernier met à disposition un script nommé &lt;code&gt;install-php-extensions&lt;/code&gt; qui vous permettra d’installer et de configurer des extensions PHP au sein de votre image. À la différence du script officiel, il ne se limite pas aux seules extensions fournies directement avec PHP puisqu’il couvre un très large panel d’extensions parmi lesquelles: &lt;code&gt;amqp&lt;/code&gt;, &lt;code&gt;cassandra&lt;/code&gt;, &lt;code&gt;ioncube_loader&lt;/code&gt;, &lt;code&gt;jsonpath&lt;/code&gt;, &lt;code&gt;newrelic&lt;/code&gt;, &lt;code&gt;rdkafka&lt;/code&gt;, &lt;code&gt;xdebug&lt;/code&gt; et bien d’autres encore.&lt;/p&gt;

&lt;p&gt;Mais il permet d’aller encore plus loin en installer également les dépendances nécessaires à l’installation des extensions. Il installera par exemple la bibliothèque &lt;code&gt;rabbitmq-c&lt;/code&gt; nécessaire au bon fonctionnement de l’extension &lt;code&gt;amqp&lt;/code&gt; permettant entre autres de communiquer avec RabbitMQ.&lt;/p&gt;

&lt;p&gt;Fonctionnant avec les images basées sur Debian et Alpine, vous aurez la possibilité d’installer les extensions pour les versions de 7.1 à 8.4 de PHP, de quoi couvrir un grand nombre de besoins et cas d’utilisation.&lt;/p&gt;

&lt;p&gt;Et pour finir, voici un exemple d’utilisation:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-dockerfile&quot; data-lang=&quot;dockerfile&quot;&gt;FROM php:8.3-cli

ADD --chmod=0755 https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/

RUN install-php-extensions amqp gd xdebug&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Si vous êtes passé à côté de cet outil et que vous utilisez encore une combinaison des utilitaires officiels et/ou &lt;code&gt;pecl&lt;/code&gt;, je ne peux que vous encourager à tester cet outil.&lt;/p&gt;
</description>
                    <pubDate>Wed, 23 Oct 2024 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2024/10/23/installer-des-extensions-php-facilement-dans-une-image-docker.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2024/10/23/installer-des-extensions-php-facilement-dans-une-image-docker.html</guid>
                </item>
            
        
            
                75
                <item>
                    <title>Installer Cursor sur Ubuntu 24.04</title>
                    <description>&lt;p&gt;Je ne sais pas pour vous, mais s’il y a bien un outil orienté IA dont j’entends beaucoup parlé en ce moment c’est bien &lt;a href=&quot;https://www.cursor.com/ &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Cursor&lt;/a&gt;, un fork de Visual Studio Code dont la promesse est de vous faire développez plus avec grâce à l’intelligence artificielle tout en restant dans un environnement connu. Par contre si comme moi, vous utilisez la version 24.04 d’Ubuntu, l’installation de l’outil peut ne pas se faire sans erreur.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Effectivement, bien que l’on se trouve avec un fichier de type &lt;a href=&quot;https://fr.wikipedia.org/wiki/AppImage &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;AppImage&lt;/a&gt; (un format d’application portable) normalement voué à lancer une application facilement, vous avez certainement rencontré l’erreur suivante:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;$ ./cursor-0.40.4x86_64.AppImage
[61312:0911/231002.352674:FATAL:setuid_sandbox_host.cc(158)] The SUID sandbox helper binary was found, but is not configured correctly. Rather than run without sandboxing I&apos;m aborting now. You need to make sure that /tmp/.mount_cursorlmVFH2/chrome-sandbox is owned by root and has mode 4755.
[1]    61312 trace trap (core dumped)  ./cursor-0.40.4x86_64.AppImage
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Pour résoudre ce problème, je vous propose deux solutions. Visual Studio Code (et donc Cursor) sont basés sur &lt;a href=&quot;https://www.electronjs.org &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Electron&lt;/a&gt; qui par défaut isole l’application dans un bac à sable. La dernière LTS d’Ubuntu (la 24.04 au moment de l’écriture de ce billet) ayant mis en place un nouveau mécanisme de sécurité, certaines applications n’ont pas encore fait les changements nécessaires pour leur bon fonctionnement. Heureusement il est possible de désactiver cette fonctionnalité en utilisant l’option &lt;code&gt;--no-sandbox&lt;/code&gt;. Bien entendu cette dernière option n’est pas forcément conseillée étant donné qu’elle désactive des couches de sécurité.&lt;/p&gt;

&lt;p&gt;Il existe heureusement une solution de contournement qui consiste à ajouter un fichier de configuration pour &lt;code&gt;AppArmor&lt;/code&gt;, le logiciel de sécurité notamment utilisé par Ubuntu, permettant de lever la restriction sur la création de la &lt;em&gt;sandbox&lt;/em&gt; nécessaire au bon fonctionnement de Cursor. Pour cela, il faudra créer un fichier &lt;code&gt;/etc/apparmor.d/cursor-appimage&lt;/code&gt; avec le contenu ci-dessous (attention, vous devez modifier ce dernier afin de mettre à jour le chemin vers le fichier AppImage):&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# This profile allows everything and only exists to give the
# application a name instead of having the label &quot;unconfined&quot;

abi &amp;lt;abi/4.0&amp;gt;,
include &amp;lt;tunables/global&amp;gt;

profile cursor /home/{YOUR-USER}/Applications/cursor*.AppImage flags=(unconfined) {
  userns,

  # Site-specific additions and overrides.  See local/README for details.
  include if exists &amp;lt;local/cursor&amp;gt;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Une fois fait, il ne reste plus qu’à informer &lt;code&gt;apparmor&lt;/code&gt; de la présence de ce nouveau fichier via la commande: &lt;code&gt;sudo apparmor_parser -r /etc/apparmor.d/cursor-appimage&lt;/code&gt;. Et voila, vous pouvez maintenant lancer Cursor sans problème.&lt;/p&gt;

&lt;p&gt;À savoir également qu’&lt;a href=&quot;https://github.com/getcursor/cursor/issues/844&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer nofollow&quot;&gt;une issue est ouverte&lt;/a&gt; auprès des équipes d’Anysphere et qui est à surveiller si vous souhaitez être informé de la correction de cette anomalie.&lt;/p&gt;
</description>
                    <pubDate>Thu, 12 Sep 2024 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2024/09/12/installer-cursor-sur-ubuntu-24-04.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2024/09/12/installer-cursor-sur-ubuntu-24-04.html</guid>
                </item>
            
        
            
                76
                <item>
                    <title>Comment récupérer le nombre d&apos;erreurs ignorées dans une analyse PHPStan</title>
                    <description>&lt;p&gt;Je travaille au quotidien avec &lt;a href=&quot;https://phpstan.org &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;PHPStan&lt;/a&gt; que ce soit pour mes projets personnels, mais également professionnels. Pour des projets complexes et ayant plusieurs années d’existence, il n’est pas rare d’ajouter des règles d’analyses graduellement afin d’éviter un trop grand nombre d’erreurs à corriger (ou à ignorer) d’un seul coup. Aussi dans une optique d’amélioration continue, il peut être intéressant de suivre le nombre d’erreurs ignorées afin de pouvoir s’assurer que le chiffre diminue (ou n’augmente pas de manière disproportionnée) au fil du temps.&lt;/p&gt;

&lt;p&gt;Malheureusement, c’est une information que PHPStan ne permet pas de récupérer simplement.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;J’ai donc cherché une solution, car c’est une métrique que je cherche à suivre. L’idée étant d’avoir une idée de l’état du code et de détecter si une équipe a tendance à ignorer des erreurs relevées par l’analyse statique ou si au contraire, elle est dans une optique d’amélioration continue du code.&lt;/p&gt;

&lt;p&gt;Lors de mes recherches, j’ai trouvé le projet &lt;a href=&quot;https://github.com/staabm/phpstan-baseline-analysis &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;phpstan-baseline-analysis&lt;/a&gt; qui permet d’analyser une &lt;code&gt;baseline&lt;/code&gt; PHPStan pour fournir un résumé de la typologie des erreurs qui sont présentes dans le fichier. C’est un outil intéressant, dont l’auteur Markus Staab, &lt;a href=&quot;https://staabm.github.io/2022/07/04/phpstan-baseline-analysis.html &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;a publié un article pour présenter le projet&lt;/a&gt;. Bien qu’intéressant, il ne permet pas de déterminer le nombre d’erreurs réellement ignorées dans le projet. Se basant sur un fichier de configuration, le projet fait par exemple l’impasse sur les erreurs directement ignorées dans le code ou ne permet pas de compter avec précision les erreurs ignorées au travers d’une expression régulière.&lt;/p&gt;

&lt;p&gt;Je me suis alors intéressé plus en détail au fonctionnement de PHPStan et à son mécanisme d’analyse. J’ai notamment pu découvrir le fonctionnement du système de cache. L’essentiel des informations est stocké dans un fichier unique (&lt;code&gt;resultCache.php&lt;/code&gt;), lequel contient l’ensemble des erreurs détectées. Ainsi, en analysant le contenu du fichier, en lisant les informations telles que &lt;code&gt;errorsCallback&lt;/code&gt;, &lt;code&gt;locallyIgnoredErrorsCallback&lt;/code&gt; et &lt;code&gt;linesToIgnore&lt;/code&gt;, il est possible de récupérer avec détails et précision, l’ensemble des erreurs présentes dans la base de code (et ainsi de pouvoir en extraire des métriques).&lt;/p&gt;

&lt;p&gt;C’est ainsi qu’est né &lt;a href=&quot;https://github.com/jdecool/phpstan-report &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;phpstan-report&lt;/code&gt;&lt;/a&gt;, une surcouche à PHPStan, capable de lancer une analyse et de lire le fichier de cache afin d’en extraire des métriques permettant de tracer et suivre l’évolution des erreurs de l’analyse statique.&lt;/p&gt;
</description>
                    <pubDate>Wed, 04 Sep 2024 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2024/09/04/comment-recuperer-le-nombre-d-erreurs-ignorees-dans-une-analyse-phpstan.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2024/09/04/comment-recuperer-le-nombre-d-erreurs-ignorees-dans-une-analyse-phpstan.html</guid>
                </item>
            
        
            
                77
                <item>
                    <title>Tester un bundle avec plusieurs versions de Symfony</title>
                    <description>&lt;p&gt;Lorsque l’on travaille et maintient un bundle Symfony, il n’est pas rare de devoir gérer la compatibilité de ce dernier avec plusieurs versions du framework. Par exemple, au moment où je publie ces lignes, les versions 5.4, 6.4 et 7.1 sont officiellement maintenues. Exécuter sa batterie de tests automatisés sur plusieurs versions peut alors être fastidieux.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Le minimum que l’on puisse faire serait de tester le bundle en installant les dépendances Composer avec les options &lt;code&gt;--prefer-stable&lt;/code&gt; et &lt;code&gt;--prefer-lowest&lt;/code&gt; (pour récupérer les versions minimales des dépendances autorisées). Cela fonctionne, mais si une version du bundle supporte les trois versions courantes, ce dernier serait alors testé avec les versions 5.4 et 7.1.&lt;/p&gt;

&lt;p&gt;Pour remédier à ce problème, on pourrait alors à l’installation des dépendances utiliser la commande &lt;code&gt;composer require&lt;/code&gt; pour installer spécifiquement les versions désirées. Par exemple, si l’on souhaite installer une version 6.4, on pourrait utiliser la commande &lt;code&gt;composer require symfony/framework-bundle:6.4.*&lt;/code&gt;. Cela fonctionne bien, mais peut rapidement être fastidieux si le nombre de dépendances Symfony est important.&lt;/p&gt;

&lt;p&gt;Pour résoudre cette problématique, les équipes de Symfony ont intégré une fonctionnalité dans &lt;a href=&quot;https://github.com/symfony/flex &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Symfony Flex&lt;/a&gt; permettant d’installer une version spécifique pour toutes les dépendances Symfony en une seule opération. Flex est une extension Composer bien connue des développeurs Symfony. Ainsi, pour mettre à jour vos dépendances sur une version spécifique, il vous suffira d’utiliser la variable d’environnement &lt;code&gt;SYMFONY_REQUIRE&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Par exemple, la commande ci-dessous mettre toutes vos dépendances &lt;code&gt;symfony/*&lt;/code&gt; en version 6.4:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;$ SYMFONY_REQUIRE=6.4 composer update
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;À noter que &lt;code&gt;symfony/flex&lt;/code&gt; peut être installé en tant que dépendance de votre projet, mais peut également être installé sur l’OS et donc en dehors de votre projet via la commande: &lt;code&gt;composer global require --no-progress --no-scripts --no-plugins symfony/flex&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Cela vous permettra de grandement simplifier vos workflows d’intégration continue. Et pour conclure cet article, voici par exemple, une configuration Github Actions que vous pouvez utiliser sur vos projets:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;name: Run tests
on: [ push ]
jobs:
    phpunit:
        runs-on: ubuntu-latest
        strategy:
            matrix:
                composer-prefs: [ &apos;--prefer-stable&apos;, &apos;--prefer-lowest&apos; ]
                php-version: [ &apos;8.2&apos;, &apos;8.3&apos; ]
                symfony-version: [ &apos;5.4.*&apos;, &apos;6.4.*&apos;, &apos;7.1.*&apos; ]
        name: &apos;PHPUnit - PHP/${{ matrix.php-version }} - SF/${{ matrix.symfony-version }} ${{ matrix.composer-prefs }}&apos;
        steps:
            -   name: Checkout
                uses: actions/checkout@v2
            -   name: Setup PHP
                uses: shivammathur/setup-php@v2
                with:
                    php-version: ${{ matrix.php-version }}
                    coverage: xdebug
            -   run: composer global config --no-plugins allow-plugins.symfony/flex true
            -   run: composer global require --no-progress --no-scripts --no-plugins symfony/flex
            -   run: composer update --prefer-dist --no-interaction ${{ matrix.composer-prefs }}
                env:
                    SYMFONY_REQUIRE: ${{ matrix.symfony-version }}
            -   name: PHPUnit
                run: vendor/bin/phpunit
&lt;/code&gt;&lt;/pre&gt;

</description>
                    <pubDate>Mon, 29 Jul 2024 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2024/07/29/tester-un-bundle-avec-plusieurs-versions-de-symfony.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2024/07/29/tester-un-bundle-avec-plusieurs-versions-de-symfony.html</guid>
                </item>
            
        
            
                78
                <item>
                    <title>Le principe de verrouillage optimiste (&lt;i&gt;optimistic locking&lt;/i&gt;) avec Doctrine ORM</title>
                    <description>&lt;p&gt;Lorsque l’on travaille sur une application manipulant une base de données, il est possible de se retrouver dans un cas où une donnée va être modifiée par deux requêtes concurrentes. Cela peut-être problématique, car cela peut entraîner des incohérences dans les données d’un système.&lt;/p&gt;

&lt;p&gt;Prenons l’exemple d’un système bancaire:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;La première requête va récupérer le solde du compte du client (disons 100€), puis va effectuer un débit de 20€, il reste donc 80€ sur le compte du client.&lt;/li&gt;
  &lt;li&gt;La seconde requête va récupérer le solde du client (80€), puis va effectuer un débit de 50€, il restera donc 30€&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Imaginons maintenant que les deux requêtes sont exécutées simultanément:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;La requête 1 consulte le solde du compte: 100€&lt;/li&gt;
  &lt;li&gt;La requête 2 consulte le solde du compte avant que la requête 1 ne fasse l’opération de modification: 100€&lt;/li&gt;
  &lt;li&gt;La requête 1 débite le compte de 50€: il reste 50€&lt;/li&gt;
  &lt;li&gt;La requête 2 effectue le débit de 50€ sur le solde connu dans son contexte d’exécution (100€), il reste 50€&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;L’exemple précédent présente les problèmes d’incohérence de données que des requêtes simultanées peuvent introduire dans un système.&lt;/p&gt;

&lt;p&gt;Pour corriger cela, la solution la plus simple et qui vient naturellement à l’esprit, est de vérouiller la donnée à manipuler le temps nécessaire à la réalisation des différentes opérations (consultation et débit) nécessaires au traitement. C’est ce que l’on appelle un &lt;strong&gt;verrouillage pessimiste&lt;/strong&gt; (ou &lt;em&gt;Pessimistic Locking&lt;/em&gt; en anglais), du fait que la donnée sera systématiquement verrouillée même s’il n’y a pas de requêtes concurrentes.&lt;/p&gt;

&lt;p&gt;Cela pourrait être fait avec le pseudo-code ci-dessous:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;function débiterCompte(string $numeroCompte)
{
    if ($this-&amp;gt;isCompteVerrouiller($numeroCompte)) {
        throw new RuntimeException(&amp;#39;Compte vérouiller&amp;#39;);
    }

    $this-&amp;gt;verrouillerCompte($numeroCompte);

    // ... on fait les traitements nécessaires ...

    $this-&amp;gt;déverrouillerCompte($numeroCompte);
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Si l’on travaille en PHP avec &lt;a href=&quot;https://www.doctrine-project.org/projects/orm.html &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Doctrine ORM&lt;/a&gt; ce dernier met à disposition plusieurs méthodes permettant de mettre en place cette stratégie de verrouillage:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code&gt;EntityManager::find($className, $id, LockMode::PESSIMISTIC_WRITE|LockMode::PESSIMISTIC_READ)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;EntityManager::lock($entity, LockMode::PESSIMISTIC_WRITE|LockMode::PESSIMISTIC_READ)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;EntityManager::refresh($entity, LockMode::PESSIMISTIC_WRITE|LockMode::PESSIMISTIC_READ&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;Query::setLockMode(LockMode::PESSIMISTIC_WRITE|LockMode::PESSIMISTIC_READ)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cette approche est très simple. Elle est également rapide à mettre en place. Mais elle présente l’inconvénient de verrouiller l’accès à une donnée durant tout le temps du process de modification, et cela de manière potentiellement inutile. Si dans des traitements simple et rapide cela n’est pas un problème, sur des traitements nécessitant des calculs importants, verrouiller la donnée peut générer de potentiels incidents.&lt;/p&gt;

&lt;p&gt;Pour éviter ces problèmes, il est possible de mettre en place un &lt;strong&gt;verrouillage optimiste&lt;/strong&gt; (on parle alors d’&lt;em&gt;optimistic locking&lt;/em&gt; en anglais) des données. Il n’est alors plus question de verrouiller une donnée de manière systématique. Le principe de fonctionnement est le suivant:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;La requête va récupérer les informations dont elle a besoin en base de données. Les informations contiennent un numéro de version de la donnée (cela peut correspondre à un numéro, une date…),&lt;/li&gt;
  &lt;li&gt;La requête modifie les données nécessaires au traitement effectué,&lt;/li&gt;
  &lt;li&gt;Au moment de valider les modifications et de les enregistrer définitivement, une vérification du numéro de version va être effectuée afin de s’assurer qu’aucune donnée n’a été modifiée par une autre requête durant le traitement:
    &lt;ul&gt;
      &lt;li&gt;Si la version ne correspond pas à l’état de la base de données, une erreur est renvoyée,&lt;/li&gt;
      &lt;li&gt;Sinon, les modifications sont validées et persistées en base de données.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ici encore, si vous utilisez un ORM, ce dernier offre les outils nécessaires pour vous simplifier le travail et vous éviter d’avoir à gérer cela manuellement. Avec Doctrine ORM, cela se fait simplement en ajoutant la métadonnée &lt;a href=&quot;https://www.doctrine-project.org/projects/doctrine-orm/en/3.6/reference/transactions-and-concurrency.html#optimistic-locking &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;Version&lt;/code&gt;&lt;/a&gt; à la propriété correspondante de votre entité.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;class CompteBancaire
{
    // ...

    #[Version, Column(type: &amp;#39;integer&amp;#39;)]
    private int $version;
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

</description>
                    <pubDate>Tue, 02 Jul 2024 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2024/07/02/gerer-la-concurrence-d-enregistrement-avec-doctrine-orm.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2024/07/02/gerer-la-concurrence-d-enregistrement-avec-doctrine-orm.html</guid>
                </item>
            
        
            
                79
                <item>
                    <title>Quelle différence entre DateTime et DateTimeImmutable en PHP ?</title>
                    <description>&lt;p&gt;Il existe en PHP deux classes pour gérer et manipuler nativement les dates: &lt;code&gt;DateTime&lt;/code&gt; et &lt;code&gt;DateTimeImmutable&lt;/code&gt;. Je me rends compte que de nombreux développeurs ne maîtrisent pas la différence entre les deux notions. Ce billet est l’occasion de voir pourquoi et dans quel cas utiliser l’un ou l’autre.&lt;/p&gt;

&lt;p&gt;Comme leur nom peut l’indiquer, la différence de fonctionnement est relative à la mutabilité de l’objet manipulé. Dans le cas de &lt;code&gt;DateTime&lt;/code&gt;, l’instance de notre objet sera mutable. Cela signifie que la valeur de ce dernier peut être modifiée après sa création. Prenons le bout de code suivant qui va calculer une date prévisionnelle d’envoi d’une commande:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;function getExpectedShippingDate(DateTime $orderDate): DateTime {
    return $orderDate-&amp;gt;add(DateInterval::createFromDateString(&amp;#39;3 day&amp;#39;));
}

$orderDate = new DateTime(&amp;#39;2024-05-27&amp;#39;);
$shippingDate = getExpectedShippingDate($orderDate);

// $orderDate = 2024-05-30
// $shippingDate = 2024-05-30&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Dans l’exemple précédent, on peut voir que l’appel à la méthode &lt;code&gt;getExpectedShippingDate&lt;/code&gt; a modifié la valeur de la date de notre objet &lt;code&gt;$orderDate&lt;/code&gt;. En PHP les objets étant passés par référence et une instance de &lt;code&gt;DateTime&lt;/code&gt; étant mutable, toute modification effectuée sur notre objet dans un bloc de code modifie la valeur de ce dernier dans le reste du code.&lt;/p&gt;

&lt;p&gt;Pour éviter ce problème, vous l’aurez deviné, il faut utiliser une instance de &lt;code&gt;DateTimeImmutable&lt;/code&gt;. Reprenons le code de notre fonction de calcul de la date prévisionnel d’envoi d’une commande:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;function getExpectedShippingDate(DateTimeImmutable $orderDate): DateTimeImmutable {
    return $orderDate-&amp;gt;add(DateInterval::createFromDateString(&amp;#39;3 day&amp;#39;));
}

$orderDate = new DateTimeImmutable(&amp;#39;2024-05-27&amp;#39;);
$shippingDate = getExpectedShippingDate($orderDate);

// $orderDate = 2024-05-27
// $shippingDate = 2024-05-30&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Contrairement à une instance de &lt;code&gt;DateTime&lt;/code&gt;, &lt;code&gt;DateTimeImmutable&lt;/code&gt; est un objet immuable. Cela signifie que lorsqu’une opération est effectuée sur ce dernier, PHP ne va pas modifier la valeur courante l’objet. Le moteur va créer une nouvelle instance de date avec la bonne valeur et la renvoyer comme résultat de la fonction. De ce fait, notre date &lt;code&gt;$orderDate&lt;/code&gt; conserve sa valeur d’origine.&lt;/p&gt;

&lt;p&gt;À partir de là, la question que l’on est en droit de se poser maintenant est: dans quel cas utiliser l’un ou l’autre ? Au risque de paraître un peu extrémiste, mon avis est que l’&lt;strong&gt;on ne devrait jamais utiliser &lt;code&gt;DateTime&lt;/code&gt;&lt;/strong&gt;. Le risque de créer un effet de bord est trop important, sans compter la difficulté à retrouver l’origine du problème. L’utilisation de &lt;code&gt;DateTime&lt;/code&gt; étant à mon sens à proscrire, vous devriez systématiquement utiliser &lt;code&gt;DateTimeImmutable&lt;/code&gt;.&lt;/p&gt;
</description>
                    <pubDate>Mon, 27 May 2024 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2024/05/27/quelle-difference-entre-datetime-et-datetimeimmutable-en-php.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2024/05/27/quelle-difference-entre-datetime-et-datetimeimmutable-en-php.html</guid>
                </item>
            
        
            
                80
                <item>
                    <title>Gérer les décisions d&apos;architectures dans les projets</title>
                    <description>&lt;p&gt;On me demande régulièrement comment sont gérées les décisions (et le suivi) d’architectures que l’on est amené à prendre dans les projets sur lesquels j’interviens. Dans la plupart des cas, cela va se dérouler en deux phases: la communication et le suivi.&lt;/p&gt;

&lt;p&gt;La première étape que j’ai nommée “la communication” se fait en deux temps. Elle commence tout d’abord par une discussion d’équipe (ou des acteurs concernés) dans laquelle sont évoquées la ou les problématiques qui sont rencontrées. Cette discussion (réalisée de manière synchrone ou asynchrone) permet de s’aligner sur le problème rencontré, d’échanger sur les solutions qui peuvent être envisagées. Une fois ce travail réalisé, l’équipe pourra se mettre d’accord sur la solution finale à adopter.&lt;/p&gt;

&lt;p&gt;Il est important de noter que l’ensemble des échanges doivent être tracés au sein d’un ADR (&lt;em&gt;Architecture Decision Record&lt;/em&gt;) afin de conserver l’historique des échanges, discussions, contexte du problème, solutions envisagées et d’expliquer comment l’équipe (à cette date) en est arrivée à la solution convenue.&lt;/p&gt;

&lt;p&gt;Une fois tout le travail de recherche et de concertation réalisé, l’équipe va alors pouvoir commencer à implémenter la solution finale. Se pose alors la question du suivi. Comment suivre et faire respecter la décision d’architecture qui a été prise ?&lt;/p&gt;

&lt;p&gt;Au sein d’une équipe qui travaille avec de la revue de code, la première idée qui nous vient naturellement en tête est que l’équipe devra alors “faire attention” lors de la relecture du code. C’est une première étape, mais elle a l’inconvénient de reposer uniquement sur l’humain. Or, s’assurer du respect de toutes les règles qui ont été établies peut-être un véritable défi sur des projets d’envergures.&lt;/p&gt;

&lt;p&gt;C’est pour cela que nous tentons (avec les équipes) d’automatiser au maximum la vérification des règles mises en place. Et pour cela, un certain nombre d’outils sont disponibles en fonction des besoins. Les principaux étant:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/qossmic/deptrac&quot;&gt;Deptrac&lt;/a&gt;: un outil qui va vérifier les dépendances et les interactions entre les différentes couches d’un projet pour s’assurer du respect des règles de couplages. Dans le cadre d’une &lt;em&gt;Clean Architecture&lt;/em&gt;, nous pouvons vérifier les dépendances entre les couches &lt;code&gt;Domain&lt;/code&gt;, &lt;code&gt;Application&lt;/code&gt;, &lt;code&gt;Infrastructure&lt;/code&gt; et &lt;code&gt;Presentation&lt;/code&gt; ,&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/PHP-CS-Fixer/PHP-CS-Fixer&quot;&gt;PHP CS Fixer&lt;/a&gt;: pour tout ce qui est vérification (et correction automatique) des règles liées aux conventions de nommages du code,&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/phpstan/phpstan&quot;&gt;PHPStan&lt;/a&gt;: principalement conçu pour analyser le code PHP et détecter erreurs et problèmes dans le code, nous l’utilisons pour écrire des règles personnalisées pour vérifier des règles d’architectures qui ne peuvent être réalisées par les outils précédents.&lt;/li&gt;
&lt;/ul&gt;
</description>
                    <pubDate>Mon, 13 May 2024 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2024/05/13/gerer-les-decisions-d-architectures-dans-les-projets.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2024/05/13/gerer-les-decisions-d-architectures-dans-les-projets.html</guid>
                </item>
            
        
            
                81
                <item>
                    <title>Retrouvez ma veille sur Mastodon &amp; Bluesky</title>
                    <description>&lt;p&gt;Cela fait maintenant plusieurs années que je partage presque quotidiennement ma veille sur &lt;a href=&quot;https://twitter.com/jdecool &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;X (Twitter)&lt;/a&gt;. Du lundi au vendredi, généralement entre 9h et 10h, je diffuse jusqu’à 5 publications sur le thème de développement et de l’informatique.&lt;/p&gt;

&lt;p&gt;Malheureusement, ces derniers temps, l’écosystème Twitter s’est dégradé et de plus en plus de personnes se tournent vers de nouvelles alternatives.&lt;/p&gt;

&lt;p&gt;C’est pour cela que depuis peu, l’ensemble de ma veille est maintenant également diffusé sur &lt;a href=&quot;https://phpc.social/@jdecool &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Mastodon&lt;/a&gt; et &lt;a href=&quot;https://bsky.app/profile/jdecool.bsky.social &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Bluesky&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Au plaisir de vous y retrouver.&lt;/p&gt;

&lt;!--more--&gt;
</description>
                    <pubDate>Mon, 06 May 2024 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2024/05/06/retrouvez-ma-veille-sur-maston-bluesky.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2024/05/06/retrouvez-ma-veille-sur-maston-bluesky.html</guid>
                </item>
            
        
            
        
            
                82
                <item>
                    <title>Non, vous ne faites pas de la programmation objet</title>
                    <description>&lt;p&gt;Si vous posez la question à n’importe quel développeur, il vous répondra très certainement que oui, il fait bien de la programmation orientée objet. Effectivement, les langages de développement les plus utilisés actuellement tels que Python, C++, Java, C#, JavaScript, PHP ou encore Ruby permettent d’écrire des objets et de nombreux &lt;em&gt;frameworks&lt;/em&gt; utilisent ces notions pour fonctionner. Est-ce pour autant que l’on fait réellement de la programmation orientée objet ?&lt;/p&gt;

&lt;p&gt;Pour moi la réponse est clairement &lt;strong&gt;non&lt;/strong&gt;. Ce n’est pas parce que l’on écrit des classes et que l’on peut les instancier, que l’on fait de la programmation orientée objet. Le fondement de ce paradigme, c’est l’&lt;strong&gt;encapsulation&lt;/strong&gt;. Il s’agit du principe qui fait qu’un objet n’est pas une simple structure de données. Un objet c’est l’association de données (les propriétés d’une classe) et des comportements (les méthodes) applicables à ce dernier.&lt;/p&gt;

&lt;p&gt;Pourtant, la majorité des frameworks et des ORM (&lt;em&gt;Object-Relational Mapping&lt;/em&gt;) nous ont habitués à avoir et à créer des objets dénués de sens. On se contente bien souvent de classes qui contiennent des propriétés que l’on définit et récupère unitairement sans sémantique particulière comme dans l’exemple suivant:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;class User
{
    private string $username;
    private string $passwordHash;
    /** @var string[] $bans */
    private array $bans;

    public function getUsername(): string
    {
        return $this-&amp;gt;username;
    }

    public function setUsername(string $username): void
    {
        $this-&amp;gt;username = $username;
    }

    public function getPasswordHash(): string
    {
        return $this-&amp;gt;passwordHash;
    }

    public function setPasswordHash(string $passwordHash): void
    {
        $this-&amp;gt;passwordHash = $passwordHash;
    }

    /**
     * @return string[]
     */
    public function getBans(): array
    {
        return $this-&amp;gt;bans;
    }

    public function addBan(Ban $ban): void
    {
        $this-&amp;gt;bans[] = $ban;
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Des objets avec uniquement des &lt;em&gt;getters&lt;/em&gt; et des &lt;em&gt;setters&lt;/em&gt;. Même &lt;a href=&quot;/blog/2020/04/04/coherence-des-donnees-dans-un-modele-oriente-objet.html&quot;&gt;le constructeur&lt;/a&gt; semble oublié. C’est ce que l’on appelle des modèles anémiques.&lt;/p&gt;

&lt;p&gt;C’est pour moi le point le plus négatif des développeurs à l’heure actuelle, car au-delà de ne pas respecter le paradigme initial, cela entraîne également une perte de sens dans le code de nos projets. Et non, modéliser nos objets ainsi ça ne vient pas du DDD (&lt;em&gt;Domain Driven Design&lt;/em&gt;) comme j’ai pu l’entendre. Faire des objets qui mutent et qui ont des modèles riches, c’est le principe de base de la programmation objet. Le DDD ne fait que s’appuyer sur ces fondements.&lt;/p&gt;

&lt;p&gt;Lorsque vous écrivez vos objets, pensez comportement. Écrivez des objets riches, appuyez-vous sur des mutations, utilisez des DTO (&lt;em&gt;Data Transfer Object&lt;/em&gt;) si nécessaire. L’utilisation de &lt;em&gt;getters&lt;/em&gt; et &lt;em&gt;setters&lt;/em&gt; devrait être une exception. Implémentez des méthodes qui représentent le comportement de vos objets.&lt;/p&gt;

&lt;p&gt;Si l’on reprend l’exemple précédent, ce dernier pourrait être modifié ainsi:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;class User
{
    public function __construct(
        private string $username,
        private string $passwordHash,
        private array $bans = [],
    ) {
    }

    public function toNickname(): string
    {
        return $this-&amp;gt;username;
    }

    public function authenticate(string $password, callable $checkHash): bool
    {
        return $checkHash($password, $this-&amp;gt;passwordHash) &amp;amp;&amp;amp; ! $this-&amp;gt;hasActiveBans();
    }

    public function changePassword(string $password, callable $hash): void
    {
        $this-&amp;gt;passwordHash = $hash($password);
    }

    public function ban(\DateInterval $duration): void
    {
        assert($duration-&amp;gt;invert !== 1);

        $this-&amp;gt;bans[] = new Ban($this);
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Cet exemple, aussi court puisse-t-il être, condense de nombreux concepts:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Un constructeur&lt;/strong&gt; pour initialiser les &lt;strong&gt;propriétés obligatoires&lt;/strong&gt;,&lt;/li&gt;
  &lt;li&gt;Des &lt;strong&gt;accesseurs&lt;/strong&gt; métier qui permettent d’accéder uniquement aux &lt;strong&gt;informations utiles en dehors de l’objet&lt;/strong&gt;,&lt;/li&gt;
  &lt;li&gt;Des &lt;strong&gt;mutations&lt;/strong&gt; qui permettent de modifier l’état de notre objet en s’assurant que toutes les propriétés liées à un comportement soient spécifiées.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Et oui, cela fonctionne avec votre framework préféré même si ce dernier ne l’a pas décrit tel quelle dans sa documentation, préférant opter pour une approche RAD (&lt;em&gt;Rapid Application Development&lt;/em&gt;) pour une prise en main simplifiée et des résultats plus rapides.&lt;/p&gt;
</description>
                    <pubDate>Sun, 05 May 2024 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2024/05/05/non-vous-ne-faites-pas-de-la-programmation-objet.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2024/05/05/non-vous-ne-faites-pas-de-la-programmation-objet.html</guid>
                </item>
            
        
            
                83
                <item>
                    <title>Concentrez-vous sur les principes, pas la technologie</title>
                    <description>&lt;p&gt;Au détour d’une discussion sur les monolithes modulaires avec un collègue développeur ce matin, je lui explique être en train de regarder une formation en ligne intitulée “Modular Monoliths in .NET”. Il me demande alors: “Pourquoi regarder une formation orientée .NET ? Alors qu’au quotidien, tu travailles essentiellement en PHP.”.&lt;/p&gt;

&lt;p&gt;C’est (malheureusement) un peu dans la tendance. Aujourd’hui beaucoup cherchent à se spécialiser sur une technologie précise. On voit souvent des développeurs se qualifier de “développeur [insérer ici une techno de votre choix]”. On cherche dorénavant à connaitre une techno sur le bout des doigts. Pourtant les langages, &lt;em&gt;frameworks&lt;/em&gt; et bibliothèques vont et viennent. Je fais de l’informatique professionnellement depuis 15 ans et lorsque j’ai commencé, les frameworks PHP en étaient à leur tout début (j’ai encore les souvenirs de &lt;a href=&quot;https://fr.wikipedia.org/wiki/Copix &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Copix&lt;/a&gt;). On était encore bien loin des frameworks type Zend, Symfony ou Laravel.&lt;/p&gt;

&lt;p&gt;Pour autant, je travaillais avec des frameworks type &lt;a href=&quot;https://fr.wikipedia.org/wiki/Apache_Struts &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Struts&lt;/a&gt;, qui reposaient sur une architecture modèle-vue-controleur (MVC) comme encore la plupart des frameworks existants. Or, si on comprend comment fonctionne ce &lt;em&gt;pattern&lt;/em&gt;, passer de l’un à l’autre est relativement simple.&lt;/p&gt;

&lt;p&gt;Bien entendu, cela ne se fait pas en un claquement de doigts. Il faudra (ré)apprendre les particularités du nouveau framework, éventuellement le langage de programmation utilisé, mais de nombreux principes restent communs à tous les outils.&lt;/p&gt;

&lt;p&gt;Et c’est bien là que je veux en venir. Ne vous focalisez par sur une technologie précise, concentrez-vous sur les principes sous-jacents. Tout d’abord, cela vous permettra de bien comprendre les tenants et les aboutissants, vous donnant ainsi la capacité de juger de la pertinence de la mise en place de l’outil dans un contexte donné.&lt;/p&gt;

&lt;p&gt;En plus de cela, vous ne serait pas enfermé dans un écosystème particulier. Ce dernier point est d’autant plus important si vous souhaitez avoir une carrière longue de développeur. Car si les technologies changent, les principes et les &lt;em&gt;patterns&lt;/em&gt; restent souvent les mêmes. Vous n’aurez donc pas à continuellement repartir de 0 lorsque vous basculerez sur de nouvelles briques techniques.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/blog/2015/01/23/comprenez-les-outils-que-vous-utilisez.html&quot;&gt;Comprendre le fonctionnement des outils&lt;/a&gt; et les fondamentaux sur lesquels ils reposent est donc le secret de la longévité de votre carrière de développeur, mais également un des éléments clés pour avoir une analyse pertinente à un problème donné.&lt;/p&gt;
</description>
                    <pubDate>Fri, 22 Mar 2024 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2024/03/22/concentrez-vous-sur-les-principes-pas-la-technologie.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2024/03/22/concentrez-vous-sur-les-principes-pas-la-technologie.html</guid>
                </item>
            
        
            
                84
                <item>
                    <title>Ne faites pas confiance aux données de vos utilisateurs</title>
                    <description>&lt;p&gt;S’il y a bien un proverbe que j’entends depuis que j’ai démarré l’informatique, c’est bien “&lt;em&gt;never trust user input&lt;/em&gt;” (ne faites pas confiance aux données de vos utilisateurs). L’idée est simple: lorsque vous attendez des données utilisateurs en entrée d’un traitement, il est nécessaire au préalable de &lt;strong&gt;vérifier&lt;/strong&gt;, &lt;strong&gt;valider&lt;/strong&gt;, voir de &lt;strong&gt;nettoyer&lt;/strong&gt; l’information reçue.&lt;/p&gt;

&lt;p&gt;On pense alors généralement à de la donnée provenant de formulaires dans le cas d’applications Web, on vérifiera le format d’une adresse e-mail qui sera utilisée pour envoyer une communication électronique. Mais la validation des données peut aussi relever d’un aspect sécurité. Par exemple, si une donnée renseignée par l’utilisateur va être utilisée au sein d’une requête SQL, on cherchera alors à &lt;em&gt;échapper&lt;/em&gt; cette dernière afin de se prémunir d’éventuelle &lt;a href=&quot;https://fr.wikipedia.org/wiki/Injection_SQL &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;injection SQL&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;S’assurer de la validité et de la cohérence d’une donnée, c’est offrir &lt;strong&gt;une meilleure expérience utilisateur&lt;/strong&gt; aux personnes qui utilisent vos outils. Une erreur de saisie, un doigt qui dérape et si vite arrivée.&lt;/p&gt;

&lt;p&gt;Aussi, dans le cas d’erreurs, de bugs ou de demandes d’assistance, on aura souvent tendance à dire que si l’utilisateur fait et renseigne n’importe quoi n’importe comment. Mais peut-il être incriminé si l’application ne fait pas les vérifications nécessaires à son bon fonctionnement ? N’avons-nous pas, nous développeurs, un rôle à jouer là-dedans ?&lt;/p&gt;

&lt;p&gt;Ne faites &lt;strong&gt;jamais&lt;/strong&gt; confiance à la donnée qui provient d’un utilisateur qu’il soit bien intentionné ou non. Appliquer des techniques de validation, nettoyer la donnée pour garantir la meilleure expérience possible et la sécurité de vos projets.&lt;/p&gt;
</description>
                    <pubDate>Thu, 14 Mar 2024 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2024/03/14/ne-faites-pas-confiance-aux-donnees-de-vos-utilisateurs.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2024/03/14/ne-faites-pas-confiance-aux-donnees-de-vos-utilisateurs.html</guid>
                </item>
            
        
            
        
            
                85
                <item>
                    <title>IA: ne ratez pas la révolution qui est en marche</title>
                    <description>&lt;p&gt;Hier soir (jeudi 15 février 2024), j’ai eu la chance de pouvoir participer à la soirée de lancement du livre &lt;a href=&quot;https://hyperprompt.fr &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Hyperprompt&lt;/a&gt;, le premier manuel français de &lt;em&gt;prompt engineering&lt;/em&gt; écrit par Flavien CHERVET (auteur d’ouvrage et conférencier sur la thématique de l’intelligence artificielle). Durant cette soirée, ce fut l’occasion de discuter du fonctionnement des IA génératives, de comprendre les bases de leur fonctionnement afin de savoir comment “leur parler”. Tout cela avec un peu d’explication de sciences cognitives et de magie.&lt;/p&gt;

&lt;p&gt;Mais pourquoi je vous parle de tout ça ? Parce que je considère que nous sommes actuellement dans un tournant technologique, un virage que je ne veux pas rater et auquel je pense qu’il est primordial de s’y intéresser. Tout d’abord parce que les IA génératives sont des outils extraordinaires qui nous permettent de nous aider dans notre travail quotidien. Que ce sont des outils incroyables pour appréhender des domaines que l’on ne maîtrise pas.&lt;/p&gt;

&lt;p&gt;Ensuite parce que ces outils n’en sont qu’à leurs prémisses, ils se développent et évoluent à une vitesse incroyable. Que je reste convaincu qu’il sera plus facile d’acquérir les bases de leurs bonnes utilisations en s’y mettant tôt plutôt que dans quelques années où ces dernières seront devenues plus puissantes, mais aussi plus complexes et qu’il faudra acquérir les bases.&lt;/p&gt;

&lt;p&gt;Je n’ai pas envie de laisser passer cette vague, de me faire remplacer. Je souhaite utiliser et comprendre ces outils pour faire un travail de qualité, plus efficace, et plus rapidement. Car comme l’a dit &lt;a href=&quot;https://eric.daspet.name &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Eric DASPET&lt;/a&gt; sur les réseaux sociaux: nous avons longtemps craint que l’évolution des machines favoriserait le remplacement des “petits boulots”. Pourtant c’est bien nous, les travailleurs intellectuels, qui sommes peut-être le plus menacés.&lt;/p&gt;
</description>
                    <pubDate>Fri, 16 Feb 2024 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2024/02/16/ia-ne-ratez-pas-la-revolution-qui-est-en-marche.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2024/02/16/ia-ne-ratez-pas-la-revolution-qui-est-en-marche.html</guid>
                </item>
            
        
            
                86
                <item>
                    <title>Sauvegarder sa base de données MySQL sans perturber sa production</title>
                    <description>&lt;p&gt;Dans la majorité des cas, pour sauvegarder une base de données MySQL, c’est la commande &lt;code&gt;mysqldump&lt;/code&gt;, l’outil fourni par le SGBD, qui va être utilisé. Mais saviez-vous que cela n’est pas sans risque et que cette opération peut perturber les opérations en cours sur votre base de données ?&lt;/p&gt;

&lt;p&gt;J’ai pu le constater à mes dépens en voulant effectuer la sauvegarde d’une base de données volumineuse. Lorsque la commande va commencer à récupérer les données, l’outil va mettre en place un verrou (&lt;code&gt;LOCK&lt;/code&gt;) au niveau des tables ce qui va avoir pour conséquence de les rendre inaccessibles. Sur de petites volumétries, cela n’aura que peu d’incidence, mais si vous avez une grande quantité de données à récupérer, cela signifie que durant tout le temps de sauvegarde, votre application ne pourra pas accéder à la base de données de manière fluide. La conséquence dans mon cas, c’est plusieurs utilisateurs qui n’ont pu travailler sur l’outil en question, entraînant une interruption de service le temps de l’opération.&lt;/p&gt;

&lt;p&gt;Pour éviter cela, &lt;code&gt;mysqldump&lt;/code&gt; propose un certain nombre d’options qui vous permettra d’éviter ces déconvenues. Les deux plus importantes et que j’utilise dorénavant systématiquement sont:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code&gt;--single-transaction&lt;/code&gt; qui va mettre en place une transaction (si cela est supporté par le moteur utilisé) afin de garantir la cohérence des données sans pour autant mettre en place un verrou&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;--skip-lock-tables&lt;/code&gt; qui comme son nom l’indique indique qu’il ne faut pas mettre en place de verrou.&lt;/li&gt;
&lt;/ul&gt;
</description>
                    <pubDate>Tue, 06 Feb 2024 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2024/02/06/sauvegarder-sa-base-de-donnees-mysql-sans-perturber-sa-production.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2024/02/06/sauvegarder-sa-base-de-donnees-mysql-sans-perturber-sa-production.html</guid>
                </item>
            
        
            
                87
                <item>
                    <title>Installer et utiliser un LLM en local</title>
                    <description>&lt;p&gt;S’il y a bien un sujet tendance ces dernières années (surtout depuis l’arrivée de &lt;a href=&quot;https://chat.openai.com &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Chat GPT&lt;/a&gt;), c’est les &lt;em&gt;&lt;a href=&quot;https://fr.wikipedia.org/wiki/Grand_mod%C3%A8le_de_langage &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Large Language Model&lt;/a&gt;&lt;/em&gt; (souvent appelé LLM). Personnellement, je n’échappe pas à ce “mouvement”. J’utilise &lt;em&gt;&lt;a href=&quot;https://github.com/features/copilot &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Github Copilot&lt;/a&gt;&lt;/em&gt; au quotidien et Chat GPT régulièrement (qui a remplacé à la fois &lt;em&gt;Google&lt;/em&gt; et &lt;em&gt;Stack Overflow&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;Faisant de la veille régulièrement, j’ai envie de comprendre comment ces outils peuvent révolutionner notre quotidien de développeur (et je pense sincèrement que c’est le cas quand on sait les manipuler correctement). Je pense même que c’est une erreur de ne pas s’y mettre.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Conscient et convaincu de l’importance de ces solutions, je n’ai pas envie de n’être qu’un simple utilisateur final. C’est pour cela que je me demande régulièrement ce que pourrait apporter l’intégration d’un LLM dans les projets sur lesquels je travaille. S’il existe de nombreuses plateformes permettant de s’interfacer avec une intelligence artificielle, je me suis demandé comment installer et utiliser un des nombreux LLM open source disponible (il y en a dorénavant pléthore sur le net).&lt;/p&gt;

&lt;p&gt;Ce fut au final beaucoup plus facile que ce que j’avais imaginé. J’utilise pour ma part &lt;a href=&quot;https://ollama.ai &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Ollama&lt;/a&gt; qui est une solution extrêmement simple d’utilisateur et qui permet d’exécuter localement un &lt;a href=&quot;https://ollama.ai/library &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;vaste choix de LLM open source&lt;/a&gt;. Disponible sur Linux, Mac OS et prochainement sur Windows, Ollama va installer un service sur votre machine qui permettra de télécharger les modèles et d’interagir avec.&lt;/p&gt;

&lt;p&gt;Par exemple, j’ai pu tester &lt;a href=&quot;https://mistral.ai/ &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Mistral AI&lt;/a&gt; le LLM &lt;em&gt;made in France&lt;/em&gt; de la manière suivante:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;$ ollama pull mistral # permet de télécharger le modèle
$ ollama run mistral # pour commencer à discuter avec le modèle&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Et cerise sur la gâteau, il est possible d’interagir avec Ollama localement via une &lt;a href=&quot;https://github.com/ollama/ollama/blob/main/docs/api.md &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;API HTTP&lt;/a&gt; qui s’utilise aussi simplement que les outils en ligne de commande.&lt;/p&gt;

&lt;p&gt;C’est avec cette dernière que je peux alors commencer à intégrer des LLM dans mes projets simplement. J’ai d’ailleurs commencé à développer un composant PHP pour interagir simplement avec l’API Ollama. Ce dernier est dorénavant &lt;a href=&quot;https://github.com/jdecool/ollama-php-client &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;disponible publiquement sur Github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Alors, qu’attendez-vous pour vous y mettre ?&lt;/p&gt;
</description>
                    <pubDate>Sun, 28 Jan 2024 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2024/01/28/installer-et-utiliser-un-llm-en-local.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2024/01/28/installer-et-utiliser-un-llm-en-local.html</guid>
                </item>
            
        
            
                88
                <item>
                    <title>Bouchon (mock) et implémentation mémoire dans les tests</title>
                    <description>&lt;p&gt;Un projet sur lequel je travaille actuellement utilise dans ses tests des bouchons (ou &lt;em&gt;mock&lt;/em&gt; en anglais), mais également des implémentations mémoires de service travaillant avec des objets. Aussi, on m’a demandé l’intérêt d’utiliser l’un ou l’autre et je trouvais le sujet assez intéressant pour en écrire un billet.&lt;/p&gt;

&lt;p&gt;Lorsque l’on écrit des tests, il y a grosso modo deux écoles. La première va utiliser les bouchons (&lt;em&gt;mock&lt;/em&gt;) pour décrire le fonctionnement attendu d’une méthode d’un objet. La seconde, quant à elle, va limiter leur utilisation au maximum (voir totalement). Ce sont ces derniers qui auront alors plus tendance à utiliser des implémentations mémoires des services. Chacune des deux méthodes a son lot d’avantages et inconvénients. À chacun de se faire sa propre opinion et d’utiliser l’approche qui lui convient dans le contexte qui lui est donné.&lt;/p&gt;

&lt;p&gt;Commençons par les bouchons. Ces derniers sont généralement simples et rapides à créer. C’est d’ailleurs un de leur principal avantage et c’est aussi pour cela qu’on a tendance à beaucoup les utiliser. D’autant plus les &lt;em&gt;frameworks&lt;/em&gt; de test proposent généralement de nombreux outils pour nous aider dans cette tâche. Derrière cette simplicité de mise en place et d’utilisation se cachent également quelques inconvénients. Tout d’abord, bien souvent la gestion des bouchons n’est pas centralisée, on se retrouve donc à dupliquer le(s) comportement(s) attendu(s) dans l’ensemble des cas de tests. De ce fait, lorsqu’un contrat d’utilisation change, il sera nécessaire de repasser sur chaque définition de bouchons pour la mettre à jour. Un autre inconvénient est que l’on peut généralement faire tout et n’importe quoi avec un bouchon, il n’est pas si rare de se retrouver à tester des comportements qui ne pourraient pas se produire dans la réalité.&lt;/p&gt;

&lt;p&gt;Avec les implémentations mémoires et contrairement aux bouchons, on ne va pas créer une classe dans le but de simuler une implémentation d’un service. L’implémentation en mémoire est l’implémentation d’un service applicatif qui va travailler avec des données uniquement en mémoire. Bien qu’il soit un peu plus verbeux qu’un bouchon, on se retrouve donc indirectement à centraliser (plus facilement) la gestion du comportement dans une classe unique et en cas de modification, les tests pourront être adaptés plus facilement.&lt;/p&gt;

&lt;p&gt;Autre aspect très intéressant, contrairement à un bouchon où l’on est obligé de connaitre le détail d’implémentation des services à simuler (la simulation du comportement étant nécessairement rattachée à l’appel d’une fonction), dans le cadre d’une implémentation mémoire on se contente de passer une instance du service que l’on souhaite utiliser. C’est un gros point, car le test va pouvoir se concentrer uniquement sur le comportement du test sans nécessiter d’avoir la connaissance de comment l’ensemble est implémenté. Il est alors dans ce cas plus facile d’avoir des tests pérennes dans le temps en les rendant moins fragiles. C’est d’autant plus intéressant qu’il sera possible d’écrire des &lt;em&gt;tests de contrat&lt;/em&gt; afin de s’assurer que l’implémentation en mémoire (que l’on appelle également &lt;code&gt;InMemory&lt;/code&gt;) se comporte exactement de la même manière que l’implémentation réelle. Un point qui est beaucoup plus difficile à mettre en place avec les bouchons.&lt;/p&gt;

&lt;p&gt;À noter également que les implémentations mémoires sont généralement très utilisées dans les architectures hexagonales. Elles permettent de démarrer la mise en place d’un projet en amont d’avoir choisi les outils et/ou technologies qui seront utilisés. Par exemple, la mise en place d’un &lt;code&gt;InMemoryRepository&lt;/code&gt; permet d’avoir une implémentation mémoire de la persistance de données avant d’avoir choisi le moteur de stockage qui sera utilisé dans le projet.&lt;/p&gt;

&lt;p&gt;Comme je le disais en introduction, il y a donc deux écoles. À vous de choisir la vôtre en fonction du contexte. Comme toujours en informatique, &lt;strong&gt;il y a une multitude de choix d’implémentation&lt;/strong&gt; à chacun de trouver la manière de travailler qui lui convienne.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Et en bonus voici un exemple pour illustrer ce billet:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;interface CommandBus
{
    public function handle(object $command): void;
}

// implémentation mémoire
class InMemoryCommandBus implements CommandBus
{
    private array $handledCommands;

    public function __construct()
    {
        $this-&amp;gt;handledCommands = [];
    }

    public function handle(object $command): void
    {
        $this-&amp;gt;handledCommands = $command;
    }

    public function getHandledCommands(): array
    {
        return $this-&amp;gt;handledCommands;
    }
}

// test utilisant les bouchons
class MockImplementationTest extends TestCase
{
    public function testCommandShouldBeHandled(): void
    {
        $expectedCommand = new stdClass;

        // ici le bouchon va simuler l&amp;#39;appel et faire l&amp;#39;assertion sur les données
        $bus = $this-&amp;gt;createMock(CommandBus::class);
        $bus-&amp;gt;expect(self::once())
            -&amp;gt;method(&amp;#39;handle&amp;#39;)
            -&amp;gt;with($exepectedCommand);

        $subject = new TestedClass($bus);
        $subject-&amp;gt;doAction();

        // réalisation d&amp;#39;autres assertions
    }
}

// test utilisant l&amp;#39;implémentation mémoire
class InMemoryImplementationTest extends TestCase
{
    // on notera également que ce test respecte la structuration
    // Arrange, Act, Assert d&amp;#39;écriture des tests
    public function testCommandShouldBeHandled(): void
    {
        $bus = new InMemoryCommandBus();

        $subject = new TestedClass($bus);
        $subject-&amp;gt;doAction();

        self::assertCount(1, $this-&amp;gt;bus-&amp;gt;getHandledCommands());
        self::assertContainsEquals(new stdClass, $this-&amp;gt;bus-&amp;gt;getHandledCommands());
        // réalisation d&amp;#39;autres assertions
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

</description>
                    <pubDate>Fri, 20 Oct 2023 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2023/10/20/bouchon-mock-et-implementation-memoire-test.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2023/10/20/bouchon-mock-et-implementation-memoire-test.html</guid>
                </item>
            
        
            
        
            
                89
                <item>
                    <title>Utiliser PHPUnit 10 avec Symfony</title>
                    <description>&lt;p&gt;Au moment où j’écris ce billet, PHPUnit 10 a été publié il y a près de 8 mois (le 3 février 2023). Et pourtant si on tente de l’utiliser dans un projet Symfony utilisant le bridge &lt;code&gt;symfony/phpunit-bridge&lt;/code&gt;, nous obtenons l’erreur suivante: &lt;code&gt;PHP Fatal error:  Uncaught Error: Class &quot;PHPUnit\TextUI\Command&lt;/code&gt;.&lt;/p&gt;

&lt;!--more--&gt;

&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;PHP Fatal error:  Uncaught Error: Class &quot;PHPUnit\TextUI\Command&quot; not found in /home/jdecool/Workspace/sandbox/test/bin/phpunit:11
Stack trace:
#0 {main}
  thrown in /home/jdecool/Workspace/sandbox/test/bin/phpunit on line 11

Fatal error: Uncaught Error: Class &quot;PHPUnit\TextUI\Command&quot; not found in /home/jdecool/Workspace/sandbox/test/bin/phpunit on line 11

Error: Class &quot;PHPUnit\TextUI\Command&quot; not found in /home/jdecool/Workspace/sandbox/test/bin/phpunit on line 11

Call Stack:
    0.0001     396248   1. {main}() /home/jdecool/Workspace/sandbox/test/bin/phpunit:0
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;La raison étant que le script fourni par Symfony utilise du code qui a été supprimé dans la dernière version majeure du framework de test.&lt;/p&gt;

&lt;p&gt;Il y a plusieurs propositions en cours pour corriger ce problème, mais aucune n’est actuellement mergée.&lt;/p&gt;

&lt;p&gt;Pour corriger cela, vous pouvez simplement modifier le fichier &lt;code&gt;bin/phpunit&lt;/code&gt; en remplaçant la ligne ci-dessous:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-patch&quot;&gt;-    PHPUnit\TextUI\Command::main();
+    exit((new PHPUnit\TextUI\Application)-&amp;gt;run($_SERVER[&apos;argv&apos;]));
&lt;/code&gt;&lt;/pre&gt;
</description>
                    <pubDate>Mon, 09 Oct 2023 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2023/10/09/utiliser-phpunit-10-avec-symfony.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2023/10/09/utiliser-phpunit-10-avec-symfony.html</guid>
                </item>
            
        
            
                90
                <item>
                    <title>Les interactions: la clé de la réussite d&apos;un développement logiciel</title>
                    <description>&lt;p&gt;Au plus j’avance dans ma carrière et au plus je m’intéresse aux interactions entre individus. Je me rends compte au fil de mes expériences que la partie technique et produit ne sont qu’une partie de la clé de la réussite d’un logiciel. On peut avoir les meilleurs &lt;em&gt;Product Owner&lt;/em&gt;, les meilleurs développeurs, cela ne suffit pas.&lt;/p&gt;

&lt;p&gt;Par exemple, &lt;a href=&quot;/blog/2023/01/08/de-l-utilite-des-reunions.html&quot;&gt;les équipes ont besoin d’interaction&lt;/a&gt;, ce ne sont pas des personnes qui travaillent au même endroit, mais &lt;a href=&quot;/blog/2022/09/16/quelle-est-la-definition-d-une-equipe.html&quot;&gt;des personnes qui poursuivent &lt;strong&gt;ensemble&lt;/strong&gt; une mission&lt;/a&gt;. Et c’est de la responsabilité du management de faire en sorte de fournir le cadre de travail idéal afin de fluidifier ces échanges.&lt;/p&gt;

&lt;p&gt;Comme le dit si bien &lt;a href=&quot;https://www.linkedin.com/feed/update/urn:li:activity:6982593503126024192/ &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Ludovic Girodon&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Un manager n’est pas là pour être un bon technicien, il est avant tout là pour avoir le recul nécessaire pour faire grandir son équipe&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Faire communiquer les équipes, travailler ensemble, partager une même vision et le chemin pour y parvenir, voila ce qui fera la réussite de votre projet.&lt;/p&gt;

&lt;p&gt;Être à l’écoute, juste, respectueux, pédagogue, savoir guider (sans imposer) donnera la confiance et l’autonomie nécessaire à une équipe pour grandir, être autonome et atteindre les objectifs qui lui sont fixés.&lt;/p&gt;
</description>
                    <pubDate>Fri, 28 Jul 2023 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2023/07/28/les-interactions-la-cle-de-la-reussite-d-un-developpement-logiciel.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2023/07/28/les-interactions-la-cle-de-la-reussite-d-un-developpement-logiciel.html</guid>
                </item>
            
        
            
                91
                <item>
                    <title>Rendez-vous le 12 mai 2023 à l&apos;AFUP Day</title>
                    <description>&lt;p&gt;L’AFUP (Association Française des Utilisateurs de PHP) vient de l’annoncer sur &lt;a href=&quot;https://afup.org/news/1175-afupday2023lyon-soldout &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;son blog&lt;/a&gt; (&lt;a href=&quot;/cache/20230119-afup-day-2023/index.html&quot;&gt;cache&lt;/a&gt;), &lt;a href=&quot;https://twitter.com/afup/status/1615753679734177794 &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Twitter&lt;/a&gt; &amp;amp; &lt;a href=&quot;https://mastodon.online/@afup/109711266321865849 &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Mastodon&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;La communauté PHP lyonnaise est en ébullition à l’idée du retour de l’AFUP Day dans sa ville. L’AFUP Day 2023 Lyon est SOLD-OUT, 4 mois avant l’événement !&lt;/p&gt;
&lt;/blockquote&gt;

&lt;!--more--&gt;

&lt;p&gt;Un peu plus de deux ans sans avoir eu l’occasion de participer à des conférences en présentiel et une participation fort agréable au Forum PHP 2022, j’aurai le plaisir d’assister aux retours des conférences de l’AFUP à Lyon !&lt;/p&gt;

&lt;p&gt;On s’y retrouve en mai ? ;)&lt;/p&gt;
</description>
                    <pubDate>Thu, 19 Jan 2023 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2023/01/19/rendez-vous-le-12-mai-2023-a-l-afup-day.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2023/01/19/rendez-vous-le-12-mai-2023-a-l-afup-day.html</guid>
                </item>
            
        
            
        
            
                92
                <item>
                    <title>De l&apos;utilité des réunions</title>
                    <description>&lt;p&gt;La tendance (qui n’est pas nouvelle, je l’entends depuis plusieurs années maintenant) est à la limitation des réunions. Dernier exemple en date, &lt;a href=&quot;https://www.developpez.com/actu/340032/Shopify-demande-a-ses-employes-de-dire-non-aux-reunions-et-de-se-retirer-des-grands-groupes-de-discussion-internes-ce-qui-relance-le-debat-sur-l-utilite-des-reunions-recurrentes-en-entreprise/ &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Shopify qui envisage de supprimer toutes les réunions récurrentes de plus de deux personnes&lt;/a&gt;. J’ai vraiment l’impression qu’il s’agit là d’une tendance de fond.&lt;/p&gt;

&lt;p&gt;Pourtant, bien que je sois loin d’être proréunion, que ces dernières puissent être improductives et une perte de temps potentielle pour les personnes qui y participent, il faut dire que c’est généralement à cause d’un manque de préparation. Un très bon article sur le sujet est certainement celui d’Eric DASPET: &lt;a href=&quot;https://n.survol.fr/n/faire-plus-de-reunions &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Faire plus de réunions&lt;/a&gt;. J’y partage l’ensemble des idées qui y sont exprimées.&lt;/p&gt;

&lt;p&gt;Pour moi, le plus important est dit dès le début: “&lt;strong&gt;se réunir c’est communiquer et collaborer&lt;/strong&gt;”.&lt;/p&gt;

&lt;p&gt;Au sein d’une équipe, il est parfois utile et primordial d’avoir une communication synchrone, que tout le monde soit là pour discuter, échanger, entendre la même chose au même moment. C’est aussi cela qui forge des équipes, participe à la cohésion et à la mise en place d’une vision commune. Ce n’est pas travailler chacun dans son coin.&lt;/p&gt;

&lt;p&gt;Bien entendu, ce n’est pas chose facile et comme &lt;a href=&quot;https://twitter.com/omartineau/status/1611315085581975552 &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;on me le fait remarquer sur Twitter&lt;/a&gt;, en plus du manque de préparation, un des problèmes majeurs des réunions: ce sont toujours les mêmes personnes qui parlent, prennent la parole, écrase parfois les autres personnes (même de manière involontaire). C’est un biais pas toujours facile à éviter auquel il faut faire attention.&lt;/p&gt;

&lt;p&gt;Oui il faut y faire attention, mais il y a de nombreuses techniques et outils pour faire attention à cela et éviter les pièges. Pour ma part, je puise beaucoup dans les outils et pratiques, rituels et cérémonies agiles.&lt;/p&gt;
</description>
                    <pubDate>Sun, 08 Jan 2023 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2023/01/08/de-l-utilite-des-reunions.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2023/01/08/de-l-utilite-des-reunions.html</guid>
                </item>
            
        
            
        
            
                93
                <item>
                    <title>À propos de la couverture de code</title>
                    <description>&lt;p&gt;Lorsque l’on met en place des tests sur un projet et que l’on souhaite commencer à avoir des indicateurs sur ces derniers, c’est naturellement que l’on se tourne sur le taux de couverture de code (alias le &lt;em&gt;code coverage&lt;/em&gt; en anglais). Pourtant, dans absolument toutes mes expériences professionnelles, cet indicateur a systématiquement été critiqué par certains développeurs.&lt;/p&gt;

&lt;p&gt;Effectivement, &lt;strong&gt;le taux de couverture de code est la mesure du nombre de lignes de code source exécutées durant les tests&lt;/strong&gt;. En d’autres termes, il s’agit de compter les lignes du code de production parcourues durant les tests. Le problème étant qu’un test peut ne faire aucune vérification et pour autant avoir un taux de couverture de code élevé.&lt;/p&gt;

&lt;p&gt;Prenons par exemple le code suivant:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;function calcul(string $op, int $x, int $y): int
{
    return match ($op) {
        &amp;#39;+&amp;#39; =&amp;gt; $x + $y,
        &amp;#39;-&amp;#39; =&amp;gt; $x - $y,
        &amp;#39;*&amp;#39; =&amp;gt; $x * $y,
        &amp;#39;/&amp;#39; =&amp;gt; $x / $y,
        default =&amp;gt; throw new \InvalidArgumentException(),
    };
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Avec le test associé:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;class CalculTest extends TestCase
{
    public function testCalcul(): void
    {
        $resultat = calcul(&amp;#39;+&amp;#39;, 1, 5);

        $this-&amp;gt;assertIsInt($resultat);
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Avec un tel test, nous obtenons le résultat ci-dessous:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;Summary:
  Classes: 100.00% (1/1)
  Methods: 100.00% (1/1)
  Lines:   100.00% (7/7)&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Nous avons donc un test qui ne fait aucune vérification et pourtant nous avons un taux de couverture de code de 100%. C’est pour cela que cette métrique est décriée.&lt;/p&gt;

&lt;p&gt;C’est aussi pour cela qu’&lt;strong&gt;il faut considérer la couverture de code comme un indicateur négatif d’un projet&lt;/strong&gt; et non pas comme un indicateur de mesure de qualité. Un projet ayant une couverture de code basse peut indiquer un nombre insuffisant de tests sur le projet. Alors qu’à l’inverse, cela indique qu’il y a des tests, mais cet indicateur à lui seul, &lt;strong&gt;ne donne pas de mesure sur la qualité&lt;/strong&gt; de ces derniers. Pour cela, il faudra le corréler avec d’autres métriques.&lt;/p&gt;

&lt;p&gt;À noter qu’il y a également une seconde métrique corrélée au taux de couverture de code, mais dont on parle beaucoup moins. Il s’agit du taux de couverture de branche (ou &lt;em&gt;branch coverage&lt;/em&gt;). On parle également du &lt;strong&gt;taux de couverture des chemins d’exécution&lt;/strong&gt; qui comme son nom l’indique permet de vérifier si chaque parcours de code possible a été exécuté ou non.&lt;/p&gt;

&lt;p&gt;Si l’on reprend notre exemple précédent avec le code effectuant les opérations de calculs et le test associé que nous avions écrit, on se retrouverait avec la couverture suivante:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;Summary:
  Classes:  0.00% (0/1)
  Methods:  0.00% (0/1)
  Paths:    20.00% (1/5)
  Branches: 42.86% (3/7)
  Lines:    100.00% (7/7)&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Ici on peut clairement voir la différence. Toutes les lignes de code ont été exécutées, mais les tests n’ont permis d’explorer qu’un seul des cinq chemins d’exécution possibles. Cela indique clairement que notre test, s’il est pertinent, ne couvre que peu de comportements.&lt;/p&gt;

&lt;p&gt;On peut donc voir le taux de couverture de branche comme un indicateur plus fiable que le taux de couverture de code. Néanmoins attention, le calcul de ce dernier est coûteux en termes de ressources (temps, CPU, mémoire) et cela peut ralentir drastiquement votre suite de tests.&lt;/p&gt;
</description>
                    <pubDate>Tue, 03 Jan 2023 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2023/01/03/a-propos-de-la-couverture-de-code.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2023/01/03/a-propos-de-la-couverture-de-code.html</guid>
                </item>
            
        
            
                94
                <item>
                    <title>Retrouvez les utilisateurs Twitter sur Mastodon</title>
                    <description>&lt;p&gt;Comme je l’ai annoncé il y a peu de temps, &lt;a href=&quot;/blog/2022/11/14/retrouvez-moi-sur-mastodon.html&quot;&gt;je suis maintenant présent sur Mastodon&lt;/a&gt;. Comme sur &lt;a href=&quot;https://twitter.com/jdecool &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Twitter&lt;/a&gt;, j’y partage ma veille, mes réflexions ainsi que mes différentes trouvailles. Mais il s’agit également d’une source de &lt;a href=&quot;/blog/2017/09/06/formation-continue-du-developpeur-veille.html&quot;&gt;veille&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Migrer vers un nouveau réseau social n’est pas aisé, encore plus quand il s’agit d’un réseau décentralisé comme Mastodon: il faut reconstruire sa “communauté”. Et ici, pas d’algorithme de recommandation pour nous aider.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;À noter ici, que quand je parle de communauté, je pense principalement aux personnes auxquelles je suis abonné et dont je peux suivre les publications plus qu’à l’inverse.&lt;/p&gt;

&lt;p&gt;Étant donné qu’il y a un exode plus ou moins massif des utilisateurs Twitter sur Mastodon, il est intéressant d’essayer de retrouver les utilisateurs Twitter qui ont migré vers Mastodon.  Et pour nous aider dans cette tâche, il existe de nombreux outils disponibles.&lt;/p&gt;

&lt;p&gt;Pour ma part, j’ai utilisé l’&lt;a href=&quot;https://twitter.com/Wattenberger/status/1594718025168523264 &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;outil développé par  Amelia Wattenberger&lt;/a&gt; disponible à l’adresse suivante &lt;a href=&quot;https://mastodon-bridge.vercel.app &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;https://mastodon-bridge.vercel.app&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Mais il en existe bien d’autres, dont le plus connu/utilisé est &lt;a href=&quot;javascript:;&quot; class=&quot;broken-link&quot; rel=&quot;nofollow&quot; data-original-url=&quot;https://twitodon.com/ &quot;&gt;Twitodon&lt;/a&gt; et dont le &lt;a href=&quot;https://github.com/diddledani/twitodon &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;code source est disponible sur Github&lt;/a&gt; contrairement à l’outil précédent (ce qui est le comble pour une application conçue par une personne travaillant chez Github).&lt;/p&gt;

&lt;p&gt;Donc si l’aventure Mastodon vous tente, après &lt;a href=&quot;https://www.numerama.com/tech/1175038-comment-choisir-son-instance-sur-mastodon.html &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;avoir choisi votre instance&lt;/a&gt;, commencez par retrouver vos utilisateurs Twitter.&lt;/p&gt;
</description>
                    <pubDate>Tue, 06 Dec 2022 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2022/12/06/retrouvez-les-utilisateurs-twitter-sur-mastodon.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2022/12/06/retrouvez-les-utilisateurs-twitter-sur-mastodon.html</guid>
                </item>
            
        
            
                95
                <item>
                    <title>Retrouvez-moi sur Mastodon</title>
                    <description>&lt;p&gt;Depuis son rachat par Elon Musk, Twitter est un peu dans la tourmente: licenciement en masse, départs volontaires des employés et des utilisateurs “effrayés” qui fuient vers des réseaux alternatifs. Il n’y a pas de doute, Twitter est dans une très mauvaise période. Je ne suis (pour le moment) pas inquiet pour le réseau social, les semaines à venir seront difficiles, mais je pense que Twitter s’en remettra.&lt;/p&gt;

&lt;p&gt;Néanmoins, cela ne m’empêche pas, comme de nombreux utilisateurs, d’aller explorer des solutions alternatives.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;J’utilise Twitter essentiellement pour partager de la veille technique. Au-delà de la partager publiquement, c’est également un moyen de stocker de l’information que je peux retrouver plus tard. Sans grande originalité, j’explore aujourd’hui la solution qui connaît le plus d’engouement actuellement: &lt;a href=&quot;https://mastodon.social&quot;&gt;Mastodon&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Ainsi, inspiré par l’&lt;a href=&quot;https://martinfowler.com/articles/exploring-mastodon.html&quot;&gt;exploration de Mastodon de Martin Fowler&lt;/a&gt;, j’ai donc créé un compte sur l’instance &lt;a href=&quot;https://phpc.social&quot;&gt;phpc.social&lt;/a&gt;, une instance dédiée à la communauté PHP.&lt;br /&gt;
Sur cette instance, n’hésitez pas à suivre mon compte &lt;a href=&quot;https://phpc.social/web/@jdecool&quot;&gt;@jdecool@phpc.social&lt;/a&gt;, vous y retrouverez mes différents partages.&lt;/p&gt;

&lt;p&gt;Pour le moment, je ne quitte pas Twitter, je reste actif sur ce dernier. Ma veille sera publiée à la fois sur &lt;a href=&quot;https://twitter.com/jdecool&quot;&gt;Twitter&lt;/a&gt; et &lt;a href=&quot;https://phpc.social/web/@jdecool&quot;&gt;Mastodon&lt;/a&gt; grâce à l’utilisation de &lt;a href=&quot;https://moa.party&quot;&gt;Moa&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Nous verrons bien ce que l’avenir nous réserve et si les deux instances ont un avenir…&lt;/p&gt;
</description>
                    <pubDate>Mon, 14 Nov 2022 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2022/11/14/retrouvez-moi-sur-mastodon.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2022/11/14/retrouvez-moi-sur-mastodon.html</guid>
                </item>
            
        
            
                96
                <item>
                    <title>Quelle est la définition d&apos;une équipe ?</title>
                    <description>&lt;p&gt;Aujourd’hui, en consultant LinkedIn, j’ai eu l’occasion de voir passer une infographie de &lt;a href=&quot;https://www.linkedin.com/in/catherine-testa/ &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Catherine TESTA&lt;/a&gt; proposant &lt;a href=&quot;https://www.linkedin.com/feed/update/urn:li:activity:6975717119543889920/?updateEntityUrn=urn%3Ali%3Afs_updateV2%3A%28urn%3Ali%3Aactivity%3A6975717119543889920%2CFEED_DETAIL%2CEMPTY%2CDEFAULT%2Cfalse%29 &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;une définition (que je trouve très juste) d’une équipe&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Je vous laisse apprécier.&lt;/p&gt;

&lt;center&gt;
    &lt;img src=&quot;/img/blog/20220916-quelle-est-la-definition-d-une-equipe/infographie-equipe.jpeg&quot; alt=&quot;Infographie - Quelle est la définition d&apos;une équipe ?&quot; width=&quot;100%&quot; height=&quot;100%&quot; /&gt;
&lt;/center&gt;

&lt;!--more--&gt;
</description>
                    <pubDate>Fri, 16 Sep 2022 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2022/09/16/quelle-est-la-definition-d-une-equipe.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2022/09/16/quelle-est-la-definition-d-une-equipe.html</guid>
                </item>
            
        
            
                97
                <item>
                    <title>Corriger un fichier UTF-8 mal encodé</title>
                    <description>&lt;p&gt;J’ai une application qui me génère pour une raison que j’ignore des fichiers UTF-8 qui contiennent des caractères mal encodés. Si ce n’est visuellement pas dérangeant, certaines applications plantent à l’ouverture du fichier à cause de cela.&lt;/p&gt;

&lt;p&gt;S’il vous arrive la même chose et que vous êtes sous Linux (ou Mac), sachez que corriger ce problème est très simple et ce fait au travers d’une ligne de commande avec &lt;code&gt;iconv&lt;/code&gt; qui est généralement installé par défaut sur les machines.&lt;/p&gt;

&lt;p&gt;Pour cela, il faudra préciser que l’encodage en entrée est de l’UTF-8 et faire de même pour l’encodage de destination:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;iconv -f utf-8 -t utf-8 -c mon-fichier-en-erreur.ext &amp;gt; destination.ext&lt;/code&gt;&lt;/p&gt;

&lt;!--more--&gt;
</description>
                    <pubDate>Wed, 14 Sep 2022 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2022/09/14/corriger-un-fichier-utf8-sous-linux.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2022/09/14/corriger-un-fichier-utf8-sous-linux.html</guid>
                </item>
            
        
            
                98
                <item>
                    <title>Le pattern &quot;Parameter Object&quot;</title>
                    <description>&lt;p&gt;Après avoir publié mon billet concernant &lt;a href=&quot;/blog/2022/02/25/le-pattern-commande.html&quot;&gt;le pattern &lt;em&gt;Commande&lt;/em&gt;&lt;/a&gt; hier, on m’a demandé s’il était possible d’utiliser une commande pour remplacer un groupe de paramètre d’une fonction ou d’une méthode avec pour objectif de simplifier le code et sa lisibilité sans pour autant mettre en place un bus de commande.&lt;/p&gt;

&lt;p&gt;Pour répondre sans détour à cette question, oui il est tout à fait possible de remplacer des paramètres de fonction par un objet, cela correspond même à un autre patron de conception nommé le &lt;strong&gt;paramètre objet&lt;/strong&gt; (ou &lt;em&gt;Parameter Object&lt;/em&gt; en anglais).&lt;/p&gt;

&lt;p&gt;Ce &lt;em&gt;pattern&lt;/em&gt; permet de regrouper un ensemble de paramètres au sein d’un ou plusieurs objets qui seront transmis à votre fonction afin de &lt;strong&gt;rendre le code plus lisible&lt;/strong&gt; en simplifiant les signatures de méthodes.&lt;/p&gt;

&lt;p&gt;Il permet également de &lt;strong&gt;supprimer de la duplication de code&lt;/strong&gt; dans le cas ou différentes méthodes définissent systématiquement un même groupe de paramètre parce que ces derniers sont fonctionnellement liés.&lt;/p&gt;

&lt;p&gt;Par exemple, je travaille sur une application de calendrier dans laquelle j’ai énormément de méthodes qui travaillent avec des dates de début et des dates fins de période:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;public function filterEventByDate(DateTime $start, DateTime $end);
public function getEvents(DateTime $start, DateTime $end);
public function getAvailableSlot(DateTime $start, DateTime $end);&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;On remarque ici que de nombreuses méthodes partagent les paramètres &lt;code&gt;$start&lt;/code&gt; et &lt;code&gt;$end&lt;/code&gt; qui partage une même sémantique fonctionnelle. On pourra alors regrouper ces deux paramètres au sein d’un object &lt;code&gt;DateRange&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;class DateRange
{
    public function __construct(
        public readonly DateTime $start,
        public readonly DateTime $end,
    ) {
    }
}
public function filterEventByDate(DateRange $dateRange);
public function getEvents(DateRange $dateRange);
public function getAvailableSlot(DateRange $dateRange);&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Si l’exemple précédent est relativement simpliste, dans des cas d’utilisation réels, on peut facilement imaginer que notre objet paramètre pourra regrouper bien plus de données.&lt;/p&gt;
</description>
                    <pubDate>Sat, 26 Feb 2022 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2022/02/26/la-pattern-parameter-object.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2022/02/26/la-pattern-parameter-object.html</guid>
                </item>
            
        
            
                99
                <item>
                    <title>Le pattern &quot;Commande&quot;</title>
                    <description>&lt;p&gt;Si je reprends &lt;a href=&quot;https://fr.wikipedia.org/wiki/Commande_(patron_de_conception) &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;la définition de wikipedia&lt;/a&gt;, il s’agit d’un patron de conception (ou &lt;em&gt;design pattern&lt;/em&gt; en anglais) qui permet d’encapsuler la notion d’&lt;em&gt;invocation&lt;/em&gt;. C’est un moyen très utile de rendre votre code modulaire et lisible en découpant votre code en différentes commandes, chacune ayant des responsabilités uniques et spécifiques.&lt;/p&gt;

&lt;p&gt;Un objet &lt;code&gt;Command&lt;/code&gt; est un DTO (&lt;code&gt;Data Transfer Object&lt;/code&gt;) qui permet d’encapsuler toutes les informations nécessaires pour réaliser une action. La commande n’a aucune logique, elle s’utilise au travers d’un bus (&lt;em&gt;command bus pattern&lt;/em&gt;) qui est chargé de recevoir &lt;a href=&quot;/blog/2020/04/04/coherence-des-donnees-dans-un-modele-oriente-objet.html&quot;&gt;une commande &lt;strong&gt;valide&lt;/strong&gt;&lt;/a&gt; et de déclencher les actions associées. Son unique responsabilité est de gérer les différents cas d’utilisation en déléguant l’exécution de la commande à une classe spécifique au traitement en question. L’objectif est de permettre au développeur d’&lt;strong&gt;émettre une intention&lt;/strong&gt; d’effectuer une action sans que ce dernier n’ait à se soucier de la manière dont elle va être traitée et/ou implémentée.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&amp;lt;?php

class CreateCustomer {
    public function __construct(
        public readonly string $name,
        public readonly string $phonenumber,
    ) {
        // assert data validity
    }
}

$this-&amp;gt;bus-&amp;gt;execute(new CreateCustomer(/* ... */));&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Il existe de &lt;a href=&quot;https://packagist.org/?query=command%20bus &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;nombreux composants PHP&lt;/a&gt; qui vous permettent d’implémenter ce pattern dans vos projets. Mais concevoir un bus de commande n’est fondamentalement pas compliqué. Dans sa version minimaliste, il s’agit de faire un lien entre une commande et son gestionnaire (ou &lt;em&gt;handler&lt;/em&gt; en anglais).&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&amp;lt;?php

class CommandBus {
    /** Handler[] */
    public function __construct(
        private readonly array $handlers,
    ) {
    }

    public function execute(object $command): void
    {
        $commandClass = get_class($command);
        $handler = $this-&amp;gt;handlers[$commandClass] ?? throw new RuntimeException(&amp;quot;No handler for $commandClass command.&amp;quot;);

        $handler($command);
    }
}

class MyCommand {}
class MyCommandHandler
{
    public function __invoke(MyCommand $command): void
    {
        echo &amp;quot;Hello World&amp;quot;, PHP_EOL;
    }
}

$bus = new CommandBus([
    MyCommand::class =&amp;gt; new MyCommandHandler(),
]);
$bus-&amp;gt;execute(new MyCommand()); // will display &amp;quot;Hello World&amp;quot;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Si ce type d’architecture a été popularisé par la montée en puissance du &lt;a href=&quot;/blog/2015/03/30/la-conception-pilotee-par-le-domaine.html&quot;&gt;Domain Driven Design&lt;/a&gt; ou des architectures &lt;a href=&quot;https://en.wikipedia.org/wiki/Command%E2%80%93query_separation &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;CQRS&lt;/a&gt;, il est tout à fait possible de l’utiliser dans n’importe quel projet.&lt;/p&gt;

&lt;p&gt;J’en suis pour ma part très friand car il y a de nombreux avantages à utiliser ce paradigme. Il permet de bien &lt;strong&gt;découper le code&lt;/strong&gt;, d’avoir des petites classes avec &lt;strong&gt;des responsabilités uniques&lt;/strong&gt; et isolées, donc faciles à tester. Au-delà de ces avantages purement techniques, cela permet également de pouvoir dresser simplement la liste de cas d’utilisation qui sont gérés par notre projet.&lt;/p&gt;

&lt;p&gt;Un autre aspect que j’apprécie particulièrement est la facilité que l’on a d’étendre le comportement d’un bus de commande. Il est possible de décorer un bus afin de lui ajouter des comportements supplémentaires qui ne sont pas directement liéss à l’intention métier que l’on souhaite réaliser dans l’application. Il sera par exemple possible de créer un bus de commande qui gérera les transactions de base de données, de gérer des logs ou d’effectuer diverses tâches de monitoring, tout cela de manière complètement &lt;strong&gt;découplée&lt;/strong&gt; de votre code orienté métier.&lt;/p&gt;
</description>
                    <pubDate>Fri, 25 Feb 2022 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2022/02/25/le-pattern-commande.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2022/02/25/le-pattern-commande.html</guid>
                </item>
            
        
            
                100
                <item>
                    <title>Gestion des identités et Domain Driven Design</title>
                    <description>&lt;p&gt;Maîtriser la gestion des identités de nos entités dans une application dont la conception est pilotée par le domaine (&lt;em&gt;Domain Driven Design&lt;/em&gt;) est un enjeu majeur. Cela permet notamment d’être découplé de son système de gestion de base de données et d’avoir une maîtrise totale de la gestion des données métier.&lt;/p&gt;

&lt;p&gt;Il existe pour cela de nombreuses solutions possibles. Celle qui est certainement la plus commune (parce qu’il s’agit d’une des plus simples) est d’utiliser des identifiants uniques universels (&lt;em&gt;Universally Unique IDentifier&lt;/em&gt; en anglais), plus couramment appelés &lt;code&gt;UUID&lt;/code&gt;. Ce dernier peut être utilisé conjointement avec l’identifiant auto-incrémenté qui est généralement utilisé par défaut dans nos bases de données relationnelles. La plupart des &lt;code&gt;ORM&lt;/code&gt; sont d’ailleurs capables de le gérer nativement. Mais il sera préférable de gérer la création de l’UUID soi-même pour éviter d’être dépendant du composant qui interagit avec votre base de données. En PHP, vous pourrez utiliser &lt;a href=&quot;https://packagist.org/packages/ramsey/uuid &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;ramsey/uuid&lt;/code&gt;&lt;/a&gt; ou encore &lt;a href=&quot;https://packagist.org/packages/symfony/uid &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;symfony/uid&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&amp;lt;?php

use Ramseu\Uuid\UuidInterface;

class MyEntity
{
    public function __construct(
        private UuidInterface $uuid,
    ){
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Il n’est pas toujours nécessaire d’utiliser un identifiant unique aléatoire. Selon le type de données que vous manipulez, vous travaillez peut-être avec des &lt;strong&gt;clés naturelles&lt;/strong&gt;. Par exemple, si vous gérez une bibliothèque de livres ou que vous êtes amené à manipuler des numéros de sécurité sociale. Dans le premier cas, un livre est identifié par un numéro international normalisé: l’&lt;a href=&quot;https://fr.wikipedia.org/wiki/International_Standard_Book_Number &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;ISBN&lt;/a&gt;. Analysez donc bien les données que vous manipulez, car selon le métier que vous cherchez à modéliser, il n’est pas toujours nécessaire de passer une solution &lt;em&gt;technique&lt;/em&gt;.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&amp;lt;?php

class Book
{
    public function __construct(
        private ISBN $isbn,
    ){
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Dans certains contextes, il est nécessaire d’utiliser des &lt;strong&gt;identifiants séquentiels&lt;/strong&gt;. Dans ce cas précis, il est intéressant d’utiliser le mécanisme d’auto-incrément de la base de données. Afin de rester indépendant de notre système de base de données, il est alors primordial d’être capable de récupérer la valeur de l’identifiant avant d’écrire la donnée. Une technique pouvant être utilisée est de créer une table avec un identifiant auto-incrémenté, que l’on utilisera comme système de réservation. Pour obtenir la valeur, il faudra insérer une ligne dans cette table et l’utiliser l’identifiant généré comme identifiant de notre entité.&lt;/p&gt;

&lt;p&gt;Nous venons de voir les 3 approches les plus couramment utilisées pour gérer les identités de nos entités dans un projet piloté par le domaine. Les approches listées ici ne sont pas exhaustives, mais ont pour but de vous montrer les solutions les plus simples et courantes. Peut-être vous demandez-vous laquelle utiliser ?&lt;/p&gt;

&lt;p&gt;Comme toujours en informatique, il n’y a pas de recette universelle, cela dépendra de votre contexte. Si dans une grande majorité des cas, l’utilisation d’&lt;code&gt;UUID&lt;/code&gt; sera suffisant, il y a des cas plus complexes qui nécessiteront une approche alternative. À vous de choisir la bonne solution en fonction de votre contexte métier.&lt;/p&gt;
</description>
                    <pubDate>Mon, 15 Nov 2021 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2021/11/15/gestion-des-identites-et-domain-driven-design.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2021/11/15/gestion-des-identites-et-domain-driven-design.html</guid>
                </item>
            
        
            
                101
                <item>
                    <title>Quand utiliser des interfaces?</title>
                    <description>&lt;p&gt;J’ai récemment découvert le concept de &lt;em&gt;Interface-based programming&lt;/em&gt; (également appelé &lt;em&gt;Interface-based architecture&lt;/em&gt;). Il s’agit d’un modèle d’architecture reposant sur des interfaces plutôt que des implémentations. J’ai notamment eu l’occasion d’expérimenter cette technique de manière dogmatique où toute classe d’un projet (service, DTO, &lt;a href=&quot;/blog/2015/03/25/les-objets-valeurs-ou-value-object.html&quot;&gt;value object&lt;/a&gt;, entité de base de données, modèle métier…) devait être liée à une interface. L’objectif d’une telle architecture est de concevoir une application par “contrat” et d’avoir du code entièrement découplé de toute implémentation.&lt;/p&gt;

&lt;p&gt;Malheureusement, avoir une approche dogmatique qui pousse à appliquer un concept de manière systématique sans se poser de questions sur le contexte dans lequel il est utilisé peut annuler les bénéfices qu’il serait possible d’en tirer. Ce fut l’occasion pour moi de me poser la question: &lt;strong&gt;quand est-il judicieux d’utiliser des interfaces de classe?&lt;/strong&gt;&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;J’ai donc beaucoup réfléchi à cette question et fait de nombreuses recherches sur le sujet. Je suis finalement tombé sur un article de Mathias NOBACK “&lt;a href=&quot;https://matthiasnoback.nl/2018/08/when-to-add-an-interface-to-a-class/ &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;When to add an interface to a class&lt;/a&gt;” qui résume tellement bien mon point de vue sur le sujet que je vous recommande vivement de le lire. À défaut vous trouverez les principales lignes directrices ci-dessous:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Une classe définit de manière implicite un contrat d’utilisation au travers de ces méthodes publiques. Créer une interface peut-être utile si ces dernières ne devraient pas être accessibles.&lt;/li&gt;
  &lt;li&gt;Lorsque du code interagit avec le système de fichier, du réseau ou n’importe quel système tiers, la mise en place d’une interface est essentielle pour se découpler de l’implémentation et faciliter l’écriture de tests.&lt;/li&gt;
  &lt;li&gt;De la même manière que le point précédent, si votre code utilise du code que vous ne maintenez pas vous-même, mettre en place une interface vous permettra de changer simplement l’implémentation que vous avez utilisée.&lt;/li&gt;
  &lt;li&gt;Si vous travaillez sur un système informatique ou des développeurs tiers peuvent surcharger ou remplacer le fonctionnement par défaut, une interface permettra de définir le contrat attendu pour fonctionner.&lt;/li&gt;
&lt;/ul&gt;
</description>
                    <pubDate>Sun, 14 Nov 2021 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2021/11/14/quand-utiliser-des-interfaces.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2021/11/14/quand-utiliser-des-interfaces.html</guid>
                </item>
            
        
            
                102
                <item>
                    <title>Lire du code est une compétence</title>
                    <description>&lt;p&gt;À ce sujet, je vous recommande la lecture de l’&lt;a href=&quot;https://trishagee.com/2020/09/07/reading-code-is-a-skill/ &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;article de Trisha GEE&lt;/a&gt; où elle explique bien le sujet et d’où je tire le titre de ce billet.&lt;/p&gt;

&lt;p&gt;Ecrire du code lisible et/ou facilement compréhensible est compliqué à juste titre, car c’est avant tout quelque chose d’extrêmement subjectif.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Nous avons tous une vision (potentiellement différente) de ce qu’est un code lisible. Nous n’en avons pas tout la même définition.&lt;/p&gt;

&lt;p&gt;Nous écrivons d’ailleurs tous du code différemment. Cela peut se voir lorsque nous travaillons en équipe et nous sommes parfois capables de reconnaître qui a écrit un morceau de code à la manière dont il a été codé.&lt;/p&gt;

&lt;p&gt;C’est pour cela que je trouve l’article assez juste sur le fait que “lire du code” est une compétence à part entière. Bien entendu, comme le souligne Trisha, il est bien évident qu’un développeur devrait tout faire pour écrire du code le plus lisible possible, qui est cela dit au passage une autre compétence à part entière.&lt;/p&gt;

&lt;p&gt;Cela n’est pas toujours quelque chose de simple, surtout si l’on reste “enfermé” dans sa bulle. C’est pour cela qu’il est d’autant plus important de regarder ce qui se fait dans les autres langages ou frameworks, de lire du code de différents projets, de s’ouvrir à d’autres communautés.&lt;/p&gt;

&lt;p&gt;Cela n’est pas facile et concerne tous les développeurs. Il m’est arrivé de rencontrer des développeurs avec une dizaine d’années d’expérience et ultracompétent, mais qui était dans une grande difficulté quand ils étaient amenés à lire du code qu’ils n’avaient pas écrit eux-mêmes et qui ne respectait pas leurs standards de code.&lt;/p&gt;

&lt;p&gt;Lire du code est une compétence à part entière, ne la négligez pas.&lt;/p&gt;
</description>
                    <pubDate>Thu, 23 Sep 2021 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2021/09/23/lire-du-code-est-une-competence.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2021/09/23/lire-du-code-est-une-competence.html</guid>
                </item>
            
        
            
                103
                <item>
                    <title>Ne soyez pas passif, créez</title>
                    <description>&lt;p&gt;Hier, &lt;a href=&quot;/blog/2021/03/03/lancez-vous.html&quot;&gt;je vous incitais à vous lancer&lt;/a&gt; et à passer l’action. Si l’on y réfléchit, cela va de pair avec le fait d’arrêter d’être passif au quotidien et de passer à l’action.&lt;/p&gt;

&lt;p&gt;C’est aussi cette idée que m’a inspiré l’article &lt;a href=&quot;javascript:;&quot; class=&quot;broken-link&quot; rel=&quot;nofollow&quot; data-original-url=&quot;https://www.tik.dev/less-twitter &quot;&gt;Less Twitter, more building&lt;/a&gt;. Je me retrouve un peu dans ce dernier, j’ai récemment changé d’employeur, je travaille maintenant dans un domaine que je trouve extrêmement stimulant et passionnant. À côté de ça, je trouve de moins en moins le temps de faire tout ce que j’aimerais faire en dehors de mon travail. C’est pour cela que j’ai décidé de passer plus de temps à créer des choses plutôt qu’à recevoir du contenu ou de l’information de manière passive.&lt;/p&gt;

&lt;p&gt;J’ai par exemple longtemps utilisé Twitter afin de &lt;a href=&quot;https://twitter.com/jdecool &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;partager la veille&lt;/a&gt; que je fais. J’ai toujours considéré qu’&lt;a href=&quot;/blog/2017/09/06/formation-continue-du-developpeur-veille.html&quot;&gt;il était important de se tenir à jour de &lt;em&gt;l’état de l’art&lt;/em&gt;&lt;/a&gt; de notre métier pour pouvoir proposer les meilleures solutions aux problèmes que l’on rencontre. Je suis également un grand adepte de Udemy que j’utilise régulièrement pour me former sur divers sujets.&lt;/p&gt;

&lt;p&gt;Mais au final, tout ça n’a que peu de sens si ce n’est pas mis en pratique. Tout ce savoir théorique n’a pas de valeur, que si l’on sait comment l’appliquer, dans quelle situation, les différents avantages et inconvénients. Or pour être certain de pouvoir mesurer correctement tout ça, il faut de la mise en pratique.&lt;/p&gt;

&lt;p&gt;C’est donc pour cela qu’il est primordial de créer, de créer des choses, de créer du contenu afin de vous permettre d’expérimenter, d’appliquer, de tester ce que vous avez en tête, de créer de la valeur ou tout simplement de vous confronter aux réelles problématiques de ce que vous pensez avoir acquis comme savoir.&lt;/p&gt;
</description>
                    <pubDate>Thu, 04 Mar 2021 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2021/03/04/ne-soyez-pas-passif-creez.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2021/03/04/ne-soyez-pas-passif-creez.html</guid>
                </item>
            
        
            
                104
                <item>
                    <title>Lancez-vous !</title>
                    <description>&lt;p&gt;Je viens de lire &lt;em&gt;&lt;a href=&quot;javascript:;&quot; class=&quot;broken-link&quot; rel=&quot;nofollow&quot; data-original-url=&quot;https://torbet.co/posts/Just-Start &quot;&gt;Just Start&lt;/a&gt;&lt;/em&gt; de Guy TORBET, un étudiant de 17 ans qui explique que peu importe ce que l’on souhaite faire et peu importe le moment où l’on a l’idée de faire quelque chose, ce n’est jamais le bon moment de se lancer.&lt;/p&gt;

&lt;p&gt;On est &lt;strong&gt;trop jeune&lt;/strong&gt;, on n’a &lt;strong&gt;pas assez d’expérience&lt;/strong&gt;, on a &lt;strong&gt;une famille à gérer&lt;/strong&gt;, on est &lt;strong&gt;trop vieux&lt;/strong&gt; et après il est &lt;strong&gt;trop tard&lt;/strong&gt; ! C’est ce que tente de nous expliquer Guy, peu importe ce que l’on voudra faire, ce ne sera jamais &lt;strong&gt;LE&lt;/strong&gt; meilleur moment. C’est donc pour cela que le meilleur c’est &lt;strong&gt;maintenant&lt;/strong&gt; !&lt;/p&gt;

&lt;p&gt;C’est un peu l’idée que l’on retrouve dans l’excellent livre &lt;strong&gt;&lt;a href=&quot;https://www.amazon.com/-/es/Josh-Long/dp/0988578603/ref=sr_1_1?__mk_es_US=%C3%85M%C3%85%C5%BD%C3%95%C3%91&amp;amp;dchild=1&amp;amp;keywords=Execute+-+Acting+on+Ideas+Immediately+When+Inspired+Rather+Than+Following+the+Normal+Rules&amp;amp;qid=1614804785&amp;amp;sr=8-1 &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Execute&lt;/a&gt;&lt;/strong&gt; qui n’est malheureusement plus disponible à la vente. L’idée que lorsque vous avez une idée, un projet, le meilleur moment pour tenter de le concrétiser, c’est d’agir maintenant, c’est-à-dire au moment où votre motivation est la plus importante. À l’inverse, au plus vous attendrez, au plus vous vous trouverez des excuses pour ne pas arriver au bout des choses.&lt;/p&gt;
</description>
                    <pubDate>Wed, 03 Mar 2021 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2021/03/03/lancez-vous.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2021/03/03/lancez-vous.html</guid>
                </item>
            
        
            
                105
                <item>
                    <title>Modéliser une relation plusieurs à plusieurs (n:n) dans un agrégat</title>
                    <description>&lt;p&gt;J’ai publié hier un article sur &lt;a href=&quot;/blog/2020/10/18/la-notion-d-agregat-en-ddd.html&quot;&gt;la notion d’agrégat&lt;/a&gt;, si le principe peut paraître relativement simple, il peut soulever quelques questions comme par exemple, comment modéliser une relation type n:n (plusieurs à plusieurs).&lt;/p&gt;

&lt;p&gt;Prenons un exemple que je suis amené à manipuler régulièrement: le cas de la gestion d’un &lt;em&gt;lead&lt;/em&gt; dans une application de type CRM. Prenons un cas où une affaire peut être liée à une ou plusieurs entreprises (dans le cas d’achat groupé par exemple). L’affaire étant l’objet de base, nous allons en faire un agrégat.&lt;/p&gt;

&lt;p&gt;Si nous cherchons à modéliser notre agrégat, nous pouvons créer un objet &lt;code&gt;Affaire&lt;/code&gt; qui sera donc lié à une collection d’objets &lt;code&gt;Entreprise&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-php&quot;&gt;class Affaire
{
    // ...

    /** @var Entreprise[] */
    private array $entreprises;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Dans notre contexte métier (que l’on pourrait représenter par un &lt;a href=&quot;https://martinfowler.com/bliki/BoundedContext.html &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;em&gt;Bounded Context&lt;/em&gt;&lt;/a&gt;), lorsqu’une entreprise est reliée à une affaire, nous souhaitons conserver la date à laquelle l’entreprise a été ajouté au &lt;em&gt;lead&lt;/em&gt; en cours.&lt;/p&gt;

&lt;p&gt;La solution la plus simple est alors d’ajouter la date directement à notre objet entreprise comme ceci:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-php&quot;&gt;class Entreprise
{
    private Siren $siren;
    private string $nom;
    private Adresse $adresse;
    private DateTimeImmutable $dateAjout;

    // ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Dans notre métier, il est possible d’avoir plusieurs &lt;em&gt;leads&lt;/em&gt; différents avec une même entreprise. Si la modélisation précédente était simple et intuitive, elle a un défaut majeur: elle va nous obliger à dupliquer les informations des entreprises.&lt;br /&gt;
Or, on souhaiterait que si une entrepise &lt;code&gt;A&lt;/code&gt;, qui est liée à une affaire &lt;code&gt;L1&lt;/code&gt; et &lt;code&gt;L2&lt;/code&gt;, est mise à jour, les informations soient mises à jour dans les deux affaires.&lt;/p&gt;

&lt;p&gt;Ici, les développeurs vont en général modéliser une association de la manière suivante:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-php&quot;&gt;class Affaire
{
    /** @var AffaireEntreprise */
    private array $entreprises;
}

class Entreprise
{
    private Siren $siren;
    private string $nom;
    private Adresse $adresse;
}

class AffaireEntreprise
{
    private Affaire $affaire;
    private Entreprise $entreprise;
    private DateTimeImmutable $dateAjout;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Cette modélisation est très courante car elle reflète en réalité la manière dont les données seraient modélisées dans un système de base de données relationnelle (notamment lorsque l’on travaille sur une application CRUD).&lt;/p&gt;

&lt;p&gt;De plus, les structures de données précédentes semblent indiquer qu’une même entité &lt;code&gt;Entreprise&lt;/code&gt; peut être partagée entre plusieurs agrégats &lt;code&gt;Affaire&lt;/code&gt;. On enfreint ici l’un des principes fondamentaux des agrégats, à savoir que &lt;strong&gt;les objets qui constituent un agrégat doivent lui être propre et ne peuvent être partagés&lt;/strong&gt; (sinon ce dernier n’est plus indépendant et il ne peut assurer la cohérence de ces données puisque les objets partagés peuvent évoluer sans qu’il en ait connaissance).&lt;/p&gt;

&lt;p&gt;À cela s’ajoute que la manipulation d’une affaire dans notre code va conduire à une utilisation qui n’est pas des plus intuitives:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-php&quot;&gt;$affaire = $repository-&amp;gt;get($id);
foreach ($affaire-&amp;gt;getEntreprises() as $entrepriseAffaire) {
    // ici la variable `entrepriseAffaire` représente en réalité une classe
    // d&apos;association entre un objet `Affaire` et un objet `Entreprise`
    // il est alors nécessaire de passer par un accesseur supplémentaire pour
    // accéder à notre entreprise
    $entreprise = $entrepriseAffaire-&amp;gt;getEntreprise();
}

// cela est d&apos;autant plus visible sur on cherche à accéder à la première
// entreprise de l&apos;affaire
$affaire-&amp;gt;getEntreprises()[0]-&amp;gt;getEntreprise();
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Personnellement, je trouve cette écriture complètement contre-intuitive. Et surtout, il y a fort à parier que les développeurs ne comprennent pas réellement ce qui est manipuler lorsque l’on accède à &lt;code&gt;$affaire-&amp;gt;getEntreprises()&lt;/code&gt;.&lt;br /&gt;
Si on en revient au DDD, cette notion de &lt;code&gt;AffaireEntreprise&lt;/code&gt; n’est pas une notion métier, c’est une glu technique. Tout cela semble indiquer que nous faisons une erreur de modélisation de notre domaine.&lt;/p&gt;

&lt;p&gt;Mais comment écrire proprement cette relation alors ? Avant de répondre à cette question, prenons le temps de réfléchir aux données que nous manipulons. Disons que nous travaillons dans un contexte métier (&lt;a href=&quot;https://martinfowler.com/bliki/BoundedContext.html &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Bounded Context&lt;/a&gt;) type &lt;em&gt;CRM&lt;/em&gt;. Ce que nous venons de mettre en évidence, c’est que dans ce contexte, nous travaillons avec des &lt;code&gt;Entreprise&lt;/code&gt; qui peuvent vivre de manière indépendantes. Ces dernières ont un cycle de vie qui n’est pas lié à notre contexte de vente.&lt;/p&gt;

&lt;p&gt;Dans notre contexte de gestion de vente, nous souhaitons simplement modéliser un lien entre une affaire et une entreprise. De ce fait, la seule information sur une société qui nous intéresse est son &lt;a href=&quot;https://www.insee.fr/fr/metadonnees/definition/c2047 &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;SIREN&lt;/a&gt; (son identifiant unique). Dans notre contexte de gestion des ventes, nous n’avons pas à nous préoccuper de comment sont gérées les informations attachées à une entreprise (soit via un système tiers, soit via un autre contexte de notre application).&lt;/p&gt;

&lt;p&gt;Concrètement, la modélisation d’une entreprise dans le cadre de notre &lt;em&gt;lead&lt;/em&gt; peut alors se modéliser ainsi:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-php&quot;&gt;class Affaire
{
    /** @var Entreprise[] */
    private array $entreprises;
}

class Entreprise
{
    private Siren $siren;
    private DateTimeImmutable $dateAjout;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Ce code nous permettra ainsi de manipuler nos objets avec une API intuitive:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-php&quot;&gt;$affaire = $repository-&amp;gt;get($id);
foreach ($affaire-&amp;gt;getEntreprises() as $entreprise) {
    // je peux accéder directement aux informations utiles à mon contexte
    $entreprise-&amp;gt;getSiren();
}

$affaire-&amp;gt;getEntreprises()[0]-&amp;gt;getSiren();
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Comme je l’ai rapidement évoqué précédemment, l’obtention des détails d’une entreprise (ainsi que leurs mises à jour) n’est pas de la responsabilité de notre contexte (ces informations ne sont pas utiles et doivent/peuvent évoluer de manière indépendante à notre système).&lt;/p&gt;

&lt;p&gt;Au travers de cette nouvelle modélisation, notre agrégat ne travaille dorénavant plus avec des données partagées et dont il n’a pas la maîtrise. Il n’est plus dépendant de modifications extérieures puisqu’il n’y a plus d’objets en commun.&lt;/p&gt;

&lt;p&gt;Si maintenant notre code doit être amené à afficher des informations concernant des informations additionnelles aux données traitées par notre agrégat (comme par exemple le nom de la société que nous ne stockons pas) cela pourra se faire à la demande au moyen de sources données externes. Ces dernières ayant la responsabilité de gérer la donnée et de la mettre à jour, garantissant ainsi que notre contexte accédera toujours à une donnée à jour.&lt;/p&gt;
</description>
                    <pubDate>Mon, 19 Oct 2020 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2020/10/19/modeliser-une-relation-n-n-dans-un-agregat.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2020/10/19/modeliser-une-relation-n-n-dans-un-agregat.html</guid>
                </item>
            
        
            
                106
                <item>
                    <title>La notion d&apos;agrégat en DDD</title>
                    <description>&lt;p&gt;La notion d’agrégat est un principe essentiel de la conception pilotée par le domaine (&lt;em&gt;Domain Driven Design&lt;/em&gt;). L’agrégat désigne un groupe d’objets du domaine qui doit être manipulé comme une seule unité métier et dont les relations enfants ne peuvent être utilisées de manière indépendante.&lt;/p&gt;

&lt;p&gt;Prenons par exemple le cas d’une facture. Une facture est composée de différentes propriétés: un émetteur, un destinataire, une date, un identifiant ainsi qu’une liste de produits ou prestations. Dans une modélisation orientée DDD, la facture est un agrégat car les différents éléments qui la composent n’ont pas de raison d’exister unitairement.&lt;/p&gt;

&lt;p&gt;De ce fait, toute modification de notre facture doit se dérouler au sein de notre agrégat. Il ne devrait par exemple, pas être possible d’ajouter une nouvelle ligne sans passer pas notre objet de base &lt;code&gt;Facture&lt;/code&gt;. De la même façon, il ne devrait pas être possible de récupérer des éléments de notre facture sans passer cette dernière. Cela implique que lorsque l’on récupère ou enregistre un agrégat, toutes les données de ce dernier sont traités en un seul bloc.&lt;/p&gt;

&lt;p&gt;Ci-dessous un exemple de comment nous pourrions modéliser l’ajout de ligne à notre &lt;code&gt;Facture&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-php&quot;&gt;class Facture
{
	/** @var FactureLigne[] */
	private array $lignes;

	public function lignes(): array
	{
		return $this-&amp;gt;lignes;
	}

	public function ajouterLigne(FactureLigne $ligne): self
	{
		// règles de validations
		// ...

		$this-&amp;gt;lignes[] = $ligne;

		return $this;
	}
}

// utilisation dans le code applicatif
$facture = new Facture(/* ... */);
foreach ($produits as $produit) {
	$facture-&amp;gt;ajouterLigne(/* ... */);
}

$entityManager-&amp;gt;save($facture);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;L’un des principaux avantages de ce mode de fonctionnement est que l’on garantie ainsi la cohérence de notre objet tout au long de son utilisation. Il est seul responsable des règles de validation, qui sont alors centralisées au sein de notre agrégat et assure ainsi la cohérence des données.&lt;/p&gt;

&lt;p&gt;Si cette modélisation peut paraître logique pour certains, on trouve de nombreuses applications CRUD qui ne modélisent pas les données de cette manière. La facture et les lignes associées étant parfois gérées indépendamment.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-php&quot;&gt;class Facture
{ /* ... */ }

class FactureLigne
{ /* ... */ }


// utilisation dans le code applicatif
$facture = new Facture(/* ... */);
$entityManager-&amp;gt;save($facture);

foreach ($produits as $produit) {
	$ligne = new FactureLigne(/* ... */);
	$entityManager-&amp;gt;save($ligne);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Dans l’exemple précédent, les lignes de facture étant ajoutées en dehors du contexte de notre facture, rien n’assure que les données des lignes soient cohérentes avec l’état de notre facture. Par exemple, si la facture est payée, il ne devrait pas être possible de pouvoir la modifier. Or si les actions de modification se déroulent en dehors de notre agrégat, il sera difficile de garantir ces règles métiers (cela ne sera pas impossible, mais les règles de validation risquent de se retrouver éparpillées dans le code au lieu d’être centralisées dans l’objet qui a la connaissance de son contexte).&lt;/p&gt;

&lt;p&gt;Dans son &lt;a href=&quot;https://martinfowler.com/bliki/DDD_Aggregate.html &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;article sur le sujet&lt;/a&gt;, &lt;a href=&quot;https://martinfowler.com &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Martin Fowler&lt;/a&gt; ajoute:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Les agrégats sont parfois confondus avec les classes de collection (telles que les listes). Les agrégats sont des concepts du domaine, tandis que les collections sont génériques. Un agrégat contient souvent des collections et des champs simples. Le terme “agrégat” est commun et est utilisé dans différents contextes, mais il ne fait pas forcément référence au même concept que les agrégats dans un contexte de conception pilotée par le métier.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Les agrégats permettent de garantir la consistance des objets de votre application. Que vous développiez une application DDD ou CRUD, je ne peux que vous conseillez d’utiliser ce &lt;em&gt;pattern&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;–&lt;/p&gt;

&lt;p&gt;Sur le même thème:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/2020/10/19/modeliser-une-relation-n-n-dans-un-agregat.html&quot;&gt;Modéliser une relation plusieurs à plusieurs (n:n) dans un agrégat&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
                    <pubDate>Sun, 18 Oct 2020 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2020/10/18/la-notion-d-agregat-en-ddd.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2020/10/18/la-notion-d-agregat-en-ddd.html</guid>
                </item>
            
        
            
                107
                <item>
                    <title>Make or buy</title>
                    <description>&lt;center&gt;
&lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;fr&quot;&gt;&lt;p lang=&quot;fr&quot; dir=&quot;ltr&quot;&gt;J’adore quand une boite qui fait plusieurs millions de CA te dis qu’un outil à 8€/mois pour 10 personnes c’est pas donné...&lt;/p&gt;&amp;mdash; Jérémy DECOOL (@jdecool) &lt;a href=&quot;https://twitter.com/jdecool/status/1283428581633335296?ref_src=twsrc%5Etfw&quot;&gt;15 juillet 2020&lt;/a&gt;&lt;/blockquote&gt; &lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;/center&gt;

&lt;p&gt;Voici un tweet que j’ai publié il y a quelques heures et qui peut paraître un peu abrupte. C’est pourtant une situation que j’ai pu constater et que je constate encore fréquemment en entreprise: faire toutes les économies possibles.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Et ce, même s’il s’agit de somme relativement dérisoire à l’échelle de l’entreprise. Dans le cas de ce tweet, l’outil en question avait un coût de 8€/mois/utilisateur et concerne grosso modo 10 utilisateurs (soit un coup de 80€/mois, environ 960€/an). La société en question emploie quasiment une centaine de personnes et réalise plusieurs millons de chiffre d’affaire chaque année. Que représente ce coût à l’échelle de la société au final ? Une goutte d’eau.&lt;/p&gt;

&lt;p&gt;Il est important de se poser les bonnes questions et de raisonner correctement. La vraie question, c’est le &lt;em&gt;make or buy&lt;/em&gt;. Combien de temps, d’économie (de temps et/ou d’argent) fait gagner l’outil que l’on souhaite mettre en place ? Si on décidait de le faire soi-même, quel serait le &lt;em&gt;vrai&lt;/em&gt; coût au final ? Dans ces cas-là il faut penser au salaire des personnes qui vont travailler sur la conception, la réalisation et la maintenance de l’outil. Quel en serait le coût d’exploitation ?&lt;/p&gt;

&lt;p&gt;Je pense qu’au final, sur un coût aussi dérisoire, pour un outil qui n’est pas stratégique et n’a aucune valeur ajoutée pour l’entreprise, on ne devrait même pas se poser la question.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Pour la petite anecdote, cela me rappelle une situation vécue il y a quelque temps. Une équipe de développeurs utilisait un outil dans le cadre de son travail. Un outil qui avait été remis en cause à cause d’une tarification qui “n’était pas justifiée”. L’outil en question avait donc été remplacé par une alternative Open Source (car considérée comme gratuite).&lt;/p&gt;

&lt;p&gt;Au final, la solution nouvellement en place, n’a pas réellement conquis l’équipe car jugé plus complexe à utiliser. Et chose que je trouve assez paradoxale, c’est rien que le serveur nécessaire pour faire tourner la plateforme coutait deux fois plus cher que la solution initiale (sans compter le temps passer par les personnes pour la mise en place et l’exploitation).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;En me relisant, je trouve que ce billet fait incroyablement écho à un article d’&lt;a href=&quot;https://twitter.com/edasfr/ &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Eric DASPET&lt;/a&gt; sur &lt;a href=&quot;https://n.survol.fr/n/toutes-ces-petites-economies-qui-coutent-un-pognon-de-dingue &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;toutes ces petites écono­mies qui coûtent un pognon de dingue&lt;/a&gt;.&lt;/p&gt;
</description>
                    <pubDate>Wed, 15 Jul 2020 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2020/07/15/make-or-buy.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2020/07/15/make-or-buy.html</guid>
                </item>
            
        
            
                108
                <item>
                    <title>Utiliser MinIO comme stockage de données objets en PHP</title>
                    <description>&lt;p&gt;Je travaille sur un projet où il est question de stocker un grand nombre de fichiers. Ce dernier devrait être hébergé dans le &lt;em&gt;Cloud&lt;/em&gt; et notamment sur des infrastructures orientées &lt;em&gt;Serverless&lt;/em&gt;. Il est donc impensable d’utiliser un système de fichiers “classique” pour stocker des fichiers. Je me suis donc tourné vers les systèmes de stockage objet (type Amazon S3). C’est alors qu’en effectuant quelques recherches, j’ai découvert &lt;a href=&quot;https://min.io &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;MinIO&lt;/a&gt;. Il s’agit d’un serveur de stockage open source compatible Amazon S3.&lt;/p&gt;

&lt;p&gt;Pour utiliser MinIO avec PHP, vous pouvez simplement utiliser le &lt;a href=&quot;https://packagist.org/packages/aws/aws-sdk-php &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;SDK d’AWS&lt;/a&gt;. Après avoir créé votre bucket dans le serveur MinIO, vous pouvez commencer à travailler avec le système de fichier comme vous le feriez avec S3:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;$s3 = new Aws\S3\S3Client([
    &amp;#39;version&amp;#39; =&amp;gt; &amp;#39;latest&amp;#39;,
    &amp;#39;region&amp;#39;  =&amp;gt; &amp;#39;eu-east-1&amp;#39;, // cette configuration n&amp;#39;a pas d&amp;#39;importance pour MinIO
    &amp;#39;endpoint&amp;#39; =&amp;gt; &amp;#39;http://localhost:9000&amp;#39;,
    &amp;#39;use_path_style_endpoint&amp;#39; =&amp;gt; true, // ce paramètre doit obligatoirement être actif
    &amp;#39;credentials&amp;#39; =&amp;gt; [
        &amp;#39;key&amp;#39;    =&amp;gt; &amp;#39;ACCESSKEYID&amp;#39;,
        &amp;#39;secret&amp;#39; =&amp;gt; &amp;#39;SECRETACCESSKEY&amp;#39;,
    ],
]);

// enregistrer un fichier
$insert = $s3-&amp;gt;putObject([
     &amp;#39;Bucket&amp;#39; =&amp;gt; &amp;#39;bucket&amp;#39;,
     &amp;#39;Key&amp;#39;    =&amp;gt; &amp;#39;key&amp;#39;,
     &amp;#39;Body&amp;#39;   =&amp;gt; &amp;#39;Hello from MinIO!!&amp;#39;,
]);

// récupérer un fichier
$retrive = $s3-&amp;gt;getObject([
     &amp;#39;Bucket&amp;#39; =&amp;gt; &amp;#39;bucket&amp;#39;,
     &amp;#39;Key&amp;#39;    =&amp;gt; &amp;#39;key&amp;#39;,
     &amp;#39;SaveAs&amp;#39; =&amp;gt; &amp;#39;key_local&amp;#39;,
]);&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Voila, ce n’est guère plus compliqué.&lt;/p&gt;

&lt;p&gt;Pour ma part, lorsque je travaille avec le système de fichiers, j’ai pour habitude d’utiliser &lt;a href=&quot;https://packagist.org/packages/league/flysystem &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;la bibliothèque Flysystem&lt;/a&gt;. Si vous ne connaissez pas &lt;em&gt;Flysystem&lt;/em&gt;, je vous recommande vivement de vous y intéresser. Il s’agit de composant permettant d’abstraire l’accès à tout système de fichiers. Cela vous permettra d’écrire un code qui sera automatiquement compatible avec votre système de fichiers local, un FTP, mais également un grand nombre de fournisseur Cloud tel que &lt;em&gt;Amazon S3&lt;/em&gt; ou &lt;em&gt;Azure Blob Storage&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Une fois le composant de base &lt;code&gt;league/flysystem&lt;/code&gt; et l’extension &lt;code&gt;league/flysystem-aws-s3-v3&lt;/code&gt; installé avec l’aide de Composer. Il vous suffit de créer l’adapteur qui va bien:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;$s3 = new Aws\S3\S3Client([
    &amp;#39;version&amp;#39; =&amp;gt; &amp;#39;latest&amp;#39;,
    &amp;#39;region&amp;#39;  =&amp;gt; &amp;#39;eu-east-1&amp;#39;, // cette configuration n&amp;#39;a pas d&amp;#39;importance pour MinIO
    &amp;#39;endpoint&amp;#39; =&amp;gt; &amp;#39;http://localhost:9000&amp;#39;,
    &amp;#39;use_path_style_endpoint&amp;#39; =&amp;gt; true, // ce paramètre doit **obligatoirement** être actif
    &amp;#39;credentials&amp;#39; =&amp;gt; [
        &amp;#39;key&amp;#39;    =&amp;gt; &amp;#39;ACCESSKEYID&amp;#39;,
        &amp;#39;secret&amp;#39; =&amp;gt; &amp;#39;SECRETACCESSKEY&amp;#39;,
    ],
]);

$adapter = new League\Flysystem\AwsS3v3\AwsS3Adapter($client, &amp;#39;bucket&amp;#39;);
$filesystem = new League\Flysystem\Filesystem($adapter);

// enregistrer un fichier
$filesystem-&amp;gt;put(&amp;#39;my-file.txt&amp;#39;, &amp;#39;Hello from MinIO!!&amp;#39;);

// récupérer un fichier
$object = $filesystem-&amp;gt;get(&amp;#39;my-file.txt&amp;#39;);&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Si vous utilisez Symfony, en ce qui me concerne j’utilise le bundle &lt;a href=&quot;https://packagist.org/packages/oneup/flysystem-bundle &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;oneup/flysystem-bundle&lt;/code&gt;&lt;/a&gt;. Pour Laravel le composant le plus complet semble être &lt;a href=&quot;https://packagist.org/packages/graham-campbell/flysystem &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;graham-campbell/flysystem&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Tout ça pour dire, que si vous souhaitez utiliser un système de stockage objet et que vous souhaitez héberger vous même ce dernier, je ne peux que vous conseiller de jeter un coup d’oeil à &lt;a href=&quot;https://min.io &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;MinIO&lt;/a&gt;.&lt;/p&gt;
</description>
                    <pubDate>Tue, 07 Jul 2020 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2020/07/07/utiliser-minio-comme-stockage-de-donnees-objets-en-php.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2020/07/07/utiliser-minio-comme-stockage-de-donnees-objets-en-php.html</guid>
                </item>
            
        
            
                109
                <item>
                    <title>Définition du rôle de Tech Lead</title>
                    <description>&lt;p&gt;Je me suis déjà exprimé plusieurs fois sur le &lt;a href=&quot;/blog/2018/12/10/le-role-de-lead-dev.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;rôle de &lt;i&gt;lead dev&lt;/i&gt;&lt;/a&gt;. Je découvre aujourd’hui la définition de Patrick KUA (si vous ne le connaissez pas, je vous recommande vivement de vous intéresser à son blog) sur &lt;a href=&quot;https://www.patkua.com/blog/the-definition-of-a-tech-lead/ &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;le sujet&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Si ce n’est pas déjà fait, je vous recommande vivement la lecture du billet avec lequel je suis assez en phase et que Patrick résume de la manière suivante:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“A Tech Lead is a software engineer responsible for leading a team and alignment of the technical direction.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;!--more--&gt;

&lt;p&gt;On est donc bien dans ce que je qualifiais d’assistant du CTO. Ce dernier est donc une personne technique (un développeur) qui s’implique pour communiquer une vision technique et gérer la qualité des livrables de l’équipe.&lt;/p&gt;

&lt;p&gt;Toujours selon la définition de Patrick, c’est aussi à lui qu’incombe de résoudre les désaccords techniques qu’il peut y avoir. Bien que sur cette dernière partie, j’avais estimé que le &lt;i&gt;lead dev&lt;/i&gt; ne devait pas avoir de missions managériales, on peut y voir ici un aspect de management pour lequel je ne peux qu’admettre que cela doit faire partie des attributions du poste.&lt;/p&gt;

&lt;p&gt;Cela ne doit pas empêcher de trouver une solution collégialement avec les différents membres de l’équipe, mais en cas de forts désaccords, il faut bien que quelqu’un puisse trancher. D’ailleurs à propos de trancher, je ne peux que vous renvoyer vers l’&lt;a href=&quot;https://gb-prod.fr/2017/07/01/qui-tranche-qui-decide.html &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;excellent article de Gilles ROUSTAN&lt;/a&gt; toujours aussi pertinent. Trancher, ce n’est pas forcément aller dans le sens que l’on avait initialement imaginé.&lt;/p&gt;
</description>
                    <pubDate>Mon, 01 Jun 2020 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2020/06/01/definition-du-role-de-tech-lead.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2020/06/01/definition-du-role-de-tech-lead.html</guid>
                </item>
            
        
            
                110
                <item>
                    <title>Cohérence des données dans un modèle orienté objet</title>
                    <description>&lt;p&gt;On ne présente plus la programmation orientée objet, c’est aujourd’hui l’un, si ce n’est le paradigme de programmation informatique le plus utilisé. Le concept central de ce paradigme: “les objets représentent un concept, une idée ou toute entité du monde physique” (&lt;a href=&quot;https://fr.wikipedia.org/wiki/Programmation_orient%C3%A9e_objet &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;définition Wikipédia&lt;/a&gt;). Le principal avantage de la programmation orientée objet (POO en abrégé) est de permettre de rassembler au sein d’une structure de données, les propriétés et les actions (méthodes) qui sont liées au concept que l’on manipule. Une des règles essentielles de la POO est que &lt;em&gt;toute action/modification d’un objet doit laisser ce dernier dans un état cohérent&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Mais qu’entend-on par &lt;em&gt;état cohérent&lt;/em&gt; ? La cohérence d’un système peut être définie comme une exigence selon laquelle toute modification de l’état de ce dernier doit conserver la validité des données selon les règles et contraintes qui le déterminent.&lt;/p&gt;

&lt;p&gt;C’est une jolie définition mais concrètement, ça veut dire quoi ? Prenant comme exemple, un objet &lt;code&gt;Facture&lt;/code&gt;. Une facture est composée de 4 propriétés: &lt;code&gt;une date&lt;/code&gt;, &lt;code&gt;un montant HT&lt;/code&gt;, &lt;code&gt;une TVA&lt;/code&gt; et &lt;code&gt;un montant TTC&lt;/code&gt; (cette dernière est discutable, mais l’exemple vous permettra de bien comprendre). On pourrait donc écrire une première version de notre objet comme ceci:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;class Facture
{
    private DateTime $date;
    private float $montantHT;
    private float $tva;
    private float $montantTTC;

    public function __construct(DateTime $date, float $montantHT, float $tva, float $montantTTC)
    {
        $this-&amp;gt;date = $date;
        $this-&amp;gt;montantHT = $montantHT;
        $this-&amp;gt;tva = $tva;
        $this-&amp;gt;montantTTC = $montantTTC;
    }

    public function getDate(): DateTime
    {
        return $this-&amp;gt;date;
    }

    public function getMontantHT(): float
    {
        return $this-&amp;gt;montantHT;
    }

    public function getTVA(): float
    {
        return $this-&amp;gt;tva;
    }

    public function getMontantTTC(): float
    {
        return $this-&amp;gt;montantTTC;
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Vous voyez ce qui ne va pas avec cette classe ? Cette dernière nous permet de créer une facture qui pourrait être &lt;code&gt;$facture = new Facture(new DateTime(&apos;2020-04-02 18:00:00&apos;), 10.0, 0.1, 50.0)&lt;/code&gt; ce qui ne serait pas du tout logique, les trois propriétés de notre facture sont liées entre elles et ne peuvent pas prendre n’importe quelles valeurs. Notre facture est donc dans un état incohérent.&lt;/p&gt;

&lt;p&gt;En POO, le &lt;em&gt;constructeur&lt;/em&gt; est une fonction appelée lors de l’instanciation d’un objet. Le rôle du constructeur est double, il doit initialiser les propriétés de notre objet, mais il doit également &lt;em&gt;s’assurer que les données sont cohérentes&lt;/em&gt;. Dans notre exemple, il doit s’assurer que le montant TTC de notre facture correspond au montant HT auquel s’ajoute le pourcentage de TVA et renvoyer une erreur si ce n’est pas le cas.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;class Facture
{
    public function __construct(DateTime $date, float $montantHT, float $tva, float $montantTTC)
    {
        if (($montantHT * (1 + $tva)) !== $montantTTC) {
            throw new DomainException();
        }

        $this-&amp;gt;date = $date;
        $this-&amp;gt;montantHT = $montantHT;
        $this-&amp;gt;tva = $tva;
        $this-&amp;gt;montantTTC = $montantTTC;
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Nous l’avons rapidement mentionné dans l’introduction de ce billet, il est possible de créer des actions sur nos objets. Dans notre exemple, nous souhaiterions par exemple pouvoir modifier le taux de TVA. Dans la plupart des projets informatiques, cela se fait via la création d’une méthode &lt;code&gt;setTVA&lt;/code&gt; dont l’implémentation pourrait ressembler à :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;class Facture
{
    // ...

    public function setTVA(float $tva): void
    {
        $this-&amp;gt;tva = $tva;
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Rien ne vous choque ? Si cette méthode nous permet effectivement de modifier le taux de TVA de notre facture, cette dernière peut mettre dans un état incohérent notre facture. Par exemple:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;// nous construisons un objet valide,
// la cohérence des données est assuré par le constructeur
$facture = new Facture(new DateTime(&amp;#39;2020-01-01 00:00:00&amp;#39;), 10.0, 0.1, 11.0);

// nous modifions la TVA (= 20%)
$facture-&amp;gt;setTVA(0.2);

// notre objet est maintenant dans un état incohérent
// si nous le montant HT est de 10€, la TVA à 20%
// le montant TTC devrait être de 12€, or voici les
// valeurs portées par notre facture :
//
// class Facture#1 (3) {
//   private $date =&amp;gt; class DateTime#2 (3) {
//     public $date =&amp;gt; string(26) &amp;quot;2020-01-01 00:00:00.000000&amp;quot;
//     public $timezone_type =&amp;gt; int(3)
//     public $timezone =&amp;gt; string(13) &amp;quot;Europe/Berlin&amp;quot;
//  }
//   private $montantHT =&amp;gt; double(10)
//   private $tva =&amp;gt; double(0.2)
//   private $montantTTC =&amp;gt; double(11)
// }&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Comme nous l’avons déjà dit, &lt;em&gt;toute modification de notre objet doit le laisser dans un état cohérent&lt;/em&gt;. Cela signifie donc que s’il est effectivement possible de modifier la TVA de notre facture, le montant TTC doit également être mis à jour en conséquence pour que notre valide soit toujours valide.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;class Facture
{
    // ...

    public function setTVA(float $tva): void
    {
        $this-&amp;gt;tva = $tva;

        $this-&amp;gt;montantTTC = $this-&amp;gt;montantHT * (1 + $this-&amp;gt;tva);
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Imaginons maintenant que cette classe est issue d’une application en production et que l’on nous demande d’implémenter une nouvelle fonctionnalité: à partir du mois d’avril 2020, la TVA aura un taux constant de 30%. Lors du développement de cette fonctionnalité, il est primordial que le fonctionnement des factures antérieures à cette date ne soit pas modifié.&lt;/p&gt;

&lt;p&gt;Si l’on souhaite effectuer ce changement rapidement, en modifiant un minimum de code, une solution naïve pourrait alors être implémentée comme ceci :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;class Facture
{
    private DateTime $date;
    private float $montantHT;
    private float $tva;
    private float $montantTTC;

    public function __construct(DateTime $date, float $montantHT, float $tva, float $montantTTC)
    {
        if (($montantHT * (1 + $tva)) !== $montantTTC) {
            throw new DomainException();
        }

        $this-&amp;gt;date = $date;
        $this-&amp;gt;montantHT = $montantHT;
        $this-&amp;gt;tva = $tva;
        $this-&amp;gt;montantTTC = $montantTTC;
    }

    public function getDate(): DateTime
    {
        return $this-&amp;gt;date;
    }

    public function getMontantHT(): float
    {
        return $this-&amp;gt;montantHT;
    }

    public function getTVA(): float
    {
        if ($this-&amp;gt;date-&amp;gt;after(&amp;#39;2020-04-01&amp;#39;)) {
            return 0.3;
        }

        return $this-&amp;gt;tva;
    }

    public function getMontantTTC(): float
    {
        return $this-&amp;gt;montantTTC;
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Si le code permet effectivement de répondre à la demande qui a été formulée, ce dernier va poser de nombreux problèmes.&lt;/p&gt;

&lt;p&gt;Tout d’abord le code précédent &lt;em&gt;enfreint la première règle que nous avons évoquée sur le constructeur&lt;/em&gt;. Effectivement, dans le cas présent, rien ne nous empêcherait de créer l’objet facture suivant &lt;code&gt;$facture = new Facture(new DateTime(&apos;2020-04-02 18:00:00&apos;), 10.0, 0.1, 50.0)&lt;/code&gt;. Or, il y a ici une incohérence, puisqu’il ne devrait pas être possible de créer un tel objet, le taux de TVA devant être à 30% à partir du mois d’avril. Il est probable que les données renseignées proviennent d’une interface utilisateur ou d’une API, ce qui signifierait ici que l’utilisateur n’est en plus pas averti d’une erreur de saisie.&lt;/p&gt;

&lt;p&gt;Pire encore, si cette solution est une &lt;em&gt;source de confusion&lt;/em&gt; pour l’utilisateur final, le code est également une source d’erreurs pour les développeurs. En effet, lorsqu’on lit le nom de la méthode &lt;code&gt;getTVA&lt;/code&gt;, on est en droit de s’attendre à ce qu’elle renvoie la valeur de la propriété correspondante. Or ce ne serait pas le cas ici, puisque la donnée peut potentiellement être modifiée à la volée selon la valeur des autres propriétés de la facture.&lt;/p&gt;

&lt;p&gt;Voilà qui conclut cette introduction à la notion de cohérence dans un modèle objet. Si ce sujet vous intéresse et que vous souhaitez aller plus loin, je ne peux que vous encourager à fouiller du côté du &lt;em&gt;Domain Driven Design (DDD)&lt;/em&gt;. &lt;a href=&quot;https://matthiasnoback.nl &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Matthias NOBACK&lt;/a&gt; a également écrit &lt;a href=&quot;https://www.manning.com/books/object-design-style-guide &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;un livre&lt;/a&gt; où il présente des techniques pour écrire un code orienté objet de qualité et pérenne (où il est notamment question de cohérence de données) que je ne peux que vous recommander.&lt;/p&gt;
</description>
                    <pubDate>Sat, 04 Apr 2020 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2020/04/04/coherence-des-donnees-dans-un-modele-oriente-objet.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2020/04/04/coherence-des-donnees-dans-un-modele-oriente-objet.html</guid>
                </item>
            
        
            
                111
                <item>
                    <title>La confusion entre symptôme et problème</title>
                    <description>&lt;p&gt;Dans mon travail de développement logiciel, il m’arrive régulièrement de parler de causes et conséquences. Il n’est en effet pas rare, lors de la correction d’un bug, de résoudre la conséquence d’un problème sans pourtant autant en corriger la cause de ce dernier. Ce qui implique qu’en réalité l’anomalie est toujours présente dans l’application et resurgira tôt ou tard sous une autre forme.&lt;/p&gt;

&lt;p&gt;J’ai toujours souhaité écrire un billet à ce sujet, mais je n’ai jamais réussi à trouver les mots pour exprimer le message que je souhaitais faire passer. Je découvre aujourd’hui (merci &lt;a href=&quot;https://twitter.com/SergeMazille &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Serge&lt;/a&gt;) une vidéo sur le sujet que je souhaite donc vous partager :&lt;/p&gt;

&lt;div style=&quot;width: 100%; text-align: center;&quot;&gt;
    &lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/G9w0YpElDY4&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
</description>
                    <pubDate>Mon, 16 Mar 2020 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2020/03/16/la-confusion-entre-symptome-et-probleme.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2020/03/16/la-confusion-entre-symptome-et-probleme.html</guid>
                </item>
            
        
            
                112
                <item>
                    <title>Interroger un serveur DNS</title>
                    <description>&lt;p&gt;Le protocole DNS (&lt;em&gt;Domain Name System&lt;/em&gt;) est un protocole central du web. C’est ce dernier qui est chargé de communiquer l’adresse IP associée à un nom de domaine. Pour gérer au mieux la croissance de cet “annuaire”, le protocole s’appuie sur un système distribué. Comme Wikipédia le décrit assez simplement, &lt;a href=&quot;https://fr.wikipedia.org/wiki/Domain_Name_System#R%C3%A9solution_du_nom_par_un_h%C3%B4te &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;la résolution d’un nom de domaine&lt;/a&gt; peut nécessiter d’interroger plusieurs serveurs pour obtenir la réponse finale. De manière simplifiée, ce système implique donc une synchronisation de données entre les différents serveurs. C’est notamment pour cela que lorsque l’on déclare un domaine, ce dernier n’est pas instantanément disponible. C’est ce que l’on appelle &lt;strong&gt;la propagation des DNS&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Je travaille actuellement sur un projet qui met à jour un grand nombre de DNS de manière relativement régulière. La propagation des DNS pouvant mettre un certain temps à être pris en compte, je souhaite pouvoir interroger directement un serveur DNS afin de pouvoir savoir si la modification que j’ai effectuée à été propagée sur ce dernier.&lt;/p&gt;

&lt;p&gt;Pour cela, j’utilise la commande &lt;code&gt;nslookup&lt;/code&gt; qui me permet d’envoyer une requête sur le serveur DNS utilisé par ma machine:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;$ nslookup jdecool.fr

Server:		127.0.0.53
Address:	127.0.0.53#53

Non-authoritative answer:
Name:	jdecool.fr
Address: 51.15.134.254&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Mais je peux également spécifier à la commande, le serveur DNS que je souhaite requêter. Par exemple, les DNS de Google:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;$ nslookup jdecool.fr 8.8.8.8

Server:		8.8.8.8
Address:	8.8.8.8#53

Non-authoritative answer:
Name:	jdecool.fr
Address: 51.15.134.254&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;De cette manière, je peux cibler les principaux serveur DNS afin d’avoir un aperçu de la propagation des domaines qui m’intéressent.&lt;/p&gt;
</description>
                    <pubDate>Tue, 18 Feb 2020 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2020/02/18/interroger-un-serveur-dns.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2020/02/18/interroger-un-serveur-dns.html</guid>
                </item>
            
        
            
                113
                <item>
                    <title>La gestion des enums en PHP</title>
                    <description>&lt;p&gt;Un enum, on parle également de type énuméré ou énumération en français, “est un type de donnée qui consiste en un ensemble de valeurs constantes” (&lt;a href=&quot;https://fr.wikipedia.org/wiki/Type_%C3%A9num%C3%A9r%C3%A9 &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;source Wikipédia&lt;/a&gt;). Il s’agit d’une structure très pratique, mais qui n’existe malheureusement pas nativement dans PHP.&lt;/p&gt;

&lt;p&gt;Si l’on se réfère à la documentation officielle de PHP, il existe bien un type similaire au travers de la classe &lt;code&gt;SplEnum&lt;/code&gt;. Mais cette structure de données a le défaut de nécessiter l’installation d’une extension supplémentaire ce qui peut ne pas être une opération triviale pour une application de production si cela n’est pas prévu en avance.&lt;/p&gt;

&lt;p&gt;C’est pour cela qu’il existe de nombreux composants qui permettent de mettre en place un système qui se rapproche d’un &lt;em&gt;enum&lt;/em&gt;. Pour ma part, j’utilise régulièrement &lt;a href=&quot;https://github.com/myclabs/php-enum &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;myclabs/php-enum&lt;/code&gt;&lt;/a&gt;. Une bibliothèque qui m’a rendu de nombreux services à maintes reprises et qui fait très bien le travail.&lt;/p&gt;

&lt;p&gt;Elle a pourtant un défaut qui m’a posé un problème sur un projet récemment. La plupart des composants d’&lt;em&gt;enum&lt;/em&gt; en PHP fonctionnent suivant le même principe. Il s’agit d’une classe de base qui est étendue dans notre code et à laquelle on attache des constantes. Par exemple:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;class WeekDay extends Enum
{
    public const MONDAY = &amp;#39;monday&amp;#39;;
    public const TUEDAY = &amp;#39;tuedsay&amp;#39;;
    // ...
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Il est ensuite possible dans notre code, d’utiliser la notation &lt;code&gt;WeekDay::MONDAY()&lt;/code&gt; pour récupérer une instance de notre &lt;em&gt;enum&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Le problème que j’ai par exemple rencontré avec &lt;code&gt;myclabs/php-enum&lt;/code&gt; est que chaque appel à &lt;code&gt;WeekDay::MONDAY()&lt;/code&gt; retourne une nouvelle instance de classe. Cela signifie que lorsque l’on récupère deux fois le même &lt;em&gt;enum&lt;/em&gt;.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;WeekDay::MONDAY() == WeekDay::MONDAY() // true
WeekDay::MONDAY() === WeekDay::MONDAY() // false&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Cela peut-être problématique car dans mon cas, il m’arrive par exemple d’utiliser la classe &lt;a href=&quot;https://www.php.net/manual/en/class.splobjectstorage.php &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;SplObjectStorage&lt;/code&gt;&lt;/a&gt; pour stocker des associations de données.&lt;/p&gt;

&lt;p&gt;Par exemple, si je conçois une application qui permet à des utilisateurs d’indiquer leur humeur de la journée, je souhaite par exemple pouvoir associer un jour de la semaine (via notre &lt;em&gt;enum&lt;/em&gt; &lt;code&gt;WeekDay&lt;/code&gt;) avec une humeur (qui serait également géré au travers d’un &lt;em&gt;enum&lt;/em&gt; &lt;code&gt;Mood&lt;/code&gt;). Or si chaque élément de mon énumération est systématiquement une instance d’objet différente, il est alors impossible de travailler avec le code suivant:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;$userMood = new SplObjectStorage();
$userMood-&amp;gt;attach(WeekDay::MONDAY(), Mood::GOOD());

$userMood-&amp;gt;contains(WeekDay::MONDAY()); // false&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Pour remédier à cette problématique, j’ai découvert le composant &lt;a href=&quot;https://github.com/marc-mabe/php-enum &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;marc-mabe/php-enum&lt;/code&gt;&lt;/a&gt; qui permet également de gérer un système d’énumération. À la différence de &lt;code&gt;myclabs/php-enum&lt;/code&gt;, cette bibliothèque renverra toujours la même instance pour une même donnée de notre &lt;em&gt;enum&lt;/em&gt;. Ce qui en reprenant notre code précédent, donnerait la chose suivante:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;WeekDay::MONDAY() == WeekDay::MONDAY() // true
WeekDay::MONDAY() === WeekDay::MONDAY() // true&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;À ce stade, vous vous dites certainement que mon choix est maintenant fait et que dorénavant j’utiliserai de manière systématique &lt;code&gt;marc-mabe/php-enum&lt;/code&gt;. Pourtant tout n’est pas aussi simple. Si on a vu un avantage d’une des bibliothèques par rapport à l’autre, il y a une fonctionnalité de &lt;code&gt;myclabs/php-enum&lt;/code&gt; que j’affectionne tout particulièrement: le fait de pouvoir mettre les constantes en privées.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;class WeekDay extends Enum
{
    private const MONDAY = &amp;#39;monday&amp;#39;;
    private const TUEDAY = &amp;#39;tuedsay&amp;#39;;
    // ...
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;C’est pour moi un énorme avantage car lorsque j’utilise un &lt;em&gt;enum&lt;/em&gt;, je ne souhaite pas savoir comment il est construit à l’intérieur, comment il fonctionne. Je veux simplement l’utiliser. Or si les constantes sont publiques, cela signifie que la donnée est visible par le développeur et pire encore, qu’il est possible qu’il l’utilise en dehors de l’énumération.&lt;/p&gt;

&lt;p&gt;Comme toujours dans l’ingénierie logicielle, il vous faudra faire le bon choix selon votre problématique et le choix de votre implémentation technique.&lt;/p&gt;
</description>
                    <pubDate>Mon, 07 Oct 2019 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2019/10/07/la-gestion-des-enums-en-php.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2019/10/07/la-gestion-des-enums-en-php.html</guid>
                </item>
            
        
            
                114
                <item>
                    <title>Tout ce que vous devez savoir sur PHP 7.4</title>
                    <description>&lt;p&gt;La fin de l’année approche à grand et comme d’habitude, nous autres développeurs PHP auront sous le sapin une nouvelle version de PHP à notre disposition. La version 7.4 est une version que j’attends avec impatience notamment pour la possibilité de pouvoir typer les propriétés de classe. Ce changement s’inscrit dans la continuité du langage d’avoir un typage fort.&lt;/p&gt;

&lt;p&gt;Comme avec l’arrivée de chaque nouvelle version, les équipes de PHP ont publié &lt;a href=&quot;https://www.php.net/manual/en/migration74.php &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;un guide de migration de la version 7.3 vers la version 7.4&lt;/a&gt; incluant notamment les nouvelles évolutions du langage, les nouvelles classes, interfaces fonction et constante à disposition des développeurs, mais aussi les fonctionnalités dépréciées, les changements non rétro-compatibles ainsi que les extensions supprimées.&lt;/p&gt;

&lt;p&gt;Je vous conseille fortement d’y jeter un coup d’oeil, mais attention le guide est tout frais et n’est actuellement disponible qu’en anglais !&lt;/p&gt;
</description>
                    <pubDate>Thu, 03 Oct 2019 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2019/10/03/tout-ce-que-vous-devez-savoir-sur-php-74.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2019/10/03/tout-ce-que-vous-devez-savoir-sur-php-74.html</guid>
                </item>
            
        
            
                115
                <item>
                    <title>Mettre en place une Review App d&apos;application statique</title>
                    <description>&lt;p&gt;J’ai décidé d’écrire une série de quelques billets sur la gestion des &lt;em&gt;Review Apps&lt;/em&gt;. Ces dernières représentent aujourd’hui une partie du Saint Graal des équipes de développement et DevOps. Le concept de &lt;em&gt;Review Apps&lt;/em&gt; a été popularisé par Gitlab et consiste à déployer automatiquement du code applicatif depuis une branche de développement. Cela permet entre autres, de pouvoir tester une fonctionnalité avant d’intégrer le code de cette dernière dans une branche de production. La boucle de feedback est alors réduite à son maximum puisqu’il est possible de tester un développement pendant sa réalisation. Dans ce billet, nous allons nous intéresser au cas le plus simple de déploiement d’une &lt;em&gt;Review Apps&lt;/em&gt;, à savoir déployer une application web statique.&lt;/p&gt;

&lt;p&gt;Commençons tout d’abord par expliquer concrêtement ce qui va être mis en place. Lorsque nous écrivons du code, nous travaillons généralement sur une branche (d’un système de versionning) dédiée. Une fois notre développement terminé, le code est revu, validé puis intégré dans une branche de production commune à toute l’équipe. L’objectif de la mise en place de notre &lt;em&gt;Review App&lt;/em&gt; sera de créer un environnement fonctionnel de notre application qui sera rattaché à la branche de travail du développeur. Cela va se traduire par un déploiement de la branche qui sera accessible via une adresse de type &lt;code&gt;application.[nom-de-la-branche].domaine.tld&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Avant toute chose, je vais partir du principe que nous avons déjà une machine en place, configurée et opérationnelle avec un serveur HTTP installé. Dans ce billet, j’utiliserai &lt;a href=&quot;https://www.nginx.com &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;NGINX&lt;/a&gt;, mais le principe est transposable sur n’importe quel autre serveur. Je suppose également que les DNS de votre domaine est également déjà configuré de manière à ce que les requêtes vers &lt;code&gt;application.*.domaine.tld&lt;/code&gt; soit redirigé vers le serveur HTTP qui va accueillir notre &lt;em&gt;Review App&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Maintenant que ces prérequis sont en place, attaquons le vif du sujet. Nous allons tout d’abird configurer le serveur HTTP pour que les requêtes de type &lt;code&gt;application.[nom-de-la-branche].domaine.tld&lt;/code&gt; accède aux sources présentes dans le dossier suivant le motif &lt;code&gt;/var/www/application/[nom-de-la-branche]&lt;/code&gt;. Avec NGINX, cela se fait relativement simplement en configurant la directive &lt;code&gt;server_name&lt;/code&gt; au travers d’une expression régulière:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-nginx&quot; data-lang=&quot;nginx&quot;&gt;server {
    listen 80;

    server_name ~^application.(?&amp;lt;sname&amp;gt;.+?).domaine.tld$;
    root /var/www/application/$sname;

    index index.html;
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Une fois la configuration du serveur HTTP réalisé, nous allons nous occuper du déploiement de l’application. Cette dernière étant statique (c’est par exemple le cas de ce blog qui est généré par &lt;a href=&quot;https://jekyllrb.com &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Jekyll&lt;/a&gt;), nous pouvons imaginer d’utiliser une commande telle que &lt;code&gt;rsync&lt;/code&gt; pour copier les fichiers source dans le répertoire qui doit accueillir notre &lt;em&gt;Review App&lt;/em&gt;. Cette commande serait idéalement lancer par votre CI (si vous en avez une), par un webhook configuré au travers de votre gestionnaire de code source ou (mais je ne vous le souhaite pas) à la main.&lt;/p&gt;

&lt;p&gt;Pour ma part, j’utilise un script &lt;code&gt;Bash&lt;/code&gt; prenant 2 paramètres en entrée: l’adresse du serveur sur lequel je souhaite déployer mon code et le dossier de destination. Le script s’assure alors que le dossier de destination existe, en le créant à défaut et effectue ensuite la copie des fichiers. Une version simplifiée de ce dernier pourrait ressembler à:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;#!/bin/bash

HOST=$1
DIR=$2

ssh user@$HOST &amp;quot;mkdir -p $DIR&amp;quot;
rsync -azv dist/ user@$HOST:$DIR&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;La gestion de notre &lt;em&gt;Review App&lt;/em&gt; est presque terminée. Il ne faudra pas oublier de gérer le cycle de vie de cette dernière, en synchronisant les fichiers lors de la mise à jour de la branche et de supprimer le dossier de travail lors de la suppression de la branche (ou de la validation de la &lt;em&gt;merge request&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;Et voilà, ce n’est pas plus compliqué que cela ! Si le terme de &lt;em&gt;Review Apps&lt;/em&gt; peut faire peur et sembler complexe, il est possible de mettre en place des solutions simples, rapides et efficaces. Nous verrons dans un prochain billet comment mettre en place ce système avec une application nécessitant une base de données.&lt;/p&gt;
</description>
                    <pubDate>Fri, 23 Aug 2019 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2019/08/23/mettre-en-place-review-app-application-statique.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2019/08/23/mettre-en-place-review-app-application-statique.html</guid>
                </item>
            
        
            
                116
                <item>
                    <title>Vous êtes lead dev ? Et maintenant ?</title>
                    <description>&lt;p&gt;Il y a quelques mois, j’écrivais un article sur &lt;a href=&quot;/blog/2018/12/10/le-role-de-lead-dev.html&quot;&gt;le rôle de lead developer&lt;/a&gt; afin de donner ma vision sur le travail qui en découle. Je découvre aujourd’hui une présentation de &lt;a href=&quot;https://twitter.com/eryno &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Eryn O’Neil&lt;/a&gt; sur le sujet.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The tech lead is the &lt;em&gt;owner of the technological vision&lt;/em&gt; for a project, and the &lt;em&gt;technical leader&lt;/em&gt; ot the project team&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Lorsque l’on devient &lt;em&gt;lead tech&lt;/em&gt; ou &lt;em&gt;tech lead&lt;/em&gt;, nous avons alors plus de responsabilité que de simplement “coder”. Dans ce cas, il faut faire encore plus attention à la conception et au respect de l’architecture logicielle que l’on souhaite mettre en place. Avec le rôle de lead dev peut également venir des responsabilités supplémentaires comme avoir une vision technique globale du projet, une gestion humaine pour motiver et former l’équipe avec laquelle on est amené à travailler et bien d’autres choses.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;It’s fine to admit not knowing something, but never ever as an excuse. “I don’t know”, should burst joyfully from your lips, followed by “but I will find out!”.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Là encore un élément-clé pour moi dans ce rôle. On ne peut pas tout savoir sur tout, mais cela ne doit pas vous empêcher de prendre la ou les bonnes décisions. Trop souvent, les entreprises et les personnes qui la composent n’ose pas s’aventurer en dehors de ce qu’il ne connaisse pas et/ou n’ont jamais pratiqué.&lt;/p&gt;

&lt;p&gt;Je ne sais pas pour vous, mais je ne compte plus les fois ou une personne à refuser de faire une tâche parce que cela la faisait sortir de sa zone de confort. Et quand je parle de zone de confort, cela peut très bien être quelque chose comme écrire un bout de code en utilisant un pattern non connu.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Donc si vous vous reconnaissez dans ces quelques lignes, que vous souhaitez en savoir plus ou tout simplement que vous vous intéressez au poste de &lt;em&gt;lead&lt;/em&gt;, je vous commande vivement de regarder la présentation &lt;a href=&quot;https://www.youtube.com/watch?v=FcyD85z3JSI &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Congrats! You’re the tech lead - now what?&lt;/a&gt; présentait lors de la conférence &lt;a href=&quot;https://theleaddeveloper.com &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;The Lead Developer&lt;/a&gt; à New York en 2017.&lt;/p&gt;

&lt;div style=&quot;width: 100%; text-align: center&quot;&gt;
&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube-nocookie.com/embed/FcyD85z3JSI&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
</description>
                    <pubDate>Tue, 13 Aug 2019 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2019/08/13/vous-etes-lead-dev-et-maintenant.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2019/08/13/vous-etes-lead-dev-et-maintenant.html</guid>
                </item>
            
        
            
                117
                <item>
                    <title>Mettre à jour automatiquement une description sur le Docker Hub</title>
                    <description>&lt;p&gt;Le &lt;a href=&quot;https://hub.docker.com &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Docker Hub&lt;/a&gt; c’est le registre officiel d’image Docker. L’annuaire où toutes les personnes qui créent et maintiennent des images Docker peuvent les mettre à la disposition de tous. Pour avoir le plus de succès possible, il est alors important de bien remplir la description de son image, en expliquant ce qu’elle contient, dans quel objectif a-t-elle était créée et surtout comment l’utiliser !&lt;/p&gt;

&lt;p&gt;Pour les images évoluant de manière régulière, il peut être fastidieux de maintenir cette description à jour. Cette dernière étant généralement versionnée dans un dépôt avec les fichiers &lt;code&gt;Dockerfile&lt;/code&gt;. C’est un cas que je rencontre avec les &lt;a href=&quot;https://hub.docker.com/r/phpdaily/php &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;em&gt;nightly builds&lt;/em&gt; des versions en développement de PHP&lt;/a&gt; où je dois maintenir à la fois le README du projet et la description sur le Docker Hub.&lt;/p&gt;

&lt;p&gt;Pour simplifier ce travail et éviter la double intervention, j’ai récemment découvert les possibilités offertes par l’API de Docker Hub. Il existe ainsi une API permettant de mettre à jour les informations d’une image disponible dans l’annuaire. J’ai donc créé un script Shell qui lit le fichier &lt;code&gt;README&lt;/code&gt; du dépôt et qui met à jour la description de l’image directement sur le Docker Hub.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;#!/bin/bash

set -e

TOKEN=$(curl -s -X POST \
    -H &amp;quot;Content-Type: application/json&amp;quot; \
    -d &amp;#39;{&amp;quot;username&amp;quot;: &amp;quot;&amp;#39;&amp;quot;$DOCKER_USERNAME&amp;quot;&amp;#39;&amp;quot;, &amp;quot;password&amp;quot;: &amp;quot;&amp;#39;&amp;quot;$DOCKER_PASSWORD&amp;quot;&amp;#39;&amp;quot;}&amp;#39; \
    https://hub.docker.com/v2/users/login/ | jq -r .token)

CODE=$(jq -n --arg msg &amp;quot;$(&amp;lt;README.md)&amp;quot; \
    &amp;#39;{&amp;quot;registry&amp;quot;:&amp;quot;registry-1.docker.io&amp;quot;,&amp;quot;full_description&amp;quot;: $msg }&amp;#39; | \
        curl -s -o /dev/null -L -w &amp;quot;%{http_code}&amp;quot; \
           https://cloud.docker.com/v2/repositories/&amp;quot;${DOCKER_IMAGE}&amp;quot;/ \
           -d @- -X PATCH \
           -H &amp;quot;Content-Type: application/json&amp;quot; \
           -H &amp;quot;Authorization: JWT ${TOKEN}&amp;quot;)

if [[ &amp;quot;${CODE}&amp;quot; = &amp;quot;200&amp;quot; ]]; then
    printf &amp;quot;Successfully pushed README to Docker Hub\n&amp;quot;
else
    printf &amp;quot;Unable to push README to Docker Hub, response code: %s\n&amp;quot; &amp;quot;${CODE}&amp;quot;
    exit 1
fi&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Ce dernier s’utilise comme ceci:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;DOCKER_USERNAME=username \
  DOCKER_PASSWORD=password \
  DOCKER_IMAGE=&amp;quot;user/image&amp;quot; \
  ./script.sh&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Et voilà le tour joué !&lt;/p&gt;

&lt;p&gt;Et pour rendre à Caesar ce qui appartient à Caesar, je n’ai aucun mérite. C’est en réalisant quelques recherches sur Internet que je suis tombé sur &lt;a href=&quot;javascript:;&quot; class=&quot;broken-link&quot; rel=&quot;nofollow&quot; data-original-url=&quot;https://github.com/moikot/golang-dep/blob/master/.travis/push.sh &quot;&gt;un projet qui avait la même problématique&lt;/a&gt; et que j’ai adapté le code à ce que je voulais en faire.&lt;/p&gt;
</description>
                    <pubDate>Sun, 28 Jul 2019 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2019/07/28/mettre-a-jour-automatiquement-une-description-docker-hub.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2019/07/28/mettre-a-jour-automatiquement-une-description-docker-hub.html</guid>
                </item>
            
        
            
                118
                <item>
                    <title>Déployer un projet PHP depuis un monorepo</title>
                    <description>&lt;p&gt;Je parlais dans un billet précédent de comment &lt;a href=&quot;/blog/2019/05/18/publier-des-dependances-php-sur-packagist-dans-un-projet-monorepo.html&quot;&gt;publier des composants PHP sur Packagist depuis un dépôt de code monolithique&lt;/a&gt;. Une autre question récurrente venant des équipes projet qui souhaitent mettre en place ce type de structure est: comment déployer un sous-projet du dépôt de manière indépendante ?&lt;/p&gt;

&lt;p&gt;En effet, il y a quelques années un formidable outil, &lt;a href=&quot;https://capistranorb.com &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Capistrano&lt;/a&gt;, a modifié la manière dont nous déployons nos applications. Capistrano est un outil Open Source développé en Ruby qui permet d’automatiser le processus de création et de publication d’une application sur un ou plusieurs serveurs Web (source &lt;a href=&quot;https://fr.wikipedia.org/wiki/Capistrano_(logiciel) &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Wikipédia&lt;/a&gt;). L’outil a eu un tel succès que différentes alternatives basées sur le même principe sont apparues. On peut par exemple citer &lt;a href=&quot;https://deployer.org &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Deployer&lt;/a&gt; en PHP ou &lt;a href=&quot;https://ansistrano.com &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Ansistrano&lt;/a&gt; une solution équivalente fonctionnant avec Ansible.&lt;/p&gt;

&lt;p&gt;Nous avons donc avec ces solutions pris l’habitude de déployer nos applications en clonant directement le dépôt de sources (que ce soit Git ou Subversion auparavant) afin de déployer le tag ou la branche de production de nos projets. Or lorsque l’on souhaite opter pour une approche &lt;em&gt;monorepo&lt;/em&gt;, cela signifie que notre déploiement va cloner tous les projets composant notre dépôt de code. Bien que cela puisse être envisagé dans certains cas, c’est loin d’être une solution optimale (notamment quand vous allez vouloir mettre vos applications sur différents serveurs).&lt;/p&gt;

&lt;p&gt;Cloner un dépôt de source n’est qu’une solution parmi tant d’autres pour déployer nos projets. C’est également omettre que les outils qui font ce travail pour nous sont un peu plus intelligents que cela et offrent de nombreuses autres possibilités. En plus de pouvoir cloner un dépôt Git, les solutions d’automatisations sont bien souvent capables de copier des fichiers localement sur un système de fichiers, par FTP ou encore via SSH. C’est donc une stratégie de copie qui peut ne pas remettre en cause votre processus de déploiement ni même la configuration (ou très peu) de ce dernier.&lt;/p&gt;

&lt;p&gt;Prenons par exemple une configuration de base générée par Deployer (le principe sera le même pour l’ensemble des outils, mais la syntaxe de configuration variera):&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&amp;lt;?php

namespace Deployer;

require &amp;#39;recipe/common.php&amp;#39;;

set(&amp;#39;application&amp;#39;, &amp;#39;my_project&amp;#39;);
set(&amp;#39;repository&amp;#39;, &amp;#39;https://github.com/foo/bar.git&amp;#39;);

set(&amp;#39;git_tty&amp;#39;, true);

set(&amp;#39;shared_files&amp;#39;, [&amp;#39;shared-files.php&amp;#39;]);
set(&amp;#39;shared_dirs&amp;#39;, [&amp;#39;var/log&amp;#39;]);
set(&amp;#39;writable_dirs&amp;#39;, [&amp;#39;data/uploads&amp;#39;]);

host(&amp;#39;project.com&amp;#39;)
    -&amp;gt;set(&amp;#39;deploy_path&amp;#39;, &amp;#39;~/&amp;#39;);

desc(&amp;#39;Deploy your project&amp;#39;);
task(&amp;#39;deploy&amp;#39;, [
    &amp;#39;deploy:info&amp;#39;,
    &amp;#39;deploy:prepare&amp;#39;,
    &amp;#39;deploy:lock&amp;#39;,
    &amp;#39;deploy:release&amp;#39;,
    &amp;#39;deploy:update_code&amp;#39;,
    &amp;#39;deploy:shared&amp;#39;,
    &amp;#39;deploy:writable&amp;#39;,
    &amp;#39;deploy:vendors&amp;#39;,
    &amp;#39;deploy:clear_paths&amp;#39;,
    &amp;#39;deploy:symlink&amp;#39;,
    &amp;#39;deploy:unlock&amp;#39;,
    &amp;#39;cleanup&amp;#39;,
    &amp;#39;success&amp;#39;
]);

after(&amp;#39;deploy:failed&amp;#39;, &amp;#39;deploy:unlock&amp;#39;);&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Comme je l’ai évoqué juste avant, par défaut la configuration se base sur un dépôt Git pour déployer (ici &lt;code&gt;https://github.com/foo/bar.git&lt;/code&gt;). Lors du processus de déploiement l’ensemble des tâches &lt;code&gt;deploy&lt;/code&gt; seront exécutées. C’est au niveau de la tâche &lt;code&gt;deploy:update_code&lt;/code&gt; que tout se joue. C’est cette dernière qui va mettre à jour le code sur notre serveur &lt;code&gt;project.com&lt;/code&gt; en clonant le dépôt spécifié dans la configuration &lt;code&gt;repository&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Nous devons donc modifier cette tâche afin de déployer non plus via Git, mais en effectuant une copie de fichier en SSH (via &lt;code&gt;rsync&lt;/code&gt; dans notre cas). Pour cela, nous allons installer les recettes additionnelles de Deployer via Composer : &lt;code&gt;composer require --dev deployer/recipes&lt;/code&gt;. Nous pourront ensuite ajouter la configuration nécessaire au fonctionnement de cette dernière:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&amp;lt;?php

namespace Deployer;

require &amp;#39;recipe/common.php&amp;#39;;
require &amp;#39;recipe/rsync.php&amp;#39;; // inclusion de la recette

set(&amp;#39;application&amp;#39;, &amp;#39;my_project&amp;#39;);
set(&amp;#39;repository&amp;#39;, &amp;#39;https://github.com/foo/bar.git&amp;#39;);

set(&amp;#39;git_tty&amp;#39;, true);

set(&amp;#39;shared_files&amp;#39;, [&amp;#39;shared-files.php&amp;#39;]);
set(&amp;#39;shared_dirs&amp;#39;, [&amp;#39;var/log&amp;#39;]);
set(&amp;#39;writable_dirs&amp;#39;, [&amp;#39;data/uploads&amp;#39;]);

// configuration de rsync
set(&amp;#39;rsync&amp;#39;,[
    &amp;#39;exclude&amp;#39;      =&amp;gt; [
        &amp;#39;.git&amp;#39;,
        &amp;#39;var&amp;#39;,
        &amp;#39;vendor&amp;#39;,
        &amp;#39;deploy.php&amp;#39;,
    ],
    &amp;#39;exclude-file&amp;#39; =&amp;gt; false,
    &amp;#39;include&amp;#39;      =&amp;gt; [],
    &amp;#39;include-file&amp;#39; =&amp;gt; false,
    &amp;#39;filter&amp;#39;       =&amp;gt; [],
    &amp;#39;filter-file&amp;#39;  =&amp;gt; false,
    &amp;#39;filter-perdir&amp;#39;=&amp;gt; false,
    &amp;#39;flags&amp;#39;        =&amp;gt; &amp;#39;rzcE&amp;#39;,
    &amp;#39;options&amp;#39;      =&amp;gt; [&amp;#39;delete&amp;#39;],
    &amp;#39;timeout&amp;#39;      =&amp;gt; 60,
]);

// configuration du répertoire devant être copié et sa destination
set(&amp;#39;rsync_src&amp;#39;, __DIR__);
set(&amp;#39;rsync_dest&amp;#39;, &amp;#39;&amp;#39;);

host(&amp;#39;project.com&amp;#39;)
    -&amp;gt;set(&amp;#39;deploy_path&amp;#39;, &amp;#39;~/&amp;#39;);

desc(&amp;#39;Deploy your project&amp;#39;);
task(&amp;#39;deploy&amp;#39;, [
    &amp;#39;deploy:info&amp;#39;,
    &amp;#39;deploy:prepare&amp;#39;,
    &amp;#39;deploy:lock&amp;#39;,
    &amp;#39;deploy:release&amp;#39;,
    &amp;#39;rsync&amp;#39;, // pour finir nous remplaçons la tâche de déploiement Git
    &amp;#39;deploy:shared&amp;#39;,
    &amp;#39;deploy:writable&amp;#39;,
    &amp;#39;deploy:vendors&amp;#39;,
    &amp;#39;deploy:clear_paths&amp;#39;,
    &amp;#39;deploy:symlink&amp;#39;,
    &amp;#39;deploy:unlock&amp;#39;,
    &amp;#39;cleanup&amp;#39;,
    &amp;#39;success&amp;#39;
]);

after(&amp;#39;deploy:failed&amp;#39;, &amp;#39;deploy:unlock&amp;#39;);&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Et voilà ! Nous avons maintenant notre projet dont le fonctionnement est identique à la première version mis à part que la copie des fichiers ne se fait plus via un clone du dépôt Git, mais via une copie de fichier en SSH.&lt;/p&gt;

&lt;p&gt;Il se peut que vous ne souhaitiez pas modifier ce qui est fait par la tâche &lt;code&gt;deploy&lt;/code&gt;. C’est le cas, si par exemple vous déployez un projet Symfony en utilisant la recette fournie par Deployer. Dans ce cas, remplacer la tâche &lt;code&gt;update_code&lt;/code&gt; par &lt;code&gt;rsync&lt;/code&gt; vous obligerez à redéfinir l’ensemble des opérations de déploiement, ce qui peut-être gênant.&lt;/p&gt;

&lt;p&gt;Il est alors préférable de redéfinir uniquement le fonctionnement de la tâche &lt;code&gt;update_code&lt;/code&gt;.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&amp;lt;?php

namespace Deployer;

require &amp;#39;recipe/symfony4.php&amp;#39;;
require &amp;#39;recipe/rsync.php&amp;#39;;

set(&amp;#39;application&amp;#39;, &amp;#39;my_symfony_project&amp;#39;);

add(&amp;#39;shared_files&amp;#39;, [&amp;#39;.env&amp;#39;]);
add(&amp;#39;shared_dirs&amp;#39;, [&amp;#39;var/log&amp;#39;]);
add(&amp;#39;writable_dirs&amp;#39;, [&amp;#39;var&amp;#39;]);

set(&amp;#39;rsync&amp;#39;,[
    /* configuration rsync */
]);

set(&amp;#39;rsync_src&amp;#39;, __DIR__);
set(&amp;#39;rsync_dest&amp;#39;, &amp;#39;&amp;#39;);

// surchage de la tâche `deploy:update_code`
task(&amp;#39;deploy:update_code&amp;#39;, function() {
    invoke(&amp;#39;rsync&amp;#39;); // appel de la tâche `rsync`
});

host(&amp;#39;symfony-project.com&amp;#39;)
    -&amp;gt;set(&amp;#39;deploy_path&amp;#39;, &amp;#39;~/&amp;#39;);&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Avec ce genre de configuration, vous avez donc la possibilité de déployer les projets présents dans votre &lt;em&gt;monorepo&lt;/em&gt; de manière complètement indépendante.&lt;/p&gt;

&lt;p&gt;J’ai dans ce billet, principalement évoqué comment configurer Deployer, mais comme je l’ai précisé au début de ce billet, les autres outils peuvent fonctionner avec le même principe. Par exemple, si vous utilisez &lt;code&gt;Ansistrano&lt;/code&gt;, vous devrez rajouter les configurations ci-dessous à votre playbook Ansible pour effectuer une copie de fichier &lt;code&gt;rsync&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;vars:
    # ...
    ansistrano_deploy_via: &amp;quot;rsync&amp;quot;
    ansistrano_rsync_extra_params: &amp;quot;&amp;quot;
    ansistrano_rsync_set_remote_user: yes
    ansistrano_rsync_path: &amp;quot;&amp;quot;
    ansistrano_rsync_use_ssh_args: no&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

</description>
                    <pubDate>Sun, 19 May 2019 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2019/05/19/deployer-un-projet-php-depuis-un-monorepo.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2019/05/19/deployer-un-projet-php-depuis-un-monorepo.html</guid>
                </item>
            
        
            
                119
                <item>
                    <title>Publier des dépendances PHP sur Packagist dans un projet monorepo</title>
                    <description>&lt;p&gt;Les dépôts monolithiques (on parle également de dépôt &lt;em&gt;monorepo&lt;/em&gt; ou &lt;em&gt;monorepository&lt;/em&gt;) consistent tout simplement à avoir un dépôt de code unique regroupant plusieurs projets. Cela peut être des &lt;em&gt;applications distinctes&lt;/em&gt; (dans le cas de microservices), des composants d’un même projet (une API, avec son interface et éventuellement des bibliothèques de code autonomes) ou tout le code appartenant à une société.&lt;/p&gt;

&lt;p&gt;À la différence d’un projet de type monolithe, chaque projet gère de manière indépendante ses propres dépendances. Cela permet d’avoir une gestion plus fine des dépendances de chaque projet, mais également de pouvoir utiliser des technologies différentes si le besoin le nécessite. On peut par exemple imaginer: une UI de type SPA développée en Javascript fonctionnant avec un backend composé d’une API PHP et d’un service de gestion des utilisateurs développé en Go. Tout cela centralisé au sein d’un même dépôt.&lt;/p&gt;

&lt;p&gt;Je passerai sur les avantages et inconvénients de ce type de gestion du code. Si vous n’êtes pas à l’aise avec le sujet, je vous laisserai vous faire votre propre opinion en parcourant &lt;a href=&quot;https://github.com/korfuri/awesome-monorepo &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;cette liste de ressources&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Si le sujet n’a rien de nouveau, je ne l’ai vu que très rarement utilisé. Je connais très peu de sociétés françaises utilisant ce modèle (je peux les compter sur les doigts de la main). S’il ne s’agit aucunement d’une &lt;em&gt;silver bullet&lt;/em&gt;, je suis convaincu que c’est une méthode efficace pour travailler sur certains projets. Par exemple, je travaille actuellement sur une plateforme orientée microservices (une dizaine), utilisée par 3 fronts et également composée de quelques bibliothèques de code indépendantes. Cette plateforme est gérée par une seule et même équipe, ce qui en fait un excellent candidat. Pourtant, nous gérons actuellement chaque brique au sein de son propre dépôt de code.&lt;/p&gt;

&lt;p&gt;Mais nous ne sommes pas là pour débattre autour de ce point, mais plutôt pour évoquer comment gérer les dépendances &lt;a href=&quot;https://getcomposer.org &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Composer&lt;/a&gt; dans un dépôt de code monolithique. C’est un sujet que j’ai déjà abordé il y a quelques années dans &lt;a href=&quot;/blog/2017/06/26/gerer-les-dependances-composer-dans-un-projet-monorepo.html&quot;&gt;un article de ce blog&lt;/a&gt;. Mais comment faire lorsque l’on souhaite partager une dépendance Composer sur &lt;a href=&quot;https://packagist.org/ &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Packagist&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Vous ne le savez peut-être pas, mais pour déclarer un composant PHP comme une bibliothèque de code pouvant être utilisée via Composer, il est nécessaire que votre code PHP soit présent dans un dépôt de code indépendant avec un fichier &lt;code&gt;composer.json&lt;/code&gt; à la racine. Ce mode de fonctionnement n’est donc pas compatible avec un dépôt monolithique.&lt;/p&gt;

&lt;p&gt;Pour obtenir ce dépôt de code indépendant, il est bien entendu hors de question de copier à la main les changements. Mais quelles solutions s’offrent à nous dans ce cas ?&lt;/p&gt;

&lt;p&gt;La première solution (en partant du principe que vous gérez votre code avec &lt;a href=&quot;https://git-scm.com &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Git&lt;/a&gt;) est d’utiliser les &lt;a href=&quot;https://git-scm.com/book/fr/v1/Utilitaires-Git-Fusion-de-sous-arborescences &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;em&gt;subtrees&lt;/em&gt;&lt;/a&gt;. Ces derniers vont permettre d’extraire une sous-arborescence de votre dépôt afin de le pousser code dans un autre dépôt.&lt;/p&gt;

&lt;p&gt;Supposons que notre dossier contienne l’arborescence ci-dessous :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;.git
/api
/backend
/ui
/packages
    /package-1
    /package-2&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Nous souhaitons extraire le dossier &lt;code&gt;/packages/package-2&lt;/code&gt; dans son propre dépôt afin de publier ce dernier sur Packagist. Pour cela, nous allons commencer par créer le dépôt de code qui accueillera le projet:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;$ cd &amp;lt;chemin-nouveau-depot&amp;gt;
$ git init --bare&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Nous allons ensuite créer notre sous-arbre Git sur le dépôt original et indiquer un nom de branche :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;$ git subtree split --prefix=packages/package-2 -b split&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Il ne nous reste plus qu’à pousser les modifications dans le dépôt du composant :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;$ git push &amp;lt;chemin-nouveau-depot&amp;gt; split:master&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Vous pouvez ensuite pousser le dépôt de code indépendant là où vous le souhaitez et déclarer ce dernier dans Package comme n’importe quel autre dépôt.&lt;/p&gt;

&lt;p&gt;Par la suite, vous n’aurez plus qu’à répéter les étapes 2 et 3 afin de mettre à jour le dépôt du composant avec les modifications du dépôt principal. Pour simplifier et automatiser ces mises à jour, je vous recommande vivement d’ajouter ces opérations dans votre intégration continue.&lt;/p&gt;

&lt;p&gt;L’utilisation de &lt;code&gt;git subtree split&lt;/code&gt; n’est en soi pas compliquée, mais elle est un peu verbeuse et nécessite de jongler entre différents dépôts de code. Il existe des outils pour simplifier cette opération.&lt;/p&gt;

&lt;p&gt;Le premier outil auquel je pense (même si je ne l’ai jamais utilisé) est &lt;a href=&quot;https://github.com/splitsh/lite &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;splitsh&lt;/code&gt;&lt;/a&gt;. Il s’agit de l’outil utilisé pour découper le projet monolithique de &lt;a href=&quot;https://github.com/symfony/symfony &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Symfony&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Le second outil est un composant PHP développé par &lt;a href=&quot;https://twitter.com/votrubaT &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Tomáš Votruba&lt;/a&gt;, membre reconnu de la communauté PHP. Tomáš a développé un composant qu’il a nommé &lt;a href=&quot;https://github.com/Symplify/MonorepoBuilder &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;MonorepoBuilder&lt;/a&gt; et qui contient tout un ensemble d’outils pour aider les développeurs à gérer un &lt;em&gt;monorepo&lt;/em&gt;. Ce qui nous intéresse particulièrement est la commande de &lt;code&gt;split&lt;/code&gt;. Pour l’utiliser, vous devrez récupérer la dépendance via Composer au travers de la commande &lt;code&gt;composer require symplify/monorepo-builder --dev&lt;/code&gt;. Il faudra ensuite écrire un fichier de configuration &lt;code&gt;monorepo-builder.yml&lt;/code&gt; qui permettra de configurer quel dossier doit être extrait vers quel dépôt.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;# monorepo-builder.yml
parameters:
    directories_to_repositories:
        packages/package-1: &amp;#39;git@github.com:Foo/Package1.git&amp;#39;
        packages/package-2: &amp;#39;git@github.com:Foo/Package2.git&amp;#39;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Vous n’avez ensuite plus qu’à lancer l’opération via la commande &lt;code&gt;vendor/bin/monorepo-builder split&lt;/code&gt; pour extraire le dossier dans son propre dépôt.&lt;/p&gt;
</description>
                    <pubDate>Sat, 18 May 2019 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2019/05/18/publier-des-dependances-php-sur-packagist-dans-un-projet-monorepo.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2019/05/18/publier-des-dependances-php-sur-packagist-dans-un-projet-monorepo.html</guid>
                </item>
            
        
            
                120
                <item>
                    <title>Tester une connexion SMTP avec SwiftMailer</title>
                    <description>&lt;p&gt;J’ai pour habitude de créer une page de statut dans les applications que je développe afin de tester que l’ensemble des services nécessaires au bon fonctionnement de cette dernière (base de données, serveur mail, API…) sont lancés et correctement configurés. Nous allons voir dans cet article comment tester une connexion SMTP au sein d’une application utilisant le composant &lt;a href=&quot;https://swiftmailer.symfony.com &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;SwiftMailer&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Si l’on prend le cas d’usage simple proposé en introduction dans la documentation du composant :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;// Create the Transport
$transport = (new Swift_SmtpTransport(&amp;#39;smtp.example.org&amp;#39;, 25))
  -&amp;gt;setUsername(&amp;#39;your username&amp;#39;)
  -&amp;gt;setPassword(&amp;#39;your password&amp;#39;)
;

// Create the Mailer using your created Transport
$mailer = new Swift_Mailer($transport);

// Create a message
$message = (new Swift_Message(&amp;#39;Wonderful Subject&amp;#39;))
  -&amp;gt;setFrom([&amp;#39;john@doe.com&amp;#39; =&amp;gt; &amp;#39;John Doe&amp;#39;])
  -&amp;gt;setTo([&amp;#39;receiver@domain.org&amp;#39;, &amp;#39;other@domain.org&amp;#39; =&amp;gt; &amp;#39;A name&amp;#39;])
  -&amp;gt;setBody(&amp;#39;Here is the message itself&amp;#39;)
  ;

// Send the message
$result = $mailer-&amp;gt;send($message);&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Le principe pour une connexion est simple, il faut tester que la couche &lt;em&gt;transport&lt;/em&gt; puisse se connecter au service. Pour cela, il faut démarrer la connexion vers le serveur SMTP et vérifier si une erreur s’est produite :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;try {
    $transport-&amp;gt;start();
    $isSmtpOk = true;
} catch (Swift_TransportException $e) {
    $isSmtpOk = false;
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;L’exemple ci-dessus marche bien, mais dans la plupart des cas, nous envoyons rarement directement un mail. Dans une grande majorité des cas, nous allons utiliser un &lt;em&gt;spool&lt;/em&gt; de mail qui va stocker les mails que l’on souhaite envoyer pour par la suite effectuer un traitement en lot. C’est par exemple, le fonctionnement par défaut de &lt;em&gt;SwiftMailer&lt;/em&gt; dans Symfony.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;$spool = new FileSpool(__DIR__.&amp;#39;/var/spool&amp;#39;);
$transport = new Swift_SpoolTransport($spool);
$mailer = new Swift_Mailer($transport);&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Or dans ce cas, on ne peut pas réellement tester la couche de transport directement car notre instance de la classe &lt;code&gt;Swift_SpoolTransport&lt;/code&gt; ne gère pas réellement l’envoi des mails, mais gère la logique permettant de traiter le mail plus tard. De ce fait l’ &lt;a href=&quot;https://github.com/swiftmailer/swiftmailer/blob/master/lib/classes/Swift/Transport/SpoolTransport.php &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;implémentation de la méthode &lt;code&gt;$transport-&amp;gt;start()&lt;/code&gt;&lt;/a&gt; est vide et ne fait donc aucune action.&lt;/p&gt;

&lt;p&gt;Il faudra donc dans ce cas, tester la méthode &lt;code&gt;start&lt;/code&gt; directement sur l’instance du spool, à savoir sur notre objet &lt;code&gt;$spool&lt;/code&gt;. Et si vous utilisez &lt;a href=&quot;https://symfony.com &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Symfony&lt;/a&gt;, le framework configure un service &lt;code&gt;swiftmailer.transport.real&lt;/code&gt; permettant d’accéder à l’instance de l’objet qui gérera réellement l’envoi des mails.&lt;/p&gt;
</description>
                    <pubDate>Mon, 13 May 2019 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2019/05/13/tester-une-connexion-smtp-avec-swiftmailer.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2019/05/13/tester-une-connexion-smtp-avec-swiftmailer.html</guid>
                </item>
            
        
            
                121
                <item>
                    <title>Open source, gratuité et utilisation professionnelle</title>
                    <description>&lt;p&gt;J’aimerais aujourd’hui, au travers de ce billet, partager une réflexion concernant l’open source, que l’on utilise bien souvent gratuitement, dans un contexte professionnel. Ce billet découle de différentes discussions (aussi bien orales qu’au travers des réseaux sociaux) que j’ai pu avoir un certain nombre de personnes.&lt;/p&gt;

&lt;p&gt;Pour bien comprendre le contexte, la semaine dernière, Gitlab a enlevé de son offre gratuite la possibilité de pouvoir approuver une &lt;em&gt;merge request&lt;/em&gt; dans les dépôts privés. Il est donc maintenant nécessaire de posséder un compte payant pour accéder à cette fonctionnalité. Or il s’agissait là d’une fonctionnalité très utilisée en entreprise.&lt;/p&gt;

&lt;p&gt;À la suite de cela, de nombreuses discussions et plaintes ont eu lieu de la part d’équipes de développement utilisant les offres gratuites de Gitlab. Ces plaintes se sont traduites par exemple par des tweets (trollesques et plus ou moins assassins) auprès de différents employés de Gitlab. De nombreux échanges tournaient également autour d’une migration vers Github.&lt;/p&gt;

&lt;p&gt;On peut apprécier ou non Gitlab, le comparer à Github sur de nombreux points, l’objectif principal des deux outils étant de gérer des dépôts Git. Néanmoins les deux outils sont sensiblement différents et n’axent pas leurs développements de la même manière. Github se concentre sur son cœur de métier: la gestion des dépôts. Gitlab s’oriente (et c’est son principal argument marketing) sur l’aspect &lt;em&gt;DevOps&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Mais le fond du problème dans ce que je viens de décrire ici, c’est que de nombreuses entreprises profitent gracieusement d’outils évolués sans jamais contribuer d’aucune sorte à ces derniers. Quand je parle de contributions, cela peut être aussi bien de manière financière qu’en reversant certains développements faisant évoluer des outils open source.&lt;/p&gt;

&lt;p&gt;Par contre, ces mêmes sociétés vont se permettre de critiquer les outils, entreprises, voire même les employés de ces dernières sous prétexte qu’on leur “enlève” des fonctionnalités pour un outil pour lequel elles profitent sans aucune contrepartie.&lt;/p&gt;

&lt;p&gt;Dans le cas de Gitlab, oui, il est fort probable que ce changement vise à inciter les entreprises à passer à des comptes &lt;em&gt;premium&lt;/em&gt;. Je pense même que si certains migrent vers Github, au final ce n’est pas très grave, car ce sont des comptes qui n’auraient jamais basculé vers une offre payante.&lt;/p&gt;

&lt;p&gt;Peut-on leur en vouloir pour ça ? Je ne pense pas. Travaillez-vous gratuitement ? Je ne pense pas. Alors pourquoi ces sociétés devraient-elles le faire ? Qui plus est quand les infrastructures pour supporter une qualité de service sont plus que non négligeables.&lt;/p&gt;

&lt;p&gt;J’ai pris ici le cas précis de Gitlab, mais des exemples similaires, on en voit presque tous les jours. Cela peut aller de l’entreprise à but lucratif au mainteneur de projets open source travaillant bien souvent seul ou en très petites équipes.&lt;/p&gt;

&lt;p&gt;Ce qui m’agace dans cette histoire, c’est que dans le lot des critiques, on retrouve parfois des sociétés faisant des millions d’euros de chiffres d’affaires, qui vendent des produits ou des services à des tarifs élevés, mais rechignent à prendre un outil à une dizaine de dollars par mois.&lt;/p&gt;

&lt;p&gt;Derrière ce petit coup de gueule du lundi matin, j’aimerais que vous preniez quelques minutes avant de critiquer un outil, service ou projet que vous utilisez gracieusement, avec lequel vous gagnez de l’argent. Demandez-vous si vous reversez à ce dernier autant que ce qu’il vous apporte.&lt;/p&gt;
</description>
                    <pubDate>Mon, 18 Mar 2019 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2019/03/18/open-source-gratuite-et-utilisation-professionnelle.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2019/03/18/open-source-gratuite-et-utilisation-professionnelle.html</guid>
                </item>
            
        
            
                122
                <item>
                    <title>phpdaily le blog</title>
                    <description>&lt;p&gt;Si vous suivez sur ce blog ou les réseaux sociaux, vous n’êtes pas sans savoir que depuis le mois de février, je travaille sur un projet qui met à disposition des &lt;a href=&quot;/blog/2019/02/01/envie-de-tester-php-74.html&quot;&gt;images Docker pour tester les versions en cours de développement de PHP&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;En plus de simplifier l’accès aux futures versions du langage, j’avais envie d’avoir un espace de publication pour communiquer les nouveautés de chaque version. C’est pour cela que j’ai démarré un blog (en anglais) rattaché au projet. Ce dernier servira à publier des articles pour décrire, expliquer et mettre en oeuvre au travers d’exemples les futures fonctionnalités du langage. Ce dernier est accessible à l’adresse &lt;a href=&quot;https://phpdaily.github.io &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;https://phpdaily.github.io&lt;/a&gt;. Un &lt;a href=&quot;https://phpdaily.github.io/feed.xml &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;flux RSS&lt;/a&gt; est disponible et un &lt;a href=&quot;https://twitter.com/phpdailybuilds &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;compte Twitter&lt;/a&gt; a été créé pour l’occasion.&lt;/p&gt;

&lt;p&gt;Le blog est actuellement hébergé sur &lt;a href=&quot;https://pages.github.com &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;GitHub Pages&lt;/a&gt; et le &lt;a href=&quot;https://github.com/phpdaily/phpdaily.github.io &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;code source est disponible&lt;/a&gt;. Aussi, je souhaite que ce blog soit communautaire. Donc si vous souhaitez écrire un billet (en anglais) à propos d’une prochaine évolution du langage, n’hésitez pas à faire une &lt;em&gt;pull request&lt;/em&gt; sur le projet.&lt;/p&gt;
</description>
                    <pubDate>Wed, 06 Mar 2019 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2019/03/06/phpdaily-le-blog.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2019/03/06/phpdaily-le-blog.html</guid>
                </item>
            
        
            
                123
                <item>
                    <title>Envie de tester PHP 7.4 ? Il y a une image Docker pour ça !</title>
                    <description>&lt;p&gt;La version 7.4 de PHP est prévue pour la fin de cette année 2019. Elle apportera un certain nombre de nouvelles fonctionnalités dont celle que j’attends avec la plus grande impatience: &lt;a href=&quot;https://wiki.php.net/rfc/typed_properties_v2 &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;les propriétés typées&lt;/a&gt;. PHP est un langage dont le code source est librement disponible, il est alors possible de tester les nouveautés du langage au fil du développement de ce dernier.&lt;/p&gt;

&lt;p&gt;Compiler PHP peut sembler être une tâche complexe et nécessitant d’installer un certain nombre d’outils avec lequel nous (développeurs PHP) ne sommes pas habitués. Pour éviter ce travail et faciliter l’utilisation de la version de développement de PHP, j’ai décidé d’utiliser &lt;a href=&quot;https://docker.com &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Docker&lt;/a&gt;. En prenant comme base &lt;a href=&quot;https://hub.docker.com/_/php &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;le dépôt des images officielles&lt;/a&gt;, j’ai adapté ces dernières afin de compiler la version en cours de développement de PHP.&lt;/p&gt;

&lt;p&gt;J’ai rendu ce &lt;a href=&quot;https://github.com/phpdaily/php &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;travail disponible sur Github&lt;/a&gt; et les images sont librement accessibles &lt;a href=&quot;https://hub.docker.com/r/phpdaily/php &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;sur le registre officiel de Docker&lt;/a&gt;. Ces dernières sont construites (presque) quotidiennement.&lt;/p&gt;

&lt;p&gt;Si vous souhaitez commencer à jouer simplement via le mode interactif de la CLI, rien de plus simple:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;$ docker run --rm -it phpdaily/php:7.4.0-dev php -a&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Si vous souhaitez tester un code existant, ce n’est guère plus compliqué:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;$ docker run --rm -it -v &amp;quot;$PWD:/src&amp;quot; -w /src phpdaily/php:7.4.0-dev php script.php&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Il se peut que vous souhaitiez tester des fonctionnalités qui ne soient pas actives de base dans les images. C’est par exemple le cas de l’extension &lt;a href=&quot;https://wiki.php.net/rfc/ffi &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;FFI (Foreign Function Interface)&lt;/a&gt; qui nécessite des options de compilation spécifiques. Pour utiliser cette dernière, les images Docker mettent en place tout le nécessaire pour que vous puissiez facilement l’installer au sein d’une image personnalisée.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-docker&quot; data-lang=&quot;docker&quot;&gt;# phpdaily/php:7.4.0-dev est une image basée sur Linux Alpine
FROM phpdaily/php:7.4.0-dev

RUN apk add --no-cache --virtual .persistent-deps libffi-dev \
    &amp;amp;&amp;amp; docker-php-ext-configure ffi --with-ffi \
    &amp;amp;&amp;amp; docker-php-ext-install ffi&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Construisez ensuite l’image Docker correspondante au travers de la commande:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;$ docker build -t you/php-ffi .&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;L’extension sera alors utilisable dans les conteneurs qui seront basés sur votre image (vous pouvez vérifier cela en listant les extensions disponibles &lt;code&gt;docker run --rm -it you/php-ffi php -m&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Toutes les images &lt;code&gt;phpdaily/php&lt;/code&gt; étant basées sur les images officielles, vous avez accès aux mêmes possibilités d’utilisation et d’extension que sur ces dernières.&lt;/p&gt;

&lt;p&gt;C’est maintenant à vous de jouer !&lt;/p&gt;
</description>
                    <pubDate>Fri, 01 Feb 2019 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2019/02/01/envie-de-tester-php-74.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2019/02/01/envie-de-tester-php-74.html</guid>
                </item>
            
        
            
                124
                <item>
                    <title>Rétrospective 2018</title>
                    <description>&lt;p&gt;2018 s’est terminé et en ce début d’année 2019, je cède à l’écriture des traditionnelles “bonnes résolutions”. Après tout, il est toujours intéressant de faire une petite rétrospective de l’année passée pour voir le chemin parcouru et de se motiver à définir de nouveaux objectifs.&lt;/p&gt;

&lt;p&gt;L’année dernière, je publiais un &lt;a href=&quot;/blog/2018/01/26/que-2018-commence.html&quot;&gt;billet similaire sur mes objectifs 2018&lt;/a&gt;. Force est de constater que je n’ai pas réalisé tous les objectifs que je m’étais fixé.&lt;/p&gt;

&lt;h2 id=&quot;retour-sur-mon-changement-professionnel&quot;&gt;Retour sur mon changement professionnel&lt;/h2&gt;

&lt;p&gt;J’ai donc démarré l’année en changeant, 2 fois d’entreprise. Comme souvent lorsque l’on quitte un poste, on sait ce que l’on &lt;em&gt;perd&lt;/em&gt;, mais pas nécessairement ce que l’on gagne. Après, avoir intégré une première société dont le poste ne correspondait en réalité pas à mes attentes, je travaille aujourd’hui sur des applications de comparaisons d’offres en énergie au sein de la société &lt;a href=&quot;https://opera-energie.com/ &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Opéra Energie&lt;/a&gt; qui a récemment été &lt;a href=&quot;https://www.brefeco.com/actualite/aide-aux-entreprises/trois-nouvelles-pepites-labellisees-1 &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;labellisée “Pépites” par la métropole de Lyon&lt;/a&gt; (au-delà de l’aspect purement marketing, il s’agit d’une reconnaissance de la croissant de la société dans la métropole Lyonnaise).&lt;/p&gt;

&lt;h2 id=&quot;la-fin-de-jobeet&quot;&gt;La fin de Jobeet&lt;/h2&gt;

&lt;p&gt;Fin 2017, je démarrais &lt;a href=&quot;/blog/2017/09/12/tutorial-jobeet-symfony-4-introduction.html&quot;&gt;l’adaptation du tutorial Jobeet&lt;/a&gt; pour la sortie de Symfony 4. Malheureusement, ce projet est plus chronophage que ce que j’avais prévu et je n’ai plus le temps nécessaire (ni la motivation peut-être) pour continuer ce dernier. Malgré le succès du tutorial et les nombreux retours positifs que j’ai pu avoir, je me suis résolu fin octobre à annoncer la fin du projet.&lt;/p&gt;

&lt;h2 id=&quot;le-retour-aux-bases&quot;&gt;Le retour aux bases&lt;/h2&gt;

&lt;p&gt;Je souhaitais revenir aux fondamentaux techniques de mon métier (la programmation), ce qui fut chose faite avec plus ou moins de réussite. J’entends par là, de découvrir et redécouvrir les algorithmes et les structures de données de base de l’informatique. Cela aura a été l’occasion de jouer avec &lt;a href=&quot;https://secure.php.net/manual/en/book.ds.php &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;PHP-DS&lt;/a&gt;, de découvrir des bibliothèques PHP sympa comme &lt;a href=&quot;https://github.com/Innmind/Immutable &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Immutable&lt;/a&gt; et de tester &lt;a href=&quot;https://github.com/facebook/immutable-js &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Immutable.js&lt;/a&gt; de Facebook (pas de raison qu’il n’y en ait que pour PHP).&lt;/p&gt;

&lt;h2 id=&quot;lannée-de-rust-ou-pas&quot;&gt;L’année de Rust, ou pas…&lt;/h2&gt;

&lt;p&gt;Cette année je devais m’intéresser à &lt;a href=&quot;https://www.rust-lang.org &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Rust&lt;/a&gt; le langage sécurisé conçu et développé par Mozilla. Malheureusement, &lt;a href=&quot;https://twitter.com/jdecool/status/1025673204063449089 &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;faute de temps&lt;/a&gt; je n’aurais pas du tout eu l’occasion de m’y mettre.&lt;/p&gt;

&lt;h2 id=&quot;automatisation-automatisation-automatisation&quot;&gt;Automatisation, automatisation, automatisation…&lt;/h2&gt;

&lt;p&gt;Il y a un peu plus d’un an, je migrais mes serveurs OVH vers Scaleway. Je voulais en profiter pour tout passer sous Docker et automatiser un maximum de choses. Effectuer cette migration manuellement a été long, laborieux et source d’erreurs. Je voulais également en profiter pour m’intéresser à l’utilisation de Docker en production.&lt;/p&gt;

&lt;p&gt;Mais finalement, je serais allé beaucoup moins loin que ce que j’avais initialement prévu. L’ensemble tourne bien, je passe l’essentiel de mon temps à mettre à jour les paquets systèmes. J’ai donc peu de chose croustillante à raconter à ce sujet.&lt;/p&gt;

&lt;h2 id=&quot;le-reste-de-lannée-en-bref&quot;&gt;Le reste de l’année en bref&lt;/h2&gt;

&lt;p&gt;Au final cette année aura été relativement calme du technique. J’ai participé à peu d’événements techniques (meetups et conférences), très peu contribué à l’Open Source. La naissance de ma fille y est certainement pour quelque chose et j’ai mis du temps à trouver une organisation qui me permette de tout concilier (vie pro, vie perso et activités tierces).&lt;/p&gt;

&lt;p&gt;Faits marquant de cette année, la fréquentation du blog qui a plus que doublé, notamment via le tutorial sur Symfony 4, qui est maintenant la raison principale de visite de ce site.&lt;/p&gt;

&lt;p&gt;Et pour en finir avec les chiffres, voici les 3 articles écrits cette année, les plus consultés :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/2018/07/23/la-gestion-des-uuid-dans-mysql.html&quot;&gt;La gestion des UUID dans MySQL&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/2018/09/03/deploiement-avec-deployer-et-gitlab-ci.html&quot;&gt;Déploiement avec Deployer et Gitlab CI&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/2018/12/10/le-role-de-lead-dev.html&quot;&gt;Le rôle de lead developer&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
                    <pubDate>Sun, 13 Jan 2019 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2019/01/13/retrospective-2018.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2019/01/13/retrospective-2018.html</guid>
                </item>
            
        
            
                125
                <item>
                    <title>Le rôle de lead developer</title>
                    <description>&lt;p&gt;J’ai l’impression que le poste de &lt;i&gt;lead developer&lt;/i&gt; (ou encore &lt;i&gt;tech lead&lt;/i&gt;) n’a pas réellement d’équivalence en français puisque, comme pour de nombreux termes dans notre industrie en fait, nous utilisons exclusivement une version anglo-saxonne du terme pour nous exprimer. Avec l’émergence de notre domaine ces dernières années, ce poste est semble-t-il l’un des plus prisés par les développeurs. Pourtant, lorsque je suis amené à discuter avec des développeurs, des recruteurs, des chefs d’entreprise voir même des &lt;i&gt;lead dev&lt;/i&gt;, je constate que chacun à sa propre définition du rôle associé à ce titre.&lt;/p&gt;

&lt;p&gt;Derrière ce libellé, parfois un titre honorifique (dans le sens où rien ne distingue les missions de ce dernier d’un autre développeur) donné à un ancien développeur présent sur un projet (ou dans l’entreprise) de longue date. On peut également retrouver derrière ce poste des développeurs expérimentés ou ayant une expertise dans une technologie ou un domaine spécifique. Il est alors intéressant de noter que l’on peut alors être &lt;i&gt;lead dev&lt;/i&gt; à plusieurs niveaux. Une personne ayant des compétences avancées en PHP peut très bien être &lt;i&gt;tech lead PHP&lt;/i&gt; par exemple. Mais si cette même personne connaît très bien un projet particulier, elle peut alors être &lt;i&gt;lead&lt;/i&gt; au niveau du projet.&lt;/p&gt;

&lt;p&gt;Partant de ce constat, le &lt;i&gt;tech lead&lt;/i&gt; est avant tout un développeur ayant une expérience et une expertise significative. Son rôle au sein d’un projet et d’une entreprise doit aller au-delà du code qu’il produit. Il a un rôle de coaching vis-à-vis de l’équipe dans laquelle il travaille ou intervient. Il doit pouvoir la faire évoluer, monter en compétence et tirer le projet (ou le domaine d’expertise qu’il exerce) vers le haut. Il est l’un des garants de la qualité (et pas  l’unique ! Cela ne dispense pas le reste de l’équipe d’y faire attention, bien au contraire).&lt;/p&gt;

&lt;p&gt;Dans les missions qui seraient à réaliser par le &lt;i&gt;lead dev&lt;/i&gt;, la principale est d’assurer la cohérence des développements réalisés au sein de l’équipe dans laquelle il travaille. Pour cela, il doit avoir une vision globale du travail à réaliser et il doit être capable d’évaluer les impacts sur l’existant. Même si ce n’est pas lui qui effectue l’implémentation, il doit veiller à industrialiser les processus de développement, s’assurer de la qualité du code en mettant en place des procédures pour garantir cette dernière. Il est également important qu’il se tienne à jour de l’état de l’art de son métier afin de pouvoir conserver un esprit critique en étant capable de prendre du recul sur les décisions qui sont prises.&lt;/p&gt;

&lt;p&gt;Je qualifierai le &lt;i&gt;lead dev&lt;/i&gt; d’assistant du CTO. Personnellement, j’aime bien utiliser l’image du chef de l’état (le CTO) et de ces ministres (les &lt;i&gt;lead developers&lt;/i&gt;). Donc en plus de ces compétences techniques, il doit être capable de communiquer de manière claire, défendre et expliquer des choix réalisés. Il doit avoir une vision critique de ce qui est fait sur son domaine d’expertise pour faire évoluer les choses dans le bon sens, tout en sachant prendre du recul sur le travail réalisé.&lt;/p&gt;

&lt;p&gt;On lui prête parfois des missions managériales. Mais à mon sens, ces missions ne font pas partie du poste. C’est au CTO, voir à des managers selon la taille de la société de faire ce travail. Le &lt;i&gt;lead dev&lt;/i&gt; doit se concentrer sur la partie technique. Il est par contre à mon avis indispensable qu’il participe aux entretiens de recrutement des personnes qui intégreront l’équipe dans laquelle il travaille. D’ailleurs, dans l’idéal, toute l’équipe de développement devrait pouvoir discuter avec un candidat qui va intégrer l’équipe.&lt;/p&gt;

&lt;p&gt;Voila en ce qui me concerne le rôle d’un &lt;i&gt;lead dev&lt;/i&gt;.&lt;/p&gt;
</description>
                    <pubDate>Mon, 10 Dec 2018 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2018/12/10/le-role-de-lead-dev.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2018/12/10/le-role-de-lead-dev.html</guid>
                </item>
            
        
            
                126
                <item>
                    <title>Déploiement avec Deployer et Gitlab CI</title>
                    <description>&lt;p&gt;Chez &lt;a href=&quot;https://opera-energie.com &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Opéra Energie&lt;/a&gt;, la société dans laquelle je travaille, nous développons nos outils en PHP et utilisons &lt;a href=&quot;https://deployer.org/ &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Deployer&lt;/a&gt; pour le déploiement de nos différents projets. Notre code est hébergé sur une instance Gitlab et c’est tout naturellement que nous utilisons Gitlab CI pour notre intégration continue et le déploiement de nos applications.&lt;/p&gt;

&lt;p&gt;Si déployer du code depuis les instances de nos runners Gitlab CI ne posent pas de problème particulier (il est facile d’y déposer une clé SSH autorisant la connexion à nos serveurs de productions et de tests), nous utilisons également les instances des runners Gitlab. Avec ces derniers, il est alors impossible d’y déposer une clé SSH permettant de se connecter à nos serveurs.&lt;/p&gt;

&lt;p&gt;Les runners Gitlab que nous utilisons fonctionnent exclusivement sur Docker. Pour le déploiement, nous installons Deployer dans une image &lt;code&gt;php:7-alpine&lt;/code&gt; des plus classiques (à laquelle nous avons néanmoins ajouté le nécessaire pour démarrer une connexion SSH, à savoir le paquet &lt;code&gt;openssh-client&lt;/code&gt; dans le cas d’une distribution &lt;em&gt;Alpine&lt;/em&gt;). Afin de pouvoir s’assurer que nos conteneurs Docker puissent se connecter sur les serveurs qui hébergeront nos applications, nous avons commencé par créer des clés SSH par serveurs (afin de pouvoir avoir une gestion fine d’une éventuelle révocation de ces dernières). Nos clés SSH seront ensuite injectées dans le conteneur qui effectuera le déploiement lors de son démarrage.&lt;/p&gt;

&lt;p&gt;Il convient tout d’abord d’ajouter les clés publiques sur chacun des serveurs concernés. Ensuite, dans Gitlab, nous créons des &lt;a href=&quot;https://docs.gitlab.com/ce/ci/variables/ &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;variables Gitlab CI&lt;/a&gt; qui contiendront les clés privées que nous avons précédemment générées. Ces dernières sont automatiquement transmises aux runners lors de leurs exécutions.&lt;/p&gt;

&lt;p&gt;Une fois Gitlab correctement configuré, il est maintenant possible d’éditer le fichier &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; décrivant les actions qui seront réalisées par notre intégration continue. C’est dans ce dernier, que nous allons récupérer le contenu de nos variables d’environnement et ajouter dynamiquement nos clés SSH à l’agent pour ensuite effectuer le déploiement de notre projet :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;image: php:7-cli-alpine

stages:
  - deploy

before_script:
    - apk add --update git openssh-client
    - mkdir -p ~/.ssh &amp;amp;&amp;amp; echo -e &amp;quot;Host *\n\tStrictHostKeyChecking no\n\n&amp;quot; &amp;gt; ~/.ssh/config
    - eval $(ssh-agent -s)
    - echo &amp;quot;$SERVER_TEST1_KEY&amp;quot; | ssh-add -
    - echo &amp;quot;$SERVER_TEST2_KEY&amp;quot; | ssh-add -
    - echo &amp;quot;$SERVER_PROD1_KEY&amp;quot; | ssh-add -

deploy:app:
    stage: deploy
    script:
        - curl --show-error --silent https://getcomposer.org/installer | php
        - php composer.phar install
        - vendor/bin/dep deploy
    when: manual
    only:
        - master
    environment:
        name: prod
        url: https://my-app.domain.com&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Vous remarquerez que le déploiement s’effectuant dans un conteneur Docker et ne pouvant prévoir le serveur sur lequel la tâche sera lancée, nous désactivons un contrôle de la clé SSH via l’option &lt;code&gt;StrictHostKeyChecking no&lt;/code&gt;. Cette action n’est à effectuer qu’au sein d’un conteneur Docker et peut-être potentiellement dangereux, car elle permet à des attaques de type &lt;em&gt;man-in-the-middle&lt;/em&gt; de pouvoir être effectué.&lt;/p&gt;

&lt;p&gt;Et c’est ainsi, en injectant dynamiquement des clés SSH au sein d’un conteneur Docker, qu’il est possible de déployer une application PHP avec Deployer (au n’importe quel autre outil de déploiement en réalité) via des runners Gitlab.&lt;/p&gt;
</description>
                    <pubDate>Mon, 03 Sep 2018 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2018/09/03/deploiement-avec-deployer-et-gitlab-ci.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2018/09/03/deploiement-avec-deployer-et-gitlab-ci.html</guid>
                </item>
            
        
            
                127
                <item>
                    <title>Docker, déploiement et PHP</title>
                    <description>&lt;p&gt;Si l’écosystème des conteneurs s’est autant démocratisé ces dernières années, c’est notamment grâce à l’apparition de Docker dont les ambitions sont clairement affichées sur le site: “Build, Ship, and Run Any App, Anywhere”, que l’on pourrait traduire par “construisez, déployez, et exécutez n’importe quelle application, n’importe où”. Si la promesse est intéressante, la mise en place d’une infrastructure autour de Docker peut ne pas être chose facile.&lt;/p&gt;

&lt;p&gt;À l’heure actuelle, énormément d’environnement de développement et de système d’intégration continue fonctionne autour de Docker. Mais ce n’est pas nécessairement le cas des applications en production, car derrière cette promesse de portabilité se cache un certain nombre de contraintes et construire une application tournant au sein d’un conteneur n’est pas nécessairement une chose aisée, surtout en PHP.&lt;/p&gt;

&lt;p&gt;Pour mettre en place avec succès une infrastructure autour de conteneur, de nombreuses entreprises ont établi un ensemble de bonnes pratiques. Parmis ces dernières, l’un des plus importante est qu’&lt;a href=&quot;https://cloud.google.com/solutions/best-practices-for-building-containers#package_a_single_application_per_container &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;un conteneur doit correspondre à une application&lt;/a&gt;. Si cela est quelque chose de pertinent et facile dans le cas d’une application Go (qui une fois compilé correspond à un binaire embarquant toutes ses dépendances) ou d’une application Java (il est possible pour une archive Java d’embarquer son propre serveur HTTP pour fonctionner de manière autonome), tout n’est pas aussi facile dans le cas d’une application PHP.&lt;/p&gt;

&lt;p&gt;Effectivement, une application Web PHP ne peut fonctionner de manière autonome. Cette dernière tourne généralement au travers de deux dépendances: un serveur HTTP (type Apache, Nginx, …) et un module PHP (qui, couplé au serveur HTTP permet d’exécuter la machine virtuelle du langage). Si nous souhaitons donc avoir un conteneur autonome et capable de faire tourner notre application PHP, nous devrions donc avoir un conteneur embarquant PHP et un serveur HTTP. Mais cette manière de procéder est-elle en adéquation avec les bonnes pratiques citées précédemment ?&lt;/p&gt;

&lt;p&gt;Si nous considérons notre application comme un service à part entière, oui. Par contre si nous considérons le serveur HTTP (le point d’entrée d’une requête auprès de notre application) et le code PHP (notre application à proprement parler) comme deux services distincts, alors on peut considérer que nous sommes en train d’enfreindre les bonnes pratiques des conteneurs (à plus forte raison, si l’on voit un conteneur comme un processus isolé).&lt;/p&gt;

&lt;p&gt;Il y a bien entendu la théorie et la pratique, et même si j’ai ma propre idée sur la mise en place d’un environnement de conteneur pour les applications PHP sur lesquelles je travaille, j’ai trouvé intéressant de poser la question sur Twitter pour avoir des retours d’expérience.&lt;/p&gt;

&lt;p&gt;Les retours que j’ai pu avoir sont très intéressant et révèlent de nombreux avis différents ainsi que divers infrastructures. Je pense d’ailleurs publier prochainement une synthèse des échanges que j’ai pu avoir.&lt;/p&gt;

&lt;p&gt;Et si vous souhaitez participer, il n’est pas encore trop tard !&lt;/p&gt;

&lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;fr&quot;&gt;&lt;p lang=&quot;fr&quot; dir=&quot;ltr&quot;&gt;Dites les gens qui utilisent &lt;a href=&quot;https://twitter.com/hashtag/Docker?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#Docker&lt;/a&gt; pour déployer des projets &lt;a href=&quot;https://twitter.com/hashtag/PHP?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#PHP&lt;/a&gt; en prod, est-ce que votre conteneur &amp;quot;applicatif&amp;quot; embarque PHP et le serveur Web (nginx, apache, ...) traitant les requêtes, ou est-ce que vous séparer les deux concepts dans des conteneurs séparés ? RT appréciés&lt;/p&gt;&amp;mdash; Jérémy DECOOL (@jdecool) &lt;a href=&quot;https://twitter.com/jdecool/status/1034827869137117184?ref_src=twsrc%5Etfw&quot;&gt;29 août 2018&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;

</description>
                    <pubDate>Sat, 01 Sep 2018 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2018/09/01/docker-deploiement-et-php.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2018/09/01/docker-deploiement-et-php.html</guid>
                </item>
            
        
            
                128
                <item>
                    <title>Soumettre un formulaire HTML en AJAX avec Javascript</title>
                    <description>&lt;p&gt;Nous travaillons aujourd’hui avec tellement de frameworks aussi bien pour concevoir les parties backend que frontend, que nous oublions parfois à quel point il peut être facile d’effectuer certaines actions pouvant paraître complexes sans utiliser aucun outil supplémentaire. C’est notamment le cas pour la soumission de formulaire en AJAX.&lt;/p&gt;

&lt;p&gt;Posez la question autour de vous, qui sait comment soumettre un formulaire en AJAX sans utiliser un framework particulier côté client (tel que React ou encore VueJS ou même encore jQuery) et sans utiliser des composants qui sont parfois fournis avec des frameworks backend, vous pourriez être surpris des réponses.&lt;/p&gt;

&lt;p&gt;Pourtant les navigateurs et le langage Javascript ont énormément évolué ces dernières années et soumettre un formulaire AJAX n’a plus rien de bien sorcier. À vous d’en juger avec le snippet ci-dessous:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&amp;lt;html&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;form method=&amp;quot;post&amp;quot; action=&amp;quot;/submit&amp;quot; enctype=&amp;quot;multipart/form-data&amp;quot;&amp;gt;
      &amp;lt;input type=&amp;quot;text&amp;quot; name=&amp;quot;filename&amp;quot;&amp;gt;
      &amp;lt;input type=&amp;quot;file&amp;quot; name=&amp;quot;file&amp;quot;&amp;gt;
      &amp;lt;input type=&amp;quot;submit&amp;quot;&amp;gt;
    &amp;lt;/form&amp;gt;

    &amp;lt;script&amp;gt;
      document.addEventListener(&amp;#39;DOMContentLoaded&amp;#39;, function() {
        document.getElementsByTagName(&amp;#39;form&amp;#39;)[0].addEventListener(&amp;#39;submit&amp;#39;, (e) =&amp;gt; {
          e.preventDefault();

          let form = e.target;

          fetch(form.action, { method: form.method, body: new FormData(form) })
            .then(response =&amp;gt; response.json())
            .then(json =&amp;gt; console.log(json));

          return false;
        });
      });
    &amp;lt;/script&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

</description>
                    <pubDate>Mon, 20 Aug 2018 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2018/08/20/soumettre-un-formulaire-html-vanilla-javascript.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2018/08/20/soumettre-un-formulaire-html-vanilla-javascript.html</guid>
                </item>
            
        
            
                129
                <item>
                    <title>Pourquoi vous devriez utiliser la librairie HTTPlug dans vos projets</title>
                    <description>&lt;p&gt;&lt;a href=&quot;http://httplug.io &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;HTTPlug&lt;/a&gt; est une librairie d’abstraction de client HTTP. HTTPlug vous permet de découpler le code de votre application d’une implémentation spécifique d’un client HTTP. Lorsque tous les composants de votre application dépendent uniquement de la librairie, vous avez alors la possibilité de choisir le client HTTP qui correspond le mieux à votre projet ou d’utiliser le même client que vos dépendances.&lt;/p&gt;

&lt;p&gt;La bibliothèque est compatible avec la &lt;a href=&quot;https://www.php-fig.org/psr/psr-7/ &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;PSR-7&lt;/a&gt; et définit un contrat pour l’envoi de requête HTTP. Elle permet également l’utilisation d’un client HTTP asynchrone. L’intérêt majeur du composant et que HTTPlug est complètement indépendant de toute implémentation cliente. C’est-à-dire, que lorsque vous utilisez HTTPlug, vous allez avoir la possibilité, sans changer quoi que ce soit à votre code, d’effectuer les requêtes HTTP via &lt;a href=&quot;https://secure.php.net/manual/en/book.curl.php &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;CURL&lt;/a&gt;, &lt;a href=&quot;http://docs.guzzlephp.org/en/stable/ &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Guzzle&lt;/a&gt;, &lt;a href=&quot;https://reactphp.org &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;ReactPHP&lt;/a&gt; ou n’importe quel client de votre choix.&lt;/p&gt;

&lt;p&gt;Mais en quoi est-ce intéressant pour votre projet ?&lt;/p&gt;

&lt;p&gt;Si &lt;em&gt;vous développez une librairie&lt;/em&gt;, comme par exemple un client d’API, vous ne savez pas à l’avance les dépendances qui seront utilisées dans les projets où votre composant va être intégré. Il peut s’agir d’un projet écrit en pur PHP, comme un projet Symfony, Laravel, Zend ou n’importe quel autre framework. Mais au-delà du framework, le projet utilisateur peut très bien utiliser un client CURL pour effectuer les appels réseaux afin d’éviter une contrainte technique pouvant être liée à Guzzle. Comment vous assurez que votre bibliothèque puisse s’adapter sans contrainte particulière au plus grand nombre de projets ? HTTPlug permet justement de répondre à cette problématique. Ne forcez pas vos utilisateurs à mettre en place une implémentation d’un outil qu’ils ne souhaitent pas !&lt;/p&gt;

&lt;p&gt;À ce stade, vous voyez peut-être l’intérêt de mettre en place HTTPlug pour le développement de composant indépendant. Mais qu’en est-il de son utilisation au sein d’un code applicatif ?&lt;/p&gt;

&lt;p&gt;Il est vrai que &lt;em&gt;dans une application&lt;/em&gt;, nous changeons rarement l’implémentation de la couche effectuant les appels réseau. Pourtant HTTPlug peut une fois encore se révéler utile. En effet, la bibliothèque vous permettra de normaliser et découpler la manière dont vous effectuer vos requêtes HTTP. Tout comme pour l’écriture d’une librairie, vous serez alors libre de choisir l’implémentation de votre choix. Vous souhaitez démarrer avec CURL, pas de problème, vous pourrez passer du Guzzle en remplaçant tout simplement le &lt;i&gt;package&lt;/i&gt; &lt;code&gt;php-http/curl-client&lt;/code&gt; par &lt;code&gt;php-http/guzzle6-adapter&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Vous n’êtes pas convaincu par ce dernier argument ? Il est vrai que l’on ne change pas d’implémentation tous les jours. Mais vous souvenez-vous du passage de la version 4 de Guzzle à la version 5 ? Ou de la 5 vers la 6, quand l’API du client avait totalement changée ? Car qu’on le veuille ou non, il est parfois indispensable de faire cette mise à jour pour profiter des dernières fonctionnalités des composants ou tout simplement pour continuer à utiliser une version maintenant du composant tier. Si vous aviez utilisé HTTPlug, cette mise à jour aurait été complètement transparente.&lt;/p&gt;

&lt;p&gt;En utilisant une abstraction d’un client HTTP, vous êtes donc libre. Libre de choisir l’implémentation que vous souhaitez utiliser, libre de changer le client HTTP pour prendre celui qui correspond réellement à votre besoin. HTTPlug est un composant dans l’air du temps, lorsque l’on parle DDD ou architecture hexagonale, lorsque l’on souhaite avoir un code proche du métier et découpler des frameworks que l’on utilise, c’est exactement la philosophie que suis ce composant.&lt;/p&gt;

&lt;p&gt;Qu’attendez-vous pour le mettre en place dans vos projets ? L’essayer, c’est l’adopter !&lt;/p&gt;
</description>
                    <pubDate>Tue, 31 Jul 2018 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2018/07/31/pourquoi-vous-devriez-utiliser-la-librairie-httplug-dans-vos-projets.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2018/07/31/pourquoi-vous-devriez-utiliser-la-librairie-httplug-dans-vos-projets.html</guid>
                </item>
            
        
            
                130
                <item>
                    <title>Optimiser le stockage des adresses IP dans MySQL</title>
                    <description>&lt;p&gt;C’est l’été, le mois d’août arrive, de manière générale les cadences et les volumes de travaillent ralentissent un peu pour la période estivale. C’est le moment parfait pour démarrer des travaux de &lt;i&gt;refactoring&lt;/i&gt; où pour travailler sur des tâches pour lesquels vous n’arrivez jamais à dégager du temps ! Si au début de la semaine j’évoquais &lt;a href=&quot;/blog/2018/07/23/la-gestion-des-uuid-dans-mysql.html&quot;&gt;comment optimiser le stockage de vos UUID&lt;/a&gt;, évoquons comme optimiser et simplifier la gestion des adresses IP dans MySQL.&lt;/p&gt;

&lt;p&gt;Pour la plupart d’entre nous, nous allons stocker les adresses IP sous la forme d’une chaîne de 15 caractères maximums dans le cas d’une adresse IPv4 et de 39 caractères pour une adresse de type IPv6. Pour la majorité de nos besoins cela est pleinement suffisant, nous effectuons en réalité assez peu de traitements sur ces dernières hormis de la consultation de données. Mais comme toujours une donnée stockée sous une forme textuelle est quelque chose de coûteux aussi bien en termes de stockage mais également en termes de performance. De plus cela peut rendre vos requêtes de sélection assez complexes (voire impossibles) à écrire.&lt;/p&gt;

&lt;p&gt;Bien heureusement pour nous, les moteurs de base de données fournissent généralement des outils pour optimiser. Mais si PostgresSQL fournit un type &lt;a href=&quot;https://www.postgresql.org/docs/8.3/static/datatype-net-types.html &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;inet&lt;/code&gt;&lt;/a&gt; permettant de gérer une donnée de type IPv4 ou IPv6, MySQL ne possède pas d’une gestion de type aussi évoluée. Néanmoins, MySQL met à disposition de ces utilisateurs 4 fonctions permettant de travailler avec des adresses IP: &lt;code&gt;INET_ATON()&lt;/code&gt;, &lt;code&gt;INET_NTOA()&lt;/code&gt;, &lt;code&gt;INET6_ATON()&lt;/code&gt; et &lt;code&gt;INET6_ATON()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Les méthodes &lt;code&gt;INET_ATON()&lt;/code&gt; et &lt;code&gt;INET6_ATON()&lt;/code&gt; vont permettre de transformer respectivement une adresse IPv4 et IPv6 d’une forme textuelle en un entier ou sous la forme d’une chaîne binaire qui représentera cette dernière sous forme numérique. Vous l’aurez donc deviné, les méthodes &lt;code&gt;INET_NTOA()&lt;/code&gt; et &lt;code&gt;INET6_ATON()&lt;/code&gt; vont procéder à l’opération inverse, c’est-à-dire prendre la forme numérique d’une adresse IP pour la convertir en forme textuelle. C’est ainsi qu’il est possible d’optimiser le stockage et améliorer la performance de vos requêtes, car effectuer une recherche numérique est plus rapide que de faire une recherche &lt;i&gt;full text&lt;/i&gt;.&lt;/p&gt;

&lt;p&gt;Un autre avantage de cette solution est que bien que les adresses IP soient transformées, la transformation conserve l’ordre de grandeur des données. Par exemple, si je veux trouver toutes les lignes de ma base qui sont comprises entre l’adresse 8.8.8.4 et 8.8.8.8, je pourrais très bien écrire la requête suivante : &lt;code&gt;SELECT * FROM table WHERE ip BETWEEN INET_ATON(&apos;8.8.8.4&apos;) AND INET_ATON(&apos;8.8.8.8&apos;)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Et parce que nous sommes nombreux à utiliser Doctrine pour gérer nos interactions avec la base de données, il existe de nombreuses bibliothèques permettant d’ajouter le support de ces fonctions. Pour ma part j’utilise le plus régulièrement &lt;a href=&quot;https://github.com/beberlei/DoctrineExtensions &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;beberlei/DoctrineExtensions&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Et si vous préférez gérer cette logique côté PHP, le langage possède ses propres fonctions permettant d’effectuer ces conversions de type de données. Il met à disposition des développeurs les fonctions &lt;a href=&quot;https://secure.php.net/manual/en/function.ip2long.php &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;ip2long&lt;/code&gt;&lt;/a&gt; et &lt;a href=&quot;https://secure.php.net/manual/en/function.long2ip.php &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;long2ip&lt;/code&gt;&lt;/a&gt; pour les adresses IPv4, ainsi que &lt;a href=&quot;https://secure.php.net/manual/en/function.inet-pton.php &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;inet_pton&lt;/code&gt;&lt;/a&gt; et &lt;a href=&quot;https://secure.php.net/manual/en/function.inet-ntop.php &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;inet_ntop&lt;/code&gt;&lt;/a&gt; pour les IPv6.&lt;/p&gt;
</description>
                    <pubDate>Wed, 25 Jul 2018 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2018/07/25/optimiser-le-stockage-des-adresses-ip-dans-mysql.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2018/07/25/optimiser-le-stockage-des-adresses-ip-dans-mysql.html</guid>
                </item>
            
        
            
                131
                <item>
                    <title>La gestion des UUID dans MySQL</title>
                    <description>&lt;p&gt;Il est maintenant acquis que l’utilisation de valeurs auto-incrémentées en tant que clé primaire présente plusieurs inconvénients lorsque cette technique est mal utilisée. C’est pour cela que l’utilisation des &lt;em&gt;UUID&lt;/em&gt; se démocratise aujourd’hui. Néanmoins toutes les bases de données ne traitent pas ce type de la même manière et cela peut avoir des répercussions sur la performance de nos requêtes. C’est notamment le cas si vous utilisez MySQL.&lt;/p&gt;

&lt;p&gt;Titouan GALOPIN a récemment rédigé un billet sur &lt;a href=&quot;https://medium.com/@galopintitouan/auto-increment-is-the-devil-using-uuids-in-symfony-and-doctrine-71763721b9a9 &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;l’utilisation des UUIDs dans Symfony avec Doctrine&lt;/a&gt;. Il recommande dans ce billet d’utiliser conjointement un identifiant auto-généré qui aura vocation à être utilisé par la base de données et un UUID qui servira de point d’entrée pour les données de notre application.&lt;/p&gt;

&lt;p&gt;MySQL commence pourtant à mettre des solutions en place afin de pouvoir gérer les problèmes de performances liés à l’utilisation des UUID. Par exemple, la version 8 de MySQL introduit trois nouvelles fonctions en ce sens : &lt;code&gt;IS_UUID&lt;/code&gt;, &lt;code&gt;UUID_TO_BIN&lt;/code&gt; et &lt;code&gt;BIN_TO_UUID&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;MySQL ne sait pas gérer les UUID et nous stockons généralement ce dernier sous sa forme textuelle, correspondant à une chaîne de 36 caractères. C’est essentiellement pour cela que cela induit des problèmes de stockage et de performance car la donnée est plus complexe et plus lourde à gérer qu’un simple entier. Les fonctions &lt;code&gt;UUID_TO_BIN&lt;/code&gt; et &lt;code&gt;BIN_TO_UUID&lt;/code&gt; permettent ainsi de convertir un UUID textuel sous sa forme binaire (correspondant au type &lt;code&gt;VARBINARY(16)&lt;/code&gt;). La données est ainsi deux fois plus petite que sous le format textuel et les données binaires sont mieux appréhendées par le système de base de données.&lt;/p&gt;

&lt;p&gt;L’inconvénient de cette solution et que pour pouvoir visualiser l’UUID en base, il sera nécessaire d’appliquer la fonction &lt;code&gt;BIN_TO_UUID&lt;/code&gt; pour rendre ce dernier lisible. Dans &lt;a href=&quot;javascript:;&quot; class=&quot;broken-link&quot; rel=&quot;nofollow&quot; data-original-url=&quot;http://mysqlserverteam.com/storing-uuid-values-in-mysql-tables/ &quot;&gt;un article sur le stockage des UUID&lt;/a&gt;, MySQL duplique la donnée avec une version textuelle pour rendre la donnée visible, mais cela revient au final à une solution similaire à celle évoquée par Titouan. La question étant de savoir si vous avez réellement besoin d’avoir cette version textuelle ?&lt;/p&gt;

&lt;p&gt;Si vous travaillez avec PHP, vous utiliserez certainement la librairie &lt;a href=&quot;https://github.com/ramsey/uuid &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;ramsey/uuid&lt;/code&gt;&lt;/a&gt; pour gérer vos UUID. Cette dernière fournit également un composant &lt;a href=&quot; https://github.com/ramsey/uuid-doctrine &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;ramsey/uuid-doctrine&lt;/code&gt;&lt;/a&gt; pour s’interfacer avec Doctrine. Il est intéressant de noter que ce dernier propose un type &lt;code&gt;uuid_binary&lt;/code&gt; utilisant les techniques citées dans ce billet.&lt;/p&gt;

&lt;p&gt;Et pour aller plus loin, voici quelques ressources supplémentaires (disponible uniquement en anglais):&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;javascript:;&quot; class=&quot;broken-link&quot; rel=&quot;nofollow&quot; data-original-url=&quot;https://mysqlserverteam.com/mysql-8-0-uuid-support/ &quot;&gt;Mysql 8.0: UUID support&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;javascript:;&quot; class=&quot;broken-link&quot; rel=&quot;nofollow&quot; data-original-url=&quot;http://mysqlserverteam.com/storing-uuid-values-in-mysql-tables/ &quot;&gt;Storing UUID Values in MySQL Tables&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.percona.com/blog/2014/12/19/store-uuid-optimized-way/ &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Store UUID in an optimized way&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
                    <pubDate>Mon, 23 Jul 2018 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2018/07/23/la-gestion-des-uuid-dans-mysql.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2018/07/23/la-gestion-des-uuid-dans-mysql.html</guid>
                </item>
            
        
            
                132
                <item>
                    <title>Pourquoi ? Donner un sens à notre travail</title>
                    <description>&lt;p&gt;Depuis quelques jours, j’ai démarré la lecture de livre &lt;a href=&quot;https://www.amazon.fr/Commencer-par-Pourquoi-Comment-inspirent/dp/2924412684/ref=sr_1_1?ie=UTF8&amp;amp;qid=1531808698&amp;amp;sr=8-1&amp;amp;keywords=commencer+par+pourquoi+simon+sinek &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Commencer par pourquoi&lt;/a&gt;, la traduction française de &lt;a href=&quot;https://www.amazon.fr/Start-Why-Leaders-Inspire-Everyone/dp/1591846447/ref=sr_1_1?ie=UTF8&amp;amp;qid=1531808705&amp;amp;sr=8-1&amp;amp;keywords=start+with+why &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Start with why&lt;/a&gt; de Simon SINEK. Dans la préface du livre, l’auteur y écrit la chose suivante :&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Je n’avais pas un poste désagréable, ce n’était pas un mauvais job en soi. Je n’éprouvais tout simplement plus de plaisir à le faire. Pourtant et d’après toutes les statistiques, j’aurais dû être heureux. Je gagnais bien ma vie, je travaillais avec des clients et des collégues intéressants. Le problème était que je n’y trouvais plus de sens. Je ne m’épanouissais plus dans mon travail.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;En lisant ces mots, je réalise pourquoi j’ai changé 2 fois de société durant la première moitié de cette année. Donner un sens aux choses, à nos actions, à notre vie, voila ce qui nous motive, nous fait avancer et nous dépasser.&lt;/p&gt;
</description>
                    <pubDate>Tue, 17 Jul 2018 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2018/07/17/pourquoi-donner-un-sens-a-notre-travail.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2018/07/17/pourquoi-donner-un-sens-a-notre-travail.html</guid>
                </item>
            
        
            
                133
                <item>
                    <title>Cloner des projets privés dans un runner Gitlab CI</title>
                    <description>&lt;p&gt;Gitlab n’est aujourd’hui plus un simple outil permettant d’héberger son code sur Git au travers d’une interface agréable. Non, Gitlab est aujourd’hui un environnement extrêmement complet et orienté vers une culture DevOps tout en un. La plateforme permet aujourd’hui de stocker son code, gérer des projets, faire de l’intégration et du déploiement continu (incluant un outil de monitoring) au sein d’un outil unique.&lt;/p&gt;

&lt;p&gt;C’est pour toutes ces raisons que nous utilisons Gitlab au quotidien chez &lt;a href=&quot;https://opera-energie.com/ &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Opéra Energie&lt;/a&gt;. Pour éviter d’avoir à gérer notre infrastructure, nous utilisons Gitlab et les outils associés en mode SaaS. Nous travaillons sur une plateforme ayant une architecture orientée services et où chacun de ces derniers est isolés au sein de son propre dépôt de code et est testé via un pipeline Gitlab CI.&lt;/p&gt;

&lt;p&gt;Il arrive que certains projets requièrent des dépendances externes pouvant être gérés dans des dépôts privés. Dans ce cas-là, il est nécessaire que le runner Gitlab CI ait accès à ce dernier pour pouvoir cloner le projet en question. Comme je l’ai signalé précédemment, nous utilisons Gitlab (et Gitlab CI) en mode SaaS et il nous ait donc impossible d’accéder aux serveurs des runners pour y déposer la clé SSH permettant de récupérer les projets privés (sans compter que nous exécutons toutes les actions des runners au sein de conteneurs Docker).&lt;/p&gt;

&lt;p&gt;Afin de contourner ce problème, nous injectons dynamiquement la configuration nécessaire durant l’exécution de notre pipeline Gitlab CI. Pour cela, nous avons commencé à créer une clé SSH uniquement dédiée à une utilisation des dépôts au sein des runners de Gitlab. La clé privée est alors utilisée en tant que &lt;a href=&quot;https://gitlab.com/help/ssh/README.md#deploy-keys &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Deploy Key&lt;/a&gt; des projets privés dont nous avons besoin.&lt;/p&gt;

&lt;p&gt;Une fois configuré, nous allons ajouter la clé privée comme une &lt;a href=&quot;https://gitlab.com/help/ci/variables/README.md#variables &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;variable d’environnement des runners&lt;/a&gt; et qui sera dynamiquement injectée dans le runner par Gitlab au moment de l’exécution de notre pipeline. Il ne reste ensuite plus qu’à utiliser notre clé SSH lors de l’exécution de nos différentes tâches :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;image: alpine:3.6

before_script:
    - apk add --update git openssh-client
    - mkdir -p ~/.ssh
    - echo -e &amp;quot;Host *\n\tStrictHostKeyChecking no\n\n&amp;quot; &amp;gt; ~/.ssh/config
    - eval $(ssh-agent -s)
    - echo &amp;quot;$SECRET_KEY&amp;quot; | ssh-add -

test:unit:
    script:
        - ./run-unit-test.sh

test:functional:
    script:
        - ./run-functional-test.sh&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

</description>
                    <pubDate>Fri, 13 Jul 2018 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2018/07/13/cloner-des-projets-prives-dans-un-runner-gitlab.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2018/07/13/cloner-des-projets-prives-dans-un-runner-gitlab.html</guid>
                </item>
            
        
            
                134
                <item>
                    <title>Réalisez vos benchmarks de code PHP avec PHPBench</title>
                    <description>&lt;p&gt;Pour mes besoins personnels, je souhaitais tester du code PHP et obtenir certaines données sur l’exécution de ce dernier (temps d’exécution, mémoire consommée, …) sans pour autant sortir l’&lt;a href=&quot;/blog/2018/05/09/les-outils-de-profiling-php-open-source.html&quot;&gt;artillerie lourde&lt;/a&gt;. En effectuant quelques recherches, j’ai alors découvert &lt;a href=&quot;https://github.com/phpbench/phpbench &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;PHPBench&lt;/a&gt; qui comme son nom l’indique est un framework de benchmark PHP.&lt;/p&gt;

&lt;p&gt;PHPBench permet de réaliser simplement des benchmarks sur du code PHP. Il permet de standardiser l’écriture du code et génère des rapports permettant de comparer différents cas d’utilisation.&lt;/p&gt;

&lt;center&gt;&lt;img src=&quot;/img/blog/20180514-realisez-vos-benchmarks-de-code-php-avec-phpbench/phpbench-report.png &quot; style=&quot;width: 100%; height: 100%;&quot; /&gt;&lt;/center&gt;

&lt;p&gt;Le framework vous propose d’écrire vos benchmarks un peu comme vous écririez un test PHPUnit. Vous allez ainsi commencer par écrire une classe contenant le code que vous souhaitez mesurer.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&amp;lt;?php

namespace Acme;

class TimeConsumer
{
    public function consume()
    {
        usleep(100);
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Puis le code permettant d’effectuer la mesure:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&amp;lt;?php

use Acme\TimeConsumer;

class TimeConsumerBench
{
    public function benchConsume()
    {
       $consumer = new TimeConsumer();
       $consumer-&amp;gt;consume();
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Il ne restera plus qu’à exécuter le benchmark au travers de la commande : &lt;code&gt;vendor/bin/phpbench run benchmarks --report=default&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Pour plus d’informations et pour découvrir les nombreuses fonctionnalités offertes par l’outil, consultez la &lt;a href=&quot;http://phpbench.readthedocs.io &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;documentation officielle&lt;/a&gt;.&lt;/p&gt;
</description>
                    <pubDate>Mon, 14 May 2018 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2018/05/14/realisez-vos-benchmarks-de-code-php-avec-phpbench.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2018/05/14/realisez-vos-benchmarks-de-code-php-avec-phpbench.html</guid>
                </item>
            
        
            
                135
                <item>
                    <title>Améliorez vos applications avec l&apos;analyse statique de code</title>
                    <description>&lt;p&gt;Pour corriger des problèmes sur un projet, il est primordial de détecter ces derniers le plus rapidement possible. Effectivement, au plus tôt un problème est détecté et au moins il sera coûteux de résoudre ce dernier. Il est pour cela possible d’avoir recours à de l’analyse statique. Il s’agit d’une opération permettant de détecter automatiquement des erreurs de programmation sans avoir à exécuter de code. Les outils utilisés vont analyser le code source afin de trouver d’éventuelles erreurs et rechercher des modèles de code reconnu comme étant à risque.&lt;/p&gt;

&lt;div class=&quot;alert alert-notice&quot;&gt;
    Ce billet a été originalement publié sur &lt;a href=&quot;https://www.novaway.fr/blog/tech/ameliorez-vos-applications-avec-l-analyse-statique-de-code &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;le blog de la société Novaway&lt;/a&gt; dans le cadre de mon travail.
&lt;/div&gt;

&lt;p&gt;Dans sa forme la plus simple, le mode CLI de PHP propose une option permettant d’analyser un fichier PHP. Il ne s’agit en réalité pas d’une analyse statique, mais plutôt d’une analyse syntaxique (l’opération qui va vérifier que le programme ne contient pas d’erreur de syntaxe). Néanmoins, cela permet d’avoir un premier retour sur le code écrit. Pour effectuer cette opération, il faut utiliser l’argument “-l” de l’interpréteur.&lt;/p&gt;

&lt;center&gt;&lt;img src=&quot;/img/blog/20180511-ameliorez-vos-applications-avec-l-analyse-statique-de-code/php-lint.png &quot; /&gt;&lt;/center&gt;

&lt;h2 id=&quot;evaluer-lenvergure-dun-projet&quot;&gt;Evaluer l’envergure d’un projet&lt;/h2&gt;

&lt;p&gt;Obtenir des informations quantifiées sur la taille d’un projet est une opération qui peut s’avérer particulièrement intéressante lors de la prise en main d’un nouveau projet. Un suivi des données recueilli peut également être utile pour mesurer la façon dont le code grossi.&lt;/p&gt;

&lt;p&gt;Dans ce cas, &lt;a href=&quot;https://github.com/sebastianbergmann/phploc &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;PHPLoc&lt;/a&gt; est l’outil idéal pour effectuer cette analyse. Ce dernier permet de mesurer rapidement la taille d’un projet PHP et d’en analyser la structure pour ensuite visualiser les résultats sous la forme de statistiques. Cet outil est particulièrement intéressant lors de la prise en main d’un nouveau projet car il permet de se rendre compte de l’envergure de ce dernier.&lt;/p&gt;

&lt;center&gt;&lt;img src=&quot;/img/blog/20180511-ameliorez-vos-applications-avec-l-analyse-statique-de-code/phploc-size.png &quot; /&gt;&lt;/center&gt;

&lt;p&gt;Les informations fournies par PHPLoc sont exhaustives. Il est possible de connaître le nombre de fichiers composant le projet, le nombre de lignes de code et de commentaires ainsi que le nombre de classes et de méthodes existantes (découpés en fonction de leurs visibilités).&lt;/p&gt;

&lt;p&gt;En plus de ces données, PHPLoc fournit des indicateurs sur les dépendances décrites dans le code (utilisation de variables globales, accès direct à des attributs de classes ou l’utilisation de méthodes statiques). Il fournira également quelques informations sur la complexité cyclomatique du projet.&lt;/p&gt;

&lt;h2 id=&quot;analyser-la-complexité-du-code-dun-projet&quot;&gt;Analyser la complexité du code d’un projet&lt;/h2&gt;

&lt;p&gt;Il est primordial pour la pérennité d’un projet de pouvoir mesurer la complexité de sa base de code. Cette mesure permet d’avoir des informations sur la facilité de maintenabilité du projet. Des outils d’analyse permettent ainsi de détecter des éventuels problèmes au travers d’un ensemble de règles prédéfinies.&lt;/p&gt;

&lt;p&gt;Dans ce sens, &lt;a href=&quot;https://phpmd.org &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;PHP Mess Detector&lt;/a&gt; (communément appelé PHPMD) vous aidera à identifier les fonctions, méthodes, paramètres ou variables inutilisés. Mais aussi de mettre en évidence des séquences de codes complexes ou des bugs potentiels. Au travers de ces différentes analyses PHPMD sera également capable de proposer des optimisations pour certaines portions de votre projet.&lt;/p&gt;

&lt;p&gt;Dans le même esprit, &lt;a href=&quot;https://pdepend.org &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;PHP Depend&lt;/a&gt; (qui est le portage PHP de JDepend) est une application d’analyse de code et de mesure de la qualité. Les instructions de l’application sont examinées afin de déterminer l’extensibilité, la facilité de réutilisation et de maintenance de la base de code.&lt;/p&gt;

&lt;center&gt;&lt;img src=&quot;/img/blog/20180511-ameliorez-vos-applications-avec-l-analyse-statique-de-code/phpdepend.png &quot; /&gt;&lt;/center&gt;

&lt;p&gt;Avec l’arrivée de PHP 7, de nouveaux outils ont également fait leur apparition. C’est le cas de Phan. Développé par Etsy, &lt;a href=&quot;https://github.com/etsy/phan &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Phan&lt;/a&gt; est un analyseur de code statique qui tente de minimiser au maximum les faux positifs. L’outil va se focaliser sur les problèmes les plus fréquents en détectant par exemple les types des paramètres passés aux fonctions et vérifier que ces derniers sont correctement définis.&lt;/p&gt;

&lt;h2 id=&quot;respecter-les-standards-de-code&quot;&gt;Respecter les standards de code&lt;/h2&gt;

&lt;p&gt;Les standards de code sont très importants pour le travail en équipe car ils permettent d’uniformiser la manière dont les développeurs écrivent du code et facilite ainsi la lecture, la compréhension et la prise en main du projet.&lt;/p&gt;

&lt;p&gt;Dans ce sens, plusieurs outils sont à la disposition du développeur. Le plus connu et plus ancien est certainement &lt;a href=&quot;https://pear.php.net/package/PHP_CodeSniffer &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;PHP_CodeSniffer&lt;/a&gt;. Il s’agit d’un ensemble de scripts qui vont analyser votre code PHP (mais aussi Javascript et CSS) afin de relever les violations de vos standards. En plus de la détection des erreurs, il sera possible de corriger automatiquement les erreurs détectées.&lt;/p&gt;

&lt;p&gt;Dans le même esprit, il existe également &lt;a href=&quot;https://github.com/FriendsOfPHP/PHP-CS-Fixer &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;PHP CS Fixer&lt;/a&gt;, un outil plus récent que le précédent et bénéficiant d’une gestion de la configuration plus « actuelle » afin d’éviter d’écrire de la configuration au format XML.&lt;/p&gt;

&lt;h2 id=&quot;tester-la-compatibilité-avec-une-version-de-php&quot;&gt;Tester la compatibilité avec une version de PHP&lt;/h2&gt;

&lt;p&gt;Proposé peu avant l’arrivée de PHP 7, &lt;a href=&quot;https://github.com/sstalle/php7cc &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;php7cc&lt;/a&gt; est l’analyseur de code statique qui vous permettra de faciliter la migration de vos projets PHP 5 vers PHP 7. Une analyse de php7cc vous permettra de détecter les portions de votre code qui ne sont pas compatibles avec les dernières versions du langage et facilitera ainsi la transition vers la dernière branche majeure du langage.&lt;/p&gt;

&lt;p&gt;Il existe également une solution nommée &lt;a href=&quot;https://github.com/llaville/php-compat-info &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;PHP CompatInfo&lt;/a&gt; qui est une librairie PHP qui va rechercher la version minimum du langage avec laquelle votre code est compatible.&lt;/p&gt;

&lt;h2 id=&quot;les-solutions-complètes&quot;&gt;Les solutions complètes&lt;/h2&gt;

&lt;p&gt;Il existe également des solutions plus complètes et qui permettent de regrouper l’ensemble des analyses citées précédemment. Ma solution préférée est certainement &lt;a href=&quot;http://www.phpmetrics.org &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;PhpMetrics&lt;/a&gt;, une des solution les plus récentes de ce billet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PhpMetric&lt;/strong&gt;, tout comme les outils précédents, permet d’analyser un projet pour obtenir des métriques sur la complexité et la maintenabilité du code. Le principal élément différenciateur de ce projet est qu’il se focalise sur les critères de qualité et fait un grand travail sur la présentation des résultats afin que ces derniers puissent être compris et interprétés par des développeurs de tout niveau au travers d’une interface agréable.&lt;/p&gt;

&lt;center&gt;&lt;img src=&quot;/img/blog/20180511-ameliorez-vos-applications-avec-l-analyse-statique-de-code/phpmetrics.png &quot; /&gt;&lt;/center&gt;

&lt;p&gt;Un &lt;a href=&quot;http://www.phpmetrics.org/report/latest/ &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;rapport de démo&lt;/a&gt; est disponible sur le site de l’outil afin de se faire une idée des capacités de ce dernier.&lt;/p&gt;

&lt;p&gt;Notons également la présence de nombreuses autres solutions telles que SonarQube qui contrairement à l’ensemble des outils présentés jusqu’à maintenant, est un outil multilangage. Ce dernier est entre autres compatibles avec les langages tels que Java, C, C++, Objective-C, JavaScript, Python… Disponible dans une version open source, SonarSource l’éditeur de la solution propose également une version commerciale disponible en SaaS avec un support technique et proposant des analyses pour des langages tel que COBOL, Objective-C, Swift.&lt;/p&gt;

&lt;p&gt;Parmi les autres outils existants, on peut également citer :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://scrutinizer-ci.com &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Scrutinizer&lt;/a&gt;, une solution propriétaire massivement utilisée pour analyser la qualité des projets open source&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://insight.sensiolabs.com &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Sensiolabs Insight&lt;/a&gt;, le produit de la société Sensiolabs créatrice du framework Symfony&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.exakat.io &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Exakat&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Voilà qui conclut notre tour d’horizon des outils d’analyse de code statique. Notons que la plupart des outils évoqués s’intègrent très facilement dans les principaux IDE (PHPStorm, Eclipse, …) et éditeur de textes spécialisés (tels que SublimeText, Atom ou VSCode) de base ou au travers de leurs systèmes d’extensions.&lt;/p&gt;

&lt;p&gt;Les solutions d’analyse statique sont nombreuses et il est souvent simple de les mettre en place pour obtenir les premiers rapports et indicateurs qui permettront d’améliorer la qualité d’un projet. Il est important de signaler que les résultats obtenus ne sont intéressants et pertinents que dans le contexte où ils sont mis en place.&lt;/p&gt;

&lt;p&gt;Il est également intéressant de signaler, qu’il ne faut pas prendre les recommandations émises par ces outils comme étant une vérité absolue. Les analyses effectuées doivent être mises en place pour indiquer le chemin à suivre et fournir des indicateurs, elles ne remplacent en aucun cas l’expérience d’un développeur.&lt;/p&gt;
</description>
                    <pubDate>Fri, 11 May 2018 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2018/05/11/ameliorez-vos-applications-avec-l-analyse-statique-de-code.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2018/05/11/ameliorez-vos-applications-avec-l-analyse-statique-de-code.html</guid>
                </item>
            
        
            
                136
                <item>
                    <title>Les outils de profiling PHP open source</title>
                    <description>&lt;p&gt;L’activité de profiling consiste à collecter un certain nombre d’informations sur l’exécution d’un code PHP. Une telle opération est effectuée lorsque l’on souhaite par exemple analyser le code d’un projet pour améliorer sa scalabilité ou encore optimiser ce dernier pour corriger un problème de performance applicative.&lt;/p&gt;

&lt;center&gt;&lt;img src=&quot;/img/blog/20180509-les-outils-de-profiling-php-open-source/xhprof.jpg &quot; /&gt;&lt;/center&gt;

&lt;div class=&quot;alert alert-notice&quot;&gt;
    Ce billet a été originalement publié sur &lt;a href=&quot;https://www.novaway.fr/blog/tech/les-outils-de-profiling-php-open-source-en-2017 &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;le blog de la société Novaway&lt;/a&gt; dans le cadre de mon travail.
&lt;/div&gt;

&lt;p&gt;Lorsque Facebook a rendu public &lt;a href=&quot;https://pecl.php.net/package/xhprof &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;XHProf&lt;/a&gt;, son outil de profiling dédié à PHP en mars 2009, cela a été une petite révolution pour la communauté. Effectivement avant cet outil, il était nécessaire d’utiliser &lt;a href=&quot;https://xdebug.org &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;XDebug&lt;/a&gt;, mais ce dernier n’est pas recommandé pour un usage en production car l’activation du mode de profiling entraîne de gros ralentissement de l’application.&lt;/p&gt;

&lt;p&gt;Contrairement à XDebug, XHProf a été conçu pour pouvoir être utilisé en production et l’impact sur les performances de l’application est minimal. Un autre avantage et qu’il est possible de profiler uniquement une portion de code. Il sera donc possible de récupérer des données telles que la consommation CPU, la consommation mémoire, le nombre d’appels et le temps passé dans chaque fonction. L’analyse de ces données est facilité par le fait que XHProf fournit l’ensemble des scripts nécessaires à cette opération.&lt;/p&gt;

&lt;center&gt;&lt;img src=&quot;/img/blog/20180509-les-outils-de-profiling-php-open-source/xhprof-gui-visualisation.png &quot; style=&quot;width: 100%; height: 100%;&quot; /&gt;&lt;/center&gt;

&lt;p&gt;Malheureusement, Facebook se concentre désormais sur le développement de sa machine virtuelle &lt;a href=&quot;http://hhvm.com &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;HHVM&lt;/a&gt; qui intègre nativement XHProf. De ce fait l’extension PHP n’est actuellement plus maintenue. Si l’extension reste compatible avec les versions 5.x de PHP, il n’est pas possible de l’utiliser avec les versions 7 du fait de changement interne dans la structure du moteur. On a alors vu ces dernières années se développer des solutions de profiling commerciales. La plus en vogue est certainement &lt;a href=&quot;https://blackfire.io &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Blackfire&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Pour remédier à ce problème, les développeurs de la société Tideways ont décidé de forker le projet XHProf et de créer leur propre extension PHP. Cette dernière propose des fonctionnalités équivalentes à XHProf, mais est compatible avec toutes les versions de PHP depuis la version 5.3. Elle est également proposée en open source et disponible sur un dépôt &lt;a href=&quot;https://github.com/tideways/php-profiler-extension &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Github&lt;/a&gt;.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&amp;lt;?php

include __DIR__ . &amp;#39;/common.php&amp;#39;;
include __DIR__ . &amp;#39;/tideways_symfony.php&amp;#39;;

tideways_enable();

$kernel = new \Symfony\Component\HttpKernel\Kernel();
$kernel-&amp;gt;boot();

$httpKernel = new \Symfony\Component\HttpKernel\HttpKernel();
$httpKernel-&amp;gt;handle(&amp;#39;indexAction&amp;#39;);
$httpKernel-&amp;gt;handle(&amp;#39;helloAction&amp;#39;);

print_spans(tideways_get_spans());

tideways_disable();&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Mais le profiling avec l’extension Tideways ne s’arrête pas à un simple fork iso-fonctionnel de XHProf, puisque les développeurs ont également ajouté des fonctionnalités supplémentaires telles que notamment la détection du framework exécutant le code “profilé” afin d’ajouter des informations essentielles et de diminuer “le bruit généré” par le code du framework. À l’heure de l’écriture de ce billet, l’extension supporte 13 frameworks.&lt;/p&gt;

&lt;p&gt;L’extension fait maintenant partie du cœur de la solution “Tideways Profiler Platform” une solution commerciale SaaS proposée par la société. Cette partie commerciale n’est en rien obligatoire, mais apporte notamment une interface de visualisation des informations remontées par l’application ainsi qu’un stockage de ces dernières directement sur les serveurs de la société.&lt;/p&gt;

&lt;p&gt;Au final, les solutions de profiling open source sont rares, mais existantes. Bien que XHProf ne soit aujourd’hui plus maintenu par Facebook pour PHP, l’extension fournie par Tideways est une excellente alternative aux diverses solutions commerciales existantes. Il sera cependant nécessaire de passer par une phase de configuration et de trouver l’outil nécessaire pour traiter les données de profiling. Cela reste certainement la meilleure solution pour des besoins ponctuels. Si par contre vous souhaitez avoir une solution fonctionnelle rapidement et avec un minimum d’effort, vous pourrez vous tourner vers les nombreuses solutions SaaS existantes.&lt;/p&gt;

&lt;p&gt;D’ailleurs si vous souhaitez tester le profiling avec Tideways, n’hésitez pas à télécharger les &lt;a href=&quot;https://github.com/novaway/tideways-profiler-stubs &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;stubs de l’extension&lt;/a&gt; afin d’avoir une auto-complétion dans votre IDE préféré.&lt;/p&gt;
</description>
                    <pubDate>Wed, 09 May 2018 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2018/05/09/les-outils-de-profiling-php-open-source.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2018/05/09/les-outils-de-profiling-php-open-source.html</guid>
                </item>
            
        
            
                137
                <item>
                    <title>Tutorial Jobeet pour Symfony 4 - Partie 6: Aller plus loin avec le modèle</title>
                    <description>&lt;p&gt;Notre application Jobeet commence à devenir utilisable. Nous savons maintenant créer des pages, les afficher et naviguer entre elles en utilisant le framework Symfony via les différents composants qui sont à notre disposition. Attardons-nous un peu sur la couche &lt;a href=&quot;/blog/2017/09/20/tutorial-jobeet-symfony-4-partie-3a-le-modele-de-donnees.html&quot;&gt;modèle&lt;/a&gt; de notre projet.&lt;/p&gt;

&lt;p&gt;Cette dernière est actuellement composée de nos entités (les classes qui représentent les données stockées en base). Nous allons dans ce chapitre, travailler sur l’optimisation de notre code, ce qui vous permettra d’en apprendre un peu plus sur le sujet.&lt;/p&gt;

&lt;p&gt;Revenons sur nos différents &lt;a href=&quot;/blog/2017/09/19/tutorial-jobeet-symfony-4-partie-2-le-projet.html&quot;&gt;scénarios&lt;/a&gt; et plus précisément sur le scénario &lt;em&gt;F1&lt;/em&gt;: &lt;code&gt;En tant qu&apos;utilisateur, je vois les dernières offres actives sur la page d&apos;accueil&lt;/code&gt;. Car si vous avez bien suivi ce que nous avons réalisé, la page d’accueil liste actuellement toutes les offres d’emploi aussi bien celles qui sont actives que celles qui ne le sont pas.&lt;/p&gt;

&lt;p&gt;Un emploi est considéré actif s’il a été posté il y a moins de 30 jours. Commençons par modifier la requête effectuée dans la méthode &lt;code&gt;JobController::index&lt;/code&gt; :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&amp;lt;?php // src/Controller/JobController.php

namespace App\Controller;

// ...
use DateTime;

class JobController extends AbstractController
{
    public function index(EntityManagerInterface $em): Response
    {
        $queryBuilder = $em-&amp;gt;getRepository(Job::class)-&amp;gt;createQueryBuilder(&amp;#39;j&amp;#39;);
        $queryBuilder-&amp;gt;andWhere(&amp;#39;j.createdAt &amp;gt; :date&amp;#39;);
        $queryBuilder-&amp;gt;setParameter(&amp;#39;date&amp;#39;, new DateTime(&amp;#39;-30 day&amp;#39;));
        $jobs = $queryBuilder-&amp;gt;getQuery()-&amp;gt;getResult();

        return $this-&amp;gt;render(&amp;#39;job/index.html.twig&amp;#39;, [
            &amp;#39;jobs&amp;#39; =&amp;gt; $jobs,
        ]);
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;blockquote&gt;
  &lt;p&gt;Pour écrire une requête “complexe”, nous utilisons l’objet &lt;a href=&quot;http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/query-builder.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;QueryBuilder&lt;/code&gt;&lt;/a&gt; fourni par Doctrine et qui permet comme son nom l’indique de créer une requête compréhensible par l’ORM sans devoir écrire de code SQL. L’avantage est que Doctrine adaptera la requête au type de base de données avec lequel il communique (SQLite, MySQL, PostgresSQL, …). L’inconvénient est que cela masque complètement la requête qui est générée.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Notre requête commence à devenir plus complexe et il devient intéressant de l’extraire de notre contrôleur pour que cette dernière puisse être réutilisée sans devoir dupliquer le code. Jusqu’à maintenant, la méthode &lt;code&gt;EntityManager::getRepository&lt;/code&gt; nous permettait d’obtenir un objet générique que Doctrine utilise pour fournir des méthodes de base permettant de faire des requêtes en base de données.&lt;/p&gt;

&lt;p&gt;Nous allons maintenant définir une classe de type &lt;code&gt;Repository&lt;/code&gt;. Les objets de types &lt;code&gt;Repository&lt;/code&gt; contiennent des méthodes permettant de récupérer des données en base. Définissons donc une classe &lt;code&gt;JobRepository&lt;/code&gt;, qui comme son nom l’indique, nous permettra de récupérer des données liées aux offres d’emploi.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&amp;lt;?php // src/Repository/JobRepository.php

namespace App\Repository;

use DateTime;
use Doctrine\ORM\EntityRepository;

class JobRepository extends EntityRepository
{
    public function findActive(DateTime $date)
    {
        return $this-&amp;gt;createQueryBuilder(&amp;#39;j&amp;#39;)
            -&amp;gt;andWhere(&amp;#39;j.createdAt &amp;gt; :date&amp;#39;)
            -&amp;gt;setParameter(&amp;#39;date&amp;#39;, $date)
            -&amp;gt;getQuery()
            -&amp;gt;getResult();
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Une fois notre classe créée, nous allons devoir modifier la configuration du mapping de l’entité &lt;code&gt;Job&lt;/code&gt; pour indiquer à Doctrine la classe que l’ORM devra utiliser pour accéder aux données. Cela se passe dans le fichier &lt;code&gt;config/doctrine/mapping/Job.orm.yml&lt;/code&gt;.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;# config/doctrine/mapping/Job.orm.yml
App\Entity\Job:
    type: entity
    repositoryClass: App\Repository\JobRepository

    # ...&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Pour finir, supprimons le code du contrôleur pour utiliser notre nouvelle classe :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&amp;lt;?php // src/Controller/JobController.php

namespace App\Controller;

// ...

class JobController extends AbstractController
{
    public function index(EntityManagerInterface $em): Response
    {
        $jobs = $em-&amp;gt;getRepository(Job::class)-&amp;gt;findActive(new DateTime(&amp;#39;-30 day&amp;#39;));

        return $this-&amp;gt;render(&amp;#39;job/index.html.twig&amp;#39;, [
            &amp;#39;jobs&amp;#39; =&amp;gt; $jobs,
        ]);
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Et voilà, nous utilisons maintenant une classe pour la récupération des données de notre offre d’emploi, ce qui permet d’isoler le code dédié à la récupération des données et permet ainsi d’améliorer la maintenabilité de notre projet.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Il est possible de consulter la requête générée par Doctrine en consultant les logs générés par l’application. Par défaut, Symfony crée les logs sur la sortie standard et son donc consultable directement sur le terminal.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot; data-lang=&quot;plaintext&quot;&gt;2018-02-10T19:32:20+01:00 [debug] SELECT j0_.id AS id_0, j0_.type AS type_1, j0_.company AS company_2, j0_.logo AS logo_3, j0_.url AS url_4, j0_.position AS position_5, j0_.location AS location_6, j0_.description AS description_7, j0_.how_to_apply AS how_to_apply_8, j0_.token AS token_9, j0_.is_public AS is_public_10, j0_.is_activated AS is_activated_11, j0_.email AS email_12, j0_.expires_at AS expires_at_13, j0_.created_at AS created_at_14, j0_.updated_at AS updated_at_15, j0_.category_id AS category_id_16 FROM job j0_ WHERE j0_.created_at &amp;gt; ?&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Un moyen plus simple d’accéder à ces informations est d’installer le composant &lt;code&gt;symfony/profiler-pack&lt;/code&gt;.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;$ composer require symfony/profiler-pack --dev&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Ce dernier permet la mise en place d’une interface graphique qui affiche un certain nombre d’informations sur votre application. Chaque bundle peut ainsi y afficher des données. Cette interface ajoute une barre permettant d’avoir un résumé des informations disponibles :&lt;/p&gt;

&lt;center&gt;&lt;img src=&quot;/img/blog/20180224-tutorial-jobeet-symfony-4-partie-6-aller-plus-loin-avec-le-modele/profiler-bar.png &quot; style=&quot;width: 100%; height: 100%;&quot; /&gt;&lt;/center&gt;

&lt;p&gt;Il est également possible d’accéder à un détail des informations récupérées en cliquant sur l’icône du composant concerné :&lt;/p&gt;

&lt;center&gt;&lt;img src=&quot;/img/blog/20180224-tutorial-jobeet-symfony-4-partie-6-aller-plus-loin-avec-le-modele/profiler-detail.png &quot; style=&quot;width: 100%; height: 100%;&quot; /&gt;&lt;/center&gt;

&lt;p&gt;Pour l’heure, notre code est encore loin d’être parfait. Pour récupérer toutes les offres actives, nous sommes systématiquement obligés de passer en paramètre la date à partir de laquelle les offres sont visibles. Cela revient à dupliquer le calcul de la date et serait source d’erreurs. Pour corriger ce problème, nous allons créer une constante qui nous permettra de masquer ce calcul. Et pour optimiser notre requête, nous allons utiliser le champ &lt;code&gt;expiresAt&lt;/code&gt; afin de stocker la date d’expiration d’une offre plutôt que de devoir la calculer.&lt;/p&gt;

&lt;p&gt;Tout comme pour les dates de création et de modification de nos offres d’emploi, nous allons utilisons le gestionnaire d’événement de Doctrine pour mettre à jour la valeur du champ automatiquement. Commençons par ajouter le code nécessaire à notre entité :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&amp;lt;?php // src/Entity/Job.php

namespace App\Entity;

// use ...

class Job
{
    public const OFFER_LIFETIME = 30; // durée de vie d&amp;#39;une offre en jours

    // ...

    public function setExpiresAtValue(LifecycleEventArgs $event): self
    {
        // nous remplissons automatiquement la date d&amp;#39;expiration si cette dernière n&amp;#39;a pas été saisie
        // manuellement
        if (!$this-&amp;gt;expiresAt) {
            $this-&amp;gt;expiresAt = new DateTime(&amp;#39;+&amp;#39;.self::OFFER_LIFETIME.&amp;#39; day&amp;#39;);
        }

        return $this;
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;N’oublions pas d’ajouter la configuration liée à cette gestion d’événement.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;# config/doctrine/mapping/Job.orm.yml
App\Entity\Job:
    # ...
    lifecycleCallbacks:
        prePersist: [ setCreatedAtValue, setExpiresAtValue ]&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Nous pouvons maintenant mettre à jour notre requête :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&amp;lt;?php // src/Repository/JobRepository.php

namespace App\Repository;

use DateTime;
use Doctrine\ORM\EntityRepository;

class JobRepository extends EntityRepository
{
    public function findActive()
    {
        return $this-&amp;gt;createQueryBuilder(&amp;#39;j&amp;#39;)
            -&amp;gt;andWhere(&amp;#39;j.expiresAt &amp;gt;= :date&amp;#39;)
            -&amp;gt;setParameter(&amp;#39;date&amp;#39;, new DateTime())
            -&amp;gt;getQuery()
            -&amp;gt;getResult();
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;blockquote&gt;
  &lt;p&gt;N’oubliez pas d’enlever le paramètre dans l’appel de la méthode dans la classe &lt;code&gt;JobController::index&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Le code de notre application est maintenant plus simple et plus maintenable, mais si nous voulons pouvoir tester que tout fonctionne correctement, encore faut-il mettre à jour nos données de test. Car dans les données actuelles, nous avons défini une date d’expiration des offres au 10/10/2012 et nous ne voyons donc maintenant plus aucune offre. Supprimons les dates d’expiration de notre jeu actuel et ajoutons une offre expirée (&lt;a href=&quot;https://github.com/jdecool/jobeet/blob/06-modele/src/DataFixtures/JobFixtures.php &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;consultez directement ce fichier pour avoir le code correspondant&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;En rechargeant les fixtures au travers de la commande &lt;code&gt;bin/console doctrine:fixtures:load&lt;/code&gt;, l’affichage ne devrait pas avoir changé, mais si vous regardez les données en base, vous constaterez qu’il y a pourtant bien 3 offres d’emploi enregistrées.&lt;/p&gt;

&lt;p&gt;Si nous revenons à nos &lt;a href=&quot;/blog/2017/09/19/tutorial-jobeet-symfony-4-partie-2-le-projet.html&quot;&gt;scénarios utilisateurs&lt;/a&gt;, nous avons spécifié que les offres devaient être classées par catégories, ce qui n’est actuellement pas le cas. Pour répondre à ce besoin, nous allons créer une classe de type &lt;code&gt;Repository&lt;/code&gt; pour notre entité &lt;code&gt;Category&lt;/code&gt;. Cette dernière nous permettra de lister les catégories existantes avec les offres d’emploi correspondantes.&lt;/p&gt;

&lt;p&gt;Commençons par modifier le mapping Doctrine :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;# config/doctrine/mapping/Job.orm.yml
App\Entity\Category:
    type: entity
    repositoryClass: App\Repository\CategoryRepository

    # ...&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Créons maintenant la classe correspondante avec la nouvelle méthode de récupération des offres par catégorie :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&amp;lt;?php // src/Entity/CategoryRepository.php

declare(strict_types=1);

namespace App\Repository;

use DateTime;
use Doctrine\ORM\EntityRepository;

class CategoryRepository extends EntityRepository
{
    public function findCategoriesWithJobs()
    {
        return $this-&amp;gt;createQueryBuilder(&amp;#39;c&amp;#39;)
            -&amp;gt;join(&amp;#39;c.jobs&amp;#39;, &amp;#39;j&amp;#39;)
            -&amp;gt;where(&amp;#39;j.expiresAt &amp;gt;= :date&amp;#39;)
            -&amp;gt;setParameter(&amp;#39;date&amp;#39;, new DateTime())
            -&amp;gt;getQuery()
            -&amp;gt;getResult();
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;blockquote&gt;
  &lt;p&gt;La requête Doctrine a été construite via le &lt;code&gt;QueryBuilder&lt;/code&gt;. Doctrine implémente également son propre langage de requête appelé DQL (dérivé du SQL). Le &lt;code&gt;QueryBuilder&lt;/code&gt; tout comme le &lt;code&gt;DQL&lt;/code&gt; se base sur nos entités pour construire les requêtes effectuées en base de données, cela permet ensuite à Doctrine de créer les objets correspondants.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Utilisons maintenant cette dernière dans l’affichage de notre homepage :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&amp;lt;?php // src/Controller/JobController.php

namespace App\Controller;

// ...

class JobController extends AbstractController
{
    public function index(EntityManagerInterface $em): Response
    {
        $categories = $em-&amp;gt;getRepository(Category::class)-&amp;gt;findCategoriesWithJobs();

        $jobsCategories = [];
        foreach ($categories as $category) {
            $jobsCategories[$category-&amp;gt;getName()] = $em-&amp;gt;getRepository(Job::class)-&amp;gt;findActiveByCategory($category);
        }

        return $this-&amp;gt;render(&amp;#39;job/index.html.twig&amp;#39;, [
            &amp;#39;categories&amp;#39; =&amp;gt; $jobsCategories,
        ]);
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Ajoutons la méthode permettant de récupérer les offres actives d’une catégorie :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&amp;lt;?php // src/Entity/JobRepository.php

declare(strict_types=1);

namespace App\Repository;

use DateTime;
use Doctrine\ORM\EntityRepository;

class JobRepository extends EntityRepository
{
    public function findActiveByCategory(Category $category)
    {
        return $this-&amp;gt;createQueryBuilder(&amp;#39;j&amp;#39;)
            -&amp;gt;where(&amp;#39;j.category = :category&amp;#39;)
            -&amp;gt;andWhere(&amp;#39;j.expiresAt &amp;gt;= :date&amp;#39;)
            -&amp;gt;setParameter(&amp;#39;category&amp;#39;, $category)
            -&amp;gt;setParameter(&amp;#39;date&amp;#39;, new DateTime())
            -&amp;gt;getQuery()
            -&amp;gt;getResult();
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Modifions ensuite le template en conséquence :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;{# templates/job/index.html #}
{% extends &amp;quot;base.html.twig&amp;quot; %}

{% block body %}
    &amp;lt;h1 class=&amp;quot;my-4&amp;quot;&amp;gt;Liste des offres&amp;lt;/h1&amp;gt;

    {% for category, jobs in categories %}
        &amp;lt;div class=&amp;quot;row&amp;quot;&amp;gt;
            &amp;lt;h2 style=&amp;quot;font-weight: bold; margin: 2rem 0;&amp;quot;&amp;gt;{{ category }}&amp;lt;/h2&amp;gt;

            {% for job in jobs %}
                &amp;lt;div class=&amp;quot;row&amp;quot;&amp;gt;
                    &amp;lt;div class=&amp;quot;col-md-7&amp;quot;&amp;gt;
                        &amp;lt;a href=&amp;quot;#&amp;quot;&amp;gt;
                            &amp;lt;img class=&amp;quot;img-fluid rounded mb-3 mb-md-0&amp;quot; src=&amp;quot;{{ asset(&amp;#39;images/&amp;#39; ~ job.logo) }}&amp;quot; alt=&amp;quot;{{ job.company }}&amp;quot;&amp;gt;
                        &amp;lt;/a&amp;gt;
                    &amp;lt;/div&amp;gt;
                    &amp;lt;div class=&amp;quot;col-md-5&amp;quot;&amp;gt;
                        &amp;lt;h3&amp;gt;{{ job.position }}&amp;lt;/h3&amp;gt;
                        &amp;lt;p&amp;gt;{{ job.description }}&amp;lt;/p&amp;gt;
                        &amp;lt;p&amp;gt;Posted on {{ job.createdAt|date(&amp;quot;m/d/Y&amp;quot;) }}&amp;lt;/p&amp;gt;
                        &amp;lt;a class=&amp;quot;btn btn-primary&amp;quot; href=&amp;quot;{{ path(&amp;#39;job_show&amp;#39;, { &amp;#39;id&amp;#39;: job.id, &amp;#39;company&amp;#39;: job.companySlug, &amp;#39;location&amp;#39;: job.locationSlug, &amp;#39;position&amp;#39;: job.positionSlug }) }}&amp;quot;&amp;gt;See more&amp;lt;/a&amp;gt;
                    &amp;lt;/div&amp;gt;
                &amp;lt;/div&amp;gt;

                &amp;lt;hr&amp;gt;
            {% endfor %}
        &amp;lt;/div&amp;gt;
    {% endfor %}

    &amp;lt;ul class=&amp;quot;pagination justify-content-center&amp;quot;&amp;gt;
        &amp;lt;li class=&amp;quot;page-item disabled&amp;quot;&amp;gt;
            &amp;lt;a class=&amp;quot;page-link&amp;quot; href=&amp;quot;#&amp;quot; aria-label=&amp;quot;Previous&amp;quot;&amp;gt;
                &amp;lt;span aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;amp;laquo;&amp;lt;/span&amp;gt;
                &amp;lt;span class=&amp;quot;sr-only&amp;quot;&amp;gt;Previous&amp;lt;/span&amp;gt;
            &amp;lt;/a&amp;gt;
        &amp;lt;/li&amp;gt;
        &amp;lt;li class=&amp;quot;page-item&amp;quot;&amp;gt;
            &amp;lt;a class=&amp;quot;page-link&amp;quot; href=&amp;quot;#&amp;quot;&amp;gt;1&amp;lt;/a&amp;gt;
        &amp;lt;/li&amp;gt;
        &amp;lt;li class=&amp;quot;page-item disabled&amp;quot;&amp;gt;
            &amp;lt;a class=&amp;quot;page-link&amp;quot; href=&amp;quot;#&amp;quot; aria-label=&amp;quot;Next&amp;quot;&amp;gt;
                &amp;lt;span aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;amp;raquo;&amp;lt;/span&amp;gt;
                &amp;lt;span class=&amp;quot;sr-only&amp;quot;&amp;gt;Next&amp;lt;/span&amp;gt;
            &amp;lt;/a&amp;gt;
        &amp;lt;/li&amp;gt;
    &amp;lt;/ul&amp;gt;
{% endblock %}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Pour finir, nous allons sécuriser la page de consultation des offres. Effectivement, si vous connaissez l’URL d’une offre, il est possible d’accéder à cette dernière, et ce, même si la date d’expiration est dépassée. Pour cela, rajouter un contrôle dans notre action d’affichage :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&amp;lt;?php // src/Controller/JobController.php

namespace App\Controller;

// ...
use DateTime;

class JobController extends AbstractController
{
    public function show(EntityManagerInterface $em, int $id, string $company, string $location, string $position) : Response
    {
        // dans un projet réel, il sera nécessaire de faire une requête permettant de vérifier que tous les éléments
        // correspondent à une offre d&amp;#39;emploi valide
        $job = $em-&amp;gt;getRepository(Job::class)-&amp;gt;find($id);
        if (null === $job) {
            throw new NotFoundHttpException();
        }

        $currentDate = new DateTime();
        if ($job-&amp;gt;getExpiresAt() &amp;lt; $currentDate) {
            throw new NotFoundHttpException();
        }

        return $this-&amp;gt;render(&amp;#39;job/show.html.twig&amp;#39;, [
            &amp;#39;job&amp;#39; =&amp;gt; $job,
        ])
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;blockquote&gt;
  &lt;p&gt;Retrouvez tous les tutorials Jobeet disponibles depuis le &lt;a href=&quot;/blog/2017/09/12/tutorial-jobeet-symfony-4-introduction.html&quot;&gt;billet d’introduction de la série&lt;/a&gt;. Le code source de cette application est également disponible sur &lt;a href=&quot;https://github.com/jdecool/jobeet/tree/06-modele &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Github&lt;/a&gt;. Vous trouverez une branche associée à l’état du projet après chaque chapitre.&lt;/p&gt;
&lt;/blockquote&gt;
</description>
                    <pubDate>Sat, 24 Feb 2018 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2018/02/24/tutorial-jobeet-symfony-4-partie-6-aller-plus-loin-avec-le-modele.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2018/02/24/tutorial-jobeet-symfony-4-partie-6-aller-plus-loin-avec-le-modele.html</guid>
                </item>
            
        
            
                138
                <item>
                    <title>Que 2018 commence !</title>
                    <description>&lt;p&gt;Oui l’année a commencé depuis presque un mois, mais il n’est jamais trop tard (ni même trop tôt) pour faire un point sur sa vie aussi bien personnelle que professionnelle, car il est important de se fixer des objectifs pour évoluer et progresser. D’ailleurs, je n’avais même pas prévu initialement de faire ce point publiquement, mais &lt;a href=&quot;http://skwi.fr &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Cédric&lt;/a&gt; m’a inspiré ce billet.&lt;/p&gt;

&lt;h2 id=&quot;changement-professionnel&quot;&gt;Changement professionnel&lt;/h2&gt;

&lt;p&gt;Du point de vue professionnel, nouvelle année et nouvelle entreprise. Après 4 ans passés au sein d’une agence Web, je démarre lundi un nouvel emploi au sein &lt;s&gt;d&apos;un éditeur (chez &lt;a href=&quot;http://www.wizaplace.fr &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Wizaplace&lt;/a&gt; pour ne pas les citer)&lt;/s&gt; du service informatique d’&lt;a href=&quot;https://opera-energie.com &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Opéra Energie&lt;/a&gt;, un courtier en énergie. Je réalise que ce type de société me convient plus, le rapport au produit étant sensiblement différent que lorsque l’on est prestataire. Travailler dans une agence Web aura été formateur, mais ne correspondait pas à ce que je recherchais pour m’épanouir et évoluer professionnellement comme je le souhaite.&lt;/p&gt;

&lt;h2 id=&quot;jobeet&quot;&gt;Jobeet&lt;/h2&gt;

&lt;p&gt;En fin d’année dernière, je démarrais &lt;a href=&quot;/blog/2017/09/12/tutorial-jobeet-symfony-4-introduction.html&quot;&gt;l’adaptation du tutorial Jobeet&lt;/a&gt; de Symfony à l’occasion la sortie de la version 4 du framework. J’ai eu beaucoup de retours positifs sur ce dernier et le projet attire de nombreuses personnes. Bien que de nouvelles mises à jour tardent à arriver, je n’ai pas abandonné ce projet. J’espère parvenir à publier les prochains chapitres sous peu. Il est vrai que les dernières semaines étaient chargées !&lt;/p&gt;

&lt;h2 id=&quot;une-année-un-langage&quot;&gt;Une année, un langage&lt;/h2&gt;

&lt;p&gt;D’un point de vue technique, si 2017 a été pour moi l’année de l’apprentissage de &lt;a href=&quot;https://golang.org &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Go&lt;/a&gt;, j’aimerai cette année me pencher un peu plus sur &lt;a href=&quot;https://www.rust-lang.org &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Rust&lt;/a&gt;. Comme toujours l’idée est de découvrir un nouveau langage qui si possible coupe avec PHP, le langage que j’utilise au quotidien. De plus ce dernier étant un langage interprété (&lt;i&gt;pardonnez-moi cet abus de langage&lt;/i&gt;), j’ai plutôt l’envie de découvrir des langages compilés.&lt;/p&gt;

&lt;h2 id=&quot;2018-le-retour-aux-bases&quot;&gt;2018, le retour aux bases&lt;/h2&gt;

&lt;p&gt;Cette année, je voudrais également en profiter pour revenir aux bases de la programmation: l’algorithmie et les structures de données. PHP est un langage puissant qui s’est nettement amélioré ces dernières années, mais la principale structure de données que l’on utilise (à l’exception des classes) sont les tableaux.&lt;/p&gt;

&lt;p&gt;Mais un tableau PHP n’est pas un simple tableau au sens structure de données comme on peut l’entendre. C’est en réalité une structure de données beaucoup plus complexe qui mélange : une liste doublement chaînée, une &lt;code&gt;map&lt;/code&gt; et qui peut être utilisé comme une pile ou un tas.&lt;/p&gt;

&lt;p&gt;J’ai envie de me replonger dans les structures de données telles que les listes chaînées, les piles, les tas, les graphs et revoir les différents algorithmes qui en découlent ainsi que leurs applications. Cela devrait me permettre d’utiliser des structures pertinentes et  optimisées dans les applications que je pourrais être amené à développer. Ce sera peut-être également l’occasion d’approfondir &lt;a href=&quot;http://php.net/manual/en/book.ds.php &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;PHP DS&lt;/a&gt;, une extension PHP permettant de travailler avec des structures de données de base et optimisées comme alternative aux tableaux.&lt;/p&gt;

&lt;h2 id=&quot;en-bonus&quot;&gt;En bonus&lt;/h2&gt;

&lt;p&gt;Ces derniers temps, je m’intéresse de plus en plus aux systèmes distribués, la montée en charge des applications et aux microservices. Plus je m’y intéresse et outre les problématiques de code, je me rends compte qu’il y a de nombreux challenges de l’ordre de l’opérationnel. J’espère ainsi avoir l’occasion de creuser ces concepts et tomber un peu plus dans la culture &lt;i&gt;devops&lt;/i&gt;.&lt;/p&gt;

&lt;p&gt;Des points pour lesquels j’ai commencé à mettre “les mains dans le cambouis” en démarrant une migration de “mon infrastructure personnelle” (un billet explicatif est prévu sur le sujet #teasing).&lt;/p&gt;

&lt;p&gt;-&amp;gt; Voilà qui devrait m’occuper pour l’année à venir ! Je ne vais pas avoir le temps de m’ennuyer, mais c’est l’une des choses que j’apprécie le plus dans notre métier: évoluer et être en perpétuel apprentissage !&lt;/p&gt;
</description>
                    <pubDate>Fri, 26 Jan 2018 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2018/01/26/que-2018-commence.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2018/01/26/que-2018-commence.html</guid>
                </item>
            
        
            
                139
                <item>
                    <title>Symfony 4 et les tests avec le logger par défaut</title>
                    <description>&lt;p&gt;Symfony 4 est sortie il y a quelques jours. Ce dernier est dorénavant fourni avec un minimum de dépendances. Monolog n’étant plus fourni par défaut, un logger proposant le strict nécessaire est inclus par défaut. Ce dernier, compatible PSR-3, va logger les informations par défaut sur la sortie standard. Ce fonctionnement peut ne pas être sans impact sur vos tests.&lt;/p&gt;

&lt;p&gt;Effectivement, certains frameworks de tests interpréteront les sorties sur la sortie standard comme un défaut de fonctionnement et considérons de ce fait votre test comme échoué.&lt;/p&gt;

&lt;p&gt;Pour cela, il sera peut-être nécessaire dans vos tests de remplacer le logger par défaut par le &lt;code&gt;Psr\Log\NullLogger&lt;/code&gt;.&lt;/p&gt;
</description>
                    <pubDate>Wed, 06 Dec 2017 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2017/12/06/symfony-4-tests-logger-par-defaut.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2017/12/06/symfony-4-tests-logger-par-defaut.html</guid>
                </item>
            
        
            
                140
                <item>
                    <title>Choisir ses dépendances applicatives</title>
                    <description>&lt;p&gt;Il est rare lorsque l’on développe une application d’écrire soi-même (ou une équipe) 100% du code qui fera fonctionner le projet. Nous utilisons et réutilisation de nombreux outils et composants que nous intégrons à notre code pour gagner du temps et éviter de réinventer la roue. Le choix de ces composants ne doit pas être pris à la légère car ils vont faire partie intégrante de notre code. Nous allons réfléchir dans ce billet aux différents aspects à prendre en compte.&lt;/p&gt;

&lt;p&gt;Le premier élément à prendre en compte est le &lt;strong&gt;risque&lt;/strong&gt; lié à la mise en place de la dépendance. Cette dernière a-t-elle une place critique dans le projet ? On ne s’attardera pas, par exemple, sur une librairie de génération d’un UUID, alors que sans aucun doute, une analyse poussée sera réalisée pour choisir le système de gestion de base de données. Effectivement ce dernier, qui servira au stockage des informations traitées par notre application est une pièce maîtresse. Utiliseriez-vous un moteur de base de données totalement inconnu pour un point aussi critique ?&lt;/p&gt;

&lt;p&gt;Déterminer le risque d’utiliser tel ou tel module, vous poussera certainement à faire une analyse plus ou moins approfondie de la &lt;strong&gt;qualité&lt;/strong&gt; du composant que vous allez mettre en place. Dans un domaine où nous utilisons de plus en plus d’outils et modules open source, n’importe quel développeur peut explorer le code pour en vérifier sa robustesse. Ce dernier respecte-t-il les “bonnes pratiques” de développement ? Est-il maintenable ? Possède-t-il des tests unitaires et/ou fonctionnels ? Autant de questions qui permettent de dresser un bilan de la qualité du projet. Une dépendance de qualité permettra de garantir son bon fonctionnement sur le long terme et facilitera d’éventuelles interventions au sein de cette dernière.&lt;/p&gt;

&lt;p&gt;Un autre point à prendre en compte est l’&lt;strong&gt;activité&lt;/strong&gt; autour du composant auquel on s’intéresse. Peut-être s’agit-il d’un projet de qualité, mais dont le développement n’est plus maintenu. Dans ce cas-là que se passe-t-il si vous rencontrez un problème lors de son utilisation ? Est-ce que l’équipe de développement pourra corriger ce dernier ? Si la réponse à cette question est “non”, il sera peut-être nécessaire de travailler sur votre propre version du module ou éventuellement d’en choisir un autre.&lt;/p&gt;

&lt;p&gt;Le dernier point qu’il est intéressant d’analyser est la &lt;strong&gt;communauté&lt;/strong&gt; qui gravite autour de la dépendance étudiée. Plus la communauté sera grande et plus vous pourrez profiter de retours d’expérience, d’aide et de conseils autour des questions et des différentes problématiques que vous pourrez rencontrer. Il faudra sur ce point, faire attention à la notion d’effet de mode. Il peut être préférable dans certains cas d’utiliser des outils éprouvés et pertinents pour l’utilisation que vous souhaitez en avoir. Focalisez-vous sur votre cas d’utilisation et ne choisissez pas spécialement des solutions “passe-partout”.&lt;/p&gt;

&lt;p&gt;Le choix d’un module dépend de nombreux critères qui ne seront pas évalués de la même manière en fonction de l’impact dans le projet. Dans tous les cas, ce n’est pas une décision à prendre à la légère car cette dernière vous suivra durant toute la durée de vie du code de l’application. Pour limiter l’impact d’une dépendance et faciliter son remplacement, il est intéressant d’abstraire le côté technique de son utilisation au sein de classes orientées métier.&lt;/p&gt;
</description>
                    <pubDate>Mon, 04 Dec 2017 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2017/12/04/choisir-ses-dependances-applicatives.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2017/12/04/choisir-ses-dependances-applicatives.html</guid>
                </item>
            
        
            
        
            
                141
                <item>
                    <title>PHP Meminfo, l&apos;extension qui vous fait voyager dans la mémoire PHP</title>
                    <description>&lt;p&gt;Savez-vous vraiment comment est consommé la mémoire de votre application PHP ? Si la réponse est non, alors devriez considérer l’utilisation de &lt;a href=&quot;https://github.com/BitOne/php-meminfo &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;PHP Meminfo&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;PHP Meminfo est une extension PHP vous permettant d’avoir un aperçu de l’utilisation de la mémoire de vos scripts PHP. Cette dernière a été créé dans le but de comprendre l’origine des fuites mémoires de vos applications et de mieux comprendre le comportement de cette dernière.&lt;/p&gt;

&lt;p&gt;L’extension vient tout récemment de sortir sa version 1.0.0 apportant notamment un support de PHP 7 et fournit une documentation plus complète.&lt;/p&gt;

&lt;p&gt;En bonus, si vous êtes sous macOS et que vous utilisez &lt;a href=&quot;https://brew.sh &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;Homebrew&lt;/code&gt;&lt;/a&gt; en tant que gestionnaire de paquet, vous aurez la possibilité d’installer très facilement l’extension sur votre système, &lt;a href=&quot;javascript:;&quot; class=&quot;broken-link&quot; rel=&quot;nofollow&quot; data-original-url=&quot;http://formulae.brew.sh/search/meminfo &quot;&gt;un paquet étant disponible pour chaque version de PHP&lt;/a&gt;.&lt;/p&gt;
</description>
                    <pubDate>Thu, 23 Nov 2017 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2017/11/23/php-meminfo-l-extension-qui-vous-fait-voyager-dans-la-memoire-php.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2017/11/23/php-meminfo-l-extension-qui-vous-fait-voyager-dans-la-memoire-php.html</guid>
                </item>
            
        
            
                142
                <item>
                    <title>Tutorial Jobeet pour Symfony 4 - Partie 5: Les routes</title>
                    <description>&lt;p&gt;Vous connaissez maintenant le principe d’une architecture MVC et comment cette dernière se met en place au sein d’un projet Symfony. Nous avons également rapidement évoqué le principe du routage (ou &lt;strong&gt;routing&lt;/strong&gt; en anglais). Ce chapitre sera entièrement consacré à ce dernier point.&lt;/p&gt;

&lt;p&gt;Dans un contexte web, une URL (&lt;em&gt;U&lt;/em&gt;niform &lt;em&gt;R&lt;/em&gt;esource &lt;em&gt;L&lt;/em&gt;ocator) désigne un contenu accessible. Lorsque vous accédez à une page au travers de son URL, vous demandez au navigateur d’aller cherche un contenu identifié. C’est cette information que nous allons devoir décrire dans notre projet.&lt;/p&gt;

&lt;p&gt;Dans la section consacrée au &lt;a href=&quot;/blog/2017/09/29/tutorial-jobeet-symfony-4-partie-4a-le-controleur-et-la-vue.html&quot;&gt;contrôleur et à la vue&lt;/a&gt;, nous avons commencé à modifier le fichier &lt;code&gt;config/routes.yaml&lt;/code&gt; qui permet de faire le lien entre l’URL courante du navigateur et l’action de notre contrôleur devant être exécutée. Nous ne sommes pas rentré dans les détails, mais une route est définie par un nom, un schéma d’URL ainsi que le contrôleur avec la méthode associée.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;C’est le composant &lt;a href=&quot;http://symfony.com/components/Routing &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;Routing&lt;/code&gt;&lt;/a&gt; de Symfony qui s’occupe d’établir la correspondance entre notre configuration et le code de notre projet. Signalons que si deux routes possèdent le même nom, la seconde route écrase la première. De ce fait la première configuration ne sera jamais prise en compte.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Dans l’état actuel de notre projet, notre fichier &lt;code&gt;config/routes.yaml&lt;/code&gt; contient la configuration suivante :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;# config/routes.yaml
index:
    path: /
    defaults: { _controller: &amp;#39;App\Controller\JobController::index&amp;#39; }

# Depends on sensio/framework-extra-bundle, doctrine/annotations, and doctrine/cache
#   install with composer req sensio/framework-extra-bundle annot
#controllers:
#    resource: ../src/Controller/
#    type: annotation&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Une seule route y est déclarée. Cette dernière correspond à la page d’accueil de notre application qui liste les offres d’emploi disponible. Ajoutons maintenant une route qui permettra d’afficher le détail d’une offre.&lt;/p&gt;

&lt;p&gt;Les offres d’emploi sont créées dynamiquement par un utilisateur. Pour accéder au détail d’une offre, nous allons faire référence à son identifiant unique de base de données (autrement dit, la propriété &lt;code&gt;$id&lt;/code&gt; de notre classe &lt;code&gt;Job&lt;/code&gt;). Nous allons créer une route qui contiendra un paramètre dynamique permettant de récupérer cette information et de la transmettre à notre action.&lt;/p&gt;

&lt;p&gt;Modifions le fichier de configuration en conséquence :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;# config/routes.yaml
index:
    path: /
    defaults: { _controller: &amp;#39;App\Controller\JobController::index&amp;#39; }

job_show:
    path: /job/{id}
    defaults: { _controller: &amp;#39;App\Controller\JobController::show&amp;#39; }

# Depends on sensio/framework-extra-bundle, doctrine/annotations, and doctrine/cache
#   install with composer req sensio/framework-extra-bundle annot
#controllers:
#    resource: ../src/Controller/
#    type: annotation&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Nous venons donc de rajouter une nouvelle route nommée &lt;code&gt;job_show&lt;/code&gt; qui correspond à l’appel de la méthode &lt;code&gt;show&lt;/code&gt; de notre classe &lt;code&gt;JobController&lt;/code&gt;. Le paramètre de la route est indiqué entre crochet (&lt;code&gt;{id}&lt;/code&gt;), ce dernier correspond au nom de la variable qui sera automatiquement passé à la méthode du contrôleur.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Sur notre environnement de développement local, il sera possible d’accéder à une offre d’emploi au travers de l’URL &lt;code&gt;http://localhost:8000/job/1&lt;/code&gt; ou &lt;code&gt;http://localhost:8000/job/2&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ajoutons maintenant l’action correspondante dans notre contrôleur :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&amp;lt;?php // src/Controller/JobController.php

namespace App\Controller;

use App\Entity\Job;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

class JobController extends AbstractController
{
    // ...

    public function show(EntityManagerInterface $em, int $id): Response
    {
        $job = $em-&amp;gt;getRepository(Job::class)-&amp;gt;find($id);
        if (null === $job) {
            throw new NotFoundHttpException();
        }

        return $this-&amp;gt;render(&amp;#39;job/show.html.twig&amp;#39;, [
            &amp;#39;job&amp;#39; =&amp;gt; $job,
        ]);
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;blockquote&gt;
  &lt;p&gt;La vue affichée par cette action ne sera pas détaillée car elle ne présente pas d’intérêt pour le routing. Je vous invite à récupérer le contenu du fichier directement sur &lt;a href=&quot;https://github.com/jdecool/jobeet/tree/05-les-routes/templates/job/show.html.twig &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;le dépôt du projet&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Nous découvrons dans ce contrôleur comment décrire une route contenant un paramètre dynamique. Dans l’exemple précédent, le paramètre &lt;code&gt;$id&lt;/code&gt; est automatiquement extrait de l’URL et transmis à l’action de notre contrôleur. Symfony faisant correspondre le nom du paramètre de notre configuration avec le nom de la variable de présent dans la définition de notre action.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Notons au passage l’utilisation de la méthode &lt;code&gt;find&lt;/code&gt; de Doctrine permettant de récupérer l’objet via sa clé primaire de base de données.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Après avoir effectué notre requête en base de données avec Doctrine, il convient de contrôler que l’identifiant transmis correspond bien à une offre d’emploi. Si ce n’est pas le cas, nous générons une exception afin d’indiquer que l’utilisateur tente d’accéder à une ressource qui n’existe pas.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;La classe &lt;code&gt;Symfony\Component\HttpKernel\Exception\NotFoundHttpException&lt;/code&gt; est une exception fournie par le composant &lt;code&gt;HttpFundation&lt;/code&gt; de Symfony. Elle permet de lancer d’indiquer au framework que l’erreur rencontrée est de type “ressource introuvable”. Le framework générera ainsi automatiquement une réponse renvoyant le code HTTP 404 au navigateur.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Nous avons créé une route qui permet de capter un paramètre correspondant à l’identifiant d’une offre d’emploi. Mais à ce stade, nous n’avons défini aucune restriction sur ce paramètre. Une donnée de type numérique est attendue, mais actuellement rien n’empêche un utilisateur d’entrer une URL du type &lt;code&gt;/job/mon-offre&lt;/code&gt;. Cette adresse est valide mais va provoquer une erreur car nous avons indiqué dans notre action que la paramètre &lt;code&gt;$id&lt;/code&gt; était de type &lt;code&gt;int&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Pour éviter ce problème, il est possible de définir des règles de validations des adresses pouvant être captées par nos routes. Ajoutons donc un prérequis sur le paramètre &lt;code&gt;{id}&lt;/code&gt; afin d’indiquer que ce dernier ne doit prendre en compte que les valeurs numériques entières.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;# config/routes.yaml

# ...
job_show:
    path: /job/{id}
    defaults: { _controller: &amp;#39;App\Controller\JobController::show&amp;#39; }
    requirements:
        id: &amp;#39;\d+&amp;#39;

# ...&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Pour définir une contrainte sur un paramètre de notre URL, nous utilisons le mot-clé &lt;code&gt;requirements&lt;/code&gt; qui permet de définir une liste de prérequis. Dans l’exemple précédent, nous spécifions que le paramètre &lt;code&gt;id&lt;/code&gt; doit être de valeur numérique.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Le format des validateurs est en réalité une expression régulière. Le format raccourci &lt;code&gt;\d+&lt;/code&gt; correspond en réalité à l’expression &lt;code&gt;[0-9]+&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Si nous réfléchissons d’un point de vue SEO, nos URL de type &lt;code&gt;/job/1&lt;/code&gt; ne sont pas très explicites et pourraient pénaliser notre référencement dans les moteurs de recherche. Une URL du type &lt;code&gt;/job/sensio-labs/1/web-developer&lt;/code&gt; serait plus pertinente car elle décrit mieux la ressource à laquelle elle fait référence. Effectuons cette modification :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;# config/routes.yaml

# ...
job_show:
    path: /job/{company}/{location}/{id}/{position}
    defaults: { _controller: &amp;#39;App\Controller\JobController::show&amp;#39; }
    requirements:
        id: &amp;#39;\d+&amp;#39;
        company: &amp;#39;[A-Za-z0-9\-]+&amp;#39;
        location: &amp;#39;[A-Za-z0-9\-]+&amp;#39;
        position: &amp;#39;[A-Za-z0-9\-]+&amp;#39;

# ...&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Et adaptons notre classe &lt;code&gt;JobController&lt;/code&gt; en conséquence :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&amp;lt;?php // src/Controller/JobController.php

namespace App\Controller;

use App\Entity\Job;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

class JobController extends AbstractController
{
    // ...

    public function show(EntityManagerInterface $em, int $id, string $company, string $position, string $location): Response
    {
        // Faire les contrôles sur les variables $company et $position
        // ou les inclures dans la requête SQL
        $job = $em-&amp;gt;getRepository(Job::class)-&amp;gt;find($id);
        if (null === $job) {
            throw new NotFoundHttpException();
        }

        return $this-&amp;gt;render(&amp;#39;job/show.html.twig&amp;#39;, [
            &amp;#39;job&amp;#39; =&amp;gt; $job,
        ]);
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;blockquote&gt;
  &lt;p&gt;Nous n’implémenterons pas les requêtes ni les vérifications, ce n’est pas l’objectif de ce tutorial. Qui se concentre sur l’écriture d’une application avec Symfony.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Maintenant que nous avons ajouté notre page permettant de visualiser le détail d’une offre d’emploi, nous allons devoir mettre à jour les liens permettant à un utilisateur de naviguer au sein de notre application. Pour faciliter la navigation entre les pages dans nos templates Twig, le module &lt;code&gt;TwigBridge&lt;/code&gt; met en place des fonctions permettant de faire référence à une page au travers du nom de la route associée. C’est notamment le cas de la fonction &lt;code&gt;path&lt;/code&gt;. Cette dernière prend en paramètre le nom de la route à laquelle nous allons faire référence ainsi que les différentes variables nécessaires à la génération de l’URL.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&amp;lt;!-- templates/job/index.html.twig --&amp;gt;
{% extends &amp;quot;base.html.twig&amp;quot; %}

{% block body %}
    &amp;lt;h1 class=&amp;quot;my-4&amp;quot;&amp;gt;Liste des offres&amp;lt;/h1&amp;gt;

    {% for job in jobs %}
        &amp;lt;div class=&amp;quot;row&amp;quot;&amp;gt;
            &amp;lt;div class=&amp;quot;col-md-7&amp;quot;&amp;gt;
                &amp;lt;a href=&amp;quot;#&amp;quot;&amp;gt;
                    &amp;lt;img class=&amp;quot;img-fluid rounded mb-3 mb-md-0&amp;quot; src=&amp;quot;{{ asset(&amp;#39;images/&amp;#39; ~ job.logo) }}&amp;quot; alt=&amp;quot;{{ job.company }}&amp;quot;&amp;gt;
                &amp;lt;/a&amp;gt;
            &amp;lt;/div&amp;gt;
            &amp;lt;div class=&amp;quot;col-md-5&amp;quot;&amp;gt;
                &amp;lt;h3&amp;gt;{{ job.position }}&amp;lt;/h3&amp;gt;
                &amp;lt;p&amp;gt;{{ job.description }}&amp;lt;/p&amp;gt;
                &amp;lt;p&amp;gt;Posted on {{ job.createdAt|date(&amp;quot;m/d/Y&amp;quot;) }}&amp;lt;/p&amp;gt;
                &amp;lt;a class=&amp;quot;btn btn-primary&amp;quot; href=&amp;quot;{{ path(&amp;#39;job_show&amp;#39;, { &amp;#39;id&amp;#39;: job.id, &amp;#39;company&amp;#39;: job.companySlug, &amp;#39;location&amp;#39;: job.locationSlug, &amp;#39;position&amp;#39;: job.positionSlug }) }}&amp;quot;&amp;gt;See more&amp;lt;/a&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;

        &amp;lt;hr&amp;gt;
    {% endfor %}

    &amp;lt;!-- ... --&amp;gt;
{% endblock %}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;blockquote&gt;
  &lt;p&gt;Pour générer le lien vers une offre d’emploi, nous avons utilisé les propriétés &lt;code&gt;job.companySlug&lt;/code&gt;, &lt;code&gt;job.locationSlug&lt;/code&gt; et &lt;code&gt;job.positionSlug&lt;/code&gt; de notre classe &lt;code&gt;Job&lt;/code&gt;. Ces dernières correspondent en réalité à des appels de méthode (au besoin, je vous invite à relire le &lt;a href=&quot;/blog/2017/09/29/tutorial-jobeet-symfony-4-partie-4a-le-controleur-et-la-vue.html&quot;&gt;chapitre concernant Twig&lt;/a&gt; pour plus d’informations). L’implémentation des méthodes ne sera pas détaillée, consultez &lt;a href=&quot;https://github.com/jdecool/jobeet/tree/05-les-routes/src/Entity/Job.php &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;le code source de la classe&lt;/a&gt; pour plus de détails.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Au fur et à mesure que nous allons ajouter des fonctionnalités à notre application, cette dernière va grossir et contenir de plus en plus de code et de configuration. Il peut être parfois utile de lister l’ensemble des routes disponibles sans pour autant devoir parcourir les différents fichiers de configuration. Pour cela, Symfony met à disposition des développeurs une commande permettant d’afficher ces dernières :&lt;/p&gt;

&lt;center&gt;&lt;img src=&quot;/img/blog/20171016-tutorial-jobeet-symfony-4-partie-5-les-routes/cmd-debug-router.png &quot; /&gt;&lt;/center&gt;

&lt;p&gt;Cette commande permet également d’obtenir des informations détaillées sur une route. Il suffit pour cela d’indiquer le nom de la route en question :&lt;/p&gt;

&lt;center&gt;&lt;img src=&quot;/img/blog/20171016-tutorial-jobeet-symfony-4-partie-5-les-routes/cmd-debug-router-details.png &quot; /&gt;&lt;/center&gt;

&lt;p&gt;Ce chapitre a introduit la notion de route et explique comment créer des liens entre les pages de votre application. Vous avez ainsi appris à utiliser le composant &lt;code&gt;Routing&lt;/code&gt; de Symfony. Le prochain sera consacré à l’approfondissement du concept de modèle. Nous y expliquerons plus en détail comment structurer l’information et faire des requêtes complexes.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Retrouvez tous les tutorials Jobeet disponibles depuis le &lt;a href=&quot;/blog/2017/09/12/tutorial-jobeet-symfony-4-introduction.html&quot;&gt;billet d’introduction de la série&lt;/a&gt;. Le code source de cette application est également disponible sur &lt;a href=&quot;https://github.com/jdecool/jobeet/tree/05-les-routes &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Github&lt;/a&gt;. Vous trouverez une branche associée à l’état du projet après chaque chapitre.&lt;/p&gt;
&lt;/blockquote&gt;
</description>
                    <pubDate>Mon, 16 Oct 2017 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2017/10/16/tutorial-jobeet-symfony-4-partie-5-les-routes.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2017/10/16/tutorial-jobeet-symfony-4-partie-5-les-routes.html</guid>
                </item>
            
        
            
                143
                <item>
                    <title>Proxifier des requêtes HTTP en PHP</title>
                    <description>&lt;p&gt;Ce soir, je me suis demandé comment il était possible de “proxifier” une requête HTTP effectuée depuis un script PHP. Pour être plus précis, je souhaitais faire une requête sur le réseau &lt;a href=&quot;https://www.torproject.org &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;TOR&lt;/a&gt; (ou via n’importe quel proxy de manière générale) via une commande PHP.&lt;/p&gt;

&lt;p&gt;En fait cela est beaucoup plus simple que je ne le pensais car l’extension &lt;a href=&quot;http://php.net/manual/en/book.curl.php &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;CURL de PHP&lt;/a&gt; contient tout le nécessaire pour réaliser cette tâche. Il suffit pour cela de passer les bonnes options à notre ressource :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&amp;lt;?php

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, &amp;#39;https://check.torproject.org&amp;#39;);
curl_setopt($ch, CURLOPT_PROXY, &amp;#39;localhost:9150&amp;#39;); // l&amp;#39;URL de notre proxy
curl_setopt($ch, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5); // le type du proxy
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 0);
curl_setopt($ch, CURLOPT_HEADER, 1);
$curl_scraped_page = curl_exec($ch);
curl_close($ch);

echo $curl_scraped_page;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Si vous utilisez une bibliothèque &lt;a href=&quot;http://docs.guzzlephp.org/en/stable/request-options.html#proxy &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;type Guzzle&lt;/a&gt;, ces dernières fournissent généralement tout le nécessaire pour proxifier vos requêtes HTTP.&lt;/p&gt;

&lt;p&gt;D’ailleurs concernant cette dernière, j’ai trouvé un &lt;a href=&quot;https://github.com/megahertz/guzzle-tor &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;middleware &lt;code&gt;megahertz/guzzle-tor&lt;/code&gt;&lt;/a&gt; dédié à la mise en place d’une connexion via TOR.&lt;/p&gt;
</description>
                    <pubDate>Wed, 11 Oct 2017 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2017/10/11/proxifier-des-requetes-http-en-php.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2017/10/11/proxifier-des-requetes-http-en-php.html</guid>
                </item>
            
        
            
                144
                <item>
                    <title>Ma semaine 40 de veille (02/10/2017 -&gt; 08/10/2017)</title>
                    <description>&lt;p&gt;Retrouvez chaque semaine la sélection de mes 5 tweets les plus populaires concernant
la veille que je partage sur Twitter. Abonnez-vous au &lt;a href=&quot;/feed/feed.xml &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;flux RSS&lt;/a&gt;,
du site ou plus précisément à celui de cette &lt;a href=&quot;/feed/feed-veille.xml &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;catégorie&lt;/a&gt;
pour être notifié des mises à jour.&lt;/p&gt;

&lt;h3 id=&quot;mon-actu-préférée&quot;&gt;Mon actu préférée&lt;/h3&gt;

&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;col-4&quot;&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-cards=&quot;hidden&quot; data-lang=&quot;fr&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;One Million Tables in MySQL 8.0 : &lt;a href=&quot;https://t.co/edGYq1Jqed&quot;&gt;https://t.co/edGYq1Jqed&lt;/a&gt;&lt;/p&gt;&amp;mdash; Jérémy DECOOL (@jdecool) &lt;a href=&quot;https://twitter.com/jdecool/status/914752563525939201?ref_src=twsrc%5Etfw&quot;&gt;2 octobre 2017&lt;/a&gt;&lt;/blockquote&gt;
    &lt;script async=&quot;&quot; src=&quot;//platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
  &lt;div class=&quot;col-8 tweet-desc&quot;&gt;
    La prochaine version majeure de MySQL arrive très prochainement. Cette semaine, Percona (société d&apos;experts reconnue en performances de bases de données) a réalisé un benchmark l&apos;efficacité de cette nouvelle monture sur un jeu de données d&apos;un million de tables.
  &lt;/div&gt;
&lt;/div&gt;

&lt;h3 id=&quot;le-top-de-la-semaine&quot;&gt;Le top de la semaine&lt;/h3&gt;

&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;col-4&quot;&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-cards=&quot;hidden&quot; data-lang=&quot;fr&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;How to Docker Compose a developer environment: an open source example : &lt;a href=&quot;https://t.co/tI7Yt7pIxO&quot;&gt;https://t.co/tI7Yt7pIxO&lt;/a&gt;&lt;/p&gt;&amp;mdash; Jérémy DECOOL (@jdecool) &lt;a href=&quot;https://twitter.com/jdecool/status/915109663775105024?ref_src=twsrc%5Etfw&quot;&gt;3 octobre 2017&lt;/a&gt;&lt;/blockquote&gt;
  &lt;/div&gt;
  &lt;div class=&quot;col-8 tweet-desc&quot;&gt;
    Docker a ouvert de nouvelles perspectives dans la gestion et le déploiement d&apos;applications informatiques. Ce dernier a également su faire sa place auprès des développeurs pour la mise en place de leurs environnements de développement. Cet article en fait la démonstration au travers d&apos;un projet open source.
  &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;&lt;div class=&quot;col-12&quot;&gt;&lt;hr /&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;col-4&quot;&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-cards=&quot;hidden&quot; data-lang=&quot;fr&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;3 Reasons Why You Should Quit Your Job : &lt;a href=&quot;https://t.co/dWH3QmMjJo&quot;&gt;https://t.co/dWH3QmMjJo&lt;/a&gt;&lt;/p&gt;&amp;mdash; Jérémy DECOOL (@jdecool) &lt;a href=&quot;https://twitter.com/jdecool/status/916194065636610048?ref_src=twsrc%5Etfw&quot;&gt;6 octobre 2017&lt;/a&gt;&lt;/blockquote&gt;
  &lt;/div&gt;
  &lt;div class=&quot;col-8 tweet-desc&quot;&gt;
    En tant que professionnel de l&apos;informatique, nous évoluons dans un domaine dynamique. Patrick God est développeur depuis une quinzaine d&apos;années. Il évoque les 3 principales raisons qui selon lui pousse quelqu&apos;un à rechercher un nouvel emploi.
  &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;&lt;div class=&quot;col-12&quot;&gt;&lt;hr /&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;col-4&quot;&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-cards=&quot;hidden&quot; data-lang=&quot;fr&quot;&gt;&lt;p lang=&quot;fr&quot; dir=&quot;ltr&quot;&gt;Comment utiliser Docker multi-stage build avec Golang : &lt;a href=&quot;https://t.co/aErQr25sUz&quot;&gt;https://t.co/aErQr25sUz&lt;/a&gt;&lt;/p&gt;&amp;mdash; Jérémy DECOOL (@jdecool) &lt;a href=&quot;https://twitter.com/jdecool/status/914754577530048514?ref_src=twsrc%5Etfw&quot;&gt;2 octobre 2017&lt;/a&gt;&lt;/blockquote&gt;
  &lt;/div&gt;
  &lt;div class=&quot;col-8 tweet-desc&quot;&gt;
    La version 17.05 de Docker a introduit la notion de &quot;&lt;a href=&quot;https://docs.docker.com/engine/userguide/eng-image/multistage-build/ &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;multistage build&lt;/a&gt;&quot;. Cette fonctionnalité offre la possibilité d&apos;avoir plusieurs phases de construction de votre image. Parfois mal compris des utilisateurs, découvrez au travers de cet exemple une mise en pratique de ce nouveau concept.
  &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;&lt;div class=&quot;col-12&quot;&gt;&lt;hr /&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;col-4&quot;&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;fr&quot;&gt;&lt;p lang=&quot;fr&quot; dir=&quot;ltr&quot;&gt;Générer de la cryptomonaie en respectant les visiteurs : &lt;a href=&quot;https://t.co/HPDYq960Xu&quot;&gt;https://t.co/HPDYq960Xu&lt;/a&gt;&lt;/p&gt;&amp;mdash; Jérémy DECOOL (@jdecool) &lt;a href=&quot;https://twitter.com/jdecool/status/915841272841211904?ref_src=twsrc%5Etfw&quot;&gt;5 octobre 2017&lt;/a&gt;&lt;/blockquote&gt;
  &lt;/div&gt;
  &lt;div class=&quot;col-8 tweet-desc&quot;&gt;
    Récemment le site ThePirateBay à mis en place un script permettant de miner de la cryptomonnaie dans les pages web visitaient par les internautes. Si l&apos;utilisateur est prévenu est favorable à ce système, cela peut représenter une alternative à de la publicité. Encore faut-il que cela se fasse en respectant nos utilisateurs.
  &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;&lt;div class=&quot;col-12&quot;&gt;&lt;hr /&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;col-4&quot;&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-cards=&quot;hidden&quot; data-lang=&quot;fr&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Event-Driven Architecture : &lt;a href=&quot;https://t.co/CgIwzevnDT&quot;&gt;https://t.co/CgIwzevnDT&lt;/a&gt;&lt;/p&gt;&amp;mdash; Jérémy DECOOL (@jdecool) &lt;a href=&quot;https://twitter.com/jdecool/status/916196430020317187?ref_src=twsrc%5Etfw&quot;&gt;6 octobre 2017&lt;/a&gt;&lt;/blockquote&gt;
  &lt;/div&gt;
  &lt;div class=&quot;col-8 tweet-desc&quot;&gt;
    Les architectures orientées événements ont le vent en poupe actuellement car elles permettent de mettre en place une stratégie de découplage de notre code. Apprenez en davantage au travers de ce billet sur quand, comment et pourquoi mettre en place une telle architecture.
  &lt;/div&gt;
&lt;/div&gt;
</description>
                    <pubDate>Mon, 09 Oct 2017 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2017/10/09/ma-semaine-de-veille-40.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2017/10/09/ma-semaine-de-veille-40.html</guid>
                </item>
            
        
            
                145
                <item>
                    <title>Ma semaine 39 de veille (25/09/2017 -&gt; 01/10/2017)</title>
                    <description>&lt;p&gt;Retrouvez chaque semaine la sélection de mes 5 tweets les plus populaires concernant
la veille que je partage sur Twitter. Abonnez-vous au &lt;a href=&quot;/feed/feed.xml &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;flux RSS&lt;/a&gt;,
du site ou plus précisément à celui de cette &lt;a href=&quot;/feed/feed-veille.xml &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;catégorie&lt;/a&gt;
pour être notifié des mises à jour.&lt;/p&gt;

&lt;h3 id=&quot;mon-actu-préférée&quot;&gt;Mon actu préférée&lt;/h3&gt;

&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;col-4&quot;&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-cards=&quot;hidden&quot; data-lang=&quot;fr&quot;&gt;&lt;p lang=&quot;fr&quot; dir=&quot;ltr&quot;&gt;Google partage gratuitement ses outils pour managers : &lt;a href=&quot;https://t.co/94eKhVpoy5&quot;&gt;https://t.co/94eKhVpoy5&lt;/a&gt;&lt;/p&gt;&amp;mdash; Jérémy DECOOL (@jdecool) &lt;a href=&quot;https://twitter.com/jdecool/status/913670192009551872?ref_src=twsrc%5Etfw&quot;&gt;29 septembre 2017&lt;/a&gt;&lt;/blockquote&gt;
    &lt;script async=&quot;&quot; src=&quot;//platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
  &lt;div class=&quot;col-8 tweet-desc&quot;&gt;
    Google vient de rendre public certains des outils mis à disposition de ses managers pour les aider dans la gestion d&apos;équipe. Des ressources très intéressantes à découvrir que l&apos;on soit manager ou travaillant dans une équipe !
  &lt;/div&gt;
&lt;/div&gt;

&lt;h3 id=&quot;le-top-de-la-semaine&quot;&gt;Le top de la semaine&lt;/h3&gt;

&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;col-4&quot;&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-cards=&quot;hidden&quot; data-lang=&quot;fr&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Practical Clean Architecture in PHP [Part 1]: Domain layer &lt;a href=&quot;https://t.co/iIEC88CdJp&quot;&gt;https://t.co/iIEC88CdJp&lt;/a&gt; &lt;a href=&quot;https://t.co/4A7fTpkjzU&quot;&gt;pic.twitter.com/4A7fTpkjzU&lt;/a&gt;&lt;/p&gt;&amp;mdash; Jérémy DECOOL (@jdecool) &lt;a href=&quot;https://twitter.com/jdecool/status/912222899553079296?ref_src=twsrc%5Etfw&quot;&gt;25 septembre 2017&lt;/a&gt;&lt;/blockquote&gt;
  &lt;/div&gt;
  &lt;div class=&quot;col-8 tweet-desc&quot;&gt;
    Lorsque l&apos;on conçoit une application, nous cherchons toujours à ce que cette dernière soit évolutive et maintenable. Pour cela nous mettons en place des pratiques de &quot;Clean Code&quot;, nous cherchons à coder proprement. Cette introduction vous fera découvrir les concepts pour séparer la logique de votre application en une couche indépendante (le domaine).
  &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;&lt;div class=&quot;col-12&quot;&gt;&lt;hr /&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;col-4&quot;&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;fr&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Goodbye controllers, hello request handlers : &lt;a href=&quot;https://t.co/krnLLj4P7W&quot;&gt;https://t.co/krnLLj4P7W&lt;/a&gt;&lt;/p&gt;&amp;mdash; Jérémy DECOOL (@jdecool) &lt;a href=&quot;https://twitter.com/jdecool/status/912574712462626816?ref_src=twsrc%5Etfw&quot;&gt;26 septembre 2017&lt;/a&gt;&lt;/blockquote&gt;
  &lt;/div&gt;
  &lt;div class=&quot;col-8 tweet-desc&quot;&gt;
    Si vous avez travaillé avec de grandes bases de code, peut-être avez-vous déjà vu des contrôleurs interminables et inmaintenable car ils ne respectaient pas les principes &lt;a href=&quot;https://fr.wikipedia.org/wiki/Ne_vous_r%C3%A9p%C3%A9tez_pas &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;DRY&lt;/a&gt; et &lt;a href=&quot;https://fr.wikipedia.org/wiki/SOLID_(informatique) &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;SOLID&lt;/a&gt;. En effet, ces concepts ne sont pas souvent utilisés pour des contrôleurs. Pourtant PHP a tout ce qu&apos;il faut pour respecter ce principe.
  &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;&lt;div class=&quot;col-12&quot;&gt;&lt;hr /&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;col-4&quot;&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-cards=&quot;hidden&quot; data-lang=&quot;fr&quot;&gt;&lt;p lang=&quot;fr&quot; dir=&quot;ltr&quot;&gt;Le documentaire Nothing To Hide est maintenant disponible gratuitement en français : &lt;a href=&quot;https://t.co/osYFeqFEdp&quot;&gt;https://t.co/osYFeqFEdp&lt;/a&gt;&lt;/p&gt;&amp;mdash; Jérémy DECOOL (@jdecool) &lt;a href=&quot;https://twitter.com/jdecool/status/914057282027163650?ref_src=twsrc%5Etfw&quot;&gt;30 septembre 2017&lt;/a&gt;&lt;/blockquote&gt;
  &lt;/div&gt;
  &lt;div class=&quot;col-8 tweet-desc&quot;&gt;
    Lorsque l&apos;on parle du problème de la surveillance de masse et du pouvoir des GAFAM ont avec les données utilisateurs, l&apos;argument qui revient le plus fréquemment est: &quot;je n&apos;ai rien à cacher&quot;. Ce documentaire, disponible sous licence Creative Commons, a pour vocation de vous démontrer pourquoi vous devriez quand même y faire attention.
  &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;&lt;div class=&quot;col-12&quot;&gt;&lt;hr /&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;col-4&quot;&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;fr&quot;&gt;&lt;p lang=&quot;fr&quot; dir=&quot;ltr&quot;&gt;Les députés adoptent l’obligation de déclarer tous ses identifiants électroniques : &lt;a href=&quot;https://t.co/46hDK6b3tl&quot;&gt;https://t.co/46hDK6b3tl&lt;/a&gt;&lt;/p&gt;&amp;mdash; Jérémy DECOOL (@jdecool) &lt;a href=&quot;https://twitter.com/jdecool/status/912959119983857664?ref_src=twsrc%5Etfw&quot;&gt;27 septembre 2017&lt;/a&gt;&lt;/blockquote&gt;
  &lt;/div&gt;
  &lt;div class=&quot;col-8 tweet-desc&quot;&gt;
    Les députés viennent de voter des mesures de contrôle et de surveillance individuelles. Parmi ces dernières, l&apos;obligation pour une personne au comportement suspect de fournir l&apos;intégralité de ses identifiants aux autorités administratives. Nextimpact nous résume cela dans cet article.
  &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;&lt;div class=&quot;col-12&quot;&gt;&lt;hr /&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;col-4&quot;&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-cards=&quot;hidden&quot; data-lang=&quot;fr&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;MySQL 8.0 RC1 – Highlights &lt;a href=&quot;https://t.co/jAYdJsxT0Z&quot;&gt;https://t.co/jAYdJsxT0Z&lt;/a&gt;&lt;/p&gt;&amp;mdash; Jérémy DECOOL (@jdecool) &lt;a href=&quot;https://twitter.com/jdecool/status/912577519743889408?ref_src=twsrc%5Etfw&quot;&gt;26 septembre 2017&lt;/a&gt;&lt;/blockquote&gt;
  &lt;/div&gt;
  &lt;div class=&quot;col-8 tweet-desc&quot;&gt;
    La sortie de la prochaine version de MySQL (la version 8.0) arrive à grands pas et propose de nombreuses évolutions. L&apos;occasion de faire un tour d&apos;horizon des nouveautés à venir.
  &lt;/div&gt;
&lt;/div&gt;
</description>
                    <pubDate>Mon, 02 Oct 2017 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2017/10/02/ma-semaine-de-veille-39.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2017/10/02/ma-semaine-de-veille-39.html</guid>
                </item>
            
        
            
                146
                <item>
                    <title>Tutorial Jobeet pour Symfony 4 - Partie 4B: La gestion des assets avec Twig</title>
                    <description>&lt;p&gt;Dans la section précédente, nous avons commencé à afficher nos premières pages et avons défini une charte graphique. Nous avons donc inclus un certain nombre de fichiers CSS, Javascript et utilisé des images. Voyons avant de continuer quelques bonnes pratiques sur la gestion des assets.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Les assets sont des fichiers statiques qui sont utilisés par l’interface de notre site. Il peut s’agir de fichiers CSS (pour la mise en page et la charte graphique du site), Javascripts (pour gérer l’interactivité de notre interface avec l’utilisateur), d’images ou par exemple des fichiers pouvant être  téléchargées par l’utilisateur.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Tout d’abord, revoyons notre template &lt;code&gt;base.html.twig&lt;/code&gt; qui inclut nos différentes feuilles de style :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;# templates/base.html.twig
&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
    &amp;lt;head&amp;gt;
        &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;
        &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1, shrink-to-fit=no&amp;quot;&amp;gt;
        &amp;lt;title&amp;gt;{% block title %}Jobeet{% endblock %}&amp;lt;/title&amp;gt;
        &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;/vendor/bootstrap/css/bootstrap.min.css&amp;quot;&amp;gt;
        &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;/vendor/1-col-portfolio.css&amp;quot;&amp;gt;
        {% block stylesheets %}{% endblock %}
    &amp;lt;/head&amp;gt;
    &amp;lt;body&amp;gt;
        &amp;lt;nav class=&amp;quot;navbar navbar-expand-lg navbar-dark bg-dark fixed-top&amp;quot;&amp;gt;
            &amp;lt;div class=&amp;quot;container&amp;quot;&amp;gt;
                &amp;lt;a class=&amp;quot;navbar-brand&amp;quot; href=&amp;quot;#&amp;quot;&amp;gt;Jobeet&amp;lt;/a&amp;gt;
                &amp;lt;button class=&amp;quot;navbar-toggler&amp;quot; type=&amp;quot;button&amp;quot; data-toggle=&amp;quot;collapse&amp;quot; data-target=&amp;quot;#navbarResponsive&amp;quot; aria-controls=&amp;quot;navbarResponsive&amp;quot; aria-expanded=&amp;quot;false&amp;quot; aria-label=&amp;quot;Toggle navigation&amp;quot;&amp;gt;
                    &amp;lt;span class=&amp;quot;navbar-toggler-icon&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;
                &amp;lt;/button&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;/nav&amp;gt;

        &amp;lt;div class=&amp;quot;container&amp;quot;&amp;gt;
            {% block body %}{% endblock %}
        &amp;lt;/div&amp;gt;

        &amp;lt;script src=&amp;quot;/vendor/jquery/jquery-3.2.1.slim.min.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
        &amp;lt;script src=&amp;quot;/vendor/popper/popper.min.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
        &amp;lt;script src=&amp;quot;/vendor/bootstrap/js/bootstrap.min.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
        {% block javascripts %}{% endblock %}
    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Pour inclure nos différents fichiers, nous avons spécifié le chemin complet pour accéder à nos ressources. Cette pratique n’est pas recommandée car elle possède de nombreux désavantages :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Elle rend nos &lt;em&gt;templates verbeux&lt;/em&gt; en nous obligeant à écrire pour chaque fichier le chemin complet pour y accéder.&lt;/li&gt;
  &lt;li&gt;Le &lt;em&gt;versionnement des fichiers&lt;/em&gt; est compliqué car il nécessite d’être géré manuellement pour chaque ressource.&lt;/li&gt;
  &lt;li&gt;Le &lt;em&gt;déplacement des assets&lt;/em&gt; peut être source d’erreurs.&lt;/li&gt;
  &lt;li&gt;L’&lt;em&gt;utilisation d’un CDN&lt;/em&gt; est impossible avec cette gestion manuelle.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Afin de résoudre toutes ces problématiques, il existe de nombreux composants PHP permettant de mettre en place une gestion d’asset simple, efficace et maintenable. Symfony propose également un composant pour ça, installons-le :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;$ composer require symfony/asset

Using version ^3.3 for symfony/asset
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 1 install, 0 updates, 0 removals
  - Installing symfony/asset (v3.3.9): Downloading (100%)
Writing lock file
Generating autoload files
Executing script make cache-warmup [OK]
Executing script assets:install --symlink --relative public [OK]&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Une fois téléchargé, le composant est tout de suite prêt à être utilisé dans notre projet. Lors de notre découverte de Twig, nous avons rapidement évoqué le composant &lt;code&gt;TwigBridge&lt;/code&gt; comme étant un module permettant d’étendre le fonctionnement de Twig en ajoutant des fonctionnalités spécifiques aux différents modules de de Symfony. Le composant Asset en fait partie. &lt;code&gt;TwigBridge&lt;/code&gt; fournit ainsi une fonction &lt;code&gt;asset&lt;/code&gt; permettant d’accéder à une ressource.&lt;/p&gt;

&lt;p&gt;Modifions notre template d’origine :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;# templates/base.html.twig
&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
    &amp;lt;head&amp;gt;
        &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;
        &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1, shrink-to-fit=no&amp;quot;&amp;gt;
        &amp;lt;title&amp;gt;{% block title %}Jobeet{% endblock %}&amp;lt;/title&amp;gt;
        &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{{ asset(&amp;#39;vendor/bootstrap/css/bootstrap.min.css&amp;#39;) }}&amp;quot;&amp;gt;
        &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{{ asset(&amp;#39;vendor/1-col-portfolio.css&amp;#39;) }}&amp;quot;&amp;gt;
        {% block stylesheets %}{% endblock %}
    &amp;lt;/head&amp;gt;
    &amp;lt;body&amp;gt;
        &amp;lt;nav class=&amp;quot;navbar navbar-expand-lg navbar-dark bg-dark fixed-top&amp;quot;&amp;gt;
            &amp;lt;div class=&amp;quot;container&amp;quot;&amp;gt;
                &amp;lt;a class=&amp;quot;navbar-brand&amp;quot; href=&amp;quot;#&amp;quot;&amp;gt;Jobeet&amp;lt;/a&amp;gt;
                &amp;lt;button class=&amp;quot;navbar-toggler&amp;quot; type=&amp;quot;button&amp;quot; data-toggle=&amp;quot;collapse&amp;quot; data-target=&amp;quot;#navbarResponsive&amp;quot; aria-controls=&amp;quot;navbarResponsive&amp;quot; aria-expanded=&amp;quot;false&amp;quot; aria-label=&amp;quot;Toggle navigation&amp;quot;&amp;gt;
                    &amp;lt;span class=&amp;quot;navbar-toggler-icon&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;
                &amp;lt;/button&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;/nav&amp;gt;

        &amp;lt;div class=&amp;quot;container&amp;quot;&amp;gt;
            {% block body %}{% endblock %}
        &amp;lt;/div&amp;gt;

        &amp;lt;script src=&amp;quot;{{ asset(&amp;#39;vendor/jquery/jquery-3.2.1.slim.min.js&amp;#39;) }}&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
        &amp;lt;script src=&amp;quot;{{ asset(&amp;#39;vendor/popper/popper.min.js&amp;#39;) }}&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
        &amp;lt;script src=&amp;quot;{{ asset(&amp;#39;vendor/bootstrap/js/bootstrap.min.js&amp;#39;) }}&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
        {% block javascripts %}{% endblock %}
    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;blockquote&gt;
  &lt;p&gt;Nous pouvons également modifier l’affichage de l’image d’une offre d’emploi dans le fichier &lt;a href=&quot;https://github.com/jdecool/jobeet/blob/04b-assets-twig/templates/job/index.html.twig#L10 &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;templates/job/index.html.twig&lt;/code&gt;&lt;/a&gt;. Notons au passage l’utilisation de l’opérateur de concaténation de chaine de caractère Twig &lt;code&gt;~&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Peut-être que vous ne remarquez que très peu de différences. Pour le moment, nous avons juste utilisé notre fonction &lt;code&gt;asset&lt;/code&gt; mais le code reste le même et le chemin vers le fichier est toujours présent.&lt;/p&gt;

&lt;p&gt;S’il y a peu de différences, c’est tout d’abord parce que nous avons placé nos fichiers dans le répertoire &lt;code&gt;public&lt;/code&gt; (qui est le point d’entrée du serveur HTTP pour rechercher nos fichiers accessibles publiquement). C’est donc le répertoire où le composant Asset va rechercher nos fichiers par défaut.&lt;/p&gt;

&lt;p&gt;Imaginons que nous souhaitons changer le répertoire contenant nos ressources, pour par exemple le déplacer dans un sous-répertoire &lt;code&gt;public/assets&lt;/code&gt;. Il ne sera maintenant plus nécessaire de modifier tous les chemins des fichiers que nous avons utilisés. Il suffira de modification la configuration du framework afin de spécifier le répertoire racine qui contient nos assets comme dans l’exemple ci-dessous :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;# config/packages/framework.yml
framework:
    # ...
    assets:
        base_path: &amp;#39;/assets&amp;#39;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Au travers de cet exemple, il est possible de commencer à voir les avantages d’utiliser un composant pour gérer nos ressources. Une autre des fonctionnalités majeures du module &lt;code&gt;Asset&lt;/code&gt; est la gestion du versionnement des fichiers. Pour plus d’informations sur le versionnement des assets, je vous propose de consulter la &lt;a href=&quot;http://symfony.com/doc/current/components/asset.html#versioned-assets &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;documentation officielle&lt;/a&gt; qui fournira tous les informations utiles.&lt;/p&gt;

&lt;p&gt;Si vous souhaitez spécifier une version d’asset dans notre projet, voici la configuration que vous allez devoir renseigner :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;# config/packages/framework.yml
# app/config/config.yml
framework:
    # ...
    assets:
        version: &amp;#39;v2&amp;#39; # asset(&amp;#39;/images/logo.png&amp;#39;) ==&amp;gt; /images/logo.png?v2&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;blockquote&gt;
  &lt;p&gt;Le versionnement des assets est très utile pour gérer la mise en cache de nos ressources. C’est une pratique courante pour optimiser les données devant être renvoyées par les serveurs HTTP et économiser des appels réseaux.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Avant de terminer sur la gestion des ressources, évoquons une dernière fonctionnalité essentielle : la gestion des CDN.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Un &lt;em&gt;Content&lt;/em&gt; &lt;em&gt;Delivery&lt;/em&gt; &lt;em&gt;Network&lt;/em&gt; (CDN ou réseau de diffusion de contenu en français) est une plate-forme de serveurs hautement distribuée optimisée pour diffuser du contenu. Les CDN ont l’avantage d’accélérer le chargement des pages.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Pour mettre en place la gestion d’un CDN, il suffit comme pour le versionnement des ressources, de spécifier les domaines qui vont être utilisés dans le fichier de configuration. Symfony sélectionnera un domaine (si vous n’en spécifiez pas un lors de l’utilisaton de la fonction &lt;code&gt;asset&lt;/code&gt;) à chaque génération d’un page.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;# config/packages/framework.yml
framework:
    # ...
    assets:
        base_urls:
            - &amp;#39;http://cdn.example.com/&amp;#39; # asset(&amp;#39;/images/logo.png&amp;#39;) ==&amp;gt; http://cdn.example.com/images/logo.png&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;blockquote&gt;
  &lt;p&gt;Retrouvez tous les tutorials Jobeet disponibles depuis le &lt;a href=&quot;/blog/2017/09/12/tutorial-jobeet-symfony-4-introduction.html&quot;&gt;billet d’introduction de la série&lt;/a&gt;. Le code source de cette application est également disponible sur &lt;a href=&quot;https://github.com/jdecool/jobeet/tree/04b-assets-twig &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Github&lt;/a&gt;. Vous trouverez une branche associée à l’état du projet après chaque chapitre.&lt;/p&gt;
&lt;/blockquote&gt;
</description>
                    <pubDate>Sat, 30 Sep 2017 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2017/09/30/tutorial-jobeet-symfony-4-partie-4b-la-gestion-des-assets-avec-twig.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2017/09/30/tutorial-jobeet-symfony-4-partie-4b-la-gestion-des-assets-avec-twig.html</guid>
                </item>
            
        
            
                147
                <item>
                    <title>Tutorial Jobeet pour Symfony 4 - Partie 4A: Le contrôleur et la vue</title>
                    <description>&lt;p&gt;Nous avons jusqu’à présent entrevu le fonctionnement de Doctrine en créant une base de données et en y insérant un jeu de données afin d’avoir des données initiales, nous évitant ainsi d’avoir une application vide. Nous allons  maintenant pouvoir commencer à afficher nos premières pages web.&lt;/p&gt;

&lt;p&gt;Un projet Symfony repose sur le pattern MVC (&lt;em&gt;M&lt;/em&gt;odel &lt;em&gt;V&lt;/em&gt;iew &lt;em&gt;C&lt;/em&gt;ontroller ou &lt;em&gt;M&lt;/em&gt;odel &lt;em&gt;V&lt;/em&gt;ue &lt;em&gt;C&lt;/em&gt;ontrôleur en français). Ce type d’architecture permet d’organiser le code en le séparant en trois couches :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;La couche &lt;code&gt;modèle&lt;/code&gt; contenant le traitement logique de vos données (on y retrouve les traitements métier, les accès à la base de données, …).&lt;/li&gt;
  &lt;li&gt;La &lt;code&gt;vue&lt;/code&gt; est la modélisation de l’IHM (Interface Homme Machine). Elle représente ce qui est rendu à l’utilisateur (sous la forme d’une page Web, d’une commande d’un terminal, de données JSON/XML, …).&lt;/li&gt;
  &lt;li&gt;Le &lt;code&gt;contrôleur&lt;/code&gt; correspond au code faisant le lien entre le &lt;code&gt;modèle&lt;/code&gt; et la &lt;code&gt;vue&lt;/code&gt;. Il récupère les données utilisateurs pour y appliquer les traitements et donner les résultats à la vue.
 Démarrons par ce dernier. Comme nous venons brièvement de le dire, le contrôleur est la couche qui va exécuter les traitements liés à notre application et transmettre les résultats à la vue pour que ces derniers puissent être affichés à l’utilisateur. Dans notre projet Web, cela va se matérialiser par des classes qui vont contenir des fonctions qui seront appelées en fonction d’une URL. Ces dernières renverront un objet de type &lt;code&gt;Symfony\Component\HttpFoundation\Response&lt;/code&gt; qui est l’abstraction d’une réponse HTTP dans Symfony.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ecrivons un premier contrôleur que nous allons nommer &lt;code&gt;DefaultController&lt;/code&gt; et que nous allons placer dans le répertoire &lt;code&gt;src/Controller&lt;/code&gt;. Ce dernier, contiendra une fonction qui permettra d’afficher un message dans le navigateur.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&amp;lt;?php // src/Controller/DefaultController.php

namespace App\Controller;

use Symfony\Component\HttpFoundation\Response;

class DefaultController
{
    public function index(): Response
    {
        return new Response(&amp;#39;Accueil Jobeet - Hello&amp;#39;);
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Pour tester ce code, nous allons devoir indiquer au framework comment accéder à ce contrôleur. Pour cela nous allons éditer le fichier &lt;code&gt;config/routes.yaml&lt;/code&gt;. Sans rentrer dans les détails (car c’est le sujet du prochain chapitre), ce fichier permet d’indiquer à Symfony l’URL qui déclenchera l’appel de la méthode &lt;code&gt;index&lt;/code&gt; de notre contrôleur.&lt;/p&gt;

&lt;p&gt;Pour le moment, décommentons simplement les 3 premières lignes :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;# config/routes.yaml

index:
    path: /
    defaults: { _controller: &amp;#39;App\Controller\DefaultController::index&amp;#39; }

# Depends on sensio/framework-extra-bundle, doctrine/annotations, and doctrine/cache
#   install with composer req sensio/framework-extra-bundle annot
#controllers:
#    resource: ../src/Controller/
#    type: annotation&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Nous pouvons maintenant démarrer notre serveur Web via l’exécution de la commande &lt;span style=&quot;text-decoration: line-through;&quot;&gt;&lt;code&gt;make serve&lt;/code&gt;&lt;/span&gt; &lt;code&gt;bin/console server:run&lt;/code&gt; dans un terminal et se rendre à l’adresse &lt;code&gt;http://localhost:8000&lt;/code&gt; avec son navigateur pour visualiser le résultat.&lt;/p&gt;

&lt;center&gt;&lt;img src=&quot;/img/blog/20170929-tutorial-jobeet-symfony-4-partie-4a-le-controleur-et-la-vue/browser.png &quot; /&gt;&lt;/center&gt;

&lt;p&gt;Pour pouvoir exécuter les différents traitements de notre application, le contrôleur doit pouvoir récupérer les données saisies par les utilisateurs. Dans un environnement Web, ces informations sont transmises par le navigateur au sein d’une requête HTTP.&lt;/p&gt;

&lt;p&gt;Symfony utilise un composant &lt;a href=&quot;https://symfony.com/components/HttpFoundation &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;HttpFoundation&lt;/code&gt;&lt;/a&gt;, dont l’objectif est de fournir une couche objet permettant de travailler avec le protocole HTTP. Nous avons, dans les exemples précédents, déjà utilisé un objet &lt;code&gt;Response&lt;/code&gt; issue de ce composant. Nous allons maintenant utiliser un objet &lt;code&gt;Symfony\Component\HttpFoundation\Request&lt;/code&gt; nous permettant d’accéder à toutes les informations d’une requête HTTP.&lt;/p&gt;

&lt;p&gt;Pour ce faire, Symfony passe automatiquement en paramètre une instance d’un objet &lt;code&gt;Request&lt;/code&gt; aux actions de nos contrôleurs si le paramètre est présent (Symfony détecte automatiquement la variable grâce à son typage).&lt;/p&gt;

&lt;p&gt;Complétons notre code précédent pour récupérer un paramètre &lt;code&gt;name&lt;/code&gt; et afficher le nom de la personne à saluer.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&amp;lt;?php // src/Controller/DefaultController.php

namespace App\Controller;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class DefaultController
{
    public function index(Request $request): Response
    {
        return new Response(&amp;#39;Accueil Jobeet - Hello &amp;#39;.$request-&amp;gt;get(&amp;#39;name&amp;#39;, &amp;#39;World&amp;#39;));
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;blockquote&gt;
  &lt;p&gt;Dans cet exemple, nous utilisons la méthode &lt;code&gt;Request::get&lt;/code&gt; pour récupérer un paramètre de la requête. Ce paramètre peut être transmis via l’URL ou au travers d’une requête de type POST. S’il n’est présent dans aucun des cas, nous avons choisi ici de retourner la valeur par défaut “World”. La valeur par défaut est facultative, si rien n’est spécifié et que le paramètre n’est pas présent &lt;code&gt;NULL&lt;/code&gt; sera renvoyé.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Intéressons-nous maintenant à la couche vue. C’est cette dernière qui renvoie et met en forme les informations qui seront affichées à l’utilisateur. Le format de retour des données dépend de plusieurs paramètres dont le contexte d’utilisation. Dans le cas d’une navigation Web classique, l’application va retourner des données au format HTML, alors que dans le cas d’une API, les données pourraient être renvoyées au format JSON, XML ou n’importe quel autre format. Dans ce cas chapitre nous allons travailler exclusivement avec des pages HTML.&lt;/p&gt;

&lt;p&gt;Comme nous l’avons déjà évoqué auparavant, Symfony 4 laisse libre le développeur de choisir les outils qu’il souhaite utiliser. Le framework ne prend aucun parti pris. De ce fait et contrairement aux versions précédentes, Symfony n’est plus fourni avec un moteur de templating par défaut.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Les moteurs de templating simplifient le travail d’écriture HTML en améliorant la lisibilité du code, son organisation et sa maintenance. Ces derniers sont généralement fournis avec un ensemble de fonctions de haut niveau permettant entre autres d’afficher des variables PHP, de créer des macros, d’utiliser des structures de boucles et de contrôles, etc.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Dans ce tutorial, nous avons fait le choix d’utiliser &lt;a href=&quot;https://twig.symfony.com &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Twig&lt;/a&gt;. Twig a été créé par l’agence SensioLabs, les créateurs du framework Symfony. Ce moteur de templating était jusqu’à maintenant celui qui était fourni par défaut avec le framework. Sa simplicité et sa puissance en font le moteur le plus populaire PHP.&lt;/p&gt;

&lt;p&gt;Avant de pouvoir commencer à nous servir de Twig dans notre projet, nous allons devoir installer les dépendances nécessaires. Pour fonctionner dans notre projet Symfony, plusieurs composants sont requis :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code&gt;Twig&lt;/code&gt;: le moteur de template en lui-même. Ce dernier n’est pas couplé à Symfony et peut ainsi être réutilisé dans n’importe quel projet (avec ou sans framework).&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;TwigBridge&lt;/code&gt;: permet d’intégrer des nouvelles fonctionnalités au moteur de template qui sont liées aux différents composants du framework (formulaires, internationalisation, …).&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;TwigBundle&lt;/code&gt;: l’intégration du moteur Twig dans Symfony.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Comme pour toute dépendance PHP, nous allons utiliser Composer (et Symfony Flex) pour installer Twig. Nous allons ainsi demander l’installation de &lt;code&gt;symfony/twig-bundle&lt;/code&gt;. Ce dernier ayant besoin des deux autres composants pour fonctionner, toutes les dépendances requises au fonctionnement de Twig dans notre projet seront mises en place.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;$ composer require symfony/twig-bundle

Using version ^3.3 for symfony/twig-bundle
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 3 installs, 0 updates, 0 removals
  - Installing twig/twig (v2.4.3): Loading from cache
  - Installing symfony/twig-bridge (v3.3.9): Downloading (100%)
  - Installing symfony/twig-bundle (v3.3.9): Downloading (100%)
Writing lock file
Generating autoload files
Symfony operations: 1 recipe
  - Configuring symfony/twig-bundle (3.3): From github.com/symfony/recipes:master
Executing script make cache-warmup [OK]
Executing script assets:install --symlink --relative public [OK]&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;blockquote&gt;
  &lt;p&gt;Il est également possible d’utiliser une notation raccourcie pour installer Twig au travers de la commande &lt;code&gt;composer require twig&lt;/code&gt;. Cela est possible grace à Symfony Flex qui permet de définir des noms alternatifs à des dépendances Composer. Dans le chapitre précédent, nous aurions pu utiliser la commande &lt;code&gt;composer require orm&lt;/code&gt; pour installer Doctrine, cette dernière commande étant un alias de la commande &lt;code&gt;composer require orm/pack&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Maintenant que Twig est installé, nous allons pouvoir créer et afficher notre premier template. Pour cela, nous allons commencer par injecter le moteur de templating dans notre contrôleur. Nous pourrons ensuite, utiliser ce dernier depuis nos actions pour récupérer le rendu de nos templates.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&amp;lt;?php // src/Controller/DefaultController.php

namespace App\Controller;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Twig\Environment;

class DefaultController
{
    private $twig;

    public function __construct(Environment $twig)
    {
        $this-&amp;gt;twig = $twig;
    }

    public function index(Request $request): Response
    {
        return new Response($this-&amp;gt;twig-&amp;gt;render(&amp;#39;home.html.twig&amp;#39;, [
            &amp;#39;name&amp;#39; =&amp;gt; $request-&amp;gt;get(&amp;#39;name&amp;#39;, &amp;#39;World&amp;#39;)
        ]));
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;blockquote&gt;
  &lt;p&gt;Notez qu’il n’est pas nécessaire de faire quoi que ce soit pour injecter notre objet &lt;code&gt;Twig\Environment&lt;/code&gt; dans le constructeur de notre contrôleur. Le composant d’injection de dépendance de Symfony utilise l’&lt;a href=&quot;https://symfony.com/doc/current/service_container/autowiring.html &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;autowiring&lt;/a&gt; pour injecter notre dépendance automatiquement.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Au moment de l’installation de Twig, Flex a préparé un dossier &lt;code&gt;template&lt;/code&gt; à la racine de notre projet et a automatiquement configuré Twig pour que ce dernier aille chercher nos vues dans ce répertoire. Nous allons donc y créer un fichier &lt;code&gt;home.html.twig&lt;/code&gt;.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;# templates/home.html.twig
Hello {{ name }}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;blockquote&gt;
  &lt;p&gt;Pour afficher les variables que nous avons passé à notre template Twig, il faut utiliser la syntaxe &lt;code&gt;{{ variable }}&lt;/code&gt;.
 Pour afficher le contenu de notre template, nous appelons la méthode &lt;code&gt;render&lt;/code&gt; de Twig et passons le résultat à notre objet &lt;code&gt;Response&lt;/code&gt; pour que le résultat puisse être retourné à l’utilisateur.
 Pour afficher le résultat de notre vue, cette syntaxe est un peu longue. Effectivement, nous allons devoir injecter dans chacun de nos contrôleurs une instance de Twig, récupérer le contenu d’un template et créer la réponse associée.
 Heureusement, Symfony met à notre disposition des outils pour simplifier notre travail et surtout mutualiser ce code au travers de la classe &lt;code&gt;AbstractController&lt;/code&gt;. Cette dernière met à disposition une méthode &lt;code&gt;render&lt;/code&gt; qui récupérera automatiquement une instance de Twig et créera notre objet &lt;code&gt;Response&lt;/code&gt; en un appel de méthode.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Notre contrôleur peut ainsi être réécrit de la manière suivante :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&amp;lt;?php  // src/Controller/DefaultController.php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class DefaultController extends AbstractController
{
    public function index(Request $request): Response
    {
        return $this-&amp;gt;render(&amp;#39;home.html.twig&amp;#39;, [
            &amp;#39;name&amp;#39; =&amp;gt; $request-&amp;gt;get(&amp;#39;name&amp;#39;, &amp;#39;World&amp;#39;)
        ]);
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Nous connaissons à présent le fonctionnement des contrôleurs et comment ces derniers communiquent avec la vue pour afficher nos données à l’utilisateur. Nous allons maintenant pouvoir afficher les premières pages de notre projet Jobeet. Commençons par la page de listing des offres d’emploi.&lt;/p&gt;

&lt;p&gt;Créons pour cela un contrôleur dédié à la gestion des offres et ajouter une méthode pour récupérer les offres disponibles.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&amp;lt;?php // src/Controller/JobController.php

namespace App\Controller;

use App\Entity\Job;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;

class JobController extends AbstractController
{
    public function index(EntityManagerInterface $em)
    {
        $jobs = $em-&amp;gt;getRepository(Job::class)-&amp;gt;findAll();

        return $this-&amp;gt;render(&amp;#39;job/index.html.twig&amp;#39;, [
            &amp;#39;jobs&amp;#39; =&amp;gt; $jobs,
        ]);
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Nous injectons ici un objet de type &lt;code&gt;EntityManagerInterface&lt;/code&gt; directement dans notre méthode grace à l’autowiring (Symfony va détecter et injecter l’instance de l’objet qui implémente l’interface).&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;L’autowiring fonctionne avec une interface uniquement s’il n’existe qu’une seule classe qui implémente l’interface en question. Si deux classes implémentent la même interface, Symfony ne sera pas capable de savoir quelle instance injecter.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;L’&lt;code&gt;EntityManager&lt;/code&gt; est l’objet Doctrine qui permet de manipuler nos entités. Dans notre code, nous demandons une instance du repository (c’est-à-dire la classe qui permet de faire les requêtes en base de données) de notre entité &lt;code&gt;Job&lt;/code&gt; pour ensuite récupérer la liste des emplois disponibles.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Doctrine fournit un ensemble de méthodes par défaut permettant de récupérer nos objets persistés. Signalons entre autres les fonctions &lt;a href=&quot;https://goo.gl/EyWW5N &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;find&lt;/code&gt;&lt;/a&gt;, &lt;a href=&quot;https://goo.gl/EyWW5N &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;findBy&lt;/code&gt;&lt;/a&gt;, &lt;code&gt;findOneBy&lt;/code&gt; et &lt;code&gt;findAll&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ajoutons la vue qui va afficher nos résultats :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&amp;lt;!-- templates/job/index.html.twig --&amp;gt;

&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
    &amp;lt;head&amp;gt;
        &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot; /&amp;gt;
        &amp;lt;title&amp;gt;Jobeet - Liste des jobs&amp;lt;/title&amp;gt;
        &amp;lt;link rel=&amp;quot;icon&amp;quot; type=&amp;quot;image/x-icon&amp;quot; href=&amp;quot;{{ asset(&amp;#39;favicon.ico&amp;#39;) }}&amp;quot; /&amp;gt;
    &amp;lt;/head&amp;gt;
    &amp;lt;body&amp;gt;
        &amp;lt;ul&amp;gt;
            {% for job in jobs %}
                &amp;lt;li&amp;gt;{{ job.position }}&amp;lt;/li&amp;gt;
            {% endfor %}
        &amp;lt;/ul&amp;gt;
    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;blockquote&gt;
  &lt;p&gt;Pour afficher la page, il sera nécessaire de modifier le fichier de configuration &lt;a href=&quot;https://github.com/jdecool/jobeet/blob/04-controleur-vue/config/routes.yaml &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;config/routes.yaml&lt;/code&gt;&lt;/a&gt; en modifiant le contrôleur devant être appelé.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;L’exemple ci-dessus nous permet de découvrir de nouveaux éléments du langage de Twig. Tout d’abord, les instructions d’itérations qui nous permettent de parcourir des données de type &lt;code&gt;array&lt;/code&gt; ou plus généralement des données &lt;a href=&quot;http://php.net/manual/en/language.types.iterable.php &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;iterable&lt;/code&gt;&lt;/a&gt; de PHP. Il s’agit d’une boucle &lt;a href=&quot;https://twig.symfony.com/doc/2.x/tags/for.html &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;for&lt;/code&gt;&lt;/a&gt; permettant de parcourir une liste d’éléments.&lt;/p&gt;

&lt;p&gt;Nous constatons également qu’il est possible de passer à notre template Twig des instances d’objets et d’accéder aux propriétés de ces derniers avec l’opérateur &lt;code&gt;.&lt;/code&gt; (cela fonctionnement également pour accéder à des tableaux).&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Bien que la propriété &lt;code&gt;position&lt;/code&gt; de notre objet &lt;code&gt;Job&lt;/code&gt; soit privée, Twig parvient à afficher cette dernière. Twig possède un mécanisme qui va automatiquement trouver la méthode &lt;code&gt;get&lt;/code&gt; associé à la propriété si cette dernière n’est pas accessible directement (si elle n’est pas &lt;code&gt;public&lt;/code&gt;). Il est toutefois possible d’appeler directement la méthode &lt;code&gt;getPosition()&lt;/code&gt; ou n’importe quelle autre méthode depuis notre template.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Pour le moment nous avons copié la totalité de la structure HTML dans notre fichier. Pour éviter d’avoir à dupliquer de nombreuses lignes de code dans tous nos templates, nous allons mutualiser le code qui va être commun à toutes les pages.&lt;/p&gt;

&lt;p&gt;Si vous jetez un oeil aux fichiers qui ont été ajoutés dans le dossier &lt;code&gt;templates&lt;/code&gt; par Flex lors de l’installation de Twig, vous noterez la présence d’un fichier &lt;code&gt;base.html.twig&lt;/code&gt;.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&amp;lt;!-- templates/base.html.twig --&amp;gt;

&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
    &amp;lt;head&amp;gt;
        &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot; /&amp;gt;
        &amp;lt;title&amp;gt;{% block title %}Jobeet{% endblock %}&amp;lt;/title&amp;gt;
        {% block stylesheets %}{% endblock %}
    &amp;lt;/head&amp;gt;
    &amp;lt;body&amp;gt;
        {% block body %}{% endblock %}
        {% block javascripts %}{% endblock %}
    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Ce fichier correspond à un template qui a vocation de définir la mise en page globale de notre application. Twig fonctionne sur le principe d’héritage. Cela signifie que vous allez pouvoir définir des templates qui contiendront des éléments qui pourront ensuite être surchargés dans d’autres templates. Pour cela, Twig utilise un système de blocs (&lt;a href=&quot;https://twig.symfony.com/doc/2.x/tags/block.html &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;block&lt;/code&gt;&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Utilisons le fichier &lt;code&gt;templates/base.html.twig&lt;/code&gt; pour définir la mise en page de Jobeet. Pour commencer, nous allons étendre ce dernier dans notre template &lt;code&gt;templates/job/index.html.twig&lt;/code&gt; et surcharger le bloc &lt;code&gt;body&lt;/code&gt; pour y faire apparaître notre contenu :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&amp;lt;!-- templates/job/index.html.twig --&amp;gt;
{% extends &amp;quot;base.html&amp;quot; %}

{% block body %}
    &amp;lt;ul&amp;gt;
        {% for job in jobs %}
            &amp;lt;li&amp;gt;{{ job.position }}&amp;lt;/li&amp;gt;
        {% endfor %}
    &amp;lt;/ul&amp;gt;
{% endblock %}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Si vous rechargez la page, le résultat devrait être identique, mais nous avons beaucoup moins de code dans notre template, qui se concentre maintenant uniquement sur une tâche bien précise : afficher notre liste d’offres d’emploi.&lt;/p&gt;

&lt;p&gt;Pour que nous interfaces soient un plus agréables à utiliser, nous allons y ajouter un peu de style avec du CSS. Pour gagner du temps, nous allons utiliser le framework &lt;a href=&quot;http://getbootstrap.com/ &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;boostrap&lt;/a&gt; au travers de ce &lt;a href=&quot;&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;thème https://startbootstrap.com/template-overviews/1-col-portfolio/ &lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Une fois les fichiers téléchargés, nous allons placer les différents fichiers CSS et Javascript dans le dossier &lt;code&gt;public&lt;/code&gt; (car ces derniers doivent être desservis par le serveur HTTP). Nous les organiserons dans un dossier &lt;code&gt;vendor&lt;/code&gt; comme vous pouvez le voir dans le &lt;a href=&quot;https://github.com/jdecool/jobeet/tree/04-controleur-vue/public/vendor &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;dépôt Github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Commençons ensuite par modifier notre mise en page globale :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&amp;lt;!-- templates/base.html.twig --&amp;gt;

&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
    &amp;lt;head&amp;gt;
        &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;
        &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1, shrink-to-fit=no&amp;quot;&amp;gt;
        &amp;lt;title&amp;gt;{% block title %}Jobeet{% endblock %}&amp;lt;/title&amp;gt;
        &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;/vendor/bootstrap/css/bootstrap.min.css&amp;quot;&amp;gt;
        &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;/vendor/1-col-portfolio.css&amp;quot;&amp;gt;
        {% block stylesheets %}{% endblock %}
    &amp;lt;/head&amp;gt;
    &amp;lt;body&amp;gt;
        &amp;lt;nav class=&amp;quot;navbar navbar-expand-lg navbar-dark bg-dark fixed-top&amp;quot;&amp;gt;
            &amp;lt;div class=&amp;quot;container&amp;quot;&amp;gt;
                &amp;lt;a class=&amp;quot;navbar-brand&amp;quot; href=&amp;quot;#&amp;quot;&amp;gt;Jobeet&amp;lt;/a&amp;gt;
                &amp;lt;button class=&amp;quot;navbar-toggler&amp;quot; type=&amp;quot;button&amp;quot; data-toggle=&amp;quot;collapse&amp;quot; data-target=&amp;quot;#navbarResponsive&amp;quot; aria-controls=&amp;quot;navbarResponsive&amp;quot; aria-expanded=&amp;quot;false&amp;quot; aria-label=&amp;quot;Toggle navigation&amp;quot;&amp;gt;
                    &amp;lt;span class=&amp;quot;navbar-toggler-icon&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;
                &amp;lt;/button&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;/nav&amp;gt;

        &amp;lt;div class=&amp;quot;container&amp;quot;&amp;gt;
            {% block body %}{% endblock %}
        &amp;lt;/div&amp;gt;

        &amp;lt;script src=&amp;quot;/vendor/jquery/jquery-3.2.1.slim.min.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
        &amp;lt;script src=&amp;quot;/vendor/popper/popper.min.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
        &amp;lt;script src=&amp;quot;/vendor/bootstrap/js/bootstrap.min.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
        {% block javascripts %}{% endblock %}
    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Nous conservons les blocs Twig &lt;code&gt;stylesheets&lt;/code&gt; et &lt;code&gt;javascripts&lt;/code&gt; même si nous ne les utiliserons pas pour le moment. C’est dernier seront utiles à l’avenir pour inclure des fichiers CSS ou Javascript spécifiques à certaines vues.&lt;/p&gt;

&lt;p&gt;Pour finir, mettons en forme la présentation de nos offres d’emploi :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&amp;lt;!-- templates/job/index.html.twig --&amp;gt;
{% extends &amp;quot;base.html.twig&amp;quot; %}

{% block body %}
    &amp;lt;h1 class=&amp;quot;my-4&amp;quot;&amp;gt;Liste des offres&amp;lt;/h1&amp;gt;

    {% for job in jobs %}
        &amp;lt;div class=&amp;quot;row&amp;quot;&amp;gt;
            &amp;lt;div class=&amp;quot;col-md-7&amp;quot;&amp;gt;
                &amp;lt;a href=&amp;quot;#&amp;quot;&amp;gt;
                    &amp;lt;img class=&amp;quot;img-fluid rounded mb-3 mb-md-0&amp;quot; src=&amp;quot;/images/{{ job.logo }}&amp;quot; alt=&amp;quot;{{ job.company }}&amp;quot;&amp;gt;
                &amp;lt;/a&amp;gt;
            &amp;lt;/div&amp;gt;
            &amp;lt;div class=&amp;quot;col-md-5&amp;quot;&amp;gt;
                &amp;lt;h3&amp;gt;{{ job.position }}&amp;lt;/h3&amp;gt;
                &amp;lt;p&amp;gt;{{ job.description }}&amp;lt;/p&amp;gt;
                &amp;lt;p&amp;gt;Posted on {{ job.createdAt|date(&amp;quot;m/d/Y&amp;quot;) }}&amp;lt;/p&amp;gt;
                &amp;lt;a class=&amp;quot;btn btn-primary&amp;quot; href=&amp;quot;#&amp;quot;&amp;gt;See more&amp;lt;/a&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;

        &amp;lt;hr&amp;gt;
    {% endfor %}

    &amp;lt;ul class=&amp;quot;pagination justify-content-center&amp;quot;&amp;gt;
        &amp;lt;li class=&amp;quot;page-item disabled&amp;quot;&amp;gt;
            &amp;lt;a class=&amp;quot;page-link&amp;quot; href=&amp;quot;#&amp;quot; aria-label=&amp;quot;Previous&amp;quot;&amp;gt;
                &amp;lt;span aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;amp;laquo;&amp;lt;/span&amp;gt;
                &amp;lt;span class=&amp;quot;sr-only&amp;quot;&amp;gt;Previous&amp;lt;/span&amp;gt;
            &amp;lt;/a&amp;gt;
        &amp;lt;/li&amp;gt;
        &amp;lt;li class=&amp;quot;page-item&amp;quot;&amp;gt;
            &amp;lt;a class=&amp;quot;page-link&amp;quot; href=&amp;quot;#&amp;quot;&amp;gt;1&amp;lt;/a&amp;gt;
        &amp;lt;/li&amp;gt;
        &amp;lt;li class=&amp;quot;page-item disabled&amp;quot;&amp;gt;
            &amp;lt;a class=&amp;quot;page-link&amp;quot; href=&amp;quot;#&amp;quot; aria-label=&amp;quot;Next&amp;quot;&amp;gt;
                &amp;lt;span aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;amp;raquo;&amp;lt;/span&amp;gt;
                &amp;lt;span class=&amp;quot;sr-only&amp;quot;&amp;gt;Next&amp;lt;/span&amp;gt;
            &amp;lt;/a&amp;gt;
        &amp;lt;/li&amp;gt;
    &amp;lt;/ul&amp;gt;
{% endblock %}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;blockquote&gt;
  &lt;p&gt;Les images liées à notre jeu de données sont disponibles directement dans les &lt;a href=&quot;https://github.com/jdecool/jobeet/tree/04-controleur-vue/public/images &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;sources de ce billet&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Et voilà le rendu final de notre application Jobeet :&lt;/p&gt;

&lt;center&gt;&lt;img src=&quot;/img/blog/20170929-tutorial-jobeet-symfony-4-partie-4a-le-controleur-et-la-vue/final-render.png &quot; style=&quot;width: 100%; height: 100%;&quot; /&gt;&lt;/center&gt;

&lt;blockquote&gt;
  &lt;p&gt;Retrouvez tous les tutorials Jobeet disponibles depuis le &lt;a href=&quot;/blog/2017/09/12/tutorial-jobeet-symfony-4-introduction.html&quot;&gt;billet d’introduction de la série&lt;/a&gt;. Le code source de cette application est également disponible sur &lt;a href=&quot;https://github.com/jdecool/jobeet/tree/04-controleur-vue &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Github&lt;/a&gt;. Vous trouverez une branche associée à l’état du projet après chaque chapitre.&lt;/p&gt;
&lt;/blockquote&gt;
</description>
                    <pubDate>Fri, 29 Sep 2017 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2017/09/29/tutorial-jobeet-symfony-4-partie-4a-le-controleur-et-la-vue.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2017/09/29/tutorial-jobeet-symfony-4-partie-4a-le-controleur-et-la-vue.html</guid>
                </item>
            
        
            
                148
                <item>
                    <title>Ma semaine 38 de veille (18/09/2017 -&gt; 24/09/2017)</title>
                    <description>&lt;p&gt;Retrouvez chaque semaine la sélection de mes 5 tweets les plus populaires concernant
la veille que je partage sur Twitter. Abonnez-vous au &lt;a href=&quot;/feed/feed.xml &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;flux RSS&lt;/a&gt;,
du site ou plus précisément à celui de cette &lt;a href=&quot;/feed/feed-veille.xml &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;catégorie&lt;/a&gt;
pour être notifié des mises à jour.&lt;/p&gt;

&lt;h3 id=&quot;mon-actu-préférée&quot;&gt;Mon actu préférée&lt;/h3&gt;

&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;col-4&quot;&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-cards=&quot;hidden&quot; data-lang=&quot;fr&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Relicensing React, Jest, Flow, and Immutable.js : &lt;a href=&quot;https://t.co/uXpiZOHW3c&quot;&gt;https://t.co/uXpiZOHW3c&lt;/a&gt;&lt;/p&gt;&amp;mdash; Jérémy DECOOL (@jdecool) &lt;a href=&quot;https://twitter.com/jdecool/status/911480162591690752&quot;&gt;23 septembre 2017&lt;/a&gt;&lt;/blockquote&gt;
    &lt;script async=&quot;&quot; src=&quot;//platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
  &lt;div class=&quot;col-8 tweet-desc&quot;&gt;
    Ces dernières semaines, il y a eu beaucoup de polémiques concernant la licence des outils open source fournie par Facebook notamment à cause de la présence de clauses de types PATENTS (brevets). Après avoir annoncé malgré cela qu&apos;il ne changerait pas la licence, la société vient de décider de faire machine arrière.
  &lt;/div&gt;
&lt;/div&gt;

&lt;h3 id=&quot;le-top-de-la-semaine&quot;&gt;Le top de la semaine&lt;/h3&gt;

&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;col-4&quot;&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-cards=&quot;hidden&quot; data-lang=&quot;fr&quot;&gt;&lt;p lang=&quot;fr&quot; dir=&quot;ltr&quot;&gt;Stack Overflow lance un calculateur de salaire pour les développeurs : &lt;a href=&quot;https://t.co/7nBwZrRTL3&quot;&gt;https://t.co/7nBwZrRTL3&lt;/a&gt;&lt;/p&gt;&amp;mdash; Jérémy DECOOL (@jdecool) &lt;a href=&quot;https://twitter.com/jdecool/status/911120140380655616&quot;&gt;22 septembre 2017&lt;/a&gt;&lt;/blockquote&gt;
  &lt;/div&gt;
  &lt;div class=&quot;col-8 tweet-desc&quot;&gt;
    Stack Overflow l&apos;un des sites les plus consultés par les développeurs, propose dorénavant un calculateur de salaire. Les chiffres sont basés sur l&apos;enquête annuelle diffusée auprès des utilisateurs. Les résultats sont classés selon cinq critères : le lieu de travail, la formation, les années d&apos;expérience, le type de développement et les technnologies utilisées.
  &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;&lt;div class=&quot;col-12&quot;&gt;&lt;hr /&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;col-4&quot;&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-cards=&quot;hidden&quot; data-lang=&quot;fr&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;What Motivates Me as a Programmer : &lt;a href=&quot;https://t.co/VQk2dGybvQ&quot;&gt;https://t.co/VQk2dGybvQ&lt;/a&gt;&lt;/p&gt;&amp;mdash; Jérémy DECOOL (@jdecool) &lt;a href=&quot;https://twitter.com/jdecool/status/910399896104161280&quot;&gt;20 septembre 2017&lt;/a&gt;&lt;/blockquote&gt;
  &lt;/div&gt;
  &lt;div class=&quot;col-8 tweet-desc&quot;&gt;
    Un article de Yegor Bugayenko, un développeur russe qui décrit dans cet article, les différents éléments qui le motive dans son travail. Je pense que parmi les nombreux points évoqués, un bon nombre de développeurs y trouveront une similitude avec eux-mêmes.
  &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;&lt;div class=&quot;col-12&quot;&gt;&lt;hr /&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;col-4&quot;&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;fr&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;🚨 iTerm2 leaks everything you hover in your terminal via DNS requests 🚨  &lt;a href=&quot;https://t.co/Ad7jzDCLJM&quot;&gt;https://t.co/Ad7jzDCLJM&lt;/a&gt;&lt;/p&gt;&amp;mdash; Jérémy DECOOL (@jdecool) &lt;a href=&quot;https://twitter.com/jdecool/status/910886133235617793&quot;&gt;21 septembre 2017&lt;/a&gt;&lt;/blockquote&gt;
  &lt;/div&gt;
  &lt;div class=&quot;col-8 tweet-desc&quot;&gt;
    Si vous développez sur Mac, il y a de très fortes chances pour que vous utilisiez iTerm, une alternative au terminal MacOS fourni par défaut. Une faille de sécurité a été découverte dans ce dernier. Vous trouverez les explications la concernant dans ce billet.&lt;br /&gt;&lt;br /&gt;
    &lt;b&gt;Edit :&lt;/b&gt; Le lien du tweet ne semble plus fonctionnel. Voici un &lt;a href=&quot;https://murze.be/2017/09/%f0%9f%9a%a8-iterm2-leaks-everything-hover-terminal-via-dns-requests-%f0%9f%9a%a8/ &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;lien valide de l&apos;article&lt;/a&gt;.
  &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;&lt;div class=&quot;col-12&quot;&gt;&lt;hr /&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;col-4&quot;&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;fr&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Promise-Based Cache With ReactPHP : &lt;a href=&quot;https://twitter.com/zhukserega&quot;&gt;@zhukserega&lt;/a&gt; &lt;a href=&quot;https://t.co/6Y8kOrZVHJ&quot;&gt;https://t.co/6Y8kOrZVHJ&lt;/a&gt;&lt;/p&gt;&amp;mdash; Jérémy DECOOL (@jdecool) &lt;a href=&quot;https://twitter.com/jdecool/status/910765791339073536&quot;&gt;21 septembre 2017&lt;/a&gt;&lt;/blockquote&gt;
  &lt;/div&gt;
  &lt;div class=&quot;col-8 tweet-desc&quot;&gt;
    Comment mettre en place une stratégie de cache en PHP basée sur des promesses avec ReactPHP ? Si vous souhaitez voir à quoi cela ressemble et si vous vous posez la question, c&apos;est &lt;a href=&quot;https://t.co/6Y8kOrZVHJ &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;par ici&lt;/a&gt;.
  &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;&lt;div class=&quot;col-12&quot;&gt;&lt;hr /&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;col-4&quot;&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-cards=&quot;hidden&quot; data-lang=&quot;fr&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Handling 1 Million Requests per Minute with Golang : &lt;a href=&quot;https://t.co/FhExHDjapH&quot;&gt;https://t.co/FhExHDjapH&lt;/a&gt;&lt;/p&gt;&amp;mdash; Jérémy DECOOL (@jdecool) &lt;a href=&quot;https://twitter.com/jdecool/status/910039271880368129&quot;&gt;19 septembre 2017&lt;/a&gt;&lt;/blockquote&gt;
  &lt;/div&gt;
  &lt;div class=&quot;col-8 tweet-desc&quot;&gt;
    Go est un langage souvent utilisé dans le cas de site à haut traffic du fait de ses performances. Dans ce billet Marcio Castilho, CEO d&apos;une solution antispam de SMS pour téléphone, explique comment en utilisant Go, ce dernier a réussi à supporter une charge de 1 million de requête par seconde. Un article très intéressant si vous êtes fan de webperf.
  &lt;/div&gt;
&lt;/div&gt;
</description>
                    <pubDate>Sun, 24 Sep 2017 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2017/09/24/ma-semaine-de-veille-38.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2017/09/24/ma-semaine-de-veille-38.html</guid>
                </item>
            
        
            
                149
                <item>
                    <title>Tutorial Jobeet pour Symfony 4 - Partie 3B: Les données initiales</title>
                    <description>&lt;p&gt;Nous avons donc créé notre base et données avec les tables qui permettront de stocker les informations que notre application va manipuler. Avant de commencer l’implémentation des fonctionnalités évoquées dans les billets précédents, nous allons commencer par insérer un jeu de données initiales (également appelées &lt;strong&gt;fixtures&lt;/strong&gt;) afin de ne pas démarrer avec un projet vide.&lt;/p&gt;

&lt;p&gt;Pour créer nos données, nous allons utiliser un bundle fourni par les équipes de Doctrine et qui propose un moyen de charger des données en base. Nous avions jusqu’à maintenant utilisé uniquement des modules officiels de Symfony. Le bundle que nous allons utiliser (&lt;code&gt;DoctrineFixturesBundle&lt;/code&gt;) est un module non officiel de Symfony. Or, par défaut, Symfony Flex ne permet de ne travailler qu’avec les bundles officiels de Symfony.&lt;/p&gt;

&lt;p&gt;Il est néanmoins possible d’utiliser Flex avec ces bundles, mais il sera nécessaire de le spécifier explicitement dans la configuration. Pour cela, nous allons modifier la valeur du paramètre &lt;code&gt;allow-contrib&lt;/code&gt; présent dans notre fichier &lt;code&gt;composer.json&lt;/code&gt; au travers de la commande suivante :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;$ composer config extra.symfony.allow-contrib true&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Une fois effectué, il sera possible de télécharger la dépendance. Cette dernière n’étant utilisée qu’en développement, nous allons utiliser l’option &lt;code&gt;--dev&lt;/code&gt; de Composer afin de l’installer comme tel :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;$ composer require doctrine/doctrine-fixtures-bundle --dev

Using version ^2.4 for doctrine/doctrine-fixtures-bundle
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 2 installs, 0 updates, 0 removals
  - Installing doctrine/data-fixtures (v1.2.2): Loading from cache
  - Installing doctrine/doctrine-fixtures-bundle (v2.4.0): Loading from cache
Writing lock file
Generating autoload files
Symfony operations: 1 recipe
  - Configuring doctrine/doctrine-fixtures-bundle (2.4): From github.com/symfony/recipes-contrib:master
Executing script make cache-warmup [OK]
Executing script assets:install --symlink --relative public [OK]&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;L’installation de cette dépendance va créer un dossier &lt;code&gt;src/DataFixtures/ORM&lt;/code&gt; qui contiendra nos déclarations de fixtures. Mais avant d’écrire ces dernières, vous avez peut-être remarqué, que lorsque nous avons écrit nos entités, nous avons déclaré les propriétés de nos objets comment étant &lt;code&gt;private&lt;/code&gt;. Nous allons donc devoir commencer par écrire les méthodes &lt;code&gt;get&lt;/code&gt; qui permettront d’accéder à nos propriétés ainsi que les méthodes &lt;code&gt;set&lt;/code&gt; qui permettront de modifier la valeur de nos propriétés.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;L’activation du paramètre &lt;code&gt;allow-contrib&lt;/code&gt; notamment permis la création du répertoire qui accueillera nos fixtures. Même si nous n’avions pas changé cette configuration, il aurait été possible d’installer le bundle, mais nous n’aurions pas profité des mécanismes de Flex qui permettent de créer une configuration du bundle par défaut.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Si vous avez eu la curiosité de regarder la liste des commandes proposées par le bundle Doctrine, vous aurez peut-être remarqué qu’il existe une commande &lt;code&gt;doctrine:generate:entities&lt;/code&gt; dont le rôle est justement de remplir cette tâche.&lt;/p&gt;

&lt;p&gt;Notre projet suit la convention &lt;a href=&quot; http://www.php-fig.org/psr/psr-4 &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;PSR-4&lt;/a&gt; décrivant la norme sur le chargement des fichiers PHP en fonction de leur arborescence (appelée &lt;a href=&quot;http://php.net/autoload &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;autoloading&lt;/a&gt;). Malheureusement, le générateur de Doctrine n’est actuellement pas compatible avec cette norme (et ce n’est pas prévu à court ou moyen terme).&lt;/p&gt;

&lt;p&gt;Mais cela n’est pas un problème étant donné que la plupart des outils de développement (tel que PHPStorm, Netbeans, Eclipse, Sublime Text, Atom, VSCode, …) ont une fonctionnalité (ou des plugins) permettant de générer du code automatiquement et notamment nos getters et setters.&lt;/p&gt;

&lt;p&gt;Nous allons donc générer des méthodes &lt;code&gt;get&lt;/code&gt; pour l’ensemble des propriétés de nos entités ainsi que les méthodes &lt;code&gt;set&lt;/code&gt; associées, à l’exception des propriétés &lt;code&gt;$id&lt;/code&gt; (car une clé primaire ne doit pouvoir être modifié), &lt;code&gt;createdAt&lt;/code&gt; et &lt;code&gt;updateAt&lt;/code&gt; (car nous avons déjà ajouté les méthodes permettant de modifier ces données).&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Si vous souhaitez gagner du temps, vous pouvez accéder au code source correspondant à ce billet sur &lt;a href=&quot;https://github.com/jdecool/jobeet/tree/03b-donnees-initiales &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Github&lt;/a&gt; pour copier/coller les méthodes décrites ci-dessus.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Nous pouvons maintenant commencer à écrire nos fixtures, c’est-à-dire les classes qui vont s’occuper de la création notre jeu de données initiales. Nous allons commencer par créer des catégories d’offre d’emploi en créant une classe &lt;code&gt;LoadCategoryData&lt;/code&gt; dans le dossier &lt;code&gt;src/DataFixtures/ORM&lt;/code&gt; :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&amp;lt;?php //src/DataFixtures/ORM/LoadCategoryData.php

namespace App\DataFixtures\ORM;

use App\Entity\Category;
use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Doctrine\Common\Persistence\ObjectManager;

class LoadCategoryData extends AbstractFixture implements OrderedFixtureInterface
{
    public function load(ObjectManager $manager)
    {
        $design = new Category();
        $design-&amp;gt;setName(&amp;#39;Design&amp;#39;);
        $manager-&amp;gt;persist($design);

        $programming = new Category();
        $programming-&amp;gt;setName(&amp;#39;Programming&amp;#39;);
        $manager-&amp;gt;persist($programming);

        $managing = new Category();
        $managing-&amp;gt;setName(&amp;#39;Manager&amp;#39;);
        $manager-&amp;gt;persist($managing);

        $administrator = new Category();
        $administrator-&amp;gt;setName(&amp;#39;Administrator&amp;#39;);
        $manager-&amp;gt;persist($administrator);

        $manager-&amp;gt;flush();

        $this-&amp;gt;addReference(&amp;#39;category-design&amp;#39;, $design);
        $this-&amp;gt;addReference(&amp;#39;category-programming&amp;#39;, $programming);
        $this-&amp;gt;addReference(&amp;#39;category-managing&amp;#39;, $managing);
        $this-&amp;gt;addReference(&amp;#39;category-administrator&amp;#39;, $administrator);
    }

    public function getOrder()
    {
        return 1;
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Pour charger des données, notre classe doit étendre de &lt;code&gt;AbstractFixture&lt;/code&gt; qui contient des fonctions de base de gestion de fixtures. Nous implémentons ensuite une interface &lt;code&gt;OrderedFixtureInterface&lt;/code&gt; permettant de spécifier un ordre d’ insertion des nos fixtures (dans le cas présent, nous ne pouvons enregistrer une offre d’emploi si nous n’avons pas auparavant créé des catégories) au travers de la méthode &lt;code&gt;getOrder()&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Mise à jour du 04/03/2018 :&lt;/strong&gt; Ce chapitre a été initialement rédigé sur la version 3.4 de Symfony. Depuis, la version installée de Doctrine a évolué. La classe &lt;code&gt;Doctrine\Common\DataFixtures\AbstractFixture&lt;/code&gt; est maintenant remplacée par &lt;code&gt;Doctrine\Bundle\FixturesBundle\Fixture&lt;/code&gt; et l’interface &lt;code&gt;Doctrine\Common\DataFixtures\OrderedFixtureInterface&lt;/code&gt; par &lt;code&gt;Doctrine\Common\DataFixtures\DependentFixtureInterface&lt;/code&gt;. Le fonctionnement pour charger des fixtures dépendantes les unes des autres a évolué, et il n’est plus nécessaire de définir l’ordre de chargement de ces dernières. L’interface &lt;code&gt;DependentFixtureInterface&lt;/code&gt; définit une méthode retournant un tableau contenant les classes dont dépend le jeu de données courant. Vous n’avez donc plus à déterminer l’ordre de chargement des &lt;em&gt;fixtures&lt;/em&gt;, Doctrine s’en charge dorénavant pour vous.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Le chargement des données se fait au sein d’une méthode &lt;code&gt;load(ObjectManager $manager)&lt;/code&gt;. Il s’agit de la méthode appelée lorsque les données doivent être insérées. Cette méthode prend en paramètre un objet de type &lt;code&gt;ObjectManager&lt;/code&gt; qui est l’objet Doctrine qui permet de travailler avec la base de données.&lt;/p&gt;

&lt;p&gt;Ce dernier permet d’appeler la méthode &lt;code&gt;persist&lt;/code&gt; pour indiquer à Doctrine que nous souhaitons persister un objet en base. Pour que la donnée soit réellement écrite en base, on utilise un appel à la méthode &lt;code&gt;flush&lt;/code&gt;. Dans le cas où l’on fait plusieurs appels à la méthode &lt;code&gt;persist&lt;/code&gt; (comme c’est le cas ici) et qu’ensuite on &lt;code&gt;flush&lt;/code&gt;, les insertions en bases sont réalisés au sein d’une transaction.&lt;/p&gt;

&lt;p&gt;Une fois les données enregistrées, nous souhaitons les rendre accessibles depuis nos autres fixtures pour par exemple pouvoir affecter une catégorie à une offre d’emploi. Pour cela, nous allons définir explicitement les objets auxquels nous souhaitons accéder en appelant la méthode &lt;code&gt;addReference&lt;/code&gt; et en associant une clé à notre donnée.&lt;/p&gt;

&lt;p&gt;Nous pouvons ensuite passer à la fixture suivante :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&amp;lt;?php  //src/DataFixtures/ORM/LoadJobData.php

namespace App\DataFixtures\ORM;

use App\Entity\Job;
use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Doctrine\Common\Persistence\ObjectManager;

class LoadJobData extends AbstractFixture implements OrderedFixtureInterface
{
    public function load(ObjectManager $manager)
    {
        $categoryProgramming = $this-&amp;gt;getReference(&amp;#39;category-programming&amp;#39;);
        $categoryDesign = $this-&amp;gt;getReference(&amp;#39;category-design&amp;#39;);

        $jobSensioLabs = new Job();
        $jobSensioLabs-&amp;gt;setCategory($categoryProgramming);
        $jobSensioLabs-&amp;gt;setType(&amp;#39;full-time&amp;#39;);
        $jobSensioLabs-&amp;gt;setCompany(&amp;#39;Sensio Labs&amp;#39;);
        $jobSensioLabs-&amp;gt;setLogo(&amp;#39;sensio-labs.gif&amp;#39;);
        $jobSensioLabs-&amp;gt;setUrl(&amp;#39;http://www.sensiolabs.com/&amp;#39;);
        $jobSensioLabs-&amp;gt;setPosition(&amp;#39;Web Developer&amp;#39;);
        $jobSensioLabs-&amp;gt;setLocation(&amp;#39;Paris, France&amp;#39;);
        $jobSensioLabs-&amp;gt;setDescription(&amp;quot;You&amp;#39;ve already developed websites with symfony and you want to work with Open-Source technologies. You have a minimum of 3 years experience in web development with PHP or Java and you wish to participate to development of Web 2.0 sites using the best frameworks available.&amp;quot;);
        $jobSensioLabs-&amp;gt;setHowToApply(&amp;#39;Send your resume to fabien.potencier [at] sensio.com&amp;#39;);
        $jobSensioLabs-&amp;gt;setIsPublic(true);
        $jobSensioLabs-&amp;gt;setIsActivated(true);
        $jobSensioLabs-&amp;gt;setToken(&amp;#39;job_sensio_labs&amp;#39;);
        $jobSensioLabs-&amp;gt;setEmail(&amp;#39;job@example.com&amp;#39;);
        $jobSensioLabs-&amp;gt;setExpiresAt(new \DateTime(&amp;#39;2012-10-10&amp;#39;));
        $manager-&amp;gt;persist($jobSensioLabs);

        $jobExtremeSensio = new Job();
        $jobExtremeSensio-&amp;gt;setCategory($categoryDesign);
        $jobExtremeSensio-&amp;gt;setType(&amp;#39;part-time&amp;#39;);
        $jobExtremeSensio-&amp;gt;setCompany(&amp;#39;Extreme Sensio&amp;#39;);
        $jobExtremeSensio-&amp;gt;setLogo(&amp;#39;extreme-sensio.gif&amp;#39;);
        $jobExtremeSensio-&amp;gt;setUrl(&amp;#39;http://www.extreme-sensio.com/&amp;#39;);
        $jobExtremeSensio-&amp;gt;setPosition(&amp;#39;Web Designer&amp;#39;);
        $jobExtremeSensio-&amp;gt;setLocation(&amp;#39;Paris, France&amp;#39;);
        $jobExtremeSensio-&amp;gt;setDescription(&amp;#39;Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in.&amp;#39;);
        $jobExtremeSensio-&amp;gt;setHowToApply(&amp;#39;Send your resume to fabien.potencier [at] sensio.com&amp;#39;);
        $jobExtremeSensio-&amp;gt;setIsPublic(true);
        $jobExtremeSensio-&amp;gt;setIsActivated(true);
        $jobExtremeSensio-&amp;gt;setToken(&amp;#39;job_extreme_sensio&amp;#39;);
        $jobExtremeSensio-&amp;gt;setEmail(&amp;#39;job@example.com&amp;#39;);
        $jobExtremeSensio-&amp;gt;setExpiresAt(new \DateTime(&amp;#39;2012-10-10&amp;#39;));
        $manager-&amp;gt;persist($jobExtremeSensio);

        $manager-&amp;gt;flush();
    }

    public function getOrder()
    {
        return 2;
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;blockquote&gt;
  &lt;p&gt;Notez dans le code ci-dessus l’utilisation de la méthode &lt;code&gt;getReference&lt;/code&gt; nous permettant de récupérer une donnée qui a été créée dans la fixture précédente.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Il ne reste maintenant plus qu’à charger nos données au travers de la commande :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;$ bin/console doctrine:fixtures:load
Careful, database will be purged. Do you want to continue y/N ?y
  &amp;gt; purging database
  &amp;gt; loading [1] App\DataFixtures\ORM\LoadCategoryData
  &amp;gt; loading [2] App\DataFixtures\ORM\LoadJobData&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Et voilà !&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Retrouvez tous les tutorials Jobeet disponibles depuis le &lt;a href=&quot;/blog/2017/09/12/tutorial-jobeet-symfony-4-introduction.html&quot;&gt;billet d’introduction de la série&lt;/a&gt;. Le code source de cette application est également disponible sur &lt;a href=&quot;https://github.com/jdecool/jobeet/tree/03b-donnees-initiales &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Github&lt;/a&gt;. Vous trouverez une branche associée à l’état du projet après chaque chapitre.&lt;/p&gt;
&lt;/blockquote&gt;
</description>
                    <pubDate>Thu, 21 Sep 2017 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2017/09/21/tutorial-jobeet-symfony-4-partie-3b-les-donnees-initiales.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2017/09/21/tutorial-jobeet-symfony-4-partie-3b-les-donnees-initiales.html</guid>
                </item>
            
        
            
                150
                <item>
                    <title>Tutorial Jobeet pour Symfony 4 - Partie 3A: Le modèle de données</title>
                    <description>&lt;p&gt;Maintenant que l’aspect fonctionnel de notre projet a été défini, nous allons pouvoir créer le modèle de données associé à notre application, c’est-à-dire les classes qui permettront d’interagir avec la base de données. Nous allons pour cela avoir recours à un ORM. Ce sera également l’occasion de voir comment ajouter et configurer un module tier dans notre projet.&lt;/p&gt;

&lt;p&gt;D’après &lt;a href=&quot;/blog/2017/09/19/tutorial-jobeet-symfony-4-partie-2-le-projet.html&quot;&gt;les scénarios&lt;/a&gt;  que nous avons précédemment écrits, voici le schéma correspondant aux relations entre entités que l’on peut en déduire :&lt;/p&gt;

&lt;center&gt;&lt;img src=&quot;/img/blog/20170920-tutorial-jobeet-symfony-4-partie-3a-le-modele-de-donnees/database.png &quot; /&gt;&lt;/center&gt;

&lt;p&gt;En plus des informations, nous avons également ajouté un champ &lt;code&gt;created_at&lt;/code&gt; et &lt;code&gt;updated_at&lt;/code&gt; à certaines tables afin de conserver une trace des dernières modifications des données que nous allons traiter.&lt;/p&gt;

&lt;p&gt;Pour stocker les informations de l’application, nous allons utiliser une base de données relationnelle. Symfony étant un framework orienté-objet, nous allons donc manipuler les informations sous la forme d’objet. Les informations de notre base de données doivent ainsi être mappées avec notre modèle objet et pour cela nous allons utiliser un ORM.&lt;/p&gt;

&lt;p&gt;Symfony 4 laisse le choix au développeur sur les outils qu’il souhaite utiliser. Contrairement aux versions précédentes, Symfony est livré “vide” et n’impose aucun choix par défaut. Pour ce tutorial, nous allons faire le choix d’utiliser l’ORM &lt;a href=&quot;http://www.doctrine-project.org/projects/orm.html &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Doctrine&lt;/a&gt; qui est l’ORM le plus répandu dans l’écosystème PHP. Mais il serait tout à fait possible d’utiliser une simple connexion &lt;a href=&quot;http://php.net/manual/fr/book.pdo.php &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;PDO&lt;/a&gt; ou &lt;a href=&quot;http://www.doctrine-project.org/projects/dbal.html &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Doctrine DBAL&lt;/a&gt;, ou un autre ORM tel que &lt;a href=&quot;http://propelorm.org/ &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Propel&lt;/a&gt;, &lt;a href=&quot;https://laravel.com/docs/5.0/eloquent &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Eloquent&lt;/a&gt;, &lt;a href=&quot;javascript:;&quot; class=&quot;broken-link&quot; rel=&quot;nofollow&quot; data-original-url=&quot;http://www.pomm-project.org/ &quot;&gt;Pomm&lt;/a&gt; ou n’importe quel autre outil.&lt;/p&gt;

&lt;p&gt;Avant de créer et configurer les classes de notre modèle de données, nous allons commencer par télécharger Doctrine. Pour intégrer ce dernier dans notre projet Symfony, nous devrons récupérer deux dépendances. La première, &lt;code&gt;doctrine/orm&lt;/code&gt; est l’ORM en tant que tel. La seconde dépendance consiste à intégrer l’ORM dans notre architecture Symfony. Symfony facilite le développement et la réutilisation de module que l’on peut partager entre plusieurs projets. Ces modules (des plugins en quelque sorte) sont appelés &lt;strong&gt;bundles&lt;/strong&gt; dans l’écosystème du framework. Il convient donc de télécharger la dépendance &lt;code&gt;doctrine/doctrine-bundle&lt;/code&gt; qui va intégrer l’ORM dans notre environnement Symfony.&lt;/p&gt;

&lt;p&gt;Afin d’éviter au développeur d’avoir à installer deux dépendances distinctes, les équipes de développement fournissent un méta-paquet Composer permettant d’installer les deux éléments d’un coup. Pour intégrer Doctrine à notre projet, il nous suffit donc de récupérer la dépendance &lt;code&gt;symfony/orm-pack&lt;/code&gt;.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;$ composer require symfony/orm-pack

Using version ^1.0 for symfony/orm-pack
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 20 installs, 0 updates, 0 removals
  - Installing ocramius/package-versions (1.1.3): Loading from cache
  - Installing zendframework/zend-eventmanager (3.2.0): Loading from cache
  - Installing zendframework/zend-code (3.3.0): Loading from cache
  - Installing ocramius/proxy-manager (2.1.1): Loading from cache
  - Installing doctrine/lexer (v1.0.1): Loading from cache
  - Installing doctrine/inflector (v1.2.0): Loading from cache
  - Installing doctrine/collections (v1.5.0): Loading from cache
  - Installing doctrine/cache (v1.7.1): Loading from cache
  - Installing doctrine/annotations (v1.5.0): Loading from cache
  - Installing doctrine/common (v2.8.1): Loading from cache
  - Installing doctrine/dbal (v2.6.3): Loading from cache
  - Installing doctrine/migrations (v1.6.1): Loading from cache
  - Installing symfony/doctrine-bridge (v4.0.0-RC1): Loading from cache
  - Installing doctrine/doctrine-cache-bundle (1.3.2): Loading from cache
  - Installing jdorn/sql-formatter (v1.2.17): Loading from cache
  - Installing doctrine/doctrine-bundle (1.8.0): Loading from cache
  - Installing doctrine/doctrine-migrations-bundle (v1.3.1): Loading from cache
  - Installing doctrine/instantiator (1.1.0): Loading from cache
  - Installing doctrine/orm (v2.5.12): Loading from cache
Writing lock file
Generating autoload files
Symfony operations: 4 recipes (9eb5d7e36646d167c1d8407c068032b9)
  - Configuring doctrine/annotations (1.0): From github.com/symfony/recipes:master
  - Configuring doctrine/doctrine-cache-bundle (1.3.2): From auto-generated recipe
  - Configuring doctrine/doctrine-bundle (1.6): From github.com/symfony/recipes:master
  - Configuring doctrine/doctrine-migrations-bundle (1.2): From github.com/symfony/recipes:master
ocramius/package-versions:  Generating version class...
ocramius/package-versions: ...done generating version class
Executing script cache:clear [OK]
Executing script assets:install --symlink --relative public [OK]


 Next: Configuration


  * Modify your DATABASE_URL config in .env

  * Configure the driver (mysql) and
    server_version&amp;lt;/fg&amp;gt; (5.7) in config/packages/doctrine.yaml&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Pour utiliser un bundle dans un projet, il est nécessaire d’activer ce dernier afin qu’il soit reconnu par le framework. Cette configuration s’effectue dans le fichier &lt;code&gt;config/bundles.php&lt;/code&gt;. Ce fichier retourne un tableau associatif où la clé des éléments correspond au namespace complet du bundle à activer et dont la valeur est également un tableau associatif indiquant les environnements pour lesquels le bundle est actif (ou non).&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&amp;lt;?php // config/bundles.php

return [
    Symfony\Bundle\FrameworkBundle\FrameworkBundle::class =&amp;gt; [&amp;#39;all&amp;#39; =&amp;gt; true],
    Symfony\Bundle\WebServerBundle\WebServerBundle::class =&amp;gt; [&amp;#39;dev&amp;#39; =&amp;gt; true],
    Doctrine\Bundle\DoctrineCacheBundle\DoctrineCacheBundle::class =&amp;gt; [&amp;#39;all&amp;#39; =&amp;gt; true],
    Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class =&amp;gt; [&amp;#39;all&amp;#39; =&amp;gt; true],
    Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class =&amp;gt; [&amp;#39;all&amp;#39; =&amp;gt; true],
];&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;En réalité, l’activation d’un bundle se fait rarement manuellement. Effectivement, &lt;a href=&quot;https://github.com/symfony/flex &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Symfony Flex&lt;/a&gt;, que nous avons évoqué brièvement lors de la mise en place du projet se chargera de cette action. Il sera néanmoins parfois nécessaire de corriger la configuration par défaut en activant ou désactivant le bundle pour certains environnements.&lt;/p&gt;

&lt;p&gt;Maintenant que Doctrine est installé et activé dans notre projet, nous allons pouvoir commencer à paramétrer notre application pour qu’elle puisse accéder à une base de données. Pour cela nous allons renseigner les paramètres nécessaires à l’établissement de la connexion. Cette dernière étant propre à l’environnement d’exécution de notre code, nous allons définir les paramètres dans le fichier &lt;code&gt;.env&lt;/code&gt;. Ce dernier a d’ailleurs été prérempli avec une configuration de base par Flex :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;# .env
# ...

###&amp;gt; doctrine/doctrine-bundle ###
# Format described at http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url
# For an SQLite database, use: &amp;quot;sqlite:///%kernel.project_dir%/var/data.db&amp;quot;
# Configure your db driver and server_version in config/packages/doctrine.yaml
DATABASE_URL=mysql://db_user:db_password@127.0.0.1:3306/db_name
###&amp;lt; doctrine/doctrine-bundle ###&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;blockquote&gt;
  &lt;p&gt;Vous avez certainement noté la présence des fichiers &lt;code&gt;.env&lt;/code&gt; et &lt;code&gt;.env.dist&lt;/code&gt;. Le fichier &lt;code&gt;.env&lt;/code&gt; est le fichier qui contient réellement la configuration de notre application. Contenant des informations pouvant être très sensibles (comme par exemple des mots de passe), il n’est pas versionné.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;C’est pour cela qu’un fichier &lt;code&gt;.env.dist&lt;/code&gt; est présent. Ce dernier qui lui est versionné sert de modèle pour que les développeurs qui vont être ammenés à travailler sur le projet puisse connaître les informations à renseigner.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;L’exemple de configuration qui a été inséré par Symfony Flex permet de se connecter à une base de données MySQL. Pour les besoins de ce tutorial, ainsi que pour éviter l’installation d’un serveur, nous allons utiliser SQLite qui est un système de base de données ne nécessitant pas une architecture client-serveur. Il sera nécessaire de vérifier que le driver SQLite de PHP (&lt;code&gt;php-sqlite3&lt;/code&gt;) soit installé et activé. Nous allons ensuite modifier la variable d’environnement &lt;code&gt;DATABASE_URL&lt;/code&gt; comme suit :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;DATABASE_URL=sqlite:///%kernel.project_dir%/var/jobeet.db&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;blockquote&gt;
  &lt;p&gt;Dans cette configuration, nous faisons référence à un paramètre &lt;code&gt;kernel.project_dir&lt;/code&gt; définit par le framework (facilement reconnaissable car il est entouré du caractère &lt;code&gt;%&lt;/code&gt;). Ce dernier fait référence à la racine du dossier du projet et permet ainsi de définir facilement l’endroit où l’on souhaite enregistrer nos données.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;span style=&quot;text-decoration: line-through;&quot;&gt;&lt;em&gt;Mise à jour du 23/09/2017 :&lt;/em&gt; En parcourant le projet sur Github, j’ai trouvé l’&lt;a href=&quot;https://github.com/symfony/flex/issues/129 &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;issue concernant ce problème&lt;/a&gt; ainsi que &lt;a href=&quot;https://github.com/symfony/symfony/pull/23901 &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;sa résolution&lt;/a&gt;. Tout devrait rentrer dans l’ordre lors de la publication de la branche 3.4 du projet.&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Le dossier &lt;code&gt;var&lt;/code&gt; qui a été créé lors de la mise en place de Doctrine est, par convention, un dossier qui va contenir les fichiers écrits par notre application durant son fonctionnement (tel que des logs, des fichiers de cache, …). C’est donc tout naturellement dans ce dernier que nous allons stocker le fichier qui contiendra nos données SQLite.&lt;/p&gt;

&lt;p&gt;Nous allons maintenant pouvoir démarrer l’écriture des classes qui vont modéliser nos données. Par défaut Doctrine est configuré pour travailler avec des annotations. Personnellement, je préfère séparer le code de sa configuration, c’est pour cela quand dans ce tutorial, nous utiliserons une configuration en YAML (il est également possible d’avoir une configuration en XML). Pour cela, nous allons éditer le fichier de configuration &lt;code&gt;config/packages/doctrine.yaml&lt;/code&gt; pour y mettre le contenu ci-dessous :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;# config/packages/doctrine.yaml
parameters:
    env(DATABASE_URL): &amp;#39;sqlite:///%kernel.project_dir%/var/jobeet.db&amp;#39;

doctrine:
    dbal:
        driver: &amp;#39;pdo_sqlite&amp;#39;
        url: &amp;#39;%env(resolve:DATABASE_URL)%&amp;#39;
    orm:
        auto_generate_proxy_classes: &amp;#39;%kernel.debug%&amp;#39;
        naming_strategy: doctrine.orm.naming_strategy.underscore
        auto_mapping: true
        mappings:
            App:
                is_bundle: false
                type: yml # annotation ou xml
                dir: &amp;#39;%kernel.project_dir%/config/doctrine/mapping&amp;#39; # configuration du mapping
                prefix: &amp;#39;App\Entity&amp;#39;
                alias: App&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;On retrouve dans cette configuration la variable &lt;code&gt;%kernel.project_dir%&lt;/code&gt; faisant référence au dossier racine de notre projet. Lorsqu’un paramètre de configuration est définit dans le framework. De la même façon, il est possible d’accéder à une variable d’environnement via la syntaxe &lt;code&gt;%env(MA_VARIABLE)%&lt;/code&gt; (comme dans le fichier Doctrine pour accéder à la chaine de connexion à la base de données).&lt;/p&gt;

&lt;p&gt;Notons également la présence du paramètre &lt;code&gt;env(DATABASE_URL)&lt;/code&gt; permettant de définir le paramètre contenant la chaîne de connexion à notre base de données dans le cas où la variable d’environnement n’existerait pas.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Si vous analysez l’arborescence des fichiers, vous constaterez qu’il existe deux fichiers de configuration Doctrine : &lt;code&gt;config/packages/doctrine.yaml&lt;/code&gt; et &lt;code&gt;config/packages/prod/doctrine.yaml&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;Le premier est un fichier de configuration commun à tous les environnements. Il est ensuite possible de définir une configuration spécifique pour un environnement (défini par la variable &lt;code&gt;APP_ENV&lt;/code&gt; du fichier &lt;code&gt;.env&lt;/code&gt;). Pour cela, il suffit de déposer le fichier de configuration dans un sous-dossier portant le nom de l’environnement pour lequel on souhaite surcharger la configuration et le framework le prendra automatiquement en compte.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Nous n’irons pas plus loin dans la configuration de Doctrine. Si vous après ce tutorial, vous souhaitez en savoir plus, je vous conseille de consulter la &lt;a href=&quot;http://symfony.com/doc/current/doctrine.html &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;documentation officielle&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Créons maintenant les classes associées à notre modèle de données. Elles vont représenter les informations de la base de données sous la forme d’objets PHP (ces derniers sont appelés des entités) que l’on va pouvoir manipuler dans notre code. Lors de l’installation de Doctrine, Flex a ajouté un dossier &lt;code&gt;src/Entity&lt;/code&gt; dans lequel nous allons créer nos classes.&lt;/p&gt;

&lt;p&gt;Les entités sont de simples objets PHP dont les propriétés vont correspondre aux champs de notre base de données. Commençons par la table la plus simple, la table &lt;code&gt;category&lt;/code&gt; :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&amp;lt;?php // src/Entity/Category.php

namespace App\Entity;

class Category
{
    private $id;
    private $name;
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Maintenant passons à la table &lt;code&gt;job&lt;/code&gt;. Un emploi étant lié à une catégorie, notre table contient une clé étrangère vers la catégorie associée. Dans notre entité, cette information va se modéliser sous la forme d’une propriété dont la valeur sera une instance de l’entité associée à la table catégorie (donc un objet &lt;code&gt;Category&lt;/code&gt;).&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&amp;lt;?php // src/Entity/Job.php

namespace App\Entity;

class Job
{
    private $id;
    private $category; // instance de Category
    private $type;
    private $company;
    private $logo;
    private $url;
    private $position;
    private $location;
    private $description;
    private $howToApply;
    private $token;
    private $isPublic;
    private $isActivated;
    private $email;
    private $expiresAt;
    private $createdAt;
    private $updatedAt;
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;La table qui va gérer les informations d’affiliation est un peu plus complexe. Dans notre modèle, une société peut-être être affiliée à plusieurs catégories et une catégorie peut avoir des affiliations de plusieurs sociétés. Avec une base de données relationnelle, cela se traduit par la création d’une table d’association pour gérer cette information (il s’agit de la table &lt;code&gt;CategoryAffiliate&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Puisque nous avons dit qu’une table de notre base de données correspondait à un objet PHP, il devrait donc être nécessaire de créer deux nouveaux objets pour gérer cette relation. Mais en réalité cette table ne sert qu’à modéliser le fait qu’un objet &lt;code&gt;Affialite&lt;/code&gt; est rattaché à plusieurs objets &lt;code&gt;Category&lt;/code&gt; et vice-versa. Donc d’un point de vue programmation, un objet &lt;code&gt;Affiliate&lt;/code&gt; devrait avoir une propriété &lt;code&gt;$categories&lt;/code&gt; qui correspond à un tableau d’objet &lt;code&gt;Affiliate&lt;/code&gt; et l’entité &lt;code&gt;Category&lt;/code&gt; une propriété &lt;code&gt;$affiliates&lt;/code&gt; correspondant un tableau d’objet &lt;code&gt;Affiliate&lt;/code&gt;.
 Notre ORM est tout à fait capable de gérer cette problématique. Nous allons donc créer notre objet &lt;code&gt;Affiliate&lt;/code&gt; avec une propriété &lt;code&gt;$categories&lt;/code&gt; que nous ferons correspondre à un tableau d’objet &lt;code&gt;Category&lt;/code&gt;. Doctrine gérera de manière automatique et transparente notre table d’association.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;S’il avait été nécessaire de gérer des informations additionnelles, telles que par exemple la date de création de l’affiliation ou l’utilisateur ayant créé l’affiliation, il aurait été nécessaire de créer une entité supplémentaire et de gérer la relation manuellement.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&amp;lt;?php // src/Entity/Affiliate.php

namespace App\Entity;

use Doctrine\Common\Collections\ArrayCollection;

class Affiliate
{
    private $id;
    private $categories; // tableau d&amp;#39;objet Category
    private $url;
    private $email;
    private $token;
    private $isActive;
    private $createdAt;

    public function __construct()
    {
        $this-&amp;gt;categories = new ArrayCollection();
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;blockquote&gt;
  &lt;p&gt;Lors de la mise en place d’une relation où l’on va gérer un tableau d’objet, il est nécessaire d’initialiser la propriété en question avec une collection vide. Pour Doctrine, cela passe par la création d’un objet &lt;code&gt;ArrayCollection&lt;/code&gt; comme dans l’exemple précédent.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;N’oublions pas de modifier notre objet &lt;code&gt;Category&lt;/code&gt; pour y ajouter la propriété correspondant à nos objets &lt;code&gt;Affialite&lt;/code&gt;. Une catégorie étant également associée à plusieurs emplois, nous allons en profiter pour y ajouter la propriété correspondante.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&amp;lt;?php // src/Entity/Category.php

namespace App\Entity;

use Doctrine\Common\Collections\ArrayCollection;

class Category
{
    private $id;
    private $name;
    private $jobs; // tableau d&amp;#39;objet Job
    private $affiliates; // tableau d&amp;#39;objet Affiliate

    public function __construct()
    {
        $this-&amp;gt;jobs = new ArrayCollection();
        $this-&amp;gt;affiliates = new ArrayCollection();
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Il est maintenant temps d’indiquer à Doctrine comment l’ORM va pouvoir faire le lien entre nos entités et les tables de la base de données. Pour cela, et comme nous l’avons spécifié précédemment, nous allons placer des fichiers de configuration dans le dossier &lt;code&gt;config/doctrine/mapping&lt;/code&gt;. Tout comme pour l’écriture des classes, nous allons créer un fichier de configuration par entité en suivant la convention &lt;code&gt;NomDeLaClasse.orm.yml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Les fichiers de configuration vont permettre d’indiquer à quelle table correspondent une entité et les différentes caractéristiques de nos propriétés (colonne de rattachement, type de données, contraintes d’intégrité, ….).&lt;/p&gt;

&lt;p&gt;Commençons par la configuration de notre entité &lt;code&gt;Category&lt;/code&gt; :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;# config/doctrine/mapping/Category.orm.yml
App\Entity\Category:
    type: entity

    # clé(s) primaire(s)
    id:
        id:
            type: integer
            generator:
                strategy: AUTO

    # colonne(s) de la table
    fields:
        name:
            type: string
            length: 63

    # relation de type un vers plusieurs
    oneToMany:
        jobs:
            targetEntity: Job
            mappedBy: category

    # relation de type plusieurs vers plusieurs
    manyToMany:
        affiliates:
            targetEntity: Affiliate
            inversedBy: categories&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;blockquote&gt;
  &lt;p&gt;Comme vous pouvez le constater, les propriétés de correspondant à des relations sont dissociés du reste des propriétés. On distingue quatre types de relation, &lt;a href=&quot;https://goo.gl/ExSdg4 &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;One-To-Many&lt;/code&gt;&lt;/a&gt; (relation de type un vers plusieurs), &lt;a href=&quot;https://goo.gl/tgffTs &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;Many-To-One&lt;/code&gt;&lt;/a&gt; (plusieurs vers un), &lt;a href=&quot;https://goo.gl/WBDHLm &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;Many-To-Many&lt;/code&gt;&lt;/a&gt; (relation de plusieurs à plusieurs) et &lt;a href=&quot;https://goo.gl/NA7LFn &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;One-To-One&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Voici la configuration de l’entité &lt;code&gt;Job&lt;/code&gt; :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;# config/doctrine/mapping/Job.orm.yml
App\Entity\Job:
    type: entity

    id:
        id:
            type: integer
            generator:
                strategy: AUTO

    fields:
        type:
            type: string
            length: 255
            nullable: true

        company:
            type: string
            length: 255

        logo:
            type: string
            length: 255
            nullable: true

        url:
            type: string
            length: 255
            nullable: true

        position:
            type: string
            length: 255

        location:
            type: string
            length: 255

        description:
            type: text

        howToApply:
            type: text

        token:
            type: string
            length: 255
            unique: true

        isPublic:
            type: boolean
            default: false

        isActivated:
            type: boolean
            default: true

        email:
            type: string
            length: 255

        expiresAt:
            type: datetime

        createdAt:
            type: datetime

        updatedAt:
            type: datetime
            nullable: true

    manyToOne:
        category:
            targetEntity: Category
            inversedBy: jobs
            joinColumn:
                name: category_id
                referencedColumnName: id&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Et pour finir le mapping correspondant à l’entité &lt;code&gt;Affiliate&lt;/code&gt; :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;# config/doctrine/mapping/Affiliate.orm.yml
App\Entity\Affiliate:
    type: entity

    id:
        id:
            type: integer
            generator:
                strategy: AUTO

    fields:
        url:
            type: string
            length: 255

        email:
            type: string
            length: 255
            unique: true

        token:
            type: string
            length: 255
            unique: true

        createdAt:
            type: datetime

    manyToMany:
        categories:
            targetEntity: Category
            mappedBy: affiliates&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Lorsque nous allons enregistrer un emploi ou une affiliation, nous souhaiterions connaître la date de création et/ou de modification de la donnée écrite. Les entités correspondantes possèdent une propriété &lt;code&gt;createdAt&lt;/code&gt; et/ou &lt;code&gt;updatedAt&lt;/code&gt;. Plutôt que de devoir gérer manuellement cette information, nous allons déléguer ce travail à Doctrine.&lt;/p&gt;

&lt;p&gt;En effet l’ORM possède un gestionnaire d’événement sur lequel nous allons nous brancher afin d’être notifié lors de l’enregistrement et la modification d’une entité. Nous allons donc ajouter cette configuration au mapping de nos entités afin d’indiquer la méthode de l’objet qui sera appelée lors de la propagation de l’événement.&lt;/p&gt;

&lt;p&gt;Pour l’entité &lt;code&gt;Job&lt;/code&gt; :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;# config/doctrine/mapping/Job.orm.yml
App\Entity\Job:
    # ...

    lifecycleCallbacks:
        prePersist: [ setCreatedAtValue ] # appelé lors de la création de l&amp;#39;entité
        preUpdate:  [ setUpdatedAtValue ] # appelé lors de la modification de l&amp;#39;entité&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Pour l’entité &lt;code&gt;Affiliate&lt;/code&gt; :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;# config/doctrine/mapping/Affiliate.orm.yml
App\Entity\Affiliate:
    # ...

    lifecycleCallbacks:
        prePersist: [ setCreatedAtValue ]&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Il ne faudra pas oublier d’ajouter les méthodes correspondantes dans les classes associées. Ces dernières sont appelées avec un paramètre de type &lt;code&gt;LifecycleEventArgs&lt;/code&gt; contenant un certain nombre d’informations sur le contexte d’exécution de l’ORM.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;// src/Entity/Job.php
// ...

use Doctrine\Common\Persistence\Event\LifecycleEventArgs;

class Job
{
    // ...

    public function setCreatedAtValue(LifecycleEventArgs $event)
    {
        $this-&amp;gt;createdAt = new DateTime();
    }

    public function setUpdatedAtValue(LifecycleEventArgs $event)
    {
        $this-&amp;gt;updatedAt = new DateTime();
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Pour l’entité &lt;code&gt;Affiliate&lt;/code&gt;, nous souhaitons connaître uniquement la date de
création de la donnée.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;// src/Entity/Affiliate.php
// ...

use Doctrine\Common\Persistence\Event\LifecycleEventArgs;

class Affiliate
{
    // ...

    public function setCreatedAtValue(LifecycleEventArgs $event)
    {
        $this-&amp;gt;createdAt = new DateTime();
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;blockquote&gt;
  &lt;p&gt;Signalons l’existence d’un bundle &lt;a href=&quot;https://goo.gl/EZWK72 &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;StofDoctrineExtensionBundle&lt;/code&gt;&lt;/a&gt; contenant un ensemble d’extensions Doctrine pouvant être ajoutées à nos entités et possédant entre autres, une extension &lt;code&gt;Timestampable&lt;/code&gt;. Cette dernière permet de gérer de manière automatique les dates de création et de modification d’une entité sans avoir à ajouter manuellement les propriétés correspondantes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Maintenant que nous avons indiqué à notre projet comment se connecter à notre base de données, créé nos entités et indiqué la configuration nécessaire à la liaison entre nos objets et le contenu de notre base, nous allons pouvoir initialiser cette dernière. Doctrine va encore nous faciliter le travail dans cette tâche car l’ORM est distribué avec des commandes qui vont nous assister dans ce travail.&lt;/p&gt;

&lt;p&gt;Dans un terminal, nous allons exécuter les commandes suivantes :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;$ bin/console doctrine:database:create # pour créer la base de données
Created database var/jobeet.db for connection named default


$ bin/console doctrine:schema:create # pour créer la structure des tables
ATTENTION: This operation should not be executed in a production environment.

Creating database schema...
Database schema created successfully!&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;blockquote&gt;
  &lt;p&gt;Vous pouvez constater que la base de données a été correctement initialisée en ouvrant le fichier contenant les données SQLite qui a été créé (&lt;code&gt;var/jobeet.db&lt;/code&gt;) avec un outil tel que &lt;a href=&quot;http://sqlitebrowser.org &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;DB Browser for SQLite&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Voilà qui conclut notre section d’introduction au modèle de données. Nous avons maintenant une base de données (presque) prête à être utilisée et qui n’attend plus que nos données.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Retrouvez tous les tutorials Jobeet disponibles depuis le &lt;a href=&quot;/blog/2017/09/12/tutorial-jobeet-symfony-4-introduction.html&quot;&gt;billet d’introduction de la série&lt;/a&gt;. Le code source de cette application est également disponible sur &lt;a href=&quot;https://github.com/jdecool/jobeet/tree/03a-modele-donnees &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Github&lt;/a&gt;. Vous trouverez une branche associée à l’état du projet après chaque chapitre.&lt;/p&gt;
&lt;/blockquote&gt;
</description>
                    <pubDate>Wed, 20 Sep 2017 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2017/09/20/tutorial-jobeet-symfony-4-partie-3a-le-modele-de-donnees.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2017/09/20/tutorial-jobeet-symfony-4-partie-3a-le-modele-de-donnees.html</guid>
                </item>
            
        
            
                151
                <item>
                    <title>Tutorial Jobeet pour Symfony 4 - Partie 2: Le projet</title>
                    <description>&lt;p&gt;Nous avons précédemment créé la structure de notre projet Symfony et préparé notre environnement de développement. Nous allons très prochainement pouvoir commencer à écrire nos premières lignes de code. Mais avant cela, attardons-nous un peu sur les exigences de notre projet.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Ce billet est principalement d’une recopie des exigences fonctionnelles du &lt;a href=&quot;https://symfony.com/legacy/doc/jobeet/1_4/fr/02?orm=Doctrine &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;tutorial initial&lt;/a&gt;. Je vous recommande vivement de lire ce dernier car des explications sur les nouvelles pratiques liées à Symfony 4 y sont décrites.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Jobeet est un logiciel open-source de recherche d’emploi qui ne fait qu’une seule chose, mais le fait bien. Il est facile d’utilisation, à adapter, à faire évoluer, et à intégrer à votre site internet. Il est multi langues dès le départ, et bien sûr il utilise les dernières technologies du web 2.0 pour améliorer l’expérience utilisateur. Il fournit également des feeds et une API pour interagir avec lui en programmant.&lt;/p&gt;

&lt;p&gt;Le site web Jobeet a quatre types d’utilisateurs :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;L’administrateur : il est propriétaire du site et il a le pouvoir magique&lt;/li&gt;
  &lt;li&gt;L’utilisateur : il visite le site pour chercher un emploi&lt;/li&gt;
  &lt;li&gt;L’annonceur : il soumet une offre d’emploi&lt;/li&gt;
  &lt;li&gt;L’affilié : il re-édite certains emplois sur son site&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nous allons maintenant découper le projet en scénarios d’utilisation. L’application est séparée en deux interfaces : le frontend (où les utilisateurs interagissent avec le site), et le backend (où les administrateurs gèrent le site).&lt;/p&gt;

&lt;p&gt;Symfony recommande maintenant et générera dorénavant des applications orientées bundle-less. Si vous débutez avec Symfony, un &lt;a href=&quot;https://symfony.com/doc/current/bundles.html &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;bundle&lt;/a&gt; peut être considéré comme un “plugin”. Ils permettent de regrouper du code qui est regroupé par fonctionnalité et redistribuable dans n’importe quel projet Symfony.&lt;/p&gt;

&lt;p&gt;Nous allons maintenant lister les exigences fonctionnelles de notre application. Ces dernières seront numérotés et débuteront par un &lt;code&gt;F&lt;/code&gt; pour les aspects frontend et par un &lt;code&gt;B&lt;/code&gt; pour les aspects backend.&lt;/p&gt;

&lt;h3 id=&quot;f1-en-tant-quutilisateur-je-vois-les-dernières-offres-actives-sur-la-page-daccueil&quot;&gt;F1: En tant qu’utilisateur, je vois les dernières offres actives sur la page d’accueil&lt;/h3&gt;

&lt;p&gt;Quand un utilisateur vient sur le site Jobeet, il voit une liste des emplois actifs. Les emplois sont classés par catégorie, puis par date de publication (emplois plus récents en premier). Pour chaque emploi, seul le lieu, la position, et la société sont affichés.&lt;/p&gt;

&lt;p&gt;Pour chaque catégorie, la liste ne montre que les 10 premiers emplois et un lien permet de lister tous les emplois pour une catégorie donnée (scénario F2).&lt;/p&gt;

&lt;p&gt;Sur la page d’accueil , l’utilisateur peut affiner la liste des travaux (scénario F3), ou par la soumission d’un nouvel emploi (scénario F5).&lt;/p&gt;

&lt;h3 id=&quot;f2-en-tant-quutilisateur-je-peux-lister-les-emplois-dune-catégorie&quot;&gt;F2: En tant qu’utilisateur, je peux lister les emplois d’une catégorie&lt;/h3&gt;

&lt;p&gt;Quand un utilisateur clique sur un nom de catégorie ou sur le lien “more jobs” sur la page d’accueil, il voit tous les emplois pour cette catégorie triée par date.&lt;/p&gt;

&lt;p&gt;La liste est paginée avec 20 emplois par page.&lt;/p&gt;

&lt;h3 id=&quot;f3-en-tant-quutilisateur-je-peux-affiner-les-offres-dune-catégorie&quot;&gt;F3: En tant qu’utilisateur, je peux affiner les offres d’une catégorie&lt;/h3&gt;

&lt;p&gt;L’utilisateur peut saisir quelques mots-clés pour affiner sa recherche. Les mots-clés peuvent être des mots trouvés dans l’emplacement, la position, la catégorie, ou les champs de l’entreprise.&lt;/p&gt;

&lt;h3 id=&quot;f4-en-tant-quutilisateur-je-peux-visualiser-le-détail-dune-offre&quot;&gt;F4: En tant qu’utilisateur, je peux visualiser le détail d’une offre&lt;/h3&gt;

&lt;p&gt;L’utilisateur peut sélectionner un emploi dans la liste pour afficher des informations plus détaillées.&lt;/p&gt;

&lt;h3 id=&quot;f5-en-tant-quutilisateur-je-peux-soumettre-une-offre-demploi&quot;&gt;F5: En tant qu’utilisateur, je peux soumettre une offre d’emploi&lt;/h3&gt;

&lt;p&gt;Un utilisateur peut soumettre un emploi (il n’est pas nécessaire de devoir créer un compte). Un emploi est composé de plusieurs informations.&lt;/p&gt;

&lt;p&gt;Le processus est simple, avec seulement deux étapes : d’abord, l’utilisateur remplit dans le formulaire toutes les informations nécessaires pour décrire l’emploi, puis il valide les informations en visualisant la page finale de l’emploi.&lt;/p&gt;

&lt;p&gt;Même si l’utilisateur n’a pas de compte, un emploi peut être modifié ultérieurement, grâce à une URL spécifique (protégé par un jeton donné à l’utilisateur lorsque l’emploi est créé).&lt;/p&gt;

&lt;p&gt;Chaque poste de travail est en ligne pendant 30 jours (ce qui est configurable par l’administrateur - voir Histoire B2). Un utilisateur peut revenir réactiver ou prolonger la validité de l’annonce pour un supplément de 30 jours, mais seulement lorsque le travail expire dans moins de 5 jours.&lt;/p&gt;

&lt;h3 id=&quot;f6-en-tant-quutilisateur-je-peux-affilier-ma-société&quot;&gt;F6: En tant qu’utilisateur, je peux affilier ma société&lt;/h3&gt;

&lt;p&gt;Un utilisateur doit demander à devenir un affilié et être autorisés à utiliser l’API Jobeet. Pour postuler, il doit donner des informations.&lt;/p&gt;

&lt;p&gt;Le compte d’ un affilié doit être activé par l’administrateur (scénario B3). Une fois activé, l’affilié reçoit un jeton pour une utilisation avec l’API par email.&lt;/p&gt;

&lt;p&gt;Lors de l’application, l’affilié peut également choisir d’obtenir des emplois auprès d’un sous-ensemble des catégories disponibles.&lt;/p&gt;

&lt;h3 id=&quot;f7-en-tant-quutilisateur-je-peux-exporter--la-liste-des-emplois-actifs&quot;&gt;F7: En tant qu’utilisateur, je peux exporter  la liste des emplois actifs&lt;/h3&gt;

&lt;p&gt;Un affilié peut récupérer la liste des emplois en cours en appelant l’API avec le jeton de sa filiale. La liste peut être retournée en XML, JSON ou YAML.&lt;/p&gt;

&lt;p&gt;La liste contient les informations publiques disponibles pour un emploi.&lt;/p&gt;

&lt;p&gt;L’affilié peut également limiter le nombre d’emplois qui seront retournés, et affiner sa requête en spécifiant une catégorie.&lt;/p&gt;

&lt;h3 id=&quot;b1-en-tant-quadministrateur-je-peux-configurer-le-site&quot;&gt;B1: En tant qu’administrateur, je peux configurer le site&lt;/h3&gt;

&lt;p&gt;L’administrateur peut modifier les catégories disponibles sur le site.&lt;/p&gt;

&lt;h3 id=&quot;b2-en-tant-quadministrateur-je-peux-administrer-les-offres-demploi&quot;&gt;B2: En tant qu’administrateur, je peux administrer les offres d’emploi&lt;/h3&gt;

&lt;p&gt;Un administrateur peut modifier et supprimer tous les emplois affichés.&lt;/p&gt;

&lt;h3 id=&quot;b3-en-tant-quadministrateur-je-peux-gérer-les-affiliés&quot;&gt;B3: En tant qu’administrateur, je peux gérer les affiliés&lt;/h3&gt;

&lt;p&gt;L’administrateur peut créer ou modifier des affiliés. Il est responsable de l’activation d’un affilié et peut également les désactiver.&lt;/p&gt;

&lt;p&gt;Lorsque l’administrateur active une nouvelle filiale, le système crée un jeton unique à utiliser par la filiale.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Retrouvez tous les tutorials Jobeet disponibles depuis le &lt;a href=&quot;/blog/2017/09/12/tutorial-jobeet-symfony-4-introduction.html&quot;&gt;billet d’introduction de la série&lt;/a&gt;. Le code source de cette application est également disponible sur &lt;a href=&quot;https://github.com/jdecool/jobeet/tree/02-projet &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Github&lt;/a&gt;. Vous trouverez une branche associée à l’état du projet après chaque chapitre.&lt;/p&gt;
&lt;/blockquote&gt;
</description>
                    <pubDate>Tue, 19 Sep 2017 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2017/09/19/tutorial-jobeet-symfony-4-partie-2-le-projet.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2017/09/19/tutorial-jobeet-symfony-4-partie-2-le-projet.html</guid>
                </item>
            
        
            
                152
                <item>
                    <title>Ma semaine 37 de veille (11/09/2017 -&gt; 17/09/2017)</title>
                    <description>&lt;p&gt;Retrouvez chaque semaine la sélection de mes 5 tweets les plus populaires concernant
la veille que je partage sur Twitter. Abonnez-vous au &lt;a href=&quot;/feed/feed.xml &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;flux RSS&lt;/a&gt;,
du site ou plus précisément à celui de cette &lt;a href=&quot;/feed/feed-veille.xml &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;catégorie&lt;/a&gt;
pour être notifié des mises à jour.&lt;/p&gt;

&lt;h3 id=&quot;mon-actu-préférée&quot;&gt;Mon actu préférée&lt;/h3&gt;

&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;col-4&quot;&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-cards=&quot;hidden&quot; data-lang=&quot;fr&quot;&gt;&lt;p lang=&quot;fr&quot; dir=&quot;ltr&quot;&gt;[Blog] Tutorial Jobeet pour Symfony 4 - Introduction : &lt;a href=&quot;https://t.co/CXGbthKSk0&quot;&gt;https://t.co/CXGbthKSk0&lt;/a&gt;&lt;/p&gt;&amp;mdash; Jérémy DECOOL (@jdecool) &lt;a href=&quot;https://twitter.com/jdecool/status/907575051112902656&quot;&gt;12 septembre 2017&lt;/a&gt;&lt;/blockquote&gt;
    &lt;script async=&quot;&quot; src=&quot;//platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
  &lt;div class=&quot;col-8 tweet-desc&quot;&gt;
    Lors de la sortie de la première version du framework Symfony, un excellent tutorial était disponible pour apprendre à utiliser ce dernier. Avec la sortie prochaine de la version 4, j&apos;ai souhaité remise à jour ce dernier dans une série de billets.
  &lt;/div&gt;
&lt;/div&gt;

&lt;h3 id=&quot;le-top-de-la-semaine&quot;&gt;Le top de la semaine&lt;/h3&gt;

&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;col-4&quot;&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-cards=&quot;hidden&quot; data-lang=&quot;fr&quot;&gt;&lt;p lang=&quot;fr&quot; dir=&quot;ltr&quot;&gt;Plongeon dans les profondeurs de l’internet : entre Darknets et Cybercriminalité - &lt;a href=&quot;https://t.co/6Cy3exbHMa&quot;&gt;https://t.co/6Cy3exbHMa&lt;/a&gt;&lt;/p&gt;&amp;mdash; Jérémy DECOOL (@jdecool) &lt;a href=&quot;https://twitter.com/jdecool/status/907898663757893632&quot;&gt;13 septembre 2017&lt;/a&gt;&lt;/blockquote&gt;
  &lt;/div&gt;
  &lt;div class=&quot;col-8 tweet-desc&quot;&gt;
    Deepweb, Darknets et Darkweb. On entend de plus en plus souvent ces termes revenir dans l&apos;actualité, parfois sans vraiment savoir réellement de quoi il s&apos;agit. Nous sommes remplis de préjugé concernant ces termes. Ce billet vous permettra d&apos;en savoir un plus sur le sujet ainsi que sur l&apos;écosystème qui gravite autour.
  &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;&lt;div class=&quot;col-12&quot;&gt;&lt;hr /&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;col-4&quot;&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-cards=&quot;hidden&quot; data-lang=&quot;fr&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;PHP 7.2 is due in November. What&amp;#39;s new ? &lt;a href=&quot;https://t.co/XN580PkJoS&quot;&gt;https://t.co/XN580PkJoS&lt;/a&gt;&lt;/p&gt;&amp;mdash; Jérémy DECOOL (@jdecool) &lt;a href=&quot;https://twitter.com/jdecool/status/907136898837102593&quot;&gt;11 septembre 2017&lt;/a&gt;&lt;/blockquote&gt;
  &lt;/div&gt;
  &lt;div class=&quot;col-8 tweet-desc&quot;&gt;
    La prochaine version de PHP (la 7.2) vient de sortir sa deuxième &lt;a href=&quot;http://php.net/archive/2017.php#id2017-09-14-1 &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Release Candidate&lt;/a&gt; pour une sortie prévue au mois de novembre. L&apos;occasion de faire un récapitulatif des nouveautés de cette nouvelle version.
  &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;&lt;div class=&quot;col-12&quot;&gt;&lt;hr /&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;col-4&quot;&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-cards=&quot;hidden&quot; data-lang=&quot;fr&quot;&gt;&lt;p lang=&quot;fr&quot; dir=&quot;ltr&quot;&gt;Comment je suis devenu heureux: retour sur 4 années d’entrepreneuriat &lt;a href=&quot;https://t.co/UQkwfwwzKP&quot;&gt;https://t.co/UQkwfwwzKP&lt;/a&gt;&lt;/p&gt;&amp;mdash; Jérémy DECOOL (@jdecool) &lt;a href=&quot;https://twitter.com/jdecool/status/907505561935204352&quot;&gt;12 septembre 2017&lt;/a&gt;&lt;/blockquote&gt;
  &lt;/div&gt;
  &lt;div class=&quot;col-8 tweet-desc&quot;&gt;
    Il m&apos;arrive de partager des articles qui ne sont pas techniques, c&apos;est le cas pour ce tweet. Dans un monde où l&apos;on coure majoritairement vers l&apos;agent qui est dans notre société le principal indicateur de réussite. L&apos;auteur nous explique comment, en ayant revu ses priorités, il est arrivé à devenir un homme plus heureux.
  &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;&lt;div class=&quot;col-12&quot;&gt;&lt;hr /&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;col-4&quot;&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-cards=&quot;hidden&quot; data-lang=&quot;fr&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Ports &amp;amp; Adapters Architecture : &lt;a href=&quot;https://t.co/Y4v1kNjgvX&quot;&gt;https://t.co/Y4v1kNjgvX&lt;/a&gt;&lt;/p&gt;&amp;mdash; Jérémy DECOOL (@jdecool) &lt;a href=&quot;https://twitter.com/jdecool/status/908586464509906944&quot;&gt;15 septembre 2017&lt;/a&gt;&lt;/blockquote&gt;
  &lt;/div&gt;
  &lt;div class=&quot;col-8 tweet-desc&quot;&gt;
    Si vous êtes développeur, vous avez forcément entendu parler des architectures hexagonales. Ce type d&apos;architecture est constitué d&apos;un ensemble de bonnes pratiques utilisant de nombreux design patterns, parmi lesquels les &lt;a href=&quot;https://fr.wikipedia.org/wiki/Pont_(patron_de_conception) &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Ports&lt;/a&gt; et les &lt;a href=&quot;https://fr.wikipedia.org/wiki/Adaptateur_(patron_de_conception) &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Adapters&lt;/a&gt; seront expliqués dans cet article.
  &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;&lt;div class=&quot;col-12&quot;&gt;&lt;hr /&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;col-4&quot;&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-cards=&quot;hidden&quot; data-lang=&quot;fr&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;How does Docker Compose help in continuous integration ? &lt;a href=&quot;https://t.co/0E0U0t08XF&quot;&gt;https://t.co/0E0U0t08XF&lt;/a&gt;&lt;/p&gt;&amp;mdash; Jérémy DECOOL (@jdecool) &lt;a href=&quot;https://twitter.com/jdecool/status/908231594837757953&quot;&gt;14 septembre 2017&lt;/a&gt;&lt;/blockquote&gt;
  &lt;/div&gt;
  &lt;div class=&quot;col-8 tweet-desc&quot;&gt;
    L&apos;intégration continue est sans conteste une des meilleurs pratiques pour assurer la qualité de nos développements. Il existe de nombreux patterns pour monter un environnement de qualité et pérenne. Comment docker-compose peut participer à mettre en oeuvre un environnement d&apos;intégration continue au travers de la conteneurisation ?
  &lt;/div&gt;
&lt;/div&gt;
</description>
                    <pubDate>Mon, 18 Sep 2017 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2017/09/18/ma-semaine-de-veille-37.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2017/09/18/ma-semaine-de-veille-37.html</guid>
                </item>
            
        
            
                153
                <item>
                    <title>Tutorial Jobeet pour Symfony 4 - Partie 1: Démarrage du projet</title>
                    <description>&lt;p&gt;Est-il encore nécessaire de présenter Symfony qui est l’un des frameworks PHP les plus populaires ? Dans cette première partie, nous allons créer la structure de notre application et voir comment s’organise un projet Symfony.&lt;/p&gt;

&lt;p&gt;Démarrons tout d’abord avec les prérequis nécessaires à la mise en place de notre projet. Concernant la version de PHP que vous allez devoir utiliser, un PHP 7.1.3 sera au minimum nécessaire. Dans cette série de billets, nous partirons du principe que vous avez installé les extensions permettant d’accéder à une base de données telle que MySQL ou PostgreSQL (via l’extension PDO et les drivers associés). Le module XML de PHP (alias &lt;code&gt;php-xml&lt;/code&gt;) est également requis. Pour finir, nous supposerons également, que &lt;a href=&quot;https://getcomposer.org &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Composer&lt;/a&gt;, l’outil de gestion des dépendances PHP est installé sur votre machine.&lt;/p&gt;

&lt;p&gt;Jusqu’à la version 4 de Symfony, la création d’un nouveau projet se faisait au travers d’un outil d’installation spécifique nommé &lt;a href=&quot;https://github.com/symfony/symfony-installer &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Symfony installer&lt;/a&gt;. Ce dernier outil est maintenant remplacé par Composer qui possède une commande &lt;a href=&quot;https://getcomposer.org/doc/03-cli.md#create-project &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;create-project&lt;/a&gt; permettant de créer un nouveau projet PHP. Cette commande est ainsi indépendante du framework. Les équipes de Symfony ont souhaité standardiser la manière de démarrer un projet PHP sans devoir y ajouter des outils spécifiques. La création de notre projet Jobeet se fera ainsi au travers de la commande : &lt;code&gt;composer create-project symfony/skeleton jobeet 4.0.0-RC1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Composer va alors créer un dossier pour notre projet en se basant sur le &lt;a href=&quot;https://github.com/symfony/skeleton &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;modèle de projet Symfony&lt;/a&gt; et télécharger les dépendances associées. Dans sa nouvelle version, le framework fournit dorénavant un composant nommé &lt;a href=&quot;https://github.com/symfony/flex &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Flex&lt;/a&gt;, une surcouche à Composer et qui permet entre autres de configurer automatiquement les dépendances que vous installez dans votre application.&lt;/p&gt;

&lt;p&gt;Analysons la structure de notre projet vide :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;jobeet
├── .env
├── .env.dist
├── composer.json
├── composer.lock
├── config
│   ├── bundles.php
│   ├── packages
│   │   ├── dev
│   │   │   └── routing.yaml
│   │   ├── framework.yaml
│   │   ├── routing.yaml
│   │   └── test
│   │       └── framework.yaml
│   ├── routes.yaml
│   └── services.yaml
├── public
│   └── index.php
├── src
│   ├── Controller
│   └── Kernel.php
└── vendor&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Contrairement aux versions précédentes, le framework a changé l’arborescence de ces fichiers afin de se rapprocher d’une arborescence de fichiers que l’on pourrait retrouver sur des systèmes Unix. On retrouve ainsi les éléments ci-dessous :&lt;/p&gt;

&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;col-2&quot;&gt;&lt;code&gt;config&lt;/code&gt;&lt;/div&gt;
  &lt;div class=&quot;col-10&quot;&gt;Dossier contenant les fichiers de configuration de notre projet&lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;col-2&quot;&gt;&lt;code&gt;public&lt;/code&gt;&lt;/div&gt;
  &lt;div class=&quot;col-10&quot;&gt;Le répertoire contenant les fichiers désservis par les serveurs HTTP&lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;col-2&quot;&gt;&lt;code&gt;src&lt;/code&gt;&lt;/div&gt;
  &lt;div class=&quot;col-10&quot;&gt;Accueillera les fichiers sources PHP contenant toute la logique de notre projet&lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;col-2&quot;&gt;&lt;code&gt;vendor&lt;/code&gt;&lt;/div&gt;
  &lt;div class=&quot;col-10&quot;&gt;Le dossier utilisé par Composer et contenant les dépendances (décrites dans le fichier &lt;code&gt;composer.json&lt;/code&gt;) nécessaires au fonctionnement de notre projet&lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;col-2&quot;&gt;&lt;code&gt;.env&lt;/code&gt;&lt;/div&gt;
  &lt;div class=&quot;col-10&quot;&gt;Fichier contenant la configuration de l&apos;environnement d&apos;exécution de notre code&lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;col-2&quot; style=&quot;text-decoration: line-through;&quot;&gt;&lt;code&gt;Makefile&lt;/code&gt;&lt;/div&gt;
  &lt;div class=&quot;col-10&quot; style=&quot;text-decoration: line-through;&quot;&gt;Décrit les tâches pouvant être exécutées par le framework&lt;/div&gt;
&lt;/div&gt;

&lt;p style=&quot;text-decoration: line-through;&quot;&gt;Même si nous n&apos;avons encore aucune ligne de code ni même de configuration (en réalité Flex s&apos;en est chargé pour nous), nous pouvons démarrer notre application Symfony. Il est possible de démarrer un serveur HTTP au travers de la commande `make serve` (si vous ne connaissez pas l&apos;outil &lt;a href=&quot;https://www.gnu.org/software/make/ &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Make&lt;/a&gt;, je vous recommande vivement de vous renseigner sur ce dernier).&lt;/p&gt;

&lt;p&gt;Même sans configuration particulière, nous pouvons démarrer notre application grâce au serveur Web embarqué dans PHP au travers de la commande &lt;code&gt;php -S localhost:8000 -t public/&lt;/code&gt;.&lt;/p&gt;

&lt;center&gt;&lt;img src=&quot;/img/blog/20170913-tutorial-jobeet-symfony-4-partie-1-demarrage-projet/jobeet-serve.png &quot; /&gt;&lt;/center&gt;

&lt;p&gt;Par défaut, le framework utilise le serveur Web embarqué avec PHP (ce qui peut être suffisant pour des besoins de développement). Symfony fournit également un bundle &lt;code&gt;symfony/web-server-bundle&lt;/code&gt; (nous reviendrons plus tard sur la notion de bundle) permettant d’apporter une meilleure expérience développeur pour manipuler ce dernier.&lt;/p&gt;

&lt;p&gt;Une fois le serveur démarré, nous pouvons nous connecter sur notre projet en ouvrant un navigateur et en tapant l’URL &lt;code&gt;http://localhost:8000&lt;/code&gt;. Bien entendu, comme nous n’avons encore rien fait, une page d’erreur sera affichée.&lt;/p&gt;

&lt;center&gt;&lt;img src=&quot;/img/blog/20170913-tutorial-jobeet-symfony-4-partie-1-demarrage-projet/jobeet-homepage.png &quot; style=&quot;width: 100%; height: 100%;&quot; /&gt;&lt;/center&gt;

&lt;p&gt;Attardons-nous maintenant le contenu du fichier &lt;code&gt;.env&lt;/code&gt;. Si vous regardez le contenu de ce dernier, vous constaterez qu’il contient un certain nombre de paramètres :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;# This file is a &amp;quot;template&amp;quot; of which env vars needs to be defined in your configuration or in a .env file
# Set variables here that may be different on each deployment target of the app, e.g. development, staging, production.
# https://symfony.com/doc/current/best_practices/configuration.html#infrastructure-related-configuration

###&amp;gt; symfony/framework-bundle ###
APP_ENV=dev
APP_DEBUG=1
APP_SECRET=f78d2a48cbd00d92acf418a47a0a5c3e
###&amp;lt; symfony/framework-bundle ###&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Notons tout d’abord les commentaires &lt;code&gt;###&amp;gt; symfony/framework-bundle ###&lt;/code&gt; et &lt;code&gt;###&amp;lt; symfony/framework-bundle ###&lt;/code&gt; présent dans le fichier. Ces derniers servent de balise de début et de fin à Flex pour ajouter et supprimer automatiquement la configuration de nos dépendances lors de leurs ajouts ou suppressions dans notre projet.&lt;/p&gt;

&lt;p&gt;Viennent ensuite les différentes variables d’environnement d’exécution de notre application :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;La variable &lt;code&gt;APP_ENV&lt;/code&gt; permet de définir l’environnement courant d’exécution du projet. Généralement 3 environnements sont utilisés : &lt;code&gt;prod&lt;/code&gt; (l’environnement où interagissent les utilisateurs finaux), &lt;code&gt;dev&lt;/code&gt; (l’environnement utilisé pendant les développements) et &lt;code&gt;test&lt;/code&gt; (l’environnement utilisé pour tester automatiquement notre projet).&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;La variable &lt;code&gt;APP_DEBUG&lt;/code&gt; de Symfony fournit un ensemble d’outils permettant de faciliter la résolution des bugs pouvant survenir lors du développement. Par exemple, c’est cette dernière qui génère l’affichage de la pile d’appel des fonctions du framework lorsqu’une erreur se produit (comme lorsque nous avons tenté d’accéder à l’application via le navigateur).&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Un des principaux avantages d’avoir recours à un framework est d’utiliser un ensemble de composants réutilisables, régulièrement mis à jour et utilisant des bonnes pratiques de développement. Cela permet au framework de guider le développeur et de minimiser les erreurs pouvant faire apparaître des failles de sécurité. La variable &lt;code&gt;APP_SECRET&lt;/code&gt; est ainsi utilisée par certains composants en tant que clé secrète pour générer des jetons de sécurité (comme par exemple lors de la mise en place de formulaire avec les jetons &lt;a href=&quot;https://fr.wikidia.org/wiki/Cross-Site_Request_Forgery &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;CSRF&lt;/a&gt;).&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;C’est sur ce point que nous allons conclure ce premier chapitre. À ce stade, nous avons créé notre projet Symfony et rapidement fait le tour de l’organisation des fichiers de notre application. Notre environnement est maintenant prêt et nous allons prochainement pouvoir commencer à développer.&lt;/p&gt;

&lt;p&gt;La section suivante dévoilera les fonctionnalités de notre application et détaillera les besoins fonctionnels et techniques à satisfaire.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Retrouvez tous les tutorials Jobeet disponibles depuis le &lt;a href=&quot;/blog/2017/09/12/tutorial-jobeet-symfony-4-introduction.html&quot;&gt;billet d’introduction de la série&lt;/a&gt;. Le code source de cette application est également disponible sur &lt;a href=&quot;https://github.com/jdecool/jobeet/tree/01-demarrage-projet &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Github&lt;/a&gt;. Vous trouverez une branche associée à l’état du projet après chaque chapitre.&lt;/p&gt;
&lt;/blockquote&gt;
</description>
                    <pubDate>Wed, 13 Sep 2017 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2017/09/13/tutorial-jobeet-symfony-4-partie-1-demarrage-projet.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2017/09/13/tutorial-jobeet-symfony-4-partie-1-demarrage-projet.html</guid>
                </item>
            
        
            
                154
                <item>
                    <title>Tutorial Jobeet pour Symfony 4 - Introduction</title>
                    <description>&lt;p&gt;J’ai récemment eu l’occasion de relire des articles concernant &lt;em&gt;Jobeet&lt;/em&gt;, un tutorial pour concevoir une application de type “job board” écrit par l’équipe du framework Symfony (la première version à l’époque) afin de se familiariser avec l’outil. Ce dernier avait par la suite été adapté pour Symfony 2.8 par &lt;a href=&quot;https://medium.com/@dragosholban/symfony-2-8-jobeet-tutorial-3a72f67cdbd8 &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Dragos HOLBAN&lt;/a&gt; et traduit tout récemment en français par &lt;a href=&quot;http://jobeet.thuau.fr &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Jonathan THUAU&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Lors de l’écriture de ce billet, la version 4 du framework est en cours de finalisation et devrait sortir dans les prochains mois (courant novembre). Cette nouvelle version apporte de nombreuses améliorations et va modifier la manière dont nous travaillons avec l’outil, notamment grâce à un nouveau composant nommé &lt;a href=&quot;https://github.com/symfony/flex &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Flex&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Quand j’ai relu le tutorial, j’ai été déçu que ce dernier ne soit pas disponible pour les dernières versions disponibles du framework. J’ai donc décidé de démarrer une série de billets consacrés à l’adaption du tutorial pour Symfony 4.&lt;/p&gt;

&lt;p&gt;Vous trouverez donc ici, au fur et à mesure de l’écriture des différents billets, les liens vers les nouveaux articles dont le sommaire se trouve ci-dessous :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/2017/09/13/tutorial-jobeet-symfony-4-partie-1-demarrage-projet.html&quot;&gt;Tutorial Jobeet pour Symfony 4 - Partie 1: Démarrage du projet&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/2017/09/19/tutorial-jobeet-symfony-4-partie-2-le-projet.html&quot;&gt;Tutorial Jobeet pour Symfony 4 - Partie 2: Le projet&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/2017/09/20/tutorial-jobeet-symfony-4-partie-3a-le-modele-de-donnees.html&quot;&gt;Tutorial Jobeet pour Symfony 4 - Partie 3A: Le modèle de données&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/2017/09/21/tutorial-jobeet-symfony-4-partie-3b-les-donnees-initiales.html&quot;&gt;Tutorial Jobeet pour Symfony 4 - Partie 3B: Les données initiales&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/2017/09/29/tutorial-jobeet-symfony-4-partie-4a-le-controleur-et-la-vue.html&quot;&gt;Tutorial Jobeet pour Symfony 4 - Partie 4A: Le contrôleur et la vue&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/2017/09/30/tutorial-jobeet-symfony-4-partie-4b-la-gestion-des-assets-avec-twig.html&quot;&gt;Tutorial Jobeet pour Symfony 4 - Partie 4B: La gestion des assets avec Twig&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/2017/10/16/tutorial-jobeet-symfony-4-partie-5-les-routes.html&quot;&gt;Tutorial Jobeet pour Symfony 4 - Partie 5: Les routes&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/2018/02/24/tutorial-jobeet-symfony-4-partie-6-aller-plus-loin-avec-le-modele.html&quot;&gt;Tutorial Jobeet pour Symfony 4 - Partie 6: Aller plus loin avec le modèle&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr class=&quot;alert-notice&quot; /&gt;

&lt;div class=&quot;alert alert-notice&quot;&gt;
    Par manque de temps, et du fait de nombreux changements personnels et professionnels
    je n&apos;ai pas eu l&apos;occasion de terminer la série d&apos;articles que j&apos;avais initialement
    prévus.
&lt;/div&gt;
&lt;hr class=&quot;alert-notice&quot; /&gt;

&lt;ul&gt;
  &lt;li&gt;Tutorial Jobeet pour Symfony 4 - Partie 7: Jouons avec la page catégorie&lt;/li&gt;
  &lt;li&gt;Tutorial Jobeet pour Symfony 4 - Partie 8: Les tests unitaires&lt;/li&gt;
  &lt;li&gt;Tutorial Jobeet pour Symfony 4 - Partie 9: Les tests fonctionnels&lt;/li&gt;
  &lt;li&gt;Tutorial Jobeet pour Symfony 4 - Partie 10: Les formulaires&lt;/li&gt;
  &lt;li&gt;Tutorial Jobeet pour Symfony 4 - Partie 11: Tester les formulaires&lt;/li&gt;
  &lt;li&gt;Tutorial Jobeet pour Symfony 4 - Partie 12: L’interface d’administration&lt;/li&gt;
  &lt;li&gt;Tutorial Jobeet pour Symfony 4 - Partie 13: La sécurité&lt;/li&gt;
  &lt;li&gt;Tutorial Jobeet pour Symfony 4 - Partie 14: Le flux de données&lt;/li&gt;
  &lt;li&gt;Tutorial Jobeet pour Symfony 4 - Partie 15: Fournir une API&lt;/li&gt;
  &lt;li&gt;Tutorial Jobeet pour Symfony 4 - Partie 16: Envoyer des emails&lt;/li&gt;
  &lt;li&gt;Tutorial Jobeet pour Symfony 4 - Partie 17: La recherche&lt;/li&gt;
  &lt;li&gt;Tutorial Jobeet pour Symfony 4 - Partie 18: L’AJAX&lt;/li&gt;
  &lt;li&gt;Tutorial Jobeet pour Symfony 4 - Partie 19: L’internationalization&lt;/li&gt;
  &lt;li&gt;Tutorial Jobeet pour Symfony 4 - Partie 20: Les bundles&lt;/li&gt;
  &lt;li&gt;Tutorial Jobeet pour Symfony 4 - Partie 21: Le cache&lt;/li&gt;
  &lt;li&gt;Tutorial Jobeet pour Symfony 4 - Partie 22: Le déploiement&lt;/li&gt;
  &lt;li&gt;Tutorial Jobeet pour Symfony 4 - Partie 23: Un autre regard sur Symfony&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Depuis la création de ce tutorial, le framework n’a cessé d’évoluer et de nouveaux composants ont fait leurs apparitions. Je tenterai d’ajouter de nouveaux articles afin d’y introduire ces derniers :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Tutorial Jobeet pour Symfony 4 - Partie 24: Les assets avec Webpack Encore&lt;/li&gt;
  &lt;li&gt;Tutorial Jobeet pour Symfony 4 - Partie 25: Gerer un workflow&lt;/li&gt;
  &lt;li&gt;Tutorial Jobeet pour Symfony 4 - Partie 26: Plus loin avec l’injection de dépendance&lt;/li&gt;
  &lt;li&gt;Tutorial Jobeet pour Symfony 4 - Annexe 1: Environnement Vagrant&lt;/li&gt;
  &lt;li&gt;Tutorial Jobeet pour Symfony 4 - Annexe 2: Environnement Docker&lt;/li&gt;
  &lt;li&gt;Tutorial Jobeet pour Symfony 4 - Annexe 3: Symfony Flex&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;N’hésitez pas à me contacter via &lt;a href=&quot;https://twitter.com/jdecool &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Twitter&lt;/a&gt; ou par &lt;a href=&quot;mailto:contact@jdecool.fr&quot;&gt;mail&lt;/a&gt; pour me faire part de vos observations et remarques.&lt;/p&gt;
</description>
                    <pubDate>Tue, 12 Sep 2017 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2017/09/12/tutorial-jobeet-symfony-4-introduction.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2017/09/12/tutorial-jobeet-symfony-4-introduction.html</guid>
                </item>
            
        
            
                155
                <item>
                    <title>Ma semaine 36 de veille (04/09/2017 -&gt; 10/09/2017)</title>
                    <description>&lt;p&gt;Retrouvez chaque semaine la sélection de mes 5 tweets les plus populaires concernant
la veille que je partage sur Twitter. La semaine qui vient de s’écouler marque la fin
des vacances scolaires ainsi que des congés d’été, cette dernière fut donc riche en
actualités partagées.&lt;/p&gt;

&lt;p&gt;Vous souhaitez suivre ma veille sans être “pollué” par les articles du blog ? Je viens
pour cela d’ajouter un &lt;a href=&quot;/feed/feed-veille.xml &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;flux RSS&lt;/a&gt; dédié au suivi de la
rubrique.&lt;/p&gt;

&lt;h3 id=&quot;mon-actu-préférée&quot;&gt;Mon actu préférée&lt;/h3&gt;

&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;col-4&quot;&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;fr&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;How Does a Database Work ? &lt;a href=&quot;https://t.co/FbxCW2VXaB&quot;&gt;https://t.co/FbxCW2VXaB&lt;/a&gt;&lt;/p&gt;&amp;mdash; Jérémy DECOOL (@jdecool) &lt;a href=&quot;https://twitter.com/jdecool/status/904604947773317121&quot;&gt;4 septembre 2017&lt;/a&gt;&lt;/blockquote&gt;
    &lt;script async=&quot;&quot; src=&quot;//platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
  &lt;div class=&quot;col-8 tweet-desc&quot;&gt;
    Savez-vous comment fonctionne une base de données ? Si la réponse et non, ou si vous souhaitez approfondir le sujet en créant un système de base de données similaire à SQLite, foncez sur ce billet !
  &lt;/div&gt;
&lt;/div&gt;

&lt;h3 id=&quot;le-top-de-la-semaine&quot;&gt;Le top de la semaine&lt;/h3&gt;

&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;col-4&quot;&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-cards=&quot;hidden&quot; data-lang=&quot;fr&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;A curated list of amazingly awesome &lt;a href=&quot;https://twitter.com/hashtag/PHPStorm?src=hash&quot;&gt;#PHPStorm&lt;/a&gt; plugins, resources and other shiny things : &lt;a href=&quot;https://t.co/0HwPGNEV5Y&quot;&gt;https://t.co/0HwPGNEV5Y&lt;/a&gt;&lt;/p&gt;&amp;mdash; Jérémy DECOOL (@jdecool) &lt;a href=&quot;https://twitter.com/jdecool/status/904962568124555264&quot;&gt;5 septembre 2017&lt;/a&gt;&lt;/blockquote&gt;
  &lt;/div&gt;
  &lt;div class=&quot;col-8 tweet-desc&quot;&gt;
    PHPStorm est certainement l&apos;un des IDE les plus en vogue actuellement pour développer avec le langage PHP. Voici une liste de ressources concernant ce dernier (blogs, comptes Twitter, plugins, ...).
  &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;&lt;div class=&quot;col-12&quot;&gt;&lt;hr /&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;col-4&quot;&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-cards=&quot;hidden&quot; data-lang=&quot;fr&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Little UI Details : &lt;a href=&quot;https://t.co/Pk52KNuvz3&quot;&gt;https://t.co/Pk52KNuvz3&lt;/a&gt;&lt;/p&gt;&amp;mdash; Jérémy DECOOL (@jdecool) &lt;a href=&quot;https://twitter.com/jdecool/status/905331233755942912&quot;&gt;6 septembre 2017&lt;/a&gt;&lt;/blockquote&gt;
  &lt;/div&gt;
  &lt;div class=&quot;col-8 tweet-desc&quot;&gt;
    Vous êtes amené à concevoir des interfaces utilisateur ? Jetez donc un oeil à ce regroupement de tweet qui est une collection de petits conseils de &lt;a href=&quot;https://twitter.com/steveschoger &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;@steveschoger&lt;/a&gt; pour améliorer vos compétences en conception visuelle au travers de petits détails qui font toute la différence.
  &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;&lt;div class=&quot;col-12&quot;&gt;&lt;hr /&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;col-4&quot;&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;fr&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;&amp;quot;Go PHP 7.1&amp;quot; because they will ... &lt;a href=&quot;https://t.co/Iwy9IxF58X&quot;&gt;https://t.co/Iwy9IxF58X&lt;/a&gt;&lt;/p&gt;&amp;mdash; Jérémy DECOOL (@jdecool) &lt;a href=&quot;https://twitter.com/jdecool/status/904597677408743425&quot;&gt;4 septembre 2017&lt;/a&gt;&lt;/blockquote&gt;
  &lt;/div&gt;
  &lt;div class=&quot;col-8 tweet-desc&quot;&gt;
    PHP 5.6 n&apos;est plus activement maintenu et arrive en fin de vie... Vous n&apos;êtes pas encore passé en version 7 ? Vous devriez, parce que eux l&apos;on fait !
  &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;&lt;div class=&quot;col-12&quot;&gt;&lt;hr /&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;col-4&quot;&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-cards=&quot;hidden&quot; data-lang=&quot;fr&quot;&gt;&lt;p lang=&quot;fr&quot; dir=&quot;ltr&quot;&gt;Votre CI de qualité : &lt;a href=&quot;https://t.co/mTHjqjCl2X&quot;&gt;https://t.co/mTHjqjCl2X&lt;/a&gt;&lt;/p&gt;&amp;mdash; Jérémy DECOOL (@jdecool) &lt;a href=&quot;https://twitter.com/jdecool/status/905692363091259392&quot;&gt;7 septembre 2017&lt;/a&gt;&lt;/blockquote&gt;
  &lt;/div&gt;
  &lt;div class=&quot;col-8 tweet-desc&quot;&gt;
    Lorsque nous travaillons sur un projet, nous souhaitons tous mettre en place des bonnes pratiques de tests et d&apos;intégration continue. Pourtant mettre en place un tel environnement peut s&apos;avérer compliqué. Découvrez le retour d&apos;expérience de la société Eleven Labs sur la mise en place d&apos;une stratégie d&apos;intégration continue.
  &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;&lt;div class=&quot;col-12&quot;&gt;&lt;hr /&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;col-4&quot;&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-cards=&quot;hidden&quot; data-lang=&quot;fr&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;How I failed to replicate an $86 million project in 1 line of code : &lt;a href=&quot;https://t.co/owY6eznu9g&quot;&gt;https://t.co/owY6eznu9g&lt;/a&gt;&lt;/p&gt;&amp;mdash; Jérémy DECOOL (@jdecool) &lt;a href=&quot;https://twitter.com/jdecool/status/904671645390004225&quot;&gt;4 septembre 2017&lt;/a&gt;&lt;/blockquote&gt;
  &lt;/div&gt;
  &lt;div class=&quot;col-8 tweet-desc&quot;&gt;
    Il y a quelques semaines, un article est devenu populaire en expliquant comment son auteur avait réalisé l&apos;équivalent d&apos;un projet évalué à 86 millions de l&apos;art en 57 lignes de code et quelques outils open source. Cet article est une réponse à ce dernier où l&apos;auteur explique comment en prenant quelques résultats d&apos;une expérience avec des technologies open source essaye de se donner &quot;bonne figure&quot;.
  &lt;/div&gt;
&lt;/div&gt;
</description>
                    <pubDate>Mon, 11 Sep 2017 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2017/09/11/ma-semaine-de-veille-36.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2017/09/11/ma-semaine-de-veille-36.html</guid>
                </item>
            
        
            
                156
                <item>
                    <title>La formation continue du développeur</title>
                    <description>&lt;p&gt;Il est primordial lorsqu’on est développeur de rester “à jour”, de garder un
oeil sur ce qui se fait et les évolutions de notre métier (aussi bien concernant les
produits, processus ou technologies que nous sommes amenés à utiliser au quotidien).
Pour cela, faire de la veille est essentiel.&lt;/p&gt;

&lt;p&gt;Mais quels sont les moyens pour faire de la veille ? C’est la question que s’est
posée cet été &lt;a href=&quot;https://www.camilleroux.com/2017/06/07/comment-je-fais-ma-veille-en-2017 &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Camille Roux&lt;/a&gt;
et à laquelle il a consacré quelques articles. Je vais pour ma part répondre à cette
question dans ce billet.&lt;/p&gt;

&lt;p&gt;Pour moi, le minimum qu’un développeur devrait faire, c’est de suivre l’actualité
de son domaine. Je parle de domaine au sens large, il ne faut pas rester cantonné
à ses acquis, mais sortir de sa zone de confort. Bien que je sois un développeur
utilisant essentiellement PHP, je surveille ce qui se fait du côté des autres
langages tels que Java, Scala, Kotlin, Javascript, Go, Rust et bien d’autres encore.&lt;/p&gt;

&lt;p&gt;L’objectif est de surveiller et de s’organiser dans une collecte d’informations sur
les sujets concernés. Je fais donc énormément de veille afin de suivre l’actualité :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;liée aux sociétés informatiques (éditeur logiciel, cloud, agence de développement, …)&lt;/li&gt;
  &lt;li&gt;concernant les outils que je suis amené à utiliser (IDE, framework, langages, …)&lt;/li&gt;
  &lt;li&gt;à propos des techniques pouvant être utiles (méthodes, architectures, …)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pour accomplir cette tâche, l’essentiel de ma veille se fait via la consultation
de flux RSS en utilisant l’outil &lt;a href=&quot;https://tt-rss.org &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Tiny Tiny RSS&lt;/a&gt;. Ce
dernier me permet d’organiser mes flux en différentes catégories. Généralement,
j’ajoute un nouveau flux au fur et à mesure que je découvre de nouvelles sources
d’information.&lt;/p&gt;

&lt;center&gt;&lt;img src=&quot;/img/blog/20170906-formation-continue-du-developpeur-veille/tinyrss.png &quot; style=&quot;width: 100%; height: 100%;&quot; /&gt;&lt;/center&gt;

&lt;p&gt;À mes débuts d’utilisation de Tiny Tiny RSS, j’ai commencé par ajouter et suivre
de sites d’agrégation de contenu tel que &lt;a href=&quot;https://news.ycombinator.com &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Hacker News&lt;/a&gt;
ou &lt;a href=&quot;http://news.humancoders.com &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Human Coders&lt;/a&gt;. Lorsque je lis un contenu
intéressant, j’ajoute ce dernier à mes flux à suivre. C’est ainsi que j’ai constitué
la base des flux que je lis aujourd’hui.&lt;/p&gt;

&lt;p&gt;C’est toujours aujourd’hui ce qui constitue l’essentiel de mes sources d’information.
En parallèle, j’utilise Twitter. Ma timeline est essentiellement construite avec des
personnes tweetant de manière régulière sur des sujets liés à l’informatique. J’utilise
également les listes Twitter afin de catégoriser les personnes qui je suis. Pour m’aider
dans cette tâche, je me sers de &lt;a href=&quot;https://tweetdeck.twitter.com &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;TweetDeck&lt;/a&gt;.&lt;/p&gt;

&lt;center&gt;&lt;img src=&quot;/img/blog/20170906-formation-continue-du-developpeur-veille/tweetdeck.png &quot; style=&quot;width: 100%; height: 100%;&quot; /&gt;&lt;/center&gt;

&lt;p&gt;Trouvez du temps pour faire de la veille n’est pas forcément quelque chose d’aisé.
Je suis persuadé que cela fait partie intégrante de notre travail de développeur et
que de ce fait, il est tout à fait normal de consacrer une partie de son temps à cette
tâche.&lt;/p&gt;

&lt;p&gt;Pour me rendre au travail, je suis amené à prendre le train. Le trajet dure environ
30 minutes. Cela fait donc 1h aller-retour. C’est l’occasion de mettre ce temps à profit.
Dans les transports je consulte donc mes flux et Twitter. Mais il m’arrive également
fréquemment de lire des livres. C’est également une façon de se former et d’avoir des
axes réflexions sur certains sujets. Sur les deux dernières années, j’ai pu lire ou
relire des livres tels que : &lt;a href=&quot;http://shop.oreilly.com/product/0636920022343.do &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;High Performance MySQL&lt;/a&gt;,
&lt;a href=&quot;https://www.pearson.com/us/higher-education/program/Martin-Clean-Coder-The-A-Code-of-Conduct-for-Professional-Programmers/PGM8366.html &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;The Clean Coder&lt;/a&gt;,
&lt;a href=&quot;http://cfeditions.com/surveillance/ &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Surveillance:// - Les libertés au défi du numériques&lt;/a&gt;,
La Méthode Google,
ou &lt;a href=&quot;https://editions.flammarion.com/free/9782081390522 &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Free! Comment marche l’économie du gratuit&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Se tenir informé de ce qui se fait, des principes qui en découle et savoir quand
utiliser ces principes, c’est déjà très bien. Mais avoir l’occasion de mettre en
pratique ce que l’on a pu découvrir, c’est encore mieux ! Personnellement, j’adore
écrire des petits bouts de code (pas nécessairement des projets complets ou
fonctionnels) pour découvrir et utiliser une bibliothèque ou un framework.&lt;/p&gt;

&lt;p&gt;Pour passer le cap de la théorie à la pratique, il y a bien tendu la documentation
associée aux outils qui vous sera d’une aide précieuse. Mais on voit également de
plus en plus de tutorials vidéo ou cours en ligne pour se former. De nombreuses
plateformes hébergent ce type de contenu, parmi lesquelles: &lt;a href=&quot;https://www.youtube.com &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Youtube&lt;/a&gt;,
&lt;a href=&quot;https://fr.coursera.org &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Coursera&lt;/a&gt;, &lt;a href=&quot;https://www.udemy.com &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Udemy&lt;/a&gt;,
&lt;a href=&quot;http://www.elephorm.com &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Elephorm&lt;/a&gt; ou encore &lt;a href=&quot;https://fr.tuto.com &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Tuto.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Il existe également des plateformes moins généralistes et donc plus spécialisées
sur un domaine. Par exemple, l’année dernière j’ai pu approfondir mes
connaissances sur le DDD, le CQRS et l’Event Sourcing au travers de
Event Sourcery. Cette année, j’ai décidé de
rattraper mon retard sur les technologies orientés frontend que j’ai pas mal
délaissées en souscrivant à &lt;a href=&quot;https://egghead.io &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Egghead&lt;/a&gt; qui est une
excellente plateforme pour se former sur l’écosystème Javascript (on y retrouve
des cours complets sur React, Angular et Vue, mais aussi des vidéos d’introduction
sur React Native, Electron, Elm, …).&lt;/p&gt;

&lt;p&gt;Après à chacun de trouver la méthode qui lui convient et la plus adaptée pour
permettre d’acquérir les connaissances et les compétences voulues.&lt;/p&gt;
</description>
                    <pubDate>Wed, 06 Sep 2017 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2017/09/06/formation-continue-du-developpeur-veille.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2017/09/06/formation-continue-du-developpeur-veille.html</guid>
                </item>
            
        
            
                157
                <item>
                    <title>Ma semaine 35 de veille (28/08/2017 -&gt; 03/09/2017)</title>
                    <description>&lt;p&gt;Cette semaine je vais essayer de démarrer une série de billets un peu particulière.
Je fais beaucoup de veille que je partage abondamment sur Twitter. Je vais donc
tenter de vous partager chaque semaine les 5 tweets ayant eu le meilleur taux
d’engagements selon la plateforme.&lt;/p&gt;

&lt;h3 id=&quot;mon-actu-préférée&quot;&gt;Mon actu préférée&lt;/h3&gt;

&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;col-4&quot;&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-cards=&quot;hidden&quot; data-lang=&quot;fr&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;&amp;quot;Clean code&amp;quot; isn&amp;#39;t actually clean : &lt;a href=&quot;https://t.co/4AIzEjIND6&quot;&gt;https://t.co/4AIzEjIND6&lt;/a&gt;&lt;/p&gt;&amp;mdash; Jérémy DECOOL (@jdecool) &lt;a href=&quot;https://twitter.com/jdecool/status/902791245277392900&quot;&gt;30 août 2017&lt;/a&gt;&lt;/blockquote&gt;
    &lt;script async=&quot;&quot; src=&quot;//platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
  &lt;div class=&quot;col-8 tweet-desc&quot;&gt;
    Comme Rober C. Martin (alias Uncle Bob) peut nous l&apos;expliquer, un mauvais code peut fonctionner. La notion de &quot;Clean Code&quot; est un ensemble de bonnes pratiques pour concevoir un logiciel qui restera maintenable et évolutif dans le temps. Maintenant cela ne veut pas pour autant dire que le code sera parfait...
  &lt;/div&gt;
&lt;/div&gt;

&lt;h3 id=&quot;le-top-de-la-semaine&quot;&gt;Le top de la semaine&lt;/h3&gt;

&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;col-4&quot;&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-cards=&quot;hidden&quot; data-lang=&quot;fr&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Event Sourcing: What it is and why it&amp;#39;s awesome &lt;a href=&quot;https://t.co/d1SmYy2qsk&quot;&gt;https://t.co/d1SmYy2qsk&lt;/a&gt;&lt;/p&gt;&amp;mdash; Jérémy DECOOL (@jdecool) &lt;a href=&quot;https://twitter.com/jdecool/status/902788151814574080&quot;&gt;30 août 2017&lt;/a&gt;&lt;/blockquote&gt;
  &lt;/div&gt;
  &lt;div class=&quot;col-8 tweet-desc&quot;&gt;
    On parle de plus en plus de l&apos;Event Sourcing en tant qu&apos;architecture applicative. Des ingénieurs de Netflix ont d&apos;ailleurs présenté &lt;a href=&quot;https://www.infoq.com/presentations/netflix-scale-event-sourcing&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;comment ils avaient conçu le système de téléchargement des vidéos de la plateforme&lt;/a&gt; sur ce principe. Découvrez dans cet article les principes de base de cette architecture avec les différents avantages et inconvénients.
  &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;&lt;div class=&quot;col-12&quot;&gt;&lt;hr /&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;col-4&quot;&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-cards=&quot;hidden&quot; data-lang=&quot;fr&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Egghead.io (&lt;a href=&quot;https://t.co/pBtakV0qYX&quot;&gt;https://t.co/pBtakV0qYX&lt;/a&gt;) is the best place to keep learning &lt;a href=&quot;https://twitter.com/hashtag/javascript?src=hash&quot;&gt;#javascript&lt;/a&gt; ecosystem !&lt;/p&gt;&amp;mdash; Jérémy DECOOL (@jdecool) &lt;a href=&quot;https://twitter.com/jdecool/status/903967267372900353&quot;&gt;2 septembre 2017&lt;/a&gt;&lt;/blockquote&gt;
  &lt;/div&gt;
  &lt;div class=&quot;col-8 tweet-desc&quot;&gt;
    J&apos;ai prévu de faire un billet très prochainement sur la continuité de se former lorsqu&apos;on est développeur, comment je tente au quotidien de garder un oeil sur l&apos;état de l&apos;art de notre métier et continue de me former autant que possible. &lt;a href=&quot;https://egghead.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Egghead.io&lt;/a&gt; est une excellente plateforme pour se former et garder un oeil sur les technologies orientés Javascript.
  &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;&lt;div class=&quot;col-12&quot;&gt;&lt;hr /&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;col-4&quot;&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-cards=&quot;hidden&quot; data-lang=&quot;fr&quot;&gt;&lt;p lang=&quot;fr&quot; dir=&quot;ltr&quot;&gt;Développer une culture de la sécurité psychologique : &lt;a href=&quot;https://t.co/7fXBmeo0MY&quot;&gt;https://t.co/7fXBmeo0MY&lt;/a&gt;&lt;/p&gt;&amp;mdash; Jérémy DECOOL (@jdecool) &lt;a href=&quot;https://twitter.com/jdecool/status/903149357649850368&quot;&gt;31 août 2017&lt;/a&gt;&lt;/blockquote&gt;
  &lt;/div&gt;
  &lt;div class=&quot;col-8 tweet-desc&quot;&gt;
    Dans un (trop) grand nombre d&apos;entreprises, l&apos;indicateur principal d&apos;une équipe performante est le profil, l&apos;ancienneté ou les niveaux de salaire. John Looney, un ancien ingénieur de Google qui au travers de son expérience autour la performance d&apos;une équipe projet et des différents éléments qui peuvent l&apos;impacter. Un article très instructif, riche en enseignement et qui fait réfléchir.
  &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;&lt;div class=&quot;col-12&quot;&gt;&lt;hr /&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;col-4&quot;&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-cards=&quot;hidden&quot; data-lang=&quot;fr&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Instagram is listening to you : &lt;a href=&quot;https://t.co/sSYxlpHFOz&quot;&gt;https://t.co/sSYxlpHFOz&lt;/a&gt;&lt;/p&gt;&amp;mdash; Jérémy DECOOL (@jdecool) &lt;a href=&quot;https://twitter.com/jdecool/status/902059491163860993&quot;&gt;28 août 2017&lt;/a&gt;&lt;/blockquote&gt;
  &lt;/div&gt;
  &lt;div class=&quot;col-8 tweet-desc&quot;&gt;
    Dans un monde où l&apos;on est de plus en plus &quot;traqué&quot; sur Internet, nous attachons une importance au respect de notre vie privée à un point que cela commence à toucher le grand public. Cet article explique comment l&apos;application mobile Instagram espionne ses utilisateurs pour obtenir un certain nombre d&apos;informations vous concernant et ainsi pouvoir vous proposer une publicité toujours plus ciblée.
  &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;&lt;div class=&quot;col-12&quot;&gt;&lt;hr /&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;col-4&quot;&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-cards=&quot;hidden&quot; data-lang=&quot;fr&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Why some people don&amp;#39;t do Open Source : &lt;a href=&quot;https://t.co/GYbrry7hnc&quot;&gt;https://t.co/GYbrry7hnc&lt;/a&gt;&lt;/p&gt;&amp;mdash; Jérémy DECOOL (@jdecool) &lt;a href=&quot;https://twitter.com/jdecool/status/902426874667831298&quot;&gt;29 août 2017&lt;/a&gt;&lt;/blockquote&gt;
  &lt;/div&gt;
  &lt;div class=&quot;col-8 tweet-desc&quot;&gt;
    Je suis fortement attaché à l&apos;Open Source. J&apos;utilise un grand nombre d&apos;outils Open Source et j&apos;y contribue lorsque j&apos;en ai l&apos;occasion. Mais pourquoi tant de personnes ne prennent-elles pas part à ce mouvement ? Cet article tentera de répondre à cette question.
  &lt;/div&gt;
&lt;/div&gt;
</description>
                    <pubDate>Mon, 04 Sep 2017 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2017/09/04/ma-semaine-de-veille-35.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2017/09/04/ma-semaine-de-veille-35.html</guid>
                </item>
            
        
            
                158
                <item>
                    <title>Les modèles économiques sur Internet</title>
                    <description>&lt;p&gt;Je suis actuellement en train de lire “&lt;a href=&quot;https://www.amazon.fr/m%C3%A9thode-Google-ferait-votre-place/dp/2753300917&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;La méthode Google&lt;/a&gt;”,
un livre qui décrypte les “lois de Google” pour en extraire des principes de
management et de comportements qui ont fait le succès de l’entreprise.&lt;/p&gt;

&lt;p&gt;Le livre aborde les choix de modèle économique de la société pour s’imposer. Un
passage m’a particulièrement interpelé : “les entreprises les plus florissantes
du Web ne font pas payer les clients le plus possible, […] tirez le moins de
gains possible du réseau [de client] pour le laisser s’étendre au maximum en
taille et en valeur”.&lt;/p&gt;

&lt;p&gt;Ce passage résonne dans ma tête tant il est criant de vérité. Jusqu’à maintenant,
les entreprises facturaient ce que le marché était prêt à payer. Mais aujourd’hui,
de nouvelles sociétés s’imposent car elle facture à leurs clients le minimum de
marges pour être viable. De cette façon, elle attire un plus grand nombre de clients
et les fidélise. Je pense immédiatement à Canal+ qui se plaint de la concurrence
d’acteurs comme Netflix. Mais les exemples sont légion ces dernières années.&lt;/p&gt;

&lt;p&gt;L’auteur, Jeff Jarvis, un journaliste américain explique aussi que ce sont les
communautés de client qui font la valeur de l’entreprise. Les sociétés du Web
doivent ouvrir leurs plateformes aux différentes communautés pour que ces
dernières s’approprient les marques. C’est ce que l’on voit de plus en plus
avec par exemple, les sites d’e-commerce qui s’ouvrent de plus en plus au travers
de Marketplace. La plupart des acteurs en sont doté Amazon, Fnac, CDiscount,
RueDuCommerce et j’en passe.&lt;/p&gt;

&lt;p&gt;Donc retenez bien ces points si vous souhaitez lancer un business dans un futur
proche. Ne facturez pas ce que les gens sont prêts à payer, facturez le minimum
dont vous avez besoin. Et surtout ne soyez pas fermé, ouvrez-vous à vos communautés
d’utilisateurs.&lt;/p&gt;
</description>
                    <pubDate>Wed, 02 Aug 2017 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2017/08/02/les-modeles-economiques-sur-internet.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2017/08/02/les-modeles-economiques-sur-internet.html</guid>
                </item>
            
        
            
                159
                <item>
                    <title>Connexion master/slave avec Doctrine</title>
                    <description>&lt;p&gt;Pour optimiser les performances d’une application, il arrive que l’on mette en place
une architecture type master/slave. Ce type de fonctionnement consiste à mettre en
place un serveur maître, répliqué sur un serveur esclave. Les accès à la base de
données sont ensuite séparées: les opérations de lecture s’effectuent sur l’instance
répliquée alors que les écritures se font sur le serveur maître.&lt;/p&gt;

&lt;p&gt;Mettre en place ce type de fonctionnement en PHP lorsque l’on utilise Doctrine est
très simple puisque la bibliothèque a prévu ce mode de fonctionnement. Pour cela,
il est nécessaire de spécifier le type de connexion à utiliser au travers du paramètre
&lt;code&gt;wrapperClass&lt;/code&gt; et de décrire comment se connecter aux différents serveurs.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;$connection = Doctrine\DBAL\DriverManager::getConnection([
    &amp;#39;wrapperClass&amp;#39; =&amp;gt; &amp;#39;Doctrine\DBAL\Connections\MasterSlaveConnection&amp;#39;,
    &amp;#39;driver&amp;#39; =&amp;gt; &amp;#39;pdo_mysql&amp;#39;,
    &amp;#39;master&amp;#39; =&amp;gt; [&amp;#39;user&amp;#39; =&amp;gt; &amp;#39;&amp;#39;, &amp;#39;password&amp;#39; =&amp;gt; &amp;#39;&amp;#39;, &amp;#39;host&amp;#39; =&amp;gt; &amp;#39;&amp;#39;, &amp;#39;dbname&amp;#39; =&amp;gt; &amp;#39;&amp;#39;],
    &amp;#39;slaves&amp;#39; =&amp;gt; [
        [&amp;#39;user&amp;#39; =&amp;gt; &amp;#39;slave1&amp;#39;, &amp;#39;password&amp;#39;, &amp;#39;host&amp;#39; =&amp;gt; &amp;#39;&amp;#39;, &amp;#39;dbname&amp;#39; =&amp;gt; &amp;#39;&amp;#39;],
        [&amp;#39;user&amp;#39; =&amp;gt; &amp;#39;slave2&amp;#39;, &amp;#39;password&amp;#39;, &amp;#39;host&amp;#39; =&amp;gt; &amp;#39;&amp;#39;, &amp;#39;dbname&amp;#39; =&amp;gt; &amp;#39;&amp;#39;],
    ]
]);&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;La configuration de la connexion s’effectue au sein de DBAL (la couche
d’abstraction d’accès à la base de données du projet). Cette configuration
est donc également possible au niveau de Doctrine ORM ainsi que son implémentation
dans les frameworks Zend et Symfony.&lt;/p&gt;

&lt;p&gt;Par exemple, pour utiliser ce mode de connexion à la base de données dans
un projet Symfony, il suffira d’ajouter la configuration suivante :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;# app/config/config.yml
doctrine:
    dbal:
        driver:   %database_driver%
        host:     %database_host%
        port:     %database_port%
        dbname:   %database_name%
        user:     %database_user%
        password: %database_password%
        slaves:
            slave1:
                host:     %database_host_slave1%
                port:     %database_port_slave1%
                dbname:   %database_name_slave1%
                user:     %database_user_slave1%
                password: %database_password_slave1%


            slave2:
                host:     %database_host_slave2%
                port:     %database_port_slave2%
                dbname:   %database_name_slave2%
                user:     %database_user_slave2%
                password: %database_password_slave2%&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Lors de l’utilisation de la connexion &lt;code&gt;MasterSlaveConnection&lt;/code&gt;, il est important
de noter les opérations qui seront effectuées sur la base maître et sur la base
esclave. Par exemple, l’appel aux méthodes &lt;code&gt;exec&lt;/code&gt;, &lt;code&gt;executeUpdate&lt;/code&gt;, &lt;code&gt;insert&lt;/code&gt;,
&lt;code&gt;delete&lt;/code&gt;, &lt;code&gt;update&lt;/code&gt;, &lt;code&gt;createSavepoint&lt;/code&gt;, &lt;code&gt;releaseSavepoint&lt;/code&gt;, &lt;code&gt;beginTransaction&lt;/code&gt;,
&lt;code&gt;rollback&lt;/code&gt;, &lt;code&gt;commit&lt;/code&gt;, &lt;code&gt;query&lt;/code&gt; et &lt;code&gt;prepare&lt;/code&gt; s’exécuteront sur la base maître.&lt;/p&gt;

&lt;p&gt;Pour lire des données depuis les connexions secondaires, il sera nécessaire d’utiliser
la méthode &lt;code&gt;executeQuery&lt;/code&gt;. Lors de cette action, un serveur “esclave” sera choisi
aléatoirement.&lt;/p&gt;

&lt;p&gt;Il est également important de noter qu’il est possible de faire une opération
d’écriture sur les serveurs esclaves si la requête effectuée n’ouvre pas de
transaction. Il est tout à fait possible d’écrire le code suivant :
&lt;code&gt;$connection-&amp;gt;executeQuery(&quot;DELETE FROM table&quot;);&lt;/code&gt;.&lt;/p&gt;
</description>
                    <pubDate>Wed, 19 Jul 2017 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2017/07/19/connexion-master-slave-avec-doctrine.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2017/07/19/connexion-master-slave-avec-doctrine.html</guid>
                </item>
            
        
            
                160
                <item>
                    <title>Les dépendances locales récursives dans un projet Composer</title>
                    <description>&lt;p&gt;Dans un précédent billet, j’ai parlé de comment il était possible de
&lt;a href=&quot;/blog/2017/06/26/gerer-les-dependances-composer-dans-un-projet-monorepo.html&quot;&gt;gérer les dépendances PHP dans un projet monorepo avec Composer&lt;/a&gt;.
Si vous avez testé la méthode décrite, vous avez peut-être rencontré des difficultés
lorsqu’une dépendance de votre projet a elle-même une dépendance dans le même dépôt.&lt;/p&gt;

&lt;p&gt;Par exemple, si je développe un site e-commerce avec un composant &lt;code&gt;Cart&lt;/code&gt; pour la
gestion du panier et que je souhaite implémenter ce dernier dans une application
Symfony. Je peux alors être tenté de créer un &lt;code&gt;CartBundle&lt;/code&gt; qui sera intégré dans
l’application principale.&lt;/p&gt;

&lt;p&gt;On aurait l’arborescence ci-dessous :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;applications/
    frontend/
        composer.json # Dépendance vers CartBundle
components/
    Cart/
        composer.json # Autonome
    CartBundle/
        composer.json # Dépendance vers Cart&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Le fonctionnement de Composer dans ce cas est clairement expliqué dans
&lt;a href=&quot;https://getcomposer.org/doc/faqs/why-can%27t-composer-load-repositories-recursively.md&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;la documentation&lt;/a&gt;:
les &lt;code&gt;repositories&lt;/code&gt; ne sont pas chargés récursivement. En effet, Composer estime
que ce paramètre de configuration est exceptionnel et doit être utilisé de
manière temporaire.&lt;/p&gt;

&lt;p&gt;Pour pallier ce problème, vous devrez spécifier explicitement les sous dépendances
dans la configuration votre application principale.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;applications/
    frontend/
        composer.json # Dépendance vers CartBundle et explicite vers Cart
components/
    Cart/
        composer.json # Autonome
    CartBundle/
        composer.json # Dépendance vers Cart&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Heureusement, pour éviter d’avoir à spécifier tous les répertoires contenant nos
dépendances, Composer autorise l’utlisation de “wildcard” tel que &lt;code&gt;*&lt;/code&gt; et &lt;code&gt;?&lt;/code&gt;.
Ainsi le fichier &lt;code&gt;composer.json&lt;/code&gt; de notre application pourrait ressembler à :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;{
  &amp;quot;repositories&amp;quot;: [
    {
      &amp;quot;type&amp;quot;: &amp;quot;path&amp;quot;,
      &amp;quot;url&amp;quot;: &amp;quot;../../components/*&amp;quot;
    }
  ],
  &amp;quot;require&amp;quot;: {
      &amp;quot;myapp/Cart&amp;quot;: &amp;quot;*&amp;quot;,
      &amp;quot;myapp/CartBundle&amp;quot;: &amp;quot;*&amp;quot;
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

</description>
                    <pubDate>Thu, 13 Jul 2017 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2017/07/13/les-dependances-locales-recursives-dans-un-projet-composer.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2017/07/13/les-dependances-locales-recursives-dans-un-projet-composer.html</guid>
                </item>
            
        
            
        
            
                161
                <item>
                    <title>Gérer les dépendances Composer dans un projet monorepo</title>
                    <description>&lt;p&gt;Mettre en place un monorepo (aka. un dépôt monolithique), c’est mettre en place
un dépôt global regroupant toutes les applications et divers composants de votre
projet. Dans ce billet, nous n’allons pas voir les avantages ou inconvénients de
ce type d’organisation, certains ont fait &lt;a href=&quot;http://danluu.com/monorepo/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;des billets sur les avantages&lt;/a&gt;,
&lt;a href=&quot;http://engineeredweb.com/blog/2016/monorepo-dangers/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;les inconvénients&lt;/a&gt; ou des
&lt;a href=&quot;https://www.youtube.com/watch?v=VBWVNIUGKW0&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;conférences&lt;/a&gt;
à ce sujet. Nous allons plutôt voir comment mettre en place une gestion de
dépendances avec Composer.&lt;/p&gt;

&lt;p&gt;Prenons par exemple, un projet ayant l’arborescence suivante :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;applications/
    api/
        composer.json
    backend/
        composer.json
    frontend/
        composer.json
    worker/
        composer.json
component/
    package1/
        composer.json
    package2/
        composer.json&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;La problématique va être de pouvoir gérer simplement dans les différentes
applications, les dépendances présentes dans le répertoire &lt;code&gt;component&lt;/code&gt;. La
première idée qui peut venir à l’esprit pourrait être de configurer
&lt;a href=&quot;https://getcomposer.org/doc/01-basic-usage.md#autoloading&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;l’autoloading de composer&lt;/a&gt;
afin de faire référence aux différents composants déclarer dans le dépôt.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;{
  &amp;quot;autoload&amp;quot;: {
    &amp;quot;psr-4&amp;quot;: {
      &amp;quot;Vendor\\Package1\\&amp;quot;: &amp;quot;../../component/package1/src&amp;quot;,
      &amp;quot;Vendor\\Package2\\&amp;quot;: &amp;quot;../../component/package2/src&amp;quot;
    }
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Bien que cette méthode fonctionne, elle n’est cependant pas optimale car avec
cette dernière, le fichier &lt;code&gt;composer.json&lt;/code&gt; du composant n’est pas utilisé. Or,
ce dernier contient diverses informations sur la bibliothèque. Dans le cas présent,
nous avons dû redéfinir la configuration de l’autoloading de la bibliothèque.&lt;/p&gt;

&lt;p&gt;Composer définit le concept de
&lt;a href=&quot;https://getcomposer.org/doc/05-repositories.md#repository&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;“repositories”&lt;/a&gt;.
Globalement, un repository correspond à une source de composant. Par défaut et
sans que le développeur n’ait besoin de faire quoi que ce soit, Composer définit
Packagist comme source principale.&lt;/p&gt;

&lt;p&gt;Il est bien évidemment possible d’ajouter de nouvelles sources de &lt;code&gt;repository&lt;/code&gt; et
Composer supporte différentes sources de données telle que &lt;code&gt;github&lt;/code&gt;, &lt;code&gt;gitlab&lt;/code&gt;,
&lt;code&gt;vcs&lt;/code&gt; et bien d’autres encore. Mais celle qui va nous intéresser est &lt;code&gt;path&lt;/code&gt;.
Cette dernière va permettre de définir une source de composants correspondant à un
chemin de notre machine.&lt;/p&gt;

&lt;p&gt;Il est donc possible d’utiliser un composant en modifiant le &lt;code&gt;composer.json&lt;/code&gt; d’une
application de la manière suivante :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;{
  &amp;quot;repositories&amp;quot;: [
    {
      &amp;quot;type&amp;quot;: &amp;quot;path&amp;quot;,
      &amp;quot;url&amp;quot;: &amp;quot;../../component/package1&amp;quot;
    },
    {
      &amp;quot;type&amp;quot;: &amp;quot;path&amp;quot;,
      &amp;quot;url&amp;quot;: &amp;quot;../../component/package2&amp;quot;
    }
  ],
  &amp;quot;require&amp;quot;: {
      &amp;quot;vendor/package1&amp;quot;: &amp;quot;*&amp;quot;,
      &amp;quot;vendor/package2&amp;quot;: &amp;quot;*&amp;quot;
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Au travers de cette modification, il n’est plus nécessaire de redéfinir la
configuration spécifiée dans le composant, puisque Composer va charger ce
dernier comme n’importe quelle autre dépendance de votre projet.&lt;/p&gt;

&lt;p&gt;En faisant des recherches sur la meilleure manière de gérer les dépendances au
sein d’un monorepo, j’ai également découvert un projet expérimental se présentant
sous la forme d’un &lt;a href=&quot;https://github.com/beberlei/composer-monorepo-plugin&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;plugin Composer&lt;/a&gt;
dédié à la gestion de ce type d’organisation. Je n’ai pas expérimenté ce dernier
et bien qu’il ne semble plus maintenu, il peut être intéressant d’y jeter un oeil.
Son auteur a publié &lt;a href=&quot;https://beberlei.de/2016/05/28/composer_monorepo_plugin_previously_called_fiddler.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;
un article expliquant l’objectif et le fonctionnement du projet&lt;/a&gt;.&lt;/p&gt;
</description>
                    <pubDate>Mon, 26 Jun 2017 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2017/06/26/gerer-les-dependances-composer-dans-un-projet-monorepo.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2017/06/26/gerer-les-dependances-composer-dans-un-projet-monorepo.html</guid>
                </item>
            
        
            
                162
                <item>
                    <title>Des &quot;metapackages&quot; Composer prochainement dans Symfony</title>
                    <description>&lt;p&gt;C’est en faisant un tour sur les &lt;a href=&quot;https://github.com/symfony&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;dépôts Github de Symfony&lt;/a&gt;
que j’ai découvert la création récente (à partir de la fin du mois de mai) de
“metapackage” Composer. Pour rappel, un métapaquet est une dépendance vide dont
l’objectif est de déclencher l’installation d’autres dépendances.&lt;/p&gt;

&lt;p&gt;Après une recherche rapide sur le blog ainsi que sur la documentation, je n’ai
pas trouvé la trace d’une communication officielle concernant ces nouvelles
dépendances. On peut donc lister à ce jour 4 métapaquets :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;javascript:;&quot; class=&quot;broken-link&quot; rel=&quot;nofollow&quot; data-original-url=&quot;https://github.com/symfony/annotations-pack &quot;&gt;annotations-pack&lt;/a&gt; pour la gestion des annotations&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/symfony/profiler-pack &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;profiler-pack&lt;/a&gt;  permettant d’avoir le Web profiler Symfony&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/symfony/orm-pack &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;orm-pack&lt;/a&gt; pour l’installation des modules Doctrine ORM&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/symfony/debug-pack &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;debug-pack&lt;/a&gt; pour obtenir les composants nécessaires au debug, à la mise en place de tests et à la gestion de logs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;La création de ces dépendances sera néanmoins pratique pour installer rapidement
des packs de fonctionnalités avec le nouveau mode de distribution de Symfony 4.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Edit&lt;/strong&gt;: Suite à mon article, Kevin Dunglas a fait un tweet pour expliquer
rapidement comment ces métapaquets étaient utilisés.&lt;/p&gt;

&lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;fr&quot;&gt;&lt;p lang=&quot;fr&quot; dir=&quot;ltr&quot;&gt;C&amp;#39;est utilisé par Flex en fait. C&amp;#39;est grâce à ces paquets que tu peux faire composer req api orm templating par exemple.&lt;/p&gt;&amp;mdash; Kévin Dunglas (@dunglas) &lt;a href=&quot;https://twitter.com/dunglas/status/878211902920413184&quot;&gt;23 juin 2017&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; src=&quot;//platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;

</description>
                    <pubDate>Fri, 23 Jun 2017 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2017/06/23/des-metapackages-composer-prochainement-dans-symfony.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2017/06/23/des-metapackages-composer-prochainement-dans-symfony.html</guid>
                </item>
            
        
            
                163
                <item>
                    <title>Novaway lance son blog</title>
                    <description>&lt;p&gt;C’est désormais officiel, le &lt;a href=&quot;https://www.novaway.fr/blog&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;blog de Novaway&lt;/a&gt;
(la société pour laquelle je travaille :)) est en ligne ! Sur ce dernier, vous
retrouverez des astuces, méthodes, technos et retours d’expérience tournant
autour de nos 3 pôles d’expertises que sont la technique, le conseil et le
marketing.&lt;/p&gt;

&lt;p&gt;Je publierai donc conjointement à ce blog, quelques articles sur le blog d’entreprise.
Là où ici, je m’attache à partager des articles courts et concis, les articles que je
publierai sur le site de Novaway seront plus exhaustifs et complets.&lt;/p&gt;

&lt;p&gt;À ce jour, j’ai publié 3 articles que je vous invite à découvrir (ainsi que ceux de
mes collègues) :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;javascript:;&quot; class=&quot;broken-link&quot; rel=&quot;nofollow&quot; data-original-url=&quot;https://www.novaway.fr/blog/tech/les-outils-de-profiling-php-open-source-en-2017 &quot;&gt;Une analyse des outils de profiling open source en 2017&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.novaway.fr/blog/tech/symfony-live-paris-2017-retours &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Les retours du Symfony Live Paris 2017&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.novaway.fr/blog/tech/retour-sur-le-mixit-2017 &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Ainsi que ceux du MixIT 2017&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Et pour ne louper aucun billet, n’hésitez pas à vous abonner au &lt;a href=&quot;javascript:;&quot; class=&quot;broken-link&quot; rel=&quot;nofollow&quot; data-original-url=&quot;https://www.novaway.fr/blog/feed &quot;&gt;flux RSS&lt;/a&gt;.&lt;/p&gt;
</description>
                    <pubDate>Thu, 01 Jun 2017 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2017/06/01/novaway-lance-son-blog.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2017/06/01/novaway-lance-son-blog.html</guid>
                </item>
            
        
            
        
            
                164
                <item>
                    <title>RSS est mort, longue vie à JSONFeed</title>
                    <description>&lt;p&gt;Peut-être que vous en avez jamais entendu parler, il a pourtant l’ambition de
remplacer le format RSS. &lt;a href=&quot;http://jsonfeed.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;JSONFeed&lt;/a&gt;,
dont la première version a été publiée le 17 mai 2017 est un nouveau format
de syndication entièrement basé comme son  nom l’indique sur JSON.&lt;/p&gt;

&lt;p&gt;Le &lt;a href=&quot;https://fr.wikipedia.org/wiki/RSS&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;format RSS&lt;/a&gt;
qui est massivement utilisé (à défaut d’alternative), a été créé en 1999 et permet
de suivre facilement un flux d’informations. Le format a évolué en 2003, pour
donner naissance à &lt;a href=&quot;https://fr.wikipedia.org/wiki/Atom&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Atom&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;JSONFeed tente d’apporter un nouveau souffle aux formats de syndication. Ce dernier
a été conçu par deux développeurs qui ont souhaité proposer une alternative moderne
en remarquant que le format JSON est devenu un standard incontournable du Web et que
la plupart des développeurs souhaiter éviter XML.&lt;/p&gt;

&lt;p&gt;Un format que l’on se doit de suivre dans les mois à venir.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;PS: Le format étant récent, il n’existe pas d’outil permettant de générer un flux JSONFeed
en PHP. J’ai donc commencé à écrire un &lt;a href=&quot;https://github.com/jdecool/jsonfeed/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;composant PHP&lt;/a&gt;
pour cela. N’hésitez pas à contribuer.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;Les fluxs JSONFeed sont disponibles pour suivre la mise à jour de ce blog&lt;/p&gt;
&lt;/blockquote&gt;
</description>
                    <pubDate>Mon, 29 May 2017 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2017/05/29/rss-est-mort-longue-vie-a-jsonfeed.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2017/05/29/rss-est-mort-longue-vie-a-jsonfeed.html</guid>
                </item>
            
        
            
                165
                <item>
                    <title>Assurer le redémarrage d&apos;une application Go en cas de plantage</title>
                    <description>&lt;p&gt;Lorsque l’on développe une application Go et que l’on souhaite déployer cette
dernière, on se pose souvent la question: “Comment assurer son redémarrage en
cas de plantage ?”. Car contrairement à un script PHP ou tout autre script qui
est exécuté par un serveur Web, en cas d’erreur fatale de notre application, le
processus faisant fonctionner cette dernière va s’arrêter.&lt;/p&gt;

&lt;p&gt;Pour cela, nous avons besoin d’utiliser un programme dont le but est de gérer
les processus. Pour cela j’utilise &lt;a href=&quot;http://supervisord.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Supervisor&lt;/a&gt;.
Ce dernier va donc s’assurer qu’il y a toujours une instance de l’application en
fonctionnement et en cas d’arrêt de cette dernière, relancera un nouveau processus
automatiquement.&lt;/p&gt;

&lt;p&gt;Dans sa version la plus simple, un service Supervisor se paramètre au travers
d’un fichier de configuration :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;[program:my_app]
command=/path/to/app&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Si vous souhaitez en savoir plus, je vous invite à parcourir &lt;a href=&quot;http://supervisord.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;la documentation de
l’outil&lt;/a&gt;. Elle vous permettra notamment de comprendre comment ce dernier fonctionne
ainsi que la paramètre sur lesquels vous pouvez agir et qui vous permettront par
exemple de définir l’utilisateur qui exécutera le processus, le nombre d’instance
que vous souhaitez démarrer, comment doivent être gérer les sorties standard et bien
d’autres encore…&lt;/p&gt;
</description>
                    <pubDate>Mon, 15 May 2017 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2017/05/15/assurer-le-redemarrage-d-une-application-go-en-cas-de-plantage.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2017/05/15/assurer-le-redemarrage-d-une-application-go-en-cas-de-plantage.html</guid>
                </item>
            
        
            
                166
                <item>
                    <title>Choisissez bien les dépendances de vos projets</title>
                    <description>&lt;p&gt;La semaine dernière, une équipe à côté de laquelle je me trouvais faisait le point
sur un projet. Il y avait alors un échange concernant l’utilisation d’une dépendance
qui n’était pas compatible PHP 7, la version de PHP choisie pour démarrer ce dernier.
Il en a résulté que l’équipe a été obligé de développer le projet en 5.6, ce qui
m’a fortement fait réagir. Bien qu’il puisse y avoir des raisons qui peuvent conduire
à prendre une telle décision, mais dans le cas présent, je trouve cela aberrant.&lt;/p&gt;

&lt;p&gt;Pourquoi ? Tout d’abord parce que la dépendance en question devait être mise en place
pour intégrer Elasticsearch dans le projet. Il existe une multitude de composants
permettant cela et le client officiel est très bien fait. Je peux comprendre que
dans le cas présent, l’équipe souhaitait un bundle Symfony qui ait quelques fonctionnalités
supplémentaires, mais vu l’envergure du projet, n’était-il possible de simplement
utiliser la librairie de base ? Mais si le bundle était si important pour l’équipe,
n’était-il pas possible de faire une PR pour apporter la compatibilité sur la branche
7 de PHP et d’en faire profiter la communauté ?&lt;/p&gt;

&lt;p&gt;Mais au-delà de tout ça, essayons de se poser les bonnes questions. En effet,
&lt;a href=&quot;http://php.net/supported-versions.php&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;PHP 5.6 est en fin de vie&lt;/a&gt;.
Cette version n’a plus de support actif et reçoit donc maintenant que des correctifs
de sécurité. PHP 7.0 est sortie il y a maintenant plus de deux ans et PHP 7.1 il y a
un an. Si un composant PHP n’est pas compatible PHP 7, nous sommes alors en droit
de se demander si ce dernier est encore actif et/ou maintenu ? Est-ce vraiment judicieux
de démarrer un nouveau projet dans ces conditions ? Est-ce un pari gagnant dans le long
terme ?&lt;/p&gt;

&lt;p&gt;Pour ma part, la réponse est clairement non, surtout pour un projet qui est amené
à être créé maintenant. Il est primordial de bien choisir les dépendances que l’on
ajoute à un projet et d’avoir également une vision à long terme dans les phases de
conceptions et réflexions autour du projet.&lt;/p&gt;

&lt;p&gt;Si l’on devait conclure ce billet, et si vous n’aviez qu’une seule phrase à retenir,
ça serait :&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Ne démarrez pas vos projets sur des versions de PHP déjà obsolètes et utilisez
des dépendances connues et maintenues !&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Votre équipe vous en remerciera, car comme je le tweetais cette semaine :&lt;/p&gt;

&lt;center&gt;
&lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;fr&quot;&gt;&lt;p lang=&quot;fr&quot; dir=&quot;ltr&quot;&gt;N&amp;#39;oubliez pas que lorsque vous prenez des raccourcis dans vos devs, vous finissez irrémédiablement par en payer les conséquences tôt ou tard&lt;/p&gt;&amp;mdash; Jérémy DECOOL (@jdecool) &lt;a href=&quot;https://twitter.com/jdecool/status/860110979987898369&quot;&gt;4 mai 2017&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; src=&quot;//platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;/center&gt;
</description>
                    <pubDate>Thu, 11 May 2017 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2017/05/11/choisissez-bien-les-dependances-projets.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2017/05/11/choisissez-bien-les-dependances-projets.html</guid>
                </item>
            
        
            
                167
                <item>
                    <title>Utiliser Chrome Headless avec Behat</title>
                    <description>&lt;p&gt;Il sera désormais possible à partir de la version 59 de Chrome (la version bêta
au moment où j’écris ces lignes) en mode “headless”, c’est-à-dire sans interface
graphique. Ce mode de fonctionnement est intéressant pour les tests par exemple,
car il ne sera plus obligatoire de “piloter” Chrome via son interface graphique.&lt;/p&gt;

&lt;p&gt;Cette nouvelle fonctionnalité a provoqué un large engouement de la part de la
communauté Web. À tel point que le mainteneur de PhantomJS, un navigateur headless,
également basé sur Webkit &lt;a href=&quot;https://groups.google.com/d/msg/phantomjs/9aI5d-LDuNE/5Z3SMZrqAQAJ&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;a annoncé la fin du projet&lt;/a&gt;.
Donc si comme moi, vous utilisiez ce dernier dans vos scénarios Behat, il va être
nécessaire de changer de solution.&lt;/p&gt;

&lt;p&gt;Pour cela, rien de plus simple. Voici par exemple la configuration Behat pour
Selenium que j’utilisais avec PhantomJS.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;# behat.yml
default:
    extensions:
        # ...
        Behat\MinkExtension:
            base_url: http://project.dev
            sessions:
                default:
                    selenium2:
                        wd_host: http://localhost:4444/wd/hub&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Pour que Selenium puisse interagir avec Chrome, nous allons devoir utiliser un
driver additionnel &lt;a href=&quot;https://sites.google.com/a/chromium.org/chromedriver/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;ChromeDriver&lt;/a&gt;.
Une fois ce dernier installé, nous devrons ajouter quelques lignes de configuration
supplémentaires.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;# behat.yml
default:
    extensions:
        # ...
        Behat\MinkExtension:
            base_url: http://project.dev
            sessions:
                default:
                    selenium2:
                        browser: chrome
                        wd_host: http://localhost:4444/wd/hub
                        capabilities:
                            chrome:
                                switches:
                                    - &amp;quot;--headless&amp;quot;
                                    - &amp;quot;--disable-gpu&amp;quot;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Parmi les changements, nous spécifions à Selenium d’utiliser Chrome en tant que
navigateur au travers de l’argument &lt;code&gt;browser&lt;/code&gt;. Puis nous spécifions des &lt;code&gt;capabilities&lt;/code&gt;,
c’est-à-dire des options permettant de personnaliser et de configurer la session
Chrome qui sera ouverte. Nous indiquons ainsi vouloir une navigateur &lt;code&gt;headless&lt;/code&gt;.
Le paramètre &lt;code&gt;--disable-gpu&lt;/code&gt; est pour le moment obligatoire.&lt;/p&gt;

&lt;p&gt;Et voilà, en ayant tout juste rajouté 5 lignes de configuration, vous avez remplacé
l’utilisation de PhantomJS par Google Chrome en mode “headless”.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;a href=&quot;https://gist.github.com/jdecool/ec2dbc08e79e66d27b3d56d10ea28bf4&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Le fichier de configuration est disponible sur Gist&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
</description>
                    <pubDate>Tue, 09 May 2017 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2017/05/09/utiliser-chrome-headless-avec-behat.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2017/05/09/utiliser-chrome-headless-avec-behat.html</guid>
                </item>
            
        
            
        
            
                168
                <item>
                    <title>Jekyll sous Docker avec des dépendances tierces</title>
                    <description>&lt;p&gt;Ce blog est généré avec &lt;a href=&quot;https://jekyllrb.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Jekyll&lt;/a&gt;
et j’utilisais jusqu’à maintenant Docker avec un conteneur personnalisé, basé sur l’image
officielle mais qui avait quelques dépendances supplémentaires. Néanmoins, je viens
de découvrir que l’image officielle était amplement suffisante, même si vous avez besoin
d’installer des gems supplémentaires.&lt;/p&gt;

&lt;p&gt;Jekyll est un outil écrit en Ruby, c’est donc tout naturellement que j’utilise
&lt;a href=&quot;http://bundler.io&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Bundler&lt;/a&gt;
pour simplifier la gestion et l’installation des dépendances que j’utilise pour
faire tourner ce blog (génération d’un sitemap, highlight, compression des assets, …).&lt;/p&gt;

&lt;p&gt;J’utilisais donc une image personnalisée qui pré-installé toutes les dépendances nécessaires.
Pourtant, je viens de découvrir qu’il était possible de spécifier à bundle où installer
les dépendances. De ce fait, j’installe ces dernières au même niveau que le blog et
non plus de manière globale comme précédemment.&lt;/p&gt;

&lt;p&gt;Si vous êtes dans le même cas que moi, vous pouvez utiliser cette commande :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;docker run --rm -v $PWD:/src -w /src -p 4000:4000 jekyll/jekyll sh -c &amp;quot;bundle install --path vendor/bundle &amp;amp;&amp;amp; exec jekyll [command]&amp;quot;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

</description>
                    <pubDate>Mon, 08 May 2017 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2017/05/08/docker-jekyll-et-les-dependances-tierces.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2017/05/08/docker-jekyll-et-les-dependances-tierces.html</guid>
                </item>
            
        
            
                169
                <item>
                    <title>Utiliser l&apos;extension PHP Tideways dans Travis CI</title>
                    <description>&lt;p&gt;Tideways est une extension PHP permettant d’ajouter le support du profiling. Le profiling
est une activité qui consiste à collecter un certain nombre d’informations sur l’exécution
d’un code (PHP dans notre cas). Tideways est en réalité la continuité de l’extension
XHProf précédemment développé par Facebook, mais aujourd’hui abandonné au profit de
HHVM. Le principal avantage de cette dernière est que l’extension est compatible avec
les versions 7 de PHP.&lt;/p&gt;

&lt;p&gt;L’extension PHP n’étant pas “standard”, il n’est pas possible de l’activer directement
dans &lt;a href=&quot;https://travis-ci.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Travis CI&lt;/a&gt;.
Il sera donc nécessaire de télécharger, compiler et activer l’extension en amont de
la phase de build. Pour cela, nous allons rajouter les commandes suivantes dans notre
fichier &lt;code&gt;.travis.yml&lt;/code&gt; :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;# ...
before_scripts:
  # ...
  - wget -Otideways-php.tar.gz https://github.com/tideways/php-profiler-extension/archive/v4.1.1.tar.gz
  - tar xvfz tideways-php.tar.gz -C /tmp
  - cd /tmp/php-profiler-extension-4.1.1
  - phpize
  - ./configure
  - make
  # ...&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Les commandes ci-dessus téléchargent les sources de l’extension, les décompressent dans
le répertoire &lt;code&gt;/tmp&lt;/code&gt; et les compilent. Une fois ces étapes, il ne reste plus qu’à activer
l’extension auprès de PHP. Cela peut être fait en ajoutant une ligne dans le fichier
de configuration PHP :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;echo &amp;quot;extension=/tmp/php-profiler-extension-4.1.1/modules/tideways.so&amp;quot; &amp;gt;&amp;gt; ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Sinon il est possible si vous utilisez PHP via la ligne de commande d’activer l’extension
à la volée via l’option &lt;code&gt;-d&lt;/code&gt; :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;php -dextension=/tmp/php-profiler-extension-4.1.1/modules/tideways.so vendor/bin/phpunit&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

</description>
                    <pubDate>Sun, 23 Apr 2017 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2017/04/23/utiliser-l-extension-php-tideways-dans-travis-ci.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2017/04/23/utiliser-l-extension-php-tideways-dans-travis-ci.html</guid>
                </item>
            
        
            
                170
                <item>
                    <title>Ce que je retiens du SymfonyLive 2017</title>
                    <description>&lt;p&gt;J’ai eu l’occasion cette année de participer au SymfonyLive organisé par SensioLabs
à Paris. Tout comme ma dernière participation en 2015, cette année encore, j’ai
pu assister à des conférences de qualité autour de PHP et du framework Symfony.&lt;/p&gt;

&lt;center&gt;&lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;fr&quot;&gt;&lt;p lang=&quot;fr&quot; dir=&quot;ltr&quot;&gt;Présent au &lt;a href=&quot;https://twitter.com/hashtag/Symfony_Live?src=hash&quot;&gt;#Symfony_Live&lt;/a&gt; pour la team &lt;a href=&quot;https://twitter.com/novaway&quot;&gt;@novaway&lt;/a&gt; avec &lt;a href=&quot;https://twitter.com/FloChntrl&quot;&gt;@FloChntrl&lt;/a&gt; &lt;a href=&quot;https://t.co/9DUFBCQ3R3&quot;&gt;pic.twitter.com/9DUFBCQ3R3&lt;/a&gt;&lt;/p&gt;&amp;mdash; Jérémy DECOOL (@jdecool) &lt;a href=&quot;https://twitter.com/jdecool/status/847346570295001088&quot;&gt;30 mars 2017&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; src=&quot;//platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;&lt;/center&gt;

&lt;p&gt;Mais plus important que les différentes conférences et présentations qui sont
données tout au long de ces 2 jours, c’est l’occasion de rencontrer et de discuter
avec la communauté. Cela permet de voir les tendances émergentes ainsi que celles
qui sont considérées comme acquis auprès de tous. Cela va même au-delà de l’écosystème
Symfony.&lt;/p&gt;

&lt;p&gt;Ce que je retiens donc de cet événement :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Parmi les “buzzwords” actuels, difficile d’éviter de parler des &lt;em&gt;microservices&lt;/em&gt;.
Les acteurs majeurs du Web ont largement migré vers ce type d’architecture. Mais
les microservices ont du mal à se frayer un chemin dans l’écosystème PHP. Bien sur
ce n’est pas une réponse universelle, mais des discussions que je peux avoir avec
de nombreux développeurs, l’idée qui en ressort c’est que le concept est intéressant
mais qu’il ne s’adapte jamais réellement au projet sur lequel on travaille. Il faut
également que ce type d’architecture à un impact organisationnel sur les équipes qui
la mette en place.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;S’il y a bien un autre concept dont on entend de plus en plus parler dans l’écosystème
PHP, c’est le &lt;em&gt;Domain Driven Design (DDD)&lt;/em&gt;. Cette technique de conception n’a rien de
nouveau puisqu’elle existe depuis de nombreuses années. Mais PHP s’étant grandement
professionnalisé, les projets que nous sommes amenés à développer sont de plus en
plus complexes. Dans ce contexte, le DDD nous permet de développer des applications
proches du métier, avec une forte expressivité de ce dernier. Sur ce point, j’ai un
peu l’impression de voir ce qui s’est passé avec la notion de tests logiciels il y
a quelques années. J’ai nettement l’impression que les développeurs, même s’ils ne vont
pas migrer complètement vers ce type de conception, ces derniers vont tenter d’appliquer
les concepts les plus importants dans leurs projets.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Le dernier point que je retiens est la démocratisation des &lt;em&gt;Platform as a service (PaaS)&lt;/em&gt;.
Les problèmes de déploiement et de gestion des ressources serveurs (aka. la “scalabilité”)
est une des préoccupations principale de nos applications. Les services de PaaS
permettent de se concentrer sur le développement de notre projet en proposant de gérer
la partie hébergement. De ce fait, les PaaS sont de plus en plus utilisés et les
offres disponibles pour ce type de service sont de plus en plus nombreuses.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;Si vous êtes intéressé par l’événement, sachez qu’il existe un
&lt;a href=&quot;https://github.com/SymfonyLive/paris-2017-talks&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;dépôt Github&lt;/a&gt;
contenant la description des présentations avec les slides associées. Les présentations
ayant été filmé, les vidéos arriveront prochainement.&lt;/p&gt;
&lt;/blockquote&gt;
</description>
                    <pubDate>Sun, 02 Apr 2017 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2017/04/02/ce-que-je-retiens-du-symfonylive-2017.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2017/04/02/ce-que-je-retiens-du-symfonylive-2017.html</guid>
                </item>
            
        
            
                171
                <item>
                    <title>L&apos;extension PHP qui permet de modifier des constantes</title>
                    <description>&lt;p&gt;Comme quoi après des années de pratique d’un langage, ce dernier peut toujours
nous surprendre. Une constante est par définition constante, sa valeur ne change pas.
Lorsque l’on en définit une en PHP via la fonction &lt;code&gt;define&lt;/code&gt;, il est donc impossible de
la changer ou de la modifier.&lt;/p&gt;

&lt;p&gt;Je viens de découvrir une extension PECL qui change tout ça ! Elle s’appelle
&lt;a href=&quot;https://pecl.php.net/package/runkit&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;runkit&lt;/a&gt;.
Cette dernière fournit un ensemble de fonction qui vous permettra entre autres,
d’ajouter, modifier et supprimer des constantes de votre code (le genre de chose
qu’on ne devrait jamais avoir à faire).&lt;/p&gt;

&lt;p&gt;Sachez en tout cas que si vous avez besoin de faire une telle chose, c’est que vous
avez un problème de conception. Et si malgré tout vous souhaitez quand même tester
l’extension, cette dernière ne semble plus maintenue et ne sera certainement pas
compatible PHP7.&lt;/p&gt;
</description>
                    <pubDate>Mon, 06 Mar 2017 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2017/03/06/l-extension-php-qui-permet-de-modifier-des-constantes.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2017/03/06/l-extension-php-qui-permet-de-modifier-des-constantes.html</guid>
                </item>
            
        
            
                172
                <item>
                    <title>Partager des clés SSH avec un conteneur Docker</title>
                    <description>&lt;p&gt;Lorsque l’on est amené à utiliser Docker, il arrive que l’on ait parfois besoin
d’accéder aux clés SSH de l’hôte. C’est notamment le cas si l’on utilise un conteneur
pour déployer une application. Il existe pour cela plusieurs solutions plus ou moins
bonnes.&lt;/p&gt;

&lt;p&gt;La première solution qui vous viendra peut-être à l’esprit et d’ajouter les clés
SSH directement dans l’image du conteneur. Cette solution est de loin la pire !
Même si vous êtes la seule personne à l’utiliser. Et si ce n’est pas le cas, c’est
une solution à proscrire, car de ce fait, vous allez partager des clés à tout un
panel d’utilisateurs. C’est une catastrophe d’un point de vue sécurité.&lt;/p&gt;

&lt;p&gt;La solution qui devrait vous venir naturellement, est de créer un volume afin de
partager les clés SSH de l’hôte avec le conteneur via une commande du style :
&lt;code&gt;docker run -it -v $HOME/.ssh:/root/.ssh my-container&lt;/code&gt; (oui, j’utilise &lt;code&gt;root&lt;/code&gt; et c’est mal).
La contrainte avec cette méthode, c’est que vous allez obtenir avec un message
d’erreur expliquant que l’utilisateur n’est soit pas le propriétaire des fichiers,
soit que les droits de ces dernières sont trop ouverts.&lt;/p&gt;

&lt;p&gt;Pour résoudre ce problème, j’ai vu des développeurs modifier directement les UID
des clés de leur machine pour le faire correspondre avec celui du conteneur. Cette
méthode n’étant pas universelle, relativement contraignante et vous obligeant à
modifier les droits et/ou le propriétaire des fichiers, est loin d’être la méthode
idéale.&lt;/p&gt;

&lt;p&gt;La meilleure solution a mon sens, est de partager les clés SSH de l’hôte dans un
répertoire temporaire du conteneur et de vérifier leurs présences au démarrage de
ce dernier. Si elle existe, elles seront alors copiées dans les répertoires de
configuration adéquate et on affectera les droits requis. Ce système est donc
universel et fonctionne peu importe l’utilisateur du conteneur et du système hôte.&lt;/p&gt;

&lt;p&gt;De manière concrète, j’ai tendance à utiliser un script de démarrage de mes conteneurs
que je définis en tant qu’&lt;code&gt;ENTRYPOINT&lt;/code&gt; dans lequel j’effectue les opérations suivantes :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;if [ -f /var/tmp/sshconf ]; then
    cp /var/tmp/sshconf /root/.ssh/config
    chmod 600 /root/.ssh/config
fi

if [ -f /var/tmp/sshid ]; then
    cp /var/tmp/sshid /root/.ssh/id_rsa
    chmod 600 /root/.ssh/id_rsa
if [ -f /var/tmp/sshconf ]; then&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Il ne vous restera plus qu’à démarrer votre conteneur Docker en définissant les fichiers
à partager : &lt;code&gt;docker run -it -v $HOME/.ssh/id_rsa:/var/tmp/sshid -v $HOME/.ssh/config:/var/tmp/sshconf my-container&lt;/code&gt;&lt;/p&gt;
</description>
                    <pubDate>Sun, 05 Mar 2017 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2017/03/05/partager-des-cles-ssh-avec-un-conteneur-docker.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2017/03/05/partager-des-cles-ssh-avec-un-conteneur-docker.html</guid>
                </item>
            
        
            
                173
                <item>
                    <title>Les déploiements automatisés</title>
                    <description>&lt;p&gt;Il est maintenant assez rare de trouver un développeur qui vous dira qu’il ne déploie
pas son projet en production de manière automatique. Cela fait aujourd’hui partie
intégrante des bonnes pratiques de l’ingénierie logicielle et les outils pour nous
aider dans cette tâche sont nombreux.&lt;/p&gt;

&lt;p&gt;Pourtant sur un grand nombre de projets que je peux voir passer ou en discutant avec
de nombreux développeurs, si le code source est effectivement poussé de manière
automatisé, le déploiement complet d’un projet informatique n’est rarement entièrement
réalisé sans intervention humaine.&lt;/p&gt;

&lt;p&gt;Par exemple, la configuration initiale du projet est très souvent réalisée manuellement.
Cette configuration permet généralement de définir les variables d’environnements et
projet nécessaire entre autres, à la connexion à la base de données. Et si même on peut
justifier que ce paramétrage n’est réalisé qu’une seule fois lors du premier déploiement,
qu’en est-il lorsqu’un paramètre doit changer ou qu’un nouveau est ajouté ?&lt;/p&gt;

&lt;p&gt;De même, si votre application nécessite la mise en place d’un système de tâches
planifiées (CRON), est-il nécessaire que quelqu’un intervienne sur le serveur ou
être vous capable de gérer cette configuration lors du déploiement ?&lt;/p&gt;

&lt;p&gt;À ces deux exemples, on pourra me répondre qu’il s’agit d’opérations ponctuelles
et qui ne sont pas réalisées de manière fréquente. En admettant que cela ne prenne
qu’un temps négligeable pour être faites, il vous sera néanmoins nécessaire d’avoir
une personne (disponible) ayant les accès requis pour effectuer le travail. Avec une
telle contrainte, on peut oublier la mise en place d’une politique de déploiement
continu. Sans parler des problèmes de sécurité potentiels liés à l’humain qui effectuera
cette tâche.&lt;/p&gt;

&lt;p&gt;Sans un processus de déploiement logiciel entièrement automatisé, on oubliera également
la possibilité pour notre infrastructure de pouvoir “scaler” si notre produit rencontre
un grand succès et est amené à croître rapidement.&lt;/p&gt;

&lt;p&gt;N’oubliez pas que déployer son application automatiquement, ce n’est pas simplement
pousser du code en production. Il y a un certain nombre de choses à prendre en compte
tel que : la mise en place et installation d’un serveur, la gestion de la configuration
du projet, une gestion autonome des secrets (notamment pour les accès base de données).&lt;/p&gt;
</description>
                    <pubDate>Mon, 27 Feb 2017 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2017/02/27/les-deploiements-automatises.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2017/02/27/les-deploiements-automatises.html</guid>
                </item>
            
        
            
                174
                <item>
                    <title>Assurer la compatibilité des tests PHPUnit 6 avec les versions antérieures</title>
                    <description>&lt;p&gt;&lt;a href=&quot;https://github.com/sebastianbergmann/phpunit/releases/tag/6.0.0&quot; target=&quot;_blank&quot; rel=&quot;noopenner noreferer&quot;&gt;PHPUnit version 6&lt;/a&gt;
a été tagguée le 3 février 2017. Cette nouvelle version n’est dorénavant compatible
qu’avec PHP 7+. Un changement majeur introduit par cette nouvelle branche est le
renommage de la classe &lt;code&gt;PHPUnit_Framework_TestCase&lt;/code&gt; en &lt;code&gt;PHPUnit\Framework\TestCase&lt;/code&gt;.
Si comme moi, il vous arrive de tester une même branche de code avec plusieurs
versions de PHP, cela pourra être un problème.&lt;/p&gt;

&lt;p&gt;J’ai eu le cas cette semaine, où, sur une base de code toujours maintenu (mais
plus pour très longtemps) sur les versions PHP 5.3+. Sans rentrer dans les détails
techniques, pour simplifier l’exécution des tests, nous récupérons une version de
PHPUnit avec la définition Composer suivante : &lt;code&gt;phpunit/phpunit:@stable&lt;/code&gt;. Ce n’est
certes pas l’idéal ni une excellente pratique, mais cela fonctionne bien.&lt;/p&gt;

&lt;p&gt;Lors du passage des tests avec PHP 7, nous récupérons ainsi la version 6 de PHPUnit
qui provoque alors une erreur puisque la classe &lt;code&gt;PHPUnit_Framework_TestCase&lt;/code&gt; qui
était utilisée jusque-là n’est plus disponible.&lt;/p&gt;

&lt;p&gt;En attendant la fin du support des versions de PHP 5.x pour le code concerné,
la solution de contournement utilisé a été de définir un alias de classe dans le
“bootstrap” de PHPUnit de la manière suivante :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;// Keep PHPUnit &amp;lt; 6.0 BC
if (!class_exists(&amp;#39;PHPUnit_Framework_TestCase&amp;#39;) &amp;amp;&amp;amp; class_exists(&amp;#39;PHPUnit\Framework\TestCase&amp;#39;)) {
    class_alias(&amp;#39;PHPUnit\Framework\TestCase&amp;#39;, &amp;#39;PHPUnit_Framework_TestCase&amp;#39;);
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;De ce fait, si la classe &lt;code&gt;PHPUnit_Framework_TestCase&lt;/code&gt; n’existe pas, c’est que nous
sommes certainement en train d’éxecuter la nouvelle version de PHPUnit. Et nous
définissons donc un alias vers la classe &lt;code&gt;PHPUnit\Framework\TestCase&lt;/code&gt; si cette
dernière existe.&lt;/p&gt;
</description>
                    <pubDate>Mon, 20 Feb 2017 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2017/02/20/assurer-la-compatibilite-des-tests-phpunit-6-avec-les-versions-anterieures.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2017/02/20/assurer-la-compatibilite-des-tests-phpunit-6-avec-les-versions-anterieures.html</guid>
                </item>
            
        
            
                175
                <item>
                    <title>Formatter des valeurs monétaires en Javascript</title>
                    <description>&lt;p&gt;Avec l’avènement des applications de type “Single Page Application” (SPA), nous
développeurs, sommes amenés à concevoir des applications qui tendent à réaliser de
plus en plus d’opérations côté client. Lorsque l’on travaille sur les problématiques
d’internationalisation, il est régulièrement nécessaire de mettre en forme des
valeurs monétaires en fonction d’une devise et d’une langue qui peut varier d’un
utilisateur à un autre.&lt;/p&gt;

&lt;p&gt;Pour cela la norme ECMAScript a introduit la fonction &lt;code&gt;Number.prototype.toLocaleString()&lt;/code&gt;
qui permet de renvoyer chaîne de caractères représentant un nombre en tenant
compte de la locale. Cette dernière gère également la mise en forme des valeurs
monétaires.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;var balance = 4200.5;

console.log(balance.toLocaleString(&amp;#39;fr-FR&amp;#39;, {
  style: &amp;quot;currency&amp;quot;,
  currency: &amp;quot;EUR&amp;quot;
})); // =&amp;gt; 4 200,50 €

console.log(balance.toLocaleString(&amp;#39;en-US&amp;#39;, {
  style: &amp;quot;currency&amp;quot;,
  currency: &amp;quot;EUR&amp;quot;
})); // =&amp;gt; $4,200.50&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Malheureusement, cette API n’est pas encore disponible dans tous les navigateurs.
Et malgré de nombreuses recherches, je ne suis pas parvenu à trouver un composant
ou un “polyfill” qui me convienne.&lt;/p&gt;

&lt;p&gt;Cela jusqu’à aujourd’hui ! En effet l’&lt;a href=&quot;https://www.linkedin.com/company/osrec-financial&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;OSREC Financial&lt;/a&gt;
de Londres, vient de publier &lt;a href=&quot;https://osrec.github.io/currencyFormatter.js/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;un composant&lt;/a&gt;
permettant de gérer le formatage des valeurs monétaires compatible sur tous les navigateurs.&lt;/p&gt;

&lt;p&gt;Bien que cette dernière ne soit pas parfaite, c’est celle qui aujourd’hui correspond
le mieux à mon besoin.&lt;/p&gt;
</description>
                    <pubDate>Fri, 30 Sep 2016 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2016/09/30/formatter-des-valeurs-monetaires-en-javascript.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2016/09/30/formatter-des-valeurs-monetaires-en-javascript.html</guid>
                </item>
            
        
            
                176
                <item>
                    <title>Accèdez facilement à vos constantes Twig avec TwigConstantAccessorBundle</title>
                    <description>&lt;p&gt;C’est sans surprise que la plupart des développeurs Symfony utilisent Twig comme
moteur de templating, il s’agit en effet du moteur fourni par défaut avec le framework.
Il arrive souvent que l’on soit amené à accéder à des constantes dans nos vues.
Pour cela, Twig nous propose différentes solutions.&lt;/p&gt;

&lt;p&gt;La solution la plus évidente est certainement l’utilisation de la fonction
&lt;a href=&quot;http://twig.symfony.com/doc/functions/constant.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;constant&lt;/code&gt;&lt;/a&gt;
ainsi que du &lt;a href=&quot;http://twig.symfony.com/doc/tests/constant.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;test correspondant&lt;/a&gt;.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-twig&quot; data-lang=&quot;twig&quot;&gt;{{ constant(&amp;#39;Namespace\\Classname::CONSTANT_NAME&amp;#39;) }}
{{ constant(&amp;#39;RSS&amp;#39;, date) }}

{% if post.status is constant(&amp;#39;Post::PUBLISHED&amp;#39;) %} ... {% endif %}
{% if post.status is constant(&amp;#39;PUBLISHED&amp;#39;, post) %} ... {% endif %}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;La &lt;a href=&quot;http://symfony.com/doc/current/templating/global_variables.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;configuration de Symfony&lt;/a&gt;
permet également de définir un certain nombre de variables, services et constantes
pouvant être accessible directement depuis vos templates Twig.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;# app/config/config.yml
twig:
    # ...
    globals:
        my_service:      &amp;quot;@my_service&amp;quot;
        my_constant_foo: foo
        my_constant_bar: bar&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Une méthode moins répandue est de passer par une &lt;a href=&quot;http://twig.symfony.com/doc/advanced.html#id1&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;extension Twig&lt;/a&gt;.
Mais attention, cette possibilité est maintenant dépréciée et sera supprimée dans
la version 2 de Twig.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;class Project_Twig_Extension extends Twig_Extension implements Twig_Extension_GlobalsInterface
{
    public function getGlobals()
    {
        return array(
            &amp;#39;text&amp;#39; =&amp;gt; new Text(),
        );
    }

    // ...
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Pour réduire tout ce travail de configuration, j’ai créé &lt;a href=&quot;https://github.com/jdecool/TwigConstantAccessorBundle&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;un bundle&lt;/a&gt;
visant à simplifier l’accès aux constantes de classe dans les templates Twig. Une fois
installé, il suffit de définir les classes dont vous souhaitez exposer les constantes
et le bundle s’occupe entièrement de la gestion de la configuration.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;# app/config/config.yml
twig_constant_accessor:
    classes:
        - AppBundle\Model\Foo
        - { class: &amp;#39;AppBundle\Model\Bar&amp;#39; }
        - { class: &amp;#39;AppBundle\Model\FooBar&amp;#39;, alias: &amp;#39;FooBarAlias&amp;#39; }&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Avec cette configuration, vous aurez directement accès aux constantes dans vos vues
Twig :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-twig&quot; data-lang=&quot;twig&quot;&gt;{{ Foo.MY_CONSTANT }} {# access AppBundle\Model\Foo::MY_CONSTANT #}
{{ Bar.KEY }}  {# access AppBundle\Model\Bar::KEY #}

{% if &amp;#39;value&amp;#39; == FooBarAlias.MY_CONSTANT %}Test is OK{% endif %}
{# FooBarAlias is an alias for AppBundle\Model\FooBar #}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

</description>
                    <pubDate>Wed, 28 Sep 2016 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2016/09/28/accedez-facilement-a-vos-constantes-dans-twig-avec-twigaccessorbundle.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2016/09/28/accedez-facilement-a-vos-constantes-dans-twig-avec-twigaccessorbundle.html</guid>
                </item>
            
        
            
        
            
                177
                <item>
                    <title>Le code, sa complexité et ses performances</title>
                    <description>&lt;p&gt;Une des choses les plus importantes de la revue de code, c’est de détecter les
rapidement les problèmes de conception et d’architecture d’un code qui va être
envoyé en production. Cette tâche est délicate, car elle fait appel à la
subjectivité du ou des relecteurs. Il faut trouver le “juste milieu” afin
d’éviter d’en faire trop ou pas assez. Souvent, le principal argument utilisé
pour éviter de découpler du code est que le fait de rajouter une classe va
complexifier le code, en plus de dégrader les performances de l’application.&lt;/p&gt;

&lt;p&gt;Dans le choix de mise en place d’une architecture, il est important de prendre
en compte un certain nombre d’aspects techniques, organisationnels et métier. Il
y a néanmoins des principes de base qui devraient (à mon sens) être systématiquement
appliqués et cela, peu importe le type de l’application développée. En respectant
certaines règles élémentaires, on facilitera l’écriture d’un code simple, évolutif,
maintenable et scalable.&lt;/p&gt;

&lt;p&gt;Le premier principe fondamental est d’avoir une séparation entre l’aspect technique
d’un code et son aspect business. Très schématiquement, il s’agit de séparer
l’objectif métier du code des détails de son implémentation technique. Par
exemple, dans le cadre d’un site d’e-commerce, un des besoins métier est de gérer
le panier afin de pouvoir y ajouter, modifier, enlever des produits que l’on
souhaite acheter ou non. L’aspect technique est de gérer la manière dont ces
informations vont être “enregistrées” (en session, en base de données…). Il est
donc important que le code sépare ces deux notions pour limiter les impacts d’une
éventuelle modification.&lt;/p&gt;

&lt;p&gt;C’est un bon début, mais cela ne suffit pas. Il faut également découper l’aspect
technique en plusieurs responsabilités. Ici, il convient d’avoir des briques
logicielles qui ne font qu’une seule chose, qui ne remplissent qu’un objectif
(récupérer de la donnée, générer des affichages, produire des logs, faire des
traitements métiers, etc.). Se retrouver avec du code qui mixe ces concepts est
une erreur. En plus de lier des notions sans rapport entre elles, cela risque de
rendre votre code difficilement testable.&lt;/p&gt;

&lt;p&gt;Les règles qui ont été présentées jusqu’ici sont d’autant plus vraies, qu’elles
peuvent s’appliquer à n’importe quel langage et pour n’importe quel type d’application.
Si vous avez du code qui ne les respecte pas, vous avez certainement une forte
dette technique.&lt;/p&gt;

&lt;p&gt;J’entends souvent (même venant de développeurs expérimentés) que découper son
code en plusieurs responsabilités n’apporte pas de valeur, complexifie le code
et dégrade les performances de l’application.&lt;/p&gt;

&lt;p&gt;Ajouter du code peut effectivement complexifier la compréhension d’un programme.
Mais, ajouter du code structuré en classe et découpé en différentes méthodes
(correctement nommées), vise bien au contraire à améliorer la compréhension de
ce dernier. De plus, cela n’a que peu d’impact sur les performances d’exécution.
En réalité, ce n’est pas le découpage du code qui nuit aux performances d’une
application, mais la manière dont ce dernier est écrit.&lt;/p&gt;

&lt;p&gt;Il faut bien se rendre compte qu’à code équivalent, même avec un découpage en
plusieurs classes, les performances seront équivalentes (les langages modernes
sont pour la plupart dotés de mécanismes d’optimisation et de mise en cache).&lt;/p&gt;
</description>
                    <pubDate>Sun, 26 Jun 2016 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2016/06/26/code-complexite-performance.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2016/06/26/code-complexite-performance.html</guid>
                </item>
            
        
            
                178
                <item>
                    <title>Pourquoi les développeurs n&apos;aiment pas Behat ?</title>
                    <description>&lt;p&gt;Depuis sa mise à disposition, Behat est un outil plutôt controversé de la part
des développeurs. Il y a ceux qui l’aiment et ne peuvent s’en passer (c’est mon
cas) et ceux qui bien au contraire le détestent. Pourtant Behat est un outil
formidable qui permet de faire le lien entre les tests d’acceptations et l’application
que l’on est en train de concevoir. Tout cela de manière automatisée. Dit autrement,
il va permettre de valider qu’une demande exprimée en langage métier donne le résultat
attendu. Pourtant, je rencontre très souvent des développeurs qui ne supportent
pas cet outil. J’ai tenté de comprendre pourquoi.&lt;/p&gt;

&lt;p&gt;Avant de tenter de répondre à cette question, je pense qu’il est nécessaire de
comprendre comment fonctionne l’outil. Comme évoqué précédemment, un des plus grands
intérêts de Behat est que l’on va exprimer le test en langue naturelle (peu importe
la langue utilisée). Le format d’écriture ressemble beaucoup à l’expression d’une
user-story utilisé dans la méthode SCRUM.&lt;/p&gt;

&lt;p&gt;Voici par exemple un exemple de scénario :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-gherkin&quot; data-lang=&quot;gherkin&quot;&gt;# language: fr
Fonctionnalité: Avoir un compte bancaire
    Afin d&amp;#39;offrir aux utilisateurs la possibilité d&amp;#39;avoir un compte bancaire
    Etant donné que je suis inscrit
    Je dois être capable d&amp;#39;ajouter ou de retirer de l&amp;#39;argent sur mon compte

    Scénario:
        Etant donné que je suis un utilisateur connecté
        Et que j&amp;#39;ai un compte bancaire
        Et que le solde de mon compte est de &amp;quot;10&amp;quot; euros
        Quand j&amp;#39;ajoute &amp;quot;5&amp;quot; euros sur mon compte
        Alors mon solde doit être de &amp;quot;15&amp;quot; euros&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Lorsque l’on écrit nos tests Behat, nous commençons par décrire la fonctionnalité
de manière générale puis on décrit un ensemble de cas d’utilisation. Ces derniers
vont alors définir un contexte d’exécution, une série d’événements et terminer par
le résultat attendu.&lt;/p&gt;

&lt;p&gt;Les scénarios Behat peuvent ensuite être exécutés dans un ou plusieurs environnements.
Par exemple, dans le cas d’un site Web, il est tout à fait envisageable d’exécuter
le scénario dans différents navigateurs pour valider que le fonctionnement de
l’application est correct aussi bien sur Firefox que Chrome ou Internet Explorer.&lt;/p&gt;

&lt;p&gt;Une fois les scénarios écrits, il faudra alors que le développeur fasse le lien
entre les étapes des différents scénarios et les actions qui en résultent dans
l’application. Heureusement, Behat nous aide dans cette tâche en prégénérant le
squelette du code correspondant.&lt;/p&gt;

&lt;p&gt;Avec ce que nous venons dire, ne trouvez-vous pas que Behat est un outil extrêmement
intéressant ? Il nous permet :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;De valider notre compréhension du besoin client&lt;/li&gt;
  &lt;li&gt;De valider que notre application réponde correctement à ce dernier&lt;/li&gt;
  &lt;li&gt;De documenter notre application et fournit un esemble de spécifications&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Alors pourquoi un développeur peut ne pas aimer Behat ?&lt;/p&gt;

&lt;p&gt;Comme l’explique son auteur. Avec Behat, un développeur ne teste pas son application,
mais il s’assure que son application réponde aux besoins métiers de son client.
Beaucoup d’applications répondent aux attentes des développeurs, mais peuvent ne
pas couvrir les besoins métiers.&lt;/p&gt;

&lt;p&gt;Toujours d’après son auteur, Behat permet en réalité d’apprendre au développeur
comment fonctionne le métier du client.&lt;/p&gt;

&lt;p&gt;Je pense que si beaucoup de développeurs n’aiment pas Behat c’est à cause de ce dernier
point. Ecrire des bons scénarios Behat est un travail délicat. Il faut exprimer le métier
du client et non pas le fonctionnement de l’application.&lt;/p&gt;

&lt;p&gt;Un développeur aura facilement tendance à écrire ce genre de scénario :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-gherkin&quot; data-lang=&quot;gherkin&quot;&gt;# language: en
Scenario: Showing delivery cost for a product on the basket page
  Given there is a product:
    | name  | White Marker |
    | price | £5           |
  And I am on the &amp;quot;/catalogue&amp;quot; page
  When I click &amp;quot;Buy&amp;quot; in the &amp;quot;White Marker&amp;quot; product block
  And I go to the &amp;quot;/basket&amp;quot; page
  Then I should see a list with 1 product
  And the overall price should be shown as £9&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Alors que le scénario exprimé du point de vue du client devrait ressembler à :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-gherkin&quot; data-lang=&quot;gherkin&quot;&gt;# language: en
Scenario: Getting the delivery cost for a single product under £10
  Given a product named &amp;quot;White Marker&amp;quot; and priced £5 was added to the catalogue
  When I add the &amp;quot;White Marker&amp;quot; product from the catalogue to the picked up basket
  Then the overall basket price should be £9&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Mais adopter ce point de vue n’est pas évident pour un développeur, qui a souvent
plus une vision technique que fonctionnelle de l’application. Cela demande donc un
véritable effort de réflexion, en plus de devoir écrire le code correspondant aux
différents scénarios.&lt;/p&gt;

&lt;p&gt;Je reste donc persuader que si de nombreux développeurs n’aiment pas Behat, c’est
essentiellement à cause de leur culture technique et qu’il est difficile pour eux
de faire un volte-face pour se mettre à la place du client.&lt;/p&gt;

&lt;p&gt;Il se peut aussi que comme j’avais tenté de l’expliquer dans un précédent billet,
que les développeurs vont tenter de se lancer directement dans l’écrire de leurs
scénarios Behat, sans prendre le temps de
&lt;a href=&quot;/blog/2015/01/23/comprenez-les-outils-que-vous-utilisez.html&quot;&gt;comprendre les outils qu’ils utilisent&lt;/a&gt;
et la manière de le configurer correctement.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ressources :&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://speakerdeck.com/everzet/behat-by-example&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Behat by example&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://stakeholderwhisperer.com/posts/2014/10/introducing-modelling-by-example&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Introducing Modelling by Example&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
                    <pubDate>Mon, 11 Apr 2016 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2016/04/11/pourquoi-les-developpeurs-n-aiment-pas-behat.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2016/04/11/pourquoi-les-developpeurs-n-aiment-pas-behat.html</guid>
                </item>
            
        
            
                179
                <item>
                    <title>Mon environnement PhpStorm</title>
                    <description>&lt;p&gt;Comme de nombreux développeurs PHP, j’utilise au quotidien l’excellent IDE développé
par Jetbrains, j’ai nommé PhpStorm. Au-delà des nombreuses fonctionnalités fournies
nativement par l’IDE, il est possible d’ajouter des modules complémentaires au
travers des nombreux plugins disponibles.&lt;/p&gt;

&lt;p&gt;Dans ce billet, je vais présenter les principaux modules que j’utilise.&lt;/p&gt;

&lt;p&gt;##&lt;a href=&quot;https://plugins.jetbrains.com/plugin/7320?pr=phpStorm&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;PHP Annotation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;PHP Annotation est un module qui comme son nom l’indique très bien permet de gérer
les annotations dans l’IDE. C’est notamment grâce à cette extension qu’il sera possible
de naviguer dans les annotations, d’obtenir de l’autocomplétion lors de l’implémentation
de ces dernières, etc.&lt;/p&gt;

&lt;p&gt;##&lt;a href=&quot;https://plugins.jetbrains.com/plugin/7219&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Symfony&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Je développe essentiellement en utilisant le framework &lt;a href=&quot;http://symfony.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Symfony&lt;/a&gt;.
Cette extension permet ainsi d’obtenir un support complet du framework dans l’IDE.&lt;/p&gt;

&lt;p&gt;##&lt;a href=&quot;https://plugins.jetbrains.com/plugin/8173?pr=phpStorm&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Atoum&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Il m’est impossible de développer sans écrire de tests (et j’espère que vous aussi).
Pour écrire mes tests unitaires, j’utilise &lt;a href=&quot;http://atoum.org&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Atoum&lt;/a&gt;
aussi personnellement que professionnellement. Ce plugin créé tout récemment permet
de faciliter la navigation dans mes tests, mais également de lancer simplement ces
derniers directement dans l’IDE.&lt;/p&gt;

&lt;p&gt;##&lt;a href=&quot;https://plugins.jetbrains.com/plugin/7631?pr=phpStorm&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;PHP composer.json&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Il est aujourd’hui devenu impossible de travailler en PHP sans utiliser
&lt;a href=&quot;http://getcomposer.org&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Composer&lt;/a&gt;, le gestionnaire de
dépendance pour le langage. Cette extension, permet d’avoir l’autocomplétion lors
de l’édition du fichier de définition des dépendances, mais permet également de consulter
les versions des dépendances installées.&lt;/p&gt;

&lt;p&gt;##&lt;a href=&quot;https://plugins.jetbrains.com/plugin/7622?pr=phpStorm&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;PHP Inspection EA&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;PHP Inspection EA est l’un des derniers modules que j’ai découvert, mais il commence
à devenir indispensable. Effectivement, ce dernier permet d’effectuer des analyses
de codes statiques en complément de celles effectuées nativement par l’IDE. Extrêmement
pratique pour améliorer son code !&lt;/p&gt;
</description>
                    <pubDate>Wed, 17 Feb 2016 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2016/02/17/environement-phpstorm.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2016/02/17/environement-phpstorm.html</guid>
                </item>
            
        
            
                180
                <item>
                    <title>Mon retour du DDD Day 2016 à Lyon</title>
                    <description>&lt;p&gt;Le &lt;a href=&quot;https://medium.com/@pockystar/ddd-day-1dab25711fb0&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;DDD-Day&lt;/a&gt;
est une journée de conférence visant à sensibiliser les développeurs PHP au DDD
(le Domain-Driven Design), démystifier son utilisation et les patterns qui y sont
rattachés. J’ai ainsi eu l’occasion d’assister à la première édition de cette
journée qui s’est déroulée le 30 janvier 2016 à Lyon.&lt;/p&gt;

&lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;fr&quot;&gt;&lt;p lang=&quot;fr&quot; dir=&quot;ltr&quot;&gt;Le &lt;a href=&quot;https://twitter.com/hashtag/dddday?src=hash&quot;&gt;#dddday&lt;/a&gt; c&amp;#39;est maintenant ! &lt;a href=&quot;https://t.co/VJOesVWTFi&quot;&gt;pic.twitter.com/VJOesVWTFi&lt;/a&gt;&lt;/p&gt;&amp;mdash; Jérémy DECOOL (@jdecool) &lt;a href=&quot;https://twitter.com/jdecool/status/693354842346131458&quot;&gt;30 Janvier 2016&lt;/a&gt;&lt;/blockquote&gt;

&lt;p&gt;Avant de faire un retour rapide sur la journée, je tenais à remercier une nouvelle
fois tous ceux grâce à qui cette journée n’aurait pu avoir lieu : l’AFUP, KnpLabs,
l’atelier des médias, Amabla et Vanoix.&lt;/p&gt;

&lt;p&gt;La journée de conférence a été filmée et sera prochainement disponible sur la chaîne
Youtube de &lt;a href=&quot;https://www.youtube.com/channel/UCGOJGYuCAGziCtqS5s6WLHQ&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;OpenTalk&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;pourquoi-le-ddd-ne-devrait-rien-changer-à-votre-vie-&quot;&gt;Pourquoi le DDD ne devrait rien changer à votre vie ?&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://twitter.com/pockystar&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Alexandre Balmes&lt;/a&gt; - &lt;a href=&quot;https://speakerdeck.com/pocky/pourquoi-le-ddd-ne-devrait-rien-changer-a-votre-vie&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Slides&lt;/a&gt; - &lt;a href=&quot;https://www.youtube.com/watch?v=nU-ez8DIko4&amp;amp;list=PLGlYeGauEwnvAsDzJr9DlLYP09UIWoW2I&amp;amp;index=1&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Vidéo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cette journée commence par une présentation du Domain-Driven Design, pourquoi et
quand l’utiliser. Derrière cette présentation Alexandre nous explique les bases du
DDD et tente de nous montrer que malgré quelques termes “barbares”, il n’y a rien
de sorcier derrière cet acronyme. Ce n’est au fond que du bon sens et peut s’intégrer
dans tout environnement projet, surtout avec les outils qui sont à notre disposition
en PHP aujourd’hui.&lt;/p&gt;

&lt;h2 id=&quot;get-off-my-domain-&quot;&gt;Get Off My Domain !&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://twitter.com/matthieunapoli&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Matthieu NAPOLI&lt;/a&gt; - &lt;a href=&quot;javascript:;&quot; class=&quot;broken-link&quot; rel=&quot;nofollow&quot; data-original-url=&quot;https://mnapoli.fr/presentations/ddd-day &quot;&gt;Slides&lt;/a&gt; - &lt;a href=&quot;https://www.youtube.com/watch?v=Yj_2hUE-Lio&amp;amp;list=PLGlYeGauEwnvAsDzJr9DlLYP09UIWoW2I&amp;amp;index=2&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Vidéo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Après l’introduction faite par Alexandre, Matthieu rentre un peu plus dans les
détails techniques d’une application DDD. Il va alors nous présenter les
design-patterns essentiels qui doivent être utilisés dans nos applications. C’était
l’occasion de revoir les notions de : &lt;code&gt;Entity&lt;/code&gt;, &lt;code&gt;Value Object&lt;/code&gt;, &lt;code&gt;Domain Service&lt;/code&gt;,
&lt;code&gt;Repository&lt;/code&gt; (au sens premier du pattern), &lt;code&gt;Aggregate&lt;/code&gt; et de &lt;code&gt;Domain Event&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;ne-laissez-pas-les-formulaires-symfony-influencer-votre-modèle&quot;&gt;Ne laissez pas les formulaires Symfony influencer votre modèle&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://twitter.com/jeremyb_&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Jérémy BARTHE&lt;/a&gt; - &lt;a href=&quot;http://jeremybarthe.com/slides/2016-domain-driven-design-day/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Slides&lt;/a&gt; - &lt;a href=&quot;https://www.youtube.com/watch?v=Svndnw8n_SY&amp;amp;list=PLGlYeGauEwnvAsDzJr9DlLYP09UIWoW2I&amp;amp;index=3&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Vidéo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;En France, nous sommes très nombreux à utiliser le framework PHP Symfony. D’ailleurs
l’ensemble des personnes présentes développaient leurs applications en utilisant
ce dernier. Partant de ce constat, Jérémy nous a fait une présentation technique
sur l’utilisation des formulaires en Symfony. L’objectif de sa présentation était
de nous permettre de garder nos formulaires les plus simples possible tout en
gardant en tête les fondamentaux du DDD : l’utilisation des &lt;code&gt;Value Object&lt;/code&gt; et de
l’&lt;code&gt;Ubiquitous Language&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;soyez-spécifiques&quot;&gt;Soyez spécifiques&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://twitter.com/KPhoen/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Kévin GOMEZ&lt;/a&gt; - &lt;a href=&quot;http://blog.kevingomez.fr/slides-ddd-day-2016/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Slides&lt;/a&gt; - &lt;a href=&quot;https://www.youtube.com/watch?v=mKtsMsBFNsc&amp;amp;list=PLGlYeGauEwnvAsDzJr9DlLYP09UIWoW2I&amp;amp;index=4&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Vidéo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Kévin nous a fait une présentation sur l’expression des règles métiers dans nos
applications et la manière de les rendre réutilisables. Pour réussir cette tâche,
il nous présente le pattern &lt;code&gt;Specification&lt;/code&gt; et une implémentation de ce pattern
qu’il a écrit dans sa librairie &lt;a href=&quot;https://github.com/K-Phoen/rulerz&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;RulerZ&lt;/a&gt;.
Avec &lt;strong&gt;RulerZ&lt;/strong&gt;, il est alors possible via un DSL (Domain Specific Language), une
règle métier qui peut ensuite être passée de manière complètement transparente à un
&lt;code&gt;QueryBuilder&lt;/code&gt; de Doctrine ou une &lt;code&gt;Query&lt;/code&gt; ElasticSearch.&lt;/p&gt;

&lt;h2 id=&quot;cqrs--quand-les-représentations-ne-sont-pas-symétriques&quot;&gt;CQRS : Quand les Représentations ne sont pas symétriques&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://twitter.com/rgousi&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Guillaume ROSSIGNOL&lt;/a&gt; - &lt;a href=&quot;https://www.youtube.com/watch?v=MTqoADImjTM&amp;amp;list=PLGlYeGauEwnvAsDzJr9DlLYP09UIWoW2I&amp;amp;index=5&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Vidéo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;CQRS (Command Query Responsibility Segregation) est un pattern applicatif qui
repose sur un principe simple : la séparation, au sein d’une application, des
composants de traitement de lecture et d’écriture. Cette présentation nous a fait
un retour d’expérience sur la “migration” d’une application legacy vers une
architecture orientée DDD et CQRS.&lt;/p&gt;

&lt;p&gt;J’ai trouvé ce retour d’expérience intéressant vis-à-vis du choix que l’équipe a
effectué. Effectivement, l’essentiel des problèmes rencontrés se trouvant côté “front”,
l’équipe a fait le choix de garder l’application legacy en fonctionnement (correspondant
à la logique d’écriture) et de démarrer une nouvelle application qui ne sert qu’à
présenter les informations enregistrées par le back-office (la partie legacy).&lt;/p&gt;

&lt;h2 id=&quot;tour-de-table&quot;&gt;Tour de table&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://twitter.com/pascal_martin&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Pascal MARTIN&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pour terminer cette journée, nous avons eu le droit à une table ronde animée par
Pascal qui a permis à chaque membre de répondre aux questions restées en suspens
lors des présentations.&lt;/p&gt;

&lt;p&gt;Ce fut également un moment d’échange pour permettre aux organisateurs d’avoir le
ressenti de chacun sur cette première édition du DDD Day.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Encore une fois merci à toutes les personnes qui ont rendu cette journée possible :
les membres de l’organisation, les conférenciers ainsi qu’à tous les participants
à cette journée. Ce fut une journée riche en information, mais cela fait également
plaisir de revoir les personnes qui font bouger l’écosystème PHP à Lyon.&lt;/p&gt;

&lt;p&gt;Merci à tous !&lt;/p&gt;

&lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;fr&quot;&gt;&lt;p lang=&quot;fr&quot; dir=&quot;ltr&quot;&gt;Très bonne journée au &lt;a href=&quot;https://twitter.com/hashtag/dddday?src=hash&quot;&gt;#dddday&lt;/a&gt; ! Merci à tous &lt;a href=&quot;https://twitter.com/pockystar&quot;&gt;@pockystar&lt;/a&gt; &lt;a href=&quot;https://twitter.com/vanoix&quot;&gt;@vanoix&lt;/a&gt; &lt;a href=&quot;https://twitter.com/AFUP_lyon&quot;&gt;@AFUP_lyon&lt;/a&gt; pour l&amp;#39;orga et à tous les speakers.&lt;/p&gt;&amp;mdash; Jérémy DECOOL (@jdecool) &lt;a href=&quot;https://twitter.com/jdecool/status/693485524842078210&quot;&gt;30 Janvier 2016&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; src=&quot;//platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;

</description>
                    <pubDate>Mon, 08 Feb 2016 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2016/02/08/mon-retour-du-ddd-day-2016-a-lyon.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2016/02/08/mon-retour-du-ddd-day-2016-a-lyon.html</guid>
                </item>
            
        
            
                181
                <item>
                    <title>Définition d&apos;un Value Object (objet-valeur)</title>
                    <description>&lt;p&gt;J’ai déjà parlé plusieurs fois des Value-Object (ou Objet-Valeur en français),
que ce soit pour tenter de &lt;a href=&quot;/blog/2015/06/23/quand-utiliser-le-pattern-value-object.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;les définir&lt;/a&gt;
ou pour donner un &lt;a href=&quot;/blog/2015/03/25/les-objets-valeurs-ou-value-object.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;cas d’utilisation&lt;/a&gt;,
mais sans jamais en donner une définition simple.&lt;/p&gt;

&lt;p&gt;J’ai eu l’occasion d’assister au &lt;a href=&quot;https://medium.com/@pockystar/ddd-day-1dab25711fb0&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;DDD Day à Lyon&lt;/a&gt;
qui a eu lieu le 30 janvier dernier. Il s’agissait d’une série de conférences avec
pour objectif de démystifier l’utilisation du DDD en PHP.&lt;/p&gt;

&lt;p&gt;Lors de cette journée, &lt;a href=&quot;http://mnapoli.fr&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Matthieu NAPOLI&lt;/a&gt;
a donné une définition des Value-Object que j’ai beaucoup appréciée car elle est à
la fois simple et permet de bien comprendre ce que représente ce concept :&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;On peut définir un Value-Object comme étant un “wrapper” d’un  type primitif ou
complexe, auquel on va pouvoir ajouter des règles métiers.&lt;/p&gt;
&lt;/blockquote&gt;
</description>
                    <pubDate>Mon, 01 Feb 2016 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2016/02/01/definition-d-un-value-object.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2016/02/01/definition-d-un-value-object.html</guid>
                </item>
            
        
            
                182
                <item>
                    <title>Comment extraire un répertoire Git dans un autre dépôt ?</title>
                    <description>&lt;p&gt;Lorsque l’on développe, nous sommes amenés à créer divers composants techniques
et/ou fonctionnels afin de découper notre architecture applicative. Parfois, ces
composants peuvent être utiles dans d’autres bases de code et il est alors
intéressant de les extraire afin de pouvoir les réutiliser plus facilement.&lt;/p&gt;

&lt;p&gt;Il est alors possible de créer un nouveau dépôt et de copier le ou les dossiers
contenant le code du composant en question. Néanmoins, cette méthode simple
présente un énorme inconvénient : le journal de commit. Effectivement, en
effectuant une simple copie des fichiers, nous perdons l’historique des commits
Git et donc l’historique du composant.&lt;/p&gt;

&lt;p&gt;Heureusement Git possède tous les outils nous permettant de réaliser cette
opération d’extraction tout en conservant notre historique de travail avec la
commande &lt;code&gt;filter-branch&lt;/code&gt;.&lt;/p&gt;

&lt;quote&gt;Note: pour les utilisateurs faisant ces opérations sous Windows, il est
important de noter qu&apos;il faut utiliser le séparateur *nix (&apos;/&apos;) pour les dossiers.
Sans quoi les commandes ne seront pas exécutées avec succès.&lt;/quote&gt;

&lt;p&gt;La commande &lt;code&gt;filter-branch&lt;/code&gt; permet de réécrire l’historique Git en appliquant des
filtres personnalisés. Les filtres peuvent permettre la réécrire des commits,
des branches (en incluant les informations de merge), …&lt;/p&gt;

&lt;p&gt;Pour extraire notre composant, nous allons commencer par cloner le dépôt dans le
répertoire qui contiendra notre composant :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;git clone –no-hardlinks /path/to/project /path/to/component&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Puis nous allons extraire les fichiers du dossier qui nous intéresse :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;cd /path/to/component
git filter-branch --prune-empty --subdirectory-filter src/path/component&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Et voilà, notre nouveau dépôt ne contient que les fichiers que nous souhaitions
extraire. Il ne reste plus qu’à supprimer définir le nouveau remote de notre
composant :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;git remote remove origin
git remote add origin url/to/new/remote&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

</description>
                    <pubDate>Mon, 11 Jan 2016 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2016/01/11/comment-extraire-un-repertoire-git-dans-un-autre-depot.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2016/01/11/comment-extraire-un-repertoire-git-dans-un-autre-depot.html</guid>
                </item>
            
        
            
                183
                <item>
                    <title>Le problème n+1</title>
                    <description>&lt;p&gt;Si vous êtes développeur et que vous travaillez régulièrement avec une base de
données, vous avez très certainement déjà été confronté à des problèmes de
performance liés à des relations de type parent/enfant. L’anti-pattern que l’on
retrouve le plus fréquemment consiste à exécuter une requête pour obtenir la
relation parente puis à récupérer les enfants un à un. C’est un cas qui se produit
souvent lorsque l’on travaille avec des ORM. On parle alors du problème N+1
(“N+1 problem” en anglais).&lt;/p&gt;

&lt;p&gt;Prenons un exemple concret : une base de données permettant de répertorier des
auteurs de livres ainsi que les livres écrits par ces derniers. Nous souhaitons
récupérer la liste des auteurs avec les livres qu’ils ont écrits. Le code permettant
d’afficher ce résultat pourrait ressembler à cela :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;// ...
$authors = $pdo-&amp;gt;query(&amp;#39;SELECT * FROM author&amp;#39;)-&amp;gt;fetch();
foreach ($author as $authors) {
    $books = $pdo-&amp;gt;query(&amp;#39;SELECT * FROM book WHERE author_id = &amp;#39;.$author[&amp;#39;id&amp;#39;]);
    // ...
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Avec le code ci-dessous, on constate que les performances de l’application vont
se dégrader de manière exponentielle au fur et à mesure que l’on va renseigner des
auteurs et ajouter des livres. Dans le cas présent, l’on récupère 10 auteurs, 11
requêtes vont être exécutées pour récupérer l’ensemble des informations voulues
(1 pour récupérer les 10 auteurs, puis 10 pour récupérer les livres de chacun des
auteurs).&lt;/p&gt;

&lt;p&gt;Cela vous paraît aberrant ? Vous ne pensez ne jamais écrire un code comme cela ?
Prenons le même exemple en utilisant un ORM (Doctrine par exemple).&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;class Author
{
    /**
     * @Id
     * @Column(name=&amp;quot;id&amp;quot;, type=&amp;quot;int&amp;quot;)
     */
    private $id;

    /**
     * @OneToMany(targetEntity=&amp;quot;Book&amp;quot;, mappedBy=&amp;quot;author&amp;quot;)
     */
    private $books;

    // ...
}

class Book
{
    /**
     * @Id
     * @Column(name=&amp;quot;id&amp;quot;, type=&amp;quot;int&amp;quot;)
     */
    private $id;

    /**
     * @ManyToOne(targetEntity=&amp;quot;Author&amp;quot;)
     */
    private $author;

    // ...
}


$authors = $entityManager-&amp;gt;getRepository(&amp;#39;Author&amp;#39;)-&amp;gt;findAll();
foreach ($author as $authors) {
    $books = $author-&amp;gt;getBooks();
    // ...
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Vous pensez peut-être que cette solution est plus performante. Pourtant le code
ci-dessus se comporte exactement de la même façon que le précédent. Par défaut
Doctrine (comme de nombreux ORM) génère des classes Proxy afin de ne récupérer
les relations enfant que lorsqu’elles sont demandées. Dans ce cas, Doctrine va
exécuter une requête à chaque appel de la méthode &lt;code&gt;getBooks&lt;/code&gt; pour récupérer les
informations correspondantes.&lt;/p&gt;

&lt;p&gt;La solution pour éviter cela est bien évidemment d’utiliser des jointures SQL afin
de récupérer les informations des auteurs avec les livres qu’ils ont écrit dans
la même requête. La modification du premier exemple conduirait à ce code :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;// ...
$authors = $pdo-&amp;gt;query(&amp;#39;SELECT * FROM author JOIN book ON author.id = book.author_id&amp;#39;)-&amp;gt;fetch();&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Les ORM permettent également d’écrire des requêtes en utilisant les jointures afin
de récupérer l’ensemble des données voulu. On peut également configurer le mapping
des relations pour effectuer une récupération agressive des données (mais cela n’est
pas forcément recommandé car il se peut que la récupération des données de la relation
ne soit pas toujours utile) :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;// utilisation du QueryBuilder de Doctrine pour récupérer l&amp;#39;ensemble des données en une requête
$authors = $entityManager-&amp;gt;createQueryBuilder()
    -&amp;gt;select([&amp;#39;a&amp;#39;, &amp;#39;b&amp;#39;])
    -&amp;gt;from(&amp;#39;Author&amp;#39;, &amp;#39;a&amp;#39;)
    -&amp;gt;join(&amp;#39;Book&amp;#39;, &amp;#39;b&amp;#39;)
    -&amp;gt;getQuery()
    -&amp;gt;getResult()
;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

</description>
                    <pubDate>Thu, 17 Dec 2015 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2015/12/17/le-probleme-n+1.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2015/12/17/le-probleme-n+1.html</guid>
                </item>
            
        
            
                184
                <item>
                    <title>Qu&apos;est-ce que l&apos;Event Sourcing ?</title>
                    <description>&lt;p&gt;L’&lt;i&gt;Event Sourcing&lt;/i&gt;, voilà un terme que l’on entend de plus en plus souvent.
Décrit, il y a maintenant une dizaine d’années, ce concept commence maintenant à
arriver dans nos architectures PHP. Mais, de quoi s’agit-il ? Derrière
l’&lt;i&gt;Event Sourcing&lt;/i&gt; se cache un mécanisme simple qui consiste à sauvegarder
chaque événement déclenché dans une application.&lt;/p&gt;

&lt;p&gt;Lorsque l’on accède et navigue au sein d’une application, en réalité nous accédons
à un état donné de cette dernière. En effectuant certaines actions, nous pouvons
par exemple modifier l’état du programme (en modifier les données qui sont gérées
par ce dernier). La plupart du temps, les actions effectuées par l’utilisateur
vont directement altérer les informations contenues dans la base de données. De
ce fait, la base de données est une copie du dernier état de notre application.&lt;/p&gt;

&lt;p&gt;Avec l’&lt;i&gt;Event Sourcing&lt;/i&gt;, nous allons stocker chacun des événements qui vont
déclencher la modification des données contenues en bases. L’objectif d’un tel
mécanisme est de s’assurer que tous les changements d’états sont stockés comme une
séquence d’événements. Il devient alors possible d’interroger n’importe quel événement
qui s’est produit et d’en déduit l’état de l’application à n’importe quel moment
de sa vie. Cela peut être considéré comme un journal d’événements. C’est d’ailleurs
en parcourant ce dernier, que l’on reconstruit l’état actuel de l’application
(pour nos utilisateurs).&lt;/p&gt;

&lt;p&gt;Avec ce type de méthodologie, il est donc primordial que les événements soient des
éléments immuables et qui ne peuvent donc être ni modifiés, ni supprimés.&lt;/p&gt;

&lt;p&gt;Maintenant, vous devez vous demander qui peut bien utiliser une telle architecture ?
La réponse la plus évidente est sans doute les systèmes de gestion de sources et
du versionnement. Mais cette méthode de développement ne s’arrête pas à ces seuls
outils. Des applications Web sont également conçues selon cette méthodologie.
C’est notamment le cas de certaines applications internes développées chez ERDF ou
encore chez BlaBlaCar.&lt;/p&gt;

&lt;p&gt;Si vous souhaitez en savoir plus, je ne peux que vous conseillez de lire l’excellent
billet de &lt;a href=&quot;http://martinfowler.com/eaaDev/EventSourcing.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Martin Fowler&lt;/a&gt;
très complet sur le sujet.&lt;/p&gt;
</description>
                    <pubDate>Tue, 01 Dec 2015 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2015/12/01/qu-est-ce-que-l-event-sourcing.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2015/12/01/qu-est-ce-que-l-event-sourcing.html</guid>
                </item>
            
        
            
                185
                <item>
                    <title>Trait ou héritage, il faut choisir</title>
                    <description>&lt;p&gt;C’est avec la version 5.4.0 que les &lt;b&gt;Traits&lt;/b&gt; sont apparus en PHP. Il s’agit
d’un mécanisme de réutilisation de code, introduit dans le but de réduire certaines
limites de l’héritage simple. Je constate que de plus en plus de développeurs se
mettent à utiliser les &lt;b&gt;Traits&lt;/b&gt; à tel point que ces certains n’utilise même
plus l’héritage et préfère avoir recours à ce mécanisme. Que se soit avant de
faire un héritage ou avant d’utiliser un trait, il convient de se poser la question
de quand utiliser l’un ou l’autre ?&lt;/p&gt;

&lt;p&gt;Revenons rapidement sur ce qu’est un &lt;b&gt;héritage&lt;/b&gt;. L’héritage permet d’établir
une relation (de type parent/enfant) entre des classes. Lorsque l’on étend une
classe, la classe fille hérite alors de toutes les méthodes publiques et protégées
de la classe parente. Il est également possible que la classe fille redéfinisse
certaines de ces méthodes pour les adapter à ces spécificités.&lt;/p&gt;

&lt;p&gt;Les &lt;b&gt;traits&lt;/b&gt; quand à eux ont comme seul objectif de partager du code entre
classes sans structuration hiérarchique. Il n’y a donc ici pas de relation entre
les classes qui utilisent les traits. Il partage seulement un comportement.&lt;/p&gt;

&lt;p&gt;Il faut bien avoir cette distinction lorsque vous écrivez du code. Voici par
exemple, du code que j’ai pu voir récemment sur un projet :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;trait RepositoryTrait
{
    public function find($id)
    { /* ... */ }

    // ...
}

class UserRepository
{
    use RepositoryTrait;

    // ...
}

class GroupRepository
{
    use RepositoryTrait;

    // ...
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Il faut faire attention à l’utilisation des traits car ces derniers peuvent
facilement casser les aspects classiques de la programmation objet. Dans ce bout
de code, les classes &lt;code&gt;UserRepository&lt;/code&gt; et &lt;code&gt;GroupRepository&lt;/code&gt; n’implémente pas d’interface
commune, elle utilise un trait. De ce fait, le principe de programmation SOLID peut
facilement être rompu.&lt;/p&gt;

&lt;p&gt;Dans ce cas-là, un héritage semble plus adapté :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;abstract class Repository
{
    public function find($id)
    { /* ... */ }

    // ...
}

class UserRepository extends Repository
{
    use RepositoryTrait;

    // ...
}

class GroupRepository extends Repository
{
    use RepositoryTrait;

    // ...
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;De plus en utilisant un héritage, il est plus facile de faire du typage explicite.
Il est alors possible de vérifier que la classe fournit en paramètre est bien de
type &lt;code&gt;Repository&lt;/code&gt;, ce qui n’est pas possible avec un trait. De plus il sera plus
facile de respecter le &lt;a href=&quot;https://fr.wikipedia.org/wiki/Principe_de_substitution_de_Liskov&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;principe de substitution de Liskov&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Il est préférable d’utiliser les traits pour partager du code avec des classes
qui n’ont pas de lien entre elles. Prenons comme exemple :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;trait TextFormater
{
    public function sanitize($text)
    {
        return strip_tags($text);
    }
}

class Mailer
{
    use TextFormater;

    public function send($subject, $text)
    {
        mail(&amp;#39;user@mail.com&amp;#39;, $this-&amp;gt;sanitize($subject), $this-&amp;gt;sanitize($text));
    }
}

class ConsoleWriter
{
    use TextFormater;

    public function write($text)
    {
        echo $this-&amp;gt;sanitize($text), PHP_EOL;
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Dans l’exemple précédent, la classe &lt;code&gt;Mailer&lt;/code&gt; a pour but d’envoyer un mail à un
utilisateur et la classe &lt;code&gt;Writer&lt;/code&gt; de générer un affichage à l’écran. Ces deux
classes n’ont aucun rapport et pourtant elles ont toutes les deux besoins de
traiter du texte fourni en paramètre. L’héritage n’a ici pas sa place et il
convient donc de partager le code via l’utilisation d’un trait.&lt;/p&gt;

&lt;p&gt;Un autre exemple concret de l’utilisation des traits est l’implémentation du
pattern &lt;a href=&quot;https://fr.wikipedia.org/wiki/Singleton_(patron_de_conception)&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Singleton&lt;/a&gt;.
Lorsque l’on souhaite utiliser ce dernier, le premier réflexe pourrait être de
créer une classe &lt;code&gt;Singleton&lt;/code&gt; et de faire hériter nos objets de cette dernière.
Mais ce n’est pas toujours possible à cause de l’impossibilité de faire de
l’héritage multiple. Partant du principe qu’il est préférable de favoriser un
héritage “fonctionnel”, il convient dans ce cas-là, d’avoir recours aux traits pour
résoudre le problème :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;trait Singleton
{
    protected static $instance = null;

    public static function getInstance()
    {
        if (null === $this-&amp;gt;instance) {
            $this-&amp;gt;instance = new self;
        }

        return $this-&amp;gt;instance;
    }
}

class Example extends Article
{
    use Singleton;
}

$expl = Example::getInstance();&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;b&gt;Héritage&lt;/b&gt; et &lt;b&gt;Trait&lt;/b&gt;, les deux concepts ont leur place dans nos projets,
utilisons-les à bon escient et au bon moment.&lt;/p&gt;
</description>
                    <pubDate>Sat, 28 Nov 2015 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2015/11/28/trait-ou-heritage-il-faut-choisir.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2015/11/28/trait-ou-heritage-il-faut-choisir.html</guid>
                </item>
            
        
            
                186
                <item>
                    <title>Support complet de Gitlab dans Composer</title>
                    <description>&lt;p&gt;Inutile de vous présenter Composer l’outil de gestion des dépendances massivement
utilisé par les développeurs PHP. Voici un court billet pour vous annoncer que ce
dernier intègre désormais le support complet de Gitlab.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/composer/composer/issues/3241&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;L’issue Github&lt;/a&gt;
datée du 26 août 2014 et demandée le support de l’API Gitlab pour la gestion des
dépendances (avec notamment la gestion des droits).&lt;/p&gt;

&lt;p&gt;C’est désormais chose faite, un an et demi après, Composer fournit maintenant
&lt;a href=&quot;https://github.com/composer/composer/pull/3765&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;un driver Gitlab&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Pour l’utiliser dans votre projet, il vous suffira de configurer votre fichier
&lt;code&gt;composer.json&lt;/code&gt; avec les éléments suivants :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;{
    &amp;quot;repositories&amp;quot;: [
        { &amp;quot;type&amp;quot;: &amp;quot;gitlab&amp;quot;, &amp;quot;url&amp;quot;: &amp;quot;http://gitlab.mysrv.com/path/to/my_project&amp;quot; }
    ],
    &amp;quot;config&amp;quot;: {
        &amp;quot;gitlab-domains&amp;quot;: [&amp;quot;gitlab.mysrv.com&amp;quot;]
    },
    &amp;quot;require&amp;quot;: {
        &amp;quot;vendor/my-project&amp;quot;: &amp;quot;~1.0&amp;quot;
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

</description>
                    <pubDate>Wed, 25 Nov 2015 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2015/11/25/support-complet-de-gitlab-dans-composer.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2015/11/25/support-complet-de-gitlab-dans-composer.html</guid>
                </item>
            
        
            
                187
                <item>
                    <title>Les patterns &quot;Builder&quot; et &quot;Factory&quot;: même combat</title>
                    <description>&lt;p&gt;Aujourd’hui, j’ai envie de vous parler des patterns “Builder” et “Factory”. S’il
y en a un qui est bien connu des développeurs, on ne fait pas toujours la distinction
entre ces deux patterns qui ont un objectif similaire: la &lt;b&gt;création de nouveaux objets&lt;/b&gt;.&lt;/p&gt;

&lt;p&gt;La &lt;b&gt;factory&lt;/b&gt; (ou &lt;i&gt;fabrique&lt;/i&gt; en français) est un patron de conception qui
permet de créer un objet sans avoir à connaître la classe exacte de l’objet retourné.
Ce dernier est alors construit en une seule étape. Exemple :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;class DatabaseConnectionFactory
{
    private $dbParams;

    public function __construct(array $params)
    {
        $this-&amp;gt;dbParams = $params;
    }

    public function create($type)
    {
        switch ($type) {
            case &amp;#39;mysql&amp;#39;:
                return new MysqlDatabaseConnection($this-&amp;gt;dbParams);

            case &amp;#39;pgsql&amp;#39;:
                $conn = new PgsqlDatabaseConnection();
                $conn-&amp;gt;setParams($this-&amp;gt;dbParams);

                return $conn;

            default:
                throw new \InvalidArgumentException();
        }
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Le pattern &lt;b&gt;builder&lt;/b&gt; quand à lui se focalise sur la construction d’objets plus
complexes qui ne peuvent être créés en une seule étape. Il est parfois nécessaire que
la création d’un objet nécessite plusieurs étapes. C’est alors que les objets de
type &lt;b&gt;builder&lt;/b&gt; sont utilisés. Exemple :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;class WebserviceClientBuilder
{
    private $serviceUrl;

    public function __construct($url)
    {
        $this-&amp;gt;serviceUrl = $url;
    }

    public function build()
    {
        $clientStrategyFactory = new clientStrategyFactory();
        $client = $clientStrategyFactory-&amp;gt;create($this-&amp;gt;serviceUrl);

        return new WebserviceClient($client);
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;L’exemple ci-dessus est très simple, mais on voit bien que la création d’un client
pour utiliser le web service nécessite la création de deux objets : le premier permettant
de définir la stratégie à adopter pour accéder à ce dernier et le second est le client
à proprement parlé.&lt;/p&gt;

&lt;p&gt;Il existe également d’autres patrons de conception permettant de créer des objets,
mais ces derniers étant moins utilisé (donc certains que je n’ai jamais pratiqués),
je n’en parlerai pas. Je vous invite notamment à découvrir les patterns
&lt;a href=&quot;https://en.wikipedia.org/wiki/Object_pool_pattern&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Object pool&lt;/a&gt;
et &lt;a href=&quot;https://en.wikipedia.org/wiki/Prototype_pattern&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Prototype&lt;/a&gt;.&lt;/p&gt;
</description>
                    <pubDate>Mon, 16 Nov 2015 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2015/11/16/les-patterns-builder-et-factory-meme-combat.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2015/11/16/les-patterns-builder-et-factory-meme-combat.html</guid>
                </item>
            
        
            
                188
                <item>
                    <title>Les commandes Git (trop) souvent ignorées</title>
                    <description>&lt;p&gt;S’il y a bien un outil de gestion de sources qui a gagné la bataille, on ne peut
citer que Git. Cet outil pratique et extrêmement puissant a été largement adopté
par les développeurs. À tel point que c’est devenu l’outil de référence pour la
gestion des versions décentralisées. Nous allons voir dans cet article quelques fonctionnalités
bien pratiques mais qui sont bien trop souvent ignorées par les développeurs.&lt;/p&gt;

&lt;h2 id=&quot;git-cherry-pick&quot;&gt;git cherry-pick&lt;/h2&gt;

&lt;p&gt;La commande &lt;code&gt;cherry-pick&lt;/code&gt; permet d’appliquer des changements effectués au sein d’un
autre commit. Lorsque l’on travaille, il est fréquent d’utiliser des branches
pour le développement des différentes fonctionnalités à implémenter. Il peut parfois
être utile de récupérer une partie du travail d’un autre développeur sans devoir
récupérer l’ensemble de sa branche.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;git cherry-pick d42c389f&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;git-bisect&quot;&gt;git bisect&lt;/h2&gt;

&lt;p&gt;Le git &lt;code&gt;bisect&lt;/code&gt; est certainement l’une des commandes que j’affectionne le plus.
Pourtant je ne l’ai presque jamais vu utilisé dans mes différentes expériences
professionnelles.&lt;/p&gt;

&lt;p&gt;Cette commande permet pourtant de faciliter grandement la correction d’un bug de
votre application en se chargeant de trouver le commit ayant introduit ce dernier.
Il s’agit d’une commande redoutable surtout lorsqu’elle est couplée avec un outil
de tests automatisés (c’est pratique mais pas obligatoire).&lt;/p&gt;

&lt;p&gt;Son &lt;a href=&quot;http://git-scm.com/docs/git-bisect&quot; target=&quot;_target&quot;&gt;utilisation&lt;/a&gt;
peut sembler complexe au premier abord, c’est peut-être ce qui freine son
utilisation, mais pourtant une fois familiarisé avec cette dernière, vous ne pourrez
plus vous en passer.&lt;/p&gt;

&lt;h2 id=&quot;git-rebase&quot;&gt;git rebase&lt;/h2&gt;

&lt;p&gt;Je pense que tout le monde connaît la commande &lt;code&gt;rebase&lt;/code&gt;, mais on ne sait jamais
réellement ce qu’il y a derrière. Nous utilisons en général le &lt;code&gt;merge&lt;/code&gt; beaucoup
plus que nous ne devrions. Certainement parce que ces deux commandes permettent
d’intégrer les modifications d’une branche dans une autre. J’ai moi-même mis beaucoup
de temps pour bien comprendre la différence.&lt;/p&gt;

&lt;p&gt;De manière générale, je vois le rebasage utilisé pour fusionner des commits au sein
d’une même branche. Le développeur a utilisé le commit pour sauvegarder son travail
à un instant T et souhaite une fois terminé, fusionner et regrouper tous les commits
correspondant à la même tâche.&lt;/p&gt;

&lt;p&gt;Le fonctionnement du &lt;code&gt;rebase&lt;/code&gt; est simple. Cette commande  permet de rejouer une
série de commits à partir d’une nouvelle base de travail.&lt;/p&gt;

&lt;p&gt;Des collègues ont rencontré le problème très récemment. Une fonctionnalité avait
été développée sur une branche qui a ensuite été laissée de côté pendant un certain
laps de temps. La branche principale ayant elle évolué pendant ce temps, lorsqu’ils
ont ensuite tenté de fusionner cette branche dans le master, ils ont constaté
que certaines portions de code avaient été écrasées.&lt;/p&gt;

&lt;p&gt;Ce fonctionnement peut sembler déroutant pourtant il est tout à fait logique. La branche
où avait été développée la fonctionnalité avait pour point de départ une version obsolète
de la branche principale. Lorsque Git a effectué sa fusion, il a réintégré les modifications
en tenant compte des changements les plus récents.&lt;/p&gt;

&lt;p&gt;Ce problème aurait pu être évité, si un rebase avait été fait avant le merge. Cette
opération ayant pour effet de remettre le point de départ de la branche à une version
récente et à jour du master.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;git checkout ma-branche-ancienne
git rebase master&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

</description>
                    <pubDate>Mon, 26 Oct 2015 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2015/10/26/les-commandes-git-souvent-ignorees-par-les-developpeurs.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2015/10/26/les-commandes-git-souvent-ignorees-par-les-developpeurs.html</guid>
                </item>
            
        
            
                189
                <item>
                    <title>Faire de la veille techno</title>
                    <description>&lt;p&gt;La veille technologique est très importante dans notre métier qui est en constante
évolution. Thibault JOUANNIC a d’ailleurs eu l’occasion de donner une conférence à
ce sujet. Conférence dont il a fait une retranscription dans
&lt;a href=&quot;http://www.miximum.fr/veille-techno-vieux-croutons-paris-web-2015.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;un article de blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Le billet en résultant est très intéressant, et si vous ne l’avez pas encore fait,
je vous recommande de le lire.&lt;/p&gt;

&lt;p&gt;Néanmoins dans son article, il y a un passage qui m’a interpelé :&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;“Je pense que la manière la plus efficace de se maintenir à jour, ce n’est pas de
tester une techno directement, par exemple sur un projet perso, c’est d’aller
chercher des retours d’expérience.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Bien que l’article ne s’attache pas à cette unique pratique, je pense que même si
l’on a peu de temps, il est important de ne pas uniquement se limiter à cela. Ne
serait-ce parce que si tout le monde fonctionne de la sorte, il n’y aurait
alors plus personne pour tester ou mettre en place de nouvelles techniques
au sein des projets actuels.&lt;/p&gt;

&lt;p&gt;De plus, il faut bien faire attention à ces retours d’expérience, les nouvelles
technos sont souvent portées par de grosses sociétés. Mais tout le monde n’est pas
Google ou Facebook, et 90% des sociétés ne font pas face aux mêmes contraintes ou
problématiques.&lt;/p&gt;

&lt;p&gt;Lorsque l’on fait de la veille techno, il faut tester ce qui nous intéresse, regarder
les retours d’expérience et mettre tout cela en parallèle avec notre environnement et
les contraintes auxquelles nous sommes liés tous les jours. Il est aussi important
de se faire sa propre idée et ne pas forcément tout le temps suivre les autres.&lt;/p&gt;
</description>
                    <pubDate>Wed, 21 Oct 2015 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2015/10/21/faire-de-la-veille-techno.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2015/10/21/faire-de-la-veille-techno.html</guid>
                </item>
            
        
            
                190
                <item>
                    <title>Gérer du multithread en PHP avec pthreads</title>
                    <description>&lt;p&gt;PHP est par “défaut” un langage mono-thread et ne permet donc, par définition, de
ne gérer qu’un seul processus à la fois. Or il peut s’avérer extrêmement avantageux
de gérer plusieurs processus afin d’effectuer un certain nombre de tâches en parallèle.
Il existe heureusement une extension PECL pour faire cela :
&lt;a href=&quot;http://pecl.php.net/package/threads&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;pthreads&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;« pthreads est une API orientée objet qui permet le multi-threading en PHP. Il
inclut tous les outils nécessaires pour créer des applications multi-threadées
pour le Web ou pour la console. Les applications PHP peuvent créer, lire, écrire,
exécuter et synchroniser des Threads, des Workers, et des objets Threaded. »&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Tout cela de manière très simple. La seule contrainte est que l’extension se base
sur les threads POSIX et nécessitera donc l’installation du projet
&lt;a href=&quot;https://sourceware.org/pthreads-win32/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;pthreads-win32&lt;/a&gt;
pour pouvoir fonctionner sous Windows.&lt;/p&gt;

&lt;p&gt;Voici un exemple très simple de l’utilisation de l’extension :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&amp;lt;?php
$thread = new class extends Thread {
    public function run() {
        echo &amp;quot;Hello World\n&amp;quot;;
    }
};

$thread-&amp;gt;start() &amp;amp;&amp;amp; $thread-&amp;gt;join();
?&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;J’ai par ailleurs publié une image Docker permettant d’exécuter PHP 7.0 préconfiguré
avec l’extension pthreads. Tout cela est disponible sur le
&lt;a href=&quot;https://hub.docker.com/r/jdecool/php-pthreads/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Docker Hub&lt;a&gt;&lt;/a&gt;.&lt;/a&gt;&lt;/p&gt;
</description>
                    <pubDate>Mon, 12 Oct 2015 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2015/10/12/gerer-du-multithread-en-php-avec-pthreads.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2015/10/12/gerer-du-multithread-en-php-avec-pthreads.html</guid>
                </item>
            
        
            
                191
                <item>
                    <title>Exporter un fichier CSV avec Symfony2</title>
                    <description>&lt;p&gt;Lorsque l’on travaille sur des applications de gestion, il est fréquent de devoir
exporter des fichiers de données dans divers formats. Le plus simple des formats
étant le CSV.&lt;/p&gt;

&lt;p&gt;La plupart du temps, l’export passe par la génération d’un fichier temporaire qui
est ensuite envoyé à l’utilisateur au travers d’un téléchargement de fichier via
le navigateur. Pourtant Symfony2 fournit des outils permettant d’éviter de manipuler
directement les fichiers temporaires via les &lt;code&gt;StreamedResponse&lt;/code&gt; et les flux PHP.&lt;/p&gt;

&lt;p&gt;La classe &lt;a href=&quot;http://api.symfony.com/master/Symfony/Component/HttpFoundation/StreamedResponse.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;StreamedResponse&lt;/code&gt;&lt;/a&gt;
permet de retourner un flux de réponse au client. Le contenu de la réponse est
représenté par une fonction PHP au lieu d’une chaine de caractère :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;public function generateCsvAction() {
    $repository = $this-&amp;gt;get(&amp;#39;app.repository.user&amp;#39;);

    $response = new StreamedResponse();
    $response-&amp;gt;setCallback(function() use ($repository) {
        $handle = fopen(&amp;#39;php://output&amp;#39;, &amp;#39;w+&amp;#39;);

        fputcsv($handle, [&amp;#39;Firstname&amp;#39;, &amp;#39;Lastname&amp;#39;, &amp;#39;Birthday&amp;#39;], &amp;#39;;&amp;#39;);

        $results = $repository-&amp;gt;findActiveUsers();
        foreach ($results as $user) {
            fputcsv(
                $handle,
                [$user-&amp;gt;getFirstname(), $user-&amp;gt;getLastname(), $user-&amp;gt;getBirthday()],
                &amp;#39;;&amp;#39;
             );
        }

        fclose($handle);
    });

    $response-&amp;gt;setStatusCode(200);
    $response-&amp;gt;headers-&amp;gt;set(&amp;#39;Content-Type&amp;#39;, &amp;#39;text/csv; charset=utf-8&amp;#39;);
    $response-&amp;gt;headers-&amp;gt;set(&amp;#39;Content-Disposition&amp;#39;,&amp;#39;attachment; filename=&amp;quot;export-users.csv&amp;quot;&amp;#39;);

    return $response;
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

</description>
                    <pubDate>Fri, 09 Oct 2015 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2015/10/09/exporter-un-fichier-csv-avec-symfony2.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2015/10/09/exporter-un-fichier-csv-avec-symfony2.html</guid>
                </item>
            
        
            
                192
                <item>
                    <title>Objets-Valeurs et immutabilité</title>
                    <description>&lt;p&gt;J’ai déjà parlé plusieurs fois des &lt;a href=&quot;/blog/2015/03/25/les-objets-valeurs-ou-value-object.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Objets-Valeurs (Value Objects)&lt;/a&gt;
et &lt;a href=&quot;/blog/2015/06/23/quand-utiliser-le-pattern-value-object.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;quand les utiliser&lt;/a&gt;,
mais je n’ai encore jamais évoqué le concept d’immutabilité qui en est indissociable.&lt;/p&gt;

&lt;p&gt;Un objet immutable possède un état qui ne peut pas changer au cours du temps. Pourquoi
est-il aussi important qu’un objet-valeurs ne puisse changer ? Les objets-valeurs ont pour
objectif de représenter une donnée à un instant T. Ce qui a de la valeur pour un tel
objet est son état. De ce fait, si l’état de notre objet change, il ne peut être
représenté par une même instance (contrairement à un objet de type &lt;code&gt;Entité&lt;/code&gt;) puisque
c’est l’état de notre objet qui détermine son identité.&lt;/p&gt;

&lt;p&gt;Prenons un exemple concret. Si dans un modèle objet, nous souhaitons représenter
une couleur par un Objet-Valeurs, il est logique que ce dernier soit immutable.
L’état caractèristique de l’objet et la couleur qui lui est associé, si cette
dernière change, elle ne peut être associé à la même instance et il devra s’agir
d’un nouvel objet.&lt;/p&gt;

&lt;p&gt;Cela veut également dire que s’il est possible d’effectuer des opérations sur notre
Objet-Valeurs, le résultat provoquera la création d’un nouvel Objet-Valeurs. Reprenons
l’exemple de notre objet &lt;code&gt;Money&lt;/code&gt; :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;class Money
{
  /** @var int */
  private $amount;

  /** @var string */
  private $currency;

  /**
   * @param int    $amount
   * @param string $currency
   */
  public function __construct($amount, $currency)
  {
    $this-&amp;gt;amount   = $amount;
    $this-&amp;gt;currency = $currency;
  }

  /**
   * @return int
   */
  public function getAmount()
  {
    return $this-&amp;gt;amount;
  }

  /**
   * @return float
   */
  public function getFormatedAmount()
  {
    return round($this-&amp;gt;amount / 100, 2);
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Nous souhaitons ajouter la possibilité d’additionner deux objets de types &lt;code&gt;Money&lt;/code&gt;.
Pour cela nous ajoutons le code suivant :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;class Money
{
  // ...

  /**
   * @param Money $money
   * @return static
   */
  public function add(Money $money)
  {
    // check same currency

    $value = $this-&amp;gt;amount + $money-&amp;gt;getAmoun();

    return new static($value, $this-&amp;gt;currency);
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Le code ci-dessus permet d’ajouter deux objets &lt;code&gt;Money&lt;/code&gt; et l’objet qui en résulte
est un nouvel objet-valeurs. Il est ainsi possible d’ajouter des comportements
à ces derniers tout en conservant le principe d’immutabilité.&lt;/p&gt;
</description>
                    <pubDate>Tue, 21 Jul 2015 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2015/07/21/objets-valeurs-et-immutabilite.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2015/07/21/objets-valeurs-et-immutabilite.html</guid>
                </item>
            
        
            
                193
                <item>
                    <title>Ecrire des logs manipulables</title>
                    <description>&lt;p&gt;La notion de logs (ou journaux en français) est primordiale en développement. Ce
sont eux qui permettent de faire remonter les informations générées par l’application
une application. Ces derniers peuvent se présenter sous différentes formes : fichiers,
base de données, sortie écran… Mais comment est-il possible de traiter ces différents
retours facilement ?&lt;/p&gt;

&lt;p&gt;Les données recueillies par les fichiers de logs informent des diverses situations
rencontrées lors de l’exécution du code. Cela peut être des cas inattendus (exceptions),
des erreurs, des informations sur les actions effectuées par les utilisateurs. Ils
permettent aux développeurs de retracer l’exécution d’un bout de code. Les fichiers
alors générés peuvent contenir un grand nombre d’informations et les analyser peut
s’avérer être une tâche longue et complexe.&lt;/p&gt;

&lt;p&gt;Bien entendu les informations écrites dans les fichiers de logs sont dépendantes
de la phase de vie du projet. Il est ainsi fort probable que pendant la phase
de développement, une grande quantité d’informations soient enregistrées afin d’avoir
un maximum de détails. Par contre, une fois en production, le niveau de log sera
réduit à son minimum afin d’avoir les informations essentielles.&lt;/p&gt;

&lt;p&gt;De ce fait, une analyse automatique des logs s’avère très intéressante et peut
révéler de nombreuses informations. Mais comment écrire des logs qui puissent
être facilement traités et analysés par une machine ?&lt;/p&gt;

&lt;p&gt;Pour cela, je vais m’appuyer sur les &lt;a href=&quot;http://www.php-fig.org&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;PSR&lt;/a&gt;
(Propose a Standards Recommendation) de PHP et plus particulièrement la
&lt;a href=&quot;http://www.php-fig.org/psr/psr-3/&quot;&gt;PSR-3&lt;/a&gt; qui concerne les bibliothèques
de journalisation. Si vous ne connaissez pas les PSR, il s’agit de recommandations
validées par le &lt;a href=&quot;http://www.php-fig.org&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;PHP FIG&lt;/a&gt;
(PHP Framework Interoperability Group) ayant pour objectif d’améliorer l’interopérabilité
entre les frameworks PHP.&lt;/p&gt;

&lt;p&gt;Pour résumer rapidement, dans PSR-3, on retrouve 8 niveaux de log avec autant de
méthodes associées. Par exemple, le niveau &lt;code&gt;DEBUG&lt;/code&gt; est associé à la méthode
&lt;code&gt;public function debug($message, array $context = array())&lt;/code&gt;. On retrouve également
une fonction générique &lt;code&gt;public function log($level, $message, array $context = array())&lt;/code&gt;;&lt;/p&gt;

&lt;p&gt;Ce que l’on peut remarquer avec les signatures de ces fonctions, c’est qu’elle ne
se limite pas à écrire un message dans un fichier. Elles permettent également de renseigner
un contexte d’exécution. Il est donc intéressant lorsque l’on écrit une ligne de log,
de fournir des données sur l’état de l’application au moment donné. Par exemple :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;$logger-&amp;gt;info(&amp;#39;User deletion&amp;#39;, [
  &amp;#39;userLogged&amp;#39;  =&amp;gt; $userLogged-&amp;gt;getId(),
  &amp;#39;userDeleted&amp;#39; =&amp;gt; $userDeleted-&amp;gt;getId(),
]);&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Le code ci-dessus génère la ligne suivante :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;[2015-06-28 16:11:12] myapp.INFO: User deletion {&amp;quot;userLogged&amp;quot;:1234,&amp;quot;userDeleted&amp;quot;:7412} []&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Cet exemple n’est pas très intéressant, je me limite à fournir les identifiants des
utilisateurs concernés par l’action de suppression d’un utilisateur. De plus, si
l’utilisateur est physiquement supprimé de la base de données, le fait d’avoir son
identifiant est inutile. Il aurait été plus logique de donner les objets à la fonction
de log qui les aurait sérialisés pour l’écrire du fichier.&lt;/p&gt;

&lt;p&gt;Il est intéressant de noter que la ligne de log générée automatiquement analysée
car elle est créée selon un schéma bien défini. Elle peut ainsi être découpée en 5 :&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;La date d’enregistrement : &lt;code&gt;2015-06-28 16:11:12&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Le niveau de log : &lt;code&gt;myapp.INFO&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Le message de log : &lt;code&gt;User deletion&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Le contexte d’exécution : &lt;code&gt;{&quot;userLogged&quot;:1234,&quot;userDeleted&quot;:7412}&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Des données additionnels (dont je ne parle pas dans ce billet) : &lt;code&gt;[]&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Il est donc très facile d’analyser les fichiers de manière automatique. L’analyse
peut permettre d’obtenir des informations sur les erreurs rencontrées le plus souvent
par l’application, mais également de faire des statistiques sur certaines utilisations
de l’application. Ce travail d’analyse peut être réduit à son minimum car ce genre de
logs peut être envoyé et analysé par des plateformes de type ELK (Elasticsearch
Logstash Kibana).&lt;/p&gt;

&lt;p&gt;Notons au passage que cette ligne de log aurait pu être plus difficilement analysable
si elle avait été écrite de la manière suivante :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;$logger-&amp;gt;info(sprintf(&amp;#39;User &amp;quot;%d&amp;quot; delete user &amp;quot;%d&amp;quot;&amp;#39;,
  $userLogged-&amp;gt;getId(),
  $userDeleted-&amp;gt;getId()
);
// =&amp;gt; [2015-06-28 16:11:12] myapp.INFO: User &amp;quot;1234&amp;quot; delete user &amp;quot;7412&amp;quot; []&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Le problème avec cette méthode est que le message de log n’est pas “unique” pour
une action donnée. Effectivement, l’action ayant déclenché l’écriture de la ligne
est mélangée avec son contexte d’exécution.&lt;/p&gt;

&lt;p&gt;Si vous souhaitez en savoir plus sur l’utilisation des fichiers de logs, Grégoire
PINEAU, développeur chez SensioLabs a fait une excellente présentation lors du
&lt;a href=&quot;https://speakerdeck.com/lyrixx/symfony-live-2015-paris-monitorer-sa-prod&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;SFLive 2015&lt;/a&gt;
sur le sujet.&lt;/p&gt;
</description>
                    <pubDate>Wed, 15 Jul 2015 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2015/07/15/ecrire-des-logs-manipulables.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2015/07/15/ecrire-des-logs-manipulables.html</guid>
                </item>
            
        
            
                194
                <item>
                    <title>Lancer des applications desktop avec Docker</title>
                    <description>&lt;p&gt;C’est un fait, l’utilisation de Docker s’est démocratisée dans notre milieu. Et
même si nous n’en sommes qu’au début, il est possible de faire tellement de choses
avec les conteneurs. Bien que dans un premier temps, ils étaient principalement
utilisés pour faciliter la gestion de la diversité des environnements de développement,
nombreux sont ceux qui commencent à l’utiliser pour gérer des déploiements d’applications
en production.&lt;/p&gt;

&lt;p&gt;On commence également à voir de plus en plus de personnes utiliser Docker pour lancer
des applications desktop. Je ne vais pas ici vous expliquer comment le faire, on
trouve sur le net de nombreux articles qui vous expliquent cela (notamment celui de
&lt;a href=&quot;https://blog.jessfraz.com/post/docker-containers-on-the-desktop/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Jessie FRAZELLE&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Le principe est simple car il suffit de partager l’environnement X11 de l’hôte avec
son conteneur. Cela se réalise en 2 étapes : le partage du dossier de travail de X11
et l’utilisation d’une variable d’environnement indiquant comment accéder au gestionnaire.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;docker run [...] -e DISPLAY=unix$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Par contre ce que l’on ne dit pas dans les articles, c’est que certains systèmes sont
configurés pour empêcher des connexions externes sur votre environnement graphique.
Il n’est pas rare alors de se retrouver avec une erreur &lt;code&gt;Unable to initialize GTK+&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Il sera alors nécessaire d’autoriser la connexion d’un système externe via la commande :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;xhost +  # pour autoriser toutes les connexions externes
xhost +local:`docker inspect --format=&amp;#39;{{ .Config.Hostname }}&amp;#39; $containerId` # pour restreindre à l&amp;#39;utilisation d&amp;#39;un conteneur&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Une fois cette configuration effectuée, vous pourrez lancer sans problème votre
application graphique dans un conteneur.&lt;/p&gt;
</description>
                    <pubDate>Mon, 13 Jul 2015 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2015/07/13/lancer-des-applications-desktop-dans-des-containers-docker.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2015/07/13/lancer-des-applications-desktop-dans-des-containers-docker.html</guid>
                </item>
            
        
            
                195
                <item>
                    <title>Quand utiliser le pattern Value Object ?</title>
                    <description>&lt;p&gt;Il y a quelques mois, je parlais du
&lt;a href=&quot;/blog/2015/03/25/les-objets-valeurs-ou-value-object.html&quot;&gt;pattern Objets-Valeur&lt;/a&gt;
(ou Value Object en anglais). Dans le billet présentant brièvement ce patron de
conception, j’évoquais le fait qu’il est souvent méconnu des développeurs PHP. De
ce fait les exemples sont assez rares et il est alors difficile de savoir quand
et/ou comment l’utiliser.&lt;/p&gt;

&lt;p&gt;Lorsque l’on évoque ce pattern, le principal exemple qui est cité est celui de
l’objet &lt;code&gt;Money&lt;/code&gt; permettant de gérer la notion d’argent (valeur + devise). Mais il
existe une multitude de projet ne faisait pas appel à cette notion et de ce fait
le concept reste abstrait.&lt;/p&gt;

&lt;p&gt;Il peut être pourtant relativement simple de détecter à quel moment nous pouvons
et/ou devons utiliser un Objet-Valeurs. La principale structure de données utilisées
par les développeurs PHP est le tableau associatif. Dans bien de cas, ce dernier
peut être remplacé par un objet de type Objet-Valeurs.&lt;/p&gt;

&lt;p&gt;Par exemple, prenons le code suivant qui parse un fichier XML :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;public function parseLine($data)
{
    // ... code permettant de traiter les données

    return [
        &amp;#39;name&amp;#39;        =&amp;gt; trim($columns[0]-&amp;gt;textContent),
        &amp;#39;frequency&amp;#39;   =&amp;gt; trim($columns[1]-&amp;gt;textContent),
        &amp;#39;location&amp;#39;    =&amp;gt; trim($columns[2]-&amp;gt;textContent),
        &amp;#39;transmitter&amp;#39; =&amp;gt; trim($columns[3]-&amp;gt;textContent),
    ];
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Dans cet exemple, la fonction renvoie un tableau associatif contenant les informations
d’une donnée lue dans un fichier XML. On pourrait dans ce cas utiliser un Value-Objet.
Cela aurait de multiples avantages :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Avoir un objet où il serait possible d’avoir les données caractéristiques de
l’élément (les propriétés) ainsi qu’un ensemble de comportement que nous pouvons
y appliquer (les méthodes).&lt;/li&gt;
  &lt;li&gt;Cela permet de rendre le code plus lisible et compréhensible par les autres
développeurs. Pas besoin d’analyser le contenu de la variable puisque cette dernière
est décrite au travers d’un objet.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On pourrait ainsi réécrire le code sous la forme suivante :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;public function parseLine($data)
{
    // ... code permettant de traiter les données

    return new RadioFrequency(
        trim($columns[0]-&amp;gt;textContent),
        trim($columns[1]-&amp;gt;textContent),
        trim($columns[2]-&amp;gt;textContent),
        trim($columns[3]-&amp;gt;textContent)
    );
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Notons que si nous souhaitons faire les choses correctement, il serait nécessaire
de déléguer la création de notre objet &lt;code&gt;RadioFrequency&lt;/code&gt; à une fabrique (Factory).
Bien que cela peut sembler compléxifier notre code en rajoutant une classe
supplémentaire, c’est en réalité tout le contraire. Au travers de cette modification,
nous respectons le principe de responsabilité unique.&lt;/p&gt;

&lt;p&gt;Ainsi, le code final serait :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;public function __construct($radioFrequencyFactory)
{
    $this-&amp;gt;radioFrequencyFactory = $radioFrequencyFactory;
}

public function parseLine($data)
{
    // ... code permettant de traiter les données

    return $this-&amp;gt;radioFrequencyFactory-&amp;gt;createFromDOMNode($columns);
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Au final, n’est-il pas plus agréable de manipuler un objet clairement identifié et
ayant un comportement défini plutôt qu’un tableau où il est difficile de connaître son
contenu et les actions qui peuvent y être appliquées sans effectuer une inspection
xDebug ou sans dumper ce dernier ?&lt;/p&gt;
</description>
                    <pubDate>Tue, 23 Jun 2015 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2015/06/23/quand-utiliser-le-pattern-value-object.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2015/06/23/quand-utiliser-le-pattern-value-object.html</guid>
                </item>
            
        
            
                196
                <item>
                    <title>La conception émergente</title>
                    <description>&lt;p&gt;Ceux qui me connaissent le savent bien, je suis un fervent défenseur des principes
et des méthodes agiles. Même si aujourd’hui ces dernières se sont énormément
démocratisées, les mettre en oeuvre au sein de projets n’est pas toujours une chose
aisée (les habitudes ont la vie dure).&lt;/p&gt;

&lt;p&gt;Ce billet, j’aurais mis un an pour l’écrire. Avec du recul ce n’est pas forcément
une mauvaise chose car je vais vous raconter l’histoire d’un projet. Un projet qui,
bien que développé en utilisant la méthode SCRUM, n’a pas fait l’objet d’une conception
agile (que je qualifie également de conception émergente).&lt;/p&gt;

&lt;p&gt;Utiliser une méthode agile est un bon début, mais ce n’est pas suffisant. Pour que
cela fonctionne, il est nécessaire (d’après moi) que le processus de développement
soit également fait de manière agile. Pour maximiser les chances de réussite,
il est important de considérer les principes KISS (Keep It Simple and Stupid:
ne compliquez pas les choses inutilement) et YAGNI (You ain’t gonna need it: vous
n’en aurez pas besoin). C’est en respectant ces deux règles et en utilisant de manière
efficace un certain nombre de Design Pattern qu’une conception émergente apparaît.&lt;/p&gt;

&lt;p&gt;Contrairement à une “conception classique” où l’essentiel de l’architecture est
réalisée en début de projet, la conception émergente a pour objectif de décrire
la conception au fur et à mesure de l’avancement du projet. C’est-à-dire que
l’architecture du produit est conçue au fil du développement des évolutions
demandées par le métier. Ce mode de fonctionnement colle très bien à des projets
agiles où les spécifications sont réalisées et priorisées tout au long de la vie
du produit.&lt;/p&gt;

&lt;p&gt;La notion de conception n’est pas remise en cause, le travail s’effectue différemment.
Cela a alors un impact direct sur l’équipe. Il est important que chaque membre
doit être capable d’identifier les éléments de conception redondants et de proposer des
simplifications du code existant. L’équipe doit également être capable de différer
une décision de conception liée à une exigence future et d’identifier le moment
pertinent pour introduire une décision structurante.&lt;/p&gt;

&lt;p&gt;Pour le projet dont je parlais au début de ce billet, l’équipe (composée d’un
product owner, un scrum master et de 3 développeurs) a décidé (à mon grand regret)
de partir sur une approche “traditionnelle”. De ce fait, une conception entière du
système a été réalisée (le travail de développement était estimé à environ 6 mois/homme).
Les sprints ont ainsi démarré par la réalisation d’un MCD complet répondant à
l’ensemble des fonctionnalités qui devaient être fournies par l’outil. De cette
conception a découlé la création de toutes les entités du modèle. Par la suite,
les différents éléments du cahier des charges ont été découpés en users stories
qui étaient réalisées de manière itérative lors de chaque sprint selon les priorités
définies par le product owner.&lt;/p&gt;

&lt;p&gt;Il est important pour la suite, de préciser que le projet en question n’est pas un
projet “prioritaire” pour l’entreprise. Il est possible de le qualifier de “side-project”
car il n’a pas d’impact business direct. Les membres de l’équipe sont donc affectés
prioritairement sur d’autres projets (en respectant un ratio de 80/20). C’est pour
cette raison, que le product owner a décidé de s’attacher à la réalisation de
fonctionnalités techniques avancées afin de s’assurer de la faisabilité de ces
dernières. Cela a eu pour conséquence de s’attarder sur des éléments techniques
au détriment de la réalisation d’une application fonctionnelle.&lt;/p&gt;

&lt;p&gt;Comme on pouvait s’y attendre, les délais de réalisation de l’application ont commencé
à être rallongés. Le projet prenait du retard et a dû être mis en pause. Cela jusqu’à
aujourd’hui, environ 6 mois après son arrêt temporaire.&lt;/p&gt;

&lt;p&gt;Seulement les choix de conception qui ont été fait, rendent la reprise du travail
difficile. La base de données a été entièrement modélisée pour les fonctionnalités
annoncées il y plus d’un an maintenant. 80% des propriétés des entités ne sont pas
utilisées (et correspondent donc à des colonnes de base de données à &lt;code&gt;null&lt;/code&gt;). De plus,
le fait de s’être concentré sur des éléments principalement techniques, a pour conséquence
que de multiples fonctionnalités ont été commencées, mais qu’aucune n’est terminée.
Il n’est donc pas possible de saisir une information dans l’interface d’administration
et de la retranscrire correctement côté interface utilisateur. Et après 6 mois de pause,
aucun membre de l’équipe n’est capable de dire ce qui fonctionne ou non, ni même où
le travail s’était arrêté.&lt;/p&gt;

&lt;p&gt;Lorsque l’on démarre un projet, il est important d’être pragmatique et d’avancer
petit à petit. Il est également primordial de commencer un projet par un MVP (Minimum
Viable Product: un produit minimum viable). Ne vous concentrez pas sur des aspects
techniques qui n’ont que peu de valeur ajoutée. Développez votre produit rapidement
(sans bafouer la qualité), mais avec un minimum de fonctionnalités. Ces dernières
s’étofferont par la suite. Le plus important c’est que l’on utilise votre projet,
cela vous rassurera en vous confrontant à votre marché, et pour l’équipe qui est derrière
ce n’est pas un long tunnel sans fin.&lt;/p&gt;
</description>
                    <pubDate>Fri, 05 Jun 2015 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2015/06/05/la-conception-emergente.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2015/06/05/la-conception-emergente.html</guid>
                </item>
            
        
            
                197
                <item>
                    <title>Manipuler un champ Select2 avec Behat</title>
                    <description>&lt;p&gt;Si vous développez en PHP, vous devez très certainement connaître Behat, le framework
permettant de faire du développement piloté par le comportement (BDD, Behaviour Driven
Development en anglais). Personnellement, je suis toujours embêté lorsque je me
retrouve à écrire des scénarios devant manipuler une interface qui utilise le composant
&lt;a href=&quot;https://select2.github.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Select2&lt;/a&gt; car il n’est pas possible
de manipuler ce champ au travers de l’extension Mink.&lt;/p&gt;

&lt;p&gt;Pour cela, il est nécessaire de créer des contextes personnalisés qui ajouteront des actions
permettant de manipuler la liste déroulante. Etant donnée que je n’ai rien trouvé sur le Web,
je vous partage le code qui permet d’interagir avec un champ Select2 dans un scénarii Behat.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;/**
 * @When /^(?:|I )fill in select2 input &amp;quot;(?P&amp;lt;field&amp;gt;(?:[^&amp;quot;]|\\&amp;quot;)*)&amp;quot; with &amp;quot;(?P&amp;lt;value&amp;gt;(?:[^&amp;quot;]|\\&amp;quot;)*)&amp;quot; and select &amp;quot;(?P&amp;lt;entry&amp;gt;(?:[^&amp;quot;]|\\&amp;quot;)*)&amp;quot;$/
 */
public function fillInSelectInputWithAndSelect($field, $value, $entry)
{
    $page = $this-&amp;gt;getSession()-&amp;gt;getPage();

    $inputField = $page-&amp;gt;find(&amp;#39;css&amp;#39;, $field);
    if (!$inputField) {
        throw new \Exception(&amp;#39;No field found&amp;#39;);
    }

    $choice = $inputField-&amp;gt;getParent()-&amp;gt;find(&amp;#39;css&amp;#39;, &amp;#39;.select2-selection&amp;#39;);
    if (!$choice) {
        throw new \Exception(&amp;#39;No select2 choice found&amp;#39;);
    }
    $choice-&amp;gt;press();

    $select2Input = $page-&amp;gt;find(&amp;#39;css&amp;#39;, &amp;#39;.select2-search__field&amp;#39;);
    if (!$select2Input) {
        throw new \Exception(&amp;#39;No input found&amp;#39;);
    }
    $select2Input-&amp;gt;setValue($value);

    $this-&amp;gt;getSession()-&amp;gt;wait(1000);

    $chosenResults = $page-&amp;gt;findAll(&amp;#39;css&amp;#39;, &amp;#39;.select2-results li&amp;#39;);
    foreach ($chosenResults as $result) {
        if ($result-&amp;gt;getText() == $entry) {
            $result-&amp;gt;click();
            break;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Avec ma société, nous avons également décidé de publier une
&lt;a href=&quot;https://packagist.org/packages/novaway/common-contexts&quot; target=&quot;_target&quot;&gt;extension Behat&lt;/a&gt;
installable via Composer contenant un contexte permettant la manipulation complète d’un champ
Select2. Cette dernière est disponible sur
&lt;a href=&quot;https://github.com/novaway/BehatCommonContext&quot; target=&quot;_target&quot;&gt;Github&lt;/a&gt;.&lt;/p&gt;
</description>
                    <pubDate>Wed, 03 Jun 2015 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2015/06/03/manipuler-un-champ-select2-avec-behat.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2015/06/03/manipuler-un-champ-select2-avec-behat.html</guid>
                </item>
            
        
            
                198
                <item>
                    <title>Le pattern &quot;conteneur&quot; (Container)</title>
                    <description>&lt;p&gt;Lorsque l’on développe une application, le principal défi est de donner du sens
à son code afin qu’il soit facilement compréhensible et maintenable.
Une des nombreuses bonnes pratiques est alors d’utiliser des
&lt;a href=&quot;/blog/2015/03/25/les-objets-valeurs-ou-value-object.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;objets-valeurs&lt;/a&gt;
plutôt que de simples variables. Pour stocker et manipuler ces derniers, il est possible
d’utiliser un &lt;a href=&quot;https://en.wikipedia.org/wiki/Container_(abstract_data_type)&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;conteneur&lt;/a&gt;,
un patron de conception souvent utilisé en parallèle.&lt;/p&gt;

&lt;p&gt;En programmation objet, un conteneur n’est ni plus ni moins qu’un objet ayant pour
but de gérer une collection d’élément. C’est un pattern assez peu utilisé dans le
monde PHP car de nombreux développeurs ont pris l’habitude d’utiliser les tableaux
associatifs fournis par le langage.&lt;/p&gt;

&lt;p&gt;Il y a pourtant de multiples avantages à utiliser un conteneur dans vos applications.
On peut citer :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Éviter la duplication de code dans les classes&lt;/li&gt;
  &lt;li&gt;Respecter le principe de responsabilité unique (vos objets métiers n’ont pas à se soucier de comment parcourir
ou rechercher un élément dans un tableau, une liste ou une collection)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pour vous aider à manipuler des conteneurs dans votre code, PHP fournit quelques
interfaces qui peuvent être utiles lors de la mise en place de ces derniers :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://php.net/manual/fr/class.iterator.php&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Iterator&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://php.net/manual/fr/class.traversable.php&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Traversable&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://php.net/manual/fr/class.arrayaccess.php&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;ArrayAccess&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Je suis toujours étonné que ce pattern ne soit pas plus connu et utilisé des développeurs
PHP. Je rencontre souvent des développeurs qui me disent que la mise en place d’un tel
objet est inutile et n’apporte pas de valeur (si ce n’est de complexifier inutilement
le code). Sachez que vous manipulez ce genre de classe très souvent sans vous en rendre
compte. Lorsque vous utilisez l’ORM Doctrine par exemple, qui utilise un conteneur
&lt;code&gt;ArrayCollection&lt;/code&gt; pour la gestion des associations multiples des entités.&lt;/p&gt;
</description>
                    <pubDate>Wed, 22 Apr 2015 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2015/04/22/le-pattern-conteneur-container.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2015/04/22/le-pattern-conteneur-container.html</guid>
                </item>
            
        
            
                199
                <item>
                    <title>Reconnaître une violation du principe de responsabilité unique</title>
                    <description>&lt;p&gt;Dans un article précédent, j’ai parlé de &lt;a href=&quot;/blog/2015/04/06/programmation-evenement.html&quot; taregt=&quot;_blank&quot;&gt;programmation événementielle&lt;/a&gt;
et comment il était possible d’utiliser les événements Symfony afin d’écrire du
code qui respecte le principe de responsabilité unique. L’exemple était extrêmement
simple et il est souvent plus difficile d’identifier les différentes responsabilités
d’une classe lorsque celle-ci fait plusieurs centaines de lignes.&lt;/p&gt;

&lt;p&gt;Le nombre de lignes de code présent (et le nombre de variables d’instances présentes)
dans une classe peut être un signal d’alarme. Un autre indicateur est la présence
de méthodes privées qui ont pour but de déléguer des tâches précises. Bien souvent
ces méthodes peuvent être extraites dans une ou plusieurs classes indépendantes.&lt;/p&gt;

&lt;p&gt;Si vous souhaitez approfondir les principes d’un code SOLID et comment écrire des
classes qui soient facilement extensibles, réutilisables et maintenables, je ne peux
que vous conseiller l’excellent livre de Matthias NOBACK :
&lt;a href=&quot;https://leanpub.com/principles-of-package-design/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Principles of Package Design&lt;/a&gt;.
Certainement l’une des meilleures ressources sur le sujet et que tout développeur
devrait avoir lu.&lt;/p&gt;
</description>
                    <pubDate>Mon, 20 Apr 2015 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2015/04/20/reconnaitre-une-violation-du-principe-de-responsabilite-unique.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2015/04/20/reconnaitre-une-violation-du-principe-de-responsabilite-unique.html</guid>
                </item>
            
        
            
                200
                <item>
                    <title>Pas de logique dans les vues de vos applications MVC</title>
                    <description>&lt;p&gt;La semaine dernière je suis tombé sur un article de blog écrit par
&lt;a href=&quot;https://twitter.com/lkdjiin&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Xavier NAYRAC&lt;/a&gt;, intitulé
“&lt;a href=&quot;https://lkdjiin.github.io/blog/2015/03/28/pas-de-logique-dans-les-vues-rails/&quot; target=&quot;_target&quot;&gt;Pas de logique dans les vues Rails&lt;/a&gt;”.
Bien que l’article donne des exemples en Ruby, les propos du billet sont valables
pour tous les langages et toutes les applications de type MVC. L’article étant
pertinent, je relaie l’information.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://lkdjiin.github.io/blog/2015/03/28/pas-de-logique-dans-les-vues-rails/&quot; target=&quot;_target&quot;&gt;Source&lt;/a&gt;&lt;/p&gt;
</description>
                    <pubDate>Tue, 07 Apr 2015 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2015/04/07/pas-de-logique-dans-les-vues-de-vos-applications-mvc.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2015/04/07/pas-de-logique-dans-les-vues-de-vos-applications-mvc.html</guid>
                </item>
            
        
            
                201
                <item>
                    <title>Utiliser les événements Symfony2 pour un code SOLID</title>
                    <description>&lt;p&gt;En tant que développeur, nous essayons d’écrire du code simple, maintenable et
facilement extensible. Pour cela, nous tentons de mettre en oeuvre les principes
évoqués dans l’acronyme &lt;a href=&quot;https://fr.wikipedia.org/wiki/SOLID_%28informatique%29&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;SOLID&lt;/a&gt;.
Le “S” correspond à la notion de “Single Responsibility” (responsabilité unique).
Cela signifie que vos classes doivent avoir une et une seule responsabilité, une
seule raison d’exister.&lt;/p&gt;

&lt;p&gt;Prenons pour exemple, un service Symfony dont l’objectif est d’enregistrer les
données d’un utilisateur issu d’un formulaire. Le code pourrait alors ressembler
à quelque chose comme cela :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;// Controller/UserController.php
public function newAction(Request $request)
{
  $user = new User();

  $form = $this-&amp;gt;createForm(&amp;#39;user_form&amp;#39;, $user, [
    &amp;#39;action&amp;#39; =&amp;gt; $this-&amp;gt;generateUrl(&amp;#39;app_new_user&amp;#39;),
    &amp;#39;method&amp;#39; =&amp;gt; &amp;#39;POST&amp;#39;
  ]);
  $form-&amp;gt;add(&amp;#39;submit&amp;#39;, &amp;#39;submit&amp;#39;, [&amp;#39;label&amp;#39; =&amp;gt; &amp;#39;Create&amp;#39;]);

  $form-&amp;gt;handleRequest($request);
  if ($form-&amp;gt;isValid()) {
    $manager = $this-&amp;gt;get(&amp;#39;app.user_manager&amp;#39;);
    $manager-&amp;gt;save($user);

    return $this-&amp;gt;redirectToRoute(&amp;#39;user_show&amp;#39;, [&amp;#39;id&amp;#39; =&amp;gt; $user-&amp;gt;getId()]);
  }

  return [ &amp;#39;form&amp;#39; =&amp;gt; $form-&amp;gt;createView() ];
}

// Manager/UserManager.php
public function save(User $user)
{
  $this-&amp;gt;entityManager-&amp;gt;persist($user);
  $this-&amp;gt;entityManager-&amp;gt;flush();
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Ce code respecte bien le principe de responsabilité unique, chacune de nos
classes n’a qu’un seul objectif. Mais imaginons maintenant que nous souhaitons
envoyer un email à l’utilisateur que nous venons de créer (pour le notifier de la
création de son compte et lui envoyer ses informations de connexion par exemple).&lt;/p&gt;

&lt;p&gt;Rien de difficile, il suffit alors de modifier notre manager pour envoyer le mail
lors de la création du compte :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;// Manager/UserManager.php
public function save(User $user)
{
  $this-&amp;gt;entityManager-&amp;gt;persist($user);
  $this-&amp;gt;entityManager-&amp;gt;flush();

  $this-&amp;gt;emailManager-&amp;gt;sendNewAccountNotification($user);
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Sauf qu’en ajoutant cette ligne, nous venons de casser le principe de responsabilité
unique de notre service gérant les utilisateurs. Réfléchissez bien, si vous souhaitez
réutiliser la classe &lt;code&gt;UserManager&lt;/code&gt; dans une autre application, mais que cette dernière
ne souhaite pas envoyer de notification, comment allez-vous faire ?&lt;/p&gt;

&lt;p&gt;Il vous faudra alors supprimer tous les appels à notre &lt;code&gt;EmailManager&lt;/code&gt; ou mettre en
place des conditions permettant de ne pas exécuter cette fonction. Dans le cas
présent, l’exemple est très simple et cela peut être fait facilement. Mais imaginez
une application de plusieurs milliers de lignes. Cela est tout de suite plus compliqué.&lt;/p&gt;

&lt;p&gt;Heureusement, Symfony2 nous permet d’éviter ce couplage très simplement, au travers
de la gestion des événements. L’idée est très simple, une fois l’enregistrement
du nouvel utilisateur effectué, nous allons émettre un signal afin d’indiquer le
succès de la création. Ce signal pourra alors être capter par différentes classes afin
de déclencher différentes actions (un envoi de notification dans notre exemple).&lt;/p&gt;

&lt;p&gt;Commençons par modifier notre classe &lt;code&gt;UserManager&lt;/code&gt; :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;// Manager/UserManager.php
public function __construct(EventDispatcherInterface $dispatcher, ...)
{
  $this-&amp;gt;dispatcher = $dispatcher;
  // ...
}

public function save(User $user)
{
  $this-&amp;gt;entityManager-&amp;gt;persist($user);
  $this-&amp;gt;entityManager-&amp;gt;flush();

  $this-&amp;gt;dispatcher-&amp;gt;dispatch(&amp;#39;user.create&amp;#39;, new UserEvent($user));
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;La classe &lt;code&gt;UserEvent&lt;/code&gt; est tout simple un conteneur pour les informations traitées
par le formulaire (ici notre utilisateur créé).&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;use Symfony\Component\EventDispatcher\Event;

class UserEvent extends Event
{
  private $user;

  public function __construct(User $user)
  {
    $this-&amp;gt;user = $user;
  }

  public function getUser()
  {
      return $this-&amp;gt;user;
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Il ne reste plus qu’à intercepter le signal et à envoyer notre email :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&amp;lt;?php

use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class UserNotificationListener implements EventSubscriberInterface
{
  private $emailManager;

  public function __construct(EmailManagerInterface $emailManager)
  {
    $this-&amp;gt;emailManager = $emailManager;
  }

  public function onUserCreate(UserEvent $event)
  {
    $this-&amp;gt;emailManager-&amp;gt;sendNewAccountNotification($event-&amp;gt;getUser());
  }

  public static function getSubscribedEvents()
  {
    return [
      &amp;#39;user.create&amp;#39; =&amp;gt; &amp;#39;onUserCreate&amp;#39;,
    ];
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Sans oublier de déclarer le service qui va bien :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;// Resources/config/services.yml
services:
  listener.user_mailer_notification:
    class: Listener\UserNotificationListener
    arguments:
      - @app.manager.email
    tags:
      - { name: kernel.event_subscriber }&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Nous avons donc maintenant des classes qui ont bien une responsabilité unique. De
ce fait, le risque d’erreur est réduit. De plus cela, les rend plus facilement
testables. Il sera également plus facile de les réutiliser dans d’autres projets.&lt;/p&gt;

&lt;p&gt;Si vous souhaitez obtenir plus d’information concernant la gestion des événements
dans Symonfy2, je vous invite à lire &lt;a href=&quot;http://symfony.com/doc/current/components/event_dispatcher/introduction.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;la documentation&lt;/a&gt;
très bien rédigé à son sujet.&lt;/p&gt;
</description>
                    <pubDate>Mon, 06 Apr 2015 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2015/04/06/programmation-evenement.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2015/04/06/programmation-evenement.html</guid>
                </item>
            
        
            
                202
                <item>
                    <title>Intégration continue sur RaspberryPI avec PHPCI</title>
                    <description>&lt;p&gt;Je possède un RaspberryPi que j’utilise essentiellement en tant que serveur
personnel. J’y entrepose entre autres quelques petits dépôts Git que je souhaite
garder privée. J’y ai également quelques scripts qui me permettent d’automatiser
le déploiement de quelques applications (comme ce blog par exemple).&lt;/p&gt;

&lt;p&gt;Pour automatiser un certain nombre de tâches et pour éviter de le faire
manuellement, je souhaitais utiliser un outil de type intégration continue. Mon
premier choix c’est alors porté sur le bien connu Jenkins. Bien évidemment, vu la
puissance de la machine (que ce soit un RPi 1 ou 2), Jenkins est lent et ne permet
pas d’être utilisé de manière fluide.&lt;/p&gt;

&lt;p&gt;En recherchant sur le Web des solutions alternatives, je suis retombé sur
&lt;a href=&quot;https://www.phptesting.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;PHPCI&lt;/a&gt;, une plateforme
d’intégration continue spécialement conçue pour PHP et développé en PHP.&lt;/p&gt;

&lt;p&gt;PHPCI ne se limite pas aux projets PHP. Je l’utilise pour une variété de tâches,
notamment grâce à un plugin “Shell” qui me permet d’exécuter des commandes bash
sur mes projets.&lt;/p&gt;

&lt;p&gt;La solution est simple, légère et extrêmement fluide sur mon Raspberry2. Un vrai
bonheur ! Comme je le racontais sur Twitter, je regrette vraiment de ne pas m’y
être penché plus tôt.&lt;/p&gt;

&lt;blockquote class=&quot;twitter-tweet&quot; data-conversation=&quot;none&quot; lang=&quot;fr&quot;&gt;
  &lt;p&gt;&lt;a href=&quot;http://t.co/0HiuvDcMWl&quot;&gt;pic.twitter.com/0HiuvDcMWl&lt;/a&gt;&lt;/p&gt;&amp;mdash; Jérémy DECOOL (@jdecool)
  &lt;a href=&quot;https://twitter.com/jdecool/status/577145197626277890&quot;&gt;15 Mars 2015&lt;/a&gt;
&lt;/blockquote&gt;
&lt;script async=&quot;&quot; src=&quot;//platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;

&lt;p&gt;En plus, le code de l’outil est plutôt simple et pouvoir mettre les mains dans le
code est un vrai bonheur. De plus, l’équipe du produit (développé par une société
anglaise pour ces besoins) est très sympathique et réactive sur Github.&lt;/p&gt;

&lt;p&gt;Bien que cela ne fasse que quelques semaines que je l’utilise, l’outil me semble
efficace et me semble une réelle alternative à Jenkins. Le seul véritable
inconvénient, c’est le manque de plugin qui se fera sans doute sentir pour des
projets complexes.&lt;/p&gt;
</description>
                    <pubDate>Wed, 01 Apr 2015 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2015/04/01/integration-continue-sur-raspberrypi-avec-phpci.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2015/04/01/integration-continue-sur-raspberrypi-avec-phpci.html</guid>
                </item>
            
        
            
                203
                <item>
                    <title>La conception pilotée par le domaine</title>
                    <description>&lt;p&gt;Le DDD (Domain-Driven Design ou conception pilotée par le domaine en français),
voila maintenant quelques années que j’en entends parler. Le plus souvent en ventant
les mérites de cette approche. Pourtant je ne l’ai jamais pratiqué, ni même vu appliqué
ou utilisé dans les différents projets sur lesquels j’ai travaillé. J’ai donc
décidé à m’y intéresser de plus près.&lt;/p&gt;

&lt;p&gt;J’ai donc commencé par effectuer des recherches pour trouver des ressources et en
apprendre plus sur le sujet. Les nombreuses recherches que j’ai pu mener, m’ont
systématiquement redirigé vers un ebook :
&lt;a href=&quot;http://www.infoq.com/fr/minibooks/domain-driven-design-quickly&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Domain-Driven Design Vite Fait&lt;/a&gt;.
“Ce livre est une introduction rapide sur les fondamentaux de DDD. Il n’introduit
pas de nouveaux concepts. Il essaye de résumer l’essentiel de DDD en se basant
principalement sur le livre d’Eric Evans” (l’auteur de DDD).&lt;/p&gt;

&lt;p&gt;Je dois dire que je suis plutôt séduit par l’approche proposée. DDD est une
méthodologie, une façon de concevoir une application, il n’y a absolument rien de
technique dedans. La méthode se base sur la création d’un langage commun (utilisé
entre les différents membres du projet, qu’ils soient techniques ou non). Ce langage
commun permet ainsi de lever toutes ambiguïtés dans les conversations. Il est aussi
&lt;em&gt;impératif&lt;/em&gt; que ce langage se retrouve dans la modélisation du code du projet. Le
gros avantage de cette façon de travailler est que les développeurs, chefs de projet
et les personnes concernées utilisent les mêmes mots pour s’exprimer et désigner
un concept précis. La communication est alors améliorée puisque tout le monde discute
de la même manière et avec &lt;em&gt;les mêmes mots&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Bien entendu, ce langage (dit omniprésent) peut être amené à évoluer, soit parce
que le besoin métier évolue lui-même, soit parce qu’un nouveau concept qui n’avait
pas été détecté apparaît. De ce fait, la modélisation de l’application doit évoluer
en conséquence et une étape de refactoring sera nécessaire.&lt;/p&gt;

&lt;p&gt;Un projet utilisant la conception pilotée par le domaine est donc voué constamment
à évoluer et à subir une refactorisation continue. Afin de garantir l’évolutivité
et la maintenance de l’application, il sera nécessaire d’implémenter de nombreux
“design pattern” afin d’être le plus flexible possible. Il aura également la
&lt;a href=&quot;/blog/2015/03/25/les-objets-valeurs-ou-value-object.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;création de nombreuses classes&lt;/a&gt;
afin de représenter les différents objets et éléments du domaine de la manière la
plus explicite possible.&lt;/p&gt;

&lt;p&gt;Cela entrainera une certaine complexité technique qui freine certains développeurs
à utiliser cette méthode. Cette même complexité, fait que DDD n’est pas adapté pour
de jeunes développeurs. Par contre cela peut être très constructif pour ce dernier
s’il est accompagné dans son travail et que ce dernier est validé (par la mise
en place de revues de code par exemple).&lt;/p&gt;

&lt;p&gt;Pour ma part, je suis convaincu de l’aspect bénéfique de cette approche de conception
pilotée par le domaine. Bien sûr elle ne s’appliquera pas à tout type de projet,
mais peut-être très efficace dans des applications métiers complexes. En plus de
l’aspect projet, cela peut être un très bon moyen de faire progresser de jeunes
développeurs.&lt;/p&gt;
</description>
                    <pubDate>Mon, 30 Mar 2015 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2015/03/30/la-conception-pilotee-par-le-domaine.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2015/03/30/la-conception-pilotee-par-le-domaine.html</guid>
                </item>
            
        
            
                204
                <item>
                    <title>Les objets-valeurs (Value Object)</title>
                    <description>&lt;p&gt;Tout récemment, nous avons eu un gros débat sur la conception d’un projet sur
lequel je travaille. Le débat portait sur l’utilisation ou non d’un objet de type
Objets-Valeur (ou Value Object en anglais). Il est vrai que dans l’environnement
PHP l’utilisation de ce type d’objet est plutôt rare et méconnu (il est plus
courant de voir utiliser des tableaux associatifs).&lt;/p&gt;

&lt;p&gt;Les objets de type Objets-Valeurs sont généralement des petits objets dont leur
objectif est d’apporter une notion sémantique au code. Contrairement à un autre
objet, on s’intéresse à son contenu (la valeur de ses attributs) plutôt qu’à sa
référence. Ces derniers sont généralement immuables.&lt;/p&gt;

&lt;p&gt;L’exemple par excellence est certainement le cas de la monnaie. Si vous souhaitez
gérer une notion d’argent, la monnaie peut alors être géré au moyen d’un
Objet-Valeur, qui pourrait être défini comme ci-dessous :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;class Money
{
  /** @var int */
  private $amount;

  /** @var string */
  private $currency;

  /**
   * @param int    $amount
   * @param string $currency
   */
  public function __construct($amount, $currency)
  {
    $this-&amp;gt;amount   = $amount;
    $this-&amp;gt;currency = $currency;
  }

  /**
   * @return int
   */
  public function getAmount()
  {
    return $this-&amp;gt;amount;
  }

  /**
   * @return float
   */
  public function getFormatedAmount()
  {
    return round($this-&amp;gt;amount / 100, 2);
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Cette exemple correspond à une implémentation du
&lt;a href=&quot;http://martinfowler.com/eaaCatalog/money.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;pattern monnaie&lt;/a&gt;
décrit par Martin Fowler.&lt;/p&gt;

&lt;p&gt;Il est alors plus agréable et compréhensible de manipuler un objet représentant
une donnée précise qu’une variable de type entier ou flottante ayant une faible
sémantique.&lt;/p&gt;

&lt;p&gt;Ce type d’objet est très utilisé dans une conception pilotée par le domaine (Domain
Driven Design) où le code est fortement basé sur le domaine métier et la logique
associée. Ils permettent alors d’avoir un code compréhensible et avec un minimum
d’ambiguïté.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;PS:&lt;/em&gt; si vous êtes intéressé par une implémentation d’un Objet-Valeur permettant de
gérer les devises. Sebastian BERGMANN a écrit une
&lt;a href=&quot;https://github.com/sebastianbergmann/money&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;implémentation complète en PHP&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
</description>
                    <pubDate>Wed, 25 Mar 2015 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2015/03/25/les-objets-valeurs-ou-value-object.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2015/03/25/les-objets-valeurs-ou-value-object.html</guid>
                </item>
            
        
            
                205
                <item>
                    <title>Les sous-modules Git</title>
                    <description>&lt;p&gt;Comment ça vous ne connaissez pas les sous-modules (submodules) de Git ? Bien que
pouvant être très pratique, ils sont souvent assez méconnus. Pourtant nous avons
tous rencontré au moins une fois un cas où ils nous auraient bien rendu services.&lt;/p&gt;

&lt;p&gt;N’avez-vous jamais eu envie d’inclure un projet tier au sein du projet sur lequel
vous étiez en train de travailler ? Je ne parle pas d’une simple dépendance tierce,
mais d’un module ou une bibliothèque que vous développez simultanément. Comment
gérez-vous le développement des deux projets distinct qui pourtant doivent se
retrouver au sein d’une même application ?&lt;/p&gt;

&lt;p&gt;C’est un cas typique d’utilisation des sous-modules Git, car ils vont vous
permettre d’inclure un autre dépôt Git au sein de votre projet actuel. Il vous
sera alors possible de gérer vos commits sépérement pour chacun des dépôts.&lt;/p&gt;

&lt;p&gt;Si vous souhaitez travailler sur une bibliothèque au sein de votre projet courant,
vous pouvez initialiser un sous-module Git avec un dépôt externe au travers de
la commande :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;git submodule add http://url/depot.git dossier/destination&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;De ce fait, un nouveau dossier (dossier/destination) sera créé dans votre
arborescence. Ce dernier fonctionnant comme un dépôt Git à part entière.&lt;/p&gt;

&lt;p&gt;Vous noterez également qu’au travers de cette opération, Git a ajouté un nouveau
fichier de configuration nommé &lt;code&gt;.gitmodules&lt;/code&gt; contenant la description des
sous-modules utilisés par le projet.&lt;/p&gt;
</description>
                    <pubDate>Tue, 17 Mar 2015 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2015/03/17/les-sous-modules-git.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2015/03/17/les-sous-modules-git.html</guid>
                </item>
            
        
            
                206
                <item>
                    <title>PHP-CLI cet outil méconnu</title>
                    <description>&lt;p&gt;De nombreux développeurs travaillent avec PHP pour concevoir des sites ou des
applications dans des environnements Web. On commence même à voir apparaître de
nombreux outils utilisant PHP en ligne de commande. Pourtant ils sont peu nombreux
à connaitre les possibilités offertes par PHP-CLI.&lt;/p&gt;

&lt;p&gt;Prenons par exemple le cas d’un développeur PHP souhaitant tester une fonction
PHP afin de l’utiliser. Je ne compte plus le nombre de développeurs ayant créé
un fichier &lt;code&gt;test.php&lt;/code&gt; afin de tester du code (fichier exécuté aussi bien via un
serveur Web qu’en ligne de commande).&lt;/p&gt;

&lt;p&gt;Effectivement, il est fréquent que les développeurs ignorent que PHP-CLI fournit
un &lt;a href=&quot;http://php.net/manual/fr/features.commandline.interactive.php&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;shell interactif&lt;/a&gt;
permettant d’exécuter du code PHP (et donc de tester une fonction par exemple).
Pour démarrer ce dernier, il suffit d’utiliser la commande &lt;code&gt;php -a&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Prenons maintenant le cas du développeur qui souhaite afficher les informations
de configuration PHP. Là encore, de nombreuses personnes créeront un fichier
contenant un appel à la fonction &lt;code&gt;phpinfo()&lt;/code&gt;. PHP-CLI fournit pourtant une
option permettant d’afficher ces informations au travers de l’option &lt;code&gt;-i&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;De même si &lt;code&gt;phpinfo()&lt;/code&gt; est également utilisé pour connaitre les modules PHP
activées sur la machine, l’option &lt;code&gt;-m&lt;/code&gt; permet d’obtenir la liste des modules
actuellement actifs.&lt;/p&gt;

&lt;p&gt;Il est de plus en plus fréquent d’utiliser des applications PHP en ligne de
commande. L’exemple le plus courant est très certainement &lt;a href=&quot;http://getcomposer.org&quot;&gt;Composer&lt;/a&gt;.
N’avez-vous jamais lancé un &lt;code&gt;composer update&lt;/code&gt; et obtenu en réponse un message
du genre &lt;code&gt;Fatal Error: Allowed Memory Size of 134217728 Bytes Exhausted&lt;/code&gt; ?&lt;/p&gt;

&lt;p&gt;Dans ce cas-là, il n’est pas toujours possible de modifier temporairement la
configuration décrite dans le fichier &lt;code&gt;php.ini&lt;/code&gt;. Vous me direz qu’il est toujours
possible d’utiliser un appel à la fonction &lt;code&gt;ini_set&lt;/code&gt;. Mais cela vous obligera à
modifier votre code. Pour éviter cela (car en plus de ne pas être propre, c’est
parfois impossible à mettre en oeuvre), PHP-CLI vous donne la possibilité de
modifier une valeur de configuration au travers de l’argument &lt;code&gt;-d&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Par exemple pour exécuter Composer avec une limite mémoire à 1 Go, il est
possible d’utiliser la commande suivante :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;php -d memory_limit=1024M /usr/local/bin/composer update&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Il est bien évidemment possible de cumuler les variables de configuration à
modifier. Vous pouvez donc appelez Composer en activer dynamiquement une
extension PHP :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;php -d memory_limit=1024M -d extension=blackfire.so composer.php&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;N’hésitez donc pas à découvrir ou redécouvrir les nombreuses possibilités de
&lt;a href=&quot;http://php.net/manual/fr/features.commandline.php&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;PHP-CLI&lt;/a&gt;,
cela pourra vous servir et vous faciliter la vie au quotidien.&lt;/p&gt;
</description>
                    <pubDate>Fri, 06 Mar 2015 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2015/03/06/php-cli-cet-outil-meconnu.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2015/03/06/php-cli-cet-outil-meconnu.html</guid>
                </item>
            
        
            
                207
                <item>
                    <title>Le databinding en Javascript natif</title>
                    <description>&lt;p&gt;Les navigateurs Web ont énormément évolué ces dernières années. Bien que jQuery
soit encore largement utilisé par les développeurs Web, les possibilités
offertes de base par Javascript tendent à le rendre obsolète.&lt;/p&gt;

&lt;p&gt;Je m’intéresse énormément aux nouvelles possibilités du langage. Cela permet de
réduire les dépendances utilisées par nos applications, d’alléger ces dernières
et ainsi de les rendre plus rapide et fluide.&lt;/p&gt;

&lt;p&gt;Je suis aussi un grand fan du framework de Google : AngularJS. Tout comme de
nombreux développeurs, j’ai été attiré par sa simplicité d’utilisation et les
avantages qu’il offre pour la réalisation d’application Web. Un de ces principaux
atouts est certainement sa gestion du data-binding.&lt;/p&gt;

&lt;p&gt;Lorsque j’ai entendu pour la première fois qu’il existait un
&lt;a href=&quot;javascript:;&quot; class=&quot;broken-link&quot; rel=&quot;nofollow&quot; data-original-url=&quot;https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Object/observe &quot;&gt;équivalent natif&lt;/a&gt; en
Javascript et avec l’emballement du Web, je me disais qu’il fallait que je regarde
ça rapidement.&lt;/p&gt;

&lt;p&gt;Avec un peu de retard, c’est chose faite ! Et c’est également une déception, car
cette fonctionnalité est en réalité une proposition d’implémentation pour
ECMAScript 7. De ce fait, très peu de navigateurs sont
&lt;a href=&quot;javascript:;&quot; class=&quot;broken-link&quot; rel=&quot;nofollow&quot; data-original-url=&quot;http://kangax.github.io/compat-table/es7/ &quot;&gt;capables d’interpréter cette instruction&lt;/a&gt;
(uniquement les versions de Chrome &amp;gt;= 33 et NodeJS lors de l’écriture de ce
billet).&lt;/p&gt;

&lt;p&gt;Bien que cela reste néanmoins prometteur, il va falloir attendre quelques années
avant de pouvoir réellement en profiter !&lt;/p&gt;
</description>
                    <pubDate>Sat, 21 Feb 2015 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2015/02/21/le-databinding-en-javascript-natif.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2015/02/21/le-databinding-en-javascript-natif.html</guid>
                </item>
            
        
            
                208
                <item>
                    <title>Tests fonctionnels et gestion des dates</title>
                    <description>&lt;p&gt;Je travaille actuellement sur une application de gestion de prise de rendez-vous
à destination des professionnels. Afin de s’assurer du fonctionnement correct de
la plateforme, cette dernière est testée au travers de nombreux scénarios Behat.&lt;/p&gt;

&lt;p&gt;L’exécution des tests Behat est relativement classique. Avant chaque scénario, un
jeu de données est chargée en base de données afin de dérouler les différentes
étapes. Néanmoins cette méthode présente un inconvénient majeur puisque le jeu
de données est statique.&lt;/p&gt;

&lt;p&gt;Or lorsque l’on travaille avec des gestions de date, cela peut poser quelques
problèmes. Effectivement, si on prend par exemple un scénario de prise de rendez-vous
en tenant compte qu’un rendez-vous ne peut pas être pris pour une date antérieure
à la date du jour, il sera nécessaire de mettre régulièrement le jeu de données
à jour afin d’éviter que les données de ce dernier ne soient obsolètes. La date
de la machine exécutant les tests étant la date de référence.&lt;/p&gt;

&lt;p&gt;Pour résoudre ce problème, on peut penser à plusieurs solutions. La première est
certainement de générer un jeu de données dynamique afin d’avoir des données relatives
à la date du jour. Je n’aime pas trop cette solution pour diverses raisons. La
première car elle nécessite du développement (et que j’ai peu de temps :)). La
seconde, car on “perd” un certain contrôle sur le jeu de données et les tests vont
nécessiter plus de codes. Or je souhaite garder ces tests aussi simple que possible.&lt;/p&gt;

&lt;p&gt;La seconde solution, m’ayant traversé l’idée était de modifier la date système lors
de l’exécution des tests. Il ne m’a pas fallu longtemps pour me dire que c’était
une très mauvaise idée !&lt;/p&gt;

&lt;p&gt;Dans le cas présent, afin de faire le minimum de modification sur l’existant, en
introduisant le minimum de développement, j’ai eu recours à une application nommée
&lt;a href=&quot;https://github.com/wolfcw/libfaketime&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;faketime&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Cette application permet d’intercepter les appels systèmes de récupération de la
date et l’heure. Faketime permet ainsi de démarrer un processus en altérant de
manière virtuelle la date de la machine uniquement pour le processus désigné.&lt;/p&gt;

&lt;p&gt;Lancer l’exécution des scénarios Behat en utilisant &lt;code&gt;faketime&lt;/code&gt; se fait au travers
d’une simple ligne de commande :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;faketime &amp;#39;2014-10-05 12:30:00&amp;#39; bin/behat  # Utilisation d&amp;#39;une date précise
faketime &amp;#39;last Friday&amp;#39; bin/behat          # Utilisation d&amp;#39;une date relative&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

</description>
                    <pubDate>Wed, 11 Feb 2015 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2015/02/11/tests-fonctionnels-et-gestion-des-dates.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2015/02/11/tests-fonctionnels-et-gestion-des-dates.html</guid>
                </item>
            
        
            
                209
                <item>
                    <title>Ne faites pas de &quot;composer update&quot;</title>
                    <description>&lt;p&gt;Un titre provoquant certes, je devrais plutôt dire : “ne faites pas de composer
update” d’un seul coup sur la totalité des dépendances de votre projet (et en
plus c’est une mauvaise pratique) ! Et à plus forte raison si votre application
n’est pas bien couverte par des tests automatisés.&lt;/p&gt;

&lt;p&gt;Personnellement, j’ai eu le cas pas plus tard qu’aujourd’hui. Un développeur
devait réaliser une évolution mineure sur le projet sur lequel je travaille. Ni
une, ni deux, il ajoute une nouvelle dépendance à la main dans le &lt;code&gt;composer.json&lt;/code&gt;
et exécute un &lt;code&gt;composer update&lt;/code&gt; pour l’installer.&lt;/p&gt;

&lt;p&gt;Je passe le fait de ne pas avoir utilisé la ligne de commande adaptée pour cette
opération (un &lt;code&gt;composer require vendor/package&lt;/code&gt;), mais en plus il n’a pas pris la
peine de lancer les tests (unitaires et fonctionnels) présent sur le projet.&lt;/p&gt;

&lt;p&gt;Le résultat ? De nombreuses dépendances du projet ont été mise à jour. Certaines
introduisaient des “breaking changes” (dans un changement de version mineur) et
50% du projet est devenu non opérationnel en 30 secondes !&lt;/p&gt;
</description>
                    <pubDate>Thu, 05 Feb 2015 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2015/02/05/ne-faites-pas-de-composer-update.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2015/02/05/ne-faites-pas-de-composer-update.html</guid>
                </item>
            
        
            
                210
                <item>
                    <title>Mocker un service Symfony2 dans un test Behat</title>
                    <description>&lt;p&gt;Il arrive fréquemment que l’on soit amené à utiliser des services Web tiers dans
les applications que nous concevons. Lors de l’écriture de nos tests, nous
utilisons des mocks (bouchons) afin de simuler le comportement de ces derniers.&lt;/p&gt;

&lt;p&gt;Dans le cas d’un test Behat le premier réflexe est certainement d’accéder au
containeur de dépendances depuis un contexte afin de modifier ce dernier à la
volée.&lt;/p&gt;

&lt;p&gt;Malheureusement cette solution ne fonctionne pas. Effectivement le conteneur de
dépendance n’est pas partagé entre notre application et les contextes utilisées
par Behat. Toute modification de nos dépendances est alors sans effet.&lt;/p&gt;

&lt;p&gt;Une solution consiste alors à utiliser une configuration du conteneur de
dépendances en fonction de l’environnement Symfony utilisé et d’exécuter nos
scénarios sur l’environnement correspondant.&lt;/p&gt;

&lt;p&gt;Commençons donc par modifier la configuration du service que nous souhaitons
mocker (un composant Facebook dans notre exemple) :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;# src/AppBundle/Resources/config/services.yml
services:
    app.component.facebook:
        class: %app.component.facebook.class%&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Définissons maintenant la classe à utiliser dans le cas “normal” :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;# app/config/config.yml
parameters:
    app.component.facebook.class: Component\Facebook\Facebook&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Il ne nous reste plus qu’à surcharger ce paramètre lorsque l’on utilise
l’application dans son environnement de test :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;# app/config/config_test.yml
parameters:
    app.component.facebook.class: AppBundle\Tests\Mock\Component\Facebook\FacebookMock&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Maintenant cette configuration terminée, en configurant Behat pour qu’il utilise
l’environnement de test de Symfony, les différents scénarios seront exécutés avec
la version de l’application utilisant les bouchons.&lt;/p&gt;

&lt;p&gt;La technique utilisée ici est simple, mais peut être contraignante si vous avez
un certain nombre de classes à “mocker”. Sur
&lt;a href=&quot;javascript:;&quot; class=&quot;broken-link&quot; rel=&quot;nofollow&quot; data-original-url=&quot;http://blog.lyrixx.info/2013/04/12/symfony2-how-to-mock-services-during-functional-tests.html &quot;&gt;son blog&lt;/a&gt;,
Grégoire Pineau (consultant chez SensioLabs) donne une autre alternative pour
réaliser cette tâche et surtout plus pertinente si vous travaillez sur de grosses
applications.&lt;/p&gt;
</description>
                    <pubDate>Tue, 03 Feb 2015 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2015/02/03/mocker-un-service-symfony2-dans-un-test-behat.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2015/02/03/mocker-un-service-symfony2-dans-un-test-behat.html</guid>
                </item>
            
        
            
                211
                <item>
                    <title>Faire un héritage propre en Javascript</title>
                    <description>&lt;p&gt;Plus je m’intéresse au langage Javascript et plus je me rends compte des erreurs
que je fais depuis maintenant des années. En fait bien peu de développeurs
connaissent réellement ce langage. À tel point que très peu de personnes (moi
inclus) savent écrire un héritage sans passer par un composant tiers.&lt;/p&gt;

&lt;p&gt;Car en attendant de pouvoir utiliser ECMAScript 6 et ainsi de bénéficier des mots-clés
&lt;code&gt;class&lt;/code&gt; et &lt;code&gt;extend&lt;/code&gt;, écrire un héritage en Javascript n’est pas trivial. Pour
comprendre comment réaliser cette opération, il vous faudra tout d’abord connaître
&lt;a href=&quot;/blog/2014/10/28/les-prototypes-javascript.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;la notion de prototype Javascript&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;En ECMAScript 5 (version actuellement supportée par la majorité des navigateurs), le
moyen le plus simple de réaliser un héritage est d’utiliser la fonction
&lt;a href=&quot;https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Object/create&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;Object.create&lt;/code&gt;&lt;/a&gt;.
Ainsi si j’ai une classe &lt;code&gt;Cat&lt;/code&gt; qui doit hériter d’une classe &lt;code&gt;Animal&lt;/code&gt;, on peut
écrire cet héritage ainsi :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;function Animal(p1, p2) {
  // ...
}

function Cat(p1, p2, p3) {
  Animal.call(this, p1, p2); // super() =&amp;gt; appel du constructeur parent
  this.myProp = p3;

  // ...
}

Cat.prototype = Object.create(Animal.prototype, {
  constructor: { value: Cat }
});&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;De cette manière la classe Cat va hériter du comportement de la classe Animal.&lt;/p&gt;

&lt;p&gt;Mais il se peut pour une raison quelconque vous utilisiez un navigateur ne supportant
pas la version 5 de Javascript. Dans ce cas-là, il vous faudra écrire manuellement
l’héritage.&lt;/p&gt;

&lt;p&gt;On pourrait alors être tenté d’écrire quelque chose comme &lt;code&gt;Child.prototype = new Parent()&lt;/code&gt;
ce qui reviendrait à mettre dans le prototype de &lt;code&gt;Child&lt;/code&gt; toutes les méthodes de &lt;code&gt;Parent&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Tout d’abord écrire cette ligne de code n’est pas anodin, car cela signifie pour
écrire un héritage, vous devez instancier un objet (ce qui n’est pas logique).
Ensuite comment gérer le cas d’un objet parent dont le constructeur contiendrait
des paramètres ? En utilisant cette méthode, il est impossible de résoudre ce cas.&lt;/p&gt;

&lt;p&gt;La manière correcte d’écrire la fonction &lt;code&gt;Object.create&lt;/code&gt; est :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;function create(Child, Parent) {
  var Obj = function() { };
  Obj.prototype = Parent.prototype;

  Child.prototype = new Obj();
  Child.prototype.constructor = Child;
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;De cette façon on écrit un héritage tout en conservant nos constructeurs
intacts. Cela passe par la création d’un objet intermédiaire n’ayant pas de
constructeur, auquel nous connectons le prototype de notre objet parent.
Nous utilisons cet objet intermédiaire dans le prototype de notre objet parent
auquel nous n’oublions pas rattaché le constructeur de l’objet initial.&lt;/p&gt;
</description>
                    <pubDate>Mon, 26 Jan 2015 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2015/01/26/faire-un-heritage-propre-en-javascript.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2015/01/26/faire-un-heritage-propre-en-javascript.html</guid>
                </item>
            
        
            
                212
                <item>
                    <title>Comprenez les outils que vous utilisez</title>
                    <description>&lt;p&gt;Le développement informatique évolue rapidement. Là où il y encore quelques
années nous programmions tout à la main from scratch, nous réutilisons de
plus en plus de composants externes. C’est d’autant plus vrai avec l’émergence
et la démocratisation de l’Open Source. Seulement utilisez ces composants
externes ne vous dispensent pas de comprendre comment ils fonctionnent.&lt;/p&gt;

&lt;p&gt;Je vois trop souvent de nombreux développeurs faisant du copier/coller de code
provenant d’internet ou d’un autre projet sans chercher à comprendre ce qu’ils
font ni même à prendre le temps de lire un minimum la documentation de l’outil,
du composant ou de l’application qu’ils utilisent.&lt;/p&gt;

&lt;p&gt;Du coup au premier problème, ils ne savent plus vraiment comment faire :&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;“Pourtant ça fonctionnait sur le projet d’avant, je ne comprends pas.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;J’ai pour habitude de dire, que pour bien utiliser un outil, il faut comprendre
sa philosophie et dans quel but il a été créé. Quand vous utilisez n’importe quel
outil, prenez quelques instants pour le comprendre, comment il fonctionne,
sa philosophie.&lt;/p&gt;

&lt;p&gt;Lorsque ce dernier ne se comportera pas de la manière attendue, vous serez
alors efficacement armé pour comprendre ce qui se passe et savoir où chercher.&lt;/p&gt;
</description>
                    <pubDate>Fri, 23 Jan 2015 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2015/01/23/comprenez-les-outils-que-vous-utilisez.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2015/01/23/comprenez-les-outils-que-vous-utilisez.html</guid>
                </item>
            
        
            
                213
                <item>
                    <title>Un avenir pour CoffeeScript avec ECMAScript 6 ?</title>
                    <description>&lt;p&gt;Il y a quelques années, j’ai découvert
&lt;a href=&quot;http://coffeescript.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;CoffeeScript&lt;/a&gt; (un langage
qui se compile en Javascript) et j’ai tout de suite été séduit. Ecrire du code
Javascript est devenu plus rapide et surtout CoffeeScript a ajouté certaines
fonctionnalités très plaisantes telles que les classes, les proxies, les générateurs
et d’autres encores.&lt;/p&gt;

&lt;p&gt;Apparu en 2009, CoffeeScript répondait à un réel besoin de simplification et
d’évolution de Javascript. Seulement aujourd’hui Javascript évolue et la nouvelle
spécification du langage (ECMAScript 6) comporte de nombreux ajouts qui ont fait
le succès de Coffee. Cela à tel point que l’on peut être amené à se demander si
CoffeeScript a encore un avenir.&lt;/p&gt;

&lt;p&gt;J’ai beau réellement accrocher à CoffeeScript, pour moi la réponse est clairement
“non”. Je pense réellement que la nouvelle spécification de Javascript rend obsolète
CoffeeScript car les apports majeurs du langage seront maintenant natifs. La chose
qui le différenciera sera sa syntaxe, mais ce n’est pour ma part pas un argument
suffisant.&lt;/p&gt;

&lt;p&gt;Seulement voilà, à l’heure actuelle, ECMAScript 6 n’est pas encore finalisé. Et bien
que les principaux navigateurs commencent à implémenter certaines fonctionnalités, la
couverture des spécifications est encore faible.&lt;/p&gt;

&lt;p&gt;Il existe néanmoins des outils (transpileurs) qui vous permettent d’écrire du code
ECMAScript 6 pour le traduire dans la version 5 du langage (qui est la version)
actuellement supportée par les navigateurs. Parmi les outils les plus connus, on
peut citer &lt;a href=&quot;https://github.com/google/traceur-compiler&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;traceur&lt;/a&gt;
et &lt;a href=&quot;javascript:;&quot; class=&quot;broken-link&quot; rel=&quot;nofollow&quot; data-original-url=&quot;https://6to5.org &quot;&gt;6to5&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Donc à la question que faire maintenant, doit-on utiliser CoffeeScript ou commencer
à utiliser ECMAScript 6 via un transpileur ? Personnellement, pour un projet qui
devra durer dans le temps et pour lequel je parie sur l’avenir, j’utiliserais sans
hésiter un transpileur car pour moi CoffeeScript est réellement sur sa fin de vie.&lt;/p&gt;
</description>
                    <pubDate>Tue, 20 Jan 2015 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2015/01/20/quel-avenir-pour-coffeescript-avec-ecmascript-6.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2015/01/20/quel-avenir-pour-coffeescript-avec-ecmascript-6.html</guid>
                </item>
            
        
            
                214
                <item>
                    <title>Ne mettez pas toute votre vie dans les fichiers .gitignore</title>
                    <description>&lt;p&gt;Lorsque l’on travaille avec un gestionnaire de version, il est souvent utile
d’ignorer et de ne pas versionner certains fichiers et/ou dossiers. C’est
extrêmement pratique pour ne pas pourrir votre dépôt avec des fichiers inutiles
tel que des logs par exemple ou pour éviter de commiter de mauvais fichier par
inadvertance. Dans Git cela se traduit par la création d’un fichier &lt;code&gt;.gitignore&lt;/code&gt;
(mais des mécanismes similaires existent pour les autres outils).&lt;/p&gt;

&lt;p&gt;Une bonne pratique est de n’ajouter que des fichiers directement liés au projet
concerné. Mais nous rencontrons tous, régulièrement, des projets avec des fichiers
&lt;code&gt;.gitignore&lt;/code&gt; “fourre tout” où l’on retrouve de nombreux fichiers divers et variés.
Avoir un fichier &lt;code&gt;.gitignore&lt;/code&gt; propre, c’est  faciliter la compréhension de ce
dernier, mais aussi de comprendre certains mécanismes de l’application et identifier
des fichiers de logs générés par des traitements (et utile pour le debug).&lt;/p&gt;

&lt;p&gt;Il est pourtant possible avec Git de définir un fichier &lt;code&gt;.gitignore&lt;/code&gt; global qui sera
appliqué par défaut sur tous les projets en plus du fichier local du dépôt de travail.
Personnellement, ce dernier contient l’ensemble des fichiers automatiquement créés par
les différents IDE que je suis ammené à utiliser.&lt;/p&gt;

&lt;p&gt;On trouve également des modèles sur Github :
&lt;a href=&quot;https://github.com/github/gitignore&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;https://github.com/github/gitignore&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pour le mettre en place, il suffit de créer un fichier ayant un format identique
à n’importe quel fichier &lt;code&gt;.gitignore&lt;/code&gt; et d’indiquer à Git de le prendre en compte
via la commande :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;git config --global core.excludesfile /path/to/.gitignore_global&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Et voilà, le tour est joué et vous ne pourrirez plus les fichiers &lt;code&gt;.gitignore&lt;/code&gt; de vos projets
et vos collègues vous en remercieront.&lt;/p&gt;
</description>
                    <pubDate>Wed, 07 Jan 2015 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2015/01/07/ne-mettez-pas-toute-votre-vie-dans-les-fichiers-gitignore.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2015/01/07/ne-mettez-pas-toute-votre-vie-dans-les-fichiers-gitignore.html</guid>
                </item>
            
        
            
                215
                <item>
                    <title>Résoudre le conflit entre Capifony et Capistrano 3</title>
                    <description>&lt;p&gt;Si vous avez installé la version 3 de Capistrano (un outil largement utilisé pour
le déploiement d’application web), vous avez peut-être remarqué qu’il y avait un
conflit avec Capifony (son adaptation pour les applications Symfony2).
Effectivement, ce dernier n’est pas (encore) compatible avec la dernière version
de Capistrano.&lt;/p&gt;

&lt;p&gt;Si vous rencontrez ce problème, il existe une solution simple permettant de
résoudre le conflit. Pour cela il vous faudra installer &lt;code&gt;bundler&lt;/code&gt; via cette
ligne de commande : &lt;code&gt;gem install bundler&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Bundler est l’outil utilisé pour gérer les dépendances des applications Ruby.
Nous allons donc nous servir de cet outil pour installer et utiliser la
version de Capistrano correspondant à nos besoins. La configuration de bundler
se passe au travers d’un fichier &lt;code&gt;Gemfile&lt;/code&gt; qui décrit les dépendances requises :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;source &amp;#39;https://rubygems.org&amp;#39;
gem &amp;#39;capifony&amp;#39;, &amp;#39;~&amp;gt; 2.8.1&amp;#39;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Il suffit ensuite de démarrer l’installation des dépendances via la commande
&lt;code&gt;bundle install&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Tout comme avec Composer, Bundler a installé les différents modules “localement”.
Pour exécuter Capifony la version décrit dans le &lt;code&gt;Gemfile&lt;/code&gt;, il faudra préfixer les
commandes par &lt;code&gt;bundle exec&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Par exemple, la commande de déploiement s’execute via la commande :
&lt;code&gt;bundle exec cap deploy&lt;/code&gt;&lt;/p&gt;
</description>
                    <pubDate>Tue, 16 Dec 2014 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2014/12/16/resoudre-le-conflit-entre-capifony-et-capistrano-3.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2014/12/16/resoudre-le-conflit-entre-capifony-et-capistrano-3.html</guid>
                </item>
            
        
            
                216
                <item>
                    <title>Une authentification Git permanente en HTTP</title>
                    <description>&lt;p&gt;Je travaille régulièrement avec Git au travers du protocole HTTP quand il m’est
impossible d’utiliser une connexion SSH. Le plus embêtant et qu’il est toujours
nécessaire de saisir ses identifiants pour pouvoir accéder au dépôt distant.&lt;/p&gt;

&lt;p&gt;Git utilisant cURL pour effectuer ses requêtes HTTP, si vous avez la chance d’être
sous Linux, vous pouvez stocker vos informations de connexion dans un fichier
&lt;code&gt;.netrc&lt;/code&gt; à mettre dans votre “home”.&lt;/p&gt;

&lt;p&gt;Par exemple :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;machine serveur.git.com
login monUtilisateurGit
password s3cret&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Attention, les vos identifiants et mots de passe étant stockés en clair, pensez
à changer les droits du fichier avec un &lt;code&gt;chmod 0600&lt;/code&gt; afin d’empêcher les autres
utilisateurs d’y avoir accès.&lt;/p&gt;

&lt;p&gt;À noter également qu’à partir de la version 1.8.3 de Git, il est tout à fait
possible d’&lt;a href=&quot;https://stackoverflow.com/questions/5343068/is-there-a-way-to-skip-password-typing-when-using-https-github/18362082#18362082&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;utiliser des fichiers &lt;code&gt;.netrc&lt;/code&gt; cryptés&lt;/a&gt;.&lt;/p&gt;
</description>
                    <pubDate>Tue, 18 Nov 2014 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2014/11/18/une-authentification-git-permanente-en-http.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2014/11/18/une-authentification-git-permanente-en-http.html</guid>
                </item>
            
        
            
                217
                <item>
                    <title>Protégez l&apos;accès à vos ressources</title>
                    <description>&lt;p&gt;Ce weekend, je baladais sur le site d’une agence musicale qui offre à ses clients
des prestations audiovisuelles. La société offre notamment un accès à un large
catalogue de musique à condition d’être un professionel du domaine de l’audiovisuel.
Sauf qu’en réalité, avec un peu de débrouille, l’ensemble du catalogue est librement
accessible.&lt;/p&gt;

&lt;p&gt;Effectivement, lorsque l’on navigue sur le site en question, un lecteur audio
permet d’écouter les titres disponibles. En analysant les trames réseaux, il est
facile d’accéder à l’adresse du fichier qui est écouté :&lt;/p&gt;

&lt;center&gt;
    &lt;img src=&quot;/img/blog/20141116-protegez-l-acces-a-vos-ressources/capture-reseau.png&quot; /&gt;
&lt;/center&gt;

&lt;p&gt;Rien que via cette URL on peut récupérer le morceau complet au format MP3 encodé
en 128 kbps. En analysant l’adresse du fichier, il est aisé de reconnaitre le
schéma adopté &lt;code&gt;http://site.com/musicfiles/[format]/[id-musique].[format]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;En recherchant les différents morceaux, les identifiants des musiques sont facilement
récupérables car ils apparaissent dans la barre d’adresse du navigateur.&lt;/p&gt;

&lt;p&gt;Cela aurait pu s’arrêter ici, mais en lisant les mentions légales du site, on
apprend qu’il est possible de télécharger les différents morceaux en format
“MP3 (320 kbps)”, “AIFF” ou “WAV” (non compressé). Et oui c’est l’ensemble du
catalogue dans tous les formats disponibles (et même haute définition) qui
sont librement accessibles.&lt;/p&gt;

&lt;p&gt;Il aurait pourtant été facile de protéger l’accès aux ressources en utilisant
un petit script PHP comme celui ci-dessous :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;ob_clean();

header(&amp;#39;Content-Type: audio/mp3&amp;#39;);
header(&amp;#39;Content-Disposition: filename=&amp;quot;fichier.mp3&amp;quot;&amp;#39;);
flush();

readfile(&amp;#39;chemin/vers/le/fichier.mp3&amp;#39;);&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Lorsque vous concevez un site, pensez bien à la sécurité des données de ce dernier
et pas seulement celles qui sont présentes en base de données.&lt;/p&gt;

&lt;p&gt;PS: un autre élément également inquiétant, les en-têtes renvoyées par le
serveur Apache et qui indique que la version de PHP utilisée est la 5.2.17. Soit
une version qui n’est plus maintenue depuis le 6 janvier 2011 !&lt;/p&gt;
</description>
                    <pubDate>Sun, 16 Nov 2014 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2014/11/16/protegez-l-acces-a-vos-ressources.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2014/11/16/protegez-l-acces-a-vos-ressources.html</guid>
                </item>
            
        
            
                218
                <item>
                    <title>Des propriétés non énumérables dans un objet Javascript</title>
                    <description>&lt;p&gt;Dans un article précédent, je parlais de comment &lt;a href=&quot;http://www.jdecool.fr/blog/2014/10/28/les-prototypes-javascript.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;créer des objets en
Javascript en utilisant les prototypes&lt;/a&gt;. Mais le fonctionnement par défaut du
langage implique que les propriétés de votre objet sont énumérables. De ce fait,
elles seront listées au sein d’une boucle &lt;code&gt;for...in&lt;/code&gt; par exemple.&lt;/p&gt;

&lt;p&gt;En reprenant l’exemple précédent :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;function MyObject() {
    this.myProperty1 = null;
    this.myProperty2 = null;
}

MyObject.prototype.myMethod1 = function() {
    return &amp;#39;method1&amp;#39;;
}

MyObject.prototype.myMethod2 = function(param1, param2) {
    return &amp;#39;method2&amp;#39;;
}

var myInstance = new MyObject();

var properties = [];
for (var prop in myInstance) {
	properties.push(prop);
}

console.log(properties); // =&amp;gt; [ &amp;#39;myProperty1&amp;#39;, &amp;#39;myProperty2&amp;#39;, &amp;#39;myMethod1&amp;#39;, &amp;#39;myMethod2&amp;#39; ]&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Il peut être dérangeant dans certains cas que les éléments constituants de notre objet
soient énumérables. Pour éviter cela, la solution est d’utiliser la méthode
&lt;code&gt;Object.defineProperty()&lt;/code&gt;. Cette dernière permet de définir une nouvelle propriété
ou d’en modifier une existante. Elle permet également de définir le comportement de la
propriété ajoutée.&lt;/p&gt;

&lt;p&gt;Il est donc possible de définir une contrainte &lt;code&gt;enumerable&lt;/code&gt; à &lt;code&gt;false&lt;/code&gt; afin d’éviter
que les propriétés n’apparaissent dans les boucles.&lt;/p&gt;

&lt;p&gt;Notre objet peut donc être défini ainsi :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;function MyObject() {
    this.myProperty1 = null;
    this.myProperty2 = null;
}

Object.defineProperty(MyObject.prototype, &amp;quot;myMethod1&amp;quot;, {
    enumerable: false,
    value: function() {
        return &amp;#39;method1&amp;#39;;
    }
});

Object.defineProperty(MyObject.prototype, &amp;quot;myMethod2&amp;quot;, {
    enumerable: false,
    value: function() {
        return &amp;#39;method2&amp;#39;;
    }
});

var myInstance = new MyObject();

var properties = [];
for (var prop in myInstance) {
	properties.push(prop);
}

console.log(properties); // =&amp;gt; [ &amp;#39;myProperty1&amp;#39;, &amp;#39;myProperty2&amp;#39; ]&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;code&gt;Object.defineProperty()&lt;/code&gt; propose également une option &lt;code&gt;writable&lt;/code&gt; qui
permet d’empêcher la modification d’une propriété de l’objet.&lt;/p&gt;

&lt;p&gt;Si vous souhaitez obtenir plus d’information sur le fonctionnement de
&lt;code&gt;Object.defineProperty()&lt;/code&gt;, je vous invite à consulter &lt;a href=&quot;https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Object/defineProperty&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;la documentation&lt;/a&gt; développeur de Mozilla.&lt;/p&gt;
</description>
                    <pubDate>Wed, 05 Nov 2014 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2014/11/05/des-proprietes-non-enumerables-dans-un-objet-javascript.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2014/11/05/des-proprietes-non-enumerables-dans-un-objet-javascript.html</guid>
                </item>
            
        
            
                219
                <item>
                    <title>Les prototypes Javascript</title>
                    <description>&lt;p&gt;Je travaille depuis quelques semaines sur une application temps réel réalisé
avec NodeJS. J’ai longtemps critiqué ce langage qui au fond est plein de qualités,
c’est juste qu’en réalité peu de gens le connaissent réellement. C’est donc
l’occasion de revoir les basiques de ce langage.&lt;/p&gt;

&lt;p&gt;Contrairement à une croyance répandue, Javascript est un langage orienté objet.
Certes la syntaxe et la façon d’écrire un objet sont différentes de la plupart des
langages, mais cela reste de la programmation objet.&lt;/p&gt;

&lt;p&gt;Souvent, lorsque l’on regarde un tutorial sur le Web, un objet est écrit comme
cela :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;function MyObject() {
    this.myProperty1 = null;
    this.myProperty2 = null;

    this.myMethod1 = function()  {
        return &amp;#39;method1&amp;#39;;
    };

    this.myMethod2 = function(param1, param2) {
        return &amp;#39;method2&amp;#39;;
    };
}

var myInstance = new MyObject();
myInstance.myMethod1(); // =&amp;gt; method1&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;C’est une des méthodes pour déclarer un objet en Javascript, mais ce n’est pas
la solution idéale.&lt;/p&gt;

&lt;p&gt;Pourquoi ? Parce que en déclarant votre objet ainsi, à chaque création d’une
instance de votre objet, la machine virtuelle Javascript va redéfinir l’ensemble
des propriétés et méthodes et donc les dupliquer en mémoire.&lt;/p&gt;

&lt;p&gt;La bonne solution est donc d’utiliser les &lt;code&gt;prototypes&lt;/code&gt;. Un prototype permet
d’attacher une propriété à un objet. Il s’agit d’un mécanisme de “fallback”.
Lorsqu’un appel sur une instance de notre objet est évalué par la machine virtuelle,
cette dernière va commencer par vérifier s’il existe une propriété attachée dans le
constructeur de l’objet (la définition de la fonction). Si rien n’est trouvé, alors
une recherche sera effectuée dans son prototype.&lt;/p&gt;

&lt;p&gt;À ce moment-là, si la propriété n’est toujours pas trouvée dans l’objet, une recherche
va être effectuée dans l’objet parent (s’il en existe un). Dans le cas contraire,
l’évalutation retournera la valeur &lt;code&gt;undefined&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Le gros avantage des prototypes est qu’un prototype est en réalité un pointeur
vers une fonction. Cela implique donc que la fonction n’est pas redéfinie et/ou
dupliquer par toutes les instances de notre objet.&lt;/p&gt;

&lt;p&gt;La bonne façon d’écrire notre code est la suivante :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;function MyObject() {
    this.myProperty1 = null;
    this.myProperty2 = null;
}

MyObject.prototype.myMethod1 = function() {
    return &amp;#39;method1&amp;#39;;
}

MyObject.prototype.myMethod2 = function(param1, param2) {
    return &amp;#39;method2&amp;#39;;
}

var myInstance = new MyObject();
myInstance.myMethod1(); // =&amp;gt; method1&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

</description>
                    <pubDate>Tue, 28 Oct 2014 00:00:00 +0100</pubDate>
                    <link>https://www.jdecool.fr/blog/2014/10/28/les-prototypes-javascript.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2014/10/28/les-prototypes-javascript.html</guid>
                </item>
            
        
            
                220
                <item>
                    <title>Accéder aux commandes Doctrine dans Symfony2</title>
                    <description>&lt;p&gt;Vous ne le saviez peut-être pas, mais lorsque vous démarrez un projet Symfony2,
Doctrine est fourni avec un certain nombre de commandes prédéfinit. Parmi ces
dernières certaines peuvent être très utiles comme par exemple une commande
permettant d’importer un fichier SQL en base de données.&lt;/p&gt;

&lt;p&gt;Si vous ne le saviez pas, c’est normal, car ces commandes n’apparaissent pas par
défaut dans la console de Symfony. Mais étant donné qu’elles utilisent le
composant &lt;code&gt;Console&lt;/code&gt; du framework, il est facile d’y avoir accès.&lt;/p&gt;

&lt;p&gt;Pour cela, la solution la plus simple est de créer une commande dans son projet
et de la faire hériter de la commande Doctrine à laquelle on souhaite accéder.&lt;/p&gt;

&lt;p&gt;Par exemple, si je souhaite accéder à la commande de Doctrine DBAL permettant
d’insérer un fichier SQL en base de données, je peux écrire la commande ci-dessous :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;namespace JDecool\Bundle\DemoBundle\Command;

use Doctrine\DBAL\Tools\Console\Command;
use Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper;
use Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class ImportCommand extends Command\ImportCommand
{
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $container = $this-&amp;gt;getApplication()-&amp;gt;getKernel()-&amp;gt;getContainer();

        $doctrine = $container-&amp;gt;get(&amp;#39;doctrine&amp;#39;);

        $em = $doctrine-&amp;gt;getEntityManager();
        $db = $em-&amp;gt;getConnection();

        $helperSet = $this-&amp;gt;getHelperSet();
        $helperSet-&amp;gt;set(new ConnectionHelper( $db ), &amp;#39;db&amp;#39;);
        $helperSet-&amp;gt;set(new EntityManagerHelper( $em ), &amp;#39;em&amp;#39;);

        parent::execute($input, $output);
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Si vous souhaitez connaitre l’ensemble des commandes proposées par Doctrine, la
liste est disponible sur la &lt;a href=&quot;http://doctrine-orm.readthedocs.org/en/latest/reference/tools.html#command-overview&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;documentation officielle du projet&lt;/a&gt;.&lt;/p&gt;
</description>
                    <pubDate>Fri, 24 Oct 2014 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2014/10/24/acceder-aux-commandes-doctrine-dans-un-projet-symfony2.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2014/10/24/acceder-aux-commandes-doctrine-dans-un-projet-symfony2.html</guid>
                </item>
            
        
            
                221
                <item>
                    <title>Un certificat SSL gratuit pour vos applications Web</title>
                    <description>&lt;p&gt;La base de la sécurité d’une application Web commence par des échanges cryptés
réalisés au travers d’un protocole de type HTTPS. Via un certificat SSL, il sera
alors possible de chiffrer les connexions entre pairs. Il semble pourtant que de
nombreux sites n’y ont pas recours.&lt;/p&gt;

&lt;p&gt;Même s’il est aujourd’hui prouvé que ce système comporte des failles, cela permet
d’accorder un minimum de confiance à l’outil que l’on est en train d’utiliser. Il
s’agit d’ailleurs, d’un gage de confiance pour le grand public, à tel point que
Google a décidé d’
&lt;a href=&quot;http://www.zdnet.fr/actualites/google-ameliore-le-referencement-des-sites-en-https-39804689.htm&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;améliorer le référencement des sites HTTPS&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Pour rappel, la mise en place d’une communication chiffrée au travers d’un certificat
SSL a pour objectif :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;d’assurer une &lt;strong&gt;confidentialité&lt;/strong&gt; des données en rendant impossible d’espionner
les informations échangées&lt;/li&gt;
  &lt;li&gt;de contrôler l’&lt;strong&gt;intégrité&lt;/strong&gt; des données et de vérifier qu’elles n’ont pas été
altérées (volontairement ou non) au cours de l’échange&lt;/li&gt;
  &lt;li&gt;de certifier de l’identité de l’hôte en &lt;strong&gt;authentifiant&lt;/strong&gt; la personne ou l’
entreprise avec qui l’on communique&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pour créer un certificat SSL valide, il est nécessaire que le certificat que vous
allez mettre en place soit signé par une autorité de certification ce qui a un coût.&lt;/p&gt;

&lt;p&gt;J’ai découvert  récemment au travers d’un &lt;a href=&quot;https://twitter.com/esion/status/515026582415872000&quot; target=&quot;_target&quot;&gt;tweet&lt;/a&gt;
que certaines entreprises proposaient de signer gratuitement des certificats SSL “de
base”. C’est notamment le cas de &lt;a href=&quot;https://www.startssl.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;StartSSL&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;J’ai testé le service pour un usage personnel et j’avoue que j’en suis pleinement
satisfait pour le moment. Cela me permet d’avoir des services sécurisés en HTTPS
reconnu par tous les navigateurs modernes.&lt;/p&gt;

&lt;p&gt;Il s’agit bien sûr d’un certificat basique et il y a très peu de vérification
pour l’obtenir. Mais si comme moi, il vous arrive de lancer quelques “side-project”
et que vous souhaitez offrir un niveau de confiance minimal à vos utilisateurs,
cela peut être une bonne façon de commencer.&lt;/p&gt;

&lt;p&gt;À noter également, que si votre projet est Open Source, vous avez la possibilité
d’obtenir un &lt;a href=&quot;https://www.globalsign.com/ssl/ssl-open-source/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;certificat GlobalSign gratuitement&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Sinon, pour un certificat de base, l’un des meilleurs rapports qualité/prix est
certainement le &lt;a href=&quot;https://www.gandi.net/ssl/standard&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;certificat standard de Gandi&lt;/a&gt;
proposé à 12€/an et gratuit la première année pour l’achat du nom de domaine.&lt;/p&gt;

&lt;p&gt;Maintenant, plus d’excuses pour ne pas fournir un des éléments de base d’une
application sécurisée !&lt;/p&gt;
</description>
                    <pubDate>Tue, 14 Oct 2014 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2014/10/14/un-certificat-ssl-gratuit-pour-vos-applications-web.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2014/10/14/un-certificat-ssl-gratuit-pour-vos-applications-web.html</guid>
                </item>
            
        
            
                222
                <item>
                    <title>Injecter la Request dans un service Symfony2</title>
                    <description>&lt;p&gt;C’est une question qui revient souvent chez les développeurs, comment injecter
la Request Symfony2 dans un service ? La réponse ne semble pas si simple à
trouver car dans de très nombreux cas, je constate que pour pallier le problème,
c’est tout le conteneur de dépendances qui est transmet au service en question.
Une très mauvaise pratique !&lt;/p&gt;

&lt;p&gt;C’était effectivement, à “une époque”, l’unique solution car Symfony gérant
plusieurs Request, le problème de l’injection est très complexe. Depuis la
version 2.4 du framework, un service “request_stack” est désormais disponible.&lt;/p&gt;

&lt;p&gt;Si vous souhaitez avoir plus d’informations sur ce service et son utilisation, je
vous renvoie vers &lt;a href=&quot;javascript:;&quot; class=&quot;broken-link&quot; rel=&quot;nofollow&quot; data-original-url=&quot;http://symfony.com/fr/doc/current/book/service_container.html#injecter-la-request &quot;&gt;la documentation du framework&lt;/a&gt;
expliquant comment utiliser la RequestStack.&lt;/p&gt;
</description>
                    <pubDate>Thu, 09 Oct 2014 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2014/10/09/injecter-la-request-dans-un-service-symfony2.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2014/10/09/injecter-la-request-dans-un-service-symfony2.html</guid>
                </item>
            
        
            
                223
                <item>
                    <title>Comment obtenir l&apos;opcode d&apos;un script PHP ?</title>
                    <description>&lt;p&gt;Si vous suivez un peu ce qui se passe dans la sphère PHP, vous avez peut-être
vu passer une explication d’Igor WIEDLER concernant l’utilisation de l’instruction
&lt;code&gt;goto&lt;/code&gt; dans une librairie qu’il a écrit. Pour justifier son choix, Igor s’est
appuyé sur l’opcode résultant de l’interprétation de son code. Je me suis alors
demandé comment est-il possible de générer l’opcode résultant d’un script PHP.&lt;/p&gt;

&lt;p&gt;Avant toute chose, je vais rappeler rapidement ce qu’est l’opcode. Lorsqu’un script
PHP est exécuté, la machine virtuelle PHP va transformer le script lu en un langage
intermédiaire qui représente des opérations de base. Ces instructions de bas niveau
sont généralement appelé “&lt;a href=&quot;javascript:;&quot; class=&quot;broken-link&quot; rel=&quot;nofollow&quot; data-original-url=&quot;http://php.net/manual/fr/internals2.opcodes.php &quot;&gt;opcode&lt;/a&gt;”.&lt;/p&gt;

&lt;p&gt;Il y a plusieurs raisons qui peuvent pousser un développeur à visualiser l’opcode
d’un fichier PHP. Tout d’abord par curiosité intellectuelle afin de comprendre
comment fonctionne le langage. Il faut également savoir que par défaut, la machine
virtuelle PHP n’optimise pas le code source qui est lu. Dans le cas, où il n’y a ni
mécanisme d’optimisation, ni cache d’opcode, il peut être intéressant de connaitre
l’opcode généré afin de faire quelques optimisations de son code.&lt;/p&gt;

&lt;p&gt;Le moyen qui est certainement le plus simple pour visualiser l’opcode d’un code PHP
est de se rendre sur le site &lt;a href=&quot;http://3v4l.org&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;3v4l&lt;/a&gt; qui
permet de tester du code sur plusieurs versions de PHP, mais il fournit également
l’opcode résultant aussi bien pour le ZendEngine (la machine virtuelle PHP) que pour
HHVM (la machine virtuelle de Facebook compatible PHP).&lt;/p&gt;

&lt;p&gt;La solution précédente est simple, mais vous oblige à posséder une connexion Internet
et à copier/coller vos différents scripts sur le site. Une autre solution consiste
alors à installer une extension PHP qui nous permettra de visualiser la représentation
interne des scripts exécutés.&lt;/p&gt;

&lt;p&gt;L’extension en question s’appelle &lt;a href=&quot;http://derickrethans.nl/projects.html#vld&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Vulcan Logic Disassembler&lt;/a&gt;.
Cette dernière s’installe facilement au travers la commande &lt;code&gt;pecl&lt;/code&gt; ou en récupérant son
code source et en le compilant.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;$ pecl install vld-beta&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Une fois l’extension installée, vous pourrez visualiser l’opcode d’un script via la commande :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;$ php -dextension=vld.so -dvld.active=1 mon-script.php

Finding entry points
Branch analysis from position: 0
Return found
filename:       /in/t0fJ6
function name:  (null)
number of ops:  2
compiled vars:  none
line     # *  op                           fetch          ext  return  operands
---------------------------------------------------------------------------------
   3     0  &amp;gt;   ECHO                                                     &amp;#39;Hello+World&amp;#39;
         1    &amp;gt; RETURN                                                   1&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Si vous vous intéressez au fonctionnement interne de PHP, je ne peux que vous conseillez
de lire &lt;a href=&quot;http://www.phpinternalsbook.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;ce livre collaboratif&lt;/a&gt;
expliquant et décrivant le fonctionne interne de PHP.&lt;/p&gt;

&lt;p&gt;Vous pourrez également en apprendre un peu plus avec le livre
&lt;a href=&quot;https://leanpub.com/developper-une-extension-php&quot; target=&quot;_balnk&quot;&gt;développer une extension PHP&lt;/a&gt;
de Pascal MARTIN, qui tient également un blog où chaque mois, il résume ce qui se passe
sur &lt;a href=&quot;http://news.php.net/php.internals&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;@internals&lt;/a&gt;, la mailing-list
des développeurs de PHP.&lt;/p&gt;

&lt;hr /&gt;

&lt;ul&gt;
  &lt;li&gt;L’&lt;a href=&quot;https://github.com/igorw/retry/issues/3&quot; target=&quot;_target&quot;&gt;issue Github&lt;/a&gt; dont il est question dans l’article&lt;/li&gt;
  &lt;li&gt;Un &lt;a href=&quot;http://blog.pascal-martin.fr/post/aller-plus-loin-avec-dump-opcodes-script-php&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;article&lt;/a&gt; de
Pascal MARTIN sur une utilisation avancée de Vulcan Logic Disassembler.&lt;/li&gt;
&lt;/ul&gt;
</description>
                    <pubDate>Tue, 07 Oct 2014 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2014/10/07/comment-obtenir-l-opcode-d-un-script-php.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2014/10/07/comment-obtenir-l-opcode-d-un-script-php.html</guid>
                </item>
            
        
            
                224
                <item>
                    <title>Un adapteur générique pour appeler les fonctions natives de PHP</title>
                    <description>&lt;p&gt;L’adaptateur (adapter en anglais) est l’un des design patterns les plus connus
et utilisés en programmation. “Il permet de convertir l’interface d’une classe
en une autre interface que le client attend. L’adaptateur fait fonctionner
ensemble des classes qui n’auraient pas pu fonctionner sans lui, à cause d’une
incompatibilité d’interfaces” (définition Wikipedia).&lt;/p&gt;

&lt;p&gt;Il ne s’agit ni plus ni moins que d’un proxy qui va se charger de faire des appels
d’une classe à un autre en transformant les données dans le format attendu. Les
adaptateurs sont très utiles pour l’écriture de tests unitaires car ils permettent
au travers de l’injection de dépendances d’être remplacé par un mock. C’est un
cas d’utilisation très pratique lorsque l’on souhaite tester des blocs de code
faisant appel à des fonctions natives de PHP.&lt;/p&gt;

&lt;p&gt;Par exemple, si l’on souhaite lire un fichier de configuration, nous pourrions
écrire le code suivant :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;public function getUserConfiguration($user, $key)
{
    $configurationFile = file_get_contents($user-&amp;gt;getConfigurationFile());

    $configuration = $this-&amp;gt;parse($configurationFile);
    return $configuration[$key];
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Le code ci-dessus pose problème, car il est impossible de contrôler l’exécution
de la méthode &lt;code&gt;file_get_contents&lt;/code&gt;. Si le test échoue, cela ne sera pas forcément
lié au code qui a été écrit, mais peut-être au système de fichiers qui est
indisponible au moment du test.&lt;/p&gt;

&lt;p&gt;On peut alors écrire le test en utilisant un adaptateur :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;public function getUserConfiguration($user, $key)
{
    $configurationFile = $this-&amp;gt;fileAdapter-&amp;gt;getFileContent($user-&amp;gt;getConfigurationFile());

    $configuration = $this-&amp;gt;parse($configurationFile);
    return $configuration[$key];
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;De cette manière, le code devient facilement testable, puisqu’il suffira
d’utiliser un mock de la classe &lt;code&gt;fileAdapter&lt;/code&gt; implémentant la méthode
&lt;code&gt;getFileContent&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;C’est d’ailleurs en jetant un oeil dans le code source
d’&lt;a href=&quot;https://github.com/atoum/atoum&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Atoum&lt;/a&gt;, un framework
de tests unitaires (que je recommande vivement) que j’ai découvert un adaptateur
générique permettant de tester naturellement les fonctions natives de PHP
(également présenté dans un très bon &lt;a href=&quot;javascript:;&quot; class=&quot;broken-link&quot; rel=&quot;nofollow&quot; data-original-url=&quot;http://jubianchi.fr/atoum-adapters.htm &quot;&gt;article de Julien BIANCHI&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Voici le code de l’adaptateur :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;// disponible à l&amp;#39;URL suivante =&amp;gt; https://github.com/atoum/atoum/blob/master/classes/adapter.php
class adapter
{
    public function __call($functionName, $arguments)
    {
        return $this-&amp;gt;invoke($functionName, $arguments);
    }

    public function invoke($functionName, array $arguments = array())
    {
        return call_user_func_array($functionName, $arguments);
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Cette classe est très astucieuse et fait appel
“&lt;a href=&quot;http://php.net/manual/fr/language.oop5.magic.php&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;aux méthodes magiques”&lt;/a&gt;
de PHP afin de rediriger les appels de l’adaptateur vers les fonctions natives du langage.
De cette manière, plus besoin de créer une multitude de classes adaptateurs pour
garantir que l’ensemble de notre code soit testable unitairement.&lt;/p&gt;

&lt;p&gt;Attention toutefois à ne pas en abuser, les adaptateurs comme tout principe de
programmation doivent être utilisé de manière judicieuse.&lt;/p&gt;
</description>
                    <pubDate>Wed, 01 Oct 2014 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2014/10/01/creer-un-adapteur-generique-pour-appeler-les-fonctions-natives-de-php.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2014/10/01/creer-un-adapteur-generique-pour-appeler-les-fonctions-natives-de-php.html</guid>
                </item>
            
        
            
                225
                <item>
                    <title>Des problèmes avec PHPStorm sous Linux ? Vérifier votre JRE</title>
                    <description>&lt;p&gt;J’utilise depuis peu un nouvel environnement de développement basé sur une
distribution Linux (Ubuntu 14.04). Ce fut l’occasion de réinstaller un stack
LAMP complète avec l’IDE par excellence, j’ai nommé PHPStorm.&lt;/p&gt;

&lt;p&gt;A priori aucun problème et tout se déroule correctement. C’est lors de
l’utilisation de PHPStorm que je me suis aperçu de nombreux petits soucis
(essentiellement graphique) mais très dérangeant pour un usage au quotidien.&lt;/p&gt;

&lt;p&gt;Par exemple, la fenêtre d’auto-complétion qui ne se positionne pas au niveau du
curseur, mais dans le coin en haut à droite de l’écran :&lt;/p&gt;

&lt;center&gt;
    &lt;img src=&quot;/img/blog/20140929-phpstorm-java/phpstorm-java-oracle.png&quot; alt=&quot;PHPStorm avec le JDK Oracle&quot; /&gt;
&lt;/center&gt;

&lt;p&gt;J’ai pourtant suivi la
&lt;a href=&quot;http://wiki.jetbrains.net/intellij/Installing_and_running_PHPStorm_on_Ubuntu&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;procédure d’installation de Jetbrains&lt;/a&gt;
en installant une version officiel du JDK d’Oracle :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;$ java -version

java version &amp;quot;1.8.0_20&amp;quot;
Java(TM) SE Runtime Environment (build 1.8.0_20-b26)
Java HotSpot(TM) 64-Bit Server VM (build 25.20-b23, mixed mode)&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Et pourtant c’est en remettant une version OpenJDK installée de base sur l’OS
que l’ensemble de mes problèmes ont disparus.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;$ java -version

java version &amp;quot;1.7.0_65&amp;quot;
OpenJDK Runtime Environment (IcedTea 2.5.2) (7u65-2.5.2-3~14.04)
OpenJDK 64-Bit Server VM (build 24.65-b04, mixed mode)&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;J’ai alors immédiatement tenté l’installation du JDK 7 d’Oracle. Et là, aucun
problème non plus. C’est donc bien la version 8 de Java qui est en défaut.&lt;/p&gt;

&lt;p&gt;Donc si vous utilisez PHPStorm sous une Ubuntu (mais peut-être que le problème
est présent sur les autres OS), pensez bien à vérifier le JDK installé sur votre
machine.&lt;/p&gt;
</description>
                    <pubDate>Mon, 29 Sep 2014 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2014/09/29/des-problemes-avec-phpstorm-sous-ubuntu-surveillez-votre-version-de-java.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2014/09/29/des-problemes-avec-phpstorm-sous-ubuntu-surveillez-votre-version-de-java.html</guid>
                </item>
            
        
            
                226
                <item>
                    <title>Monitorez vos applications Symfony2 !</title>
                    <description>&lt;p&gt;Vous venez de terminer votre application Symfony2 et de la déployer en
production. Tous vos tests (unitaires et fonctionnels) sont au vert. Pourtant
qu’est-ce qui vous assure que tout fonctionne correctement sur votre serveur ?
Afin d’obtenir des informations sur ce qui se passe, il est nécessaire de
mettre en place une solution de monitoring au sein de votre projet Symfony2.&lt;/p&gt;

&lt;p&gt;Pour cela, je ne peux que vous conseiller d’utiliser le bundle
&lt;a href=&quot;https://github.com/SoCloz/SoclozMonitoringBundle&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;SoclozMonitoringBundle&lt;/a&gt;
qui vous permettra de monitorer (simplement et rapidement) le code Symfony sur vos serveurs de
production afin de :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;vous envoyer des emails lors du lancement d’exception&lt;/li&gt;
  &lt;li&gt;profiler le code PHP et d’envoyer les informations à &lt;a href=&quot;https://github.com/etsy/statsd/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;statsd&lt;/a&gt;
(un système de statistique)&lt;/li&gt;
  &lt;li&gt;logguer les informations de profiling&lt;/li&gt;
  &lt;li&gt;ajouter des en-têtes HTTP en cas de bugs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;La mise en place (ainsi que la configuration) de ce bundle est extrêmement simple et
rapide. Il vous faudra bien sûr, commencer par déclarer la dépendance dans votre
fichier &lt;em&gt;composer.json&lt;/em&gt; :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;{
    &amp;quot;require&amp;quot;: {
        ...,
        &amp;quot;socloz/monitoring-bundle&amp;quot;: &amp;quot;dev-master&amp;quot;
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Et activer le bundle dans le kernel de Symfony :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;class AppKernel extends Kernel
{
    public function registerBundles()
    {
        // ...

        if ($this-&amp;gt;getEnvironment() == &amp;#39;prod&amp;#39;) {
            $bundles[] = new Socloz\MonitoringBundle\SoclozMonitoringBundle();
        }

        // ...

        return $bundles;
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Une fois ces deux opérations effectués, il ne vous reste plus qu’à configurer
le bundle. Par exemple, l’exemple ci-dessous enverra un mail à l’adresse
&lt;em&gt;monitoring@mon-serveur.com&lt;/em&gt; lorsqu’une exception sera levée par l’application
sauf pour les exceptions &lt;em&gt;NotFoundHttpException&lt;/em&gt; et &lt;em&gt;AccessDeniedHttpException&lt;/em&gt; :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;socloz_monitoring:
    exceptions:
        enable: true
        ignore:
            - &amp;#39;Symfony\Component\HttpKernel\Exception\NotFoundHttpException&amp;#39;
            - &amp;#39;Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException&amp;#39;
    profiler:
        enable: false
    mailer:
        enable: true
        from: system@mon-serveur.com
        to: monitoring@mon-serveur.com
    statsd:
        enable: false&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Un bundle indispensable pour garder le contrôle et améliorer la qualité de vos
projets.&lt;/p&gt;
</description>
                    <pubDate>Tue, 23 Sep 2014 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2014/09/23/monitorez-vos-applications-symfony2.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2014/09/23/monitorez-vos-applications-symfony2.html</guid>
                </item>
            
        
            
                227
                <item>
                    <title>Protéger un champ de formulaire Symfony2 avec un droit</title>
                    <description>&lt;p&gt;Il arrive fréquemment que l’on souhaite restreindre l’accès à certains champs de
nos formulaires avec un (ou plusieurs) droit(s) utilisateur. Pour cela, une
majorité de développeurs passera un paramètre dans le tableau d’options reçu
par la fonction &lt;em&gt;buildForm&lt;/em&gt; obligeant alors à modifier le code du formulaire pour
traiter l’information reçue. Nous allons voir une alternative beaucoup plus
élégante.&lt;/p&gt;

&lt;p&gt;Pour cela nous allons créer &lt;a href=&quot;javascript:;&quot; class=&quot;broken-link&quot; rel=&quot;nofollow&quot; data-original-url=&quot;http://symfony.com/fr/doc/current/cookbook/form/create_form_type_extension.html &quot;&gt;une extension de type de formulaire&lt;/a&gt;
qui se chargera de la vérification des droits et affichera ou non auprès de l’
utilisateur de notre application.&lt;/p&gt;

&lt;p&gt;Prenons pour exemple un formulaire permettant de créer un billet de blog :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&amp;lt;?php

namespace JDecool\Bundle\ForumBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

class ThreadType extends AbstractType
{
     /**
     * @param FormBuilderInterface $builder
     * @param array $options
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            -&amp;gt;add(&amp;#39;title&amp;#39;)
            -&amp;gt;add(&amp;#39;content&amp;#39;, &amp;#39;textarea&amp;#39;)
            -&amp;gt;add(&amp;#39;solved&amp;#39;, &amp;#39;checkbox&amp;#39;, [
                &amp;#39;required&amp;#39;   =&amp;gt; false,
            ])
        ;
    }

    /**
     * @param OptionsResolverInterface $resolver
     */
    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver-&amp;gt;setDefaults(array(
            &amp;#39;data_class&amp;#39; =&amp;gt; &amp;#39;JDecool\Bundle\ForumBundle\Entity\Thread&amp;#39;
        ));
    }

    /**
     * @return string
     */
    public function getName()
    {
        return &amp;#39;forum_thread&amp;#39;;
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Maintenant que notre formulaire est défini, nous allons restreindre l’affichage
du champ &lt;em&gt;solved&lt;/em&gt; uniquement au utilisateurs ayant le rôle &lt;em&gt;ROLE_ADMIN&lt;/em&gt;. Pour cela
nous allons simplement ajouter une nouvelle propriété au champ correspondant.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        -&amp;gt;add(&amp;#39;title&amp;#39;)
        -&amp;gt;add(&amp;#39;content&amp;#39;, &amp;#39;textarea&amp;#39;)
        -&amp;gt;add(&amp;#39;solved&amp;#39;, &amp;#39;checkbox&amp;#39;, [
            &amp;#39;required&amp;#39;   =&amp;gt; false,
            &amp;#39;is_granted&amp;#39; =&amp;gt; &amp;#39;ROLE_ADMIN&amp;#39;,
        ])
    ;
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Pour que ce paramètre soit interprété, nous allons créer notre extension de type
formulaire en lui injectant le &lt;a href=&quot;http://api.symfony.com/master/Symfony/Component/Security/Core/SecurityContext.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;SecurityContext&lt;/a&gt;
Symfony afin de vérifier les droits de l’utilisateur connecté.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&amp;lt;?php

namespace JDecool\Bundle\ForumBundle\Form\Extension;

use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Security\Core\SecurityContextInterface;

class SecurityTypeExtension extends AbstractTypeExtension
{
    /**
     * The security context
     * @var SecurityContextInterface
     */
    private $securityContext;

    /**
     * Object constructor
     */
    public function __construct(SecurityContextInterface $securityContext)
    {
        $this-&amp;gt;securityContext = $securityContext;
    }

    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $grant = $options[&amp;#39;is_granted&amp;#39;];
        if (null === $grant || $this-&amp;gt;securityContext-&amp;gt;isGranted($grant)) {
            return;
        }

        $builder-&amp;gt;addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
            $form = $event-&amp;gt;getForm();
            if ($form-&amp;gt;isRoot()) {
                return;
            }

            $form-&amp;gt;getParent()-&amp;gt;remove($form-&amp;gt;getName());
        });
    }

    /**
     * {@inheritdoc}
     */
    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver-&amp;gt;setDefaults(array(&amp;#39;is_granted&amp;#39; =&amp;gt; null));
    }

    /**
     * {@inheritdoc}
     */
    public function getExtendedType()
    {
        return &amp;#39;form&amp;#39;;
    }

}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Il ne reste plus qu’à enregistrer notre extension en tant que service :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;services:
    jdecool_forum_bundle.security_type_extension:
        class: JDecool\Bundle\ForumBundle\Form\Extension\SecurityTypeExtension
        arguments:
            - @security.context
        tags:
            - { name: form.type_extension, alias: form }&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Et voilà, il est maintenant possible de restreindre n’importe quel champ de
formulaire avec un droit utilisateur sans avoir besoin de donner en paramètre
l’utilisateur connecté ou à manipuler ce dernier dans notre classe de formulaire.&lt;/p&gt;

&lt;hr /&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Mise à jour du 10/09/2014:&lt;/strong&gt; Suite à de nombreuses réactions sur Twitter, je tiens
à rendre à Ceasar ce qui appartient à Ceasar. &lt;strong&gt;Je ne suis pas l’auteur de ce code&lt;/strong&gt;.
Ce code a été trouvé dans un projet sur lequel je travaillais et que j’ai souhaité
partager. Après des discussions, l’auteur est &lt;a href=&quot;http://alexandre-salome.fr/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Alexandre SALOME&lt;/a&gt;,
consultant chez SensioLabs qui avait donné cette astuce lors d’une
&lt;a href=&quot;http://alexandre-salome.fr/media/formulaires-symfony2.pdf&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;présentation à un sfPot à Paris&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
</description>
                    <pubDate>Tue, 09 Sep 2014 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2014/09/09/proteger-un-champ-de-formulaire-symfony2-avec-un-droit.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2014/09/09/proteger-un-champ-de-formulaire-symfony2-avec-un-droit.html</guid>
                </item>
            
        
            
                228
                <item>
                    <title>Utiliser une branche Github avec Composer</title>
                    <description>&lt;p&gt;Lorsque l’on travaille sur un projet avec de multiples dépendances, il arrive que l’on soit amené à faire évoluer l’une d’entre elles pour les besoins de notre développement. Nous devons alors spécifier à Composer sur qu’elle branche il doit travailler.&lt;/p&gt;

&lt;p&gt;Pour cela, nous allons prendre pour exemple une dépendance récupérée depuis un dépôt Github (le fonctionnement est similaire pour tout autre dépôt, via un Satis par exemple).&lt;/p&gt;

&lt;p&gt;Il faut donc tout d’abord spécifier à Composer sur quel dépôt il va devoir travailler. Cette configuration se fait au travers d’une section “repositories” à ajouter dans le fichier &lt;code&gt;composer.json&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
	// ...

    &quot;repositories&quot;: [
        {
            &quot;type&quot;: &quot;git&quot;,
            &quot;url&quot;: &quot;https://github.com/jdecool/my-lib&quot;
        }
    ],

    // ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Une fois le dépôt spécifié, il ne reste plus qu’à indiquer la branche de travail de votre dépendance.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
	// ...

	&quot;require&quot;: {
        // ...
        &quot;vendor/my-lib&quot;: &quot;dev-my-branch&quot;
    },

    // ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Vous pouvez maintenant effectuer un &lt;code&gt;composer update vendor/my-lib&lt;/code&gt; pour mettre à jour la dépendance avec la branche du dépôt que vous venez de configurer.&lt;/p&gt;
</description>
                    <pubDate>Fri, 05 Sep 2014 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2014/09/05/utiliser-une-branche-github-avec-composer.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2014/09/05/utiliser-une-branche-github-avec-composer.html</guid>
                </item>
            
        
            
                229
                <item>
                    <title>Protégez vos actions Symfony2</title>
                    <description>&lt;p&gt;Il est parfois excessif de créer un formulaire pour effectuer certaines actions
au sein d’une application. Il est alors d’usage d’utiliser un lien HTML
permettant d’effectuer ces opérations (c’est par exemple souvent le cas pour
supprimer un élément dans une liste). Il est alors important de sécuriser ses
actions.&lt;/p&gt;

&lt;p&gt;Pour cela, imaginons une liste d’élément s’affichant de la manière suivante :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&amp;lt;table&amp;gt;
  &amp;lt;tr&amp;gt;
    &amp;lt;td&amp;gt;Mon élement 1&amp;lt;/td&amp;gt;
    &amp;lt;td&amp;gt;&amp;lt;a href=&amp;quot;/element/1/delete&amp;quot;&amp;gt;Supprimer&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
  &amp;lt;/tr&amp;gt;

  &amp;lt;tr&amp;gt;
    &amp;lt;td&amp;gt;Mon élement 2&amp;lt;/td&amp;gt;
    &amp;lt;td&amp;gt;&amp;lt;a href=&amp;quot;/element/2/delete&amp;quot;&amp;gt;Supprimer&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
  &amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Bien sûr il ne s’agit là que d’un exemple, et dans une application réelle, il
faudrait que l’action de suppression soit effectuée au travers d’une méthode
POST ou DELETE.&lt;/p&gt;

&lt;p&gt;En supposant qu’il y ait déjà une restriction en fonction des droits de l’
utilisateur connecté, nous allons donc protéger cette action d’une attaque de type
&lt;a href=&quot;http://fr.wikipedia.org/wiki/Cross-Site_Request_Forgery&quot;&gt;CSRF&lt;/a&gt;. Pour cela nous
allons voir comment générer un jeton qui permettra d’identifier la requête qui
sera effectuée.&lt;/p&gt;

&lt;p&gt;Nous allons donc commencer par créer le jeton dans l’action qui affiche la liste
et ajouter la vérification du token dans l’action de suppression :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;// src/JDecool/MyBundle/Controller/DemoController.php

public function listAction(Request $request)
{
	// ...
	$token = $this-&amp;gt;get(&amp;#39;form.csrf_provider&amp;#39;)-&amp;gt;generateCsrfToken(&amp;#39;element_list&amp;#39;);

	return $this-&amp;gt;render(&amp;#39;MyBundle::list.html.twig&amp;#39;, [
		// ...
		&amp;#39;token&amp;#39; =&amp;gt; $token,
	]);
}

public function deleteAction(Request $request, $id)
{
	if (!$this-&amp;gt;get(&amp;#39;form.csrf_provider&amp;#39;)-&amp;gt;isCsrfTokenValid(&amp;#39;element_list&amp;#39;, $token)) {
		$this-&amp;gt;redirect(&amp;#39;message_list&amp;#39;);
	}

	// ...
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Une fois ce code ajouté, il ne restera plus qu’à passer le jeton lors de la
génération du lien de l’action.&lt;/p&gt;

&lt;p&gt;Notez également que le jeton généré ici restera valide pour l’ensemble de la
durée de la session de l’utilisateur et qu’il pourra être réutilisé. Le jeton
est également généré à l’aide du paramètre &lt;code&gt;secret&lt;/code&gt;présent dans le fichier
&lt;code&gt;app/config/parameters.yml&lt;/code&gt; d’où l’importance de modifier ce dernier.&lt;/p&gt;

&lt;p&gt;C’est une méthode simple et rapide qui vise à protéger votre application, et
pourtant peu de développeurs l’utilise.&lt;/p&gt;

</description>
                    <pubDate>Tue, 02 Sep 2014 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2014/09/02/protegez-vos-actions-symfony2.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2014/09/02/protegez-vos-actions-symfony2.html</guid>
                </item>
            
        
            
                230
                <item>
                    <title>Visualiser le code coverage des tests Atoum dans PHPStorm</title>
                    <description>&lt;p&gt;Lorsque je parle d’Atoum auprès de personnes qui ne l’utilisent pas, un des éléments qui revient très souvent (pour les utilisateurs de PHPStorm tout du moins), est l’impossibilité de visualiser la couverture du code par les tests directement dans l’IDE. Or à partir de maintenant, cela sera un argument qui ne tient plus.&lt;/p&gt;

&lt;p&gt;Effectivement, il est désormais possible de visualiser le code coverage des tests Atoum directement dans PHPStorm grâce au plugin &lt;a href=&quot;http://plugins.jetbrains.com/plugin/6167?pr=phpStorm&quot;&gt;PHPUnit code coverage&lt;/a&gt; qui est désormais compatible avec les fichiers de type “clover” générés par Atoum.&lt;/p&gt;

&lt;p&gt;Pour installer le plugin dans l’IDE, il faut aller dans les préférences de l’outil, puis dans la section “Plugin”. Cliquez sur le bouton “Browse repositories…” pour parcourir l’ensemble des plugins disponibles, et recherchez “PHPUnit code coverage” pour l’installer (il sera nécessaire de redémarrer PHPStorm).&lt;/p&gt;

&lt;center&gt;
    &lt;img src=&quot;/img/blog//20140819-atoum-phpstorm/phpstorm-plugin.png&quot; alt=&quot;Installation PHPUnit code coverage&quot; /&gt;
&lt;/center&gt;

&lt;p&gt;Pour pouvoir utiliser le plugin, nous allons maintenant devoir configurer Atoum pour que ce dernier nous génère un rapport de type “clover”. Ce rapport se présente sous la forme d’un fichier XML décrivant tous les fichiers qui ont été parcouru par nos tests et indique quelles lignes ont été executé ou non. Le plugin lit ce fichier pour retranscrire son contenu dans l’IDE.&lt;/p&gt;

&lt;p&gt;Pour générer ce rapport, il faut ajouter les lignes ci-dessous dans votre fichier &lt;strong&gt;.atoum.php&lt;/strong&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;$cloverWriter = new atoum\writers\file(__DIR__.&amp;#39;/build/atoum.clover.xml&amp;#39;);
$cloverReport = new atoum\reports\asynchronous\clover();
$cloverReport-&amp;gt;addWriter($cloverWriter);
$runner-&amp;gt;addReport($cloverReport);&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Une fois cela effectué, il ne vous reste plus qu’à configurer le plugin en lui indiquant où trouver le fichier généré. Il faut alors retourner dans les préférences de PHPStorm et ouvrir la section “PHPUnit code coverage”:&lt;/p&gt;

&lt;center&gt;
    &lt;img src=&quot;/img/blog//20140819-atoum-phpstorm/phpstorm-configuration.png&quot; alt=&quot;Configuration PHPUnit code coverage&quot; /&gt;
&lt;/center&gt;

&lt;p&gt;La configuration terminée, vous pourrez alors visualiser les blocs de votre code couvert par les tests et ceux qui ne le sont pas.&lt;/p&gt;

&lt;center&gt;
    &lt;img src=&quot;/img/blog//20140819-atoum-phpstorm/phpstorm-code-coverage.png&quot; alt=&quot;PHPStorm Atoum code coverage&quot; /&gt;
&lt;/center&gt;

&lt;p&gt;&lt;a href=&quot;http://plugins.jetbrains.com/plugin/6167?pr=phpStorm&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Plugin PHPUnit code coverage&lt;/a&gt;&lt;/p&gt;
</description>
                    <pubDate>Tue, 19 Aug 2014 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2014/08/19/visualiser-le-code-coverage-des-tests-atoum-dans-phpstorm.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2014/08/19/visualiser-le-code-coverage-des-tests-atoum-dans-phpstorm.html</guid>
                </item>
            
        
            
                231
                <item>
                    <title>Des dépôts Composer privés avec Satis</title>
                    <description>&lt;p&gt;Composer est certainement l’un des outils PHP les plus connus et utilisé
à l’heure actuelle. Il permet en effet de gérer très facilement les dépendances
d’un projet PHP. Cependant, (trop) peu de développeurs connaissent ou utilisent
Satis, l’outil de création de dépôts Composer.&lt;/p&gt;

&lt;p&gt;Pourtant Satis est un outil très pratique et permet de mettre en place une gestion
de dépôts privés. On peut également l’utiliser afin de mirrorer nos dépendances
Composer provenant de Github, ce qui pourrait permettre de continuer à travailler
même si Github venait à ne plus être disponible.&lt;/p&gt;

&lt;p&gt;Si peu de personnes utilisent Satis, c’est peut-être à cause de sa mise en place qui
peut sembler (au premier abord du moins) complexe. Pourtant, nous allons voir qu’il
n’y a rien de compliqué et que la configuration de Satis ressemble étrangement à celle
de Composer.&lt;/p&gt;

&lt;p&gt;Nous allons donc commencer par installer Satis. Bien entendu, nous allons utiliser
Composer pour cela. L’installation de Satis se fait en une ligne de commande:
&lt;code&gt;php composer.phar create-project composer/satis --stability=dev --keep-vcs&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Une fois installé, il faut configurer Satis en lui indiquant comment accéder à
nos dépôts. Voici un exemple de fichier de configuration:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;{
    &amp;quot;name&amp;quot;: &amp;quot;Satis&amp;quot;,
    &amp;quot;homepage&amp;quot;: &amp;quot;https://satis.local.dev&amp;quot;,
    &amp;quot;archive&amp;quot;: {
        &amp;quot;directory&amp;quot;: &amp;quot;dist&amp;quot;,
        &amp;quot;absolute-directory&amp;quot; : &amp;quot;/home/satis/dist&amp;quot;,
        &amp;quot;format&amp;quot;: &amp;quot;zip&amp;quot;,
        &amp;quot;skip-dev&amp;quot;: true
    },
    &amp;quot;repositories&amp;quot;: [
        { &amp;quot;type&amp;quot;: &amp;quot;composer&amp;quot;, &amp;quot;url&amp;quot;: &amp;quot;https://packagist.org&amp;quot; },
        { &amp;quot;type&amp;quot;: &amp;quot;git&amp;quot;, &amp;quot;url&amp;quot;: &amp;quot;git://git.local.dev/my-bundle&amp;quot; },
    ],
    &amp;quot;require&amp;quot;: {
        &amp;quot;symfony/symfony&amp;quot;: &amp;quot;2.5.*&amp;quot;,
        &amp;quot;doctrine/orm&amp;quot;: &amp;quot;*&amp;quot;,
        &amp;quot;private/my-bundle&amp;quot;: &amp;quot;*&amp;quot;
    },
    &amp;quot;require-all&amp;quot;: true
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Je ne vais pas détailler ici l’ensemble des éléments de la configuration de Satis, je
vous invite à lire la documentation de Composer pour cela. J’attire cependant votre
attention sur la section &lt;code&gt;require&lt;/code&gt; qui, comme dans un fichier &lt;code&gt;composer.json&lt;/code&gt;, indique
les dépendances que vous souhaitez mettre à disposition. Dans l’exemple ci-dessus, la configuration
crée un miroir des dépôts &lt;code&gt;symfony/symfony&lt;/code&gt; et &lt;code&gt;doctrine/orm&lt;/code&gt; ainsi qu’un dépôt privé
permettant d’exposer le composant &lt;code&gt;private/my-bundle&lt;/code&gt; provenant d’un dépôt Git.&lt;/p&gt;

&lt;p&gt;Pour démarrer la création du dépôt Satis, il suffit d’exécuter la commande:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;php /path/to/satis/bin/satis build /path/to/satis.conf /path/to/packages-repository&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Il ne reste plus qu’à donner un accès au dépôt que l’on vient de créer en configurant
un serveur Web:&lt;br /&gt;&lt;code&gt;php -S 0.0.0.0:4680 -t /path/to/packages-repository&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Votre dépôt Satis est maintenant configuré et vous pouvez l’utiliser dans l’ensemble
de vos projets PHP via Composer. Il faudra, au préalable, préciser l’adresse de
votre dépôt Satis dans le fichier &lt;code&gt;composer.json&lt;/code&gt; en ajoutant une section &lt;code&gt;repositories&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&amp;quot;repositories&amp;quot;: [
    {
        &amp;quot;type&amp;quot;: &amp;quot;composer&amp;quot;,
        &amp;quot;url&amp;quot;: &amp;quot;http://your-mirror-server:4680&amp;quot;
    }
],&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;a href=&quot;https://getcomposer.org/doc/articles/handling-private-packages-with-satis.md&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Documentation de Satis&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/composer/satis&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Dépôt Github&lt;/a&gt;&lt;/p&gt;
</description>
                    <pubDate>Tue, 12 Aug 2014 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2014/08/12/des-depots-composer-prives-avec-satis.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2014/08/12/des-depots-composer-prives-avec-satis.html</guid>
                </item>
            
        
            
                232
                <item>
                    <title>Tester rapidement un site mobile avec Behat</title>
                    <description>&lt;p&gt;J’utilise Behat au quotidien pour écrire mes tests fonctionnels. J’ai récemment eu besoin de mettre en place
des tests pour un site mobile conjointement aux tests existant sur le projet. N’ayant que peu de temps alloué
sur le projet, j’ai dû mettre en place des derniers très rapidement.&lt;/p&gt;

&lt;p&gt;Pour cela, la solution qui me semblait la plus évidente était de modifier le user-agent du navigateur utilisé
en le remplaçant par celui d’un appareil mobile. Or j’utilise Behat avec le driver Selenium2 qui n’autorise pas
cette modification.&lt;/p&gt;

&lt;p&gt;Les navigateurs modernes sont capables de gérer différentes configurations au travers d’un gestionnaire de profils.
Une autre solution consiste alors à utiliser ces derniers en créant un profil qui aurait le user-agent voulu.
Mes tests étant exécutés par Firefox, il suffit de démarrer ce dernier avec l’option &lt;code&gt;-p&lt;/code&gt; pour ouvrir le
gestionnaire de profil.&lt;/p&gt;

&lt;p&gt;Une fois le nouveau profil créé, nous allons accéder à l’interface de configuration de Firefox en saisissant
&lt;code&gt;about:config&lt;/code&gt; dans la barre d’adresse. Il ne reste plus qu’à ajouter une nouvelle clé de configuration (si
elle n’existe pas) nommée &lt;code&gt;general.useragent.override&lt;/code&gt; en lui donnant la valeur désirée (dans mon cas un iPhone
4S utilisant Safari &lt;code&gt;Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_0 like Mac OS X; en-us) AppleWebKit/532.9 (KHTML,
like Gecko) Version/4.0.5 Mobile/8A293 Safari/6531.22.7&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Il ne reste plus qu’à créer une archive ZIP contenant le profil que l’on vient d’ajouter. Ces derniers se
trouvent dans les dossiers suivants :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;em&gt;Mac :&lt;/em&gt; &lt;code&gt;/Users/&amp;lt;loginUtilisateur&amp;gt;/Library/Application\ Support/Firefox/Profiles/&amp;lt;identifiant.NomDuProfil&amp;gt;&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Linux :&lt;/em&gt; &lt;code&gt;~/.mozilla/firefox/&amp;lt;identifiant.NomDuProfil&amp;gt;&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Windows :&lt;/em&gt; &lt;code&gt;C:\Users\&amp;lt;loginUtilisateur&amp;gt;\AppData\Roaming\Mozilla\Firefox\Profiles\&amp;lt;identifiant.NomDuProfil&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Maintenant il ne nous reste plus qu’à configurer un nouveau profil Behat pour l’exécution de nos tests avec ce
nouvel environnement. Voici une configuration type que j’utilise dans mes projets Symfony2 :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;mobile:
    paths:
        features: features/mobile
        bootstrap: %behat.paths.features%/bootstrap
    extensions:
        Behat\Symfony2Extension\Extension:
            mink_driver: true
            kernel:
                env: test
                debug: true
        Behat\MinkExtension\Extension:
            base_url: http://localhost:8000/app_test.php
            browser_name: firefox
            goutte: ~
            selenium2:
                capabilities:
                    firefox:
                        profile: &amp;#39;/chemin/vers/le/profil/firefox-mobile.zip&amp;#39;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Vous pouvez maintenant lancer vos tests avec un navigateur mobile en exécutant behat : &lt;code&gt;bin/behat --profile mobile&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Cette installation rapide est surtout destinée à pouvoir écrire des tests fonctionnels rapidement et peut convenir
pour des projets de faibles envergures. De plus elle ne fonctionne que pour des sites Web devant fonctionner sur
mobile. Pour des tests plus pérennes ou pour des projets plus conséquents il conviendra d’utiliser des outils
spécialisés dans les tests mobiles tels que &lt;a href=&quot;https://saucelabs.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;SauceLabs&lt;/a&gt;,
&lt;a href=&quot;http://appium.io&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Appium&lt;/a&gt;, &lt;a href=&quot;http://selendroid.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Selendroid&lt;/a&gt;,
&lt;a href=&quot;https://ios-driver.github.io/ios-driver/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;ios-driver&lt;/a&gt;.&lt;/p&gt;
</description>
                    <pubDate>Wed, 06 Aug 2014 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2014/08/06/tester-rapidement-un-site-mobile-avec-behat.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2014/08/06/tester-rapidement-un-site-mobile-avec-behat.html</guid>
                </item>
            
        
            
                233
                <item>
                    <title>Présentation &quot;Introduction PHPUnit et Atoum&quot;</title>
                    <description>&lt;p&gt;Il y a quelques jours, j’ai eu l’occasion de parler des tests unitaires en PHP lors d’une
présentation chez mon employeur actuel (Novaway). L’objectif était d’introduire les concepts
fondamentaux liés aux tests unitaires et de présenter rapidement les 2 principaux frameworks
de tests PHP : PHPUnit et Atoum.&lt;/p&gt;

&lt;p&gt;J’ai réalisé les slides de la manière la plus objective possible en essayant de ne pas tenir
compte de mon opinion concernant chacun des frameworks de manière à être le plus neutre possible.&lt;/p&gt;

&lt;p&gt;&lt;span style=&quot;text-decoration: line-through;&quot;&gt;Les slides sont disponibles sur
&lt;a href=&quot;#&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer nofollow&quot;&gt;Github&lt;/a&gt;
et vous pouvez accéder à la
&lt;a href=&quot;#&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer nofollow&quot;&gt;présentation en ligne&lt;/a&gt;.&lt;/span&gt;
Les slides ne sont plus disponibles sur Github, mais sont dorénavant
&lt;a href=&quot;/talks/20140731-tests-unitaires-en-php-atoum-phpunit/index.html &quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;consultable sur ce site&lt;/a&gt;.&lt;/p&gt;
</description>
                    <pubDate>Thu, 31 Jul 2014 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2014/07/31/presentation-introduction-phpunit-atoum.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2014/07/31/presentation-introduction-phpunit-atoum.html</guid>
                </item>
            
        
            
                234
                <item>
                    <title>Atoum et la gestion du error_reporting</title>
                    <description>&lt;p&gt;J’utilise depuis quelques années maintenant Atoum pour écrire les tests unitaires sur mes différents projets
(Open Source et professionnels). Pourtant cela ne m’empêche pas d’avoir encore quelques surprises lors
de l’écriture de mes tests.&lt;/p&gt;

&lt;p&gt;Ce weekend, en écrivant des tests sur une librairie PHP “legacy”, et en profitant pour y faire un peu de
refactoring, mes tests Atoum me généraient une erreur PHP (E_STRICT) que je ne parvenais pas à reproduire via
une application test.&lt;/p&gt;

&lt;p&gt;La seule explication de ce phénomène est qu’&lt;strong&gt;Atoum surchage la configuration error_reporting de votre
environnement PHP.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Après une rapide réflexion, cela n’est en soi pas une mauvaise idée, car cela vous permet de vous assurer que
votre code ne contient réellement aucune erreur et cela indépendamment de la configuration qui sera utilisée
sur la machine qui utilisera votre code (comme l’explique Frédéric HARDY lors d’un
&lt;a href=&quot;https://twitter.com/mageekguy/status/490935784326037506&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;échange de tweet&lt;/a&gt;).&lt;/p&gt;
</description>
                    <pubDate>Mon, 21 Jul 2014 00:00:00 +0200</pubDate>
                    <link>https://www.jdecool.fr/blog/2014/07/21/atoum-error-reporting.html</link>
                    <guid isPermaLink="true">https://www.jdecool.fr/blog/2014/07/21/atoum-error-reporting.html</guid>
                </item>
            
        
    </channel>
</rss>
