Swell

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.

ChampType
namestring
slugstring unique
logo_urlstring 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).

ChampType
namestring
slugstring unique
descriptiontext nullable
statusboolean default:true
parent_idnullable nullOnDelete

Relations :

  • belongsTo(Category) - Parent category (pour hiérarchie)
  • hasMany(Category) - Sous-catégories enfants
  • hasMany(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.

ChampType
namestring
slugstring unique nullable
skustring unique
descriptiontext
short_descriptionstring max:500
pricedecimal:10,2
discount_pricedecimal:10,2 nullable
cost_pricedecimal:10,2
stockunsignedInteger default:0
reorder_levelunsignedInteger default:10
sales_countunsignedInteger default:0
statusboolean default:true
meta_titlestring nullable
meta_descriptionstring max:255 nullable
meta_keywordsstring max:255 nullable
brand_idinteger restrictOnDelete
category_idinteger nullOnDelete
collection_idinteger nullable nullOnDelete

Relations :

  • belongsTo(Category) - Catégorie du produit
  • belongsTo(Brand) - Marque du produit
  • belongsTo(Collection) - Collection associée (optionnel)
  • hasMany(ProductImage) - Images du produit
  • hasOne(ProductImage) - Image featured (via featuredImage())
  • hasMany(ProductOption) - Options/variantes (taille, couleur, etc.)
  • hasMany(Review) - Avis clients (module Review)
  • hasMany(OrderItem) - Éléments de commandes
  • belongsToMany(Wishlist) - Listes de souhaits (module Wishlist)

Méthodes utiles :

  • getPrice() - Retourne discount_price si défini, sinon price
  • isOutOfStock() - Vérifie si le stock est épuisé
  • incrementSales($quantity) - Incrémente le compteur de ventes
  • scopePopular($limit) - Scope pour récupérer les produits populaires

Traits :

  • Searchable (Laravel Scout) - Synchronisation automatique avec Algolia
  • shouldBeSearchable() - Seuls les produits avec status = true sont indexés

Collection

Regroupe plusieurs produits sous une collection thématique (ex: "Nouveautés", "Soldes d'été").

ChampType
titlestring
slugstring 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).

ChampType
product_idinteger cascadeOnDelete
image_urlstring
alt_textstring nullable
is_featuredboolean default:false
orderunsignedInteger

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").

ChampType
product_idinteger cascadeOnDelete
namestring

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, Noir

ProductOptionValue

Valeurs possibles pour une option de produit.

ChampType
product_option_idinteger cascadeOnDelete
valuestring

Relations :

  • belongsTo(ProductOption) - Option associée

Cart

Panier d'achat (session pour invités, persistant pour utilisateurs authentifiés).

ChampType
session_idstring nullable
user_idinteger 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.

ChampType
cart_idinteger cascadeOnDelete
product_idinteger cascadeOnDelete
quantityunsignedInteger default:1
optionsjson nullable

Relations :

  • belongsTo(Cart) - Panier associé
  • belongsTo(Product) - Produit ajouté

Casts :

  • optionsarray - 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.

ChampType
order_numberstring unique
stripe_checkout_session_idstring
amount_discountunsignedInteger
amount_subtotalunsignedInteger
amount_totalunsignedInteger
billing_addressjson
shipping_addressjson
user_idinteger cascadeOnDelete

Relations :

  • belongsTo(User) - Client de la commande
  • hasMany(OrderItem) - Produits commandés

Casts :

  • billing_addresscollection
  • shipping_addresscollection

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).

ChampType
order_idinteger
product_idinteger
namestring
descriptiontext
priceunsignedInteger
quantityunsignedInteger
amount_discountunsignedInteger
amount_totalunsignedInteger

Relations :

  • belongsTo(Order) - Commande associée
  • belongsTo(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.

ChampType
messagestring
is_activeboolean
orderunsignedInteger default:0

Emplacement : app/Modules/Banner/Models/BannerMessage.php

Casts :

  • is_activeboolean

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.

ChampType
user_idinteger cascadeOnDelete
product_idinteger cascadeOnDelete
titlestring
commenttext nullable
ratingunsignedTinyInteger default:0

Emplacement : app/Modules/Review/Models/Review.php

Relations :

  • belongsTo(User) - Auteur de l'avis
  • belongsTo(Product) - Produit évalué

Casts :

  • ratinginteger (généralement 1-5 étoiles)

Wishlist (Module Wishlist)

Liste de souhaits utilisateur.

ChampType
user_idinteger cascadeOnDelete

Emplacement : app/Modules/Wishlist/Models/Wishlist.php

Relations :

  • belongsTo(User) - Propriétaire de la wishlist
  • belongsToMany(Product) - Produits dans la wishlist (via table pivot wishlist_items)

LoyaltyAccount (Module Loyalty)

Compte de points de fidélité utilisateur.

ChampType
user_idinteger cascadeOnDelete
pointsinteger default:0
lifetime_pointsinteger default:0

Emplacement : app/Modules/Loyalty/Models/LoyaltyAccount.php

Relations :

  • belongsTo(User) - Utilisateur propriétaire
  • hasMany(LoyaltyTransaction) - Historique des transactions

Accesseurs calculés :

  • available_points - Points disponibles (non expirés)
  • expiring_points - Points expirant dans les 30 prochains jours

Casts :

  • pointsinteger
  • lifetime_pointsinteger

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.).

ChampType
loyalty_account_idinteger cascadeOnDelete
typeenum (TransactionType)
pointsinteger
balance_afterinteger
descriptionstring
order_idinteger nullable
expires_atdatetime 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és
  • SPENT - Points dépensés
  • EXPIRED - Points expirés
  • REFUNDED - Points remboursés
  • ADMIN_ADJUSTMENT - Ajustement administrateur

Casts :

  • typeTransactionType (enum)
  • pointsinteger
  • balance_afterinteger
  • expires_atdatetime

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()->points

2. 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 :

  1. Ajouter des champs : Créez une migration
  2. Ajouter des relations : Définissez-les dans le modèle
  3. Ajouter des méthodes : Ajoutez vos méthodes métier
  4. Ajouter des scopes : Pour des requêtes réutilisables
  5. 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.