Laravel : increment() et decrement(), bonnes pratiques
DOG&DEV · 25/01/2025
Laravel : increment() et decrement(), bonnes pratiques
Lorsqu’il s’agit de mettre à jour des colonnes numériques (stock, compteurs, votes, etc.), une approche naïve consiste à lire la valeur, la modifier en PHP puis sauvegarder. Cette méthode expose à des race conditions en environnement concurrent. Laravel propose increment() et decrement() pour effectuer ces mises à jour directement en base, de façon atomique.
Prérequis
- Projet Laravel avec Eloquent
- Colonnes numériques (integer, bigInteger, decimal) sur le modèle
1. Le piège : lire–modifier–sauvegarder
// ❌ Risqué : deux requêtes + logique en PHP
$product = Product::find($id);
$product->stock = $product->stock + 1;
$product->save();
Entre le find() et le save(), une autre requête peut modifier stock. Les deux processus lisent la même valeur, incrémentent en parallèle et écrivent : une mise à jour est perdue. Pour un compteur de vues, de likes ou un stock, le résultat est faussé.
2. La bonne approche : increment() et decrement()
// ✅ Atomique : une seule requête UPDATE
$product = Product::find($id);
$product->increment('stock');
// Ou avec un pas personnalisé
$product->increment('stock', 5);
$product->decrement('stock', 2);
L’opération est réalisée côté base (UPDATE products SET stock = stock + 1 WHERE id = ?), sans passer par une lecture préalable en PHP. Les requêtes concurrentes sont correctement sérialisées par le moteur SQL.
3. Sans charger le modèle
Inutile de récupérer l’enregistrement si vous n’avez besoin que de la mise à jour :
Product::where('id', $id)->increment('stock');
Product::where('id', $id)->decrement('stock', 3);
4. Mettre à jour d’autres colonnes en même temps
Le troisième argument permet d’ajouter des paires clé–valeur :
$product->increment('views', 1, ['last_viewed_at' => now()]);
Utile pour un compteur de vues tout en enregistrant la dernière date de consultation.
5. Récupérer la nouvelle valeur
increment() et decrement() renvoient le nombre de lignes affectées, pas la valeur finale. Pour obtenir la valeur à jour :
$product->increment('stock');
$product->refresh();
$newStock = $product->stock;
// Ou sans avoir chargé le modèle au préalable
Product::where('id', $id)->increment('stock');
$newStock = Product::find($id)->stock;
Dépannage
| Symptôme | Cause possible | Correctif |
|---|---|---|
| Compteur incohérent | Lecture–modification–sauvegarde manuelle | Remplacer par increment() / decrement() |
| Erreur sur colonne non numérique | Colonne string ou type inadapté | Vérifier la migration et le type de la colonne |
| Pas de mise à jour | Condition where trop restrictive |
Vérifier que la ligne existe et que le where correspond |
Bonnes pratiques
- Privilégier toujours
increment()/decrement()pour les compteurs et stocks, dès qu’il peut y avoir concurrence. - Pour des calculs plus complexes (multiples colonnes, règles métier), envisager des transactions et des locks (
lockForUpdate()) si nécessaire. - Ne pas oublier les index et le niveau d’isolation de la base si les conflits sont très fréquents.
Ressources
- Documentation Laravel – Eloquent – increment / decrement
- Documentation Laravel – Query builder – increment / decrement
Cet article s’inscrit dans notre série de guides technique et développement web. Pour un serveur ou une application sur-mesure, contact.