Mise en cache du bloc product/list : la solution

  • De le 06 octobre 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.

commentaires

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