Modèles
Les modèles dans Swell, adaptez-les à votre projet.
Les modèles de données
Swell propose une collection de modèles prêts à l'emploi, conçus pour couvrir les besoins courants d'un projet e-commerce moderne.
Ces modèles servent de base solide, mais restent entièrement personnalisables afin de s'adapter à la structure et aux spécificités de votre application.
Vous pouvez ainsi les modifier ou en créer de nouveaux selon les exigences de votre domaine métier, tout en profitant d'une intégration fluide avec l'écosystème Swell.
Adapter un modèle
Les modèles Swell sont situés dans le dossier app/Models, à l'exception des fonctionnalités
optionnelles dont les modèles sont placés dans app/Modules/{ModuleName}/Models.
Ils sont associés à leurs fichiers de migration respectifs dans database/migrations ou
app/Modules/{ModuleName}/database/migrations, permettant de définir la structure des tables correspondantes.
Swell n'impose aucune logique métier. Les modèles proposés servent de socle initial, à adapter librement. Pour toute modification de structure ou comportement, référez-vous à la documentation officielle de Laravel.
Modèles Core
Ces modèles constituent le cœur de l'application e-commerce et sont situés dans app/Models/.
Brand
Représente une marque de produits.
| Champ | Type |
|---|---|
name | string |
slug | string unique |
logo_url | string nullable |
Relations :
hasMany(Product)- Une marque possède plusieurs produits
Category
Représente une catégorie de produits avec support hiérarchique (catégories/sous-catégories).
| Champ | Type |
|---|---|
name | string |
slug | string unique |
description | text nullable |
status | boolean default:true |
parent_id | nullable nullOnDelete |
Relations :
belongsTo(Category)- Parent category (pour hiérarchie)hasMany(Category)- Sous-catégories enfantshasMany(Product)- Produits de cette catégorie
Le champ parent_id permet de créer une arborescence de catégories/sous-catégories.
Product
Modèle principal représentant un produit avec support des variantes, images, et intégration Algolia.
| Champ | Type |
|---|---|
name | string |
slug | string unique nullable |
sku | string unique |
description | text |
short_description | string max:500 |
price | decimal:10,2 |
discount_price | decimal:10,2 nullable |
cost_price | decimal:10,2 |
stock | unsignedInteger default:0 |
reorder_level | unsignedInteger default:10 |
sales_count | unsignedInteger default:0 |
status | boolean default:true |
meta_title | string nullable |
meta_description | string max:255 nullable |
meta_keywords | string max:255 nullable |
brand_id | integer restrictOnDelete |
category_id | integer nullOnDelete |
collection_id | integer nullable nullOnDelete |
Relations :
belongsTo(Category)- Catégorie du produitbelongsTo(Brand)- Marque du produitbelongsTo(Collection)- Collection associée (optionnel)hasMany(ProductImage)- Images du produithasOne(ProductImage)- Image featured (viafeaturedImage())hasMany(ProductOption)- Options/variantes (taille, couleur, etc.)hasMany(Review)- Avis clients (module Review)hasMany(OrderItem)- Éléments de commandesbelongsToMany(Wishlist)- Listes de souhaits (module Wishlist)
Méthodes utiles :
getPrice()- Retournediscount_pricesi défini, sinonpriceisOutOfStock()- Vérifie si le stock est épuiséincrementSales($quantity)- Incrémente le compteur de ventesscopePopular($limit)- Scope pour récupérer les produits populaires
Traits :
Searchable(Laravel Scout) - Synchronisation automatique avec AlgoliashouldBeSearchable()- Seuls les produits avecstatus = truesont indexés
Collection
Regroupe plusieurs produits sous une collection thématique (ex: "Nouveautés", "Soldes d'été").
| Champ | Type |
|---|---|
title | string |
slug | string unique |
Relations :
hasMany(Product)- Produits de cette collection
Les collections permettent d'associer plusieurs produits entre eux pour des mises en avant thématiques ou temporelles.
ProductImage
Images associées à un produit avec support d'image principale (featured).
| Champ | Type |
|---|---|
product_id | integer cascadeOnDelete |
image_url | string |
alt_text | string nullable |
is_featured | boolean default:false |
order | unsignedInteger |
Relations :
belongsTo(Product)- Produit associé
Une seule image peut avoir is_featured = true par produit. Elle est utilisée comme image principale dans les listings.
ProductOption
Options/variantes disponibles pour un produit (ex: "Taille", "Couleur").
| Champ | Type |
|---|---|
product_id | integer cascadeOnDelete |
name | string |
Relations :
belongsTo(Product)- Produit associéhasMany(ProductOptionValue)- Valeurs possibles pour cette option
Exemple d'utilisation :
// Option: Taille
// Values: S, M, L, XL
// Option: Couleur
// Values: Rouge, Bleu, NoirProductOptionValue
Valeurs possibles pour une option de produit.
| Champ | Type |
|---|---|
product_option_id | integer cascadeOnDelete |
value | string |
Relations :
belongsTo(ProductOption)- Option associée
Cart
Panier d'achat (session pour invités, persistant pour utilisateurs authentifiés).
| Champ | Type |
|---|---|
session_id | string nullable |
user_id | integer nullable cascadeOnDelete |
Relations :
belongsTo(User)- Utilisateur (si authentifié)hasMany(CartItem)- Éléments du panier
Swell utilise CartFactory pour gérer automatiquement les paniers invités (session) vs utilisateurs authentifiés.
CartItem
Élément individuel dans un panier avec support des options de produit.
| Champ | Type |
|---|---|
cart_id | integer cascadeOnDelete |
product_id | integer cascadeOnDelete |
quantity | unsignedInteger default:1 |
options | json nullable |
Relations :
belongsTo(Cart)- Panier associébelongsTo(Product)- Produit ajouté
Casts :
options→array- Options sélectionnées pour ce produit (ex:{"Taille": "M", "Couleur": "Bleu"})
Order
Commande finalisée avec génération automatique du numéro de commande.
| Champ | Type |
|---|---|
order_number | string unique |
stripe_checkout_session_id | string |
amount_discount | unsignedInteger |
amount_subtotal | unsignedInteger |
amount_total | unsignedInteger |
billing_address | json |
shipping_address | json |
user_id | integer cascadeOnDelete |
Relations :
belongsTo(User)- Client de la commandehasMany(OrderItem)- Produits commandés
Casts :
billing_address→collectionshipping_address→collection
Génération automatique :
Le order_number est généré au format : {PREFIX}-{YEAR}-{RANDOM} (ex: ORD-2025-A3F8K9L2)
- Prefix configurable via
SWELL_ORDER_PREFIX(défaut:ORD)
Méthodes utiles :
total()- Calcule le total de la commande depuis les items
OrderItem
Produit individuel dans une commande (snapshot pour historique).
| Champ | Type |
|---|---|
order_id | integer |
product_id | integer |
name | string |
description | text |
price | unsignedInteger |
quantity | unsignedInteger |
amount_discount | unsignedInteger |
amount_total | unsignedInteger |
Relations :
belongsTo(Order)- Commande associéebelongsTo(Product)- Référence au produit
Les détails du produit sont copiés dans OrderItem pour préserver l'historique même si le produit est modifié ou supprimé ultérieurement.
Modèles des Modules
Ces modèles font partie des fonctionnalités optionnelles et sont situés dans app/Modules/{ModuleName}/Models/.
BannerMessage (Module Banner)
Messages d'information affichés sur le site.
| Champ | Type |
|---|---|
message | string |
is_active | boolean |
order | unsignedInteger default:0 |
Emplacement : app/Modules/Banner/Models/BannerMessage.php
Casts :
is_active→boolean
Les bannières avec is_active = true sont affichées dans l'ordre défini par le champ order.
Review (Module Review)
Avis et évaluations produits laissés par les clients.
| Champ | Type |
|---|---|
user_id | integer cascadeOnDelete |
product_id | integer cascadeOnDelete |
title | string |
comment | text nullable |
rating | unsignedTinyInteger default:0 |
Emplacement : app/Modules/Review/Models/Review.php
Relations :
belongsTo(User)- Auteur de l'avisbelongsTo(Product)- Produit évalué
Casts :
rating→integer(généralement 1-5 étoiles)
Wishlist (Module Wishlist)
Liste de souhaits utilisateur.
| Champ | Type |
|---|---|
user_id | integer cascadeOnDelete |
Emplacement : app/Modules/Wishlist/Models/Wishlist.php
Relations :
belongsTo(User)- Propriétaire de la wishlistbelongsToMany(Product)- Produits dans la wishlist (via table pivotwishlist_items)
LoyaltyAccount (Module Loyalty)
Compte de points de fidélité utilisateur.
| Champ | Type |
|---|---|
user_id | integer cascadeOnDelete |
points | integer default:0 |
lifetime_points | integer default:0 |
Emplacement : app/Modules/Loyalty/Models/LoyaltyAccount.php
Relations :
belongsTo(User)- Utilisateur propriétairehasMany(LoyaltyTransaction)- Historique des transactions
Accesseurs calculés :
available_points- Points disponibles (non expirés)expiring_points- Points expirant dans les 30 prochains jours
Casts :
points→integerlifetime_points→integer
lifetime_points représente le total de points gagnés depuis l'inscription (ne diminue jamais). points représente le solde actuel.
LoyaltyTransaction (Module Loyalty)
Transaction de points de fidélité (gain, dépense, expiration, etc.).
| Champ | Type |
|---|---|
loyalty_account_id | integer cascadeOnDelete |
type | enum (TransactionType) |
points | integer |
balance_after | integer |
description | string |
order_id | integer nullable |
expires_at | datetime nullable |
Emplacement : app/Modules/Loyalty/Models/LoyaltyTransaction.php
Relations :
belongsTo(LoyaltyAccount)- Compte de fidélitébelongsTo(Order)- Commande associée (si applicable)
Types de transaction (Enum) :
EARNED- Points gagnésSPENT- Points dépensésEXPIRED- Points expirésREFUNDED- Points remboursésADMIN_ADJUSTMENT- Ajustement administrateur
Casts :
type→TransactionType(enum)points→integerbalance_after→integerexpires_at→datetime
Méthodes utiles :
isExpired()- Vérifie si les points ont expiréisExpiringSoon()- Vérifie si les points expirent dans les 30 jours
Relations entre modèles
Diagramme simplifié
User
├── hasMany(Order)
├── hasMany(Review)
├── hasOne(Cart)
├── hasOne(Wishlist)
└── hasOne(LoyaltyAccount)
Product
├── belongsTo(Brand)
├── belongsTo(Category)
├── belongsTo(Collection)
├── hasMany(ProductImage)
├── hasMany(ProductOption)
├── hasMany(Review)
└── belongsToMany(Wishlist)
Order
├── belongsTo(User)
├── hasMany(OrderItem)
└── hasMany(LoyaltyTransaction)
LoyaltyAccount
├── belongsTo(User)
└── hasMany(LoyaltyTransaction)Bonnes pratiques
1. Utiliser les relations Eloquent
✅ Préférez :
$product->brand->name
$user->loyaltyAccount->points❌ Évitez :
Brand::find($product->brand_id)->name
LoyaltyAccount::where('user_id', $user->id)->first()->points2. Eager loading pour éviter N+1
// ✅ Bon - 2 requêtes
$products = Product::with(['brand', 'category', 'images'])->get();
// ❌ Mauvais - 1 + N requêtes
$products = Product::all();
foreach ($products as $product) {
echo $product->brand->name; // Requête pour chaque produit
}3. Utiliser les scopes
// Défini dans le modèle Product
public function scopePopular($query, int $limit = 10)
{
return $query->orderBy('sales_count', 'desc')->limit($limit);
}
// Utilisation
$popularProducts = Product::popular(5)->get();4. Casts pour les types de données
Les modèles Swell utilisent déjà les casts appropriés :
protected $casts = [
'status' => 'boolean',
'price' => 'float',
'options' => 'array', // JSON → Array automatique
'type' => TransactionType::class, // String → Enum
];Personnalisation
Pour personnaliser un modèle :
- Ajouter des champs : Créez une migration
- Ajouter des relations : Définissez-les dans le modèle
- Ajouter des méthodes : Ajoutez vos méthodes métier
- Ajouter des scopes : Pour des requêtes réutilisables
- Ajouter des accesseurs : Pour des attributs calculés
Exemple :
// Migration
Schema::table('products', function (Blueprint $table) {
$table->string('barcode')->nullable();
});
// Modèle Product
protected $fillable = [..., 'barcode'];
public function isSoldOut(): bool
{
return $this->stock === 0;
}Consultez la documentation Laravel Eloquent pour plus de détails sur la personnalisation des modèles.