Mettre à jour le stock depuis hookActionValidateOrder dans PrestaShop
Comment modifier le stock produit via hookActionValidateOrder dans PrestaShop : classe Db, ObjectModel, erreurs courantes et bonnes pratiques.
En bref : Pour mettre à jour le stock depuis hookActionValidateOrder, utilisez Db::getInstance()->update() ou mieux, la classe StockAvailable. Les erreurs courantes sont les colonnes dupliquées dans le tableau PHP, un hook non enregistré, ou des colonnes NOT NULL manquantes.
Pourquoi modifier le stock au moment de la validation de commande ?
Le hook actionValidateOrder se déclenche immédiatement après qu'une commande est validée par PrestaShop. C'est un point d'entrée stratégique pour les développeurs de modules qui ont besoin d'appliquer une logique métier personnalisée sur les stocks : synchronisation avec un ERP, gestion de stock multi-entrepôt, réservation conditionnelle, ou encore mise à jour de quantités physiques calculées.
Pourtant, beaucoup de développeurs se heurtent à des requêtes UPDATE qui échouent silencieusement depuis ce hook. Le problème est rarement lié au hook lui-même, mais plutôt à la façon dont la requête SQL est construite.
L'erreur classique : écrire du SQL brut au lieu d'utiliser la classe Db
Une erreur fréquente consiste à écrire des requêtes SQL directement avec mysql_query() ou PDO en contournant complètement l'abstraction de PrestaShop. Cela pose plusieurs problèmes :
- Le préfixe de table (`ps_`) n'est pas géré automatiquement
- Les connexions à la base de données peuvent entrer en conflit avec celles de PrestaShop
- Les mécanismes de cache interne ne sont pas invalidés
- Aucune protection contre les injections SQL
La bonne approche : `Db::getInstance()->update()`
PrestaShop fournit la classe Db qui encapsule toutes les opérations SQL. La méthode update() gère automatiquement le préfixe de table, l'échappement des valeurs et la construction de la clause WHERE.
$db = \Db::getInstance();
$db->update('stock_available', [
'physical_quantity' => 100,
'reserved_quantity' => 1,
'quantity' => 99,
], 'id_product_attribute = 352711');
Points importants :
- Le premier paramètre est le nom de la table **sans le préfixe** (`stock_available` et non `ps_stock_available`)
- Le deuxième paramètre est un tableau associatif `colonne => valeur`
- Le troisième paramètre est la clause `WHERE` sous forme de chaîne
- Attention à ne pas dupliquer de colonnes dans le tableau — PHP écrasera silencieusement la première occurrence
Erreurs courantes qui font échouer la requête
1. Colonnes dupliquées dans le tableau
Si vous passez deux fois la même clé dans un tableau PHP, seule la dernière valeur est conservée :
// ❌ BUG : 'reserved_quantity' apparaît deux fois
$data = [
'physical_quantity' => 100,
'reserved_quantity' => 1,
'quantity' => 100,
'reserved_quantity' => 0, // Écrase la valeur précédente !
];
Cette erreur est particulièrement sournoise car PHP ne génère aucun warning.
2. Colonnes NOT NULL sans valeur par défaut
La table ps_stock_available contient des colonnes obligatoires. Si votre requête est en réalité un INSERT déguisé (par exemple via ON DUPLICATE KEY UPDATE), vous devez fournir toutes les colonnes NOT NULL sans valeur par défaut :
- `id_product`
- `id_product_attribute`
- `id_shop` (ou `id_shop_group`)
Pour diagnostiquer, inspectez la structure de la table :
DESCRIBE ps_stock_available;
Vérifiez la colonne Null et Default pour chaque champ.
3. Mauvais id_stock_available
La table ps_stock_available associe un id_stock_available unique à chaque combinaison id_product + id_product_attribute + id_shop. Utiliser directement id_product_attribute dans le WHERE fonctionne, mais assurez-vous que la valeur existe réellement dans la table.
// Vérifier que la déclinaison existe dans stock_available
$exists = (bool) Db::getInstance()->getValue(
'SELECT id_stock_available
FROM ' . _DB_PREFIX_ . 'stock_available
WHERE id_product_attribute = ' . (int) $idProductAttribute
);
if (!$exists) {
PrestaShopLogger::addLog(
'Stock update failed: id_product_attribute ' . $idProductAttribute . ' not found',
2 // severity: warning
);
return;
}
L'approche recommandée : utiliser l'ObjectModel StockAvailable
Plutôt que de manipuler la base directement, PrestaShop propose la classe StockAvailable qui gère toute la logique métier associée (cache, hooks, multiboutique) :
use PrestaShop\PrestaShop\Adapter\StockManager;
// Méthode simple pour modifier la quantité
StockAvailable::setQuantity($idProduct, $idProductAttribute, $newQuantity, $idShop);
// Ou via l'ObjectModel pour un contrôle plus fin
$stockId = StockAvailable::getStockAvailableIdByProductId($idProduct, $idProductAttribute, $idShop);
if ($stockId) {
$stock = new StockAvailable($stockId);
$stock->physical_quantity = 100;
$stock->reserved_quantity = 1;
$stock->quantity = 99;
$stock->update();
}
Avantages de l'ObjectModel :
- Invalidation automatique du cache
- Respect des règles multiboutique
- Déclenchement des hooks associés (`actionUpdateQuantity`)
- Validation des données avant écriture
Implémenter correctement le hook actionValidateOrder
Enregistrer le hook dans votre module
Première cause d'échec : le hook n'est tout simplement pas enregistré. Vérifiez votre méthode install() :
public function install()
{
return parent::install()
&& $this->registerHook('actionValidateOrder');
}
Après installation, confirmez l'enregistrement en back-office : Modules > Positions et recherchez actionValidateOrder.
Récupérer les données de la commande
Le hook fournit un tableau de paramètres contenant toutes les informations nécessaires :
public function hookActionValidateOrder($params)
{
/** @var Order $order */
$order = $params['order'];
/** @var Cart $cart */
$cart = $params['cart'];
$products = $order->getProducts();
foreach ($products as $product) {
$idProduct = (int) $product['id_product'];
$idProductAttribute = (int) $product['product_attribute_id'];
$quantityOrdered = (int) $product['product_quantity'];
// Votre logique de mise à jour du stock ici
$this->updateCustomStock($idProduct, $idProductAttribute, $quantityOrdered);
}
}
Déboguer une requête qui échoue silencieusement
PrestaShop ne lève pas d'exception par défaut sur une erreur SQL. Pour identifier le problème :
$db = Db::getInstance();
$result = $db->update('stock_available', [
'quantity' => 99,
], 'id_product_attribute = ' . (int) $idProductAttribute);
if (!$result) {
// Récupérer l'erreur MySQL
$error = $db->getMsgError();
$errno = $db->getNumberError();
PrestaShopLogger::addLog(
"Stock update error [{$errno}]: {$error}",
3 // severity: error
);
}
Vous pouvez également tester votre requête directement dans phpMyAdmin pour obtenir un retour d'erreur immédiat de MySQL, ce qui permet d'identifier rapidement les problèmes de structure de table ou de contraintes.
Bonnes pratiques pour PrestaShop 8.x
Depuis PrestaShop 8, certaines évolutions impactent la gestion du stock :
- **Symfony Services** : privilégiez `StockManager` via l'injection de dépendances plutôt que les appels statiques
- **Doctrine** : les nouvelles entités utilisent Doctrine, mais `StockAvailable` reste en ObjectModel
- **Multiboutique** : la colonne `id_shop` est critique — testez toujours en contexte multiboutique si votre module le supporte
- **Type safety** : castez systématiquement vos identifiants avec `(int)` pour éviter les injections SQL
// PrestaShop 8.x — via le container Symfony
$stockManager = $this->get('prestashop.core.data_provider.stock_interface');
Questions fréquentes
Tout ce que vous devez savoir sur ce sujet.
Un projet PrestaShop ?
Discutons-en directement.
193 projets livrés
Lire sur le blog

Alexandre Carette
Expert PrestaShop & Architecture E-commerce
Développeur PrestaShop depuis 2014, 193 projets livrés. Je conçois des architectures headless Nuxt + PrestaShop et des outils d'automatisation IA pour les e-commerçants.