Charger les pages CMS en ajax sur Magento

Depuis quelques temps maintenant, je bosse sur un gros projet Magento. Et aujourd’hui j’ai été confronté à un problème pour lequel je n’ai pas trouvé de réelles solutions sur internet.

Ma problématique est la suivante: j’ai plusieurs pages CMS (et non pas des blocs statics) que je souhaiterai rende disponible en appel direct par leurs URL mais aussi dans une popin grâce à une requête ajax sans afficher le template global (1column, 2columns, …).

Et c’est ici que la prise de tête commence. En effet sur Magento, vous ne pouvez pas spécifier un template par environnement. Et nativement lorsque vous faites appel à une page CMS (que ce soit en ajax ou non), le template sélectionné en backoffice sera chargé :

choix du template en backoffice

Le but de cette article est de vous apprendre à modifier ce comportement, et de by-passer le template défini pour charger un template personnalisé.

Nous allons commencer par créer un module que nous nommerons pour l’exemple Webaki_Popin. (Je pars du principe que vous connaissait la démarche pour créer et déclarer votre module). Celui-ci surveillera l’événement cms_page_render fourni nativement par la plateforme.

L’ecouteur d’evenement

Dans votre fichier config.xml, il vous suffit de rajouter le handler de l’event:

<?xml version="1.0"?>
<config>
    <!-- [...] -->
    <global>
        <!-- [...] -->
        <events>
            <cms_page_render> <!-- Il s'agit ici de l'événement magento à utiliser -->
                <observers>
                    <webaki_popin_page_render>
                        <type>singleton</type>
                        <class>Webaki_Popin_Model_Observer</class>
                        <method>pageRender</method>
                    </webaki_popin_page_render>
                </observers>
            </cms_page_render>
        </events>
        <!-- [...] -->
    </global>
    <!-- [...] -->
</config>

Nous pouvons maintenant passer à la partie PHP. Dans le fichier Webaki_Popin_Model_Observer nous allons créer la structure de base de la classe:

class Webaki_Popin_Model_Observer
{
    /**
     * @param Varien_Event_Observer $observer
     */
    public function pageRender($observer)
    {
        
    }
}

L’événement est déclenché dans la classe Mage_Cms_Helper_Page grâce à l’instruction suivante:

Mage::dispatchEvent('cms_page_render', array('page' => $page, 'controller_action' => $action));

Nous avons donc tout ce qu’il faut pour pouvoir modifier les informations de la page mais aussi du contrôleur. Il faut également penser à vérifier que nous sommes bien en présence d’une requête ajax:

/**
 * @param Varien_Event_Observer $observer
 */
public function pageRender($observer)
{
    $page = $observer->getPage();
    /** @var Mage_Core_Controller_Varien_Action $action */
    $action = $observer->getControllerAction();

    if($action->getRequest()->isXmlHttpRequest())
    {
        $page->setRootTemplate('ajax');
    }
}

Chargement du template

Malheureusement rien n’est simple avec Magento, si vous chargez votre page en ajax, vous allez toujours avoir le même rendu car, tout simplement, il faut définir les informations du template.

Il suffit pour cela de le déclarer dans votre fichier de config. Je ne sors pas cette configuration de mon chapeau (parce que j’en est pas, déjà !), il vous suffit de vous inspirer de la configuration déjà créée par Magento dans le fichier ./app/code/core/Mage/Page/etc/config.xml :

<?xml version="1.0"?>
<config>
    <!-- [...] -->
    <global>
        <!-- [...] -->
        <page>
            <layouts>
                <ajax module="page" translate="label">
                    <label>Ajax</label>
                    <template>page/ajaxhtml.phtml</template>
                    <layout_handle>page_ajax</layout_handle>
                </ajax>
            </layouts>
        </page>
        <!-- [...] -->
    </global>
    <!-- [...] -->
</config>

Si vous rechargez à nouveau la page, …. rien n’à changé !
Quand je vous dis que rien n’est simple avec Magento 😉

Nous allons devoir supprimer le template déclaré dans la back-office puis ensuite ajouter le template « ajax »:

public function pageRender($observer)
{
    $page = $observer->getPage();
    /** @var Mage_Core_Controller_Varien_Action $action */
    $action = $observer->getControllerAction();

    if($action->getRequest()->isXmlHttpRequest())
    {
        // On commence par supprimer le template du layout si celui-ci est défini
        if ($page->getRootTemplate()) 
        {
            $inRange = Mage::app()->getLocale()->isStoreDateInInterval(null, $page->getCustomThemeFrom(), $page->getCustomThemeTo());
            // On récupère le bon handle
            $handle = ($page->getCustomRootTemplate()
                && $page->getCustomRootTemplate() != 'empty'
                && $inRange) ? $page->getCustomRootTemplate() : $page->getRootTemplate();

            // On charge la configuration du handle pour pouvoir ensuite supprimer le template
            $pageLayout = Mage::getSingleton('page/config')->getPageLayout($handle);
            if($pageLayout)
                $action->getLayout()->getUpdate()->removeHandle($pageLayout->getLayoutHandle());
        }
        $page->setRootTemplate('ajax');

        // On ajoute notre nouveau template
        $action->getLayout()->helper('page/layout')->applyHandle($page->getRootTemplate());
    }
}

Si maintenant vous rechargez votre page, vous allez pouvoir constater que le template est bien celui nommé « ajax » (n’oubliez pas de le fichier page/ajaxhtml.phtml).


OU