Optimisation Magento – Mise à plat des tables Customer

magento_logo

Un des atouts de la plateforme E-commerce Magento est son architecture de base de données dite "EAV". Cette architecture offre une grande flexibilité lorsqu'il s'agit par exemple d'ajouter un attribut sur l'entité Client ou Produit. L'avantage vient du fait que chaque ajout d'attribut ne va modifier en rien la structure des tables car la liste des attributs ainsi que leurs valeurs sont stockées dans des tables bien distinctes.

Bien évidemment, cette architecture a un inconvénient non négligeable : les performances. Les données pour une entité étant réparties dans plusieurs tables, la visualisation globale des informations de cette entité demande un nombre de jointures assez conséquent. Magento, conscient de ce problème, a mis en place un système de tables "à plat". Par exemple, il existe la table "sales_flat_order_grid". Cette table contient toutes les informations utiles qui seront affichées en Back Office sur la page de la liste des commandes et offre ainsi un gain de performance.

Ce système de tables "flat" n'est cependant pas mis en place pour toutes les données de la plateforme Magento. L'entité Client ne bénéficie pas de ce système. Nous avons ainsi été confronté à un souci de performance sur l'un de nos projets. La plateforme Magento en question était très utilisée du côté Back Office notamment au niveau de la visualisation et de la recherche des clients présents sur la boutique. Le listing des clients étant consulté un très grand nombre de fois par jour et par un grand nombre d'administrateurs en même temps, la plateforme subissait des baisses de performances en adéquation avec le nombre croissant de nouveaux clients.

Cet article présente la solution de mise à plat des tables relatives aux données clients dans le but d'optimiser les performances du Back Office.

Solution

Du côté Back Office, les données de la grid affichées sur le listing des clients ne proviendront plus que depuis une seule table : "customer_flat_grid". Cette table sera alimentée de la même manière que la table "sales_flat_order_grid".

L'appel de la méthode :

$this->updateGridRecords($object->getId());

de la fonction

saveAttribute()

de la classe

Mage_Sales_Model_Mysql4_Order_Abstract

met à jour la table de la Grid des commandes.

Création de la table customer_flat_grid

La première étape va consister à créer un setup permettant de créer la table avec les colonnes dont nous avons besoin. Nous allons pour cela créer un module "CustomerFlat" dans le pool local de Magento.

Le setup mysql4-install-1.0.php est à créer dans le répertoire  sql/customerflat_setup du module :

<?php
$this->startSetup();

$this->run('

CREATE TABLE `customer_flat_grid` (
  `entity_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `firstname` varchar(255) NOT NULL,
  `lastname` varchar(255) NOT NULL,
  `email` varchar(255) NOT NULL,
  `company` varchar(255) DEFAULT NULL,
  `telephone` varchar(255) DEFAULT NULL,
  `postcode` varchar(255) DEFAULT NULL,
  `group_id` smallint(3) unsigned NOT NULL DEFAULT 0,
  `created_at` datetime DEFAULT NULL
  PRIMARY KEY (`entity_id`),
  CONSTRAINT `FK_CUSTOMER_FLAT_GRID_PARENT` FOREIGN KEY (`entity_id`) REFERENCES `customer_entity` (`entity_id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
');

$this->endSetup();
Création de la table customer_flat_grid

N'oubliez pas d'ajouter bien évidemment les différentes colonnes qui correspondent à vos attributs personnels que vous voulez voir apparaître sur la grid des clients. A noter que les clients qui ont été créés avant la mise en place de cette table n'y seront donc pas présents. Vous pouvez par conséquent prévoir un script d'initialisation qui alimente en "one shot" cette table pour les clients déjà existants.

Déclaration XML du module

<?xml version="1.0"?>
<config>
    <modules>
        <Perso_CustomerFlat>
            <version>1.0</version>
        </Perso_CustomerFlat>
    </modules>
    <!-- Déclaration du script setup -->
    <resources>
        <customerflat_setup>
            <setup>
                <module>Perso_CustomerFlat</module>
            </setup>
        </customerflat_setup>
    </resources>
    <models>
        <customer>
            <rewrite>
                <!-- Surcharge du model Customer -->
                <customer>Perso_CustomerFlat_Model_Customer</customer>
            </rewrite>
        </customer>
        <customer_entity>
            <rewrite>
                <!-- Surcharge du model Customer Entity -->
                <customer>Perso_CustomerFlat_Model_Entity_Customer</customer>
                <!-- Déclaration du model utilisé pour la collection de la grid -->
                <customer_grid_collection>Perso_CustomerFlat_Model_Entity_Customer_Grid_Collection</customer_grid_collection>
            </rewrite>
            <!-- Déclaration de la nouvelle table -->
            <entities>
                <customer_grid><table>customer_flat_grid</table></customer_grid>
            </entities>
        </customer_entity>
    </models>
</config>
Déclaration XML du module

Comme vous pouvez le constater, nous avons surchargé le modèle Customer ainsi que le ressource modèle Entity Customer. Cela va nous permettre d'ajouter à la fonction _afterSave du modèle l'appel de la méthode updateGridRecords que nous allons implémenter dans la ressource modèle Entity Customer.

After save

La fonction _afterSave est appelée dans la fonction save de tous les modèles Magento.Voici le code de la classe Perso_CustomerFlat_Model_Customer :

<?php
class Perso_CustomerFlat_Model_Customer extends Mage_Customer_Model_Customer
{
    protected function _afterSave()
    {
        $this->_getResource()->updateGridRecords($this);
        return parent::_afterSave();
    }
}
Surcharge de la méthode _afterSave

Mise à jour de la table Grid

Voici le code de la classe Perso_CustomerFlat_Model_Entity_Customer :

<?php
class Perso_CustomerFlat_Model_Entity_Customer extends Mage_Customer_Model_Entity_Customer
{
    const GRID_TABLE_NAME = 'customer_flat_grid';

    /**
     * @param $customer Mage_Customer_Model_Customer
     * @return $this
     */
    public function updateGridRecords($customer)
    {
        $datas = array();
        //... récupération des données nécessaires

        $this->_getWriteAdapter()->insertOnDuplicate(self::GRID_TABLE_NAME, $datas);
        return $this;
    }
}
Mise à jour de la table grid

Vous l'aurez compris, à chaque sauvegarde d'un client, la fonction insertOnDuplicate sera appelée et exécutera ainsi l'instruction SQL permettant d'ajouter un nouveau client ou de mettre à jour un client existant :

INSERT INTO customer_flat_grid ... ON DUPLICATE KEY ...

Modification de la table source en Back Office

Il ne nous reste plus qu'une étape : le changement de la table source dans laquelle la grid de la liste des clients en Back Office pioche ses données.

Souvenez vous, nous avons déclaré dans la configuration XML du module un nouveau modèle collection "customer_grid_collection". C'est maintenant qu'il entre en jeu. Nous devons surcharger la classe Mage_Adminhtml_Block_Customer_Grid et plus particulièrement la méthode _prepareCollection. La collection utilisée est à présent (je vous laisse la tâche de créer le code complet de la classe) :

$collection = Mage::getResourceModel('customer/customer_grid_collection')
Modification de la collection utilisée

Conclusion

Ceci clos cet article sur la mise en place d'une (petite) optimisation des performances du côté back office de Magento (version 1.x), utile lorsque votre boutique commence à posséder un nombre important de clients avec un nombre d'attributs conséquents et une utilisation massive de la gestion des clients par les administrateurs.

Certains morceaux de fonctions et de classes ne sont pas forcément complets dans l'état tel que je vous les ai copiés pour que cela puisse fonctionner en les reprenant à l'identique. J'espère néanmoins que le principe qui a été mis en place est compréhensible, les commentaires sont là pour ça dans le cas contraire.

Un commentaire

Laisser un commentaire

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

Captcha *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.