Ibexa Commerce 4.6 : créer une boutique de démonstration complète
Installer Ibexa Commerce ne suffit pas pour obtenir immédiatement une boutique exploitable. Il faut encore configurer la fiscalité, créer un type de produit, publier un catalogue, ajouter les prix et les stocks, puis préparer le paiement et la livraison.
Dans ce tutoriel, nous allons construire une petite boutique fonctionnelle avec Ibexa Commerce 4.6.32. Le résultat contient trois produits physiques, un catalogue public, un panier, un checkout, un paiement hors ligne et une livraison forfaitaire. Nous irons jusqu'au traitement complet d'une commande dans le back-office.
Ce guide est basé sur une installation réellement exécutée et testée. Les pages du catalogue et des trois produits répondent avec un statut HTTP 200.
Ce que nous allons construire
La démonstration contient :
- un type de produit physique
demo_product; - trois produits avec image, description, prix et stock ;
- un catalogue publié
demo_catalog; - la devise EUR et une TVA standard à 20 % ;
- un paiement « Pay on delivery » ;
- une livraison standard à 5,90 € ;
- un storefront personnalisé avec Twig et CSS ;
- un cycle de commande complet : panier, checkout, paiement, expédition et clôture.
Les trois produits utilisés sont les suivants :
| Produit | Code | Prix | Stock |
|---|---|---|---|
| Ibexa Commerce course | IBEXA-COURSE-001 |
99,00 € | 25 |
| Commerce developer notebook | IBEXA-NOTEBOOK-001 |
29,90 € | 40 |
| Commerce architecture poster | IBEXA-POSTER-001 |
19,90 € | 60 |
1. Vérifier l'édition et la version
Depuis la racine du projet :
composer show ibexa/commerce
Dans notre cas, Composer retourne la version v4.6.32. Le métapaquet Commerce installe notamment les composants Cart, Checkout, Payment, Shipping, Storefront et Order Management.
Cette vérification est importante : Ibexa Experience et Ibexa Commerce ne fournissent pas exactement les mêmes fonctionnalités. Une instance Experience existante doit recevoir les paquets et la configuration Commerce adaptés avant de pouvoir utiliser ces migrations.
2. Démarrer les services locaux
Le projet utilise Docker pour les services techniques et le serveur Symfony pour l'application :
docker compose up -d
symfony server:start --port=8003 -d
L'application est ensuite disponible sur :
http://127.0.0.1:8003
Le back-office se trouve sous /admin et le catalogue public sous /product-catalog.
Erreur Docker : tous les pools d'adresses sont utilisés
Nous avons rencontré l'erreur suivante :
all predefined address pools have been fully subnetted
Elle signifie que Docker ne parvient plus à attribuer automatiquement un nouveau sous-réseau. Une solution propre consiste à définir explicitement les sous-réseaux du projet dans compose.yaml, avec des plages qui ne chevauchent pas les réseaux existants.
3. Configurer la région et la TVA
Un produit Commerce a besoin d'une région et d'une catégorie de TVA. Dans config/packages/ibexa.yaml, nous déclarons une TVA standard à 20 % :
ibexa:
repositories:
default:
product_catalog:
engine: default
regions:
default:
vat_categories:
standard: 20
La configuration storefront active ensuite l'euro, la région par défaut et notre catalogue :
ibexa:
system:
storefront_group:
product_catalog:
currencies:
- EUR
regions:
- default
storefront:
name: Ibexa
catalog: demo_catalog
4. Séparer les migrations de la démonstration
Les recettes Ibexa installent leurs propres migrations dans src/Migrations/Ibexa. Pour ne pas mélanger le code fourni par l'éditeur et notre démonstration, nous avons créé un dossier distinct :
src/Migrations/
├── Demo/
│ ├── 01_product_type.yaml
│ ├── 02_product.yaml
│ ├── 03_product_image.yaml
│ ├── 04_checkout.yaml
│ └── 05_additional_products.yaml
└── Ibexa/
└── ... migrations des recettes
Ibexa est configuré pour lire notre dossier dans config/packages/ibexa_migrations.yaml :
ibexa_migrations:
migration_directory: '%kernel.project_dir%/src/Migrations/Demo'
migrations_files_subdir: '.'
Cette séparation rend le dépôt plus lisible et évite de versionner des dizaines de fichiers générés sans rapport direct avec la démonstration.
5. Commerce fournit-il déjà un type de produit ?
Une installation Commerce propre fournit le moteur de catalogue et le FieldType ibexa_product_specification, mais pas notre type de produit métier prêt à l'emploi. C'est donc à nous de définir la structure adaptée au projet : produit physique, formation, abonnement, livre, etc.
Dans cette démonstration, nous avons créé nous-mêmes le type demo_product. Un type de contenu devient un type de produit Commerce lorsqu'il appartient au groupe product et contient le champ obligatoire ibexa_product_specification. Ce champ porte notamment le code produit et relie la structure de contenu aux fonctionnalités commerciales.
Groupe, type, définition de champ et FieldType : quelle différence ?
Ces notions forment une hiérarchie :
Groupe de types « product »
└── Type de produit « demo_product »
├── définition de champ « specification » → FieldType ibexa_product_specification
├── définition de champ « name » → FieldType ezstring
├── définition de champ « description » → FieldType ezrichtext
└── définition de champ « image » → FieldType ezimage
└── produit concret « IBEXA-COURSE-001 » avec ses valeurs
| Élément | Rôle | Exemple dans la démo |
|---|---|---|
| Groupe de Content Types | Classe et organise plusieurs schémas. Le service Commerce cherche ses types dans le groupe configuré product. |
product |
| Content Type / Product Type | Définit le schéma d'un produit : quels champs existent et quelles règles ils suivent. | demo_product |
| Field Definition | Champ configuré à l'intérieur du schéma : identifiant, position, obligation, traduction, validation. | le champ name, obligatoire et traduisible |
| FieldType | Composant technique qui définit la nature et le comportement d'une valeur. | ezstring, ezimage, ezrichtext |
| Produit | Instance concrète du Product Type, contenant les valeurs des champs. | IBEXA-COURSE-001 |
Le groupe product ne transforme pas, à lui seul, un Content Type en Product Type. Il sert de périmètre de classement et de recherche pour le service Commerce. La détection technique est ensuite effectuée par la présence du FieldType ibexa_product_specification.
Il faut également distinguer l'identifiant de la définition de champ et l'identifiant de son FieldType :
- identifier: specification # nom du champ dans notre schéma
type: ibexa_product_specification # moteur technique utilisé par ce champ
Nous aurions pu appeler la définition product_data, mais son type devrait toujours être ibexa_product_specification. Pour rester lisibles et proches des conventions Ibexa, nous utilisons specification.
Ce que le champ Product Specification ne contient pas
Le champ ibexa_product_specification donne au contenu son identité commerciale et porte notamment son code produit unique. Sa présence permet à Ibexa Commerce de reconnaître le Content Type comme un Product Type.
Il ne faut cependant pas confondre ce champ avec toutes les données commerciales du produit :
| Notion | Où elle est gérée | Rôle |
|---|---|---|
| Code produit | Product Specification | Identifie de manière unique le produit ou une variante. |
| Prix | Product Price | Associe un montant au produit dans une devise donnée. |
| Disponibilité et stock | Product Availability | Indique si le produit peut être vendu et la quantité disponible. |
| TVA | Région et catégorie de TVA du Product Type | Détermine le taux fiscal applicable selon la région. |
| Attributs | Configuration du Product Type | Décrivent des caractéristiques telles que la taille, la couleur ou le format et peuvent servir à générer des variantes. |
| Catalogue | Product Catalog | Sélectionne les produits visibles grâce à des règles de filtrage. |
Cette séparation explique pourquoi créer le Content Type et son champ Product Specification ne suffit pas à obtenir un article immédiatement achetable : le prix et la disponibilité doivent ensuite être créés explicitement.
Produit physique ou virtuel
Lors de la création depuis Product Catalog → Product Types, Ibexa demande la nature du Product Type. Un produit physique entre dans un parcours comprenant normalement la livraison et le stock. Un produit virtuel représente un bien immatériel et ne nécessite pas d'expédition physique.
Notre type demo_product est physique : la commande de démonstration possède donc une expédition, un mode de livraison et un statut qui évolue jusqu'à Shipped.
Le chemin recommandé dans le back-office est Product Catalog → Product Types → Create. Ibexa crée alors la structure dans le groupe de produits configuré et ajoute le champ Product Specification obligatoire. La même structure reste visible dans Content structure → Content types → Product, car un Product Type est construit au-dessus du système de Content Types d'Ibexa.
Le premier fichier crée cette structure :
- type: content_type
mode: create
metadata:
identifier: demo_product
mainTranslation: eng-GB
nameSchema: '<name>'
contentTypeGroups:
- product
fields:
- identifier: specification
type: ibexa_product_specification
required: true
translatable: false
fieldSettings:
regions:
default:
region_identifier: default
vat_category_identifier: standard
- identifier: name
type: ezstring
required: true
searchable: true
- identifier: description
type: ezrichtext
required: false
searchable: true
Nous ajoutons ensuite un champ ezimage utilisé comme miniature du produit :
- type: content_type
mode: update
match:
field: content_type_identifier
value: demo_product
fields:
- identifier: image
type: ezimage
position: 3
thumbnail: true
Le paramètre thumbnail: true permet à Ibexa d'utiliser cette image dans les cartes du catalogue.
6. Créer un produit, son prix et son stock
Un produit est créé comme un contenu du type demo_product. Le champ specification contient son code unique :
- type: content
mode: create
metadata:
contentType: demo_product
mainTranslation: eng-GB
remoteId: demo_product_ibexa_course
alwaysAvailable: true
section: 1
location:
parentLocationId: 60
fields:
- fieldDefIdentifier: specification
languageCode: eng-GB
value:
code: IBEXA-COURSE-001
- fieldDefIdentifier: name
languageCode: eng-GB
value: 'Ibexa Commerce course'
Le prix et la disponibilité sont deux étapes séparées :
- type: product_price
mode: create
product_code: IBEXA-COURSE-001
currency_code: EUR
amount: 99.00
- type: product_availability
mode: create
product_code: IBEXA-COURSE-001
is_available: true
is_infinite: false
stock: 25
Cette distinction est utile : le contenu décrit le produit, tandis que Commerce gère séparément ses données commerciales.
7. Configurer paiement et livraison
Pour une démonstration locale, un paiement hors ligne évite toute intégration bancaire :
- type: payment_method
mode: create
paymentType: offline
identifier: demo_offline_payment
enabled: true
name:
- languageCode: eng-GB
value: 'Pay on delivery'
La livraison utilise un tarif fixe de 5,90 € :
- type: shipping_method
mode: create
identifier: demo_standard_delivery
enabled: true
shippingType: flat_rate
vatCategory: standard
options:
price: '5.90'
currency: EUR
regions:
- default
Ce scénario suffit pour tester tout le checkout sans appeler un service externe.
8. Exécuter les migrations
Sur une installation Commerce neuve, les fichiers sont exécutés dans l'ordre :
php bin/console ibexa:migrations:migrate --file=01_product_type.yaml
php bin/console ibexa:migrations:migrate --file=02_product.yaml
php bin/console ibexa:migrations:migrate --file=03_product_image.yaml
php bin/console ibexa:migrations:migrate --file=04_checkout.yaml
php bin/console ibexa:migrations:migrate --file=05_additional_products.yaml
L'ordre est volontaire : le type doit exister avant les produits, et le champ image doit exister avant d'être alimenté.
9. Publier un catalogue avec une commande Symfony
La commande personnalisée suivante crée un catalogue publié filtré sur notre type de produit :
$catalog = $this->catalogService->createCatalog(new CatalogCreateStruct(
'demo_catalog',
new ProductType(['demo_product']),
['eng-GB' => 'Demo catalog'],
['eng-GB' => 'Published catalog for the Commerce demonstration.'],
Status::PUBLISHED_PLACE,
14,
));
La commande est idempotente pour le catalogue : si demo_catalog existe déjà, elle ne le recrée pas.
php bin/console app:commerce-demo:setup
Elle associe également à chaque produit son image et une description RichText. Le service local des produits est préférable à une modification SQL directe : il respecte les API et les événements Ibexa.
10. Reconstruire l'index
Après la création ou la modification de produits, il faut reconstruire l'index de recherche :
php bin/console ibexa:reindex --siteaccess=admin
php bin/console cache:clear
Sans cette étape, les produits peuvent exister dans la base tout en restant absents du catalogue ou des résultats de recherche.
11. Personnaliser le storefront sans modifier vendor/
Il ne faut jamais éditer directement les templates du paquet Storefront. Nous surchargeons le template dans le projet :
templates/themes/storefront/storefront/catalog.html.twig
templates/themes/storefront/storefront/product.html.twig
La surcharge conserve le template officiel et ajoute notre feuille de style :
{% extends '@IbexaStorefront/themes/storefront/storefront/product.html.twig' %}
{% block stylesheets %}
{{ parent() }}
<link rel="stylesheet" href="{{ asset('assets/demo/product.css') }}">
{% endblock %}
Notre CSS ajoute notamment :
- une grille de produits plus aérée ;
- des cartes avec ombre, arrondis et animation au survol ;
- une meilleure hiérarchie typographique ;
- une mise en avant du prix et du bouton d'ajout au panier ;
- un comportement responsive sur mobile.
Les personnalisations restent ainsi indépendantes du code installé par Composer.
12. Tester le parcours de commande
Le test fonctionnel complet suit ce parcours :
- ouvrir
/product-catalog; - ouvrir une fiche produit ;
- ajouter le produit au panier ;
- saisir l'adresse de livraison ;
- sélectionner « Standard delivery » ;
- sélectionner « Pay on delivery » ;
- confirmer la commande ;
- ouvrir la commande dans
/admin/order/list; - confirmer le paiement ;
- préparer puis expédier la livraison ;
- terminer la commande.
Les statuts observés suivent la progression métier :
Pending → Processing → Completed
Le paiement passe à Paid et l'expédition à Shipped. Cette vérification est plus importante qu'un simple affichage du catalogue : elle valide l'interaction entre Cart, Checkout, Payment, Shipping et Order Management.

La capture publiée utilise volontairement un nom, un e-mail, un téléphone et une adresse fictifs. Les données personnelles de la commande originale ne doivent jamais apparaître dans un article public.
13. URLs utiles
Storefront
http://127.0.0.1:8003/product-catalog
http://127.0.0.1:8003/product-catalog/ibexa-commerce-course
http://127.0.0.1:8003/product-catalog/commerce-developer-notebook
http://127.0.0.1:8003/product-catalog/commerce-architecture-poster
Back-office
http://127.0.0.1:8003/admin/product/list
http://127.0.0.1:8003/admin/product-type/list
http://127.0.0.1:8003/admin/catalog
http://127.0.0.1:8003/admin/order/list
http://127.0.0.1:8003/admin/payment-method
http://127.0.0.1:8003/admin/shipping_method
Les routes /product/list et /order/list sans préfixe /admin ne sont pas des pages publiques. Elles peuvent provoquer une erreur de SiteAccess ou tenter de charger un template storefront qui n'est pas prévu pour le back-office.
14. Garder un dépôt Git propre
Le .gitignore exclut les éléments locaux ou régénérables :
/.idea/
/.vscode/
/public/var/
/public/assets/ibexa/
/public/assets/storefront/
/public/assets/richtext/
/public/assets/translations/
/src/Migrations/Ibexa/
En revanche, le CSS de la démo, les images utilisées par les produits, la commande Symfony et src/Migrations/Demo restent versionnés. Ils sont nécessaires pour reproduire le résultat.
15. Commandes de contrôle
Avant de livrer ou de commiter :
php bin/console lint:yaml config/packages/ibexa_migrations.yaml src/Migrations/Demo
php bin/console lint:twig templates/themes/storefront/storefront
php bin/console ibexa:reindex --siteaccess=admin
git diff --check
Un contrôle HTTP simple permet aussi de vérifier les pages :
curl -I http://127.0.0.1:8003/product-catalog
Conclusion
Cette démonstration montre qu'Ibexa Commerce ne se limite pas à une liste de produits. Son modèle sépare clairement le contenu éditorial, les données commerciales, les catalogues, la tarification, le stock, le paiement, la livraison et les commandes.
La bonne approche consiste à automatiser les données reproductibles avec des migrations et une commande Symfony, puis à personnaliser le storefront dans le projet sans modifier les paquets installés. Nous obtenons ainsi une base propre pour aller plus loin : promotions, variantes, catégories, comptes clients, paiement en ligne ou intégration à une instance multisite existante.
