💻 DéveloppementIntermédiaire PS 1.7 PS 8.x

Ajouter un champ date personnalisé aux détails de commande PrestaShop

Guide complet pour ajouter une date de livraison aux lignes de commande PrestaShop : override OrderDetail, requêtes SQL, AJAX et bonnes pratiques ObjectModel.

En bref : Pour ajouter une date personnalisée aux lignes de commande PrestaShop, ajoutez une colonne à ps_order_detail, créez un override d'OrderDetail pour déclarer le champ dans l'ObjectModel, puis mettez à jour via AJAX avec instanciation propre de l'objet et casting systématique des identifiants en (int).

Publié le 21 mars 2026 6 min de lecture Alexandre Carette

Pourquoi ajouter une date personnalisée aux lignes de commande ?

Dans de nombreux contextes e-commerce — livraison planifiée, production sur-mesure, précommande — il est indispensable d'associer une date spécifique à chaque ligne de commande (et non à la commande globale). PrestaShop ne propose pas cette fonctionnalité nativement : la table ps_order_detail ne contient aucun champ de date personnalisable.

Cet article détaille la méthode propre pour ajouter un champ date_livraison aux détails de commande, avec mise à jour via AJAX depuis le back-office.

Étape 1 : Ajouter la colonne en base de données

Avant toute chose, on ajoute le champ dans la table ps_order_detail :


ALTER TABLE `ps_order_detail`
  ADD COLUMN `date_livraison` DATETIME NULL DEFAULT NULL
  AFTER `product_quantity`;

Pourquoi ne pas créer une table séparée ? Sauf besoin d'historiser les changements de date, ajouter un champ directement dans ps_order_detail est plus simple et plus performant. Une table séparée n'est justifiée que si vous devez conserver un journal des modifications (audit trail).

PrestaShop 8.x : La structure de ps_order_detail est identique. Cette requête fonctionne sur toutes les versions 1.7+ et 8.x.

Étape 2 : Override de la classe OrderDetail

Pour que PrestaShop reconnaisse le nouveau champ via son ORM (ObjectModel), il faut créer un override :

Fichier : override/classes/order/OrderDetail.php


<?php
/**
 * Override OrderDetail — Ajout du champ date_livraison
 */
class OrderDetail extends OrderDetailCore
{
    /** @var string Date de livraison prévue */
    public $date_livraison;

    public function __construct($id = null, $id_lang = null, $context = null)
    {
        // Déclaration du champ AVANT l'appel au constructeur parent
        self::$definition['fields']['date_livraison'] = [
            'type' => self::TYPE_DATE,
            'validate' => 'isDate',
        ];

        parent::__construct($id, $id_lang, $context);
    }
}

Point critique : après avoir créé cet override, supprimez le fichier var/cache/dev/class_index.php et var/cache/prod/class_index.php (ou cache/class_index.php en 1.7) pour forcer PrestaShop à recharger l'index des classes.


rm -f var/cache/dev/class_index.php var/cache/prod/class_index.php

PrestaShop 8.x : Le système d'override fonctionne toujours, mais Symfony encourage les décorateurs de service pour les classes injectées. OrderDetail étant un ObjectModel legacy, l'override reste la méthode recommandée.

Étape 3 : Mise à jour via ObjectModel (la bonne méthode)

Une erreur fréquente consiste à écrire des requêtes SQL brutes (INSERT INTO, UPDATE) pour modifier les données. PrestaShop fournit un ORM natif — utilisez-le :


// Récupérer tous les détails d'une commande
$orderDetails = Db::getInstance()->executeS(
    'SELECT `id_order_detail`
     FROM `' . _DB_PREFIX_ . 'order_detail`
     WHERE `id_order` = ' . (int) $idOrder
);

// Mettre à jour la date sur chaque ligne
foreach ($orderDetails as $row) {
    $detail = new OrderDetail((int) $row['id_order_detail']);
    $detail->date_livraison = pSQL($dateLivraison); // Format: 'Y-m-d H:i:s'
    $detail->update();
}

Pourquoi ObjectModel plutôt que du SQL brut ?

ApprocheAvantagesInconvénients `new OrderDetail($id)` + `->update()`Validation automatique, hooks déclenchés, cache invalidéLégèrement plus lent (1 requête par ligne) `Db::getInstance()->execute()`Rapide, une seule requête possiblePas de validation, hooks ignorés, risque d'injection SQL

Pour des mises à jour massives (centaines de lignes), le SQL direct avec pSQL() reste acceptable. Pour le back-office classique, ObjectModel est la voie propre.

Étape 4 : Appel AJAX depuis le back-office

Côté JavaScript, dans le template de la page commande :


document.getElementById('btn-save-date').addEventListener('click', function () {
    const idOrder = parseInt(document.getElementById('id_order').value, 10);
    const dateLiv = document.getElementById('date_livraison').value;

    if (!idOrder || !dateLiv) {
        console.error('ID commande ou date manquante');
        return;
    }

    fetch(ajaxUrl, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
        },
        body: new URLSearchParams({
            ajax: 1,
            action: 'updateDateLivraison',
            id_order: idOrder,
            date_livraison: dateLiv,
        }),
    })
    .then(response => response.json())
    .then(data => {
        if (data.success) {
            // Feedback visuel
            alert('Date enregistrée');
        }
    })
    .catch(err => console.error('Erreur AJAX:', err));
});

Pièges courants de l'appel AJAX

  1. **`console.log(id_order)` en premier** — Toujours vérifier que la valeur est bien récupérée avant d'envoyer la requête.
  2. **Vérifier le type** — `id_order` doit être un entier. Si vous récupérez une chaîne depuis le DOM, castez-la avec `parseInt()` côté JS et `(int)` côté PHP.
  3. **Format de date** — MySQL attend `Y-m-d H:i:s`. Un datepicker HTML5 renvoie `Y-m-d` : ajoutez ` 00:00:00` si nécessaire.
  4. Étape 5 : Traitement côté contrôleur

    Dans votre module ou contrôleur admin :

    
    public function ajaxProcessUpdateDateLivraison()
    {
        $idOrder = (int) Tools::getValue('id_order');
        $dateLivraison = pSQL(Tools::getValue('date_livraison'));
    
        if (!$idOrder || !Validate::isDate($dateLivraison)) {
            die(json_encode(['success' => false, 'error' => 'Paramètres invalides']));
        }
    
        $orderDetails = Db::getInstance()->executeS(
            'SELECT `id_order_detail`
             FROM `' . _DB_PREFIX_ . 'order_detail`
             WHERE `id_order` = ' . $idOrder
        );
    
        foreach ($orderDetails as $row) {
            $detail = new OrderDetail((int) $row['id_order_detail']);
            $detail->date_livraison = $dateLivraison;
            $detail->update();
        }
    
        die(json_encode(['success' => true]));
    }
    

    Points d'attention

    • **`getValue` vs `executeS`** : `getValue` retourne une seule valeur. Pour récupérer plusieurs lignes (une commande a potentiellement plusieurs produits), utilisez `executeS` qui retourne un tableau.
    • **Le `return` dans le `foreach`** : une erreur classique consiste à placer le traitement SQL en dehors de la boucle. La requête ne s'exécute alors que pour la dernière ligne. Chaque itération doit traiter sa propre ligne.
    • **Casting `(int)`** : indispensable pour tout identifiant passé dans une requête SQL. Sans cela, vous envoyez une chaîne là où la base attend un entier, ce qui provoque des erreurs silencieuses.

    Techniques de débogage essentielles

    Quand une requête ne fonctionne pas, voici la méthodologie systématique :

    1. Vérifier les variables avec var_dump

    
    foreach ($orderDetails as $row) {
        var_dump($row['id_order_detail']); // Vérifier le type et la valeur
        echo '<br/>';
        var_dump($dateLivraison);          // Vérifier le format de date
        die(); // Arrêter après la première itération
    }
    

    2. Tester la requête dans phpMyAdmin

    Avant d'encapsuler une requête en PHP, testez-la en SQL pur :

    
    UPDATE ps_order_detail
       SET date_livraison = '2026-03-15 00:00:00'
     WHERE id_order_detail = 285;
    

    phpMyAdmin vous indiquera immédiatement si le nom de colonne est incorrect, si le type est incompatible, ou si une contrainte bloque l'opération.

    3. Vérifier le retour de la requête

    
    $result = Db::getInstance()->execute($sql);
    var_dump($result); // true = succès, false = erreur SQL
    die();
    

    Erreurs fréquentes et solutions

    ErreurCauseSolution La date ne s'enregistre pasVariable non castée en `(int)` pour l'ID`(int) $row['id_order_detail']` Mise à jour sur une seule ligne`return` placé en dehors du `foreach`Déplacer le traitement dans la boucle Erreur SQL silencieuseNom de colonne incorrect dans la requêteVérifier avec `DESCRIBE ps_order_detail` Format de date rejetéDate au mauvais formatUtiliser `date('Y-m-d H:i:s', strtotime($input))` Override non pris en compteCache de classe non vidéSupprimer `class_index.php`

    Bonne pratique : faut-il une table séparée ?

    Si votre besoin est simplement de stocker une date par ligne de commande, ajoutez un champ à ps_order_detail avec un override. C'est simple, performant et maintenable.

    En revanche, si vous devez :

    • Historiser chaque modification de date (qui a changé quoi, quand)
    • Stocker plusieurs dates par ligne (date prévue, date réelle, date reportée)
    • Ajouter des métadonnées complexes (commentaire, statut de livraison)

    Alors créez une table dédiée avec son propre ObjectModel. Dans ce cas, assurez-vous que :

    • L'`id` primaire est en **auto-increment**
    • Vous créez une classe ObjectModel complète (pas du SQL brut)
    • Un index existe sur `id_order_detail` pour les performances de jointure
#OrderDetail #override #ObjectModel #AJAX #back-office #personnalisation commande

Questions fréquentes

Tout ce que vous devez savoir sur ce sujet.

Un projet PrestaShop ?

Discutons-en directement.

★★★★★

193 projets livrés

Gratuit & sans engagement — réponse sous 24h

Alexandre Carette

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.