Design patterns dans Magento - observer et lazy loading

applications internet utilitaires

Ce site ne sera plus alimenté de contenu après août 2014. Tous les nouveaux articles seront redigés pour www.waitingforcode.com
A travers les derniers articles on a pu voir que même les design patterns un peu exotiques, comme prototype, object pool et iterator, peuvent être utilisés en PHP. Cette troisième partie de la série consacrée à des patrons de conception dans Magento sera consacrée aux derniers résistants.

Dans un premier temps on abordera l'observer qui permet d'aligner l'exécution du code externe au comportement "naturel" du système. On finira avec le lazy loading dont le but est l'économie de ressources.

Design pattern : observer
Ce patron de conception garantit une bonne flexibilité de l'application. Il se base sur une relation entre deux acteurs :
- un sujet observable : représente un objet qui va notifier les observateurs d'un changement à l'intérieur de sa classe
- des observateurs : ce sont des objets qui vont exécuter des actions appropriées suite à la notification reçue par le sujet observable

Si l'on voudrait comparer ceci à une situation de la vie quotidienne, on pourrait parler du passage piétons. Le feu vert est un sujet observable - tous les piétons le regardent pour savoir quand traverser la rue. Dès que le feu vert s'allume, les gens passent de l'autre côté. Le changement de couleur est donc une notification envoyée aux observateurs pour les autoriser à effectuer une action. Réinscrivons cet exemple dans le code PHP :


// our subject : call observers only when the light is green
class Lights
{
const GREEN = "green";
const RED = "red";
private $observers = array();

public function __construct()
{
$this->light = self::RED;
}

public function changeLight($color)
{
if($color != self::GREEN && $color != self::RED) throw new \Exception("Color was not defined");
echo "Light changed from {$this->light} to {$color}
";
$this->light = $color;
if($color == self::GREEN) $this->notifyObservers();
}

public function setObservers($observers)
{
foreach($observers as $observer)
{
$this->observers[] = $observer;
}
}

private function notifyObservers()
{
foreach($this->observers as $observer)
{
$observer->cross();
}
}

}

// our observers will implement this interface
interface Person
{
public function setQuantity($quantity);
public function cross();
}

class Women implements Person
{
private $quantity;

public function setQuantity($quantity)
{
$this->quantity = $quantity;
}
public function cross()
{
echo "{$this->quantity} women are crossing the street
";
}
}

class Men implements Person
{
private $quantity;
public function setQuantity($quantity)
{
$this->quantity = $quantity;
}
public function cross()
{
echo "{$this->quantity} men are crossing the street
";
}
}

class Children implements Person
{
private $quantity;
public function setQuantity($quantity)
{
$this->quantity = $quantity;
}
public function cross()
{
echo "{$this->quantity} children are crossing the street
";
}
}

$w = new Women;
$w->setQuantity(3);
$m = new Men;
$m->setQuantity(6);
$c = new Children;
$c->setQuantity(11);

$lights = new Lights();
$lights->changeLight(Lights::GREEN);
$lights->setObservers(array($w, $m, $c));
$lights->changeLight(Lights::RED);
$lights->changeLight(Lights::GREEN);


Magento utilise observer afin de permettre au développeur d'exécuter les opérations supplémentaires après chaque événement du système (mise à jour du panier, choix du transporteur). Pour avoir plus de détails, vous pouvez consulter la page répertoriant tous les événements de l'application.

L'implémentation d'un observer se déroule via le fichier config.xml du module qu'on veut rajouter au Magento. Pour une action déclenchée après l'affichage de la page de confirmation de commande, il peut se présenter de la manière suivante :


<frontend>
<events>
<checkout_onepage_controller_success_action>
<observers>
<plugin_action>
<type>singleton
<class>myplugin/observer
<method>checkIfSponsorship
</plugin_action>
</observers>
</checkout_onepage_controller_success_action>
</events>
</frontend>


Design pattern : lazy loading
Ce design pattern est très populaire dans tout ce qui concerne la représentation des classes métier (objets représentatifs des objets qui sont manipulés par l'application). Son principe repose sur le fait de suspendre l'utilisation d'un objet jusqu'au moment où l'application en a réellement besoin pour la première fois. Cet aspect peut également concerner les propriétés d'un objet. De fois, on doit en utiliser juste quelques-unes. On peut donc économiser de ressources et accélérer significativement le fonctionnement de l'application.

Dans la vie réelle on pourrait résumer lazy loading à la préparation d'une soupe aux choux. En la préparant, on ne mélange pas toutes les ingrédients d'un seul coup. A la place de faire ce geste incontrôlable et très gourmand dans les ressources physiques, on prépare chaque ingrédient séparément. Grâce à cela on peut contrôler mieux la préparation ainsi que garder la force pour déguster la soupe à la fin. On peut illustrer cet exemple dans le code :


class CabbageSoup
{
protected $_salt = "";
protected $_cabbage = "";
protected $_singleCream = "";

/**
* Setters.
*/
public function setSalt($value)
{
$this->_salt = $value;
}
public function setCabbage($value)
{
$this->_cabbage = $value;
}
public function setSingleCream($value)
{
$this->_singleCream = $value;
}

/**
* Getters.
*/
public function getSalt()
{
return $this->_salt;
}
public function getCabbage()
{
return $this->_cabbage;
}
public function getSingleCream()
{
return $this->_singleCream;
}

}

$soup = new CabbageSoup;
echo "I'll prepare my favorite cabbage soup. I've already bought 2 ingredients : ";
$soup->setSalt("salt");
$soup->setCabbage("cabbage");
echo "{$soup->getSalt()} and {$soup->getCabbage()} . ";
echo "
20 minutes later I bought the last ingredient. ";
$soup->setSingleCream("single cream");
echo "
Now, I have 3 needed ingredients : {$soup->getSalt()}, {$soup->getCabbage()} and {$soup->getSingleCream()}. I can start to cook.";


Magento utilise lazy loading pour la gestion des requêtes SQL. En fait, une requête n'est générée qu'au moment où l'on tente d'y accéder. Cela veut dire que l'utilisation d'une collection se fait avec l'appel de la méthode getData().

A travers ces 3 articles consacrés aux design patterns sous Magento on a pu voir qu'une application user- et developper-friendly ne peut pas être écrite sur un genou en 2h pendant la pause déjeuner. C'est un processus qui nécessite beaucoup d'effort d'anticipation et d'organisation. Les design patterns sont des composants incontournables de ce travail. Malheureusement, ce n'est pas le cas de la majorité d'applications open-source qui souvent sont soit user-, soit developer-friendly, rarement les deux. Et c'est ça qu'il faut pour qu'elles connaissent un succès comparable au Magento.
Bartosz KONIECZNY 04-05-2012 18:18 applications web
Moi

Développeur d'applications Internet et journaliste passionné par l'adjectif français. Un aigle polonais orienté vers la progression, volant très haut et écoutant du zouk après les matches du foot français.

Vous appréciez mon travail ?

Pour contribuer au développement de ce site, ou pour remercier pour des articles rédigés, vous pouvez faire un don.

Un conseil PHP

Utiliser XPath pour calculer le nombre d'apparitions d'un élément.

XPath est un langage qui sert à se déplacer au sein d'un document XML. Il contient beaucoup de fonctionnaltiés utilies lors de l'analyse de ces documents. Par exemple, pour voir le nombre d'apparition d'un tag, il suffit d'utiliser la méthode count et placer comme son paramètre le chemin vers les éléments à calculer. Imaginons qu'on veut calculer le nombre de noeds d'erreur. En PHP cela va se présenter ainsi :

// $this->xpath is the instance of DOMXPath
echo (int)$this->xpath->evaluate("count(/error)");