Compter les commandes par statut sans charger toutes les Order
Le piège classique : `Order::getOrders()` puis `count()`. Sur 100k commandes, c'est plusieurs secondes. Faites-le en SQL.
Le piège classique sur les dashboards custom : on veut afficher "12 commandes en attente, 47 livrées, 3 retours" et on écrit ça :
// ❌ À ne PAS faire sur 100k+ commandes
$orders = Order::getOrders();
$counts = [];
foreach ($orders as $o) {
$counts[$o['current_state']] = ($counts[$o['current_state']] ?? 0) + 1;
}
Sur 100 000 commandes, ce code charge 100 000 lignes complètes en mémoire pour ne lire qu'une colonne. Comptez 4-6 secondes et 200 Mo de RAM.
La version SQL pure
$sql = '
SELECT current_state, COUNT(*) as nb
FROM ' . _DB_PREFIX_ . 'orders
WHERE date_add >= DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY current_state
';
$rows = Db::getInstance()->executeS($sql);
$counts = [];
foreach ($rows as $r) {
$counts[(int) $r['current_state']] = (int) $r['nb'];
}
Mêmes 100 000 commandes : 40 ms, 0,5 Mo de RAM.
Avec libellés des statuts
$idLang = (int) $context->language->id;
$sql = '
SELECT o.current_state, COUNT(*) as nb, osl.name
FROM ' . _DB_PREFIX_ . 'orders o
INNER JOIN ' . _DB_PREFIX_ . 'order_state_lang osl
ON osl.id_order_state = o.current_state
AND osl.id_lang = ' . $idLang . '
WHERE o.date_add >= DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY o.current_state, osl.name
ORDER BY nb DESC
';
Cas plus large : agrégation multi-dimension
Pour des statistiques croisées (par statut + par mois), GROUP BY multiple :
SELECT
DATE_FORMAT(date_add, '%Y-%m') AS mois,
current_state,
COUNT(*) AS nb,
SUM(total_paid_tax_incl) AS ca
FROM ps_orders
WHERE date_add >= '2026-01-01'
GROUP BY mois, current_state
ORDER BY mois DESC, nb DESC;
Index utile pour ce type de requête : (date_add, current_state) — déjà présent par défaut sur PS 1.7+.
Règle générale : dès que vous itérez sur plus de 1000 ObjectModel pour faire un calcul, demandez-vous si une requête SQL ne ferait pas le boulot 100× plus vite.