Nicolas le 13 décembre 2009

Objectif

Voyons comment écrire un script PHP ayant accès à l’API Magento (ici version 1.3.2.4) pour réaliser divers traitements.

L’intérêt de ce type de script est par exemple d’automatiser une tâche répétitive (en utilisant un Cron) tout en utilisant les méthodes d’accès aux données fournies par Magento et donc, sans avoir à écrire de requêtes SQL complexes sur un schéma qui l’est tout autant.

La manipulation de collections d’objets via le modèle de données Magento est détaillée dans ce billet précédent.

Exemple de script basique

Le script suivant récupère et affiche les SKU et les prix de tous les produits activés :

<?php
// /repertoire_magento/scripts/my_script.php
$compilerConfig = '../includes/config.php';
if (file_exists($compilerConfig)) {
    include($compilerConfig);
}
$mageFilename = '../app/Mage.php';
if (!file_exists($mageFilename)) {
    echo $mageFilename." was not found";
    exit;
}
require_once $mageFilename;
// si 'admin', on utilise le modèle EAV, sans argument, on utilise le flat
Mage::app('admin');
 
// préparer la collection de produits
$model = Mage::getModel('catalog/product');
$collection = $model->getCollection()
    ->addAttributeToSelect(array('price'))
    ->addAttributeToFilter('status', True);
 
// afficher les infos de chaque produit
foreach ($collection as $product) {
    echo "{$product->getSku()} : {$product->getPrice()}".PHP_EOL;
}

Pour le lancer il suffit d’utiliser le client PHP en ligne de commande :

nico@laptop:/var/www/magento/scripts$ php my_script.php
my-sku-1234 : 6.5000
my-sku-5678 : 4.5000
my-sku-9101 : 5.8000
my-sku-1121 : 5.9500
my-sku-1314 : 7.5000

Question performances ?

Ce n’est pas une surprise, sur des requêtes complexes et/ou travaillant sur de gros volumes de données, nous payons la souplesse du modèle de données Magento par des performances parfois moyennes.

Celles-ci dépendent évidemment de la complexité des objets construits et du nombre de tables utilisées pour les stocker.

N’oublions pas que Magento se base sur l’excellent framework Zend, que son API est facilement accessible, et qu’elle nous permettra d’accéder aux données plus efficacement.

Pour nous aider dans la construction de la requête, il suffit d’afficher celle effectivement exécutée par Magento via :

// afficher la requête SQL exécutée
echo "{$collection->getSelect()}".PHP_EOL;

Ce qui donne :

SELECT `e`.*, `_table_status`.`value` AS `status`
FROM `catalog_product_entity` AS `e`
INNER JOIN `catalog_product_entity_int` AS `_table_status`
ON (_table_status.entity_id = e.entity_id)
AND (_table_status.attribute_id='80')
AND (_table_status.store_id=0)
WHERE (_table_status.value = '1')

Nous remarquerons que bizarrement, cette requête ne récupère pas le prix d’un produit, celui-ci est récupéré ultérieurement.

La portion de code suivante effectue donc la même opération via l’API Zend :

$resource = Mage::getSingleton('core/resource');
$read= $resource->getConnection('core_read');
$productEntityTable = $resource->getTableName('catalog_product_entity');
$productIntTable = $resource->getTableName('catalog_product_entity_int');
$productDecimalTable = $resource->getTableName('catalog_product_entity_decimal');
$select = $read->select()
    ->from(
        array('e' => $productEntityTable),
        // juste les champs nécessaires
        array(
            'sku' => 'e.sku',
            'price' => "_table_price.value")
    )
    // jointure statut (code attribut 80 issu de eav_attribute)
    ->join(
        array('_table_status' => $productIntTable),
        "(_table_status.entity_id = e.entity_id)
        AND (_table_status.attribute_id='80')
        AND (_table_status.store_id=0)",
        array()
    )
    // jointure prix (code attribut 60 issu de eav_attribute)
    ->join(
        array('_table_price' => $productDecimalTable),
        "(_table_price.entity_id = e.entity_id)
        AND (_table_price.attribute_id='60')
        AND (_table_price.store_id=0)",
        array()
    )
    // juste les produits activés
    ->where("_table_status.value = ?", True);
 
// exécuter la requête, et afficher les infos
$res = $select->query();
$rows = $res->fetchAll();
foreach ($rows as $row) {
    echo "{$row['sku']} : {$row['price']}".PHP_EOL;
}

Dans l’absolu, l’objet collection utilisant directement l’object Zend Select, on peut se contenter d’écrire (le snippet précédent permet de mieux comprendre le mécanisme) :

$select = $collection->getSelect();
$select
    // jointure prix (code attribut 60 issu de eav_attribute)
    ->join(
        array('_table_price' => $productDecimalTable),
        "(_table_price.entity_id = e.entity_id)
        AND (_table_price.attribute_id='60')
        AND (_table_price.store_id=0)",
        array('price' => '_table_price.value')
    );
// exécuter la requête, et afficher les infos
$res = $select->query();
$rows = $res->fetchAll();
foreach ($rows as $row) {
    echo "{$row['sku']} : {$row['price']}".PHP_EOL;
}

Conclusion

Il est donc simple de récupérer des données issues de Magento via un petit script pour effectuer des exports ou des synchronisations avec d’autres systèmes.

Dans le cas où l’on rencontrerait des problèmes de performances, il est également possible d’utiliser l’API Zend et logiquement, en « descendant d’un niveau », l’accès aux données est plus rapide et moins gourmand, la contrepartie étant la complexité de l’écriture de la requête et la perte de la structuration des données.

A bientôt pour de nouvelles aventures ! :)

Ressources complémentaires

Mots-clefs: , ,

Laisser une réponse

Vous pouvez utiliser ces mots-clés: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="">