Le catalogue produit dynamique en profondeur
📦 Chapitre 23 — Le catalogue produit dynamique en profondeur
Le catalogue produit dynamique transforme votre table llx_product en pages web auto-générées. Ce chapitre détaille toute la mécanique côté développeur : architecture, classes, configuration, extensibilité.
🏗️ Architecture en couches
Couche | Rôle |
|---|---|
📊 StudioProductCatalog | Lit les produits Dolibarr (filtres, tris, multilingue), expose un format normalisé. |
🔧 StudioSolutionWrapper | Génère / supprime les wrappers Apache
en fonction des produits publiés. |
📄 page<X>.tpl.php (template solution-detail) | Le template unique qui affiche un produit. Une seule page Dolibarr Website pour N produits. |
🪝 Trigger PRODUCT_*/CATEGORY_* | Régénère les wrappers automatiquement à chaque modification de produit ou catégorie. |
⏰ Cron horaire | Filet de sécurité — relance la génération si un trigger a été manqué. |
📋 Le modèle de données produit
Chaque produit Dolibarr utilisé dans le catalogue exploite plusieurs tables :
Table | Données |
|---|---|
| Champs natifs : ref, label, description, prix, tosell. |
| Traductions natives (label, description par locale). |
| Extrafields canoniques : tagline, hero_image, badge, cta_label, cta_url, deployment, compatibility, support, languages, features (JSON), pricing_tiers (JSON), infrasstudio_published. |
| Overrides locale des extrafields traduisibles. |
| Liens produit ↔ catégories (pour la cartographie univers). |
🧩 Provisionner les extrafields
Les 11 extrafields nécessaires ne sont pas dans Dolibarr de base. Pour les créer en une commande, utilisez le script preset générique :
php htdocs/custom/infrasstudio/scripts/preset_default.php \
htdocs/custom/infrasstudio/presets/keaticweb.json
Le fichier JSON livre les définitions par défaut (voir Chapitre 28 pour le format). Vous pouvez créer votre propre preset si vous avez besoin d'autres extrafields.
📊 Utiliser StudioProductCatalog
La classe expose plusieurs méthodes pour interroger les produits :
dol_include_once('/infrasstudio/class/studioproductcatalog.class.php');
$catalog = new StudioProductCatalog($db);
// Tous les produits publiés, locale courante
$products = $catalog->fetchPublishedProducts(array(), $iso2);
// Filtres
$products = $catalog->fetchPublishedProducts(array(
'univers' => 'supply-chain',
'type' => 'saas',
), $iso2);
// Un produit par ref
$product = $catalog->fetchProductBySlug('supplyflow-pro', $iso2);
// Univers utilisés (pour le filtre du catalogue)
$univers = $catalog->fetchUsedUnivers();
Format normalisé d'un produit
array(
'id' => 5,
'ref' => 'supplyflow-pro',
'label' => 'SupplyFlow Pro', // résolu selon locale
'description' => '<p>Description...</p>',
'price' => 290.00,
'badge' => 'Nouveau',
'hero_image' => '/medias/...',
'cta_label' => 'Demander une démo',
'cta_url' => '/contact',
'tagline' => 'Optimisez votre supply chain...',
'deployment' => 'Cloud SaaS',
'compatibility'=> 'SAP, Oracle, Sage',
'support' => '24/7',
'languages' => 'fr,en,de',
'features' => array('Prévision IA', 'Multi-entrepôts'),
'pricing_tiers'=> array(...),
'univers' => 'supply-chain',
'type' => 'saas',
)
🌍 Cartographie catégorie → univers
Les produits sont rattachés à des catégories Dolibarr. Le module mappe ces catégories à un univers (concept éditorial : Supply Chain, Health, Legal, …).
Mapping par défaut
// Les 6 univers livrés par défaut
'supply-chain' => array(...catégories supply chain),
'health' => array(...catégories santé),
'legal' => array(...catégories juridique),
'digital-humanities' => array(...catégories sciences humaines),
'transversal' => array(...catégories outils transversaux),
'fsm' => array(...catégories field service)
Override JSON
Pour personnaliser, posez la constante :
// dolibarr_set_const ou via setup admin
$conf->global->INFRASSTUDIO_PRODUCT_UNIVERS_MAP = json_encode(array(
'mon-univers' => array(12, 13, 14), // IDs catégories
'autre' => array(15, 16),
));
🔧 StudioSolutionWrapper
Cette classe gère la génération des wrappers Apache à partir des produits publiés.
Générer manuellement
dol_include_once('/infrasstudio/class/studiosolutionwrapper.class.php');
require_once DOL_DOCUMENT_ROOT . '/website/class/website.class.php';
$website = new Website($db);
$website->fetch(0, 'monsite');
$wrapper = new StudioSolutionWrapper($db);
$stats = $wrapper->rebuildAll($website, '/var/www/monsite');
print_r($stats);
// array('created' => 3, 'updated' => 2, 'deleted' => 1, 'skipped' => 5)
Anatomie d'un wrapper généré
<?php
// /var/www/monsite/solution-supplyflow-pro.php
// Generated by StudioSolutionWrapper — do not edit manually.
$solution_ref = 'supplyflow-pro';
$infrasstudio_current_product_ref = 'supplyflow-pro';
global $dolibarr_main_data_root, $conf;
if (empty($dolibarr_main_data_root)) {
$res = include './page42.tpl.php'; // page solution-detail
} else {
$res = include $dolibarr_main_data_root
. ($conf->entity > 1 ? '/' . $conf->entity : '')
. '/website/monsite/page42.tpl.php';
}
if ($res === false) { http_response_code(500); print 'Failed to make include'; }
✅ Sécurité anti-collision — Le module ne touche qu'aux wrappers qu'il a lui-même créés (présence du marqueur "StudioSolutionWrapper" dans le header). Les pages Dolibarr standards portant solution-* sont préservées.
⚙️ Configuration du wrapper builder
Constante | Rôle |
|---|---|
| Référence du site cible. |
| Docroot Apache absolu (ex.
). |
| Préfixe des wrappers (défaut :
). Ex :
,
. |
| Pageurl du template solution-detail (défaut :
). |
📄 Le template solution-detail
C'est une page Dolibarr Website normale (avec un slug, un tpl.php), mais utilisée par tous les wrappers. Elle reçoit la variable globale $solution_ref qui identifie le produit à afficher.
Squelette typique
<?php
// page42.tpl.php (slug='solution-detail', type_container='page')
require_once __DIR__ . '/master.inc.php';
require_once DOL_DOCUMENT_ROOT . '/core/lib/website.lib.php';
require_once DOL_DOCUMENT_ROOT . '/core/website.inc.php';
dol_include_once('/infrasstudio/class/studioproductcatalog.class.php');
$catalog = new StudioProductCatalog($db);
$iso2 = infrasstudio_current_lang();
$product = $catalog->fetchProductBySlug($solution_ref, $iso2);
if (!$product) {
http_response_code(404);
print '<h1>Produit introuvable</h1>';
exit;
}
ob_start();
try {
?>
<html lang="<?php echo $iso2; ?>">
<head>
<title><?php echo dol_escape_htmltag($product['label']); ?></title>
</head>
<body>
<section class="hero">
<h1><?php echo dol_escape_htmltag($product['label']); ?></h1>
<p><?php echo dol_escape_htmltag($product['tagline']); ?></p>
<img src="<?php echo $product['hero_image']; ?>" alt="...">
</section>
<section class="features">
<ul>
<?php foreach ($product['features'] as $feature): ?>
<li><?php echo dol_escape_htmltag($feature); ?></li>
<?php endforeach; ?>
</ul>
</section>
</body>
</html>
<?php
} catch (Exception $e) { print $e->getMessage(); }
include dol_buildpath('/infrasstudio/core/tpl/website_output.tpl.php', 0);
💡 Mixer slots et données produit — Vous pouvez mélanger : les zones « éditoriales » (titre marketing au-dessus de la grille) en slots, et les zones « produit » (label, prix, features) lues via StudioProductCatalog. Les deux cohabitent sans conflit.
🪝 Le trigger PRODUCT_*/CATEGORY_*
Le module installe un trigger qui écoute :
PRODUCT_CREATEPRODUCT_MODIFYPRODUCT_DELETEPRODUCT_PRICE_MODIFYCATEGORY_LINK(uniquement si l'objet lié est un produit)CATEGORY_UNLINK
À chaque événement, le trigger appelle StudioSolutionWrapper::rebuildAll() sur le site configuré.
ℹ️ Abort silencieux — Si INFRASSTUDIO_WEBSITE_KEY ou INFRASSTUDIO_PUBLIC_DOCROOT ne sont pas configurés, le trigger sort silencieusement. Pas de bruit, pas d'erreur. Pratique pour les instances Dolibarr qui n'utilisent pas le catalogue dynamique.
⏰ Le cron horaire de filet de sécurité
Une tâche cron tourne toutes les heures et appelle StudioSolutionWrapper::rebuildAllConfigured(). Elle lit les constantes et relance la régénération.
Utilité :
- Rattrapage si un trigger a été manqué (import en masse, modification SQL directe, …).
- Synchronisation après une migration.
- Garantie de cohérence sans intervention manuelle.
⌨️ CLI : rebuild manuel
php htdocs/custom/infrasstudio/scripts/rebuild_solution_wrappers.php \
<ref-site-ou-id> <docroot-public> [entity]
# Exemple
php htdocs/custom/infrasstudio/scripts/rebuild_solution_wrappers.php \
monsite /var/www/monsite 2
📋 Récapitulatif
✅ Vous savez maintenant :
- Comprendre l'architecture catalogue (StudioProductCatalog + StudioSolutionWrapper + tpl + trigger + cron).
- Provisionner les extrafields produit avec le preset générique.
- Interroger les produits via
StudioProductCatalog. - Mapper catégories Dolibarr vers univers via
INFRASSTUDIO_PRODUCT_UNIVERS_MAP. - Comprendre comment
StudioSolutionWrapper::rebuildAll()génère les wrappers Apache. - Configurer prefix et template pageurl par site.
- Écrire le template
solution-detailqui sert tous les produits. - Comprendre le rôle du trigger automatique et du cron de filet de sécurité.
- Lancer un rebuild manuel via CLI.
🎉 Fin de la Partie IV — Vous maîtrisez maintenant l'intégration côté développeur : préparer un site, annoter avec slots, comprendre la grammaire, brancher des données Dolibarr, gérer le multilingue, créer vos gabarits, exploiter le catalogue dynamique. Vous pouvez livrer un site clé en main.
La Partie V aborde l'administration et la maintenance. Si vous êtes admin Dolibarr, c'est votre prochaine destination.