Guide Magento Developpeur (Leçon 7) – Le Grid dans Magento

Ce tutoriel est le 7eme d’une longue série, si vous ne l’avez pas déjà fait je vous conseille de lire les tutoriels en commençant par le sommaire de cette série.

Souvenez vous, dans le tutoriel précédent on avait crée une partie dans l’administration de magento pour gérer notre liste de contact directement depuis l’administration de magento.

Dans l’administration magento, quand vous voulez lister les produits, vous voyez une GridView (un espèce de tableau avec lequel pouvez trier, editer un article, le supprimer etc…) et vous vous dites que ca serai bien de savoir utiliser la même chose dans notre module. Dans ce tutoriel on va voir comment insérer un Grid dans la partie administration de notre module Magento.

Un Grid Magento est construit à partir d’un ensemble de fichiers qui vont se trouver dans le dossier Block/Adminhtml/ de votre module.

1- Le Grid Container

Créez un dossier : \app\code\local\Pfay\Test\Block\Adminhtml\ et mettez dedans un fichier Grid.php. C’est le fichier qui (comme son nom l’indique) va contenir votre Grid, il contiendra :

<?php
class Pfay_Test_Block_Adminhtml_Grid extends Mage_Adminhtml_Block_Widget_Grid_Container
{
    public function __construct()
    {
     //on indique ou va se trouver le controller
     $this->_controller = 'adminhtml_test';
     $this->_blockGroup = 'test';
     //le texte du header qui s’affichera dans l’admin
     $this->_headerText = 'Gestion de mon carnet d\'adresse';
     //le nom du bouton pour ajouter une un contact
     $this->_addButtonLabel = 'Ajouter un contact';
     parent::__construct();
     }
}

2- Le Grid

Vous indiquez que le controller du grid se trouvera dans le dossier test, creez donc le dossier Créez un dossier : \app\code\local\Pfay\Test\Block\Adminhtml\Test\ et dedans créez un fichier Grid.php qui contiendra :

<?php
class Pfay_Test_Block_Adminhtml_Test_Grid extends Mage_Adminhtml_Block_Widget_Grid
{
   public function __construct()
   {
       parent::__construct();
       $this->setId('contactGrid');
       $this->setDefaultSort('id_pfay_test');
       $this->setDefaultDir('DESC');
       $this->setSaveParametersInSession(true);
   }
   protected function _prepareCollection()
   {
      $collection = Mage::getModel('test/test')->getCollection();
      $this->setCollection($collection);
      return parent::_prepareCollection();
    }
   protected function _prepareColumns()
   {
       $this->addColumn('id_pfay_test',
             array(
                    'header' => 'ID',
                    'align' =>'right',
                    'width' => '50px',
                    'index' => 'id_pfay_test',
               ));
       $this->addColumn('nom',
               array(
                    'header' => 'nom',
                    'align' =>'left',
                    'index' => 'nom',
              ));
       $this->addColumn('prenom', array(
                    'header' => 'prenom',
                    'align' =>'left',
                    'index' => 'prenom',
             ));
        $this->addColumn('telephone', array(
                     'header' => 'telephone',
                     'align' =>'left',
                     'index' => 'telephone',
          ));
         return parent::_prepareColumns();
    }
    public function getRowUrl($row)
    {
         return $this->getUrl('*/*/edit', array('id' => $row->getId()));
    }
}

On configure notre Grid magento en lui indiquant la collection a utiliser pour interagir avec la base de donnée dans prepareCollection() (ici on va utiliser la collection du model ‘test/test’ qu’on a crée dans les tutoriels précédents). Puis avec prepareColumns() on lui indique quelles colonnes de notre table afficher grâce a la méthode addColumn(). Je n’explique pas plus le code que ca, il me parait assez évident que si vous voulez rajouter une colone, il suffit de rajouter une inscription du style :

$this->addColumn('telephone',
       array(
           'header' => 'telephone',
           'align' =>'left',
           'index' => 'telephone',
       ));

Voila, notre Grid est maintenant configuré. Maintenant, quand on va cliquer sur une des colonnes, il faut qu’on arrive sur un formulaire pour pouvoir éditer notre contact.

3- Le Form Container

Créez donc un fichier Edit.php dans le même répertoire : \app\code\local\Pfay\Test\Block\Adminhtml\Test\
Ce fichier Edit.php contiendra le code suivant :

<?php
class Pfay_Test_Block_Adminhtml_Test_Edit extends
                    Mage_Adminhtml_Block_Widget_Form_Container{
   public function __construct()
   {
        parent::__construct();
        $this->_objectId = 'id';
        //vous remarquerez qu’on lui assigne le même blockGroup que le Grid Container
        $this->_blockGroup = 'test';
        //et le meme controlleur
        $this->_controller = 'adminhtml_test';
        //on definit les labels pour les boutons save et les boutons delete
        $this->_updateButton('save', 'label','save reference');
        $this->_updateButton('delete', 'label', 'delete reference');
    }
       /* Ici,  on regarde si on a transmit un objet au formulaire,
            afin de mettre le bon texte dans le  header (Editer ou
             Ajouter) */
    public function getHeaderText()
    {
        if( Mage::registry('test_data')&&Mage::registry('test_data')->getId())
         {
              return 'Editer la reference '.$this->htmlEscape(
              Mage::registry('test_data')->getTitle()).'<br />';
         }
         else
         {
             return 'Ajouter une reference';
         }
    }
}

J’ai commenté le code, ce fichier est un Form Container (voir le extends) c’est-à-dire une structure qui va vous permettre de recevoir des formulaires, maintenant il faut donc faire le formulaire en lui-même.

4- Créer le Formulaire

Créer maintenant le dossier : \app\code\local\Pfay\Test\Block\Adminhtml\Test\Edit\ et dedans le fichier Form.php

Ce fichier contiendra le code suivant :

class Pfay_Test_Block_Adminhtml_Test_Edit_Form extends
                          Mage_Adminhtml_Block_Widget_Form
{
    protected function _prepareForm()
    {
         $form = new Varien_Data_Form(
                array(
                  'id' => 'edit_form',
                  'action' => $this->getUrl('*/*/save', array('id' => $this->getRequest()->getParam('id'))
                     ),
                 'method' => 'post',
                 )
              );
      $form->setUseContainer(true);
      $this->setForm($form);
      return parent::_prepareForm();
   }
}

On crée ici un objet de type Varien_Data_Form qui est le type « standard » de formulaire magento, on défini l’attribut action du formulaire grâce a la fonction getUrl() et on définit la méthode d’envoie des information (ici post).
Et voila, on a définit l’information de notre formulaire. Maintenant on va créer les lignes de notre formulaire. Dans notre repertoire \app\code\local\Pfay\Test\Block\Adminhtml\Test\Edit\, creez un fichier Tabs.php qui est en fait une sorte de « tabs container » ;)

Ce fichier contiendra le code suivant :

  <?php
  class Pfay_Test_Block_Adminhtml_Test_Edit_Tabs extends Mage_Adminhtml_Block_Widget_Tabs
  {
     public function __construct()
     {
          parent::__construct();
          $this->setId('test_tabs');
          $this->setDestElementId('edit_form');
          $this->setTitle('Information sur le contact');
      }
      protected function _beforeToHtml()
      {
          $this->addTab('form_section', array(
                   'label' => 'Contact Information',
                   'title' => 'Contact Information',
                   'content' => $this->getLayout()
     ->createBlock('test/adminhtml_test_edit_tab_form')
     ->toHtml()
         ));
         return parent::_beforeToHtml();
    }
}

On lui dit que les entrées seront crée grâce au block dans le module Test, qui sera adminhtml_test_edit_tab_form, créons donc le dossier: \app\code\local\Pfay\Test\Block\Adminhtml\Test\Edit\Tab et dedans créez le Fichier Form.php.

<?php
class Pfay_Test_Block_Adminhtml_Test_Edit_Tab_Form extends Mage_Adminhtml_Block_Widget_Form
{
   protected function _prepareForm()
   {
       $form = new Varien_Data_Form();
       $this->setForm($form);
       $fieldset = $form->addFieldset('test_form',
                                       array('legend'=>'ref information'));
        $fieldset->addField('nom', 'text',
                       array(
                          'label' => 'Nom',
                          'class' => 'required-entry',
                          'required' => true,
                           'name' => 'nom',
                    ));
        $fieldset->addField('prenom', 'text',
                         array(
                          'label' => 'Prenom',
                          'class' => 'required-entry',
                          'required' => true,
                          'name' => 'prenom',
                      ));
          $fieldset->addField('telephone', 'text',
                    array(
                        'label' => 'telephone',
                        'class' => 'required-entry',
                        'required' => true,
                        'name' => 'telephone',
                 ));
 if ( Mage::registry('test_data') )
 {
    $form->setValues(Mage::registry('test_data')->getData());
  }
  return parent::_prepareForm();
 }
}

5- Insérer notre block dans le Layout

Puis dans le layout.xml de votre thème d’administration, changer votre block par :

    <block type="test/adminhtml_grid" name="test" />

Et voila, votre grid marche ;) felicitation…ha non ca marche pas il ne fait aucune modification ? c’est normal, il faut que vous implémentiez la méthode save dans votre controller magento !

6- Implémenter les fonctions dans le controller

Allez donc dans votre fichier \app\code\local\Pfay\Test\controllers\Adminhtml\IndexController.php et editez le comme ceci :

<?php
class Pfay_Test_Adminhtml_IndexController extends Mage_Adminhtml_Controller_Action
{
    protected function _initAction()
    {
        $this->loadLayout()->_setActiveMenu('test/set_time')
                ->_addBreadcrumb('test Manager','test Manager');
       return $this;
     }
      public function indexAction()
      {
         $this->_initAction();
         $this->renderLayout();
      }
      public function editAction()
      {
           $testId = $this->getRequest()->getParam('id');
           $testModel = Mage::getModel('test/test')->load($testId);
           if ($testModel->getId() || $testId == 0)
           {
             Mage::register('test_data', $testModel);
             $this->loadLayout();
             $this->_setActiveMenu('test/set_time');
             $this->_addBreadcrumb('test Manager', 'test Manager');
             $this->_addBreadcrumb('Test Description', 'Test Description');
             $this->getLayout()->getBlock('head')
                  ->setCanLoadExtJs(true);
             $this->_addContent($this->getLayout()
                  ->createBlock('test/adminhtml_test_edit'))
                  ->_addLeft($this->getLayout()
                  ->createBlock('test/adminhtml_test_edit_tabs')
              );
             $this->renderLayout();
           }
           else
           {
                 Mage::getSingleton('adminhtml/session')
                       ->addError('Test does not exist');
                 $this->_redirect('*/*/');
            }
       }
       public function newAction()
       {
          $this->_forward('edit');
       }
       public function saveAction()
       {
         if ($this->getRequest()->getPost())
         {
           try {
                 $postData = $this->getRequest()->getPost();
                 $testModel = Mage::getModel('test/test');
               if( $this->getRequest()->getParam('id') <= 0 )
                  $testModel->setCreatedTime(
                     Mage::getSingleton('core/date')
                            ->gmtDate()
                    );
                  $testModel
                    ->addData($postData)
                    ->setUpdateTime(
                             Mage::getSingleton('core/date')
                             ->gmtDate())
                    ->setId($this->getRequest()->getParam('id'))
                    ->save();
                 Mage::getSingleton('adminhtml/session')
                               ->addSuccess('successfully saved');
                 Mage::getSingleton('adminhtml/session')
                                ->settestData(false);
                 $this->_redirect('*/*/');
                return;
          } catch (Exception $e){
                Mage::getSingleton('adminhtml/session')
                                  ->addError($e->getMessage());
                Mage::getSingleton('adminhtml/session')
                 ->settestData($this->getRequest()
                                    ->getPost()
                );
                $this->_redirect('*/*/edit',
                            array('id' => $this->getRequest()
                                                ->getParam('id')));
                return;
                }
              }
              $this->_redirect('*/*/');
            }
          public function deleteAction()
          {
              if($this->getRequest()->getParam('id') > 0)
              {
                try
                {
                    $testModel = Mage::getModel('test/test');
                    $testModel->setId($this->getRequest()
                                        ->getParam('id'))
                              ->delete();
                    Mage::getSingleton('adminhtml/session')
                               ->addSuccess('successfully deleted');
                    $this->_redirect('*/*/');
                 }
                 catch (Exception $e)
                  {
                           Mage::getSingleton('adminhtml/session')
                                ->addError($e->getMessage());
 $this->_redirect('*/*/edit', array('id' => $this->getRequest()->getParam('id')));
                  }
             }
            $this->_redirect('*/*/');
       }
}
?>

Maintenant ca marche vraiment !
Fin de ce tutoriel voila c’est fini pour aujourd’hui ;) Ca peu paraitre abstrait, mais à force de l’utiliser ca viendra tout seul ne vous inquiétez pas.
N’hésitez pas à laisser vos messages en commentaire, ca fait toujours plaisir et si vous avez des questions je vous répondrai ;)


Retrouvez le sommaire de ce tutoriel

 

Posted in Magento and tagged , , , , , , , , , , , .


42 Comments

  1. ce tutoriel est un peu plus compliqué que d’habitude, essayez de l’adapter, vous devriez y arriver.

    Le code est pas super bien indenté, c’est pas évident avec la longueur de la zone de texte, a vous de le remettre en forme ;)

    A bientôt,

    Pierre.

  2. Je dirais que dans la classe « Pfay_Test_Block_Adminhtml_Test_Edit_Tab_Form » il y a tentative d’accès à la clé « references_data » dans le registre alors qu’il faudrait « test_data » ?

    Et concernant la ligne :
    if ( Mage::getSingleton(‘adminhtml/session’)->getBrandsData()) { … }
    a-t-elle une utilité dans ce module ?

    Sinon tout le reste fonctionne bien :)

    • Non effectivement la ligne que tu me décris ne sert à rien ^^

      effectivement c’est bien test_data, c’est parce que je me suis inspiré d’un d’un module que j’avais crée quand j’apprenais à créer un Grid il y a un moment (module que j’avais appelé » références » ) et j’ai mal remplacé le code pour ce tutoriel ;)

      J’ai modifié le tuto encore merci Marc (@MT) ;)

  3. Salut

    Le tuto est très clair, mais un problème se pose quand on souhaite faire une jointure ou rajouter des attributs d’une entité et afficher le tout dans le grid sans passer par un renderer qui prohibera le tri ou la recherche.

    je donne un exemple assez simple, si l’on souhaite créer un grid pour les produits et afficher aussi le nombre de fois qu’ils ont été vendu. il faudra faire une jointure avec l’entité sales_order.

    L’affichage marche généralement assez bien mais les filtres et le tri c’est une autre histoire.

    Ca serait vraiment bien d’avoir un tuto sur cette partie

    Merci et Bon courage pour la suite des tutos

    Ils sont vraiment bien fait :)

    • je vais essayer de faire un tuto dessus par la suite, c’est une bonne idée kyudo. ca sera pas pour tout de suite par contre il me faut un peu de temps.

  4. Bonjour,

    Merci pour les tutos mais j’ai copié tous les script du debut jusqu’à la fin mais quand j’arrive sur « carnet d’adresses » dans le BO, ca m’affiche que la page est introuvable. Pourrais tu me dire ce qui ne va pas ? merci

  5. Hello,

    pour ce dernier tuto, ainsi que le précédent, j’ai bien les onglets qui s’affichent mais pas le contenu… la page est vide.

    Serait-il possible de m’aiguiller ?

    Tes tutos sont excellents au passage ;-)

    bgthw.

  6. Bonjour,

    Super les tutos.

    Même soucis que Kyudo pour les jointures. Par exemple, j’aimerais afficher dans un grid les données de log_visitor et log_visitor_info.

    Quelqu’un aurait une idée?

    Merci d’avance

    Helgvor

  7. Bonjour,

    Tout d’abord merci pour ces tutoriels super efficaces.

    Pour ma part, je suis en train d’essayer de créer un module afin d’afficher un report de toutes les adresses ip (du jour ou dans un intervalle de temps paramètrable) qui sont venues sur mon site (affichage d’un logo rouge/vert si en ligne ou pas et du favicon du moteur de recherche si c’en est un, lien avec http://www.utrace.de pour localiser l’adresse, …). Bref, j’aurai quelques suggestions de tutoriaux sur ces points:
    1.- Comment simuler l’équivalent d’une vue sql pour alimenter la collection du grid ou créer une collection from scratch (dans ce cas non lié à une table) à partir de plusieurs models (dans mon cas je dois avoir un ‘select distinct remote_addr from log_visitor_info’ et d’autres données compilées pour alimenter le grid)
    2.- Comment empaqueter le module une fois créé afin que l’on puisse facilement le déployer et/ou le distribuer à la communauté

    J’espère pouvoir vous lire prochainement sur ces sujets afin de mener à bout mon premier module.

    Cordialement,

    Helgvor STOLL

    • huhu ^^ Beau projet de module Helgvor, je pense que je vois ce que tu veux faire et c’est assez malin ;)
      tu veux envoyer ta pub papier graces aux visites c’est ca ? tu recuperes les secteurs ou ya eu beaucoups de visites et tu cible tes envois papier ? ;)

      Je pense que tu devrais enregistrer les ips qui passent sur ton site dans une table et creer un grid comme dans le tuto, par contre pour le enligne/hors ligne est ce vraiment important ?

      Au pire tu cree une vue sous PhpMyAdmin et puis apres tu fais une collection pour ta table, j’ai jamais essayé mais je pense que ca devrai marcher.

      A bientot,
      Pierre

  8. Bonjour,

    Entres autres mais il n’y a pas que ça. J’avais développé un petit module (moins complet) sur prestashop pour le faire et ce que je souhaiterais en plus c’est répondre à d’autres questions du genre:
    1.- Est-ce une première visite pour l’ip en cours (direct ou pas) afin de voir si les gens arrivent par hasard ou s’il y’a de la récidive (=> intéressé par le site)
    2.- Le nombre de pages visitées par ip qui me permettent de connaître l’intéret du visiteur et donc de réajuster mon catalogue en fonction des produits vus
    Y’a plein d’autres utilisations possibles mais ça me permet en un coup d’oeil de suivre l’activité à la journée, au mois ou à l’année.

    J’ai pensé à quelques éléments pour les 2 questions posées:
    ==>>jointures: utiliser Mage::getModel(‘visitors/visitor_info’)->getCollection()->getSelect()->join(‘log_visitor’, ‘main_table.visitor_id = log_visitor.visitor_id’, array(‘last_visit_at’=>’last_visit_at’)); pour récupérer les données des 2 tables visitor & visitor_info en inner join sur visitor_id.
    ==> Pourquoi ne pas créer une vue en utilisant Varien_Object et Varien_Data_Collection, puisque c’est un Varien_Data_Collection qu’on passe en argument dans setCollection(). Apparemment ça pourrait marcher mais les tri ne fonctionnent pas encore dans mes exemples de test.

    A bientôt,

    Helgvor STOLL

  9. Merci Pierre pour ces tutos !

    En une journée hop, je fais des grid !

    Petit question au passage, puis-je modifier l’affichage d’un champ du Grid ? Dans mon cas j’ai un chemin d’image en base et je souhaite simplement affiché un dans la liste au lieu du chemin brut.

    Merci encore.

  10. Merci pour ce très bon tuto !

    Je n’ai pas eu de soucis car depuis le début j’ai remplacé les noms « Test » par des noms explicites (du style « Monmodule », « Mongrid »…etc…).

    En tout cas, excellent pour apprendre à manipuler Magento, merci ! =)

  11. Bonjour, j’ai suivi cet article avec succes, quoi que en fin j’ai le grid, mais impossible d’editer les entrees:

    Fatal error: Call to a member function setData() on a non-object in C:\xampp\htdocs\magento\app\code\core\Mage\Adminhtml\Block\Widget\Form\Container.php on line 129

    Quelqu’un a une idee sur cette erreur ?

    Merci tout le monde.

  12. Bonjour,
    deux questions :
    - comment ajouter un datetime dans le Tab_Form ? ($fieldset->addField…)
    - comment faire un controller qui fasse correctement le save de ce datetime ?

    merci,

  13. « Puis dans le layout.xml de votre thème d’administration, changer votre block par : »

    euh je suis un peu ignare mais il est ou ce fichier layout.xml, c’est le fichier admin.xml? « changer votre block »: quel block?

  14. En définitive est-il possible de mettre une ségrégation ‘acl’ sur des éléments de ‘menu’ (disposition horizontale) comme on peut le faire lorsque l’on crée des éléments dans ‘config’ (disposition verticale) ?

  15. Quelqu’un a-t-il compris comment le bouton du grid container ‘Ajouter un contact’ se voit assigner l’url contenant l’action ‘new’ ?

    Est-ce un convention implicite au moment du :
    $this->_addButtonLabel = ‘Ajouter un contact’;
    dans le grid container?

    Merci pour toute suggestion.
    Clasie

  16. j’ai passer 2h avant de réussir a afficher la grille ! ce n’est pas précisé mais il faut dans le template utiliser echo $this->getChildHtml(‘grid’);

  17. Même problème que Bizboss

    Fatal error: Call to a member function setData() on a non-object in C:\xampp\htdocs\magento\app\code\core\Mage\Adminhtml\Block\Widget\Form\Container.php on line 129

    Help please

  18. Salut, une petite précision pour ceux qui ont du mal avec la page qui n’affiche rien :

    Ça vient surement du _blockGroup ou du _controller, surtout si vous avez changez les « test » du tutoriel.

    $this_controller c’est en fait le block class name, et non le controller du dossier controller.
    Exemple : vous avez appelé votre block Block/Adminhtml/Mygrid.php, votre _controller sera adminhtml_mygrid.

    $this->blockGroup est le nom de votre module principal.
    Exemple : vous avez appelé votre module ainsi : Mynamespace/Mymodule, votre _blockGroup sera mymodule.

    Voilà, j’espère que ça aura pu vous aider.
    A+

  19. Pour ceux qui ont la version 1.7 il faut changer le block:

    par

    dans le fichier /var/www/magento/magento/app/design/adminhtml/default/default/layout/test.xml

  20. Merci Pierre, très bon tuto comme d’habtiude.

    Pour ceux qui son bloquer sur une page blanche, vérifier le nom de fichier de votre block. Il doit commencer par une majuscule et la suite en minuscule. ce n’est pas comme les controller.

    Block/Adminhtml
    Block/Adminhtml/Invoicesgrid.php
    Block/Adminhtml/Invoices
    Block/Adminhtml/Invoices/Grid.php

    class NS_MDname_Block_Adminhtml_InvoicesGrid

  21. Super tuto.

    Beaucoup d’infos ici, mais peut-être pas assez de détails sur certains point, par ex :

    $this->_blockGroup = ‘test’;
    J’ai fait fonctionner le module d’admin en découvrant qu’il fallait mettre le nom du ‘tag XML’ de mon module perso à la place de ‘test’

    Sinon, super merci à toi Pierre Fay.

Laisser un commentaire

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

Vous pouvez utiliser ces balises et attributs HTML : <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>