La version 1.7 de prestashop apporte des changements dans la gestion des champs clients.
Cet article est une mise à jour avec  des articles suivants  qui ne fonctionnent donc plus sur prestashop 1.7

Le but de ce tutoriel va d’être d’ajouter 2 nouveaux champs à l’entité client :

  • professionnal_id => champ standard qui sera un input text
  • justificatif => champ de type file

Vous trouverez l’ensemble du code en fin d’article.

Nous allons voir la base du module qui s’appellera hhcustomer et son fonctionnement global.

Pour commencer voici le code d’initialisation, d’installation / désinstallation du module.

class HhCustomer extends Module {
 
    public function __construct() {
 
        $this->name = 'hhcustomer';
        $this->tab = 'others';
        $this->author = 'hhennes';
        $this->version = '0.1.0';
        $this->need_instance = 0;
        $this->bootstrap = true;
 
        parent::__construct();
 
        $this->displayName = $this->l('hhcustomer');
        $this->description = $this->l('add new fields to customer');
        $this->ps_versions_compliancy = array('min' => '1.7.1', 'max' => _PS_VERSION_);
    }
 
    /**
     * Installation du module
     * @return boolean
     */
    public function install() {
        if (!parent::install() 
               // Install Sql du module
                || !$this->_installSql() 
                //Hooks Admin
                || !$this->registerHook('actionAdminCustomersControllerSaveAfter') 
                || !$this->registerHook('actionAdminCustomersFormModifier')
                //Hooks Front        
                || !$this->registerHook('additionalCustomerFormFields')
                //Hooks objects 
                || !$this->registerHook('actionObjectCustomerAddAfter') 
                || !$this->registerHook('actionObjectCustomerUpdateAfter')
        ) {
            return false;
        }
 
        return true;
    }
 
    /**
     * Désinstallation du module
     * @return boolean
     */
    public function uninstall() {
        return parent::uninstall() && $this->_unInstallSql();
    }
 
    /**
     * Modifications sql du module
     * @return boolean
     */
    protected function _installSql() {
        $sqlInstall = "ALTER TABLE " . _DB_PREFIX_ . "customer "
                . "ADD professionnal_id VARCHAR(255) NULL, "
                . "ADD justificatif VARCHAR(255) NULL";
 
        return Db::getInstance()->execute($sqlInstall);
    }
 
    /**
     * Suppression des modification sql du module
     * @return boolean
     */
    protected function _unInstallSql() {
        $sqlUnInstall = "ALTER TABLE " . _DB_PREFIX_ . "customer "
                . "DROP professionnal_id,"
                . " DROP justificatif";
 
        return Db::getInstance()->execute($sqlUnInstall);
    }
}

Ce module ajoute donc 2 colonnes correspondants à nos 2 champs supplémentaires dans la table customer.
Il se greffe également sur les hooks suivants :

  • actionAdminCustomersControllerSaveAfter : Exécuté après la sauvegarde d’un client en back office
  • actionAdminCustomersFormModifier : Exécuté avant l’affichage du formulaire d’édition en back office
  • additionalCustomerFormFields : Ajout de champs supplémentaires dans le formulaire d’édition client
  • actionObjectCustomerAddAfter : Exécuté après l’ajout d’un nouveau client
  • actionObjectCustomerUpdateAfter : Exécuté après la mise à jour d’un client

Nous verrons plus loin le détails des différentes fonctions.

Il est également nécessaire de surcharger l’objet Customer et de lui ajouter ces nouveaux champs.
Pour cela créér un fichier Customer.php avec le contenu suivant dans le dossier override/classes du module.

<?php 
class Customer extends CustomerCore { 
//Nouveaux paramètres de classe 
public $professionnal_id; 
public $justificatif;
 
public function __construct($id = null) { 
//Définition du nouveau champ professionnal_id 
       self::$definition['fields']['professionnal_id'] = [ 'type' => self::TYPE_STRING,
            'required' => false, 'size' => 255
        ];
        //Définition du nouveau champ justificatif
        self::$definition['fields']['justificatif']     = [
            'type' => self::TYPE_STRING,
            'required' => false, 'size' => 255
        ];
        parent::__construct($id);
    }
}

lors de l’installation du module ce fichier sera automatiquement placé dans le dossier des override par prestashop.

 

Ajout d’un nouveau champ standard (pour le client) :

 

Le fonctionnement est très simple, ( après l’exécution des étapes précédentes ), nous utiliserons le hook AdditionalCustomerFormFields dans la fonction hookAdditionalCustomerFormFields du module.

il faut déclarer les nouveaux champs de la manière suivante :

   /**
     * Ajout d'un champ client supplémentaire en FO
     * @param type $params
     */
    public function hookAdditionalCustomerFormFields($params) {
 
        return [
                    (new FormField)
                    ->setName('professionnal_id')
                    ->setType('text')
                    ->setLabel($this->l('Professionnal id')),
                    (new FormField)
                    ->setName('justificatif_upload')
                    ->setType('file')
                    ->setLabel($this->l('document ID'))
        ];
    }

Et puis c’est tout … :), tout fonctionne très bien ensuite tant que l’attribut « name » de votre FormField correspond bien au nom du champ dans la classe et dans la base de donnée, prestashop enregistera automatiquement les nouvelles informations

 

Ajout d’un nouveau champ standard (dans l’administration ) :

 

Pour l’ajout des champs dans l’administration je ne détaille pas, vous pouvez voir le fonctionnement général dans cet article : Prestashop : Ajouter des champs dans un formulaire d’administration

Nous utlisons le hook actionAdminCustomersFormModifier pour ajouter les nouveaux champs et définir leur valeur.

/**
 * Modification du formulaire d'édition d'un client en BO
 * @param type $params
 */
public function hookActionAdminCustomersFormModifier($params) {
 
	$params['fields'][0]['form']['input'][] = [
		'type' => 'text',
		'label' => $this->l('Professionnal id'),
		'name' => 'professionnal_id',
		'class' => 'input fixed-width-xxl',
		'hint' => $this->l('Professionnal id')
	];
 
	//Définition de la valeur du champ supplémentaire
	$params['fields_value']['professionnal_id'] = $params['object']->professionnal_id;
}

Et voila l’enregistrement du nouveau champ fonctionne également dans le backoffice.
Vous vous dites sans-doute que nous n’avons pas utilisé l’ensemble des hooks énoncés plus hauts…, c’est pourquoi nous allons voir un cas particulier, l’ajout d’un champ de type file

 

Cas particulier : ajout d’un nouveau champ de type file

 

Pour les champs de type file 2 contraintes s’ajoutent par rapport aux champs standards :

  • Upload de la pièce jointe
  • Mise à jour du champ ( et aucune action si celui-ci est vide )

J’ai eut un peu de mal à trouver une solution propre pour gérer la mise à jour de cette données, mais finalement je suis passé par le contournement suivant :

Dans les formulaires , le nom du champ ajouté n’est pas celui-correspondant à la propriété de la classe.

Par exemple pour le champ « justificatif » j’ai rajouté dans les formulaires un champ « justificatif_upload »
L’assignation de la valeur au client est ensuite gérée via des hooks prestashop.

Dans l’administration :

Affichage du champ + ajout d’un champ pour gérer sa suppression

/**
* Modification du formulaire d'édition d'un client en BO
* @param type $params
*/
public function hookActionAdminCustomersFormModifier($params) {
 
$params['fields'][0]['form']['input'][] = [
'type' => 'file',
'label' => $this->l('File upload'),
'name' => 'justificatif_upload',
'hint' => $this->l('File upload field add with a module')
];
 
//Si la pièce jointe est déjà assignée on donne la possibilité de la supprimer
if ($params['object']->justificatif && $params['object']->justificatif != '') {
$fileUrl = $this->context->link->getBaseLink() . 'modules/' . $this->name . '/files/' . $params['object']->justificatif;
$params['fields'][0]['form']['input'][] = [
'type' => 'html',
'label' => $this->l('Delete justificatif'),
'name' => 'delete_justificatif',
'html_content' => '<div class="checkbox">
<p>' . $this->l('Current File') . ': <a href="' . $fileUrl . '" target="_blank">' . $params['object']->justificatif . '</a></p>
<label for="delete_justificatif">
<input type="checkbox" name="delete_justificatif" value="' . $params['object']->justificatif . '"/>
' . $this->l('Delete File ?') . '
</label>
</div>',
'hint' => $this->l('Check this to delete current justificatif')
];
}
 
}

Gestion de la suppression via le hook adminCustomersControllerSaveAfter

/**
     * Evénement après la sauvegarde du client dans le back-office
     * @param type $params
     */
    public function hookActionAdminCustomersControllerSaveAfter($params) {
 
        //Suppression du fichier actuel si défini
        if ($deletejustificatif = Tools::getValue('delete_justificatif')) {
 
            $currentFile = dirname(__FILE__) . '/files/' . $justificatif;
            if (is_file($currentFile)) {
                unlink($currentFile);
            }
 
            try {
                $params['return']->justificatif = '';
                $params['return']->save();
            } catch (Exception $ex) {
                PrestaShopLogger::addLog($this->l('Error in customer in module ' . $this->name));
            }
        }
    }

Sur le front office :

Attention pour permettre l’envoi de fichiers via la formulaire d’inscription il faut ajouter la propriété suivante : enctype= »multipart/form-data » au formulaire de création de compte de compte dans le fichier :
themes/votre-theme/customer/__partials/customer-form.tpl

Lors de la création d’un compte client ( en front office ou via l’administration ) nous allons utiliser le hook actionObjectCustomerAddAfter via le code suivant :

/**
     * Ajout de la pièce jointe à la création du compte
     * @param type $params
     */
    public function hookActionObjectCustomerAddAfter($params) {
        //Gestion de l'upload du fichier lors de la création de compte en Front office et en BO
        if (
                $this->context->controller->php_self == 'authentication'
                || $this->context->controller instanceof AdminCustomersController
            ) {
 
            if (isset($_FILES['justificatif_upload']) && $_FILES['justificatif_upload']['name'] != "") {
                $fileUpload = $this->_uploadFile('justificatif_upload');
                //En cas d'erreur on supprime la valeur
                if ($fileUpload[0]['error'] == '0') {
                    $params['object']->justificatif = $fileUpload[0]['name'];
                    try {
                        $params['object']->save();
                    } catch (Exception $ex) {
                        PrestaShopLogger::addLog($this->l('Error in customer in module ' . $this->name));
                    }
                }
            }
        }
    }

Celui-ci permet de n’effectuer ce traitement uniquement dans les pages de création de compte client.

Pour finir pour gérer la mise à jour du client nous utiliserons le hook : actionObjectCustomerUpdateAfter

/**
     * Action effectuée APRES la mise à jour d'un client
     * Gestion de l'upload de la pièce jointe si il y'a an une
     * @param type $params
     */
    public function hookActionObjectCustomerUpdateAfter($params) {
 
        if (
                $this->context->controller->php_self == 'identity'
                || $this->context->controller instanceof AdminCustomersController
            ) {
 
            if (isset($_FILES['justificatif_upload']) && $_FILES['justificatif_upload']['name'] != "" && !$this->context->cookie->__isset('customer_justificatif_updated')) {
 
                $fileUpload = $this->_uploadFile('justificatif_upload');
 
                //En cas d'erreur on supprime la valeur
                if ($fileUpload[0]['error'] != '0') {
                    $params['object']->justificatif = '';
                } else {
                    $params['object']->justificatif = $fileUpload[0]['name'];
                }
 
                try {
                    //Mise en place d'un flag pour éviter une boucle de redirection
                    $this->context->cookie->__set('customer_justificatif_updated', 1);
                    $params['object']->save();
                } catch (Exception $ex) {
                    PrestaShopLogger::addLog($this->l('Error in customer in module ' . $this->name));
                }
            } else {
                $this->context->cookie->__unset('customer_justificatif_updated');
            }
        }
    }

La principale difficulté dans ce hook étant qu’il est effectué lors de la mise à jour d’un client, et que ce code génère la mise à jour d’un client…
Pour éviter les boucles infinies il est donc nécessaire de mettre en place un cookie définissant que le client a déjà été mis à jour.

Nous en avons fini avec cette problèmatique d’ajout des nouveau champs, vous pouvez voir le code complet du module ci-dessous :

<?php class HhCustomer extends Module { public function __construct() { $this->name = 'hhcustomer';
        $this->tab = 'others';
        $this->author = 'hhennes';
        $this->version = '0.1.0';
        $this->need_instance = 0;
        $this->bootstrap = true;
 
        parent::__construct();
 
        $this->displayName = $this->l('hhcustomer');
        $this->description = $this->l('add new fields to customer');
        $this->ps_versions_compliancy = array('min' => '1.7.1', 'max' => _PS_VERSION_);
    }
 
    /**
     * @return boolean
     */
    public function install() {
        if (!parent::install() || !$this->_installSql()
                //Hooks Admin
                || !$this->registerHook('actionAdminCustomersControllerSaveAfter') 
                || !$this->registerHook('actionAdminCustomersFormModifier')
                //Hooks Front        
                || !$this->registerHook('additionalCustomerFormFields')
                //Hooks objects 
                || !$this->registerHook('actionObjectCustomerAddAfter') 
                || !$this->registerHook('actionObjectCustomerUpdateAfter')
        ) {
            return false;
        }
 
        return true;
    }
 
    public function uninstall() {
        return parent::uninstall() && $this->_unInstallSql();
    }
 
    /**
     * Modifications sql du module
     * @return boolean
     */
    protected function _installSql() {
        $sqlInstall = "ALTER TABLE " . _DB_PREFIX_ . "customer "
                . "ADD professionnal_id VARCHAR(255) NULL, "
                . "ADD justificatif VARCHAR(255) NULL";
 
        return Db::getInstance()->execute($sqlInstall);
    }
 
    /**
     * Suppression des modification sql du module
     * @return boolean
     */
    protected function _unInstallSql() {
        $sqlUnInstall = "ALTER TABLE " . _DB_PREFIX_ . "customer "
                . "DROP professionnal_id,"
                . " DROP justificatif";
 
        return Db::getInstance()->execute($sqlUnInstall);
    }
 
    /**
     * Evénement après la sauvegarde du client dans le back-office
     * @param type $params
     */
    public function hookActionAdminCustomersControllerSaveAfter($params) {
 
        //Suppression du fichier actuel si défini
        if ($deletejustificatif = Tools::getValue('delete_justificatif')) {
 
            $currentFile = dirname(__FILE__) . '/files/' . $justificatif;
            if (is_file($currentFile)) {
                unlink($currentFile);
            }
 
            try {
                $params['return']->justificatif = '';
                $params['return']->save();
            } catch (Exception $ex) {
                PrestaShopLogger::addLog($this->l('Error in customer in module ' . $this->name));
            }
        }
    }
 
    /**
     * Modification du formulaire d'édition d'un client en BO
     * @param type $params
     */
    public function hookActionAdminCustomersFormModifier($params) {
 
        $params['fields'][0]['form']['input'][] = [
            'type' => 'text',
            'label' => $this->l('Professionnal id'),
            'name' => 'professionnal_id',
            'class' => 'input fixed-width-xxl',
            'hint' => $this->l('Professionnal id')
        ];
 
        $params['fields'][0]['form']['input'][] = [
            'type' => 'file',
            'label' => $this->l('File upload'),
            'name' => 'justificatif_upload',
            'hint' => $this->l('File upload field add with a module')
        ];
 
       //Si la pièce jointe est déjà assignée on donne la possibilité de la supprimer
         if ($params['object']->justificatif && $params['object']->justificatif != '') {
           $fileUrl = $this->context->link->getBaseLink() . 'modules/' . $this->name . '/files/' . $params['object']->justificatif;
           $params['fields'][0]['form']['input'][] = [
           'type' => 'html',
           'label' => $this->l('Delete justificatif'),
           'name' => 'delete_justificatif',
           'html_content' => '<div class="checkbox">
           <p>' . $this->l('Current File') . ': <a href="' . $fileUrl . '" target="_blank">' . $params['object']->justificatif . '</a></p>
           <label for="delete_justificatif">
           <input type="checkbox" name="delete_justificatif" value="' . $params['object']->justificatif . '"/>
           ' . $this->l('Delete File ?') . '
           </label>
           </div>',
           'hint' => $this->l('Check this to delete current justificatif')
          ];
         }
 
        //Définition de la valeur du champ supplémentaire
        $params['fields_value']['professionnal_id'] = $params['object']->professionnal_id;
    }
 
    /**
     * Ajout d'un champ client supplémentaire en FO
     * @param type $params
     */
    public function hookAdditionalCustomerFormFields($params) {
 
        return [
                    (new FormField)
                    ->setName('professionnal_id')
                    ->setType('text')
                    ->setLabel($this->l('Professionnal id')),
                    (new FormField)
                    ->setName('justificatif_upload')
                    ->setType('file')
                    ->setLabel($this->l('document ID'))
        ];
    }
 
    /**
     * Ajout de la pièce jointe à la création du compte
     * @param type $params
     */
    public function hookActionObjectCustomerAddAfter($params) {
        //Gestion de l'upload du fichier lors de la création de compte en Front office et en BO
        if (
                $this->context->controller->php_self == 'authentication'
                || $this->context->controller instanceof AdminCustomersController
            ) {
 
            if (isset($_FILES['justificatif_upload']) && $_FILES['justificatif_upload']['name'] != "") {
                $fileUpload = $this->_uploadFile('justificatif_upload');
                //En cas d'erreur on supprime la valeur
                if ($fileUpload[0]['error'] == '0') {
                    $params['object']->justificatif = $fileUpload[0]['name'];
                    try {
                        $params['object']->save();
                    } catch (Exception $ex) {
                        PrestaShopLogger::addLog($this->l('Error in customer in module ' . $this->name));
                    }
                }
            }
        }
    }
 
    /**
     * Action effectuée APRES la mise à jour d'un client
     * Gestion de l'upload de la pièce jointe si il y'a an une
     * @param type $params
     */
    public function hookActionObjectCustomerUpdateAfter($params) {
 
        if (
                $this->context->controller->php_self == 'identity'
                || $this->context->controller instanceof AdminCustomersController
            ) {
 
            if (isset($_FILES['justificatif_upload']) && $_FILES['justificatif_upload']['name'] != "" && !$this->context->cookie->__isset('customer_justificatif_updated')) {
 
                $fileUpload = $this->_uploadFile('justificatif_upload');
 
                //En cas d'erreur on supprime la valeur
                if ($fileUpload[0]['error'] != '0') {
                    $params['object']->justificatif = '';
                } else {
                    $params['object']->justificatif = $fileUpload[0]['name'];
                }
 
                try {
                    //Mise en place d'un flag pour éviter une boucle de redirection
                    $this->context->cookie->__set('customer_justificatif_updated', 1);
                    $params['object']->save();
                } catch (Exception $ex) {
                    PrestaShopLogger::addLog($this->l('Error in customer in module ' . $this->name));
                }
            } else {
                $this->context->cookie->__unset('customer_justificatif_updated');
            }
        }
    }
 
    /**
     * Envoi du fichier en Front et en back
     */
    protected function _uploadFile($index) {
        $_registration_allowed_extensions = array('txt', 'rtf', 'doc', 'docx', 'pdf', 'png', 'jpeg', 'gif', 'jpg');
        $uploader = new Uploader($index); //Renseigner ici le nom du champ input
        return $uploader->setAcceptTypes($_registration_allowed_extensions) // Définition des extensions autorisées
                        ->setCheckFileSize(Uploader::DEFAULT_MAX_SIZE) //Taille maximum des fichiers à envoyer
                        ->setSavePath(dirname(__FILE__) . '/files/') // Répertoire de destination
                        ->process(); // Traitement de l'envoi
    }
 
}