Comment créer un Model sur Magento

Bonjour à tous, pour faire suite au premier tutoriel sur la création d’un formulaire de contact à travers un module Magento, je vais vous présenter comment créer un Model sur magento.

Dans notre cas, nous voulons enregistrer en base de données toutes les demandes de contact effectuées à travers notre formulaire de contact. Ca peut être utile de garder un historique des demandes en les stockant dans la base de données. Et puis c’est un exemple plutôt simple pour la réalisation d’un premier Model.

Attention, puisque nous avons besoin d’une table pour enregistrer les messages, il vous faut créer un setup sql dans votre module qui créera la table.

Vous pouvez suivre ce tutoriel suivant pour en connaitre la procédure : Comment créer un setup sur Magento

Qu’est-ce qu’un Model sur magento ?

Un model est une classe PHP qui est associé à votre module.
Ce model est dans le cas le plus simple représenté par un seul fichier.

Ce fichier est dans un répertoire Model de votre module. Exemple :

Un model est appelé de la manière suivante (dans un controller par exemple) :

$contact = Mage::getModel('sample_contact/contact')

Son utilisation sera détaillé un peu plus bas.

Ce seul fichier Contact.php aujourd’hui ne suffirait pas si l’on souhaite créer/supprimer/lire des entrées dans une table de contact en base de données. Il nous faudra l’accompagner de plusieurs autres classes que l’on appelle ResourceModel et Collection.

Vous pouvez commencer par créer le fichier Model Contact.php à l’emplacement suivant :
– app/code/local/Sample/Contact/Model/Contact.php

<?php

/**
 * Sample Contact Model Contact
 *
 * @category   Sample
 * @package    Sample_Contact
 * @author    Nicolas Verhoye <nicolas.verhoye@gmail.com>
 */

class Sample_Contact_Model_Contact extends Mage_Core_Model_Abstract
{
    public function _construct()
    {
        parent::_construct();
        $this->_init('sample_contact/contact');
    }
}

Comment se compose un Model ?

Comme dit précédemment, Un Model magento peut se présenter sous la forme d’un seul fichier, ou plus. Si vous souhaitez effectuer des opérations en base de données avec une table de votre module (préalablement créée via un Setup SQL) il vous faudra créer deux fichiers PHP supplémentaires en plus de votre classe Model.

A savoir donc :
– Le Model
Le Resource Model
La Collection

Ces trois classes vont étendre des classes natives de Magento. Cela nous permettra d’avoir accès à des propriétés comme la sauvegarde, la lecture ou la suppression d’entrées de message contact dans notre table.

En dehors de ces fonctionnalités de sauvegarde/lecture/suppression nous n’avons pas aujourd’hui besoin de plus de choses pour notre Module.

Mais nous pourrions imaginer par la suite d’ajouter des méthodes dans notre model de contact.
Comme nettoyer les éventuelles balises HTML du message lors qu’ils seront affichés.

<?php

/**
 * Sample Contact Model Contact
 *
 * @category   Sample
 * @package    Sample_Contact
 * @author    Nicolas Verhoye <nicolas.verhoye@gmail.com>
 */

class Sample_Contact_Model_Contact extends Mage_Core_Model_Abstract
{
    public function _construct()
    {
        parent::_construct();
        $this->_init('sample_contact/contact');
    }

    /* 
     * Afficher le contenu d'un message enregistré
     * en retirant les tags HTML.
     */
    public function getEscapedMessage() 
    {
        return strip_tags($this->getData('message'));
    }

}

Exemple d’utilisation :

<?php 
/* Création de l'objet */
$contact = Mage::getModel('sample_contact/contact');

/* Chargement avec un identifiant */
$contact->load(124); 

/* Affichage du message sans tags */
echo $contact->getEscapedMessage(); 

Qu’est-ce qu’un Resource Model sur magento ?

Le Resource Model est une extension de votre Model, c’est dans cette classe que vous placerez vos méthodes d’échanges avec la base de données. Vous aurez accès naturellement à des méthodes de lecture et d’écriture de données.

Ce fichier est dans un répertoire Model/Resource/ de votre module. Exemple :

Toutefois, cette classe Resource Model n’est pas dans la plupart des cas utilisé directement.
En effet il passe par le Model, qui lui appelle une méthode du Resource Model.

Pour illustrer avec un exemple, imaginons que nous souhaitons retrouver le client à partir de l’email qu’il aurait saisi. Nous avons une table avec plusieurs messages enregistrés, et une colonne email.

Nous avons donc besoin d’une méthode qui va échanger avec la base de données (table client et table contact), nous allons l’appeler findCustomerFromEmail

/**
 * Model : Sample/Contact/Model/Contact.php
 */

public function findCustomerFromEmail() 
{
    return $this->_getResource()->findCustomerFromEmail($this);
}

On retrouve cette même méthode dans la classe Resource, mais avec des requêtes SQL :

/**
 * Resource Model : Sample/Contact/Model/Resource/Contact.php
 */

public function findCustomerFromEmail($object) 
{
    $query = "SELECT customer_id ... WHERE email = ...";
    return $this->_getReadadapter()->fetchOne($query);
}

Et en utilisation concrète cela donne :

<?php 
/* Création de l'objet */
$contact = Mage::getModel('sample_contact/contact');

/* Chargement avec un identifiant */
$contact->load(124); 

/* Récupération client à partir de l'email du message de contact */
$customer = $contact->findCustomerFromEmail(); 

Ceci est juste un exemple, nous n’allons pas ajouter cette méthode à notre cas, mais pour la suite de notre tutoriel vous pouvez maintenant ajouter ce fichier Resource Model à cet endroit : app/code/local/Sample/Contact/Model/Contact/Resource/Contact.php

<?php
/**
 * Sample Contact Model Resource Contact
 *
 * @category   Sample
 * @package    Sample_Contact
 * @author    Nicolas Verhoye <nicolas.verhoye@gmail.com>
 */
class Sample_Contact_Model_Resource_Contact extends Mage_Core_Model_Resource_Db_Abstract
{
    protected function _construct()
    {
        $this->_init('sample_contact/contact', 'contact_id');
    }
}

Notez que le deuxième paramètre de la méthode _init (ici contact_id) est en fait la PRIMARY KEY de votre table contact.

Qu’est-ce qu’une Collection sur magento ?

Pour faire suite au deux précédentes classe (Model et Resource Model) le fichier de Collection permettra de traiter un ensemble de vos Models. Pour les afficher, les supprimer ou les modifier massivement.

Ce fichier est toujours dans le répertoire Model de votre module. A ce niveau :
– app/code/local/Sample/Contact/Model/Resource/Contact/Collection.php

Une collection est appelé de la manière suivante :

/**
 * Afficher la liste des messages
 */
$contactCollection = Mage::getModel('sample_contact/contact')->getCollection();

foreach($contactCollection as $contact)
{
    echo $contact->getMessage();
}

Il est tout à fait possible d’ajouter des méthodes à cette classe Collection, par exemple si vous souhaitez implémenter un filtre pour récupérer les messages qui ont été postés au-delà d’une date :

/**
 * Model : Sample/Contact/Model/Contact/Collection.php
 */
public function filterOlderThan($date) 
{
    $this->addFieldToFilter('posted_at', array('gt' => $date));
}

Et en utilisation concrète cela donne :

<?php 
/* Création de la collection */
$contactCollection = Mage::getModel('sample_contact/contact')->getCollection();
$contactCollection->filterOlderThan('2015-12-25');

/* Affichage des messages postés après le 25 décembre 2015 */
foreach($contactCollection as $contact)
{
    echo $contact->getMessage();
}

Pour la suite de notre tutoriel vous pouvez maintenant ajouter ce fichier Collection à cet endroit :
app/code/local/Sample/Contact/Model/Contact/Resource/Contact/Collection.php

/**
 * Sample Contact Model Resource Contact Collection
 *
 * @category   Sample
 * @package    Sample_Contact
 * @author    Nicolas Verhoye <nicolas.verhoye@gmail.com>
 */
class Sample_Contact_Model_Resource_Contact_Collection extends Mage_Core_Model_Resource_Db_Collection_Abstract
{
    /**
     * Resource initialization
     */
    protected function _construct()
    {
        $this->_init('sample_contact/contact');
    }
}

 

3. Déclaration des Models

Maintenant que nos trois fichiers sont créés, il nous faut les déclarer à Magento.
Comme souvent, cela se passe dans un fichier XML. En l’occurrence il s’agit du fichier etc/config.xml de votre module.

Ajoutez le noeud <models> et son contenu au fichier config.xml

<?xml version="1.0"?>
<config>
    <modules>
        <Sample_Contact>
            <version>0.1.0</version>
        </Sample_Contact>
    </modules>
    <global>
        <models>
            <sample_contact>
                <class>Sample_Contact_Model</class>
                <resourceModel>sample_contact_resource</resourceModel>
            </sample_contact>
            <sample_contact_resource>
                <class>Sample_Contact_Model_Resource</class>
                <entities>
                    <contact>
                      <table>sample_contact</table>
                    </contact>
                </entities>
            </sample_contact_resource>
        </models>
    </global>
    <frontend>
       (...)
    </frontend>
</config>

Ces nouvelles lignes, vont « informer » Magento de l’existence de Model, associé à une Resource Model, ainsi que le nom de la table (sample_contact).

Vous pouvez à présenter purger le cache de Magento (via suppression du répertoire /var/cache, ou depuis le back-office).

4. Mise en place dans notre exemple

Maintenant que les étapes de création du Model sont réalisées, nous pouvons utiliser le model dans notre module de formulaire de contact :

class Sample_Contact_IndexController extends Mage_Core_Controller_Front_Action
{
    public function indexAction()
    {
        $this->loadLayout();
         
        if ($this->getRequest()->isPost()) {
             
            $postData = $this->getRequest()->getParams();
             
            $email = $postData['email'];
            $message = $postData['message'];
            $subject = "Nouveau message de la part de $email !";
            $to = 'nicolas.verhoye@gmail.com';
 
            if (mail($to, $subject, $message)) {

                $contact = Mage::getModel('sample_contact/contact');
                $contact->setData('email', $email);
                $contact->setData('message', $message);
                $contact->save();

                $this->_redirectUrl('contact/index/confirm');
            }
             
        }
         
        $this->renderLayout();
    }
 
    public function confirmAction()
    {
        $this->loadLayout();
        $this->renderLayout();
    }
}

Créer un attribut produit avec un setup sur Magento

Les attributs produits dans Magento sont très important, ils permettent de définir les propriétés de votre produit. Une bonne série d’attributs sont déjà disponibles lors de l’installation de votre projet (poids, sku, fabricant…), mais peut-être auriez vous besoin par la suite d’en créer de nouveaux. Voici un tutoriel pour vous aider dans cette procédure.

Nous partirons du principe que vous ayez déjà créer votre module avec un setup.
Si vous ne savez pas comment créer un setup je vous invite à lire mon tutoriel à ce sujet : Comment créer un setup sur Magento ?

Création d’un attribut produit dans notre setup

C’est très simple il suffit d’appeler une méthode « addAttribute » en précisant quelques options pour créer notre attribut :

$installer = $this;
$installer->startSetup();

$this->addAttribute( Mage_Catalog_Model_Product::ENTITY, 'attribut_test', array(
	'group' => 'General',
	'type' => 'varchar',
	'label' => 'Nom de mon attribut', 
	'input' => 'text', 
	'global' => Mage_Catalog_Model_Resource_Eav_Attribute::SCOPE_GLOBAL, 
	'visible' => true, 
	'required' => false, 
	'default' => '',
	'searchable' => false, 
	'visible_in_advanced_search' => true, 
	'filterable' => true,
	'used_in_product_listing' => true,
	'unique' => false, 
	'apply_to' => 'simple', 
));

$installer->endSetup();

Créer votre premier module sur Magento

Nous allons voir ensemble comment créer un premier module sur Magento. Il s’agit d’un module assez simple, nous voulons proposer aux clients du site un formulaire de contact. Ce tutoriel a été réalisé sur la version 1.7 de Magento, mais rassurez-vous compte tenu de la simplicité du module il sera compatible sur toutes les versions.

Fonctionnalités de notre module

Notre module servira à générer un formulaire de contact, il faudra donc prévoir d’intervenir sur deux parties distinctes :
– Une partie visuel : affichage du formulaire avec ses champs (nom, email et message).
– Une partie code : envoi du mail et redirection vers une page de confirmation.

Développement du code

Tout d’abord il nous faut choisir deux choses :
– Un nom
– Un namespace (c’est un nom de groupe où sera placé le module)

Nous choisirons « Sample » pour le namespace, et « Contact » pour le nom du module.
Vous pouvez choisir ce que vous voulez, rien n’est imposé.

Le nom complet de notre module sera alors Sample_Contact (nom du namespace suivi du nom du module), vous retrouverez ce nom un peu partout dans les fichiers que nous allons créer ensemble.

Création du fichier « Sample_Contact.xml »

Il faut maintenant se rendre dans le répertoire app/etc/modules :
sample_2
Et y créer un nouveau fichier portant le nom « Sample_Contact.xml », qui contiendra ceci :

<?xml version="1.0"?>
<config>
	<modules>
		<Sample_Contact>
			<active>true</active>
			<codePool>local</codePool>
		</Sample_Contact>
	</modules>
</config>

Cette étape est systématique lorsque vous voulez créer un nouveau module.
Il est indispensable de le créer sinon votre module sera entièrement ignoré.

Notez que nous avons placé le nom complet de notre module (Sample_Contact) dans ce fichier ainsi que deux paramètres :
<active> qui sert à gérer l’état du module (on/off).
<codePool> qui sert à préciser que notre module est « fait main », ce n’est ni un module natif magento, ni un module communautaire téléchargé sur le web. Dans notre cas il faut mettre local.

Création de l’arborescence pour le code du module

Rendons nous à présent dans le répertoire app/code afin d’y créer le dossier « local » si il n’existe pas déjà.

A l’intérieur de ce dossier « local » ajouter un nouveau répertoire « Sample » (qui est notre namespace).
Dans ce dossier Sample ajouter un répertoire « Contact » (qui est le nom de notre module).
Ce qui donnera au final : « app/code/local/Sample/Contact » comme suit :
sample_1

Vous venez de créer l’arborescence de votre nouveau module, c’est ici que sera placé votre code pour récupérer les informations de notre utilisateur.
Une autre arborescence sera à créer plus tard pour s’occuper du rendu graphique du module (notre formulaire en html). Mais focalisons nous d’abord sur app/code/local/Sample/Contact.

Contenu de notre module Sample_Contact

Nous allons devoir respecter une nomenclature précise pour développer notre module, soyez très attentif au nom et à la casse des dossiers et des fichiers que vous allez créer. Vous pourriez très bien vous retrouver à chercher bêtement pourquoi le module ne marche pas à cause d’une majuscule oublié!

Le contenu de notre module se composera de 2 répertoires :
– etc
– controllers

Il peut y avoir d’autres dossiers comme « Block », « Model » et « Helper » mais nous n’en avons pas l’utilité. Ces répertoires seront expliqués dans un autre article.
sample_3

Le répertoire « etc »

Il contiendra uniquement des fichier de type xml, qui serviront à la configuration de votre module. Dans notre cas seul un fichier « config.xml » sera nécessaire. Voici son contenu :

<?xml version="1.0" encoding="UTF-8"?>
<config>
	<modules>
		<Sample_Contact>
			<version>0.1.0</version>
		</Sample_Contact>
	</modules>
	<frontend>
		<routers>
			<Sample_Contact>
				<use>standard</use>
				<args>
					<module>Sample_Contact</module>
					<frontName>contact</frontName>
				</args>
			</Sample_Contact>
		</routers>
	</frontend>
</config>

Emplacement : app/code/local/Sample/Contact/etc/config.xml

On notera deux informations dans ce fichier :
– La version de notre module :0.1.0, mais vous pouvez mettre ce que vous voulez.
– Le router : nous définissons ici le chemin d’accès au module. Exemple avec « contact » où le module sera accessible via l’adresse : monsite.com/contact

Le répertoire « controllers »

Si vous êtes familier avec le pattern MVC le nom « controller » ne devrait pas vous faire peur.
Dans notre cas un simple controller portant le nom ‘index’ suffira : IndexController.php

Dans cet IndexController, nous allons créer deux pages (actions) :
– Une page pour notre formulaire de contact.
– Une autre page pour la confirmation ou remerciement.

/* app/code/local/Sample/Contact/controllers/IndexController.php */
class Sample_Contact_IndexController extends Mage_Core_Controller_Front_Action
{
    public function indexAction()
    {
        $this->loadLayout();
        
        if ($this->getRequest()->isPost()) {
            
            $postData = $this->getRequest()->getParams();
            
            $email = $postData['email'];
            $message = $postData['message'];
            $subject = "Nouveau message de la part de $email !";
            $to = 'nicolas.verhoye@gmail.com';

            if (mail($to, $subject, $message)) {
                $this->_redirectUrl('contact/index/confirm');
            }
            
        }
        
        $this->renderLayout();
    }

    public function confirmAction()
    {
        $this->loadLayout();
        $this->renderLayout();
    }
}

Développement du template

Maintenant que la partie « code » de notre module est terminé nous allons nous occuper du template du module. Pour ce faire, rendez-vous dans le répertoire :

app/design/frontend/base/default/template/

A l’intérieur, créez un répertoire « contact » qui contiendra deux fichiers :

1) Le formulaire de contact : form.phtml

<!-- app/design/frontend/base/default/template/contact/form.phtml -->
<form action="" method="post" id="contact-form">
	<div class="content">
		<h2>Formulaire de contact</h2>
		<p>Veuillez remplir le formulaire suivant :</p>
		<ul class="form-list">
			<li>
				<label for="email" class="required"><em>*</em>Adresse email</label>
				<div class="input-box">
					<input type="text" name="email" id="email" class="input-text required-entry validate-email" />
				</div>
			</li>
			<li>
				<label for="message" class="required"><em>*</em>Message</label>
				<div class="input-box">
				    <textarea name="message" class="input-text required-entry"></textarea>
				</div>
			</li>
		</ul>
        <div class="actions">
            <button type="submit" class="button"><span><span>Valider</span></span></button>
        </div>
		<p class="required">* Required Fields</p>
	</div>
</form>

<script type="text/javascript">
//<![CDATA[
var contactForm = new VarienForm('contact-form');
//]]>   
</script>

2) La message de confirmation ou remerciement : confirm.phtml

<!-- app/design/frontend/base/default/template/contact/confirm.phtml -->
<div class="confirm">
    Merci, votre message a bien été envoyé !
</div>

En image cela donne :
samle_4

Maintenant que nos deux fichiers html sont crées il faut les rattacher à nos deux pages.
Pour ce faire nous allons ajouter un nouveau fichier dans le répertoire : app/design/frontend/base/default/layout qui s’appellera : sample_contact.xml :

<?xml version="1.0"?>
<layout version="0.1.0">

	<sample_contact_index_index>
		<reference name="content">
			<block type="core/template" name="contact_form" template="contact/form.phtml" />
		</reference>
	</sample_contact_index_index>
	
	<sample_contact_index_confirm>
		<reference name="content">
			<block type="core/template" name="contact_success" template="contact/confirm.phtml" />
		</reference>
	</sample_contact_index_confirm>
	
</layout>

Emplacement : app/design/frontend/base/default/layout/sample_contact.xml

Vous noterez qu’il y a deux nœuds :

<sample_contact_index_index></sample_contact_index_index>
<sample_contact_index_confirm></sample_contact_index_confirm>

Chacun des nœuds correspond à une action/page de notre controller.
Attention à bien respecter la nomenclature pour le nom de ces noeuds, c’est à dire :

<{namespace}_{module}_{controller}_{action}>

Dans chaque nœud nous appelons la référence « content » qui correspond au contenu de notre page.
Dans cette référence « content », nous précisons que nous voulons placer notre fichier html (form.phtml ou confirm.phtml).

C’est presque terminé, il nous reste juste à relier le fichier que nous venons de créer (layout/sample_contact.xml) à notre module.
Pour ça éditez le fichier app/code/local/Sample/Contact/etc/config.xml comme suit :

<?xml version="1.0" encoding="UTF-8"?>
<config>
    <modules>
        <Sample_Contact>
            <version>0.1.0</version>
        </Sample_Contact>
    </modules>
    <frontend>
        <routers>
            <Sample_Contact>
                <use>standard</use>
                <args>
                    <module>Sample_Contact</module>
                    <frontName>contact</frontName>
                </args>
            </Sample_Contact>
        </routers>
        <layout>
            <updates>
                <sample_contact>
                    <file>sample_contact.xml</file>
                </sample_contact>                
            </updates>
        </layout>
    </frontend>
</config>

Cette modification sert juste à préciser le l’existence de notre fichier template « sample_contact.xml » à Magento, sans quoi il ne sera pas prit en compte.

Notre module est terminé

Si vous vous rendez sur la page monsite.com/contact vous devrez y trouver ceci :
module
Bien sûr ce n’est pas grand chose, mais cela vous permettra de découvrir les bases d’un module sur Magento. Si vous le souhaitez les sources de ce module sont disponible en téléchargement.

Comment créer un setup sur Magento ?

Les setup sur Magento vous serviront à exécuter des requêtes SQL sur votre base de données : par exemple lorsque vous voulez ajouter un nouvel attribut produit, ou créer une page CMS.

On distingue deux types de setup à partir de Magento 1.6 :
– Les SETUP SQL : ils doivent être utilisés pour créer des attributs, des tables, ajouter des colonnes… bref, tout ce qu’il peut altérer la structure d’une table.
– Les SETUP DATA : ils doivent être utilisés pour l’insertion de données/contenus, comme des pages ou block CMS, des produits, etc.

Pour nous rapprocher d’un cas concret nous allons créer ensemble dans ce tutoriel une table « blog » et une page CMS.

Création d’un Setup SQL

Nous partirons du principe que vous ayez déjà créer votre module. Pour ceux qui ne savent pas comment faire je vous invite à lire mon tutoriel à ce sujet : Créez votre premier module sur Magento.
Notre module s’appellera Sample_Blog.

1. Déclaration du setup dans le fichier config.xml

Rendez-vous dans le fichier config.xml afin d’y déclarer votre setup :

<?xml version="1.0" encoding="UTF-8"?>
<config>
    <modules>
        <Sample_Blog>
            <version>0.1.0</version>
        </Sample_Blog>
    </modules>
    <global>
        <resources>
            <sample_blog_setup>
                <setup>
                    <module>Sample_Blog</module>
                </setup>
            </sample_blog_setup>
        </resources>
    </global>
</config>

Vous devez préciser deux choses ici :
– Le numéro de version de votre module, on le réutilisera tout à l’heure.
– La déclaration du setup. Notez que le nom de nœud ‘sample_blog_setup’ est important, nous l’utiliserons comme nom de dossier un peu plus tard.

2. Création du répertoire et du fichier de setup

Il nous faut maintenant créer le répertoire suivant dans notre module (sample_blog_setup, comme précisé dans le config.xml plus haut) :

app/code/local/Sample/Blog/sql/sample_blog_setup/

A l’intérieur de celui-ci nous allons ajouter un nouveau fichier que nous appellerons install-0.1.0.php (notez que nous avons placé le numéro de version du module dans le nom de fichier) :

<?php 
$installer = $this;
$installer->startSetup();
$installer->run("
    CREATE TABLE `blog` (
      `id` int(11) NOT NULL auto_increment,
      `title` text,
      `post` text,
      `date` datetime default NULL,
      `timestamp` timestamp NOT NULL default CURRENT_TIMESTAMP,
      PRIMARY KEY  (`blogpost_id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
");
$installer->endSetup();

Ce fichier créera une nouvelle table ‘blog’ pour l’exemple.

Pour exécuter ce setup il suffit de vous rendre sur n’importe quelle page de votre site, ce code sera exécuté en « arrière plan », ça sera entièrement transparent pour vos clients (veillez tout de même à vider votre cache si votre setup ne s’exécute pas !).

Si jamais vous vouliez ajouter un deuxième setup sql à votre module il vous faire deux choses :
– Changer le numéro de version de module dans le config.xml (pour 0.1.1 par exemple)
– Créer le fichier upgrade-0.1.0-0.1.1.php dans le répertoire sql/sample_blog_setup. Le nom du fichier se compose de l’ancien numéro de version (0.1.0), tiret le nouveau numéro de version (0.1.1).

Passons maintenant à la création du deuxième type de setup.

Création d’un Setup DATA

Dans cet exemple nous allons ajouter une page CMS à notre projet.
Attention votre version de Magento doit être au minimum 1.6 pour que cela fonctionne.

1. Déclaration du setup dans le fichier config.xml

La première étape est identique à celle d’un setup sql (voir point numéro 1 au dessus). Inutile de refaire ce point si vous aviez déjà créer un setup sql.

2. Création du répertoire et du fichier de setup

Il nous faut maintenant créer le répertoire suivant dans notre module (sample_blog_setup, comme précisé dans le config.xml plus haut) :

app/code/local/Sample/Blog/data/sample_blog_setup/

Notez que cette fois-ci nous somme dans le répertoire « data » et non plus « sql« .

A l’intérieur de celui-ci nous allons ajouter un nouveau fichier que nous appellerons data-install-0.1.0.php (notez que nous avons placé le numéro de version du module dans le nom de fichier) :

<?php 
$installer = $this;
$installer->startSetup();
$newBlock = Mage::getModel('cms/page')
      ->setTitle('Test CMS Page Title')
      ->setContent('Hello Im a new cms page.')
      ->setIdentifier('this-is-the-page-url')
      ->setIsActive(true)
      ->save();
$installer->endSetup();

Ce fichier créera une nouvelle page CMS pour l’exemple.

Comme pour le premier type de setup (sql), il suffit de vous rendre sur n’importe quelle page de votre site, ce code sera exécuté en « arrière plan ».

Si jamais vous vouliez ajouter un deuxième setup data à votre module il vous faire deux choses :
– Changer le numéro de version de module dans le config.xml (pour 0.1.1 par exemple)
– Créer le fichier data-upgrade-0.1.0-0.1.1.php dans le répertoire data/sample_blog_setup. Le nom du fichier se compose de l’ancien numéro de version (0.1.0), tiret le nouveau numéro de version (0.1.1).

Charger une collection ou un registre avec efficacité

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.

Quelques conseils pour commenter votre code

Dans la plupart des projet Magento on ne travaille jamais seul, c’est pourquoi il est très important de toujours commenter vos classes, vos méthodes, vos variables et vos templates. Mais pas n’importe comment, voici une présentation des différentes nomenclatures à respecter dans votre programmation.

Les classes

Entêtes à placer en haut de chaque classe :

/**
 * Catalog category helper
 *
 * @category   Sample
 * @package    Sample_Catalog
 * @author    Nicolas Verhoye <nverhoye@atecna.fr>
 */
class Sample_Catalog_Helper_Category extends Mage_Catalog_Helper_Category {}

Vous noterez que tout est en anglais (bah oui sinon ce serait trop simple :)).
On précisera 4 choses :
– Le nom de la classe
– La catégorie (namespace)
– Le package (nom de module complet)
– L’auteur

Les méthodes

Voici les entêtes à placer en haut de chaque méthode :

/**
 * Retrieve category url
 *
 * @param   Mage_Catalog_Model_Category $category
 * @return  string
 */
public function getCategoryUrl($category)
{
	if ($category instanceof Mage_Catalog_Model_Category) {
		return $category->getUrl();
	}
	return Mage::getModel('catalog/category')
		->setData($category->getData())
		->getUrl();
}

On précisera 3 choses :
– Le nom de la méthode, avec pourquoi pas une description
– Les paramètres possible à passer (avec le cast)
– Le retour de la méthode (avec le cast)
Notez que sur Eclipse vous pouvez générer de manière automatique ces commentaires en vous plaçant juste avant la méthode et en tapant « /** » puis « Entrer ».
D’autre chose peuvent être précisé comme le @todo (un pense-bête pour une futur évolution de la méthode).

Les variables

Voici les entêtes à placer au dessus des variables dont vous jugez l’utilité :

/**
 * Some function
 *
 * @return void
 */
public function someFunction()
{
	/** @var $product Mage_Catalog_Model_Product */
	$product = Mage::getModel('catalog/product');
}

Pas la peine de commenter chacune des variables que vous coderez, focalisez-vous sur les essentiels.
Exemple dans notre cas nous voyons à vue d’oeil que la variable est une instance de la classe Mage_Catalog_Model_Product. De plus un simple « CTRL+CLICK » sur la variable nous amènera sur la classe en question (sur Eclipse ou Netbeans).
On précisera 3 choses :
– Le noeud @var
– Le nom de la variable
– Le cast de la variable (ou sa classe).

Les templates

Voici les entêtes à placer en haut des templates, ca reste facultatif mais c’est toujours utile de les retrouver :

<?php
/**
 * Shopping cart template
 *
 * @see Mage_Checkout_Block_Cart
 */
?>

<div class="cart"> (...) </div>

On précisera 2 choses :
– Une description du template
– La classe du Block rattaché à votre template

Présentation de l’extension MGT Developer Toolbar

Voici un petit article pour vous parler de l’extension magento MGT Developer Toolbar. Cette extension (gratuite) ajoute une barre d’outil fixé en haut de vos pages Magento avec rapport complet sur les requêtes SQL, les observers, les blocks, et pleins d’autres elements appelés par votre page. De quoi être très utile si vous cherchez à débugger ou à optimiser les performances de votre projet !

mgt0

Téléchargement de l’extension MGT Developer Toolbar

Rendez-vous sur le site officiel de l’auteur pour récupérer les sources de cette outil. Il vous faudra vous inscrire et ajouter l’extension à votre panier pour pouvoir la télécharger : MGT Developer Toolbar

Installation et configuration

Une fois les sources récupérés vous trouverez un fichier Installation.pdf à l’intérieur pour vous aider.
L’installation se résume en quelques étapes :
– Copiez-collez le contenu de l’archive dans le dossier « app » de votre projet.
– Rendez-vous sur le back-office pour vider le cache.
– Déconnectez-vous et reconnectez vous pour mettre à jour les ACL du module.
– Allez dans Admin -> System -> Confguration -> MGT-COMMERCE.COM -> Developer Toolbar -> Settings -> Active -> Yes
– Vous pouvez préciser une adresse IP pour restreindre l’affichage de la barre d’outil (exemple : 127.0.0.1)
– Ouvrez votre fichier index.php à la racine de votre projet et dé-commentez la ligne « Varien_Profiler::enable(); »

mgt1

Utilisation de l’extension

Vous devriez à présent voir une barre d’outil en haut de vos pages :
mgt3
De gauche à droite vous trouverez :
– Un bouton pour réduire/maquer la barre d’outil
– Le numéro de version Magento de votre projet
– Un lien pour avoir des informations sur les Observers, Blocks, Handles…
– Un lien pour avoir le détail du profiler
– Le temps d’exécution de votre page (en secondes)
– La mémoire utilisé (en MB)
– Le nombre de requête SQL (cliquez sur l’icone pour plus de détails).

1) Bouton « Info »

Quand vous cliquez sur ce bouton, la barre d’outil se dépliera pour laisser place à plusieurs onglets :

– Onglet Request : Cet onglet vous donnera le nom du controller, du module et de l’action. Ainsi que les paramètres présent dans le Request (en POST/GET) : info_1
– Onglet General : Ce onglet vous donnera des informations sur le website et le store en cours : info_2
– Onglet Handles : Ce onglet vous donnera la liste des handles : info_3
– Onglet Events/Observer : Ce onglet vous donnera la liste des Events/Observers regroupés par lot : info_4
– Onglet Blocks : Ce onglet vous donnera l’arborescence de tout les blocks de votre page, si vous cliquez sur un des blocks vous aurez son type de le chemin du fichier phtml : info_5
– Onglet Config : Ce onglet vous propose un raccourci vers trois fonctionnalités natives de magento : info_6
– Et un dernier onglet phpinfo (je crois qu’il est inutile de décrire son utilité !)

2) Bouton « Profiler »

Quand vous cliquez sur ce bouton, la barre d’outil se dépliera pour laisser place à un tableau regroupant tout ce qui a pu être tracés sur Magento (events, chargement des layouts, models…). Vous pouvez ainsi vérifier si quelque chose prend anormalement trop de ressources.
mgt-profiler
Et si vous souhaitez tracer vous-même du code dans un de vos modules vous pouvez faire comme suit :

Varien_Profiler::start('Sample_Foo');
// Chargement d'une collection, sauvegarde d'un objet, etc.
Varien_Profiler::stop('Sample_Foo');

Une trace « Sample_Foo » apparaîtra dans le tableau avec le temps d’exécution et la mémoire utilisé.

2) Bouton « Database Queries »

Quand vous cliquez sur le dernier bouton, la barre d’outil se dépliera pour vous donner quelques informations sur les requêtes envoyées à votre base de données. Notamment :
– La requête la plus longue/lourde
– Le nombre de requête par seconde
– La liste complète de toute les requêtes
– Les transactions
– etc.
mgt_db

Surcharger des blocks, models, helpers et controllers sur Magento

Certains des modules natifs de Magento ne correspondent peut-être pas à 100% aux besoins de votre projet, vous aurez alors sûrement besoin de modifier le comportement d’un block, helper, model ou controller de l’un ou plusieurs de ces modules. Voici un petit tutoriel pour vous expliquer comment faire.

Pour notre exemple nous surchargerons un block, un helper, un model et un controller du module Mage_Catalog de Magento. Cette surcharge sera faite dans un nouveau module Sample_Catalog.

Surcharger un Block

Nous allons surcharger le block « catalog/product_list ».
Allez dans le fichier config.xml de votre module et ajoutez-y les lignes suivantes :

<?xml version="1.0" encoding="UTF-8"?>
<config>
	<modules>
		<Sample_Catalog>
			<version>0.1.0</version>
		</Sample_Catalog>
	</modules>
	<global>
		<blocks>
			<catalog>
				<rewrite>
					<product_list>Sample_Catalog_Block_Product_List</product_list>
				</rewrite>
			</catalog>
		</blocks>
	</global>
</config>

Créez le fichier « app/code/local/Sample/Catalog/Block/Product/List.php » avec le contenu suivant :

/**
 * Product list
 *
 * @category   Sample
 * @package    Sample_Catalog
 * @author     Nicolas Verhoye <contact@nicolas-verhoye.com>
 */
class Sample_Catalog_Block_Product_List extends Mage_Catalog_Block_Product_List
{
}

Surcharger un Helper

Nous allons surcharger le helper « catalog/category ».
Allez dans le fichier config.xml de votre module et ajoutez-y les lignes suivantes :

<?xml version="1.0" encoding="UTF-8"?>
<config>
	<modules>
		<Sample_Catalog>
			<version>0.1.0</version>
		</Sample_Catalog>
	</modules>
	<global>
		<helpers>
			<catalog>
				<rewrite>
					<category>Sample_Catalog_Helper_Category</category>
				</rewrite>
			</catalog>
		</helpers>
	</global>
</config>

Créez le fichier « app/code/local/Sample/Catalog/Helper/Category.php » avec le contenu suivant :

/**
 * Catalog category helper
 *
 * @category   Sample
 * @package    Sample_Catalog
 * @author     Nicolas Verhoye <contact@nicolas-verhoye.com>
 */
class Sample_Catalog_Helper_Category extends Mage_Catalog_Helper_Category
{
}

Surcharger un Model

Nous allons surcharger le model « catalog/product ».
Allez dans le fichier config.xml de votre module et ajoutez-y les lignes suivantes :

<?xml version="1.0" encoding="UTF-8"?>
<config>
	<modules>
		<Sample_Catalog>
			<version>0.1.0</version>
		</Sample_Catalog>
	</modules>
	<global>
		<models>
			<catalog>
				<rewrite>
					<product>Sample_Catalog_Model_Product</product>
				</rewrite>
			</catalog>
		</models>
	</global>
</config>

Créez le fichier « app/code/local/Sample/Catalog/Model/Product.php » avec le contenu suivant :

/**
 * Catalog product model
 *
 * @category   Sample
 * @package    Sample_Catalog
 * @author     Nicolas Verhoye <contact@nicolas-verhoye.com>
 */
class Sample_Catalog_Model_Product extends Mage_Catalog_Model_Product
{
}

Surcharger un controller

Nous allons surcharger le controller « ProductController ».
Allez dans le fichier config.xml de votre module et ajoutez-y les lignes suivantes :

<?xml version="1.0" encoding="UTF-8"?>
<config>
	<modules>
		<Sample_Catalog>
			<version>0.1.0</version>
		</Sample_Catalog>
	</modules>
	<frontend>
		<routers>
			<catalog>
				<args>
					<modules>
						<Sample_Catalog before="Mage_Catalog">Sample_Catalog</Sample_Catalog>
					</modules>
				</args>
			</catalog>
		</routers>
	</frontend>
</config>

Créez le fichier « app/code/local/Sample/Catalog/controllers/ProductController.php » avec le contenu suivant :

/**
 * Product controller
 *
 * @category   Sample
 * @package    Sample_Catalog
 * @author     Nicolas Verhoye <contact@nicolas-verhoye.com>
 */
require_once(Mage::getModuleDir('controllers','Mage_Catalog').DS.'ProductController.php');
class Sample_Catalog_ProductController extends Mage_Catalog_ProductController 
{
}

Validation Javascript des formulaires sur Magento

Magento dispose de sa propre librairie javascript de validation de formulaire. Celle-ci a été développé à l’aide du framework Prototype. Si la curiosité vous pousse à aller voir cette librairie vous la trouverez dans le répertoire « js/prototype/validation.js ».

Comment ajouter dès règles de validation sur un formulaire ?

Voici un exemple de formulaire sur lequel nous avons ajouté quelques règles de validation.

<form id="contact-form" method="post" >
	<h2>Formulaire de contact</h2>
	<p>Veuillez remplir le formulaire suivant :</p>
	<ul class="form-list">
		<li>
			<label for="email" class="required"><em>*</em>Adresse email</label>
			<div class="input-box">
				<input type="text" name="email" id="email" class="input-text required-entry validate-email" />
			</div>
		</li>
		<li>
			<label for="message" class="required"><em>*</em>Message</label>
			<div class="input-box">
				<textarea name="message" class="input-text required-entry"></textarea>
			</div>
		</li>
	</ul>
	<div class="actions">
		<button type="submit" class="button"><span><span>Valider</span></span></button>
	</div>
</form>
 
<script type="text/javascript">
var contactForm = new VarienForm('contact-form');
</script>

Il faut dans un premier temps donner un identifiant à notre formulaire (sur la balise form), puis dans la partie javascript il faut instancier un nouvel objet VarienForm en passant en paramètre l’identifiant du formulaire, comme suit :

var contactForm = new VarienForm('contact-form');

Il ne nous reste plus qu’a placer quelques classes sur les champs de notre formulaire :

<input class="input-text required-entry validate-email" type="text"/>
<textarea class="input-text required-entry"></textarea>

Dans notre cas nous avons utilisé deux classes CSS qui ont chacune leur utilité:
required-entry : qui signifie que le champs est obligatoire.
validate-email : qui signifie que le champs doit être composé d’un email valide.

validateform

Voici la liste complète des classes CSS qui peuvent être utilisées :

Nom de classe Message d’erreur
validate-no-html-tags HTML tags are not allowed
validate-select Please select an option.
required-entry This is a required field.
validate-number Please enter a valid number in this field.
validate-number-range The value is not within the specified range.
validate-digits Please use numbers only in this field. Please avoid spaces or other characters such as dots or commas.
validate-digits-range The value is not within the specified range.
validate-alpha Please use letters only (a-z or A-Z) in this field.
validate-code Please use only letters (a-z), numbers (0-9) or underscore(_) in this field, first character should be a letter.
validate-alphanum Please use only letters (a-z or A-Z) or numbers (0-9) only in this field. No spaces or other characters are allowed.
validate-alphanum-with-spaces Please use only letters (a-z or A-Z), numbers (0-9) or spaces only in this field.
validate-street Please use only letters (a-z or A-Z) or numbers (0-9) or spaces and # only in this field.
validate-phoneStrict Please enter a valid phone number. For example (123) 456-7890 or 123-456-7890.
validate-phoneLax Please enter a valid phone number. For example (123) 456-7890 or 123-456-7890.
validate-fax Please enter a valid fax number. For example (123) 456-7890 or 123-456-7890.
validate-date Please enter a valid date.
validate-email Please enter a valid email address. For example johndoe@domain.com.
validate-emailSender Please use only visible characters and spaces.
validate-password Please enter 6 or more characters. Leading or trailing spaces will be ignored.
validate-admin-password Please enter 7 or more characters. Password should contain both numeric and alphabetic characters.
validate-cpassword Please make sure your passwords match.
validate-url Please enter a valid URL. Protocol is required (http://, https:// or ftp://)
validate-clean-url Please enter a valid URL. For example http://www.example.com or www.example.com
validate-identifier Please enter a valid URL Key. For example « example-page », « example-page.html » or « anotherlevel/example-page ».
validate-xml-identifier Please enter a valid XML-identifier. For example something_1, block5, id-4.
validate-ssn Please enter a valid social security number. For example 123-45-6789.
validate-zip Please enter a valid zip code. For example 90602 or 90602-1234.
validate-zip-international Please enter a valid zip code.
validate-date-au Please use this date format: dd/mm/yyyy. For example 17/03/2006 for the 17th of March, 2006.
validate-currency-dollar Please enter a valid $ amount. For example $100.00.
validate-one-required Please select one of the above options.
validate-one-required-by-name Please select one of the options.
validate-not-negative-number Please enter a number 0 or greater in this field.
validate-zero-or-greater Please enter a number 0 or greater in this field.
validate-greater-than-zero Please enter a number greater than 0 in this field.
validate-state Please select State/Province.
validate-new-password Please enter 6 or more characters. Leading or trailing spaces will be ignored.
validate-cc-number Please enter a valid credit card number.
validate-cc-type Credit card number does not match credit card type.
validate-cc-type-select Card type does not match credit card number.
validate-cc-exp Incorrect credit card expiration date.
validate-cc-cvn Please enter a valid credit card verification number.
validate-data Please use only letters (a-z or A-Z), numbers (0-9) or underscore(_) in this field, first character should be a letter.
validate-css-length Please input a valid CSS-length. For example 100px or 77pt or 20em or .5ex or 50%.
validate-length Text length does not satisfy specified text range.
validate-percents Please enter a number lower than 100.
required-file Please select a file
validate-cc-ukss Please enter issue number or start date for switch/solo card type.

Comment créer de nouvelles règles de validation ?

Admettons que nous voulons empêcher le visiteur d’entrer autre chose que « Bonjour » dans un champs texte (oui c’est bête mais c’est pour l’exemple!).

Nous plaçons sur notre page (ou dans un fichier javascript externe) le code suivant :

Validation.add('validate-bonjour',"La valeur doit être 'Bonjour'", function(value) {
    if(value === 'Bonjour') {
        return true;
    } else {
        return false;
    }
});

Le premier paramètre est le nom de votre validateur : validate-bonjour.
Le second paramètre correspond au message d’erreur.
Le troisième paramètre est la fonction qui validera votre champs, où value est la valeur entré par le visiteur.

Il nous reste plus qu’à placer la classe « validate-bonjour » sur notre champs texte :

<form id="sample-form">
  <input type="text" name="message" id="message" class="validate-bonjour" />
  <input type="submit" value="Valider" />
</form>

<script type="text/javascript">
var sampleForm = new VarienForm('sample-form');
Validation.add('validate-bonjour',"La valeur doit être 'Bonjour'", function(value) {
    if(value === 'Bonjour') {
        return true;
    } else {
        return false;
    }
});
</script>

Et c’est tout! Magento s’occupera du reste. Quand votre visiteur validera le formulaire si le champs texte ne vaut pas « Bonjour » il ne pourra pas passer à la suite.

Mailcheck aide à réduire les fautes de frappe

Mailcheck.js est un plugin jQuery qui propose de corriger/rectifier les fautes de frappe de vos utilisateurs lorsqu’il doivent se connecter ou s’inscrire avec un formulaire sur votre site.
Quand votre visiteur entrera « nicolas@hotnail.com »,
Mailcheck lui proposera de corriger par « nicolas@hotmail.com ».

Incroyable non ? Bien sûr, ceci marche également avec d’autres noms de domaines (yahoo, gmail…)
D’après l’auteur le script peut réduire jusqu’à 50% le taux de rebond suite à un mail mal entré dans un formulaire d’inscription.

Téléchargement : https://github.com/Kicksend/mailcheck