Prestashop 1.7 : Ajouter des champs produit

Ce tutoriel est compatible avec les versions de Prestashop suivantes :
1.7 1.7.5 1.7.6 1.7.7 +
Cet article est assez ancien, malgré toute l'attention que j' apporte à mes contenus il est possible que celui-ci ne soit plus d'actualité.
N'hésitez pas à me le signaler si nécessaire via le formulaire de contact.

La nouvelle version 1.7 de Prestashop introduit de gros changements dans la gestion backoffice des fiches produits.
Cette page utilise les nouvelles méthodes symfony et tout les modules souhaitant ajouter des informations produits doivent se mettre à jour pour utiliser les nouvelles méthodes.
Comme d’habitude la documentation prestashop n’est pas exhaustive et je n’ai à date trouvé aucune information ni tutoriel sur le sujet.

Nous allons donc voir ensemble comment rajouter des nouveaux champs produits et les gérer dans l’administration avec Prestashop 1.7
Pour cela nous allons créer un module hhproduct.

Celui-ci ajoutera des nouveaux champs à notre entité produit.

  • custom_field
  • custom_field_lang
  • custom_field_lang_wysiwyg

Nous allons donc de surcharger l’objet Product afin de lui ajouter ces nouveaux champs.
Pour cela créer un fichier Product.php avec le contenu suivant dans le dossier override/classes du module.

class Product extends ProductCore {
 
    public $custom_field;
    public $custom_field_lang;
    public $custom_field_lang_wysiwyg;
 
    public function __construct($id_product = null, $full = false, $id_lang = null, $id_shop = null, \Context $context = null) {
        //Définition des nouveaux champs
        self::$definition['fields']['custom_field'] = [
            'type' => self::TYPE_STRING,
            'required' => false, 'size' => 255
        ];
        self::$definition['fields']['custom_field_lang']     = [
            'type' => self::TYPE_STRING,
            'lang' => true,
            'required' => false, 'size' => 255
        ];
        self::$definition['fields']['custom_field_lang_wysiwyg']     = [
            'type' => self::TYPE_HTML,
            'lang' => true,
            'required' => false,
            'validate' => 'isCleanHtml'
        ];
        parent::__construct($id_product, $full, $id_lang, $id_shop, $context);
    }
}

lors de l’installation du module ce fichier sera automatiquement placé dans le dossier des overrides par Prestashop.
Contrairement aux autres formulaires Prestashop les hooks spécifiques d’ajout produit sont situés dans un fichier twig,s situé sur le chemin suivant :
src/PrestaShopBundle/Resources/views/Admin/Product/form.html.twig

Les hooks disponibles sont les suivants :

  • displayAdminProductsExtra
  • displayAdminProductsMainStepLeftColumnMiddle
  • displayAdminProductsMainStepLeftColumnBottom
  • displayAdminProductsMainStepRightColumnBottom
  • displayAdminProductsQuantitiesStepBottom
  • displayAdminProductsPriceStepBottom
  • displayAdminProductsOptionsStepTop
  • displayAdminProductsOptionsStepBottom
  • displayAdminProductsSeoStepBottom ( dans le fichier src/PrestaShopBundle/Resources/views/Admin/Product/Include/form_seo.html.twig )

Vous pouvez voir leurs emplacements sur les captures ci-dessous.


prestashop-product-field-1

prestashop-product-field-2


prestashop-product-field-3


prestashop-product-field-4

Nous pouvons donc greffer notre module sur l’ensemble de ces hooks ou uniquement sur celui sur lequel vous souhaitez afficher vos champs.

Pour l’exemple nous utiliserons le hook AdminProductsMainStepLeftColumnMiddle
Dans cette fonction nous allons récupérer et afficher les nouvelles informations produit.

Comme vous pouvez le voir dans le fichier twig , la fonction récupère en paramètre l’identifiant du produit édité.

{{ renderhook('displayAdminProductsMainStepLeftColumnMiddle', { 'id_product': id_product }) }}

Voici le contenu de la fonction

/**
* Affichage des informations supplémentaires sur la fiche produit
* @param type $params
* @return type
*/
public function hookDisplayAdminProductsMainStepLeftColumnMiddle($params) {
$product = new Product($params['id_product']);
$languages = Language::getLanguages($active);
$this->context->smarty->assign(array(
'custom_field' => $product->custom_field,
'custom_field_lang' => $product->custom_field_lang,
'customer_field_lang_wysiwyg' => $product->custom_field_lang_wysiwyg,
'languages' => $languages,
'default_language' => $this->context->employee->id_lang,
)
);
 
return $this->display(__FILE__, 'views/templates/hook/extrafields.tpl');
}

Note : Pour l’instant je n’ai pas trouvé de helper spécifique pour générer le contenu du formulaire, nous allons donc devoir le réaliser à la main dans le fichier extrafields.tpl qui sera situé dans le dossier views/templates/hook/ du module

Voici son contenu :

<div class="m-b-1 m-t-1">
<h2>{l s='Custom Attribute from module' mod='hhproduct'}</h2>
 
<fieldset class="form-group">
<div class="col-lg-12 col-xl-4">
 
{*Champ Standard *}
<label class="form-control-label">{l s='my custom field' mod='hhproduct'}</label>
<input type="text" name="custom_field" class="form-control" {if $custom_field && $custom_field != ''}value="{$custom_field}"{/if}/>
 
{* Champ langue avec une structure particulière *}
<label class="form-control-label">{l s='my custom lang field' mod='hhproduct'}</label>
<div class="translations tabbable">
<div class="translationsFields tab-content">
{foreach from=$languages item=language }
<div class="tab-pane translation-label-{$language.iso_code} {if $default_language == $language.id_lang}active{/if}">
<input type="text" name="custom_field_lang_{$language.id_lang}" class="form-control" {if isset({$custom_field_lang[$language.id_lang]}) && {$custom_field_lang[$language.id_lang]} != ''}value="{$custom_field_lang[$language.id_lang]}"{/if}/>
</div>
{/foreach}
</div>
</div>
</div>
 
{* Champ wysiwyg avec TinyMce *}
<div class="col-lg-12 col-xl-12">
<label class="form-control-label">{l s='my custom lang field wysiwyg' mod='hhproduct'}</label>
<div class="translations tabbable">
<div class="translationsFields tab-content bordered">
{foreach from=$languages item=language }
<div class="tab-pane translation-label-{$language.iso_code} {if $default_language == $language.id_lang}active{/if}">
<textarea name="custom_field_lang_wysiwyg_{$language.id_lang}" class="autoload_rte">{if isset({$custom_field_lang_wysiwyg[$language.id_lang]}) && {$custom_field_lang_wysiwyg[$language.id_lang]} != ''}{$custom_field_lang_wysiwyg[$language.id_lang]}{/if}</textarea>
</div>
{/foreach}
</div>
</div>
</div>
 
</fieldset>
 
<div class="clearfix"></div>
</div>

Pour la gestion des langues,nous pouvons voir dans le fichier admin-dir/themes/default/js/bundle/product/form.js qu’il faut respecter une certaine structure d’affichage pour que le changement de langue soit géré.
Version 1.7.1 de prestashop

function switchLanguage(iso_code) {
$('div.translations.tabbable > div > div.tab-pane:not(.translation-label-' + iso_code + ')').removeClass('active');
$('div.translations.tabbable > div > div.tab-pane.translation-label-' + iso_code).addClass('active');
}

Le code a légèrement changé depuis :

function switchLanguage(iso_code) {
    $('div.translations.tabbable > div > div.translation-field:not(.translation-label-' + iso_code + ')').removeClass('show active');
    $('div.translations.tabbable > div > div.translation-field.translation-label-' + iso_code).addClass('show active');
}

Une fois notre module installé le résultat obtenu sera le suivant :

Champs produits supplémentaire prestashop 1.7

Pour finir voici le contenu complet du fichier hhproduct.php

<?php class HhProduct extends Module { public function __construct() { $this->name = 'hhproduct';
        $this->tab = 'others';
        $this->author = 'hhennes';
        $this->version = '0.1.0';
        $this->need_instance = 0;
        $this->bootstrap = true;
 
        parent::__construct();
 
        $this->displayName = $this->l('hhproduct');
        $this->description = $this->l('add new fields to product');
        $this->ps_versions_compliancy = array('min' => '1.7.1', 'max' => _PS_VERSION_);
    }
 
   public function install() {
        if (!parent::install() || !$this->_installSql()
                //Pour les hooks suivants regarder le fichier src\PrestaShopBundle\Resources\views\Admin\Product\form.html.twig
                || ! $this->registerHook('displayAdminProductsExtra')
                || ! $this->registerHook('displayAdminProductsMainStepLeftColumnMiddle')       
        ) {
            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_ . "product "
                . "ADD custom_field VARCHAR(255) NULL";
        $sqlInstallLang = "ALTER TABLE " . _DB_PREFIX_ . "product_lang "
                . "ADD custom_field_lang VARCHAR(255) NULL,"
                . "ADD custom_field_lang_wysiwyg TEXT NULL";
 
        $returnSql = Db::getInstance()->execute($sqlInstall);
        $returnSqlLang = Db::getInstance()->execute($sqlInstallLang);
 
        return $returnSql && $returnSqlLang;
    }
 
    /**
     * Suppression des modification sql du module
     * @return boolean
     */
    protected function _unInstallSql() {
       $sqlInstall = "ALTER TABLE " . _DB_PREFIX_ . "product "
                . "DROP custom_field";
        $sqlInstallLang = "ALTER TABLE " . _DB_PREFIX_ . "product_lang "
                . "DROP custom_field_lang,DROP custom_field_lang_wysiwyg";
 
        $returnSql = Db::getInstance()->execute($sqlInstall);
        $returnSqlLang = Db::getInstance()->execute($sqlInstallLang);
 
        return $returnSql && $returnSqlLang;
    }
 
    public function hookDisplayAdminProductsExtra($params)
    {
 
    }
 
    /**
     * Affichage des informations supplémentaires sur la fiche produit
     * @param type $params
     * @return type
     */
    public function hookDisplayAdminProductsMainStepLeftColumnMiddle($params) {
        $product = new Product($params['id_product']);
        $languages = Language::getLanguages($active);
        $this->context->smarty->assign(array(
            'custom_field' => $product->custom_field,
            'custom_field_lang' => $product->custom_field_lang,
            'customer_field_lang_wysiwyg' => $product->custom_field_lang_wysiwyg,
            'languages' => $languages,
            'default_language' => $this->context->employee->id_lang,
            )
           );
        /** 
         * @Todo Faire marcher le champ langue
         */
        return $this->display(__FILE__, 'views/templates/hook/extrafields.tpl');
    }
}

Le processus pourra surement être amélioré lors des prochaines mise à jour de prestashop, n’hésitez pas à remonter vos astuces.
Vous pouvez télécharger le code l’ensemble du module :hhproduct
Par contre si vous ajoutez/changez des champs il faudra réinitialiser le module à chaque fois pour la bonne prise en compte de l’override

 

Le code ne fonctionne pas ?

  • Supprimez les dossiers du cache ( app/cache/* pour version < 1.7.4 ou var/cache/* ) et réessayez.
  • Si ce n’est toujours pas bon, essayez de créer le produit via le code sans passer par la page de gestion des produits
  • Si cela ne fonctionne pas, votre surcharge n’est pas prise en compte ou comporte une erreur
  • Si cela fonctionne c’est qu’il y’a une erreur dans l’affichage ou l’envoi des données sur la page de gestion des produits

 

174 réflexions sur “Prestashop 1.7 : Ajouter des champs produit”

  1. Bonjour,

    Merci pour ce tuto.
    Une petite question : Y-a t’il une contrainte ou autre lorsqu’on ajoute des nouveaux champs dans la table product ?

    J’essai tout simplement à rajouter 2 champs :
    $sqlInstall = « ALTER TABLE  » . _DB_PREFIX_ . « product  »
    . « ADD promotion LONGTEXT NULL, »
    . « ADD ispaper tinyint(1) NULL »;

    Impossible de créer ces champs…comprends pas

    Merci.

    1. Bonjour,
      J’ai bien exécuté ajouté ces champs sans problèmes.
      Est-ce que la fonction d’installation du module renvoie une erreur ? ( ça devrait être le cas si la requête échoue )

      protected function _installSql() {
      $sqlInstall = "ALTER TABLE " . _DB_PREFIX_ . "product "
      . " ADD promotion LONGTEXT NULL,";
      . " ADD ispaper tinyint(1) NULL";

      return Db::getInstance()->execute($sqlInstall);
      }

      Cordialement,
      Hervé

      1. Bonjour Hervé Hennes
        Suivant votre tutoriel sur l’ajout des champs de produit, j’ai bien ajouter ces champs
        custom_field
        custom_field_lang
        custom_field_lang_wysiwyg
        Mon souci, ce qu’on me dit qu’il existe pas dans la base de données
        Voici l’exception unknown column ‘

        J’utilise prestashop 1.7.7.4
        Comment proceder svp?
        Merci

        1. Bonjour Blaise,

          Est-ce que vous avez bien ajouté les champs dans la base de données ?
          C’est géré dans le module d’exemple lors de son installation.
          Dans le doute vérifiez la structure des tables concernées.

          Cordialement,
          Hervé

  2. Bonjour Hervé,

    En fait, j’avais un problème avec ma base 🙁

    Par contre, au lieu d’utiliser le hook hookDisplayAdminProductsMainStepLeftColumnMiddle, j’utilise hookDisplayAdminProductsExtra. J’ai créer une vue dans views/templates/hook/displayAdminProductsExtra.tpl avec un formulaire (checkbox + input).
    Dans le fichier hhproduct.php, j’ai rajouté une fonction processAdminProductsExtra() pour recuperer les infos……est-ce une bonne solution ?

    Merci de votre retour.
    Pierre.

  3. Hervé,

    J’arrive bien à sauvegarder mes datas en base mais il passe pas par ma fonction. Du coup, quand je change de produit et que je reviens sur celui que j’ai modifier, ca met pas à jour.

    1. Bonjour Pierre,
      Il me semble que j’avais testé hookDisplayAdminProductsExtra sans succès également.
      Mais je n’avais pas persévéré comme l’autre méthode a fonctionné.
      Si vous parvenez à faire marcher ce point je suis intéressé par vos retours.

      Cordialement,
      Hervé

  4. En fait, ca marche avec le hookDisplayAdminProductsExtra.
    J’ai repris votre code et l’enregistrement se passe bien. Meme la recuperation. Je n’ai crée aucune fonction en plus, ca doit être la fonction de Prestashop par défault qui doit faire son boulot

  5. Bonjour,
    je cherche à modifier le fichier tpl.C’est possible de faire grouper les champs SPE dans un table et le mettre avec les autres onglets par défaut.

    Cordialement,

  6. Bonjour,
    Merci beaucoup pour ce tuto et vos remarques, elles m’ont beaucoup aidé à avancer sur la compréhension des modules (je débute mais j’apprends vite).
    Pour info les hook displayProductTab et displayProductTabContent sont dépreciés dans la version 1.7 Prestashop.
    https://github.com/PrestaShop/PrestaShop/pull/6216
    Je cherche une solution pour rajouter le champ custom_field_lang_wysiwyg dans un troisieme onglet du Front produit.

    1. Bonjour,

      En suivant la logique de ce pull request c’est très facile.
      Il suffit de greffer le module sur le hook displayProductExtraContent et de mettre le code suivant

      /**
      * Affichage du contenu sur la fiche produit
      * @param type $params
      */
      public function hookDisplayProductExtraContent($params)
      {

      $return = [];
      $return[] = (new PrestaShop\PrestaShop\Core\Product\ProductExtraContent())
      ->setTitle('Custom Lang Field')
      ->setContent($params['product']->custom_field_lang_wysiwyg);

      return $return;
      }

      En sachant que les caractéristiques du produit sont déjà disponibles dans les paramètres.
      Il faudrait que je détaille l’affichage dans le front, je vais voir si j’en fait un nouvel article ou si je complète celui-ci, car les questions sont récurrentes

      Cordialement,
      Hervé

  7. Bonjour,
    Oooops j’avais oublié d’activer le suivi, désolé !
    Merci beaucoup Hervé, pour la partie front si il ne s’agit que des onglets supplémentaires, pas de soucis particuliers ils sont générés automatiquement en fonction des tableaux compris dans le -> hookDisplayProductExtraContent($params) { /****
    *****/ }
    Je m’explique peut-être mal, mais normalement aucun appels ne sera nécéssaire dans dans le fichier product.tpl.

    Didier

  8. Bonjour,
    Hervé je n’ai plus de problème ! Je débute et je n’avais pas compris le fonctionnement du ProductExtraContent(). Jétais persuadé qu’il fallait passé par le front pour faire un {hook : h=’DisplayProductExtraContent’} ou demander un {$product.custom_field_lang_wysiwyg} dans le template (product.tpl)
    Finalement votre première réponse m’a ouvert les yeux de suite!
    Je vais essayé de me lancer maintenant dans la réalisation de ce petit module car je n’ai besoin que de 1 ou 2 onglets.

  9. Bonjour
    Je débute sous prestashop avec la 1.7.2.4 et je souhaite ajouter dans le BackOffice Catalogue\Produits, un champ Textarea dans l’onglet Déclinaisons des produits avec le Hook displayAdminProductsCombinationBottom.
    J’ai bien mon champ mais il se comporte comme un Type Text. C’est-à-dire qu’on ne peut pas aller à la ligne en faisant Entrer (avec le clavier).
    Pourtant le même champ avec le hook DisplayAdminProductsMainStepLeftColumnMiddle fonctionne correctement comme dans votre tuto.
    Avez-vous une idée de ce qui cloche ?

    1. Bonjour,

      Est-ce que votre texteara est de type wysiwyg ?
      Dans mon cas j’ai le même problème si l’éditeur ne se charge pas sur le bloc
      Est-ce que vous avez une erreur js dans la console ?

      Cordialement,
      Hervé

  10. Bonjour,
    concernant le textearea il est de type wysiwyg (avec la class= »autoload_rte »). J’ai « copier/coller » les lignes du template de votre example de champ wysiwyg.
    Au chargement du produit, je n’ai pas d’erreur js sur la console FireFox. Et comme vous le souligner, l’éditeur ne se charge pas pour mon champ.
    Savez-vous comment « forcer » le chargement de l’éditeur ?
    Cordialement.

    1. Bonjour,
      La problématique de ce hook est qu’il est chargé en ajax.
      Du coup l’initialisation du wysiwyg ne fonctionne pas car l’élément n’existe pas encore au chargement de la page.
      Je n’ai pour l’instant pas de solution à cette problématique, mais n’hésitez pas à la partager si vous la trouvez

      Cordialement,
      Hervé

  11. Salut,
    je ne parviens pas à créer ce module et à l’installer. n’y a t-il pas un zip que je pourrais utiliser comme c’est le cas pour le module de paiement ?
    Ou un lien pour les infos permettant d’installer ce module ?
    Merci

  12. Bonjour,
    je veux ajouter des champs dans la page de prix de produit… quel hook à utiliser ?
    si vous avez une image avec des hooks merci d’avance.
    Cordialement,

  13. Bonjour,
    j’ai essayé de faire apparaitre le 1 ere champ custom_field avec le displayAdminProductsMainStepLeftColumnMiddle comme le tuto , mais j’arrive pas!!
    malgré que le module est installé

  14. Tu as créer combien de fichier pour ton module, je pense que le minimum est de 3.

    Product.php /* pour surcharger l’Objet produit */
    extrafields.tpl /* pour afficher tes champs en BO */
    hhproduct.php /* le fichier de module */

  15. Bonjour,
    J’ai crée un nouveau module avec :
    hookDisplayAdminProductsExtra
    hookActionProductUpdate
    un noveau hook perso( j’ai ajouter cette ligne dans la page product.tpl :
    {hook h=’displayNewH’ product=$product}
    … il y a un code pour le refaire automatique au niveau d’installation du module?!
    )
    et avec un nouveau table
    Mais au niveau de backoffice, je peux pas récupère les donnes de ce champs pour le modifier ou supprimer.?!

    Cordialement,

    1. Bonjour,

      Pour votre problématique de nouveau hook front la logique reste la même que pour les versions précédentes. *
      Il faut que votre module implémente le hook , c’est à dire dans votre cas, la classe du module ait une fonction « hookDisplayNewH »
      Vous pouvez ensuite greffer le module via le backoffice sans le réinstaller.

      * une autre solution est possible cf. article : https://www.h-hennes.fr/blog/2017/06/05/prestashop-1-7-les-widgets/

  16. Bonjour,

    Merci pour ce tuto qui fonctionne parfaitement.

    Cependant, j’aimerais ajouter un champ de type fichier, pour ajouter une bannière associée à chaque produit.

    Est-ce possible? Comment traiter les données envoyées via le champ de type « file » ?

    Merci d’avance

    1. Bonjour,

      Pour l’instant je n’ai pas encore étudié ni était confronté à cette problématique.
      Je ne saurais donc vous répondre, si vous trouvez une solution je serais intéressé de votre retour d’expérience 😉
      Sinon je me mets ça dans ma liste en R&D, mais je ne sais pas trop quand j’aurais le temps de me pencher sur le sujet.

      Cordialement,

  17. Bonjour,
    pour ce qui est de l’ajout d’un champ Textarea dans hook displayAdminProductsCombinationBottom, j’ai trouvé une solution satisfaisante après 3 à 4 dizaines séries de tests.
    Dans mon module, le hook display un template qui contient un bouton, un textarea et quelques lignes de script.
    On doit obligatoirement avoir un page construite (donc après la création du DOM), pour pouvoir changer les propriétés du textarea, d’où le bouton et le script.
    Un script avec l’event Ready (‘$( document ).ready(function() {‘) ne fonctionne pas.
    Voici mes lignes de script:
    $(‘.edit-area’).on(‘click’,function(){
    var dataLibel = $( « #Lib_composition » );
    $(function() {
    tinySetup(dataLibel);
    });

    Ici ‘.edit-area’ est ma class de bouton, ‘Lib_composition’ est ma textarea.
    Au clic sur le bouton, on récupère la zone textarea que la fonction tinySetup, présente naturellement, transforme en vrai zone wysiwyg.

    J’espère qu’ils vont retravailler cette partie déclinaison car c’est vraiment un sac de ‘nœuds’ pour rester poli (afficher les mêmes données sous 3 formes avec un seul hook, ce n’est pas très fonctionnel)!!!

    1. Merci pour votre retour et votre solution pour ce point 🙂
      Effectivement cette partie est encore à optimiser par Prestashop, espérons que ce sera le cas dans les prochaines versions !

  18. Bonjour Hervé, merci pour ce tuto, très bien expliqué comme vos autres tutos.
    Je souhaite afficher, via les hooks que vous citez notamment hookDisplayAdminProductsMainStepLeftColumnMiddle, une checkbox nommée « dlc » qui me permet de déterminer qu’un produit a une date limite.
    Dans le template extrafields.tpl du module:
    {l s=’Concerné par une date de péremption ?’ mod=’produitdlc’}

    Mais côté fiche produit rien ne s’affiche.
    J’ai mis des tests en place et je vois que c’est le hook hookDisplayAdminProductsExtra qui appelé et non le MainStepLeftColumnMiddle. J’ai donc mis le même code dans les 2 hooks, mais sans plus de succès…

  19. En fait, je progresse dans le dépistage de l’erreur. C’est bien le hook ProductsExtra qui est activé et dans le code source je vois bien ma checkbox, mais dans l’onglet « Module » de la fiche produit, pas dans « Essentiel ».
    Le hook hookDisplayAdminProductsMainStepLeftColumnMiddle n’est pas du tout appelé.

    1. Bonjour Renaud,

      Avez-vous vérifié que le module st bien hook sur le hook hookDisplayAdminProductsMainStepLeftColumnMiddle ?
      Quelle est votre version de prestashop ?

      De mon côté je n’ai pas rencontré de problèmes particuliers avec ce hook.

      Cordialement,
      Hervé

  20. Bonjour Hervé, en fait le problème est résolu ! Tout simplement le hook n’était pas déclaré dans le formulaire twig: {{renderhook(‘displayAdminProductsMainStepLeftColumnMiddle’, { ‘id_product’: id_product }) }}
    Je pensais qu’il y figurait par défaut.
    Merci encore por la qualité de vos tutos, c’est vraiment une grande aide.

  21. Bonjour Hervé, j’ai juste une petite question. Dans le même esprit que ton tuto je veux ajouter des options au select « type_product », notamment drop-ship. Dans le form twig il y a seulement cette ligne {{ form_widget(form.step1.type_product) }}
    Peux tu me dire dans quel fichier et comment est généré ce form_widget ? Merci beaucoup d’avance. Si cela peut intéresser je ferai un tuto.

    1. Bonjour Renaud,

      Le champ {{ form_widget(form.step1.type_product) }} est un placeholder symfony.
      Les valeurs de cet élément sont définies dans le fichier : src/PrestaShopBundle/Form/Admin/Product/ProductInformation.php


      $builder->add('type_product', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array(
      'choices' => array(
      $this->translator->trans('Standard product', [], 'Admin.Catalog.Feature') => 0,
      $this->translator->trans('Pack of products', [], 'Admin.Catalog.Feature') => 1,
      $this->translator->trans('Virtual product', [], 'Admin.Catalog.Feature') => 2,
      ),
      'choices_as_values' => true,
      'label' => $this->translator->trans('Type', [], 'Admin.Catalog.Feature'),
      'required' => true,
      ))

      Ce code n’est pour l’instant pas surchargeable.

      Cordialement,
      Hervé

  22. Merci Hervé, tu confirmes ce que je pensais. J’avais modifié ce fichier manuellement (et d’autres), mais suite à ton tuto j’ai pensé qu’on pouvait faire ça plus proprement…d’autant que je vais passer à 1.7.2. Merci de ta disponibilité. Cordialement. Renaud.

  23. bonjour avez vous une version zippé prete pour insatllation car je n’arrive absolument pas à faire fonctionner le module ou bien pouvez vous m’aider ?

    j’ai trouvé une version zippé de votre module sur un blog dont je ne citerai pas le nom sans votre autorisation mais egalement impossible de le faire fonctionner.

    merci par avance

    1. Bonjour Nedim,

      Comme vous n’êtes pas le premier à me faire cette demande, je viens de mettre à jour le contenu de l’article avec un lien vers un fichier zip comportant l’ensemble des sources.
      Par contre j’insiste encore une fois sur le fait que c’est plus du code de démo du fonctionnement de la solution, qu’un module prêt à l’emploi et il nécessitera sans doute des modifications.
      Oui j’ai connaissance d’un article sur un autre blog, mais il doit marcher correctement normalement.
      Quelle est votre version de prestashop ?

  24. bonjour

    merci pour ce zip mais malheureusement je n’arrive toujours pas a faire fonctionner le module. j’ai proceder à son installation mais je n’ai aucun visu en admin.

    1. Bonjour,
      Comme dit ditfau, cela doit certainement être un problème de cache.
      De mon côté je vient de tester à nouveau l’archive sur une version 1.7.2.4 sans problème particulier.

      Cordialement,
      Hervé

    1. Bonjour Nedim,

      Dans mon archive j’avais un bug sur le champ wysywig langue.
      Je l’ai remise à jour et cela fonctionne bien. ( Les autres champs fonctionnaient déjà )
      Avez-vous d’autres modules installés qui pourraient interférer ?

  25. Bonjour,
    je cherche comment ajouter un simple boutton dans la fiche de produit ( coté admin ) pour exécuté une fonction qui me permet d’envoyer et recevoir des données.
    Cordialement,

  26. Bonjour,

    J’ai tenté sans succès d’ajouter également un textarea sur les déclinaisons. Dans mon cas une fois le module modifié pour faire correspondre le hook : displayAdminProductsCombinationBottom : aucun affichage sur les declinaisons.
    Pouvez-vous détailler les différentes étapes pour ajouter un champ aux déclinaisons ?

    D’avance, merci.

  27. Hello!

    Dear, after install this module, the product don’t update the existing fields, or new added. Also, the product don’t save if i change from Simple to pack, or other modification make. I think we need to make a function to update.

    1. Hello Sergiu,

      As this module create override, if it seems not to work the first thing to do is :
      remove file app/cache/prod/class_index.php ( or app/cache/dev/class_index.php if you are in debug mode )
      Can you try ?

      Regards,

  28. Bonjour Hervé, je me permets d’intervenir pour te poser une question simple: il est donc impossible, selon toi, de concevoir un module « transportable » pour les versions 1.7 dès lors que ce module a besoin de modifier des fichiers du répertoire « src » ?
    Merci d’avance !
    Renaud.

    1. Bonjour Renaud,

      Oui les fichiers du répertoire src ne sont pas modifiables ni suchargeables pour les versions inférieures à la 1.7.3
      Celle-ci apporte pas mal de nouveautés, mais je n’ai pas encore eut le temps de le tester je ne peux donc pas être affirmatif sur cette version.

  29. Mickaël Andrieu

    Bonjour Hervé, merci pour ses tutos.

    Je suis actuellement en train de tester ton tutoriel (et pas mal d’autres) sur la 1.7.4-dev et les champs s’affichent mais… les valeurs de ces champs ne sont pas persistés en base. Je pense que c’est pas magique mais du coup que ferais-tu pour contrôler la valeur de ces champs en formulaire à la soumission et pour les persister en base?

    A propos, j’ai trouvé une solution pour ne pas avoir à surcharger la classe Product et depuis la version 1.7.3 il est possible de surcharger les templates dans les modules 🙂

    Mickaël

    1. Bonjour Mickaël,

      Oui j’ai vu ta contribution qui permets de surcharger symfony à partir de la 1.7.3 🙂
      J’ai prévu de mettre à jour l’article en conséquence dans les prochains jours.

      Concernant la version 1.4-dev je t’avoue que je n’ai encore rien testé ^^
      Pour l’instant la saisie de ces informations fonctionne grâce à l’appel de la fonction de l’ancien controller cf.
      https://github.com/PrestaShop/PrestaShop/blob/1.7.3.x/src/PrestaShopBundle/Controller/Admin/ProductController.php#L516

      De ce que j’ai vu c’est toujours le cas, je suppose que c’est un point qui va disparaitre ?

      Cordialement,
      Hervé

  30. Mickaël Andrieu

    Non, en 1.7.4 on a rien touché 🙂

    C’est un gros objectif des prochaines releases de permettre aux développeurs d’ajouter et mettre à jour des champs.

    Je fais le tour de l’existant, et ça m’inquiète que ton tutoriel ne fonctionne plus. Si à l’occasion tu peux jeter un oeil sur la 1.7.3.0 qui vient de sortir et me partager ton point de vue n’hésites pas 🙂

    Bien à toi

    1. C’est noté je vais faire une repasse de ce tuto sur la 1.7.3 je regarderais sur la version de 1.4dev au passage également.
      Je ne manquerais pas de remonter les informations, c’est cool de voir que certains de mes articles peuvent intéresser Prestashop !

      On commence un nouveau projet au boulot sur un 1.7.3 , je ne manquerais pas de faire des retours, il y’en aura surement.

      Cordialement,
      Hervé

    2. Bon je viens de tester le module initial sur la version 1.3.0 et et sur la branche develop sur github.
      Tout fonctionne correctement de mon côté. 🙂
      Je suppose que le problème rencontré devait être lié à un fichier class_index.php non mis à jour ou à un conflit avec un autre module

    1. Cette contrib simplifie effectivement la surcharge des éléments.
      Par contre par ricochet elle fait disparaitre l’autocomplétion des propriétés de classes l’IDE :-/

      J’aurais également une question, le point qui m’avait le plus gêné sur l’ajout de champs produits était le retour en arrière sur la gestion de la partie HTML des champs.
      La version 1.6 proposait des helpers pratiques pour éviter ça, ce que je n’avais pas retrouvé sur les versions inférieures à la 1.7.3
      Est-ce que la surcharge symfony permets à présent de ne plus générer ce code à la main ? ( Je n’ai pas encore creusé ce point )

  31. Bonjour Hervé, je me permets de revenir vers toi car je viens à nouveau de tester ton module et je constate que:

    – Le hook displayAdminProductsExtra ajoute un bloc dans l’onglet « modules » avec un bouton Configurer, lequel affiche le formulaire du tpl extrafields.tpl. Cependant, lorsqu’on remplit le champ et valide, la BDD n’est pas mouvementée. J’ai bien entendu pris soin d’ajouter un champ à la table products.
    – Le hook displayAdminProductsQuantitiesStepBottom quant à lui n’affiche rien.

    Saurais-tu me conseiller ? Faut-il que je déclare ce champ BDD dans la class product ?

  32. PS: si j’ajoute ma variable dans la classe Products et, dans la fiche produit, je passe par l’onglet Module, puis Configure, puis saisis ma donnée, elle est bien enregistrée.
    Mais ce n’est pas pratique, et je ne comprends pas pourquoi mon template ne s’affiche pas dans l’onglet Quantité avec le hook displayAdminProductsQuantitiesStepBottom

    1. Bonjour Renaud,
      Effectivement c’est nécessaire de surcharger la classe Product dans tous les cas pour ajouter un nouveau champ.
      Concernant le hook : displayAdminProductsQuantitiesStepBottom , il faudrait vérifier si votre module est bien greffé.
      J’ai testé le module dernièrement sur les dernières versions de prestashop sans problèmes particuliers.

      Cordialement,
      Hervé

  33. Merci Hervé. Bah oui il est bien greffé – je le vois dans la table hook_module (position 1) avec le DisplayAdminProductsExtra qui a la position 5. J’ai vidé les caches, et rien n’apparaît dans l’onglet Quantités. J’ai la 1.7.1

  34. Hi guys!

    Thanks for your tutorial. I got those extra fields on the product page, even multilanguage is working … but: when i save some changes, those changes will not be saved in the database -> after a refresh of the page, the entered data is gone.

    I’ve already checked the transmitted POST data, and there are definitely my extra fields. But I guess they are not processed, since the breakpoint in the hookDisplayAdminProductsExtra or hookDisplayAdminProductsMainStepLeftColumnMiddle function in my module.php is not hit.

    Do you any idea how to solve this?

    Regards,

    1. Hi,

      Do you have well override the product model with your new fields ?
      Remember also to delete the file /app/cache/prod/class_index.php ( or /app/cache/dev/class_index.php if you’re in debug mode )

      Regards,

  35. Bonjour, Hervé

    Quand je clique sur enregistrer, les valeurs des champs ‘custom_field’,etc. ne peuvent plus s’enregistrer dans la base de données, je ne sais pas s’ils ont besoin d’un script d’enregistrement pendant le clique ou autre chose.

    1. Bonjour Nicolas,
      Même question que pour tous les autres qui ont rencontré ce problème.
      Avez-vous bien supprimé le fichier de cache des classes ? /app/cache/prod/class_index.php ( ou /app/cache/dev/class_index.php if you’re en mode debug )
      Si ce n’est pas le cas l’override de la classe produit n’est pas prise en compte et donc les champs ne sont pas enregistrés.

      Cordialement,
      Hervé

  36. Bonjour, Hervé

    Merci, j’ai trouvé le problème,

    c vrai que l’override de product.php n’est pas prise en compte.

    Deuxièmement il y a un erreur de frappe de variable pendant l’assignation vers le views :

    $this->context->smarty->assign(array(
    ‘custom_field’ => $product->custom_field,
    ‘custom_field_lang’ => $product->custom_field_lang,
    ‘customer_field_lang_wysiwyg’ => $product->custom_field_lang_wysiwyg,
    ‘languages’ => $languages,
    ‘default_language’ => $this->context->employee->id_lang,
    )
    );

    Nous voyons que le variable de champ textarea soit nommé : « customer_field_lang_wysiwyg ».

    Mais sur le view :

    {* Champ wysiwyg avec TinyMce *}

    {l s=’my custom lang field wysiwyg’ mod=’hhproduct’}

    {foreach from=$languages item=language }

    {if isset({$custom_field_lang_wysiwyg[$language.id_lang]}) && {$custom_field_lang_wysiwyg[$language.id_lang]} !=  »}{$custom_field_lang_wysiwyg[$language.id_lang]}{/if}

    {/foreach}

    il a porté le nom comme : « custom_field_lang_wysiwyg ».

    l’enregistrement de donnée n’arrive plus sur ce champ si on ne modifie pas.
    Pour les autres amies : faut pas faire de copie coller bêtement comme moi 🙂 :p .

  37. Bonjour,
    Je veux crée un hook avec un formulaire qui va être afficher dans le popup de cart et bien sure dans le back-office.
    Pour le moment j’ai crée (à laide des tuto en net) un hook qui affiche une chose statique.
    Vous avez un tuto ou un module avec un formulaire en font?
    Cordialement,

  38. bonjour herve, j’ai installer votre module ,fonctionne bien, quand j’ajoute une valeur dans le champ je le trouver dans la base de donnée mais dans la fiche produit je pas le trouver.

  39. Bonjour,

    Tout d’abord merci pour ce plugin. J’ai cependant une question: Je suis sur Prestashop 1.7.3.3 j’ai installé votre plugin tel quel depuis le lien de téléchargement.

    Celui-ci s’installe correctement et vient bien se placer dans la config produit en Back office. Cependant en remplissant les champs, il ne se passe rien, les données ne sont pas enregistrées. J’ai suivi vos conseils en commentaire en supprimant le ficher de cache, mais ça n’a rien changé.

    Avez-vous une idée du problème? Merci d’avance pour votre précieuse aide 😀

  40. Bonjour,

    Je n’arrive pas à faire fonctionner les champs avec les traductions : lorsque je change le texte sur une autre langue ça change pour toutes les autres.

    Avez-vous une idée du problème ? Je suis sur PS 1.7.3.3

  41. Bonjour,

    merci pour code vraiment très utile !
    Je viens cependant de me rendre compte d’un problème : dans le cas de multiboutique, la modification des nouveaux champs multilingues n’est pas prise en compte, cela ne fonctionne qu’en vue boutique. Savez-vous pourquoi, et comment résoudre ce point ?
    Merci !

    1. Bonjour,

      Merci d’avoir relevé ce problème, il est vrai que j’utilise très rarement le mode multiboutique, et donc je ne le teste pas forcément.
      Il faut que je regarde en détails, mais de but en blanc je ne vois pas trop la source de l’erreur.

    2. Bonjour,

      C’est un peu tard mais au cas où, reprenant un module existant pour y ajouter cette fonctionnalité, j’ai rencontré le même souci.

      A mon avis, ce doit être lié à la propriété multishop_context dans le construct du module.
      En recherchant un peu, on trouve des modules avec cette valeur à Shop::CONTEXT_ALL, ce doit être ce qu’il faut mettre pour que cela marche en multiboutique.

  42. Bonjour Hervé,

    Merci pour ce tuto bien utile.
    Je débute… J’ai réussi à insérer en backend les champs comme tu l’as si bien expliqué.
    Si j’ai bien saisi, maintenant des champs supplémentaires sont dans chaque produit dans la bdd. Maintenant comment fait on pour afficher la valeur de ce champ sur la page produit frontend?
    Je ne trouve aucun tuto la dessus…

    Au final je souhaite ajouter une courte description supplémentaire sous le prix dans la page produit.

    Merci de tes lumières!
    David

    1. Bonjour David,
      Normalement le nouveau paramètre est automatiquement disponible dans la variable $product dans le tpl
      Pour en être sur tu peux taper le code suivant n’importe ou dans le fichier themes/classic/templates/catalog/product.tpl ( a adapter en fonction de ton thème )
      {$product|print_r}

      Si ton attribut est bien présent il suffira ensuite de l’afficher ou tu le souhaite via le code
      {$product.tonAttribut}

      Cordialement,

  43. Bonjour Hervé,

    Merci pour ta réponse très rapide!
    J’y suis arrivé sur la page produit front avec le code suivant :
    {$product.monAttribut}, tout simplement. Merci!

    Maintenant je me dis que dans le pannier (cart.tpl), ce serait bien d’avoir ce champs également sous le prix…
    Et le code seul ne fonctionne pas dans Cart.tpl
    J’ai essayé de rajouter un Cart.php dans le dossier override/classes/module/hhproduct avec le code suivant :

    class cart extends CartCore {

    public $custom_field;

    public $custom_field_lang;

    public $custom_field_lang_wysiwyg;

    public function __construct($id_product = null, $full = false, $id_lang = null, $id_shop = null, \Context $context = null) {
    self::$definition[‘fields’][‘custom_field’] = [
    ‘type’ => self::TYPE_STRING,
    ‘required’ => false, ‘size’ => 255
    ];
    self::$definition[‘fields’][‘custom_field_lang’] = [
    ‘type’ => self::TYPE_STRING,
    ‘lang’ => true,
    ‘required’ => false, ‘size’ => 255
    ];
    self::$definition[‘fields’][‘custom_field_lang_wysiwyg’] = [
    ‘type’ => self::TYPE_HTML,
    ‘lang’ => true,
    ‘required’ => false,
    ‘validate’ => ‘isCleanHtml’
    ];
    parent::__construct($id_product, $full, $id_lang, $id_shop, $context);
    }
    }

    Voici mon message d’erreur :

    ContextErrorException in smarty_internal_templatebase.php(157) : eval()’d code line 214:
    Notice: Undefined index: product

    Je tatonne en prenant mon temps, mais je ne sais pas sur quoi avancer pour résoudre mon problème maintenant!

    Bien à toi

  44. (SUITE)
    J’ai réussi à insérer le champ sur ‘Featured product miniature’, ‘catégory miniature’, ‘quick-view product’

    Avec le code {$product.tonAttribut}

    ….mais pas sur le Panier (Cart) !!!

  45. Merci Hervé!

    Je me sens moins seul du coup avec mes bugs, je n’aurais jamais trouvé la solution seul!

    Bon…. ;-(
    même avec l’article que tu as fait, je nage complètement.
    Je me rends compte que je ne saisi pas du tout ce que tu explique, et à partir du tuto sur ‘product’ et du module hhproduct, je ne vois pas comment et quoi modifier dans cart.

    Je fais une pause… 😉
    En tout cas vraiment MERCI !!!

    1. Il faut simplement rajouter le hook hookActionCartGetProductsSqlQuery dans le module hhproduct ( le faire via un update du module si tu as déjà des données, sinon via un reset )
      Ce qu’il faut comprendre de ce hook c’est qu’il permets de modifier la requête exécutée pour récupérer les informations des produits dans le panier.

      Si j’adapte la logique à de cette article au propriétés ajoutées dans cet article le code à mettre en place serait le suivant :

      $params['query']->select('p.custom_field , pl.custom_field_lang , pl.custom_field_lang_wysiwyg');

      C’est à dire que je souhaite ajouter le champ custom_field de la table produit, et les champs custom_field_lang custom_field_lang_wysiwyg de la table product_lang dans ma requête.

  46. Je bloque! ;-(

    Voilà ce que j’ai fait :
    – J’ai modifié modules/hhproduct/hhproduct.php
    avec le code suivant :

    class HhProduct extends Module {

    public function hookActionCartGetProductsSqlQuery($params)
    {
    $params[‘query’]->select(‘p.custom_field , pl.custom_field_lang , pl.custom_field_lang_wysiwyg’);

    } …

    – J’ai créé override/classes/Cart.php
    avec le code suivant:

    class cart extends CartCore {

    public function getProducts($refresh = false, $id_product = false, $id_country = null)
    {
    if (!$this->id) {
    return array();
    }
    // Product cache must be strictly compared to NULL, or else an empty cart will add dozens of queries
    if ($this->_products !== null && !$refresh) {
    // Return product row with specified ID if it exists
    if (is_int($id_product)) {
    foreach ($this->_products as $product) {
    if ($product[‘id_product’] == $id_product) {
    return array($product);
    }
    }
    return array();
    }
    return $this->_products;
    }

    // Build query
    $sql = new DbQuery();

    // Build SELECT
    $sql->select(‘cp.`id_product_attribute`, cp.`id_product`, cp.`quantity` AS cart_quantity, cp.id_shop, cp.`id_customization`, pl.`name`, p.`is_virtual`,
    pl.`description_short`, pl.`available_now`, pl.`available_later`, product_shop.`id_category_default`, p.`id_supplier`,
    p.`id_manufacturer`, m.`name` AS manufacturer_name, product_shop.`on_sale`, product_shop.`ecotax`, product_shop.`additional_shipping_cost`,
    product_shop.`available_for_order`, product_shop.`show_price`, product_shop.`price`, product_shop.`active`, product_shop.`unity`, product_shop.`unit_price_ratio`,
    stock.`quantity` AS quantity_available, p.`width`, p.`height`, p.`depth`, stock.`out_of_stock`, p.`weight`,
    p.`available_date`, p.`date_add`, p.`date_upd`, IFNULL(stock.quantity, 0) as quantity, pl.`link_rewrite`, cl.`link_rewrite` AS category,
    CONCAT(LPAD(cp.`id_product`, 10, 0), LPAD(IFNULL(cp.`id_product_attribute`, 0), 10, 0), IFNULL(cp.`id_address_delivery`, 0), IFNULL(cp.`id_customization`, 0)) AS unique_id, cp.id_address_delivery,
    product_shop.advanced_stock_management, ps.product_supplier_reference supplier_reference’);

    // Build FROM
    $sql->from(‘cart_product’, ‘cp’);

    // Build JOIN
    $sql->leftJoin(‘product’, ‘p’, ‘p.`id_product` = cp.`id_product`’);
    $sql->innerJoin(‘product_shop’, ‘product_shop’, ‘(product_shop.`id_shop` = cp.`id_shop` AND product_shop.`id_product` = p.`id_product`)’);
    $sql->leftJoin(
    ‘product_lang’,
    ‘pl’,
    ‘p.`id_product` = pl.`id_product`
    AND pl.`id_lang` = ‘.(int)$this->id_lang.Shop::addSqlRestrictionOnLang(‘pl’, ‘cp.id_shop’)
    );

    $sql->leftJoin(
    ‘category_lang’,
    ‘cl’,
    ‘product_shop.`id_category_default` = cl.`id_category`
    AND cl.`id_lang` = ‘.(int)$this->id_lang.Shop::addSqlRestrictionOnLang(‘cl’, ‘cp.id_shop’)
    );

    $sql->leftJoin(‘product_supplier’, ‘ps’, ‘ps.`id_product` = cp.`id_product` AND ps.`id_product_attribute` = cp.`id_product_attribute` AND ps.`id_supplier` = p.`id_supplier`’);
    $sql->leftJoin(‘manufacturer’, ‘m’, ‘m.`id_manufacturer` = p.`id_manufacturer`’);

    // @todo test if everything is ok, then refactorise call of this method
    $sql->join(Product::sqlStock(‘cp’, ‘cp’));

    // Build WHERE clauses
    $sql->where(‘cp.`id_cart` = ‘.(int)$this->id);
    if ($id_product) {
    $sql->where(‘cp.`id_product` = ‘.(int)$id_product);
    }
    $sql->where(‘p.`id_product` IS NOT NULL’);

    // Build ORDER BY
    $sql->orderBy(‘cp.`date_add`, cp.`id_product`, cp.`id_product_attribute` ASC’);

    if (Customization::isFeatureActive()) {
    $sql->select(‘cu.`id_customization`, cu.`quantity` AS customization_quantity’);
    $sql->leftJoin(
    ‘customization’,
    ‘cu’,
    ‘p.`id_product` = cu.`id_product` AND cp.`id_product_attribute` = cu.`id_product_attribute` AND cp.`id_customization` = cu.`id_customization` AND cu.`id_cart` = ‘.(int)$this->id
    );
    $sql->groupBy(‘cp.`id_product_attribute`, cp.`id_product`, cp.`id_shop`, cp.`id_customization`’);
    } else {
    $sql->select(‘NULL AS customization_quantity, NULL AS id_customization’);
    }

    if (Combination::isFeatureActive()) {
    $sql->select(‘
    product_attribute_shop.`price` AS price_attribute, product_attribute_shop.`ecotax` AS ecotax_attr,
    IF (IFNULL(pa.`reference`, \’\’) = \’\’, p.`reference`, pa.`reference`) AS reference,
    (p.`weight`+ pa.`weight`) weight_attribute,
    IF (IFNULL(pa.`ean13`, \’\’) = \’\’, p.`ean13`, pa.`ean13`) AS ean13,
    IF (IFNULL(pa.`isbn`, \’\’) = \’\’, p.`isbn`, pa.`isbn`) AS isbn,
    IF (IFNULL(pa.`upc`, \’\’) = \’\’, p.`upc`, pa.`upc`) AS upc,
    IFNULL(product_attribute_shop.`minimal_quantity`, product_shop.`minimal_quantity`) as minimal_quantity,
    IF(product_attribute_shop.wholesale_price > 0, product_attribute_shop.wholesale_price, product_shop.`wholesale_price`) wholesale_price
    ‘);

    $sql->leftJoin(‘product_attribute’, ‘pa’, ‘pa.`id_product_attribute` = cp.`id_product_attribute`’);
    $sql->leftJoin(‘product_attribute_shop’, ‘product_attribute_shop’, ‘(product_attribute_shop.`id_shop` = cp.`id_shop` AND product_attribute_shop.`id_product_attribute` = pa.`id_product_attribute`)’);
    } else {
    $sql->select(
    ‘p.`reference` AS reference, p.`ean13`, p.`isbn`,
    p.`upc` AS upc, product_shop.`minimal_quantity` AS minimal_quantity, product_shop.`wholesale_price` wholesale_price’
    );
    }

    $sql->select(‘image_shop.`id_image` id_image, il.`legend`’);
    $sql->leftJoin(‘image_shop’, ‘image_shop’, ‘image_shop.`id_product` = p.`id_product` AND image_shop.cover=1 AND image_shop.id_shop=’.(int)$this->id_shop);
    $sql->leftJoin(‘image_lang’, ‘il’, ‘il.`id_image` = image_shop.`id_image` AND il.`id_lang` = ‘.(int)$this->id_lang);

    Hook::exec(‘actionCartGetProductsSqlQuery’,[‘query’ => &$sql]);

    $result = Db::getInstance()->executeS($sql);

    /**
    * Nouveau Hook pour modifier la requête de sélection des produits du panier
    * Cf. override du cart
    * @param array $params => ‘query’ => &DbQuery
    */
    /*
    * module: hhproduct
    * date: 2018-07-20 16:52:03
    * version: 0.1.0
    */
    public function hookActionCartGetProductsSqlQuery($params)
    {
    $params[‘query’]->select(‘p.custom_field , pl.custom_field_lang , pl.custom_field_lang_wysiwyg’);
    }

    – J’ai vider le cache. disable module hhproduct puis enable.
    – ajouter la ligne {$hook hookActionCartGetProductsSqlQuery}
    dans theme/templates/checkout/_partials/cart-detailled-product-line.tpl

    Je suis complétement à côté de la plaque ou juste un peu?

    Merci Hervé!

    1. Aucun intérêt de faire ça :
      – ajouter la ligne {$hook hookActionCartGetProductsSqlQuery} dans theme/templates/checkout/_partials/cart-detailled-product-line.tpl
      Attention activer/ désactiver un module n’a aucune incidence sur ses hooks.
      Il faut soit le mettre à jour, soit le désinstaller / réinstaller pour que les hooks soient bien pris en compte.

  47. Je n’y arrive pas…. sorry.

    Tu m’as déjà donné beaucoup de temps, je reprendrais tout demain à zéro…
    En faisant un reset du module j’ai dû tout recommencer…..le message d’erreur était sur toutes les pages.

    Là j’ai réussi à tout repositionner dans les pages product qui fonctionnaient. Sauf Cart. J’ai tout repris au début.

    Si ce n’est pas abusé… un pas à pas me serait nécessaire pour sortir ce champs custom_field sur la page panier ….

    Merci de ton aide précieuse!
    Bonne soirée

    1. Si la surcharge ne fonctionne pas correctement tu peux également rajouter directement tes champs dans la surcharge du panier.
      A la fin du premier $sql->select(

      Penses bien a vider le cache pour que la surcharge soit prise en compte.

  48. Yeeeesssss !
    Ca fonctionne.

    L’erreur basique venait de la Majuscule à cart dans l’override Class Cart :
    Début du code
    « class cart extends CartCore  »
    Il fallait
    « class Cart extends CartCore »

    2 jours 1/2 la dessus….

    J’imagine du coup que ta proposition avec le Hook devrait fonctionner sans problème, mon erreur devait être la même. Je testerais pour bien comprendre.

    En tous cas encore un Grand merci pour le temps que tu m’as accordé!

  49. Bonjour, j’ai voulu installé le module avec le fichier zip téléchargé mais ça n’a pas marché sur 1.7.4.3 au premier essaie, au deuxieme c’était ok. Par contgre il n y a aucun nouveau champs disponible en BO sur les fiches produit.

    1. Bonjour,
      Pensez à bien vider le cache, et si nécessaire manuellement ( supprimer le dossier /var/cache/dev/ ou /var/cache/prod sur cette version ), sur les versions précédentes c’est app/cache

      Cordialement,

  50. Bonjour Hervé,

    Merci pour ce tuto et ce module, cela m’a permis de m’en inspirer et d’ajouter avec succès 3 champs personnalisés sur ma page produit !

    J’ai alors voulu ajouter 3 champs identiques sur les déclinaisons de produit (table ps_product_attribute). Même module, nom des champs différents, mais je n’y arrive pas.

    J’ai surchargé ma classe Combination comme ceci :

    class Combination extends CombinationCore {

    public $champ_1;
    public $champ_2;
    public $champ_3;

    public function __construct($id_product = null, $full = false, $id_lang = null, $id_shop = null, \Context $context = null) {
    //Définition des nouveaux champs
    self::$definition[‘fields’][‘champ_1’] = [
    ‘type’ => self::TYPE_STRING,
    ‘required’ => false, ‘size’ => 255
    ];
    self::$definition[‘fields’][‘champ_2’] = [
    ‘type’ => self::TYPE_STRING,
    ‘required’ => false, ‘size’ => 255
    ];
    self::$definition[‘fields’][‘champ_3’] = [
    ‘type’ => self::TYPE_STRING,
    ‘required’ => false, ‘size’ => 255
    ];
    parent::__construct($id_product, $full, $id_lang, $id_shop, $context);
    }
    }

    Et j’ai rajouté dans le fichier principal de mon module :

    public function hookDisplayAdminProductsCombinationBottom($params) {
    $combination = new Combination($params[‘id_product_attribute’]);
    $this->context->smarty->assign(array(
    ‘champ_1’ => $combination->champ_1,
    ‘champ_2’ => $combination->champ_2,
    ‘champ_3’ => $combination->champ_3,
    )
    );

    return $this->display(__FILE__, ‘views/templates/hook/combinationfields.tpl’);
    }

    Mon template est similaire à celui que j’utilise pour le produit, j’ai bien ajouté les champs en base SQL (je les vois) et j’ai bien fait ceci :
    registerHook(‘displayAdminProductsCombinationBottom’)

    Pour autant, à l’install du module j’ai l’erreur suivante :

    Error!
    L’action enable est impossible pour le module extrafields. Impossible d’installer la surcharge : La méthode __construct dans la classe Combination est déjà surchargée.

    J’ai beau regarder ma classe Combination je ne vois pas de moyen de l’écrire autrement.

    Avez-vous une idée ? Je n’ai trouvé absolument aucune référence sur internet qui m’aide…

    Cordialement,

    1. Bonjour Ludo,
      Votre erreur semble claire, la classe des combinaisons est déjà surchargée.
      Regardez dans le dossier overrides/classes/ le fichier Combination.php doit déjà exister avec des modifications qui rentre en conflit avec celles de votre module.
      Si c’est votre site et que vous ne souhaitez pas distribuer votre module vous pouvez donc ajouter vos modifications directement dans ce fichier.

      Cordialement,
      Hervé

    2. Bonjour Ludo,
      J’aimerai faire la meme chose que vous, a savoir rajouter un champ sur la fiche declinaison.
      Avez vous reussi? parce que de mon coté je n’y arrive pas, rien ne s’affiche.
      Si vous avez la solution je suis intéressé s’il vous plait.

      Merci d’avance

  51. Bonjour Hervé,

    C’était bien ça ! L’erreur était claire et c’est pourtant la première chose que j’ai vérifié, mais noyé dans mes tests, j’avais dû passer à côté : il y avait bien déjà un fichier Combination.php dans classes/override. En le supprimant j’ai pu installer mon module.

    Une dernière chose sans vouloir abuser de votre temps… j’ai bien mes champs sur ma déclinaison produit désormais mais quand je rentre une valeur à l’intérieur et que j’enregistre elle n’est pas sauvegardée en base. Et pourtant lorsque je rentre une valeur en base via phpMyAdmin, elle se retrouve bien dans mon champ à l’actualisation de ma page.

    Le binding est fait mais que dans un sens.

    Si vous avez une idée je suis preneur. Je vais continuer de chercher de mon côté.

    Cordialement,

    1. Bonjour,
      Penser a bien vider le cache et à supprimer le dossier class_index.php contenu dans celui-ci pour vérifier que l’override est bien pris en compte.
      Vous pouvez valider également le bon fonctionnement de votre objet prestashop en essayant de créer un objet combination directement avec un appel de classe
      Ex :
      ( Il faudra également rajouter tous les champs obligatoire de l’objet Combination)

      $combination = new Combination();
      $combination->votreparametre = 'votre valeur';
      $combination->save();

      Si ce code est fonctionnel c’est que les informations nécessaire ne sont pas envoyés du controller de l’admin vers votre objet.

      Cordialement,

  52. Bonjour,
    J’ai suivi votre tuto, je suis en version 1.7.4.4, et ça ne fonctionne pas..
    L’installation du module se fait, aucune erreur, mais mon champs n’apparaît pas sur le back office :

    public function displayAdminProductsMainStepLeftColumnMiddle($params) {
    $product = new Product($params[‘id_product’]);

    $this->context->smarty->assign(array(
    ‘id_product_jshop’ => $product->id_product_jshop
    )
    );

    if (substr(_PS_VERSION_, 0, 3) == « 1.6 »)
    return $this->display(__FILE__, ‘/views/templates/1.6/admin/tabproduitjshop.tpl’);
    else return $this->display(__FILE__, ‘/views/templates/1.7/admin/jshop.tpl’);
    }

    $this->registerHook(‘displayAdminProductsMainStepLeftColumnMiddle’);

    {l s=’Custom Attribute from module’ mod=’jshopimport’}

    {l s=’ID JSHOP’ mod=’jshopimport’}

    Une idée?

  53. Bonjour Hervé,
    Je n’ai pas réussi à ajouter des champs sur les déclinaisons sans modifier Prestashop.
    Tous les attributs des combinaisons sont « en dur » dans le code donc j’ai dû ajouter mes champs dans les classes, les controllers, les templates voire les requêtes SQL Prestashop directement.
    Voici les fichiers que j’ai modifiés au final :
    classes/Combination.php
    classes/Product.php
    src/Adapter/CombinationDataProvider.php
    src/Adapter/Product/AdminProductWrapper.php
    src/PrestaShopBundle/Form/Admin/Product/ProductCombination.php
    src/PrestaShopBundle/Resources/views/Admin/Product/Include/form_combination.html.twig
    src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Forms/form_combination.html.twig

    Dommage de ne pas avoir pu passer par un module, cela rend mon Prestashop difficile à mettre à jour désormais.

    Merci pour vos réponses.

    1. Bonjour Ludo,

      Merci de votre retour.
      De mon côté je prévoie de mettre à jour ce tutoriel dans les prochaines semaines ( sans doute via un nouvel article ) en testant l’approche expliquée dans la documentation sur la page suivante :
      https://devdocs.prestashop.com/1.7/modules/concepts/forms/admin-forms/
      C’est un point qui m’embête un peu dans cette version 1.7 en fonction des versions l’approche de développement ne peut plus être la même.
      Cela complique la compatibilité des modules.

  54. Bonjour,

    J’ai suivi les dernières lignes de votre tuto, mais rien à faire, mon champs supplémentaire ne s’affiche toujours pas sur ma fiche produit, et je n’ai aucune erreur PHP ou à l’installation de mon module.

    Ces hook ne fonctionnent plus en version 1.7.4 ?

  55. Bonjour tout le monde,

    Pour faire fonctionner ce module afin d’avoir du texte sous les photos des fiches produit il faut modifier le fichier tpl qui est dans le zip (donc avant décompression) du module
    « \hhproduct\views\templates\hook\extrafields.tpl »
    Remplacez tous les termes « tab-pane » par « translation-field » (y en a 2).

    Ensuite dans le fichier tpl:
    \templates\catalog\product.tpl il faut
    Ajoutez à la ligne 74:

    {$product.custom_field_lang}
    {$product.custom_field_lang_wysiwyg nofilter}

    Puis dans le fichier custom.css il faut ajouter ce code:
    .texte-produit {
    box-shadow: 2px 2px 8px 0 rgba(0,0,0,.2);
    margin-top: 1rem;
    background: #fff;
    padding: 1.25rem 1.875rem;
    }

    Et voilà! on dirait que c’est une zone native du theme classic, a modifier comme on veut celon le thème utilisé, on peux changer la classe. par un id#, etc..

    1. Bonjour Sylvain,
      Si l’autre module réalise également une surcharge de la classe produit il est possible qu’il y’ai un conflit.
      Dans ce cas il faudra appliquer la surcharge manuellement ( c’est à dire copier les nouvelles propriétés ajoutées par le module dans le fichier situé dans override/classes/Product.php )

      Cordialement,
      Hervé

  56. bonjour, merci pour votre disponibilité et pour ce tutoriel, j’ai une question, est-il possible d’utiliser l’importation par fichier csv de ces champs?

    1. Bonjour,
      Malheureusement non ce n’est pas le cas.
      Il faut apporter des changements dans le fichier controllers/admin/AdminImportController.php pour y ajouter les nouveaux champs pour que cela fonctionne.

      Cordialement,
      Hervé

  57. Bonjour,

    Merci pour cet article qui fait ganger un temps fou. Pour les classes css, au niveau du changement de langue il semblerait que cela ai changé dans la version 1.7.5.1, j’ai remplacer les classes par les suivantes:
    {foreach from=$languages item=language }

    {if isset({$custom_field_lang_wysiwyg[$language.id_lang]}) && {$custom_field_lang_wysiwyg[$language.id_lang]} !=  »}{$custom_field_lang_wysiwyg[$language.id_lang]}{/if}

    {/foreach}
    Et le changement fonctionne pour moi.

    Cordialement

  58. Bonjour, j’exploite actuellement votre module et je voudrais faire un retour à la ligne dans le champs « my custom lang field ». Seulement je sais pas comment faire, j’ai essayer de mettre un >br/< il est bien interprété comme du code malgré que je l'ai écrit dans le champs mais par contre il ne prend pas effet, y a pas de retour à la ligne. Savez vous comment faire ? Merci. Une autre question: Qu’est ce qui ce passe après avoir rempli plein de champs sur fiche produit et qu’on désinstalle le module ? On perd tout ? Ou bien en le réinstallant tout revient ? Merci de votre réponse.

    1. Bonjour,

      Effectivement le champ my custom lang field n’a pas vocation à contenir du code html.
      C’est pour cela qu’il n’est pas interprété.
      Je viens d’ailleurs de remarquer qu’une partie du contenu de mon article a disparu … je l’ai remis en place cela devrait être plus clair à présent.
      Pour gérer le html il faut appliquer le même type et la même validation que pour le champ custom_field_lang_wysiwyg si vous voulez utiliser du html dans ce champ.

      Si vous désinstaller le module tout le contenu additionnel disparait, car la désinstallation supprime les champs dans la base de données.

      Cordialement,
      Hervé
      Il est de nouveau en place.

  59. Bonjour, merci pour cet article! concernant la prise en compte du champs au niveau du listing des catégories, auriez-vous une idée de la solution à adapter en 1.7?
    merci

  60. Bonjour, merci pour cet article grandement utile !

    Je rencontre actuellement un problème pour l’ajout des champs dans le panier.

    Sur la page produit pas de problème ils s’affichent bien.

    Par contre sur la page du panier le champs
    $product.custom_field_lang ne s’affiche pas et le champs :

    $product.custom_field_lang_wysiwyg nofilter affiche « Array ».

    J’ai bien suivi les différentes étapes :
    Modifier le hhproduct.php pour ajouter le hook :

    public function hookActionCartGetProductsSqlQuery($params)
    {
    $params[‘query’]->select(‘p.custom_field , pl.custom_field_lang , pl.custom_field_lang_wysiwyg’);
    }

    ensuite j’ai créer le fichier Cart.php dans le override et j’ai modifié la fonction getProducts pour ajouter la ligne :

    Hook::exec(‘actionCartGetProductsSqlQuery’,[‘query’ => &$sql]);

    et j’ai désinstaller puis réinstaller le module.

    Je suis sous Prestashop 1.7.5.2

  61. Bonjour Hervé,

    Tout d’abord merci pour vos nombreux articles très instructifs.

    Vous n’avez pas détaillé l’ajout de champ images dans les pages produits. Est-ce possible ? si oui comment ?

    Merci,
    Cordialement,
    Benjamin

    1. Bonjour Benjamin,

      Je n’ai effectivement pas détaillé l’ajout d’une image car je l’ai encore jamais testé.
      Je pense toutefois que c’est possible mais dans l’immédiat je n’ai pas de piste à vous donner pour avancer.
      Si vous parvenez à le faire je serais intéressé par votre retour d’expérience !

      Cordialement,
      Hervé

  62. Salut Herve! Merci beaucoup pour la documentation. Je ne parle pas français donc j’utilise un traducteur, excusez-moi.

    Je voudrais vous poser une question, j’ai pu placer des champs de texte avec succès (texte, date booléenne), mais j’ai besoin de placer un champ de type de fichier (le fichier peut être un fichier pdf ou une image) mais je ne sais pas comment le faire, avez-vous soudainement la connaissance que je peux instruire un peu?

    1. Bonjour Johan,

      Comme déjà évoqué dans plusieurs réponses je n’ai pas encore eut le temps d’implémenter cette fonctionnalité.
      Je ne saurais donc pas vous répondre dans l’immédiat.
      C’est dans ma todo ( parmis pas mal d’autres choses ) je mettrais à jour l’article en conséquent dès que ce sera fait.
      N’hésitez pas à partager vos retours.

      Cordialement,
      Hervé

  63. Bonjour Hervé,

    Merci pour le partage de ce code très utile !
    Une question : est-ce que l’import de ce nouveau champ est possible via le fichier CSV des produits ?

    Merci

    1. Bonjour Rachel,
      Nativement ces champs ne seront pas disponible via l’import csv dans l’administration.
      Il sera nécessaire de surcharger le controller AdminImportController.php pour gérer ces champs également.
      ( Je n’ai pas de code sous la main dans l’immédiat par contre )

      Cordialement,
      Hervé

  64. Merci pour ce supertuto. J’aimerai faire la même chose pour les déclinaisons, mais il semblerait que ma valeur ne soit pas prise en compte lors de la soumission du formulaire… J’ai adapté le code du tuto, je récupère bien la valeur lors du chargement mais celle ci n’est pas transmise lors de la soumission du formulaire.

    En regardant de plus prés la valeur du name ne semble pas bonne , les autres champs ont comme valeur de name un tableau, où chaque champ a une clef bien précise. je me suis donc dit que j’allais adapter le name de mon champ, sauf que là j’ai une erreur de Symfony qui me signale que le nouveau champ fournit est de trop et donc ne passe pas le validateur… bref je sens que je suis pas loin mais je bloque… Si quelqu’un a une idée

    Merci d’avance.

  65. Bonjour,

    Merci pour vos tuto de qualités et voici mon fichier tpl qui gère les langues pour wysiwig et champs languages normaux !

    {l s=’Informations complémentaires’ mod=’bwa_product_modifier’}

    {* Champ langue avec une structure particulière *}
    {l s=’Couleur’ mod=’bwa_product_modifier’}

    {foreach from=$languages item=language }

    {/foreach}

    {l s=’Nez’ mod=’bwa_product_modifier’}

    {foreach from=$languages item=language }

    {/foreach}

    {l s=’Saveur’ mod=’bwa_product_modifier’}

    {foreach from=$languages item=language }

    {/foreach}

    {* Champs wysiwyg avec TinyMce *}

    {l s=’Ingrédients’ mod=’bwa_product_modifier’}

    {foreach from=$languages item=language}

    {if isset({$ingredients[$language.id_lang]}) && {$ingredients[$language.id_lang]} !=  »}
    {$ingredients[$language.id_lang]}
    {/if}

    {/foreach}

    {l s=’Informations nutritionnelles’ mod=’bwa_product_modifier’}

    {foreach from=$languages item=language}

    {if isset({$info_nutri[$language.id_lang]}) && {$info_nutri[$language.id_lang]} !=  »}
    {$info_nutri[$language.id_lang]}
    {/if}

    {/foreach}

    1. Effectivement.
      Renvoyez moi l’information par email sinon 😉
      Mais je n’ai toujours pas compris si c’était une question ou un code d’exemple que vous souhaitiez partager 😉

  66. Il s’agit d’une solution permettant d’enregistrer en plusieurs langues les champs wysiwyg et textes avec le comportement natif de l’édition de produit ! Il suffit d’avoir la bonne structure dans le tpl, je vous l’envoie par mail.
    Edit Hhennes : code rajouté ci-dessous

    <div class="m-b-1 m-t-1">
    <h2>{l s='Informations complémentaires' mod='bwa_product_modifier'}</h2>
    <div class="row">
    <div class="col-lg-12 col-xl-12">

    {* Champ langue avec une structure particulière *}
    <label class="form-control-label">{l s='Couleur' mod='bwa_product_modifier'}</label>
    <div class="translations tabbable">
    <div class="translationsFields tab-content">
    {foreach from=$languages item=language }
    <div data-locale="{$language.iso_code}" class="translationsFields-form_step1_couleur_{$language.id_lang} tab-pane translation-field translation-label-{$language.iso_code} {if $default_language == $language.id_lang}show active{/if}">
    <input type="text" name="couleur_{$language.id_lang}" class="form-control" {if isset({$couleur[$language.id_lang]}) && {$couleur[$language.id_lang]} != ''}value="{$couleur[$language.id_lang]}"{/if}/>
    </div>
    {/foreach}
    </div>
    </div>

    <label class="form-control-label">{l s='Nez' mod='bwa_product_modifier'}</label>
    <div class="translations tabbable">
    <div class="translationsFields tab-content">
    {foreach from=$languages item=language }
    <div data-locale="{$language.iso_code}" class="translationsFields-form_step1_nez_{$language.id_lang} tab-pane translation-field translation-label-{$language.iso_code} {if $default_language == $language.id_lang}show active{/if}">
    <input type="text" name="nez_{$language.id_lang}" class="form-control" {if isset({$nez[$language.id_lang]}) && {$nez[$language.id_lang]} != ''}value="{$nez[$language.id_lang]}"{/if}/>
    </div>
    {/foreach}
    </div>
    </div>

    <label class="form-control-label">{l s='Saveur' mod='bwa_product_modifier'}</label>
    <div class="translations tabbable">
    <div class="translationsFields tab-content">
    {foreach from=$languages item=language }
    <div data-locale="{$language.iso_code}" class="translationsFields-form_step1_saveur_{$language.id_lang} tab-pane translation-field translation-label-{$language.iso_code} {if $default_language == $language.id_lang}show active{/if}">
    <input type="text" name="saveur_{$language.id_lang}" class="form-control" {if isset({$saveur[$language.id_lang]}) && {$saveur[$language.id_lang]} != ''}value="{$saveur[$language.id_lang]}"{/if}/>
    </div>
    {/foreach}
    </div>
    </div>
    </div>

    {* Champs wysiwyg avec TinyMce *}
    <div class="col-lg-12 col-xl-12">
    <div class="summary-description-container">
    <label class="form-control-label">{l s='Ingrédients' mod='bwa_product_modifier'}</label>
    <div class="tab-pane panel panel-default active" id="ingredients">
    <div class="translations tabbable" id="form_step1_ingredients">
    <div class="translationsFields tab-content bordered">
    {foreach from=$languages item=language}
    <div data-locale="{$language.iso_code}" class="translationsFields-form_step1_ingredients_{$language.id_lang} tab-pane translation-field translation-label-{$language.iso_code} {if $default_language == $language.id_lang}show active{/if}">
    <textarea name="ingredients_{$language.id_lang}" class="autoload_rte form-control" aria-hidden="true">
    {if isset({$ingredients[$language.id_lang]}) && {$ingredients[$language.id_lang]} != ''}
    {$ingredients[$language.id_lang]}
    {/if}
    </textarea>
    </div>
    {/foreach}
    </div>
    </div>
    </div>

    <label class="form-control-label">{l s='Informations nutritionnelles' mod='bwa_product_modifier'}</label>
    <div class="tab-pane panel panel-default active" id="info_nutri">
    <div class="translations tabbable" id="form_step1_info_nutri">
    <div class="translationsFields tab-content bordered">
    {foreach from=$languages item=language}
    <div data-locale="{$language.iso_code}" class="translationsFields-form_step1_info_nutri_{$language.id_lang} tab-pane translation-field translation-label-{$language.iso_code} {if $default_language == $language.id_lang}show active{/if}">
    <textarea name="info_nutri_{$language.id_lang}" class="autoload_rte form-control" aria-hidden="true">
    {if isset({$info_nutri[$language.id_lang]}) && {$info_nutri[$language.id_lang]} != ''}
    {$info_nutri[$language.id_lang]}
    {/if}
    </textarea>
    </div>
    {/foreach}
    </div>
    </div>
    </div>
    </div>
    </div>
    </div>

    <div class="clearfix"></div>
    </div>

  67. Hi, thank you for your work. I also create a module for expanding the product, however, the data must be stored in a separate table, and we need a button to add a new line (similar to the feature). Any ideas how to implement this? SQL table is ready. the module will also be happy with any feedback.

  68. Bonjour et merci pour ce tuto.
    par contre comment faire pour que le nouveau champs ajouté soit disponible dans la recherche par façette (facetedsearch).

    Merci

    1. Bonjour,

      Si vous souhaitez utiliser la navigation à facette le mieux est d’utiliser les caractéristiques qui fonctionnent nativement.
      Ajouter un champ custom nécessite sans doute des développements assez lourds ( je n’ai jamais été amené à le faire )

      Cordialement,
      Hervé

  69. Bonjour et merci à toi pour cet article bien écrit.
    Cependant je suis face à un problème d’interaction avec la base de données. Comment sont enregistrées et récupérées les données? J’ai exactement le même code que toi (sauf que je n’ai gardé que le champ wysiwyg) et quand je rentre une valeur et la valide rien est enregistré. Par ailleurs, quand je rentre une valeur manuellement dans phpmyadmin elle n’est pas récupérée pour alimenter le champ du formulaire.

    Merci pour tout!

    1. Bonjour Aurélien,

      Avez-vous testé les éléments listé dans la partie « Le code ne fonctionne pas ? » de l’article.
      Je pense que votre problème est lié au cache.

      Cordialement,
      Hervé

  70. Merci pour ta réaction.

    J’ai d’abord tenté d’utiliser les caractéristiques (avec la navigation à façette) mais cela ne résous pas mon problème

    En fait, j’ai deux champs Age_min et Age_max pour chaque produit. Et je souhaite faire une recherche de tous les produits compris entre age_min et age_max. mais la navigation à façette ne recherche que les jeux égale à cette valeur d’age. c’est ce qui m’a poussé à suivre ton tuto pour ajouter ces deux champs.

    Il faudrait que je trouve le moyen d’importer ces 2 nouveaux champs (CSV).

    puis que je modifie la requête de façette pour tenir compte de ces champs

  71. J’ai repris ton code exact, sans rien modifier. J’ai donc crée un nouveau module. J’ai supprimé les dossiers dev et prod du dossier var/cache. Le module s’installe parfaitement, les tables sont bien créées dans la base de données, je n’ai aucune erreur lors de l’enregistrement du produit modifié, mais il n’y a rien à faire, aucune donnée n’est enregistrée dans la base.

    J’ai relu tous les commentaires de cette page et j’ai bien pris note du fait que l’override ne se faisait probablement pas. Mais du coup même après avoir vidé le cache comment faire?

    Cordialement.

  72. Je suis sur la version 1.7.6.2
    J’ai réglé le problème de l’override. Il était dû à une erreur de ma part. Un autre fichier « Product.php » était présent dans le dossier override/classes situé à la racine du prestashop. Donc le fichier Product.php de mon module n’avait pas la priorité.

    Maintenant je suis face à un autre soucis. Je peux à présent enregistrer les données dans la base mais que je choisisse français ou anglais, elles sont toujours enregistrées avec lang_id = 1 soit la langue française. Donc je n’arrive pas à avoir un enregistrement pour le Fr et un pour la version En.

    Cordialement.

  73. Bonjour Aurélien,

    Je pense que tu as un souci avec ta requête SQL « ALTER . . .  »

    si tu as fait un copier/coller, les retour à la ligne sont encoder dans la chaine de caractère.

    j’ai eu le même problème : peut tu réécrire la requête SQL sur une seule ligne :

    Comme ceci :

    $sqlInstall = « ALTER TABLE  » . _DB_PREFIX_ . « product ADD custom_field VARCHAR(255) NULL »;

  74. Bonsoir gmisterk,

    Merci pour ton aide.

    J’ai bien mis les requêtes sur une seule ligne mais mes enregistrements arrivent toujours dans la ligne pour la version française même quand je switch le produit en version anglaise.

    Voilà ce que j’ai :

    $sqlInstall = « ALTER TABLE  » . _DB_PREFIX_ . « product « . « ADD custom_field VARCHAR(255) NULL »;
    $sqlInstallLang = « ALTER TABLE  » . _DB_PREFIX_ . « product_lang « . « ADD custom_field_lang VARCHAR(255) NULL, ». « ADD custom_field_lang_wysiwyg TEXT NULL »;

    (chaque requête sur une seule ligne donc)

  75. Tu as bien placé le bout de code proposé par Herve à a fin du tuto :

    function switchLanguage(iso_code) {
    $(‘div.translations.tabbable > div > div.translation-field:not(.translation-label-‘ + iso_code + ‘)’).removeClass(‘show active’);
    $(‘div.translations.tabbable > div > div.translation-field.translation-label-‘ + iso_code).addClass(‘show active’);
    }

  76. Dans la version 1.7.6.2 le code dont tu parles est le bon de base. Voilà ce que j’ai par défaut dans le fichier form.js :

    function switchLanguage(iso_code) {
    $(‘div.translations.tabbable > div > div.translation-field:not(.translation-label-‘ + iso_code + ‘)’).removeClass(‘show active’);
    const langueTabSelector = ‘div.translations.tabbable > div > div.translation-field.translation-label-‘ + iso_code;
    $(langueTabSelector).addClass(‘show active’);
    resetEditor();
    }

  77. J’ai trouvé!

    Dans le template extrafields.tpl, il manque une classe, à la fois dans le fichier fourni dans l’archive hhproduct.rar mais aussi dans le code montré sur cette page.

    Il faut rajouter la classe « translation-field » aux div ayant la classe « tab-pane ». C’est d’ailleurs de cette façon que le code JS fonctionne, il cherche bien un élément avec la classe translation-field ET translation-label. Et dans le template fourni ici, seule la classe translation-label est présente donc il ne peut pas trouver nos champs de formulaire pour switcher la langue.

    Merci pour votre aide à tous!

  78. Bonjour herve,
    Tu aurais une piste pour pouvoir faire une recherche (à fecette) avec un range de deux valeurs (caractéristique age) ?
    Je sais pas trop par où commencer.

    Merci d’avance pour ton aide

    1. Bonjour,

      Plusieurs possibilités en fonction de ce que tu souhaites faire.
      – Regarder si il existe des modules qui font déjà ça
      – Le faire toi même.

      Je n’ai pas de réponse toute faite et actuellement je ne sais pas comment traiter ta demande.
      La piste que je creuserais serai de regarder dans le module ps_facetedsearch, comment sont gérés les différents types de filtres.
      Et j’essaierais d’en rajouter un custom.
      Après je ne te cache pas que le code du module est assez long à prendre en main.

      Cordialement,
      Hervé

  79. Bonjour,
    merci pour cette article. cependant j’ai un probleme pour ajouter un produit dans la base de données avec le nouveau champ via le code.
    j’ai bien ajouter le champ characteristic a la table produit
    j’override la classe product

    class Product extends ProductCore
    {
    public $characteristic;
    public function __construct($id_product = null, $full = false, $id_lang = null, $id_shop = null, Context $context = null)
    {
    self::$definition[‘fields’][‘characteristic’] = array(‘type’ => self::TYPE_HTML, ‘lang’ => true, ‘validate’ => ‘isCleanHtml’);
    parent::__construct($id_product, $full, $id_lang, $id_shop, $context);
    }
    }

    $product->characteristic = $tableau;
    $product->add();

    mais lorsque j’essaie d’ajouter mon produit j’ai une exception :

    [PrestaShopDatabaseException]

    Unknown column ‘characteristic’ in ‘field list’

    Edit :
    En fait c’est bon, c’est juste qu’il inserer dans `ps_product_lang` et pas dans `ps_product`

  80. bonjour ami, merci pour ce tutoriel, il a été très utile, mais je vous demande une faveur, mon site est multilingue mais ce n’est pas enregistré en anglais mais seulement en italien
    Merci

  81. Bonjour,

    merci pour ces informations !
    par contre j’ai l’impression que les champs rajoutés ne sont pas pris en compte si nous sommes en multistore.
    alors je ne sais pas si c’est un bug de Presta mais j’ai été contraint d’ajouter une action sur le hookActionProductSave pour aller manuellement renseigner mes champs

    Ce qui donne :

    public function hookActionProductSave($params) {

    $id_product = $params[‘id_product’];

    //to_do : module should create another table with its columns

    //to_do 2 : manage duplicate which is not considering
    $languages = Language::getLanguages();
    foreach ($languages as $language) {
    $new_field_1 = Tools::getValue(« new_field_1 ».$language[‘id_lang’]);
    $new_field_2 = Tools::getValue(« new_field_2 ».$language[‘id_lang’]);
    $sql_query = « UPDATE ` » . _DB_PREFIX_ . « product_lang` SET `new_field_1` = ‘ ».$new_field_1. »‘,`new_field_2` = ‘ ».$new_field_2. »‘ WHERE 1 AND `id_lang`= « .$language[‘id_lang’]. » AND `id_product` = « .$id_product.  » « .Shop::addSqlRestriction(false);
    $this->query($sql_query);

    }
    }

    Par la suite, je me suis rendu compte que la dupli du produit ne prenait pas non plus ces nouveaux champs.

    je cherche un moyen de faire en sorte que ces champs soient reportés sur le produit dupliqué mais sans conclusion pour l’instant. Auriez-vous une piste?

    merci par avance,

    1. Bonjour Alex,

      Merci de votre retour.
      C’est vrai que j’utilise très rarement le multi boutique sur Prestashop je n’ai donc pas testé ce tutoriel dessus.
      Parfait si vous avez trouvé une solution.
      Concernant la duplication je pense qu’en reprenant votre logique et en utilisant le hook actionObjectProductAddAfter il doit être possible de récupérer et de mettre à jour les valeurs également.

      Cordialement,
      Hervé

  82. Bonjour,

    Merci beaucoup pour le tutoriel, en en suivant des différents j’ai réussi à créer un module avec des champs textes et éditeurs HTML sur la fiche produit dans deux langues.

    Mais je voudrais ajouter une liste déroulante (les options peuvent être saisies en dur dans le template du formulaire, avec un label texte et une valeur numéraire qui serait la seule à être sauvegardée en DB) et je n’ai pour l’instant aucune idée de comment procéder.

    Si vous avez le temps pour un exemple ou des indications ce serait super !

    Merci d’avance.
    Cordialement,
    Claire

  83. Bonjour Hervé,

    Merci pour ce tuto très pratique !
    Comment faire pour faire remonter ce champ dans le mail new_order.html (du module Mailalerts) ?
    Merci d’avance!

    1. Bonjour Rachel,

      Votre commentaire tombe bien car j’ai justement fait un article hier sur comment modifier les emails via les modules prestashop.
      Du coup vous rentrez pile dans ce cas : https://www.h-hennes.fr/blog/2021/02/01/prestashop-gerer-les-emails-dans-vos-modules/
      A partir de cet article vous devriez comprendre comment modifier l’email concerné.
      Par contre il faudra tout de même implémenter la logique de récupération de l’information.

      Cordialement,
      Hervé

  84. Je rebondi au message d’Aurélien (merci à toi), effectivement il faut ajouter la classe translation-field aux divs avec la classe tab-pane pour que le switch de langue fonctionne (en tout cas sur la 1.7.7)

  85. J’aimerai savoir comment afficher un nouveau champ, exemple « video » dans ce même sujet mais juste à côté de l’OPTIONS comme ce MODULES mais ce sera VIDEO dans mon exemple.

    Merci d’avance pour votre réponse.

    1. Bonjour Sandy,

      Il n’existe pas de hook pour ajouter un nouvel onglet à cet endroit.
      Vous avez toujours la possibilité d’en créer un via le javascript de votre module, mais cela ne fonctionnera pas forcément de manière optimale.

      Cordialement,
      Hervé

  86. Bonjour Hervé,
    Merci beaucoup pour tous vos tutos qui m’ont débloqués pas mal de situations.
    J’ai donc créé sur ma boutique un module pour ajouter plusieurs champs custom à mes produits et cela fonctionne très bien (affichage sur différents hooks, mise à jour ok) quand je suis logguée en tant que super-admin.
    Mais les champs doivent pouvoir être visibles pour un utilisateur back-office lambda hors ceux-ci ne sont plus affichés dans ce cas. Je n’ai pas trouvé dans les permissions ce qui pourrait déclencher leur affichage (peut-être mettre les permissions dans la colonne à droite sur le module mais celui-ci est grisé…)
    Cordialement,
    Gwenaelle

  87. Bonjour Hervé,
    Super Tuto (et encore je me suis contenté d’installé direct le module 🙂 ) mais je n’arrive pas à reproduire la meme chose sur la fiche déclinaison. Auriez vous le code pour le faire s’il vous plait?
    Merci d’avance

    Voici mon code actuel: ca n’affiche rien et je n’arrive meme plus a ouvrir la ficher déclinaison avec

    class HhProduct extends Module {

    public function __construct() {

    $this->name = ‘hhproduct’;
    $this->tab = ‘others’;
    $this->author = ‘hhennes’;
    $this->version = ‘0.1.0’;
    $this->need_instance = 0;
    $this->bootstrap = true;

    parent::__construct();

    $this->displayName = $this->l(‘hhproduct’);
    $this->description = $this->l(‘add new fields to product’);
    $this->ps_versions_compliancy = array(‘min’ => ‘1.7.1’, ‘max’ => _PS_VERSION_);
    }

    public function install() {
    if (!parent::install() || !$this->_installSql()
    //Pour les hooks suivants regarder le fichier src\PrestaShopBundle\Resources\views\Admin\Product\form.html.twig
    || ! $this->registerHook(‘displayAdminProductsExtra’)
    || ! $this->registerHook(‘displayAdminProductsCombinationBottom’)
    ) {
    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_ . « product_attribute  »
    . « ADD custom_field_dan VARCHAR(255) NULL »;

    $returnSql = Db::getInstance()->execute($sqlInstall);
    return $returnSql;
    }

    /**
    * Suppression des modification sql du module
    * @return boolean
    */
    protected function _unInstallSql() {
    $sqlInstall = « ALTER TABLE  » . _DB_PREFIX_ . « product_attribute  »
    . « DROP custom_field_dan »;
    $returnSql = Db::getInstance()->execute($sqlInstall);
    return $returnSql;

    }

    public function hookDisplayAdminProductsExtra($params)
    {
    return $this->_displayHookContentBlock(__FUNCTION__);
    }

    /**
    * Affichage des informations supplémentaires sur la fiche produit
    * @param type $params
    * @return type
    */
    public function hookDisplayAdminProductsCombinationBottom($params) {
    $combinaison = new Combination($params[‘id_product_attribute’]);
    $this->context->smarty->assign(array(
    ‘custom_field_dan’ => $combinaison->custom_field_dan,
    )
    );
    $combinaison->save();

    return $this->display(__FILE__, ‘views/templates/hook/extrafields.tpl’);
    }

    /**
    * Fonction pour afficher les différents blocks disponibles
    * @param type $hookName
    * @return type
    */
    protected function _displayHookContentBlock($hookName) {
    $this->context->smarty->assign(‘hookName’,$hookName);
    return $this->display(__FILE__, ‘views/templates/hook/hookBlock.tpl’);
    }

    }

    1. Bonjour Dan,

      Pour l’ajout des champs aux déclinaisons lors de mes tests j’avais utilisé un template smarty standard et c’est bien fonctionnel.
      Dans votre code je ne comprends pas l’intérêt du $combinaison->save(); dans la fonction hookDisplayAdminProductsCombinationBottom
      Si vous souhaitez gérer l’enregistrement de votre donnée vous pouvez plutôt utiliser le hook ActionAdminProductsControllerSaveAfter

      Cordialement,
      Hervé

      1. Merci Hervé de votre réponse,
        mon erreur venait de mon override Combination que j’avais mal nommé.
        Du coup je vois bien mon champs sur la fiché déclinaison mais aucun enregistrement en base de données. J’ai bien sur vidé les caches et supprimé les fichiers index_class.php mais rien y fait, cela ne fait aucun enregistrement. Si maintenant je fais le changement direct dans la BDD je vois mon champs rempli.
        Vous parlez du hook ActionAdminProductsControllerSaveAfter mais comment et où dois je l’utiliser s’il vous plait?
        Merci d’acance 😉

        1. Bonjour Dan,
          Concernant l’utilisation de ce hook il suffit d’ajouter votre module dessus comme pour n’importe quel autre hook.
          Au niveau du fonctionnement dans ce hooks vous allez recevoir toutes les données POST du controller.
          Vous avez donc accès à l’ensemble des données envoyées par le formulaire, il ne vous reste plus qu’a insérer en base de données celles qui vous intéressent.
          ( Si vous avez un doute sur les données envoyées, vous pouvez faire un dump($_POST) suivi d’un die(); et regarder dans le panneau ajax les informations envoyées.

          Cordialement,
          Hervé

Laisser un commentaire

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