Modéliser commandes et lignes de commande
Structurez les commandes et les lignes de commande pour suivre les achats des utilisateurs.
Introduction
Dans cette leçon, tu vas apprendre à modéliser deux entités clés d’un site e-commerce : les commandes et leurs lignes de commande. L’objectif est de créer des collection types Strapi qui garantissent :
- l’intégrité des données (prix, quantités, statuts),
- la relation entre commandes et lignes,
- la liaison avec l’utilisateur et les produits.
Ceci servira de fondation à la gestion du panier, à la génération des factures, et à l’historique des achats dans ton app Remix + Strapi 5 headless.
Modélisation dans Strapi
Définir le collection type commande
On commence par le fichier de schéma Strapi. Il se trouve typiquement dans
src/api/commande/content-types/commande/schema.json.
1{2"kind": "collectionType",3"collectionName": "commandes",4"info": {5"singularName": "commande",6"pluralName": "commandes",7"displayName": "Commande"8},9"options": {10"draftAndPublish": false11},12"attributes": {13"totalPrice": {14"type": "integer"15},16"lines": {17"type": "relation",18"relation": "oneToMany",19"target": "api::ligne-de-commande.ligne-de-commande"20},21"user": {22"type": "relation",23"relation": "manyToOne",24"target": "plugin::users-permissions.user",25"inversedBy": "orders"26},27"orderStatus": {28"type": "enumeration",29"enum": ["en attente de paiement","payé"],30"default": "en attente de paiement",31"required": true32}33}34}
totalPricestocke la somme de toutes les lignes.linescrée une relation 1⇾n versligne-de-commande.userrelie chaque commande à un utilisateur Strapi natif.orderStatusest un enum qui contrôle le flux de paiement.
Draft & publish
On désactive draftAndPublish car une commande doit être validée
immédiatement : pas de brouillon.
Définir le collection type ligne-de-commande
Ensuite, on modélise chaque article acheté. Le fichier est
src/api/ligne-de-commande/content-types/ligne-de-commande/schema.json.
1{2"kind": "collectionType",3"collectionName": "ligne_de_commandes",4"info": {5"singularName": "ligne-de-commande",6"pluralName": "ligne-de-commandes",7"displayName": "Ligne de commande"8},9"options": {10"draftAndPublish": true11},12"attributes": {13"produit": {14"type": "relation",15"relation": "oneToOne",16"target": "api::produit.produit"17},18"quantity": {19"type": "integer",20"default": 1,21"required": true22},23"price": {24"type": "integer",25"default": 026}27}28}
produitest un lien 1⇾1 vers ton contenuproduit.quantityetpricepermettent de conserver l’historique, même si le prix du produit change plus tard.draftAndPublishest activé pour pouvoir ébaucher des lignes pendant le processus de checkout.
Tip
Pense à calculer totalPrice en backend via une fonction
lifecycle (beforeCreate, beforeUpdate) pour éviter les manipulations
côté client.
Relier les deux modèles
La relation oneToMany sur commande.lines s’appuie sur la clé
étrangère générée dans ligne-de-commande. Strapi synchronise
automatiquement les IDs.
En REST, tu pourras obtenir une commande complète avec :
1GET /api/commandes?populate=lines.produit,user
populategarantit que tes données associées (produit, user) sont incluses.
Intégration dans Remix
Pour exploiter ces modèles dans ton app Remix, crée des types TypeScript puis un loader :
1export interface LigneCommande {2id: number3produit: { id: number; name: string; price: number }4quantity: number5price: number6}78export interface Commande {9id: number10totalPrice: number11orderStatus: "en attente de paiement" | "payé"12lines: LigneCommande[]13user: { id: number; username: string }14}
1import type { LoaderArgs } from "@remix-run/node"2import { json } from "@remix-run/node"34export const loader = async ({ params, request }: LoaderArgs) => {5const res = await fetch(6`${process.env.API_URL}/commandes/${params.id}?populate=lines.produit,user`7)8const data = await res.json()9return json<Commande>(data.data)10}
1import { useLoaderData } from "@remix-run/react"23export default function CommandePage() {4const commande = useLoaderData<Commande>()5return (6<div>7<h2>Détails de la commande #{commande.id}</h2>8<ul>9{commande.lines.map((line) => (10<li key={line.id}>11{line.produit.name} × {line.quantity} = {line.price} €12</li>13))}14</ul>15<p>Total : {commande.totalPrice} €</p>16<p>Status : {commande.orderStatus}</p>17</div>18)19}
Sécurité
N’oublie pas de sécuriser la route avec authentification Remix.
Points clés
- Utiliser enum pour contrôler les statuts et éviter les chaînes libres.
- Préférer
draftAndPublish: falsepour les commandes en production. - Calculer
totalPricecôté serveur pour fiabilité. populatepermet de récupérer les relations en un seul appel.- Définir des types TS pour exploiter la puissance du typage en front.
Exercices
-
Ajouter un statut “expédié”
Mets à jour l’énumérationorderStatuspour inclure"expédié"
puis teste le endpoint REST pour valider ton schéma. -
Appliquer une remise
Ajoute un champdiscountCode(relation vers un nouveaupromo-code) et un champdiscountAmount.
Modifie letotalPricepour en tenir compte. -
Page utilisateur
Crée une route Remix/mon-compte/commandes
qui liste toutes les commandes de l’utilisateur connecté.
Filtre via?filters[user][id][$eq]=:userId.
Pour aller plus loin, explore la leçon suivante sur la gestion du panier en React Context.