Prestashop 1.7 : Ajouter des champs dans un formulaire d’administration

Ce tutoriel est compatible avec les versions de Prestashop suivantes :
1.6 1.7.6 1.7.7 1.7.8 +

Cet article est une mise à jour de l’article suivant https://www.h-hennes.fr/blog/2017/06/21/prestashop-ajouter-des-champs-dans-un-formulaire-dadministration/ qui s’applique aux controllers qui utilisent symfony, c’est le cas à partir 1.7.6 de Prestashop

Si vous avez des doutes si le controller sur lequel vous souhaitez ajouter des champs fonctionne avec ce méthode, n’hésitez pas à consulter l’article suivant pour l’identifier  : https://www.h-hennes.fr/blog/2019/07/25/prestashop-1-7-identifier-si-un-controller-admin-a-ete-migre-vers-symfony/

L’objectif est d’ajouter un nouveau champ dans un formulaire d’administration de manière propre via un module.

Fonctionnement technique

Comme pour les versions précédentes des hooks dynamiques sont présents dans le code prestashop pour vous permettre gérer des informations supplémentaires via vos modules dans les formulaires.

Pour ajouter des champs le hook est exécuté dans la fonction buildForm du fichier src/Core/Form/IdentifiableObject/Builder/FormBuilder.php

Les noms des hooks sont construits sous la forme suivante :

action . Container::camelize($formBuilder->getName()) . FormBuilderModifier

/**
     * @param string $formType
     * @param array $data
     * @param int|null $id
     * @param array $options
     *
     * @return FormInterface
     */
    private function buildForm($formType, $data, $id = null, array $options = [])
    {
        $formBuilder = $this->formFactory->createBuilder($formType, $data, $options);
        $this->hookDispatcher->dispatchWithParameters('action' . Container::camelize($formBuilder->getName()) . 'FormBuilderModifier', [
            'form_builder' => $formBuilder,
            'data' => &$data,
            'id' => $id,
        ]);
 
        return $formBuilder->getForm();
    }

Pour la création et la mise à jour des données les hooks sont appellés dans les fonctions handleFormUpdate ou handleFormCreate du fichier src/Core/Form/IdentifiableObject/Handler/FormHandler.php

Les noms des hooks sont construits sous la forme suivante :

actionBeforeUpdate . Container::camelize($form->getName()) . FormHandler
actionAfterUpdate. Container::camelize($form->getName()) . FormHandler

Voici le code exécuté par exemple après la mise à jour d’un formulaire

/**
     * @param FormInterface $form
     * @param int $id
     *
     * @return FormHandlerResultInterface
     */
    private function handleFormUpdate(FormInterface $form, $id)
    {
        $data = $form->getData();
 
        $this->hookDispatcher->dispatchWithParameters('actionBeforeUpdate' . Container::camelize($form->getName()) . 'FormHandler', [
            'form_data' => &$data,
            'id' => $id,
        ]);
 
        $this->dataHandler->update($id, $data);
 
        $this->hookDispatcher->dispatchWithParameters('actionAfterUpdate' . Container::camelize($form->getName()) . 'FormHandler', [
            'id' => $id,
            'form_data' => &$data,
        ]);
 
        return FormHandlerResult::createWithId($id);
    }

Exemples de noms de hooks

Suite aux informations précédentes voici donc des exemples de hook disponibles pour l’objet Category :

  • hookActionCategoryFormBuilderModifier
  • hookActionBeforeCreateCategoryFormHandler
  • hookActionAfterCreateCategoryFormHandler
  • hookActionBeforeUpdateCategoryFormHandler
  • hookActionAfterUpdateCategoryFormHandler

Objets disponibles.

A ce jour sur la version 1.7.6.0 ce fonctionnement est disponible pour les objets suivants :

  • SqlRequest
  • Customer
  • Language
  • Currency
  • WebserviceKey
  • Meta
  • Category
  • RootCategory
  • Contact
  • CmsPage
  • CmsPageCategory
  • Tax
  • Manufacturer
  • Employee
  • Profile
  • ManufacturerAddress

Exemple d’implémentation

Voici comment procéder pour rajouter des champs via un module qui s’appellera hh_sampleadminform

Pour l’exemple nous allons ajouter :

  • un champ simple
  • un champ langue

Voici le code complet du module avec l’implémentation des différents hooks évoqué plus hauts.

J’ai mis un maximum de commentaires directement dans le code pour expliquer les différentes possibilités.
( Et laissé le nom des classes complets pour s’y retrouver plus facilement )

Pour ceux qui utilisent déjà symfony la modification des formulaires sera relativement facile à comprendre

 

<?php
class Hh_SampleAdminForm extends Module
{
 
    /**
     * Hh_SampleAdminForm constructor
     * Instanciation du module
     */
    public function __construct()
    {
        $this--->name = 'hh_sampleadminform';
        $this->tab = 'others';
        $this->version = '0.2.0';
        $this->author = 'hhennes';
        $this->bootstrap = true;
        parent::__construct();
 
        $this->displayName = $this->l('Sample Admin form');
        $this->description = $this->l('Sample module for admin form hooks for ps 1.7.6 and > ');
    }
 
    /**
     * Installation du module
     * @return bool
     */
    public function install()
    {
        if (!parent::install()
            //Installation des hooks
            || !$this->registerHook([
                'actionCategoryFormBuilderModifier',
                'actionAfterCreateCategoryFormHandler',
                'actionAfterUpdateCategoryFormHandler',
            ])
        ) {
            return false;
        }
 
        return true;
    }
 
    /**
     * Modification du formulaire de la catégorie
     * @param array $params
     */
    public function hookActionCategoryFormBuilderModifier(array $params)
    {
        //Récupération du form builder
        /** @var \Symfony\Component\Form\FormBuilder $formBuilder */
        $formBuilder = $params['form_builder'];
 
 
        //Ajout de notre champ spécifique
        $formBuilder->add($this->name . '_newfield1',
            //Cf génériques symonfy https://symfony.com/doc/current/reference/forms/types.html
            // et spécificiques prestashop https://devdocs.prestashop.com/1.7/development/components/form/types-reference/
            \Symfony\Component\Form\Extension\Core\Type\TextType::class,
            [
                'label' => $this->l('Custom field 1'), //Label du champ
                'required' => false, //Requis ou non
                'constraints' => [ //Contraintes du champs
                    //cf. génériques symfony : https://symfony.com/doc/current/reference/constraints.html
                    // Ou vous pouvez écrire la votre cf. https://symfony.com/doc/current/validation/custom_constraint.html
                    new \Symfony\Component\Validator\Constraints\Length([
                        'max' => 20,
                        'maxMessage' => $this->l('Max caracters allowed : 20'),
                    ]),
                ],
                //La valeur peut être setée ici
                'data' => 'test valeur', //Valeur du champ
                // Texte d'aide
                'help' => $this->l('help text')
            ]
        );
 
        //Ou surchargée ici
        $params['data'][$this->name . '_newfield1'] = 'Custom value 1';
 
      //Ajout d'un champ langue
        $formBuilder->add($this->name . '_newfield_lang',
            // cf. https://devdocs.prestashop.com/1.7/development/components/form/types-reference/
            \PrestaShopBundle\Form\Admin\Type\TranslatableType::class,
            [
                'label' => $this->l('Custom field Lang'), //Label du champ
                'required' => false, //Requis ou non
                'type' => \Symfony\Component\Form\Extension\Core\Type\TextType::class // OU TextAreaType::class
            ]
        );
        //Définition des données du champ langue
        $languages = Language::getLanguages(true);
        foreach ( $languages as $lang){
            $params['data'][$this->name . '_newfield_lang'][$lang['id_lang']] = 'Custom value for lang '.$lang['iso_code'];
        }
 
        //On peut également changer facilement la donnée de n'importe quel autre champ du formulaire
        $params['data']['active'] = false;
 
        //Il faut bien penser à mettre cette ligne pour mettre à jour les données au formulaire
        $formBuilder->setData($params['data']);
    }
 
    /**
     * Action effectuée après la création d'une catégorie
     * @param array $params
     */
    public function hookActionAfterCreateCategoryFormHandler(array $params)
    {
        $this->updateData($params['form_data']);
    }
 
    /**
     * Action effectuée après la mise à jour d'une catégorie
     * @param array $params
     */
    public function hookActionAfterUpdateCategoryFormHandler(array $params)
    {
        $this->updateData($params['form_data']);
    }
 
    /**
     * Fonction qui va effectuer la mise à jour
     * @param array $data
     */
    protected function updateData(array $data)
    {
        //Réalisation du traitement de mise à jour
    }
 
 
}




Les nouveaux champs sont ensuites bien présents dans la fiche d’édition de la catégorie 🙂

Champs catégorie

Il est à présent facile de réaliser les traitements souhaités sur les différents objets , n’hésitez pas à partager vos astuces ou à remonter les problèmes rencontrés sur cette partie

87 réflexions sur “Prestashop 1.7 : Ajouter des champs dans un formulaire d’administration”

    1. Bonjour,

      Sur quelle entité souhaitez vous faire la modification ?
      Et ou souhaitez-vous afficher ces informations ?
      Cet article étant générique et orienté administration cette partie n’est effectivement pas évoquée.

      Cordialement,
      Hervé

  1. Et bien typiquement j’ai pu ajouter des champs supplémentaire dans le controller Manufacturer mais je ne suis pas parvenu à trouver la bonne méthode pour récupérer les données sur la page des marques du FrontOffice.

    En fait j’ai réussi en overridant la class Manufacturer et en y ajoutant les définitions du genre:

    Manufacturer::$definition[‘fields’][‘ml_country’] = array(‘type’ => self::TYPE_STRING, ‘lang’ => true, ‘validate’ => ‘isGenericName’, ‘size’ => 255);

    Mais il doit y avoir une autre méthode que je ne connais pas pour faire ça plus proprement non?

    1. Bonjour,

      Votre approche est la bonne si vous souhaitez surcharger l’entité Manufacturer.
      Dans le cas ou vous souhaitez que l’information soit disponible partout la solution la plus rapide reste de faire un override de l’objet.
      Pour plus d’évolutivité, vous pouvez incorporer cet override directement dans votre module, prestashop se chargera ensuite de l’ajouter/supprimer lors de l’installation/désinstallation du module.
      Je n’ai pas détaillé cette partie car elle n’est pas directement liée aux formulaires admin 😉

      Cordialement,
      Hervé

  2. Tout fonctionne très bien, sauf que j’ai déjà mon champ de type text en base de données avec une valeur dedans, lorsque je suis sur le formulaire d’édition de ma Catégorie, le champ ne se pré-remplit pas avec la valeur qui est pourtant déjà en DB.

    Merci pour votre aide herve !

    1. Bonjour,
      Est-ce que vous renseignez bien la valeur dans le hook ?
      $params['data'][$this->name . '_newfield1'] = 'Custom value 1';
      Attention car si c’est un champ spécifique et que vous souhaitez ajouter à l’objet il est toujours nécessaire de surcharger l’objet Catégorie.

  3. Si je fais $params[‘data’][‘community’] = ‘Custom value 1’; , alors la valeur de mon champ est écrasée ! ce que je ne souhaite pas, puisque je veux saisir moi-même la valeur à l’intérieur de mon input (et non pas via le code…)

    La classe Category est déjà surchagée et le champ a bien été ajouté à la variable self::$definition[‘fields’][‘community’] et en tant que variable de classe. Je ne comprends pas pourquoi l’input ne récupère pas la valeur que j’ai enregistré lorsque je suis sur la page d’édition. J’ai trouvé des références à un fichier EditableCategory.php dans le Core… mais cela me paraît lourd quand même, tout ça pour récupérer une valeur et pré-remplir le champ… normalement Symfony le fait tout seul dans le form mais pas ici…

    1. C’est justement ce que je disais c’est à vous de remplir la valeur du champ.
      Comme il est géré par un module il n’est pas remplis automatiquement.
      Dans votre cas il est donc nécessaire d’instancier la catégorie courante et d’assigner sa valeur au formulaire via un code du type
      $category = new Category($params['id]);
      $params[‘data’][‘community’] = $category->community;

  4. Hello,

    Pour l’enregistrement des données dans la base, j’utilise, dans la méthode updateData(), la classe Db et plus précisément insert() :

    Db::getInstance()->insert(« ps_category_lang », array( ‘id_category’ => (int)$params[‘id’],
    ‘id_lang’ => (int)$this->context->language->id,’color’ => $data[‘color’]););

    Est-ce la bonne approche?

    Merci d’avance !

    1. Bonjour,

      Cette question pourrait faire l’objet d’un article à part entière 🙂
      Car plusieurs approches sont possibles.
      – L’approche recommandée par prestashop est d’utiliser le concept cqrs, vous pouvez voir un exemple sur github : https://github.com/PrestaShop/demo-cqrs-hooks-usage-module/blob/master/ps_democqrshooksusage.php
      – L’approche override , c’est à dire surcharger l’objet et l’enregistrer via la méthode save de celui-ci.
      – Les requêtes directes telles que vous souhaitez le faire.

      Pour ma part je préfère actuellement utiliser l’approche objet qui ressemble plus à ce qui ce faisait sur les anciennes versions 🙂

    1. Super merci du retour :-), c’est effectivement plus propre qu’un override.

      Je n’ai jamais utilisé cette approche est-ce qu’elle marche correctement pour le front aussi ?
      Ce serait pas mal de mettre à jour ton module pour illustrer comment le faire « proprement ».
      Pour ma part je passe quasi exclusivement par des overrides dans le cas d’ajout de champs spécifiques à des objets, sans doute par habitude :p

      Cordialement,
      Hervé

  5. Bonjour,

    merci pour l’exemple, très instructif. Je cherche a rendre les champs traductibles, j’ai un Presta Multilangue, je galère un peu a trouver comment ajouter le selecteur de langue à côté des champs, une idée ?

    Merci

  6. Merci 😉

    En attendant j’ai cherché de mon côté et voici mon code (ne pas oublier les use PrestaShopBundle\Form\Admin\Type\TranslatableType;
    use Symfony\Component\Form\Extension\Core\Type\TextType;) :

    $formBuilder->add(‘texte_presentation’, TranslatableType::class, [
    ‘label’ => $this->l(‘Test Multilangue’),
    ‘required’ => false,
    ‘type’ => TextType::class,
    ‘data’ => array(« 1″=>’testfr’, « 2 »=>’testen’),
    ]);

    Bien entendu il faut loader les datas déjà enregistrées 😉

    Je me suis attaqué à la sauvegarde en BDD via la méthode updateData

    Après plusieurs tentatives différentes, voici ce qui marche :

    $cat = new Category((int)$params[‘id’]);
    $cat->texte_presentation = $data[‘texte_presentation’];
    $cat->update();

    J’avais tenté en passant par les services, mais je dois louper quelque chose, car la valeur reste vide dans la requete :

    $category_form_data_handler_service = $this->get(‘prestashop.core.form.identifiable_object.data_handler.category_form_data_handler’);
    $category_form_data_handler_service->update((int)$params[‘id’], $data);

    Hervé, vous êtes déjà passé par les services pour updater ?

    Merci

    1. Ma réponse va être un peu près similaire à celle-ci : https://www.h-hennes.fr/blog/2019/08/05/prestashop-1-7-ajouter-des-champs-dans-un-formulaire-dadministration/#comment-24601
      Le module de démo proposé par Prestashop : https://github.com/PrestaShop/demo-cqrs-hooks-usage-module/blob/master/ps_democqrshooksusage.php illustre plus en détails le concept.
      Je ne suis pas encore familier de l’utilisation de ce concept mais il nécessaire de créer par mal de fichiers pour gérer votre nouveau champ car il ne sera pas géré par les services natif 😉

  7. Merci pour le tuto.

    Y a t’il un moyen de passer de l’ancien override à cette méthode en gardant les valeurs des champs anciennement créés?

    J’arrive à afficher les nouveaux champs, mais je ne sais pas comment récupérer la value data du champ.

    Merci d’avance

  8. Si ça peut aider quelqu’un, pour le WYSIWYG multilingue :

    $cat = new Category((int)$params[‘id’]);
    $locales = Language::getLanguages(true);

    $formBuilder->add(‘description_2’, PrestaShopBundle\Form\Admin\Type\TranslateType::class, [
    ‘locales’ => $locales,
    ‘hideTabs’ => false,
    ‘label’ => $this->l(‘Description 2’),
    ‘required’ => false,
    ‘type’ => PrestaShopBundle\Form\Admin\Type\FormattedTextareaType::class,
    ‘data’ => $cat->description_2
    ]);

    1. Bonjour Mickael,

      Je n’ai effectivement pas documenté cet exemple, voici un exemple de code qui fonctionne :


      $locales = $this->get('prestashop.adapter.legacy.context')->getLanguages();
      $formBuilder->add($this->name . '_newfield_wysiwyg_lang_2', \PrestaShopBundle\Form\Admin\Type\TranslateType::class, [
      'type' => \PrestaShopBundle\Form\Admin\Type\FormattedTextareaType::class,
      'locales' => $locales,
      'hideTabs' => false,
      'required' => false,
      'options' => [
      'constraints' => [
      new \PrestaShop\PrestaShop\Core\ConstraintValidator\Constraints\CleanHtmlCleanHtml([
      'message' => $this->l('This field is invalid'),
      ]),
      ],
      ],
      ]);

      //Définition des données du champ langue
      $languages = Language::getLanguages(true);
      foreach ($languages as $lang) {
      $params['data'][$this->name . '_newfield_wysiwyg_lang_2'][$lang['id_lang']] = 'Custom value for lang ' . $lang['iso_code'];
      }

      Cordialement,
      Hervé

  9. Bonjour et merci pour ces supers tutos, j’ai cependant une erreur lors de l’update lorsque je souhaite faire une insertion en base de données.

    Tout ce que me donne Prestashop est cette erreur qui n’aide pas :
    « Une erreur inattendue s’est produite. [PrestaShopDatabaseException code 0] »

    Avez-vous déjà eu ce problème ?

    1. Bonjour,

      Sans connaitre votre version ni voir votre code je ne saurais pas vous dire votre erreur exacte
      Si ce n’est que c’est une erreur dans votre requête sql , comme le signifie : PrestaShopDatabaseException
      Mettez un try / catch autour de votre code pour capturer l’exception.

      Cordialement,
      Hervé

  10. Bonjour,

    Est-il possible d’ajouter une note d’information avec le champ? Comme on peut le voir sous certains champs dans une page catégorie de l’admin.

    Merci

  11. Bonjour ,

    ces quelques lignes m’ont l’air très intéressante et semblent résoudre à mon problème
    En revanche, je n’ai aucune idée de savoir ou ajouter ce bout de code
    si vous pouvez m’aider …
    Merci d’avance

    1. Bonjour Amar,
      Désolé mais je ne peux pas vraiment être plus clair.
      Cet article est à un public orienté développeur qui précise le fonctionnement de prestashop et donne un module d’exemple.
      Celui-ci peut servir de base pour vos développements futurs mais n’est pas destiné à une utilisation en production.

      Cordialement,
      Hervé

  12. Bonjour,
    Merci pour ce tuto très bien fait.
    Ici les exemples permettent d’ajouter un champs dans un formulaire.
    Mais dans mon cas je dois changer la règle de validation de mon champ « firstname » pour mon formulaire Customer.
    J’ai changé la règle de validation dans ma class Customer (en override) :

    ‘firstname’ => array(‘type’ => self::TYPE_STRING, ‘validate’ => ‘isString’, ‘required’ => true, ‘size’ => 255),

    Donc j’ai changé le validate en isString au lieu de isCustomerName

    Comment je peux modifier le validate de ce champs dans le formulaire d’édition en admin?

    Merci pour votre aide

    1. Bonjour,

      J’aurais tendance à penser que votre surcharge devrait suffire à appliquer la validation en back office également.
      Car dans le formulaire symfony ( cf. src/PrestaShopBundle/Form/Admin/Sell/Customer/CustomerType.php )
      Les seules contraintes ajoutées au champ sont qu’il ne doit pas être vide et une limite de longueur.
      N’hésitez pas à nous partager vos retours de tests sur le sujet.

      Cordialement,
      Hervé

  13. Bonjour,

    Je viens d’implémenter votre code dans un module (je vous en remercie car très utiles).

    Néanmoins, je n’arrive pas à enregistrer la valeur choisi dans le back-office.

    Comment fait-on pour enregistrer la valeur choisi ?

    Cordialement,

    Code :

    public function hookActionCmsPageFormBuilderModifier($params){
    //Récupération du form builder
    $formBuilder = $params[‘form_builder’];

    //Ajout de notre champ spécifique
    $formBuilder->add(‘use_title_h1’,
    //Spécificiques prestashop https://devdocs.prestashop.com/1.7/development/components/form/types-reference/
    SwitchType::class,
    [
    ‘label’ => $this->l(‘Utiliser le titre pour la balise H1 ?’), //Label du champ
    ‘required’ => true, //Requis ou non
    ‘choices’ => [
    ‘NON’ => false,
    ‘OUI’ => true,
    ],
    ]
    );

    $formBuilder->setData($params[‘data’]);
    }

    /**
    * Action effectuée après la création d’une catégorie
    * @param array $params
    */
    public function hookActionAfterCreateCmsPageFormHandler(array $params)
    {
    $this->updateData($params[‘form_data’]);
    }

    /**
    * Action effectuée après la mise à jour d’une catégorie
    * @param array $params
    */
    public function hookActionAfterUpdateCmsPageFormHandler(array $params)
    {
    $this->updateData($params[‘form_data’]);
    }

    /**
    * Fonction qui va effectuer la mise à jour
    * @param array $data
    */
    protected function updateData(array $data)
    {
    $cms = new CMS($data[‘id’]);
    $data[‘data’][‘use_title_h1’] = Tools::getValue(‘use_title_h1’);
    $cms->update();
    }

    1. Bonjour,
      Dans votre cas je vois que vous avez rajouté un nouveau champs sur la page cms
      Est-ce que votre classe CMS est bien surchargée avec votre nouveau paramètre ?
      L’affichage dans l’administration n’est pas suffisant si vous souhaitez modifier les paramètres d’un objet.

      Cordialement,
      Hervé

  14. Bonjour Hervé,

    Merci pour vos tutoriels et surtout pour les temps que vous passez à répondre aux questions.

    J’ai besoin de déplacer le bloc gestion des catégories dans la fiche produit de la colonne de droite, à la colonne du milieu, juste en dessous du bloc produit associés et je ne vois pas comment procéder.

    Pensez-vous que cette optimisation est possible, via un module et les hook ou via un module et du js ?

    merci pour votre aide.
    Cordialement.

    1. Bonjour,
      Je pense que ce doit être possible de déplacer cette boite en js ( ajouté via un module dans la fiche produit )
      En revanche ce n’est pas quelque chose que je recommande car la page d’édition des fiches produits est relativement complexe.
      Et il n’est pas exclus qu’un changement d’emplacement introduise des effets de bords.

      Cordialement,
      Hervé

  15. I did created file for add new field in category page, but its not working,
    can you help me and tell me any other files need modify or just I created file Hh_SampleAdminForm in module folder?

    1. Hi,
      You need to manage the insertion and update of your value in database in order to persist your data.
      If you check the comments of the articl you’ll find some clues as you’re not the first to ask 😉

      Regards,

  16. Bonjour et merci beaucoup pour cet article qui m’a servi un bon nombre de fois!
    Cependant je me pose la question suivante : comment afficher un placeholder dans les champs du formulaire ajoutés ou alors une petite ligne informative juste en dessous du champ? Y-a-t-il une clé du tableau des options de la méthode$formBuilder->add qui ferait cela? J’ai cherché sur le net.. en vain.

    1. Hi,
      No need to use the prestashop generator you juste have to create a directory hh_sampleadminform in module directory
      And to create a file hh_sampleadminform.php inside it when you copy/paste the given content
      This module is juste a proof of concept which display the field but do nothing more.

      Regards,

  17. Hello, thanks for response. I did as you said, uploaded folder with php file into module folder, but nothing happened. I am sorry to bother you, but i am just learning prestashop.

  18. Hello, yes it is in back office and when i try to install it error appears that it is not possible to install this module. I have only that folder with php file in it.

  19. Hello,
    I try to install but i see the error message :

    « L’action install est impossible pour le module hh_sampleadminform. Le module n’est pas valide et ne peut pas être chargé. »

    My version is 1.7.6.4

    Why ?

  20. Bonjour Hervé

    Merci pour ce tuto !
    Pour les catégories par exemple, ill faut créer le champs dans la table et l’ajouter dans l’override de la classe ou non ?
    Car tout fonctionne presque nickel, sauf l’enregistrement du champs

    PS 1.7.6.4

  21. Bonjour,

    Merci pour ce tuto très utile. Je m’interogge cependant : comment ajouter un champ de type image puisqu’ils ne sont pas enregistrés dans la base de données ? Dans mon cas, je souhaiterais ajouter un champ image dans les pages CMS (PS 1.7.6.0).

    D’avance merci pour votre retour 🙂

    1. Bonjour Gaëlle,
      Ce n’est effectivement pas l’image en elle même qui est envoyée en base de données.
      Mais il reste nécessaire de stocker à minima le nom du fichier avec son extension ( monimage.jpg par ex )
      Ainsi lors de son affichage en front ou en back on peut l’afficher en y ajoutant juste le chemin dans lequel le fichier a été envoyé. ( http://www.monsite.com/images/monimage.jpg)

      Cordialement,
      Hervé

  22. Bonjour,

    D’abord super doc, j’ai pu y trouver beaucoup d’info utiles, merci !
    Cependant je tente d’ajouter un champ texte aux catégories CMS avec l’éditeur WYSIWYG activé mais il ne s’affiche pas… (j’ai suivi ce que propose David Rensonnet) voilà le code que ça donne dans le hook pour le form des catégories CMS :

    public function hookActionCmsPageCategoryFormBuilderModifier(array $params) {

    […]

    $formBuilder->add(‘content’, TranslateType::class, [
    ‘locales’ => $locales,
    ‘label’ => ‘Content’,
    ‘hideTabs’ => false,
    ‘type’ => FormattedTextareaType::class,
    ‘required’ => false
    ]);

    […]
    }

    Prestashop est en version 1.7.6.5, et la classe CSS « autoload_rte » est bien ajoutée au textarea dans l’admin, la team de Presta aurait oubliée d’ajouter le JS ? Ou j’ai raté quelque chose ? (cache?)

    Merci d’avance pour votre réponse, à bientôt ! 🙂

  23. Après pas mal de recherche dans le code j’ai pu trouver qu’il manquait bien un JS ou que le JS cms_page.bundle.js a été compilé sans TinyMCE.

    Pour résumer, un JS est ajouté dans les templates d’ajout et de modification des catégories CMS (edit_category.form.twig / create_category.form.twig), et en le changeant par cms_pages_form.bundle.js, celui utilisé sur les pages d’ajout et modif. d’une page CMS, le WYSIWYG fonctionne bien de nouveau !

    C’est un problème seulement pour les catégories CMS, cependant j’avais aussi un soucis lors de l’ajout du champs au formulaire, il se retrouvait après les boutons de sauvegarde/annulation car le bout de code {{ form_rest(cmsPageCategoryForm) }}, qui complète le formulaire avec les champs ajoutés par un hook, se trouve après les boutons et non après les champs…

    1. Bonjour Aurélien,

      Merci pour votre partage d’expérience avec tout le monde sur ce problème 🙂
      De mon côté je n’avais effectivement pas testé l’implémentation spécifiquement sur les catégories CMS
      N’hésitez pas à ouvrir une issue github sur le projet prestashop pour que le problème soit corrigé dans les prochaines versions!

      Cordialement,
      Hervé

  24. Bonjour Hervé,

    Merci pour ce tutoriel très bien expliqué et très utile encore une fois..

    Je souhaite rajouter un champ d’édition du numéro de téléphone (que le client renseigne au signup, grace a votre tuto 😉 d’un client sur la page d’édition du client (Clients > Client X > Editer l’information).

    Je peux récupérer l’information en BDD dans la table ps_customer. Ma question : Est-il possible à partir du formulaire de la fonction hookActionCustomerFormBuilderModifier de récupérer l’ID du client et donc de pré-remplir la value de l’input avec la valeur en BDD ?

    Cordialement

    1. Bonjour Jean-Bapstiste,

      Oui bien sur vous pouvez récupérer l’identifiant de l’élément courrant via la variables $params[‘id’] dans votre fonction hookActionCustomerFormBuilderModifier
      N’hésitez pas à faire un simple print_r() du tableau des paramètres pour voir les éléments disponibles.

      Cordialement,
      Hervé

  25. Merci pour votre réponse, c’est parfait tout marche! Je ne pouvais pas print_r($params) car je reçois l’exception OutOfMemoryException. J’ai remarqué qu’il y a un petit oubli dans votre exemple du code du module, il manque une virgule après ‘test valeur’ pour bien indiquer qu’on change d’argument dans la fonction $formBuilder->add(..).

    Cordialement,

    Jean-Baptiste

  26. Bonjour Hervé,

    Étant principalement développeur Magento, je m’intéresse que depuis peu au développement sous Prestashop.. et à vrai dire votre article m’a fait gagné un temps fou.
    Petite question pour bonne pratique : j’ai pour objectif de rajouter un nouveau champs image au formulaire Manufacturer (une seconde image en plus du logo). Avez-vous une préconisation pour mettre en place un upload propre (comme celui du logo) ? Puis-je tout géré à partir d’une surcharge de la classe Manufacturer ?

    Merci pour votre retour,

    Pierre

    1. Bonjour Pierre,

      Développeur Magento est également mon activité principale ( ce qui ne montre pas ce blog 😀 )
      Vous pouvez gérer l’upload facilement de votre côté en utilisant la class Uploader de base qui gère bien les envois.
      Est-ce que vous souhaitez faire la modification en back ou en front office ?
      Il n’est pas recommandé d’utiliser les override mais en fonction du contexte il n’y a malgré tout souvent pas le choix.

      Cordialement,
      Hervé

  27. Bonjour,

    J’aimerais retirer des champs de la fiche employé comme « avatar, « news Letterman prestashop »,…

    Comment puis je faire?

    J aimerais aussi modifier la liste des employés

    Merci pour votre aide

  28. Bonjour,

    Tout d’abord (re)merci pour ce tuto puisque c’ets mon 2ème commentaire sur celui-ci..

    J’ai un champ « code_erp » que je souhaite ajouter dans la table customer. J’ai bien récupéré le code et modifié suivant mes besoins, et le champ s’affiche bien dans le formulaire client sur le BO, cependant il ne s’enregistre pas quand je met à jour une fiche client 🙁
    J’ai bien fait la surcharge du modèle :

    class Customer extends CustomerCore {
    public $code_erp;

    public function __construct($id = null) {
    //Définition du nouveau champ professionnal_id
    self::$definition[‘fields’][‘code_erp’] = [‘type’ => self::TYPE_STRING,
    ‘required’ => false, ‘size’ => 255
    ];
    parent::__construct($id);
    }
    }

    Pouvez-vous m’aider ? D’avance merci 🙂

  29. Ma faute, j’avais pas rempli la partie « UpdateData »… Ca marche mais je n’arrive pas à récupérer la valeur stockée dans la base dans le champ 🙁

    1. Bonjour Gaëlle,
      Super vous avez trouvé la solution 🙂
      En espérant que vos commentaires puissent aiguiller d’autres utilisateurs qui rencontreraient les mêmes problèmes.

      Cordialement,
      Hervé

  30. Bonjour,

    Merci pour ce tutoriel fort utile.

    j’ai repris votre module pour l’objet CustomerAddressForm, qui a la différence prend le hook actionValidateCustomerAddressForm pour la vérification.
    Cependant je bute sur le fonctionnement de ce hook pour vérifier les nouveaux champs.

    En effet l’attribut formfields étant en protected n’est pas utilisable et la fonction getfield() ne renvoie que les attributs native.

    Auriez vous une idée de comment il est possible de faire une vérification sur les nouveaux champs rajoutés?

    1. Bonjour Alex,

      A priori je ne vois aucun lien entre votre commentaire et cet article (?!)
      Pour autant il n’y a pas de problèmes particulier pour récupérer ces valeurs puisqu’il existe la fonction getField($field_name) qui est publique et permets donc d’accéder aux différents champs.

      Cordialement,
      Hervé

  31. Hervé,

    j’ai repris votre article en l’adaptant ) l’objet Adress. Effectivement cette article traite exclusivement de la partie admin. j’ai repris un autre article où vous présentiez l’ajout de champs sur le front également. Mea culpa.

    Si je puis me permettre de continuer ici, il semblerait que le hook de validation address pour le front ne garde pas la logique des autres objet(customer par exemple qui eux renvoie $param[‘fields’] et où on a accès aux fields).

    Pour les champs adresses, la validation se fait via un hook avec une dénomination différente(hookActionValidateCustomerAddressForm VS hookValidateCustomerFormFields). le hook en question renvoie un objet form, dont les éléments fields sont protected, donc non atteignable.

    Je trouve l’approche très bizarre de prestashop. mais je pense plutot qu’une chose m’échappe!
    mais je creuse pour comprendre 🙂

    Bien à vous,
    Alexandre

    1. Bonjour Alexandre,

      Effectivement la logique change un peu.
      En revanche comme je vous l’ai dit dans mon précédent message je ne vois pas de problèmes pour accéder aux informations pour autant.
      L’approche est différente mais c’est fonctionnel pour tout les champs de ce que j’en comprends ( j’ai regardé sur une 1.7.6.9 et une 1.7.7.1 )

      Dans le hook on récupère l’instance du CustomerAdressForm dans le paramètre ‘form’.
      Cf. classes/form/CustomerAddressForm.php:123

      if (($hookReturn = Hook::exec('actionValidateCustomerAddressForm', ['form' => $this])) !== '') {
      $is_valid &= (bool) $hookReturn;
      }

      Il n’est pas possible de boucler sur les champs, mais il est possible de récupérer les valeurs de chaque champ via la fonction getField de la classe CustomerAddressForm ( qui est publique )
      Et qui renvoie les informations de chaque champ

      public function getField($field_name)
      {
      if (array_key_exists($field_name, $this->formFields)) {
      return $this->formFields[$field_name];
      }

      return null;
      }

      Du coup dans votre module vous devriez pouvoir récupérer le code postal par exemple sous cette forme.
      $params['form']->getField('postcode');

      Cordialement,
      Hervé

  32. Je viens de tester pour la partie Customer, cela ne semble pas fonctionner sur la version 1.7.7.1
    Savez-vous si des modifications importante on eu lieu

    Cordialement

  33. Merci de nous partager tes connaissances et compétence, c’est très sympathique.

    J’ai bien réussi à ajouter un champ.
    Maintenant, est-ce possible dans le même (hook) d’ajouter plusieurs champ ?
    Si je fais n deuxième
    $formBuilder->add(
    J’ai une erreur sans rapport :
    Notice: Undefined variable: customerForm

    1. Bonjour David,

      Dans mon exemple l’ajout de 2 champs est bien fonctionnel.
      Je ne vois pas de raisons qu’on ne puisse en ajouter qu’un.
      Comment est-ce que tu as fait l’implémentation des 2 champs ?

      Cordialement,
      hervé

  34. Bonjour,

    Je suis bloqué pour passer les variables de la langue en cours et du contenu du champs traduisible dans la fonction updateData. J’utilise un simple Db::getInstance()->Execute() pour mettre la base de données à jour.

    Auriez-vous une suggestion?

    J’ai essayé de mettre print_r($params) dans la fonction hookActionCategoryFormBuilderModifier mais j’obtiens un OutOfMemoryException.

    Merci

    1. Bonjour Cédric,

      Pour réduire les champs à affichez vous pouvez faire un

      dump($params['form_data']);
      die('debug');

      Les champs langues sont des tableaux sous la forme ‘id_lang’ => ‘valeur’
      ex de la sortie du dump :
      « hh_sampleadminform_newfield_lang » => array:2 [▼
      1 => « Custom value for lang en »
      2 => « Custom value for lang fr »
      ]

      Cordialement,
      Hervé

  35. Bonjour,

    Je suis peut-être un peu perdu avec l’intégration symfony, mais comment se présente l’architecture d’un module pour un prestashop 1.7.6 et > ?
    quels sont les arborescences ? Y a-t-il des fichiers/dossiers en plus de la mainclass que vous proposer dans ce tuto ?

    Merci pour vos réponses.

    Bien à vous.

    1. Bonjour,
      Concrètement il n’y a pas de différences particulières sur un module compatible avec la version 1.7.6 et + de prestashop 🙂
      La manière de faire des modules reste la même.

  36. En fait j’ai réussi à installer le module grâce au generateur de module prestashop https://validator.prestashop.com/generator.

    Par contre je n’arrive pas à faire fonctionner la méthode updateData() pour sauvegarder les entrées du formulaire !
    En explorant le code, je ne vois pas non plus comment il associe l’entrée à l’id de la catégorie ?
    Avez-vous un exemple de code avec la méthode protected function updateData(array $data) fonctionnelle ?

    Merci à vous tous.

    1. Bonjour Florian,
      Il n’y a effectivement pas de code dans la fonction updateData, car cela dépends de l’implémentation que vous faites derrière.
      – Surcharge de l’objet catégorie
      – Enregistrement directement dans une base de données
      – Utilisation d’un objet prestashop spécifique.

      En l’état la récupération de l’identifiant de la catégorie ( via le paramètre $params[‘id’] ) ainsi que des données du formulaire via les paramètres $params[‘form’] vous permettent de faire ce que vous voulez 🙂

      Cordialement,
      Hervé

      1. Bonjour Hervé,

        Merci pour votre réponse.
        Je n’arrive cependant pas à enregistrer mon nouveau champs.
        Voici mon code peut-être cela sera plus aisé de comprendre où je m’y prend mal.

        <?php

        use PrestaShopBundle\Form\Admin\Type\TranslatableType;
        use Symfony\Component\Form\Extension\Core\Type\TextType;

        if (!defined(‘_PS_VERSION_’)) {
        exit;
        }

        class Ap_titlemenucat extends Module
        {
        protected $config_form = false;

        public function __construct()
        {
        $this->name = ‘ap_titlemenucat’;
        $this->tab = ‘administration’;
        $this->version = ‘1.0.0’;
        $this->author = ‘AquaPure’;
        $this->need_instance = 0;
        $this->bootstrap = true;
        parent::__construct();
        $this->displayName = $this->l(‘AP Title Menu Category’);
        $this->description = $this->l(‘Add a title menu name on categories’);
        $this->confirmUninstall = $this->l(‘Are you sure you want to uninstall AP Title Menu Category Module ?’);
        $this->ps_versions_compliancy = array(‘min’ => ‘1.7’, ‘max’ => _PS_VERSION_);
        }

        public function isUsingNewTranslationSystem()
        {
        return true;
        }

        public function install()
        {
        Configuration::updateValue(‘AP_TITLEMENUCAT_LIVE_MODE’, false);

        include(dirname(__FILE__).’/sql/install.php’);

        return parent::install() &&
        $this->registerHook(‘actionCategoryFormBuilderModifier’) &&
        $this->registerHook(‘actionAfterCreateCategoryFormHandler’) &&
        $this->registerHook(‘actionAfterUpdateCategoryFormHandler’) &&
        $this->alterCategoryLangTable();
        }

        public function uninstall()
        {
        include(dirname(__FILE__).’/sql/uninstall.php’);

        return parent::uninstall() && $this->uninstallAlterCategoryLangTable();
        }

        /**
        * Alter customer table, add module fields
        *
        * @return bool true if success or already done.
        */
        protected function alterCategoryLangTable()
        {
        $sql = ‘ALTER TABLE `’ . pSQL(_DB_PREFIX_) . ‘category_lang` ADD `menu_title` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL’;
        // CLEAN_INSTALATION 1/2 (if you don’t want to delete all data after an installation)
        // uncomment:
        Db::getInstance()->execute($sql);
        return true;
        }

        /**
        * Uninstalls sample tables required for demonstration.
        *
        * @return bool
        */
        private function uninstallAlterCategoryLangTable()
        {
        // CLEAN_INSTALATION 2/2 (if you don’t want to delete all data after an installation)
        // comment:
        $sql = ‘ALTER TABLE `’ . pSQL(_DB_PREFIX_) . ‘category_lang` DROP `menu_title`’;
        return Db::getInstance()->execute($sql);

        }

        /**
        * Modification du formulaire de la catégorie
        * @param array $params
        */
        public function hookActionCategoryFormBuilderModifier(array $params)
        {
        //Récupération du form builder
        /** @var \Symfony\Component\Form\FormBuilder $formBuilder */
        $locales = $this->get(‘prestashop.adapter.legacy.context’)->getLanguages();
        $formBuilder = $params[‘form_builder’];

        //Ajout d’un champ langue
        $formBuilder->add(‘menu_title’, TranslatableType::class,
        [
        ‘label’ => $this->l(‘Menu Title’), //Label du champ
        ‘required’ => false, //Requis ou non
        ‘type’ => TextType::class, // OU TextAreaType::class
        ‘locales’ => $locales //langues
        ]
        );
        //Définition des données du champ langue
        $languages = Language::getLanguages(true);
        foreach ( $languages as $lang){
        $params[‘data’][‘menu_title’][$lang[‘id_lang’]] = ‘Menu Title for lang ‘.$lang[‘iso_code’];
        }

        $formBuilder->setData($params[‘data’], $params);;
        }

        /**
        * Action effectuée après la création d’une catégorie
        * @param array $params
        */
        public function hookActionAfterCreateCategoryFormHandler(array $params)
        {
        $this->updateData($params[‘form_data’], $params);
        }

        /**
        * Action effectuée après la mise à jour d’une catégorie
        * @param array $params
        */
        public function hookActionAfterUpdateCategoryFormHandler(array $params)
        {
        $this->updateData($params[‘form_data’], $params);
        }

        /**
        * Fonction qui va effectuer la mise à jour
        * @param array $data
        */

        protected function updateData(array $data, $params)
        {
        $query = « UPDATE ` »._DB_PREFIX_. »category_lang` SET menu_title=' ».$data[‘menu_title’]. »‘
        WHERE id_category = ‘ ».(int)$params[‘id’]. »‘
        AND id_lang = ‘ ».(int)$data[‘locales’]. »‘ »;
        Db::getInstance()->Execute($query);

        }
        }

        1. Bonjour Florian,
          Il faut tester votre requête d’insertion dans la fonction updateData.
          Faites des dump() et analysez le contenu de vos array $data et $params dans la fonction pour identifier les données que vous avez.
          En faisant cette vérification vous pouvez constater que les champs de type langue sont fournis sous la forme d’un table

          $data['menu_title'] => [ 1 => ' valeur champ pour id_lang 1' ,2 => 'valeur champ pour id_lang 2'];

          Votre requête en l’état ne peut donc pas fonctionner.
          Il faut boucler sur les langues et faire une insertion par langue.

          Cordialement,

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *