Version 1.0.0 - 2006/01/31

8.4 Mapping objet / relationnel

L’un des points fort de Noas PHP est d’avoir une couche d’abstraction qui supporte nativement le mapping objet / relationnel. Comprenez par-là que vous n’avez pas de module supplémentaire à ajouter pour faire des sauvegardes, des récupérations, des suppressions d’objets. Ces actions sont naturelles pour le framework. N’importe quel objet peut être manipulé, à condition que vous respectiez les conventions pour les attributs accessibles. Cela présente plusieurs avantage, vous n’êtes pas obligé de surchanger vos objets métier et vous pouvez utiliser des classes qui n’ont pas été conçu pour Noas PHP. Par contre vous devez spécifier, soit par programmation, soit par déclaration, la façon dont les échanges sont effectués.

Les contrôles

Les contrôles sont des éléments primordiaux pour les échanges. Les informations qu’ils portent permettent au framework de connaître avec précision les actions à effectuer pour réaliser une opération en base de données. Le principe est simple, souple et efficace :
Pour chaque propriété d’une classe que vous souhaitez synchroniser, vous devez lui associer le contrôle adéquat avec les bons paramètres. Il en existe plusieurs, qui ont chacun leur objectif. Les informations minimales dont ils ont besoins sont le nom de la propriété, le nom de la colonne de la table, la nullité possible, l’autorisation d’écrire et l’autorisation de lire. Les autres sont purement spécifiques à l’utilisation destinée.
Les contrôles sont le plus souvent utilisez par les connexions et l’interface ( formulaire et validation des champs). Nous verrons plus loin comment le développeur doit s’en servir.
Pour le moment garder à l’esprit que pour chaque attribut mappé en base de données, vous devez définir un contrôle.
Si la gamme de contrôle fourni ne vous suffit pas, vous pouvez toujours, avec de l’expérience, développer les votre.
Notons que PHP n’est pas un langage strictement typé alors que la politique de typage des SGDB est assez rigoureuse. Ainsi pour définir vos contrôles vous devez donc être plus précis, d’autant plus que le framework s’en sert pour faire les conversions de données. Les contrôles de types NoasTimestampControl sont intraitables à ce sujet.

Les proxys

Nous y voilà, le moment tend attendu où nous allons comprendre comment utiliser le mapping en Noas PHP. Nous avons dit précédemment que vos objets métiers n’avaient pas besoin d’être customiser, pour être manipulé. Cette commodité est possible parce que d’autres classes, propre au framework, effectuent le travail à leur place. Toute classe d’objet métier possède une classe associée appelée proxy. Ce n’est pas une relation bijective, un proxy peut être associé à plusieurs classes d’objet métier. Pour associé un proxy à une classe, vous devez effectuer un référencement qui ne se fait pas automatiquement. Vous avez deux manières de procéder. La première consiste à définir toutes les références à l’initialisation de l’application. Cette méthode, dite référencement dynamique, à l’avantage de localiser toutes les instructions. Vous n’avez besoin d’intervenir qu’à un seul endroit. Par contre, cette manière n’est pas optimisée. En effet, si vous travaillez sur de gros projets, vous risquerez de référencer trop de proxy inutilisé.

Référencement dynamique

<?php
..
class BridgeBackOffice extends NoasApplicationContext {

  public function initialize(){
    parent::initialize();
    NoasExchangeManager::setProxy("BridgeBoxContent"
                                                              , "BridgeBoxContentProxy");
    NoasExchangeManager::setProxy("BridgeBoxResource"
                                                             , "BridgeBoxResourceProxy");
    NoasExchangeManager::setProxy("BridgeLink"
                                                                          , "BridgeLinkProxy");
    NoasExchangeManager::setProxy("BridgeEmail"
                                                                        , "BridgeEmailProxy");
   
    NoasExchangeManager::setProxy("BridgeUserProfile"
                                                               , "BridgeUserProfileProxy");
   
    NoasExchangeManager::setProxy("BridgeHelp"
                                                                          , "BridgeHelpProxy");
    NoasExchangeManager::setProxy("Bridge", "BridgeProxy");
  }

}
?>
La deuxième méthode, dite référencement statique, consiste à référencer les proxy seulement lorsqu’on en a besoin. Dans chaque fichier de proxy vous devez placer une instruction statique pour le référencer. De cette façon, seul les proxy utilisés seront chargés. L’inconvénient est que vous créez une bijection entre proxy et objet métier. Mais dans la plus part des cas elle n’est contraignante. Rien ne vous empêche d’utiliser les deux méthodes simultanément, selon vos besoins spécifiques.
Attention, le fait référencer un proxy ne vous dispense pas de charger sa définition. Ce dernier est de votre responsabilité. Vous devez toujours faire ne sorte que la définition du proxy soit présente lors de la manipulation des objets associés. Dans un schéma complexe, vous pouvez vite être gêné par l’ordre de chargement qui peut conduire à des références cycliques. La meilleure façon de procéder est de toujours importer la définition de la classe métier dans celle du proxy. Puis importer toujours celle du proxy lorsque vous voulez utiliser la classe métier.

Référencement statique

<?php

class BridgeBoxProxy extends NoasExchangeProxy {
..
}
NoasExchangeManager::setProxy("BridgeBox","BridgeBoxProxy");
?>
Ne perdons de pas de vu que le premier rôle du proxy est de décrire la manière dont l’échange va se produire. Peu importe la façon dont elle est construite, la liste des contrôles que le proxy géré doit être disponible. Lorsque la plate-forme invoque la méthode getProperties() du proxy, il s’attend à obtenir la liste complète des contrôles.

Les habitués du mapping savent que les problèmes commencent lorsqu’il faut gérer des agrégats, des références ou des compositions. Heureusement, les proxy ont également été conçu pour traiter ces cas particuliers. Les proxy possède des méthodes événementielles qui vous permet de traquer tous les mouvements en base de données et ainsi effectuer les traitements nécessaires.

Proxy d'objet métier

<?php
Noas::import("noas.exchange.NoasExchangeProxy");
Noas::import("bridge.businessobject.BridgeEmail");
class BridgeEmailProxy extends NoasExchangeProxy {
  function __construct() {
     parent :: __construct("bridge_email", "BRIDGE_EMAIL");
  }
  public function createInstance() {
    return new BridgeEmail();
  }
  public function getExchangeKey($exchangable) {
   $key = new NoasExchangeKey("BridgeEmail");
   $key->setExchangeValue("EMAIL_ID"
                                                        , $exchangable->getEmailId());
    return $key;
  }
  public function setExchangeKey(NoasExchangeKey $key
                                                                                   , $exchangable) {
    $exchangable->setEmailId(
                                            $key->getExchangeValue("EMAIL_ID"));
   
  }
  protected function createProperties() {
    $proterties = array();
    $proterties["emailId"] = new NoasIntegerControl("EMAIL_ID"
   , "BRIDGE_EMAIL_EMAIL_ID", "emailId", 10, true, true, true
                                                                                                   , true);
    $proterties["code"] = new NoasStringControl("CODE"
                , "BRIDGE_EMAIL_CODE", "code", 100, true, true, true);
    $proterties["label"] = new NoasStringControl("LABEL"
             , "BRIDGE_EMAIL_LABEL", "label", 100, true, true, true);
    $proterties["description"] =NoasStringControl("DESCRIPTION"
, "BRIDGE_EMAIL_DESCRIPTION", "description", 1064, false
                                                                                          , true, true);
   $proterties["dateMod"] =
      new NoasTimestampControl("DATE_MOD"
      , "BRIDGE_EMAIL_DATE_MOD", "dateMod", true, true, true);
   return $proterties;
  }
  protected function createOrder() {
    $order = array ();
    $order[] = "CODE";
    $order[] = "EMAIL";
    return $order;
  }
  public function onCreate(NoasExchangeContext $context
                                                                                  , $exchangable) {
    $oUser = NoasApplicationContext::instance()->getSession()->getRemoteUser();
    $exchangable->setEmailId(
                  $context->getSequence("EMAIL_ID")->getNextValue());
    $exchangable->setDateMod(NoasTimestamp::getTimestamp());
    $exchangable->setUserModId($oUser->getUserId());
  }
  public function onSave(NoasExchangeContext $context
                                                                                   , $exchangable) {
    $oUser = NoasApplicationContext::instance()->getSession()->getRemoteUser();
    $exchangable->setDateMod(NoasTimestamp::getTimestamp());
    $exchangable->setUserModId($oUser->getUserId());
  }
  public function onSelectForUpdate(NoasExchangeContext              $context, $exchangable) {
    $oUser = NoasApplicationContext::instance()->getSession()->getRemoteUser();
    $exchangable->setDateMod(NoasTimestamp::getTimestamp());
    $exchangable->setUserModId($oUser->getUserId());
  }   
}
?>

Fichier de description XML

Utilisant le mapping  comme nous l’avons fait précédemment, vous trouverez vite la tache fastidieuse. C’est pour cette raison, et également pour se rapprocher des solutions professionnelles des autres langages, Noas PHP vous propose une assistance. Elle se traduit par une implémentation de NoasExchangeProxy qui se configure à partir de fichier XML.

Assistance au mapping

<?php
Noas::import("noas.exchange.NoasExchangeProxyWizard");
Noas::import("exemple.ExchangeDummy");

class ExchangeDummyProxy extends NoasExchangeProxyWizard  {
  function __construct() { parent::__construct(
 dirname(__FILE__).NOAS_SEPARATOR."ExchangeDummy.xml");
  }
  public function onCreate(NoasExchangeContext $context
                                                                                    , $exchangable) {
    parent::onCreate($context, $exchangable);   
$exchangable->setETimestamp(NoasTimestamp::getTimestamp());
  } 
  public function onSelectForUpdate(NoasExchangeContext $context, $exchangable) {
    parent::onSelectForUpdate($context, $exchangable);
$exchangable->setETimestamp(NoasTimestamp::getTimestamp())
  } 
}
NoasExchangeManager::setProxy("ExchangeDummy"
                                                                  ,"ExchangeDummyProxy");
?>
Non seulement l’économie de ligne est effective, mais aussi vous pouvez éditer vos fichiers avec un éditeur XML pour gagnez en productivité. Cette solution est plus simple, contrairement à l’autre méthode, vous n’avez pas besoin de maîtriser le fonctionnement interne des proxy pour réussir votre mapping. Il devient intuitif.

Description de mapping par XML

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE exchange-description PUBLIC "-//Noas PHP, The Framework.//DTD Exchange Description 1.0//FR" "http://noas.sourceforge.net/dtds/noas-exchange_1_0.dtd">
<exchange-description>
   <entity>ExchangeDummy</entity>
   <exchange>EXCHANGE_DUMMY</exchange>
   <code>EXCHANGE_DUMMY</code>
   <lazy>true</lazy>
   <desc>false</desc>
   <field>
      <type>INTEGER</type>
      <property>exchangeDummyId</property>
      <exchange>EXCHANGE_DUMMY_ID</exchange>
      <size>10</size>
      <notnull>true</notnull>
      <readonly>true</readonly>
      <writeonly>true</writeonly>
      <primary>true</primary>
      <sequence>EXCHANGE_DUMMY_SEQ</sequence>
   </field>
   <field>
      <type>INTEGER</type>
      <property>eInteger</property>
      <exchange>E_INTEGER</exchange>
      <size>10</size>
      <notnull>true</notnull>
      <readonly>true</readonly>
      <writeonly>true</writeonly>
   </field>
   <field>
      <type>NUMERIC</type>
      <property>eNumeric</property>
      <exchange>E_NUMERIC</exchange>
      <size>10</size>
      <precision>0</precision>
      <notnull>true</notnull>
      <readonly>true</readonly>
      <writeonly>true</writeonly>
   </field>
   <field>
      <type>STRING</type>
      <property>eString</property>
      <exchange>E_STRING</exchange>
      <size>100</size>
      <notnull>true</notnull>
      <readonly>true</readonly>
      <writeonly>true</writeonly>
   </field>
   <field>
      <type>STRING</type>
      <property>eEmail</property>
      <exchange>E_EMAIL</exchange>
      <size>50</size>
      <notnull>true</notnull>
      <readonly>true</readonly>
      <writeonly>true</writeonly>
   </field>
   <field>
      <type>BOOLEAN</type>
      <property>eBoolean</property>
      <exchange>E_BOOLEAN</exchange>
      <readonly>true</readonly>
      <writeonly>true</writeonly>
   </field>
   <field>
      <type>TIMESTAMP</type>
      <property>eTimestamp</property>
      <exchange>E_TIMESTAMP</exchange>
      <notnull>true</notnull>
      <readonly>true</readonly>
      <writeonly>true</writeonly>
   </field>
   <field>
      <type>INTEGER</type>
      <property>friendId</property>
      <exchange>FRIEND_ID</exchange>
      <size>10</size>
      <notnull>false</notnull>
      <readonly>true</readonly>
      <writeonly>true</writeonly>
   </field>
   <field>
      <type>INTEGER</type>
      <property>parentId</property>
      <exchange>PARENT_ID</exchange>
      <size>10</size>
      <notnull>false</notnull>
      <readonly>true</readonly>
      <writeonly>true</writeonly>
   </field>
   <field>
      <type>VERSION</type>
      <property>version</property>
      <exchange>VERSION</exchange>
      <size>10</size>
      <notnull>true</notnull>
      <readonly>true</readonly>
      <writeonly>true</writeonly>
   </field>
   <order>
      <exchange>EXCHANGE_DUMMY_ID</exchange>
      <exchange>E_STRING</exchange>
   </order>
   <reference>
      <property>friend</property>
      <entity>ExchangeDummy</entity>
      <foreingkey>
         <property>friendId</property>
         <fkproperty>exchangeDummyId</fkproperty>
      </foreingkey>
   </reference>
   <aggregate>
      <property>child</property>
      <entity>ExchangeDummy</entity>
      <foreingkey>
         <property>exchangeDummyId</property>
         <fkproperty>parentID</fkproperty>
      </foreingkey>
   </aggregate>
</exchange-description>

 

© 2005 Réoka Djohou, tous droits réservés.