Surcharger le core de Magento : contrôleur, modèle, bloc, helper
- De Anthony Charrex le 08 mai 2010
- Difficulté : 2/4
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
- Configuration de base
- Surcharger une classe
- Les différents types de surcharge
- Remarques importantes
- Références
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.
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>
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.


Si après tout ça, y en a qui continue à toucher au Core...faut changer de métier je pense ;-)
Félicitations Anthony !
Un must have pour le développeur Magento !!
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 !
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
J'ai l'impression que c'est impossible et que magento se mélange les pinceaux...
Merci pour votre avis.
cricri
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).
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.
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.
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.
?
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();