Configurer et utiliser plusieurs bases de données

  • De le 03 août 2012
  • Difficulté : 3/4

Configurer et utiliser plusieurs bases de données Magento permet l'utilisation de plusieurs bases de données. Au sein d'un module, la configuration d'une nouvelle connexion est extrêmement simple. Cet article présente un exemple concret de module ayant recours à une base externe.

Configuration

Pour configurer l'accès à une nouvelle base de données dans Magento, rien de plus simple. Dans le fichier de configuration du module, il nous suffit de déclarer une connexion :

config.xml

<!-- ... -->
<global>
    <resources>
        <myconnection_write>
            <connection>
                <use>myconnection_database</use>
            </connection>
        </myconnection_write>
        <myconnection_read>
            <connection>
                <use>myconnection_database</use>
            </connection>
        </myconnection_read>
        <myconnection_database>
            <connection>
                <host><![CDATA[host]]></host>
                <username><![CDATA[username]]></username>
                <password><![CDATA[password]]></password>
                <dbname><![CDATA[dbname]]></dbname>
                <model>mysql4</model>
                <type>pdo_mysql</type>
                <active>1</active>
            </connection>
        </myconnection_database>
    </resources>
</global>
<!-- ... -->

Pour instancier les connexions :

Mymodel.php

/* @var $connectionWrite Varien_Db_Adapter_Pdo_Mysql */
$connectionWrite = Mage::getSingleton('core/resource')->getConnection('myconnection_write');

/* @var $connectionRead Varien_Db_Adapter_Pdo_Mysql */
$connectionRead = Mage::getSingleton('core/resource')->getConnection('myconnection_read');

Il est maintenant possible d'écrire et de lire dans notre nouvelle base en exécutant une requête quelconque :

Mymodel.php

$connectionWrite = Mage::getSingleton('core/resource')->getConnection('myconnexion_write');

$writeConnection->query("INSERT INTO `mytable` (`foo`) VALUES ('bar')");

Pour plus d'informations sur les requêtes directes, vous pouvez consulter l'article Les requêtes SQL directes dans Magento.

Etude de cas

Les cas d'utilisation d'une base externe sont multiples. La fonction principale de Magento reste d'enregistrer des commandes. Ce n'est pas un outil de gestion comptable ni un ERP. Ainsi, on peut imaginer un grand nombre d'applications autour de la solution.

Souvent les échanges de données se font par l'intermédiaire de Webservices (SOAP ou REST), ou par la génération de flux XML ou CSV. Cas de figure systématique si l'outil est géré par un prestataire externe. Mais si vous décidez de développer les applications en interne, le plus simple reste d'envoyer directement les informations en base (les avantages sont énumérés dans la conclusion).

Pour notre exemple, nous souhaitons créer une application dédiée à l'envoi de newsletters, avec possibilité de sélectionner les utilisateurs selon certains critères, par exemple les produits commandés (pour un e-mailing ciblé).

Nous allons demander à Magento d'alimenter la base de données de notre application en temps réel.

Architecture du module

  • app/code/local/Magentix/MailingApp/etc/
  • config.xml
  • app/code/local/Magentix/MailingApp/Model/
  • Mailing.php
  • Observer.php
  • app/etc/modules/
  • Magentix_MailingApp.xml

Développement du module

La première étape est de modéliser la base de données de l'application que l'on souhaite créer. Un modèle simple car c'est aussi le but de l'application. J'ai simplifié au maximum avec seulement 2 tables pour l'exemple. La première contient les e-mails et les statuts des clients, la deuxième les produits commandés. Une jointure entre ces 2 tables permet de récupérer un e-mail selon un produit.

Database

CREATE DATABASE `mailing`;

CREATE TABLE IF NOT EXISTS `mgx_emails` (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `email` varchar(255) NOT NULL DEFAULT '',
  `is_subscribe` tinyint(1) NOT NULL,
  `date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `email` (`email`)
) ENGINE=InnoDB;


CREATE TABLE IF NOT EXISTS `mgx_products` (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `email_id` int(10) NOT NULL,
  `sku` varchar(255) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`),
  UNIQUE KEY `email_id` (`email_id`,`sku`)
) ENGINE=InnoDB;

La requête :

Request

SELECT email FROM `mgx_emails` e,`mgx_products` p
WHERE e.id = p.email_id AND e.is_subscribe = 1 AND p.sku = 'XXXXX'

Nos tables doivent être alimentées lorsque :

  • Un compte client est créé
  • Le statut d'inscription à la newsletter est modifié
  • Une commande est validée

Nous avons alors recours à 3 observers, dans l'ordre :

  • customer_save_after
  • newsletter_subscriber_save_after
  • checkout_onepage_controller_success_action

On peut maintenant établir le fichier de configuration du module :

app/code/local/Magentix/MailingApp/etc/config.xml

<?xml version="1.0"?>
<config>
    <modules>
        <Magentix_MailingApp>
            <version>0.1.0</version>
        </Magentix_MailingApp>
    </modules>
    <global>
        <models>
            <mailingapp>
                <class>Magentix_MailingApp_Model</class>
            </mailingapp>
        </models>
        <resources>
            <mailing_write>
                <connection>
                    <use>mailing_database</use>
                </connection>
            </mailing_write>
            <mailing_database>
                <connection>
                    <host><![CDATA[localhost]]></host>
                    <username><![CDATA[myname]]></username>
                    <password><![CDATA[mypassword]]></password>
                    <dbname><![CDATA[mailing]]></dbname>
                    <model>mysql4</model>
                    <type>pdo_mysql</type>
                    <active>1</active>
                </connection>
            </mailing_database>
        </resources>
    </global>
    <frontend>
        <events>
            <!-- Add Customer Email -->
            <customer_save_after>
                <observers>
                    <mailing_add_email>
                        <type>singleton</type>
                        <class>mailingapp/observer</class>
                        <method>addEmail</method>
                    </mailing_add_email>
                </observers>
            </customer_save_after>
            <!-- Add Products Ordered -->
            <checkout_onepage_controller_success_action>
                <observers>
                    <mailing_add_products>
                        <type>singleton</type>
                        <class>mailingapp/observer</class>
                        <method>addProducts</method>
                    </mailing_add_products>
                </observers>
            </checkout_onepage_controller_success_action>
            <!-- Update Customer subscription status -->
            <newsletter_subscriber_save_after>
                <observers>
                    <mailing_update_email>
                        <type>singleton</type>
                        <class>mailingapp/observer</class>
                        <method>updateEmail</method>
                    </mailing_update_email>
                </observers>
            </newsletter_subscriber_save_after>
        </events>
    </frontend>
</config>

L'observer doit présenter nos 3 méthodes : addProducts, addEmail et updateEmail. Les requêtes SQL sont gérées dans la classe Magentix_MailingApp_Model_Mailing que l'on écrira par la suite.

app/code/local/Magentix/MailingApp/Model/Observer.php

<?php

class Magentix_MailingApp_Model_Observer {

    /**
     * Add Products Ordered
     * (checkout_onepage_controller_success_action)
     *
     * @param Varien_Event_Observer $observer
     */
    public function addProducts($observer) {
        /* @var $orderId Int */
        $orderId = $observer->getEvent()->getOrderIds();

        $this->_getModel()->addProducts(Mage::getModel('sales/order')->load($orderId));
    }

    /**
     * Add Customer Email 
     * (customer_save_after)
     *
     * @param Varien_Event_Observer $observer
     */
    public function addEmail($observer) {
        /* @var $customer Mage_Customer_Model_Customer */
        $customer = $observer->getEvent()->getCustomer();

        $this->_getModel()->addEmail($customer);
    }

    /**
     * Update Customer subscription status
     * (newsletter_subscriber_save_after)
     *
     * @param Varien_Event_Observer $observer
     */
    public function updateEmail($observer) {
        /* @var $subscriber Mage_Newsletter_Model_Subscriber */
        $subscriber = $observer->getEvent()->getSubscriber();

        $this->_getModel()->updateEmail($subscriber);
    }

    /**
     * Retrieve Model
     *
     * @return Magentix_MailingApp_Model_Mailing
     */
    public function _getModel() {
        return Mage::getModel('mailingapp/mailing');
    }

}

Pour les requêtes j'ai choisi d'utiliser des requêtes SQL directes, plus rapide. Nous allons étudier chacune des méthodes séparément. Commençons par ajouter la classe. Pour le moment elle ne contient qu'une méthode chargée d'instancier la connexion.

app/code/local/Magentix/MailingApp/Model/Mailing.php

<?php

class Magentix_MailingApp_Model_Mailing extends Mage_Core_Model_Abstract {

    /**
     * Retrieve Connection
     *
     * @return Varien_Db_Adapter_Pdo_Mysql
     */
    public function _getConnection() {
        return Mage::getSingleton('core/resource')->getConnection('mailing_write');
    }

}

La première méthode addEmail se charge d'ajouter l'adresse e-mail du client lorsque son compte est créé. Un compte est créé lors de la passation de commande ou depuis la page dédiée à la création du compte. Peu importe, nous récupérons dans les 2 cas un objet customer. Pour éviter les doublons j'ignore l'enregistrement si l'e-mail est déjà présent. Improbable mais on ne sait jamais.

Il nous faut aussi déterminer que le client est bien inscrit pour recevoir des newsletters : méthode isSubscribed de la classe Mage_Newsletter_Model_Subscriber.

app/code/local/Magentix/MailingApp/Model/Mailing.php

    /**
     * Add Customer Email
     *
     * @param Mage_Customer_Model_Customer $customer
     * @return bool
     */
    public function addEmail(Mage_Customer_Model_Customer $customer) {
        $connection = $this->_getConnection();

        $connection->query("INSERT IGNORE INTO `mgx_emails` (`email`,`is_subscribe`)
                            VALUES ('".$customer->getEmail()."','".$this->isSubscribed($customer->getEmail())."')");

        return $connection->lastInsertId() ? true : false;
    }

    /**
     * Retrieve customer subscription status
     *
     * @param string $email
     * @return bool
     */
    public function isSubscribed(string $email) {
        return Mage::getModel('newsletter/subscriber')->loadByEmail($email)->isSubscribed();
    }

La deuxième méthode updateEmail met à jour le statut d'inscription lorsque celui-ci est modifié par le client (depuis son compte par exemple). Rien de très complexe :

app/code/local/Magentix/MailingApp/Model/Mailing.php

    /**
     * Update Email Status
     *
     * @param Mage_Newsletter_Model_Subscriber $subscriber
     * @return bool
     */
    public function updateEmail(Mage_Newsletter_Model_Subscriber $subscriber) {
        $connection = $this->_getConnection();

        $update = $connection->query("UPDATE `mgx_emails`
                            SET `is_subscribe` = '".$subscriber->isSubscribed()."'
                            WHERE email = '".$subscriber->getEmail()."'");

        return $update->rowCount() ? true : false;
    }

La dernière méthode addProducts est utilisée après que la commande ait été validée. Elle associe les produits commandés à l'e-mail du compte client. Avant d'insérer les lignes produits je récupère l'identifiant de l'e-mail. Si l'e-mail n'existe pas je n'ajoute rien (commande sans création de compte par exemple).

app/code/local/Magentix/MailingApp/Model/Mailing.php

    /**
     * Add Products Ordered
     *
     * @param Mage_Sales_Model_Order $order
     * @return bool
     */
    public function addProducts(Mage_Sales_Model_Order $order) {
        $connection = $this->_getConnection();

        $email = $connection->fetchRow("SELECT IF(COUNT(*) > 0,true,false) AS exist,`id`
                                        FROM `mgx_emails`
                                        WHERE `email` = '".$order->getCustomerEmail()."'");

        if($email['exist']) {
            $values = array();
            foreach($order->getAllItems() as $item) $values[] = "('".$email['id']."','".$item->getSku()."')";
            if(count($values)) $connection->query("INSERT IGNORE INTO `mgx_products` (`email_id`,`sku`)
                                                   VALUES ".implode(',',$values));

            return true;
        }

        return false;
    }

On active enfin le module :

app/etc/modules/Magentix_MailingApp.xml

<?xml version="1.0"?>
<config>
    <modules>
        <Magentix_MailingApp>
            <active>true</active>
            <codePool>local</codePool>
            <depends>
                <Mage_Sales />
                <Mage_Customer />
                <Mage_Newsletter />
            </depends>
        </Magentix_MailingApp>
    </modules>
</config>

Voilà, il ne reste plus qu'à développer l'application de gestion des newsletters ciblées ^^. Pour le lien de désinscription je recommande un contrôleur qui viendra mettre à jour le statut du client dans Magento. La modification sera automatiquement répercutée sur la base de données de l'application.

Mailing App Database

Conclusion

Cette application est un exemple simpliste, mais démontre que l'utilisation d'une base externe pour l'analyse des données a beaucoup d'avantages :

  • Je ne consomme pas de ressources supplémentaires sur le site principal avec des requêtes souvent lourdes
  • Je peux créer un modèle de base de données simple en m'affranchissant des contraintes liées au modèle EAV
  • Je peux développer une application dans n'importe quelle langage, avec le framework de mon choix
  • Je peux communiquer en temps réel (pas de flux à générer et à envoyer régulièrement)
commentaires

Commentez cet article : Configurer et utiliser plusieurs bases de données