Surcharger le core de Magento : contrôleur, modèle, bloc, helper

  • De le 08 mai 2010
  • Difficulté : 2/4

Surcharger le core de Magento : contrôleur, modèle, bloc, helper Lors du développement d'un nouveau module, il vous sera parfois nécessaire de surcharger le core de Magento afin de modifier son comportement par défaut. Les techniques divergeant en fonction qu'il s'agisse d'un contrôleur, d'un modèle, d'un bloc ou d'un helper, nous allons essayer dans cet article de mettre à plat toutes ces différentes techniques notamment au travers de plusieurs exemples.

Table des matières

Objectif

Afin d'étendre les fonctionnalités natives de Magento, nous allons nous intéresser ici au mécanisme de surcharge du noyau de Magento.

Cette pratique consiste à surcharger une partie d'un module (contrôleur, modèle, bloc, helper...) afin de modifier son comportement par défaut et de rendre possible les mises à jours futures de Magento et cela sans risquer la perte des modifications qui auraient pu être codées en dur dans son core.

Notez que les explications ci-dessous concernent les différentes pratiques permettant la surcharge d’une partie d’une classe, en y remplaçant ou en y ajoutant des méthodes.

Si vous souhaitez surcharger la totalité d’une classe (ou d’un module), rendez-vous à la section Remarques importantes, point Surcharge de la totalité d’une classe (ou d’un module) se trouvant au bas de cet article.

Configuration de base

Afin d'effectuer une surcharge, nous devons tout d'abord créer un nouveau module. Pour ce faire, ajoutez un nouveau répertoire dans le dossier ./app/code/local en vous basant sur l'architecture suivante :

  • Mynamespace
  • Mymodule
  • etc
  • config.xml

(où Mynamespace et Mymodule peuvent être remplacés respectivement par le nom de votre société et par le nom du module que vous souhaitez surcharger)

Puis, configurez le fichier config.xml de cette manière :

app/code/local/Mynamespace/Mymodule/etc/config.xml

<?xml version="1.0"?>
<config>
    <modules>
        <Mynamespace_Mymodule>
            <version>0.1.0</version>
        </Mynamespace_Mymodule>
    </modules>
</config>

Et, afin d'informer Magento de l'existence de ce nouveau module, créez un nouveau fichier nommé Mynamespace_Mymodule.xml dans le répertoire ./app/etc/modules :

app/etc/modules/Mynamepsace_Mymodule.xml

<?xml version="1.0"?>  
<config>
    <modules>
        <Mynamespace_Mymodule>
            <active>true</active>
            <codePool>local</codePool>
            <!--
            <depends>
                <Mage_Catalog />
                <Mage_Cms />
            </depends>
            -->
        </Mynamespace_Mymodule>
    </modules>
</config>

Remarque :

Afin que Magento puisse gérer efficacement l’ordre de chargement des modules, il est nécessaire de définir dans la section depends tous les modules dont dépend celui créé.

N'oubliez pas que si le cache de Magento est activé, les modifications effectuées dans les fichiers de configurations XML ne seront prises en compte que lorsque vous le rafraîchirez.

Surcharger une classe

Pour surcharger un contrôleur, un modèle, un bloc ou un helper, tous basés sur des classes PHP, nous aurons besoin de créer une ou plusieurs classes étendant les fonctionnalités par défaut du core de Magento.

Cette extension se fera à l'aide du mot-clé extends de PHP et du fichier config.xml permettant d'indiquer à Magento le type de surcharge à effectuer.

Les différents types de surcharge

Afin de vous présenter au mieux les différents types de surcharge, nous allons à chaque fois nous baser sur plusieurs exemples différents représentant certaines subtilités qui peuvent souvent poser beaucoup de problèmes.

Surcharge d'un contrôleur

Dans ce document, nous allons nous intéresser à deux types de contrôleur : les contrôleurs dits accessibles et les contrôleurs d'arrière-plan.

Le premier type représente les contrôleurs auxquels on peut accéder directement grâce à leur URL. Par exemple, le contrôleur Customer_Account est appelé à l'adresse http://www.store.com/customer/account/.

Quant au deuxième type, il représente les contrôleurs qui sont appelés de façon indirecte. Prenez l'exemple du contrôleur Cms_Index ou Cms_Page qui sont respectivement appelés lors de la visualisation de la page d'accueil (ex. http://www.store.com/) et d'une page du CMS, par exemple http://www.store.com/mycmspage.

Cas 1 : le contrôleur dit "accessible"

Pour ce cas, nous allons nous pencher sur le contrôleur Customer_Account en partant sur l'arborescence suivante :

  • Mynamespace
  • Mymodule
  • controllers
  • Customer
  • AccountController.php
  • etc
  • config.xml

Comme vous pouvez le voir, nous avons ajouté un répertoire controllers à notre configuration de base dans lequel se trouve un dossier Customer puis un fichier AccountController.php. C'est dans ce fichier que nous surchargerons les diverses méthodes du contrôleur Customer_Account (fichier ./app/code/Mage/Customer/controllers).

Pour commencer, nous allons réécrire la méthode _loginPostRedirect du fichier AccountController.php afin que l'utilisateur soit redirigé sur la page d'accueil après s'être connecté à son compte :

app/code/local/Mynamespace/Mymodule/controllers/Customer/AccountController.php

<?php
     require_once 'Mage/Customer/controllers/AccountController.php';

     class Mynamespace_Mymodule_Customer_AccountController extends Mage_Customer_AccountController
     {
          /**
          * Redirects user to home page...
          **/
          protected function _loginPostRedirect()
          {
               $this->_redirectUrl(Mage::getBaseUrl());
          }
     }

(notez bien qu'il vous faudra obligatoirement inclure la classe Mage_Customer_AccountController au travers d'un appel à l'instruction require_once)

Ensuite, nous allons rajouter une règle rewrite à notre fichier config.xml :

app/code/local/Mynamespace/Mymodule/etc/config.xml

<?xml version="1.0"?>
<config>
    <modules>
        <Mynamespace_Mymodule>
            <version>0.1.0</version>
        </Mynamespace_Mymodule>
    </modules>
    <global>
        <rewrite>
            <mynamespace_mymodule_customer_account>
                <from><![CDATA[#^/customer/account/#]]></from> <!-- traduit par http://www.store.com/customer/account/ -->
                <to>/mymodule/customer_account/</to> <!-- traduit par /Mynamespace/Mymodule/controllers/Customer/AccountController.php -->
            </mynamespace_mymodule_customer_account>
        </rewrite>
    </global>
    <frontend>
        <routers>
            <Mynamespace_Mymodule>
                <use>standard</use>
                <args>
                    <module>Mynamespace_Mymodule</module>
                    <frontName>mymodule</frontName>
                </args>
            </Mynamespace_Mymodule>
        </routers>
    </frontend>
</config>
Cas 2 : le contrôleur "d'arrière-plan"

Dans ce deuxième cas, nous allons surcharger le contrôleur Mage_Cms_IndexController (fichier ./app/code/core/Mage/Cms/controllers/IndexController.php).

Commençons par définir notre nouvelle arborescence :

  • Mynamespace
  • Mymodule
  • controllers
  • Cms
  • PageController.php
  • etc
  • config.xml

Ensuite, éditons le fichier PageController.php et ajoutons-y le code suivant :

app/code/local/Mynamespace/Mymodule/controllers/Cms/PageController.php

<?php
require_once 'Mage/Cms/controllers/PageController.php';

class Mynamespace_Mymodule_Cms_PageController extends Mage_Cms_PageController
{
    public function viewAction()
    {
        $pageId = $this->getRequest()->getParam('page_id', $this->getRequest()->getParam('id', false));
        if (!Mage::helper('cms/page')->renderPage($this, $pageId)) {
            $this->_forward('noRoute');
        }
    }
}

Enfin, configurons une nouvelle règle de rewrite dans le fichier config.xml :

app/code/local/etc/config.xml

<?xml version="1.0"?>
<config>
    <modules>
        <Mynamespace_Mymodule>
            <version>0.1.0</version>
        </Mynamespace_Mymodule>
    </modules>
    <global>
        <routers>
            <cms>
                <rewrite>
                    <page>
                        <to>/mymodule/cms_page</to>
                        <override_action>true</override_action>
                        <actions>
                            <view><to>mymodule/cms_page/view</to></view>
                        </actions>
                    </page>
                </rewrite>
            </cms>
        </routers>
    </global>
    <frontend>
        <routers>
            <Mynamespace_Mymodule>
                <use>standard</use>
                <args>
                    <module>Mynamespace_Mymodule</module>
                    <frontName>mymodule</frontName>
                </args>
            </Mynamespace_Mymodule>
        </routers>
    </frontend>
</config>

Surcharge d'un modèle

Pour surcharger un modèle, nous allons nous baser sur l'exemple du modèle Cms_Page (fichier ./app/code/core/Mage/Cms/Model/Page.php).

Pour commencer, reprenons notre arborescence de base et ajoutons-y un répertoire Model, un dossier Cms puis un fichier Page.php :

  • Mynamespace
  • Mymodule
  • etc
  • config.xml
  • Model
  • Cms
  • Page.php

Ensuite, dans le fichier Page.php, étendons la classe Mage_Cms_Model_Page de cette façon :

app/code/local/Mynamespace/Mymodule/Model/Cms/Page.php

<?php

class Mynamespace_Mymodule_Model_Cms_Page extends Mage_Cms_Model_Page
{

    /* ... */

}

Puis, dans le fichier config.xml, indiquons à Magento que nous souhaitons surcharger ce modèle :

app/code/local/Mynamespace/Mymodule/etc/config.xml

<?xml version="1.0"?>
<config>
    <modules>
        <Mynamespace_Mymodule>
            <version>0.1.0</version>
        </Mynamespace_Mymodule>
    </modules>
    <global>
        <models>
            <cms>
                <rewrite>
                    <page>Mynamespace_Mymodule_Model_Cms_Page</page>
                </rewrite>
            </cms>
        </models>
    </global>
</config>

Vous remarquerez qu'au sein de la balise models nous retrouvons les balises cms et page, qui représentent respectivement le nom du module et le nom du modèle. Pour illustrer au mieux ce mécanisme, voici d'autres exemples de configuration :

app/code/local/Mynamespace/Mymodule/etc/config.xml

<global>
    <models>

         <!-- Surcharge du modèle Mage_Cms_Page -->
         <cms>
             <rewrite>
                 <page>Mynamespace_Mymodule_Model_Cms_Page</page>
             </rewrite>
         </cms>

         <!-- Surcharge du modèle Mage_Adminhtml_Model_Customer_Renderer_Region -->
         <adminhtml>
             <rewrite>
                 <customer_renderer_region>Mynamespace_Mymodule_Model_Adminhtml_Customer_Renderer_Region</customer_renderer_region>
             </rewrite>
         </adminhtml>        

         <!-- Surcharge du modèle Mage_Customer_Model_Entity_Customer_Collection -->
         <customer_entity>
             <rewrite>
                 <customer_collection>Mynamespace_Mymodule_Model_Customer_Entity_Customer_Collection</customer_collection>
             </rewrite>
         </customer_entity>        

         <!-- Surcharge du modèle Mage_CatalogInventory_Model_Stock_Item_Api_V2 -->
         <cataloginventory_stock>
             <rewrite>
                 <item_api_v2>Mynamespace_Mymodule_Model_CatalogInventory_Stock_Item_Api_V2</item_api_v2>
             </rewrite>
         </cataloginventory_stock>

         <!-- Surcharge du modèle Mage_Cms_Model_Mysql4_Page -->
         <cms_mysql4>
             <rewrite>
                 <page>Mynamespace_Mymodule_Model_Cms_Mysql4_Page</page>
             </rewrite>
         </cms_mysql4>

         <!-- Surcharge du modèle Mage_Cms_Model_Mysql4_Page_Collection -->
         <cms_mysql4>
             <rewrite>
                 <page_collection>Mynamespace_Mymodule_Model_Cms_Mysql4_Page_Collection</page_collection>
             </rewrite>
         </cms_mysql4>

         <!-- Surcharge du modèle Mage_CatalogSearch_Model_Mysql4_Advanced_Collection -->
         <catalogsearch_mysql4>
             <rewrite>
                 <advanced_collection>Mynamespace_Mymodule_Model_CatalogSearch_Mysql4_Advanced_Collection</advanced_collection>
             </rewrite>
         </catalogsearch_mysql4>

         <!-- Surcharge du modèle Mage_Tax_Model_Mysql4_Calculation_Rate_Title_Collection -->
         <tax_mysql4>
             <rewrite
                 <calculation_rate_title_collection>Mynamespace_Mymodule_Model_Tax_Mysql4_Calculation_Rate_Title_Collection</calculation_rate_title_collection>
             </rewrite>
         </tax_mysql4>

         <!-- Surcharge du modèle Mage_Catalog_Model_Resource_Eav_Mysql4_Product -->
         <catalog_resource_eav_mysql4>
             <rewrite>
                 <product>Mynamespace_Mymodule_Model_Catalog_Resource_Eav_Mysql4_Product</product>
             </rewrite>
         </catalog_resource_eav_mysql4>

    </models>
</global>

Remarque : un modèle basé sur une classe abstraite ne peut pas être surchargé de cette façon.

Surcharge d'un bloc

Dans cette section, nous allons nous baser sur l'exemple du bloc Mage_Cms_Page. Dans un premier temps, nous allons donc mettre en place l'arborescence suivante :

  • Mynamespace
  • Mymodule
  • Block
  • Cms
  • Page.php
  • etc
  • config.xml

Dans le fichier Page.php, nous allons étendre la classe Mage_Cms_Block_Page et redéfinir la méthode _toHtml afin d'ajouter au contenu des pages du CMS affiché sur le frontend leur titre (qui par défaut n'est affiché que dans la barre de titre du navigateur) :

app/code/local/Mynamespace/Mymodule/Block/Cms/Page.php

<?php
class Mynamespace_Mymodule_Block_Cms_Page extends Mage_Cms_Block_Page
{
    protected function _toHtml()
    {
        $processor = Mage::getModel('core/email_template_filter');
        $html = $processor->filter($this->getPage()->getContent());
        $html = $this->getMessagesBlock()->getGroupedHtml() . $html;
        return $this->getPage()->getTitle() . $html; // ajout du titre au contenu
    }
}

Ensuite, nous allons déclarer la surcharge dans le fichier config.xml :

app/code/local/Mynamespace/Mymodule/etc/config.xml

<?xml version="1.0"?>
<config>
    <modules>
        <Mynamespace_Mymodule>
            <version>0.1.0</version>
        </Mynamespace_Mymodule>
    </modules>
    <global>
        <blocks>
            <cms>
                <rewrite>
                    <page>Mynamespace_Mymodule_Block_Cms_Page</page>
                </rewrite>
            </cms>
        </blocks>
    </global>
</config>

Vous remarquerez qu'au sein de la balise blocks nous retrouvons les balises cms et page, qui représentent respectivement le nom du module et le nom du bloc. Mais que faire lorsque le bloc à surcharger se trouve dans un sous-répertoire du dossier Blocks ?

Et bien il suffira de remplacer le nom du bloc par son chemin à partir du dossier Blocks. Afin d'illustrer ce propos, prenons comme exemple le bloc Customer_Form_Login (fichier ./app/code/core/Mage/Customer/Blocks/Form/Login.php) dont voici le paramétrage du fichier config.xml :

app/code/local/Mynamespace/Mymodule/etc/config.xml

<global>
    <blocks>
        <customer>
            <rewrite>
                <form_login>Mynamespace_Mymodule_Block_Customer_Form_Login</form_login>
            </rewrite>
        </customer>    
    </blocks>
</global>

Et pour compléter ce propos, voici un autre exemple avec le bloc Customer_Account_Dashboard_Address (fichier ./app/code/core/Mage/Customer/Blocks/Account/Dashboard/Address.php) :

app/code/local/Mynamespace/Mymodule/etc/config.xml

<global>
    <blocks>
        <customer>
            <rewrite>
                <account_dashboard_address>Mynamespace_Mymodule_Block_Customer_Account_Dashboard_Address</account_dashboard_address>
            </rewrite>
        </customer>
    </blocks>
</global>

Enfin, lorsqu'il s'agit d'un bloc appelé dans le backend (situé dans le répertoire ./app/code/core/Mage/Adminhtml/Block), voici la configuration à utiliser dans le fichier config.xml :

app/code/local/Mynamespace/Mymodule/etc/config.xml

<blocks>
    <!-- Surcharge du bloc Mage_Adminhtml_Block_Cms_Page_Grid -->
    <adminhtml>
        <rewrite>
            <cms_page_grid>Mynamespace_Mymodule_Block_Adminhtml_Cms_Page_Grid</cms_page_grid>
        </rewrite>
    </adminhtml>
</blocks>

Surcharge d'un helper

Tous les principes évoqués dans la section Surcharge d'un bloc sont également valables pour les helpers, à l'exception près qu'il faudra utiliser la balise helpers à la place de la balise blocks dans le fichier de configuration config.xml, et copier les helpers à surcharger dans le répertoire Helper.

Dans cette section, nous n'allons donc pas re-détailler les différents cas pouvant se présenter à vous.

Remarques importantes

  • Surcharge de la même classe par deux modules différents

A l’heure actuelle, Magento ne supporte pas la surcharge d'une même classe par deux modules différents.

En effet, si deux règles rewrite portent sur la même classe, seule une d'entre elles sera exécutée. Pour éviter ce problème, il faudra faire en sorte que le la surcharge du module 1 étende celle du module 2 qui étendra quant à elle la classe de base.


  • Surcharge de la totalité d’une classe (ou d’un module)

Si vous souhaitez surcharger la totalité d’un module (ex. Mage_Catalog) ou une classe en entière, copiez simplement les fichiers en question dans le répertoire ./app/code/local/Mage en y respectant la structure de base.

Pour exemple, la surcharge du modèle Mage_Catalog_Model_Product (fichier ./app/code/core/Mage/Catalog/Model/Product.php) devra être placé dans le répertoire ./app/code/local/Mage/Catalog/Model.

Pour activer cette surcharge, il n’est pas nécessaire de créer une nouvelle règle rewrite.

Références

20 commentaires

Commentez cet article : Surcharger le core de Magento : contrôleur, modèle, bloc, helper

Christophe Le 21/06/2010 à 00:03
Bel article Magentix !
Si après tout ça, y en a qui continue à toucher au Core...faut changer de métier je pense ;-)
Commentaire #1
Magentix Le 21/06/2010 à 00:04
L'article a été rédigé par Anthony Charrex, un très bon développeur Magento !
Commentaire #2
Frédéric MARTINEZ Le 21/06/2010 à 00:04
Cet article est tout simplement monstrueux !
Félicitations Anthony !
Commentaire #3
Julien MOIRENC Le 25/11/2010 à 13:50
Un grand merci à Anthony.

Un must have pour le développeur Magento !!
Commentaire #4
Julien Le 09/01/2011 à 21:17
Merci pour cette article!!
Commentaire #5
Hervé Le 14/01/2011 à 11:34
Très bon tout çà. Merci Magentix !
Petite question cependant : quelle différence entre "controller accessible" et "controller d'arrière plan" ?
Lequel utiliser dans quel cas ?

Merci beaucoup pour la réponse !
Commentaire #6
Anthony Charrex Le 18/01/2011 à 20:07
Salut,

La différence est expliquée dans l'introduction du chapitre "Surcharge d'un contrôleur" ;)
http://www.magentix.fr/modules-magento/surcharger-core-magento-controleur-modele-bloc-helper.html#p4-1

Anthony
Commentaire #7
Youpi Le 18/02/2011 à 16:39
Quelqu'un a-t-il déjà tenté de réécrire à la fois, avec la seconde méthode, et avec la premiere ?
J'ai l'impression que c'est impossible et que magento se mélange les pinceaux...
Commentaire #8
cricri!HelpMe Le 21/07/2011 à 16:12
J'aimerais bien toucher au core (et c'est plus facile, quand on sait faire) mais seulement dans le "Rapport du facturé en fonction du payé". J'aimerais simplement déjà modifier une des colonnes pour cumuler les ventes faites par Paypal (j'ai vu la TABLE sales_flat_order_payment : method='paypal_express'), mais est-ce assez simple pour démarrer (en mettant les pb de mise à jour / upgrade du core ;-)
Merci pour votre avis.
cricri
Commentaire #9
Adrien Le 14/09/2011 à 10:16
Merci ce tuto m'a beaucoup aidé, je débute sous magento (pas en PHP) et je me rends compte qu'on peut presque tout faire au final !
Commentaire #10
Rsi Le 01/02/2012 à 13:10
Merci pour cet article clair et concis.
Commentaire #11
benjion Le 14/02/2012 à 11:00
Impeccable, j'ai super bien compris en un quart d'heure ^^ je me permet de relayer cet article sur mon blog http://benjion.wordpress.com
Commentaire #12
Seb2nim Le 25/02/2012 à 01:11
Effectivement, article très intéréssant.
J'ai cependant 3 questions :

1) Pour le controlleur d'arrière plan, c'est une coquille dans l'exemple n'est-ce pas ? On aprle de surcharger Mage_Cms_IndexController pais la surcharge est faite pour Mage_Cms_PageController hein ??

2) A quoi correspond le bloc 'view' dans le rewrite ?
<actions>
<view><to>mymodule/cms_page/view</to></view>
</actions>

3) Comment sait on quel à quel type de Controller on a affaire quand on veut effectuer une surcharge ? Quelle est la classe parent qui différencie les deux ?

Encore merci pour cet article (et les autres).
Commentaire #13
Welinton Le 20/03/2012 à 20:23
Hey Branko, nice work thanks for sraihng this. I agree that it can be a pain setting up the skeleton modules in Magento, so anything that automates the process slightly is great! It's also very easy to not bother writing an admin module to interface with the config for a custom module, and instead obting to just hard code default values in your config.xml, so this will make me shift from doing that
Commentaire #14
nhkwgt Le 21/03/2012 à 13:24
ssmm0B , [url=http://kgdgtszusdxb.com/]kgdgtszusdxb[/url], [link=http://skbmvoqkifac.com/]skbmvoqkifac[/link], http://qamjopzzgqrz.com/
Commentaire #15
rcjpufuatv Le 23/03/2012 à 04:26
oTh2wL , [url=http://txatdroribzd.com/]txatdroribzd[/url], [link=http://lapmmynfvhrg.com/]lapmmynfvhrg[/link], http://yfwqbzxpqzmj.com/
Commentaire #16
Smoi Le 31/07/2012 à 16:26
Paye ton copast http://www.magentocommerce.com/wiki/groups/179/comment_surcharger_le_core_de_magento_v_1.3.0
T'aurais pu y ajouter des infos, il en manque plein, genre expliquer les différentes balises des xml

bref peu d'intérêt cet article.
Commentaire #17
BOB Le 07/03/2013 à 16:29
Bonjour, petite question,

Lorsque l'on surcharge un block, comment fait pour crée une variable public.

J'ai crée la variable comme ceci :
public $padre;
Ensuite j'ai mis un constructeur :
function __construct() // constructeur
{

$this->padre=array();
}

Mon problème à chaque fois dans ma function mon tableau se fait écraser. A chaque appel de block magento réinstancie t'il la class ?

Concrètement j'ai surcharger le block Block_Product_View_Options_Type_Select pour changer sa structure.
Commentaire #18
BOB Le 08/03/2013 à 15:38
Ok ma question ne sert à rien, sa ré-instancie donc sa écrase.
Maintenant la question c'est comment je crée une variable de session.
$session = Mage::getSingleton('core/session');
$session->setData('option', 'test');
Mage::log('CREATION SESSION '. $session->getOption());
Sa ne m'affiche pas test dans mes log.
?
Commentaire #19
BOB Le 08/03/2013 à 15:44
Bon tu peux delete mes messages j'ai trouvé.
En gros ton tuto http://www.magentix.fr/divers/stocker-afficher-variable-session-magento.html pour les variable de session ne marche pas.
Faut faire directement comme sa :
$session = Mage::getSingleton('core/session');
$session->setOption('test');
echo $session->getOption();
Commentaire #20
Rédiger un commentaire

Code de sécurité

* champs obligatoires