Charger une collection ou un registre avec efficacité

optimcol

Voici ci dessous quelques préconisations rédigées par Gabriel BOUHATOUS, expert Magento à l’e-Commerce Academy. Bonne lecture 😉

Il est régulièrement nécessaire de pouvoir accéder aux données d’une entité ou d’une collection, qu’il s’agisse d’une entité produit, catégorie, d’une collection de clients ou de commandes…
Dans 90% des cas, les informations qu’il est nécessaire de récupérer sont déjà disponibles en mémoire, et ne demandent aucun nouveau chargement d’entité ou de collection.

Ce point est très important, car les conséquences des loads() sur les entités et les collections sont généralement très, très mauvaises sur les performances.
Un load de produit ou de catégorie peut occasionner jusqu’à plusieurs dizaines de requêtes SQL pour chaque nouveau chargement.
Imaginons un load de produit dans une boucle, et on monte rapidement à 100, 500, 1000 requêtes SQL à cause d’une seule méthode : load.
Multiplié par le nombre de visiteurs uniques sur la page : c’est le drame :-)

Solution : dans les 90% des cas indiqués, l’information sera déjà accessible via le registre.

Exemple général : utilisation du registre

La mise en mémoire d’informations par le biais du registre, qui est un tableau multidimensionnel dont la durée de vie est égale à la durée de vie du thread php, se fait grâce à Mage::register(‘key’, $value)
Exemple : vous avez loadé un objet et vous souhaitez qu’il soit accessible à n’importe quel autre moment du thread php, vous faites Mage::register(‘nom_de_lobjet’, $object);
La lecture d’une valeur dans le registre se fait grâce à Mage::registry(‘key’).
L’objet est alors conservé en mémoire, et n’importe quel code du process pourra s’en servir par la suite.

Exemple particulier : page produit

Sur une page produit, quelque soit l’endroit sur la page (c’est à dire quelque soit le block : header, footer ou même head), le produit est déjà accessible par le biais du registre.
N’importe quel block peut donc appeler Mage::registry(‘current_product’) pour récupérer un $product, à partir duquel tout est possible, ou presque : $product->getName(), $product->getUrl(), etc.
La mise dans le registre est réalisée par le controller Mage_Catalog_ProductController : le load du produit est donc réalisée en tout est pour tout UNE FOIS et une seule.

Exemple particulier : page catégorie

Même logique ici : le controller de category Mage_Catalog_CategoryController a déjà chargé la catégorie, celle-ci est mise à disposition via Mage::registry(‘current_category’).
A partir de là, tout est possible, dans n’importe quel block de la page catégorie (quelque soit son niveau évidemment) : $category->getName(); $category->getChildrenCategories(), …

Comment savoir ce qui est disponible dans le registre ?

Le registre n’est pas facile à débuguer mais vous pouvez intervenir sur la méthode register de la classe final Mage (fichier app/Mage.php) pour lister toute les clés qui y sont stockées et trouver celle qui pourrait vous servir.
Sinon vous avez un moyen artisanal + rapide (que j’utilise) : une recherche dans le code sur « Mage::register » est efficace (sur tout app/code/core/, on compte 187 occurrences, mais moins quand on cherche module par module – exemple : 13 pour Mage_Catalog).

Warning sur l’utilisation de la méthode getFirstItem() sur les collections

Vous avez peut-être déjà utilisé la méthode getFirstItem() sur une collection : celle-ci permet de récupérer le premier élément de la collection manipulée.
Warning : cette méthode est en fait aussi coûteuse qu’un load() de la collection… puisqu’avant de retourner le premier élément de la collection, getFirstItem load… l’intégralité de la collection :-)
Ceci peut être constaté dans la définition de la méthode : Varien_Data_Collection::getFirstItem. Exemple :

 
public function getFirstItem()
{
$this->load();
if (count($this->_items)) {
reset($this->_items);
return current($this->_items);
}
}

Solution pour éviter le drame : limiter la collection grâce à la méthode limit(), exemple :
$collection->getFirstItem() devient $collection->getSelect()->limit(1)->getFirstItem();
Notez que la méthode setPageSize peut aussi être utilisée (cf. Varien_Data_Collection et Varien_Data_Collection_Db).

A retenir en bref

Tout load d’entité ou de collection doit vous levez une alerte, et si un tel chargement vous paraît nécessaire, je vous invite à vérifier que rien n’existe dans le registre.

Suivre ces bonnes pratiques et bannir au maximum les chargements non nécessaires ne sera que bénéfique sur les performances de votre projet.

Nicolas Verhoye

Développeur Magento, Freelance

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *