Compte unifié Drupal et Magento
Nous avons une application Drupal et une application Magento. Nous voulons que les deux applications aient le mĂȘme compte utilisateur. L'utilisation d'une solution de SSO est proscrite.
Toutes les actions de connexion et de déconnexion, ainsi que de création et de modification des utilisateurs, se feront depuis l'application Drupal.
L'essentiel des informations détaillées données ici sont pour le cÎté Magento. Seules les grandes lignes sont esquissées pour Drupal.
Prérequis
Les deux applications doivent partager le mĂȘme nom de domaine. Elles peuvent ĂȘtre, en revanche, sous deux sous-domaines diffĂ©rents (Magento.mon-domaine.com et Drupal.mon-domaine.com dans les exemples suivants).
Les deux applications doivent pouvoir rĂ©cupĂ©rer le contenu de la session de l'autre application. Ce qui signifie qu'il faut mettre mettre en place une architecture en consĂ©quence : si les deux sont sur le mĂȘme serveur, ça fonctionnera, je vous conseille de vĂ©rifier par le simple test suivant que c'est le cas.
Test du partage de la session
Créez deux virtual hosts test1.mon-domaine.com et test2.mon-domaine.com pointant vers deux répertoires contenant les fichiers test1.php et test2.php respectivement.
<?php // test1.php session_start(); $_SESSION['test1'] = "successfull"; ?> <html> <body> <p><?=session_id()?></p> <iframe src="http://test2.mon-domaine.com/test2.php?sessionId=<?=session_id()?>"> </iframe> </body> </html
<?php // test2.php session_start(); echo "Initial session id : ".session_id()."<br/>"; session_write_close(); session_id($_GET['sessionId']); session_start(); echo "Changed for session id : ".session_id()."<br/>"; echo "Test : " . $_SESSION['test'];
Allez sur http://test1.mon-domaine.com/test1.php et normalement vous devriez voir les deux numéros de session et Test : successfull.
Vous devrez sans doute définir le X-Frame-Options. Pour l'éviter, vous pouvez noter le session_id() renvoyé par test1.php, et appeler directement l'url http://test2.mon-domaine.com/test2.php?sessionId=<sessionIdRelevée>
Synchroniser les comptes
Préparation : API SOAP de Magento
Dans le backend Magento :
Créez un nouveau rÎle SOAP nommé 'Drupal', et donnez-lui l'accÚs aux resources "Clients" ("Customers").
SystĂšme > Services web > SOAP/XML-RPC - RĂŽles > Nouveau rĂŽle
Infos du rĂŽle > Nom du rĂŽle : Drupal
Ressources du rÎle > AccÚs aux ressources : Personnalisé + Ressources : Clients
Créez un nouvel utilisateur SOAP, et affectez-lui le rÎle 'Drupal' nouvellement créé.
SystĂšme > Services web > SOAP/XML-RPC - Utilisateurs > Nouvel utilisateur
Infos de l'utilisateur > Nom d'utilisateur : Drupal (pour le reste, complétez)
RĂŽle de l'utilisateur > Cochez le rĂŽle 'Drupal'
Une fois ces préparations faites, nous pouvons intercepter les créations, modifications et suppression de compte cÎté Drupal.
Utilisation de l'API SOAP
Pour utiliser l'API SOAP de Magento, nous créons et initialisons un object SoapClient.
<?php $soap_url = "http://Magento.mon-domaine.com/index/api/soap?wsdl=1"; $proxy = new SoapClient($soap_url); $session = $proxy->login('Drupal', 'votre-cle-api');
Nous utilisons ici la V1 de l'API SOAP, le code peut ĂȘtre facilement mis Ă jour pour passer sur la V2.
Création du compte
Une fois le compte créé sur Drupal, nous utilisons l'API SOAP de Magento pour ajouter l'utilisteur à Magento :
<?php $newCustomer = array( // fill with firstname, lastname, email, // password_hash, store_id (=0 by default), website_id (=0 by default) ); $newCustomerId = $proxy->call($session, 'customer.create', array($newCustomer)); echo "User created : {$newCustomerId}\n";
Le numéro d'utilisateur est l'identifiant Magento. Conservez-le dans Drupal. Nous y référerons à présent comme $magento_id.
Modification du compte
De mĂȘme lors d'une modification du compte :
<?php $updatedCustomer = array( // fill with firstname, lastname etc... // the values in this array will be updated, the others are kept unchanged ); $hasUpdatedCustomer = $proxy->call($session, 'customer.update', array($MagentoId, $updatedCustomer)); if ($hasUpdatedCustomer) { echo "Updated successfull\n"; } else { echo "Error during update\n"; }
Suppression du compte
<?php $hasDeletedCustomer = $proxy->call($session, 'customer.delete', array($MagentoId)); if ($hasDeletedCustomer) { echo "Delete successfull\n"; } else { echo "Error during delete\n"; }
Et les adresses ?
Vous pouvez gérer également les adresses en utilisant :
<?php $proxy->call($session, 'customer_address.create', $newCustomerAddress);
Et équivalents pour la mise à jour et la suppression.
Les comptes sont maintenant synchronisés (du moment que toutes les modifications sont faites depuis Drupal). Nous allons maintenant nous occuper de la connexion partagée.
Connexion partagée
Jouer avec les sessions et les cookies
L'idée est relativement simple : quand Drupal se connecte, il envoie son identifiant de session et l'utilisateur connecté à Magento, qui se connecte et ajoute les informations de sa session à celle de Drupal.
AprÚs on joue avec les cookies pour faire passer des données (comme les identifiants de session Drupal) entre les deux applications.
Se partager les cookies
Les deux applications doivent pouvoir s'échanger des cookies :
DrupalSession contient l'identifiant de session Drupal si l'utilisateur est connecté, il n'existe pas sinon
MagentoSession contient l'identifiant de session Magento.
Pour cela, si les applications ne sont pas sous le mĂȘme sous-domaine (magento.mon-domaine.com et drupal.mon-domaine.com), il faut qu'ils puissent s'Ă©changer les cookies. Il faut donc dĂ©finir la valeur 'session.cookie_domaine', directement dans php.ini, ou au dĂ©but du code avec
<?php ini_set('session.cookie_domain', '.mon-domaine.com');
Les cookies seront donc créés de cette maniÚre :
<?php setcookie('DrupalSession', session_id(), null, null, '.mon-domaine.com');
Voici les rĂšgles Ă mettre en oeuvre :
au chargement de toute page Drupal, si l'utilisteur est dĂ©connectĂ©, supprimer le cookie DrupalSession, le crĂ©er sinon, avec comme valeur session_id(), et la mĂȘme durĂ©e que la durĂ©e de connexion de Drupal.
au chargement de toute page Magento, intercepter l'évÚnement controller_action_predispatch pour créer le cookie MagentoSession, et récupérer la session Drupal. (cf partie suivante)
Récupérer la session Drupal sous Magento
Tout d'abord, ajoutez dans le fichier config.xml de votre module (que j'appelerai ici My_Module) les lignes permettant l'interception de l'évÚnement :
<global> <events> <controller_action_predispatch> <observers> <my_predispatch_event> <type>singleton</type> <class>My_Module_Model_Observer</class> <method>controllerPredispatch</method> </my_predispatch_event> </observers> </controller_action_predispatch> </events> </global>
Puis créez la classe My_Module_Model_Observer, et dedans la méthode controllerPredispatch :
<?php public function controllerPredispatch(Varien_Event_Observer $observer) { $action = $observer->getEvent()->getControllerAction(); $actionName = $action->getFullActionName(); if (strpos($actionName, "adminhtml") === false) { // avoiding admin pages ... terrible code ... $sessionId = getcookie('DrupalSession'); if ($sessionId && ($sessionId != session_id())) { // writing the new session id in cookies. setcookie('PHPSESSID', $sessionId, 0, '/', 'Magento.mon-domaine.com'); setcookie('frontend', $sessionId, 0, '/', 'Magento.mon-domaine.com'); // loading the new session. session_write_close(); session_id($sessionId); session_start(); // reloading the page, for Magento to load all with the Drupal session Mage::app()->getResponse() ->setRedirect($this->curPageURL()) ->sendResponse(); exit(); } // if user is logged in Magento, but not Drupal, logout him. $customerSession = Mage::getSingleton('customer/session'); if ($customerSession->isLoggedIn()) { // check cookie if (!$sessionId) { $customerSession->logout(); Mage::ap()->getResponse() ->setRedirect($this->curPageURL()) ->sendResponse(); exit(); } } // finally add the Magento session cookie. setcookie('MagentoSession', session_id(), null, '/', '.mon-domaine.com'); } }
Le code pour avoir l'url de la page courante :
<?php protected function curPageURL() { $pageURL = 'http'; if ($_SERVER["HTTPS"] == "on") {$pageURL .= "s";} $pageURL .= "://"; if ($_SERVER["SERVER_PORT"] != "80") { $pageURL .= $_SERVER["SERVER_NAME"].":".$_SERVER["SERVER_PORT"].$_SERVER["REQUEST_URI"]; } else { $pageURL .= $_SERVER["SERVER_NAME"].$_SERVER["REQUEST_URI"]; } return $pageURL; }
Ok, nous avons maintenant la session Drupal récupérée par Magento quand Drupal est connecté. Mais nous ne connectons jamais Magento (puisque nous avons dit que seul Drupal se connecte). Il faut donc à nouveau faire appel à une API de Magento, que nous allons créer, pour fusionner les sessions.
Fusionner les sessions Magento et Drupal
Créer l'API SOAP
Contrairement à celle permettant de gérer les utilisateurs, l'API SOAP pour gérer notre login trÚs spécial n'existe pas. Il va donc falloir la créer.
Créez dans le dossier etc de votre module, le fichier api.xml :
<?xml version="1.0"?> <config> <api> <resources> <customerlogin translate="title" module="my_module"> <model>My_Module_Model_Customerlogin_Api</model> <title> Customer login resource </title> <acl>customerlogin</acl> <methods> <create translate="title" module="my_module"> <title> Create customer login </title> <acl>customerlogin/create</acl> </create> </methods> <faults module="my_module"> <invalid_id> <code>400</code> <message>Invalid id</message> </faults> </customerlogin> </resources> <acl> <resources> <customerlogin translate="title" module="my_module"> <title>Customer login</title> <create translate="title" module="my_module"> <title>Login</title> </create> </customerlogin> </resources> </acl> </api> </config>
Puis créez la classe :
<?php class Focal_Main_Model_Customerlogin_Api extends Mage_Api_Model_Resource_Abstract { public function create($MagentoUserId, $DrupalSessionId, $MagentoSessionId) { // todo }
Pensez maintenant à ajouter cette API aux ressources autorisées pour l'utilisateur SOAP Drupal.
Fusionner les sessions
<?php public function create($MagentoUserId, $DrupalSessionId, $MagentoSessionId) { $customerCollection = $this->getCustomerModel()->getCollection() ->addAttributeToSelect('id') ->addAttributeToFilter('id', $MagentoUserId)->load(); if ($customerCollection->count() == 0) { $this->_fault('invalid id', 'no user with this id'); } foreach($customerCollection as $customer) { break; } // set identity // we must use here the session loaded with Magento SOAP API // we can't use the Magento session id, refering to the browser session $customerSession = Mage::getSingleton("customer/session"); $customerSession->setCustomerAsLoggedIn($customer); $customerSession->setUsername($customer->username); $sessionCustomer = $_SESSION['customer']; if ($MagentoSessionId) { // use the session linked to browser to have all carts and others informations session_write_close(); session_id($MagentoSessionId); session_start(); } $sessionCore = $_SESSION['core']; $sessionCheckout = $_SESSION['checkout']; // the open the Drupal session to put in all the Magento informations session_write_close(); session_id($DrupalSessionId); session_start(); $_SESSION['core'] = $sessionCore; $_SESSION['customer'] = $sessionCustomer; $_SESSION['checkout'] = $sessionCheckout; return true; }
Il ne reste plus qu'à appeler l'API SOAP depuis Drupal à la connexion et le tour est joué :
<?php $resultLogin = $proxy->call($session, 'customerlogin.create', array($MagentoUserId, $DrupalSessionId, $MagentoSessionId));
Finaliser tout cela
Il faut encore nettoyer Magento de tout ses formulaires de connexion pour remplacer par des liens vers le formulaire de Drupal. Et aussi de rediriger aprĂšs la connexion vers la page correspondante de Magento.
On pourrait également inclure via iframe le formulaire de login de Drupal dans Magento, pour éviter une étape supplémentaire, peu efficace en terme d'expérience client.














