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

Ajouter des champs personnalisés au formulaire d'inscription PrestaShop via un module

Apprenez à ajouter des champs personnalisés au formulaire d'inscription client PrestaShop sans override, grâce à un module utilisant les hooks natifs.

En bref : Pour ajouter des champs au formulaire d'inscription PrestaShop, créez un module utilisant le hook additionalCustomerFormFields plutôt qu'un override de la classe Customer. Cette approche est compatible 1.7 à 8.x, sans conflit entre modules et désinstallable proprement.

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

Pourquoi ajouter des champs au formulaire d'inscription ?

Le formulaire d'inscription par défaut de PrestaShop collecte le strict minimum : nom, prénom, email, mot de passe. Or, de nombreux marchands ont besoin d'informations complémentaires dès la création du compte : numéro SIRET pour le B2B, secteur d'activité, source de découverte du site, ou encore un identifiant professionnel.

La tentation est forte de modifier directement la classe Customer via un override. C'est une erreur classique qui expose votre boutique à des conflits lors des mises à jour et rend la maintenance cauchemardesque dès qu'un second module touche au même fichier.

La bonne approche repose sur les hooks natifs de PrestaShop, disponibles depuis la version 1.7, qui permettent d'étendre le formulaire client sans jamais toucher au cœur du CMS.

Architecture du module

Le module s'articule autour de trois hooks complémentaires :

HookRôle `additionalCustomerFormFields`Injecte les champs supplémentaires dans le formulaire front-office `actionObjectCustomerAddAfter`Sauvegarde les données après création d'un compte `actionObjectCustomerUpdateAfter`Met à jour les données quand le client modifie son profil

Cette approche respecte le cycle de vie complet du compte client : création, modification et affichage.

Structure des fichiers


ac_customerfields/
├── ac_customerfields.php          # Fichier principal du module
├── classes/
│   └── CustomerFieldsModel.php    # ObjectModel pour stocker les données
├── sql/
│   ├── install.sql                # Création de la table
│   └── uninstall.sql              # Suppression propre
└── views/
    └── templates/
        └── admin/
            └── customer_fields.tpl # Affichage back-office (optionnel)

Étape 1 : Créer la table de stockage

Les champs personnalisés sont stockés dans une table dédiée, liée au client par son id_customer. Ne modifiez jamais la table ps_customer directement.


CREATE TABLE IF NOT EXISTS `PREFIX_customer_custom_fields` (
    `id_customer_custom_fields` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
    `id_customer` INT(10) UNSIGNED NOT NULL,
    `siret` VARCHAR(14) DEFAULT NULL,
    `company_type` VARCHAR(100) DEFAULT NULL,
    `referral_source` VARCHAR(255) DEFAULT NULL,
    `date_add` DATETIME NOT NULL,
    `date_upd` DATETIME NOT NULL,
    PRIMARY KEY (`id_customer_custom_fields`),
    UNIQUE KEY `idx_customer` (`id_customer`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

Point technique : Utilisez toujours utf8mb4 et non utf8 pour supporter l'intégralité des caractères Unicode. Le préfixe PREFIX_ sera remplacé automatiquement par le préfixe de vos tables lors de l'installation.

Étape 2 : L'ObjectModel

Créez un ObjectModel propre pour manipuler vos données. C'est la couche d'abstraction standard de PrestaShop et elle vous offre validation, sanitisation et compatibilité avec le système de hooks.


<?php

if (!defined('_PS_VERSION_')) {
    exit;
}

class CustomerFieldsModel extends ObjectModel
{
    public $id_customer;
    public $siret;
    public $company_type;
    public $referral_source;
    public $date_add;
    public $date_upd;

    public static $definition = [
        'table' => 'customer_custom_fields',
        'primary' => 'id_customer_custom_fields',
        'fields' => [
            'id_customer' => [
                'type' => self::TYPE_INT,
                'validate' => 'isUnsignedId',
                'required' => true,
            ],
            'siret' => [
                'type' => self::TYPE_STRING,
                'validate' => 'isGenericName',
                'size' => 14,
            ],
            'company_type' => [
                'type' => self::TYPE_STRING,
                'validate' => 'isGenericName',
                'size' => 100,
            ],
            'referral_source' => [
                'type' => self::TYPE_STRING,
                'validate' => 'isCleanHtml',
                'size' => 255,
            ],
            'date_add' => [
                'type' => self::TYPE_DATE,
                'validate' => 'isDate',
            ],
            'date_upd' => [
                'type' => self::TYPE_DATE,
                'validate' => 'isDate',
            ],
        ],
    ];

    /**
     * Récupère les champs personnalisés d'un client
     */
    public static function getByCustomerId(int $idCustomer): ?self
    {
        $id = (int) Db::getInstance()->getValue(
            'SELECT id_customer_custom_fields
             FROM `' . _DB_PREFIX_ . 'customer_custom_fields`
             WHERE id_customer = ' . (int) $idCustomer
        );

        if ($id) {
            return new self($id);
        }

        return null;
    }
}

Étape 3 : Le fichier principal du module

Voici le cœur du module avec l'enregistrement des hooks et la logique métier :


<?php

if (!defined('_PS_VERSION_')) {
    exit;
}

require_once __DIR__ . '/classes/CustomerFieldsModel.php';

class ac_customerfields extends Module
{
    public function __construct()
    {
        $this->name = 'ac_customerfields';
        $this->tab = 'administration';
        $this->version = '1.0.0';
        $this->author = 'Alexandre Carette';
        $this->need_instance = 0;
        $this->bootstrap = true;

        parent::__construct();

        $this->displayName = $this->l('Champs client personnalisés');
        $this->description = $this->l('Ajoute des champs supplémentaires au formulaire d\'inscription client.');
        $this->ps_versions_compliancy = ['min' => '1.7.0.0', 'max' => '8.99.99'];
    }

    public function install(): bool
    {
        $sql = file_get_contents(__DIR__ . '/sql/install.sql');
        $sql = str_replace('PREFIX_', _DB_PREFIX_, $sql);

        return parent::install()
            && Db::getInstance()->execute($sql)
            && $this->registerHook([
                'additionalCustomerFormFields',
                'actionObjectCustomerAddAfter',
                'actionObjectCustomerUpdateAfter',
                'displayAdminCustomers',
            ]);
    }

    public function uninstall(): bool
    {
        $sql = file_get_contents(__DIR__ . '/sql/uninstall.sql');
        $sql = str_replace('PREFIX_', _DB_PREFIX_, $sql);

        return parent::uninstall()
            && Db::getInstance()->execute($sql);
    }

    /**
     * Hook : injecte les champs dans le formulaire d'inscription
     */
    public function hookAdditionalCustomerFormFields(array $params): array
    {
        $fields = [];

        // Champ SIRET
        $fieldSiret = (new FormField())
            ->setName('siret')
            ->setType('text')
            ->setLabel($this->l('Numéro SIRET'))
            ->setRequired(false)
            ->addConstraint('isGenericName');

        // Si le client est connecté, pré-remplir
        if ($this->context->customer->isLogged()) {
            $existing = CustomerFieldsModel::getByCustomerId(
                (int) $this->context->customer->id
            );
            if ($existing) {
                $fieldSiret->setValue($existing->siret);
            }
        }

        $fields['siret'] = $fieldSiret;

        // Champ source de découverte
        $fieldSource = (new FormField())
            ->setName('referral_source')
            ->setType('select')
            ->setLabel($this->l('Comment nous avez-vous connu ?'))
            ->setRequired(false)
            ->addAvailableValue('', $this->l('-- Choisir --'))
            ->addAvailableValue('google', $this->l('Recherche Google'))
            ->addAvailableValue('social', $this->l('Réseaux sociaux'))
            ->addAvailableValue('recommendation', $this->l('Recommandation'))
            ->addAvailableValue('other', $this->l('Autre'));

        $fields['referral_source'] = $fieldSource;

        return $fields;
    }

    /**
     * Hook : sauvegarde après création du compte
     */
    public function hookActionObjectCustomerAddAfter(array $params): void
    {
        $this->saveCustomerFields($params['object']);
    }

    /**
     * Hook : sauvegarde après modification du profil
     */
    public function hookActionObjectCustomerUpdateAfter(array $params): void
    {
        $this->saveCustomerFields($params['object']);
    }

    /**
     * Logique commune de sauvegarde
     */
    private function saveCustomerFields(Customer $customer): void
    {
        $idCustomer = (int) $customer->id;
        if (!$idCustomer) {
            return;
        }

        $model = CustomerFieldsModel::getByCustomerId($idCustomer);
        if (!$model) {
            $model = new CustomerFieldsModel();
            $model->id_customer = $idCustomer;
        }

        $model->siret = pSQL(Tools::getValue('siret', ''));
        $model->referral_source = pSQL(Tools::getValue('referral_source', ''));
        $model->save();
    }
}

Étape 4 : Afficher les données dans le back-office

Pour que les données personnalisées soient visibles depuis la fiche client du back-office, utilisez le hook displayAdminCustomers :


public function hookDisplayAdminCustomers(array $params): string
{
    $idCustomer = (int) $params['id_customer'];
    $model = CustomerFieldsModel::getByCustomerId($idCustomer);

    if (!$model) {
        return '';
    }

    $this->context->smarty->assign([
        'siret' => $model->siret,
        'company_type' => $model->company_type,
        'referral_source' => $model->referral_source,
    ]);

    return $this->display(__FILE__, 'views/templates/admin/customer_fields.tpl');
}

Bonnes pratiques et pièges à éviter

Pourquoi ne pas utiliser un override ?

L'override de la classe Customer est la méthode historique, mais elle présente des inconvénients majeurs :

  • **Conflits entre modules** : un seul override possible par classe. Si deux modules veulent modifier `Customer`, l'un des deux sera ignoré.
  • **Mises à jour risquées** : lors d'une montée de version, l'override peut devenir incompatible silencieusement.
  • **Maintenance complexe** : le code est dispersé entre le module et le dossier `/override/`.

Avec les hooks, chaque module est autonome et désinstallable proprement.

Validation côté serveur

Ne faites jamais confiance aux données du formulaire. Le hook additionalCustomerFormFields permet de définir des contraintes via addConstraint(), mais ajoutez également une validation dans votre méthode de sauvegarde :


// Validation du SIRET (14 chiffres)
$siret = Tools::getValue('siret', '');
if (!empty($siret) && !preg_match('/^[0-9]{14}$/', $siret)) {
    // Gérer l'erreur
    return;
}

Compatibilité PrestaShop 8.x

Sur PrestaShop 8.x, le système de hooks reste identique, mais quelques points d'attention :

  • La classe `FormField` est toujours disponible nativement, pas besoin de `use` supplémentaire dans le contexte d'un module classique.
  • PrestaShop 8 utilise Symfony plus largement côté back-office. Pour une intégration back-office avancée, envisagez un `AdminController` basé sur Symfony plutôt que Smarty.
  • Le hook `displayAdminCustomers` fonctionne toujours, mais PrestaShop 8.1+ encourage l'utilisation des hooks Symfony (`CustomerFormDataProvider`) pour les formulaires admin.

Migration depuis un override existant

Si vous avez déjà un override en place :

  1. Installez d'abord le module sur un environnement de test
  2. Migrez les données de la colonne ajoutée dans `ps_customer` vers votre nouvelle table
  3. Supprimez l'override (`/override/classes/Customer.php`)
  4. Videz le cache de classes (`/var/cache/` ou via back-office)
  5. Vérifiez que le fichier `/app/cache/class_index.php` a été régénéré
  6. Script de migration des données

    Si vous passez d'un override qui stockait les données directement dans ps_customer vers cette approche modulaire :

    
    INSERT INTO `ps_customer_custom_fields` (id_customer, siret, date_add, date_upd)
    SELECT id_customer, siret, NOW(), NOW()
    FROM `ps_customer`
    WHERE siret IS NOT NULL AND siret != '';
    

    Résultat attendu

    Après installation du module, le formulaire d'inscription affiche automatiquement les champs supplémentaires. Les données sont sauvegardées dans une table dédiée, consultables depuis la fiche client en back-office, et le tout est désinstallable proprement sans laisser de traces dans le cœur de PrestaShop.

#module prestashop #formulaire inscription #hooks prestashop #champs personnalisés #customer form

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.