Mise en cache du bloc product/list : la solution

  • Par Magentix le 06/10/2011
  • Difficulté : 2/4

Mise en cache du bloc product/list : la solution Afin d'accroître les performances du site, la mise en cache des blocs est une solution extrêmement efficace. J'ai donc pris cette habitude de mettre en cache un maximum d'éléments sur la page. Pour les listes produits cela se révèle un peu plus compliqué...

L'affichage des listes produits génère un grand nombre de requêtes. Sur un catalogue important le temps de chargement de ces pages gonfle rapidement (voir article Accroître les performances par la mise en cache des blocs).

Problème de taille, ces listes de produits sont personnalisables pour l'internaute : nombre de produits, tri, direction, page... Mettre en cache le bloc produt/list est donc plus délicat qu'il n'y paraît. Prenons un cas de figure simple : je décide de mettre en cache le bloc selon l'identifiant de la catégorie. Magento exécute. Sur cette même catégorie j'applique maintenant un affichage de mes produits par prix décroissant. L'identifiant de ma catégorie est toujours le même, le contenu de la page restera donc inchangé.

Nous pourrions donc simplement inclure les filtres appliquées sur la catégorie dans la clé du cache du bloc. Soit quelque chose de ce type :

  • PRODUCT_LIST_[store_id]_[is_secure]_[package]_[template]_[category_id]_[params]_[filters]

Cette solution semble bonne et fonctionne correctement, à un détail près : certains paramètres appliqués sont enregistrés en session. Si je modifie le tri en sélectionnant "prix", le paramètre est enregistré pour l'ensemble des pages du site. J’enregistre donc les blocs en cache avec le filtre que j'ai préalablement appliqué pour une catégorie. Les internautes se retrouveront donc avec un affichage par défaut ne correspondant pas à ce que j'ai défini dans l'administration. Pire, le tri pourra être différent entre la page 1 et la page 2 !

Il faut donc se résoudre à ne pas exécuter la mise en cache du bloc product/list sur toutes les pages.

Le solution retenue est la suivante : j'utilise la mise en page uniquement si aucun filtre n'a été appliqué ou si ce filtre correspond à celui par défaut.

Ce cas de figure correspond à la majorité des pages affichées par les internautes. J'économise ainsi un grand nombre de requêtes simultanées, ce qui reste bénéfique pour l'ensemble du site.

Cela réduira déjà considérablement les temps de chargement du site :

Cache Google

Développement du module

Le bloc à surcharger est donc Mage_Catalog_Block_Product_List. Je passerai les détails du développement du module, il s'agit d'une simple surcharge de bloc (voir l'excellent article d'Anthony Charrex à ce sujet : Surcharger le core de Magento : contrôleur, modèle, bloc, helper).

Il nous faut ajouter un constructeur chargé de la mise en cache du bloc, ainsi qu'une méthode utilisée pour générer la clé.

Mage_Catalog_Block_Product_List

/* ... */

protected function _construct() {
     $category = Mage::registry('current_category');

     $this->addData(array(
          'cache_lifetime' => 86400,
          'cache_tags'     => array(Mage_Catalog_Model_Category::CACHE_TAG."_".$category->getId()),
          'cache_key'      => $this->getCacheKey(),
     ));
}

public function getCacheKey() {
      return 'PRODUCT_LIST_' . Mage::app()->getStore()->getId()
                . '_' . (int)Mage::app()->getStore()->isCurrentlySecure()
                . '_' . Mage::getDesign()->getPackageName()
                . '_' . Mage::getDesign()->getTheme('template')
                . '_' . Mage::registry('current_category')->getId();
}

/* ... */

Notons ici plusieurs problèmes :

  • Le bloc product/list est utilisé en dehors des pages catégories (Par exemple pour les recherches).
  • La mise en cache est appliquée quelque soit les filtres enregistrées.

Avec ces 2 paramètres pris en compte, nous obtenons le code suivant :

Mage_Catalog_Block_Product_List

/* ... */

protected function _construct() {
     $category = Mage::registry('current_category');

     if($category) {

          $order = Mage::getSingleton('catalog/session')->getSortOrder();
          $direction = Mage::getSingleton('catalog/session')->getSortDirection();
          $mode = Mage::getSingleton('catalog/session')->getDisplayMode();

          /* Soit je n'ai rien en session, soit la session contient mon affichage par défaut */
          $default = (!$order) || ($order == 'position' && $direction == 'asc' && $mode == 'list'); // Cette ligne est à compléter / modifier / corriger selon votre boutique

          /* Je ne dois pas avoir de paramètres autre que l'identifiant de la catégorie */
          $params = count(Mage::app()->getRequest()->getParams()) == 1;

          if($params && $default) {
              $this->addData(array(
                    'cache_lifetime' => 86400,
                    'cache_tags'     => array(Mage_Catalog_Model_Category::CACHE_TAG."_".$category->getId()),
                    'cache_key'      => $this->getCacheKey(),
               ));
          }

     }
}

public function getCacheKey() {
      return 'PRODUCT_LIST_' . Mage::app()->getStore()->getId()
                . '_' . (int)Mage::app()->getStore()->isCurrentlySecure()
                . '_' . Mage::getDesign()->getPackageName()
                . '_' . Mage::getDesign()->getTheme('template')
                . '_' . Mage::registry('current_category')->getId();
}

/* ... */

De cette façon je n'applique le cache que sur certaines pages, celles affichées majoritairement !

N'hésitez pas à apporter votre expérience sur le sujet, ou à apporter des améliorations... (On peut imaginer un module beaucoup plus poussé)

Attention : la mise en cache de la liste des produits impacte évidemment sur l'affichage des tarifs et des stocks, pouvant alors être erroné. Pensez à régler correctement le temps de mise en cache selon le débit de commande ou la fréquence de modification des prix.

9

Commentez cet article Mise en cache du bloc product/list : la solution

Agnès Le 06/10/2011 à 14:17
Attention, il vaut mieux éviter d'appeler addData('cache_key') dans le construct. Il suffit de donner un cache_lifetime dans le construct, et de surcharger la méthode getCacheKeyInfo. C'est lié au fait qu'au moment où on appelle le construct(), un certain nombre d'informations dynamiques (le nom du template, etc.) ne sont pas disponibles. Pour la product_list vous avez eu de la chance, mais si vous aviez tenté de mettre un bloc produit en cache, en utilisant l'ID du produit et le nom du template pour la clé de cache, vous auriez eu des surprises. :)
#1
Magentix Le 06/10/2011 à 14:20
Merci pour l'info Agnès ! A prendre en compte donc...
#2
Magentix Le 06/10/2011 à 16:05
Petite précision aussi, je n'ai pas pris en compte la limit, accessible avec :

$limit = Mage::getSingleton('catalog/session')->getLimitPage();
#3
Louis Le 18/10/2011 à 18:44
Bonjour,
avec la 1.5 il y a le problème du cache de block HTML invalide, si on utilise la solution donnée par inchoo consistant a supprimer ce cache a chaque enregistrement de produit ( http://inchoo.net/ecommerce/magento/magento-debugging/magento-how-to-fix-one-or-more-of-the-cache-types-are-invalidated-blocks-html-output-click-here-to-go-to-cache-management-and-refresh-cache-types/ ) peut on perdre du temps a recréer le cache a chaque fois (=l'affichage d'une page et la création de son cache est elle aussi rapide qu'une page appelé sans cache) ?

Merci en tout cas.
#4
Ed. Le 21/10/2011 à 16:06
intéressant, mais c'est dommage de supprimer tout le cache block HTML à chaque enregistrement de produit.
#5
Magentix Le 21/10/2011 à 16:19
En fait ici le cache n'est pas rafraîchie lors de la modification d'un produit, mais d'une mise à jour sur la catégorie.
J'avoue avoir un doute sur mon "cache tag". Ajouter l'identifiant de la catégorie n'a je pense pas d’intérêt (Magento le gère pour les produits mais pour les catégories tous les blocs sont apparemment rafraîchies systématiquement).

En gros avec mon cache tag :
Mage_Catalog_Model_Category::CACHE_TAG."_".$category->getId())

Pour rafraîchir le cache de la catégorie je dois faire :
Mage::app()->cleanCache(array(Mage_Catalog_Model_Category::CACHE_TAG."_".$category->getId()))

Je pense pas que Magento le fasse quelque part dans le code.

On pourrait se contenter de :
Mage_Catalog_Model_Category::CACHE_TAG
#6
adil Le 22/11/2011 à 13:35
bonjour
j’aimerai supprimer quelque information de information produit ex: j’ai besion de laisse seulement (prise ; général ; image ;inventaire et supprime les autre aide moi svp
#7
Hervé Le 10/01/2012 à 18:21
Au sujet du problème des paramètres mis en session, perso, je le règle ainsi :

<?xml version="1.0"?>
<layout version="0.1.0">
<catalog_category_default>
<reference name="product_list_toolbar">
<action method="disableParamsMemorizing"/>
</reference>
</catalog_category_default>
<catalog_category_layered>
<reference name="product_list_toolbar">
<action method="disableParamsMemorizing"/>
</reference>
</catalog_category_layered>
<catalogsearch_result_index>
<reference name="product_list_toolbar">
<action method="disableParamsMemorizing"/>
</reference>
</catalogsearch_result_index>
<catalogsearch_advanced_result>
<reference name="product_list_toolbar">
<action method="disableParamsMemorizing"/>
</reference>
</catalogsearch_advanced_result>
</layout>

Certes, on perd l'enregistrement en session et l'internaute devra choisir ses paramètres à chaque affichage de product_list (ce qui n'est pas du tout gênant à mes yeux) mais on gagne tellement à pouvoir mettre ainsi en cache les listes filtrées...
#8
Magentix Le 10/01/2012 à 20:40
Oui c'est une bonne solution ! Merci pour l'info.
#9
Rédiger un commentaire

Cliquez pour générer un nouveau code

* champs obligatoires