<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Php | Julien Maxant</title><link>https://www.julien-maxant.com/tags/php/</link><description>Articles, projets et veille sur le thème « Php ».</description><generator>Hugo -- gohugo.io</generator><language>fr-FR</language><copyright>2026 - Julien Maxant</copyright><lastBuildDate>Fri, 15 May 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://www.julien-maxant.com/tags/php/index.xml" rel="self" type="application/rss+xml"/><item><title>Mise en place d'une navigation multi-audience pour un site institutionnel</title><link>https://www.julien-maxant.com/projets/navigation-multi-audience/</link><guid>https://www.julien-maxant.com/projets/navigation-multi-audience/</guid><pubDate>Fri, 15 May 2026 00:00:00 +0000</pubDate><description>Étude de cas : cadrer un besoin éditorial multi-audience en un système de navigation et classification unique, sans multiplier menus ni taxonomies.</description><content:encoded><![CDATA[<h2 id="contexte">Contexte</h2>
<ul>
<li>Refonte de site institutionnel</li>
<li>Drupal 10+ (10.3.x à sa mise en ligne)</li>
<li>Site multilingue</li>
<li>Trafic international important (notamment saisonnier), allant jusqu&rsquo;à 15 000 visites uniques quotidiennes</li>
</ul>
<p>Les contenus du site sont structurés en trois espaces distincts par public cible :</p>
<ul>
<li>grand public (B2C)</li>
<li>professionnels (B2B)</li>
<li>relations publiques (PR)</li>
</ul>
<p>Dans chacune de ces sections, les gabarits et comportements des types contenus sont partagés, il n&rsquo;est donc ni pertinent ni requis de créer des types de contenus dédiés.</p>
<p>Les éléments de navigation du site (menu principal et breadcrumb) doivent refléter cette organisation, c&rsquo;est-à-dire que l&rsquo;élément &ldquo;Accueil&rdquo; du breadcrumb doit renvoyer vers la page d&rsquo;accueil de la section.</p>
<p>Le besoin éditorial, formulé simplement : permettre au contributeur de rattacher chaque contenu à l&rsquo;une des trois sections, pour que :</p>
<ul>
<li>le fil d&rsquo;Ariane affiche la bonne page d&rsquo;accueil de rattachement ;</li>
<li>le menu du header reflète l&rsquo;espace courant.</li>
</ul>
<h2 id="les-contraintes">Les contraintes</h2>
<ul>
<li>Un menu de header en deux temps : un premier bloc pour naviguer entre les trois espaces, un second pour les rubriques de l&rsquo;espace courant. Entre ces deux menus, des éléments statiques s&rsquo;intercalent, cassant le flux du rendu et compromettant l&rsquo;intégration du menu. Le rendu du menu ne peut donc pas se reposer sur le rendu natif de Drupal et impliquerait une préparation en amont de l&rsquo;affichage (<code>hook_preprocess</code>, duplication de blocks ou manipulation en javascript)</li>
<li>Une classification de contenu à gérer sur chaque page pour piloter ces deux menus et le fil d&rsquo;Ariane</li>
<li>Un besoin de simplification de la contribution</li>
<li>Côté technique, un choix délibéré de limiter autant que possible le recours aux hooks</li>
<li>Cette notion doit aussi s&rsquo;appliquer sur des pages techniques (Views, page de terme de taxonomie ou <code>Controller</code>)</li>
</ul>
<p>L&rsquo;approche la plus directe — une taxonomie dédiée en parallèle du menu, ou un menu par espace — revenait à maintenir deux structures pour une seule idée (l&rsquo;appartenance à un espace), avec le risque classique de désynchronisation entre les deux au fil des évolutions.</p>
<h2 id="les-choix-écartés">Les choix écartés</h2>
<ul>
<li>Un menu par section : multiplication du coût en maintenance et impact possible de performance (×3), requiert des hooks preprocess pour gérer l&rsquo;élément actif du menu des landing pages de sections.</li>
<li>Catégorisation par vocabulaire de taxonomie : deux sources de vérité alimentées manuellement (menu et termes de taxonomie), avec le risque de désynchronisation</li>
</ul>
<h2 id="décisions-techniques">Décisions techniques</h2>
<p>Chaque contenu porte un champ optionnel qui référence l&rsquo;un des trois éléments racine du menu principal (basé sur le plugin_id du <code>MenuLinkTreeElement</code>).
Laissé vide, il prend pour valeur par défaut, par ordre de priorité :</p>
<ol>
<li>Son élément parent, s&rsquo;il est présent dans le menu</li>
<li>La page d&rsquo;accueil générale</li>
</ol>
<p>Cette valeur est calculée dans un <code>ContextProvider</code>, qui l&rsquo;expose dans les blocks de menu. Deux blocks sont déclarés, tous deux basés sur le menu principal :</p>
<ul>
<li>Le menu des sections (B2C, B2B, PR)</li>
<li>La navigation dans les enfants (niveau 1 et plus)</li>
</ul>
<p>Le <code>ContextProvider</code> devient la source de vérité unique de la section active et permet de piloter l&rsquo;affichage et le rendu des différents éléments via une couche d&rsquo;abstraction rendant cette valeur accessible à l&rsquo;ensemble du site, pas seulement les nodes.</p>
<p>À partir de cette valeur :</p>
<ul>
<li>le fil d&rsquo;Ariane utilise la landing page correspondante comme racine, plutôt que systématiquement <code>/</code> ;</li>
<li>le premier bloc du menu header affiche les liens vers les deux autres espaces (ex. dans l&rsquo;espace B2B, il pointe vers B2C et PR) ;</li>
<li>le second bloc affiche les enfants de l&rsquo;élément racine de l&rsquo;espace courant.</li>
</ul>
<p>Le rendu dépend de cette valeur, pas de la route ni de l&rsquo;utilisateur connecté — les contextes de cache natifs de Drupal (route, utilisateur, langue&hellip;) n&rsquo;en savent rien. Deux conséquences concrètes sans contexte dédié :</p>
<ul>
<li>le filtrage du menu (accès autorisé ou refusé par élément selon l&rsquo;espace, via <code>AccessResult</code>) n&rsquo;est associé à aucun contexte de cache existant : Drupal peut resservir un menu déjà filtré pour le mauvais espace ;</li>
<li>changer l&rsquo;espace d&rsquo;un contenu produit deux jeux de contextes de cache, avant et après, sans rien en commun aux yeux de Drupal — qui lève une erreur de redirection de cache.</li>
</ul>
<p>Le cache context personnalisé rend l&rsquo;espace explicite dans la clé de cache : le menu filtré n&rsquo;est plus resservi au
mauvais espace, et le changement d&rsquo;espace d&rsquo;un contenu ne provoque plus d&rsquo;erreur de redirection de cache.</p>
<p>Le <code>ContextProvider</code> devient le point central de toute la mécanique, sans taxonomie parallèle à synchroniser ou de menu multiple à contribuer.</p>
<h2 id="résultat">Résultat</h2>
<p>En production, sans incident depuis la mise en ligne (septembre 2025), sur un site à fort trafic. La classification est prise en main par les équipes éditoriales sans formation particulière — un seul champ à renseigner, dont le sens (quel espace) leur est déjà familier puisqu&rsquo;il reprend directement la structure du menu qu&rsquo;elles connaissent.</p>
]]></content:encoded></item></channel></rss>