Noas php - mise en ?uvre

Noas php - mise en oeuvre

Installation

Noas PHP n'est pas une extension PHP, ni un module Apache, ou autres composants « exécutables ». Il s'agit purement et simplement d'ensemble de classes et d'un fichier de configuration. Procurez vous l'archive en prenant soin de choisir celle qui correspond à l'extension des scripts PHP5 reconnue par votre serveur (.php ou .php5 ou utilisez un logiciel pour renommer tous les fichiers à votre convenance ). Décompressez le contenu de l'archive dans un répertoire neutre, que nous appellerons « NOAS_INSTALL ». Il ne vous reste plus qu'à définir correctement les constantes du fichier de configuration « noas-server-conf.inc ».

Définissez la constante « NOAS_SEPARATOR » à « \\ » si vous développez sous Windows ou à « / » pour Linux.

Définissez « NOAS_HOME » à « NOAS_INSTALL ».

Définissez « NOAS_WORK_DIR » en spécifiant le répertoire dans lequel vous souhaitez que le framework génère ses fichiers temporaires. Veuillez à ce que PHP ai le droit d'y créer des dossiers et des fichiers.

Définissez « NOAS_LOG_DIR » en spécifiant le répertoire dans lequel vous souhaitez que le framework génère les fichiers de logs. Veuillez à ce que PHP ai le droit d'y créer des dossiers et des fichiers.

Définissez « NOAS_PHP_EXTENSION » en spécifiant l'extension PHP5 reconnue par votre serveur (.php , .php5 ou autres ).

La configuration étant terminée, nous pouvons débuter la découverte du framework au travers d'un petit projet que nous appellerons EMC (Entreprise Message Center), une application de dépôt et de consultation de messages centralisés.

Classes et Packages

En PHP5, vous pouvez placer vos définitions de classes à peu prés n'importe où. Par contre, vous devez faire vous-même l'effort de les retrouver avec le risque de produire une application non portable. Noas PHP a donc ajouté les notions de chemin de classes et packages qui permettent d'importer une définition de classe aisément en faisant abstraction de sa location physique. Un chemin de classes est une liste de répertoires, séparée par des « ; », qui va servir de base pour la recherche des définitions. Le répertoire « NOAS_HOME »/classes et « MA_WEB_APP »/NOAS-INF/classes sont toujours inclus par défaut, vous n'avez pas besoin de les spécifier. Comme nous le verrons plus loin, cette valeur est définissable pour chaque application.

Un package représente une arborescence de dossiers que l'on exprime par la liste des dossiers séparés par un « . ». En utilisant les packages pour organiser vos classes, vous obtiendrez des projets plus limpides, donc plus facile à maintenir. La base d'un package valide est toujours un répertoire du chemin de classes.

Le fichier d'implémentation d'une classe doit suivre une nomenclature particulière afin d'être retrouvée : « MA_CLASSE ».class« EXTENSION_PHP »

Une fois implémentée dans le bon fichier, accessoirement le bon package, il vous suffit d'importer sa définition en utilisant son nom complet comme ci-dessous.

Noas::import("noas.core.NoasUserRequest");

Dans ce cas la classe NoasUserRequest se trouve dans le fichier « NOAS_HOME »/classes/noas/core/NoasUserRequest.class.php
Nous vous rappelons que les classes du package « noas.type » n'ont pas besoin d'être importées. Elles sont chargées en même temps que le framework.

Nouveau projet

Exceptionnellement, le temps que vous vous familiarisiez avec le framework, nous vous proposons d'utiliser le squelette type, « noasphp-blank.zip », disponible uniquement sur votre CD. Il contient des dossiers « standards » comme « images », « css », etc. et le dossier obligatoire « NOAS-INF » pré-structuré selon les spécifications en vigueur imposées par le framework. Décompressez le contenu de l'archive dans le répertoire « emc » sur votre serveur.

Intéressons-nous à la configuration de l'application « noas-blank-conf.inc ». Renommez le en « noas-emc-conf.inc ». Bien que son nom et son emplacement soient arbitraires, vous avez tout intérêt à définir votre propre nomenclature afin de pouvoir le distinguer et le retrouver facilement.

Définissez la constante « NOAS_APPLICATION_HOME » qui correspond au répertoire de votre application.

Définissez la constante « NOAS_APPLICATION_HTTP » qui correspond à l'adresse de votre application.

Définissez la constante « NOAS_APPLICATION_GROUP », qui correspond au groupe de votre application, à « EMC_GROUP ». Nous rappelons que les applications Noas PHP peuvent se grouper et disposer de données communes (langue, format, préférence utilisateur, etc.).

Définissez la constante « NOAS_APPLICATION_NAME », qui correspond au nom de votre application, à « EMC ».

Définissez la constante « NOAS_APPLICATION_CLASS », qui correspond au nom complet de la classe de votre application, à « emc.env.EmcApplicationContext ».

Vous devez nécessairement inclure le fichier de configuration du framework comme dans l'exemple ci-dessous.

<?php
//////////////////////////////////////////////////////////////////////
//  File : noas-emc-conf.inc
/////////////////////////////////////////////////////////////////////
include_once("/dev/noasphp1.0.0/noas-server-conf.inc");
define('NOAS_APPLICATION_HOME', '/dev/www/emc');
define('NOAS_APPLICATION_HTTP', 'http://localhost/emc/');
define('NOAS_CLASS_PATH','');
define('NOAS_APPLICATION_NAME', 'EMC');
define('NOAS_APPLICATION_GROUP', 'EMC_GROUP');
define('NOAS_APPLICATION_CLASS', 'emc.env.EmcApplicationContext');
define('NOAS_APPLICATION_LOCAL', 'fr');
define('NOAS_APPLICATION_DATE_FORMAT', 'd/m/Y H:i:s');
define('NOAS_APPLICATION_TEMP', '/dev/www/emc/tmp');
define('NOAS_APPLICATION_TIMEOUT', 60 * 60);
define('NOAS_TRACE_LEVEL', 100);
define('NOAS_LOG_FILE_PATTERN', '&host/&name/&ip_&d-&m-&y.txt');
?>

Environnement d'exécution

L'environnement d'exécution est l'ensemble des classes définissant votre application. Il est constitué de classes nécessaires et suffisantes pour que votre application se charge sans erreur. De manière générale, comme dans notre cas, il s'agit des classes de session, d'interface et d'application.

Nous allons toutes les placer dans le package « emc.env » de notre application.

La classe « emc.env.EmcSession » va modéliser notre session utilisateur.

<?php
//////////////////////////////////////////////////////////////////////
//  File :    EmcSession.class.php
/////////////////////////////////////////////////////////////////////
Noas::import("noas.core.NoasUserSession");

class EmcSession extends NoasUserSession {
  function __construct() {
    parent::__construct();
  }

  public function initialize() {
    parent::initialize();
  }
}
?>

La classe « emc.env.EmcInterface » va modéliser notre interface utilisateur. Choisissez le mode « debug » pour le temps du développement. Vous passerez en mode « release » une fois le projet achevé.

//////////////////////////////////////////////////////////////////////
//  File :    EmcInterface.class.php
/////////////////////////////////////////////////////////////////////
Noas::import("noas.core.NoasUserInterface");

class EmcInterface extends NoasUserInterface {

  function __construct() {
    parent::__construct(self::debug);
  }
}

Il ne vous reste plus qu'à implémenter la classe « emc.env.EmcApplicationContext » pour modéliser l'application.

<?php
//////////////////////////////////////////////////////////////////////
//  File :    EmcApplicationContext.class.php
/////////////////////////////////////////////////////////////////////
Noas::import("noas.core.NoasUserRequest");
Noas::import("noas.core.NoasApplicationContext");
 
Noas::import("emc.env.EmcSession");
Noas::import("emc.env.EmcInterface");
 
class EmcApplicationContext extends NoasApplicationContext {

  function __construct() {
    parent::__construct("php/error".NOAS_PHP_EXTENSION);
  }

  protected function createSession(){
    return new EmcSession();
  }

  protected function createInterface(){
    return new EmcInterface();
  }

  protected function createRequest(){
    return new NoasUserRequest();
  }

  public function initialize(){
    parent::initialize();
  }
}
?>

Vous devez impérativement définir une page d'erreur. Elle sera affichée chaque fois qu'une exception n'a pas été capturée. Libre à vous de renvoyer une erreur HTTP standard, comme ci-dessous ou de l'adapter à votre guise.

<?php
//////////////////////////////////////////////////////////////////////
//  File :    error.php
/////////////////////////////////////////////////////////////////////
header("HTTP/1.0 500 Internal Server Error");
?>

Notez que nous utilisons toujours la constante « NOAS_PHP_EXTENSION » à la place de l'extension reconnue par l'interpréteur PHP. Cela nous permet de déployer les applications quelle que soit la stratégie du serveur cible.

Modèle, Vue, Contrôleur

Le concept de page en Noas PHP suit le très célèbre design pattern « MCV ». Pour notre part, la template représente une vue et le contexte un modèle (enrichi d'un état et d'un comportement). Le contrôle étant assuré par un élément interne du framework..

Une template est un fichier HTML quelconque, placé dans le répertoire « MA_WEB_APP »/NOAS-INF/template (vous pouvez créer des arborescences), composée de tagues spécifiques au framework. Lors du chargement de la page, le framework va la « compiler » en code PHP selon le mode de l'interface (release, design, debug). Une fois compilé le code PHP produit sera exécuté sur le contexte. Le mode « debug » spécifie que la template sera toujours recompilée à chaque nouvelle requête. Le mode « design » spécifie que la template ne sera compilée que si elle a été modifiée depuis la dernière compilation. Le mode « release » spécifie que la template ne sera compilée qu'une seule fois.

Pour placer des commentaires, non visibles après compilation, placez les entre « <%-- ... --%> ».

Créons notre première template, « home.tpl ».

<%--
//////////////////////////////////////////////////////////////////////
//  File :    home.tpl
/////////////////////////////////////////////////////////////////////
--%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
  <meta content="text/html; charset=ISO-8859-1"
        http-equiv="content-type">
  <title>Démo - Noas PHP</title>
</head>
<body>
  <h1>Noas PHP, extrême framework</h1>
  <hr style="width: 100%; height: 2px;">
  <div style="text-align: center;">
    programmez!
  </div>
</body>
</html>

Tous les contextes de page dérivent de la classe « noas.web.context.NoasPageDeviceContext». Il faut ensuite définir la template à utiliser, la page de retour valide et la page de retour invalide. La page de retour valide correspond au script PHP responsable du chargement du contexte. La page de retour invalide correspond au script PHP à exécuter lorsque le contexte n'est pas valide, généralement des points d'entré (page d'accueil, formulaire de connexion, etc.). Les chemins sont données en relatif.

Créons le contexte « emc.context.EmcHomePage ».

<?php
//////////////////////////////////////////////////////////////////////
//  File :    EmcHomePage.class.php
/////////////////////////////////////////////////////////////////////

Noas::import("noas.web.context.NoasPageDeviceContext");

class EmcHomePage extends NoasPageDeviceContext {

  function __construct() {
    parent::__construct("home.tpl"
                       ,"php/home".NOAS_PHP_EXTENSION);
    $this->setInvalidPage("php/home".NOAS_PHP_EXTENSION);
  }
}
?>

Vous remarquerez que la page de retour valide et invalide sont identiques car il s'agit d'un point d'entré sur l'application.

Intéressons-nous à la page qui doit charger le contexte, « php/home.php ». Vous n'avez que deux lignes de code à y ajouter. Premièrement insérez le fichier de configuration de l'application, deuxièmement chargez le contexte en spécifiant la classe du contexte et si le contrôle de la navigation doit être activé. Le contrôle de la navigation est une technique qui permet au développeur d'interdire l'affichage asynchrone d'une page. Imaginez si un utilisateur entre directement dans le navigateur l'adresse d'une page sensée afficher le détail d'une commande ! Comme cette commande n'existe pas, il est fort à parier que cette manipulation va déstabiliser votre application.

Dans notre cas, et de manière générale pour les points d'entré, désactivez se contrôle ( au risque de voir apparaître des boucles infinies !).

<?php
//////////////////////////////////////////////////////////////////////
//  File :    home.php
/////////////////////////////////////////////////////////////////////

include_once("../noas-emc-conf.inc");
Noas::loadPageContext("emc.context.EmcHomePage", false);
?>

Saisissez l'adresse de la page dans votre navigateur et observez le résultat.

Internationalisation

Le framework dispose d'un mécanise vous permettant de réaliser des applications en plusieurs langues tout en conservant le même code source. Vous avez la possibilité de placer des ressources dans un répertoire qui varie en fonction de langue de l'interface. Le changement se fait de manière transparente pour le développeur. Il existe trois types de ressource standard, les messages d'erreurs, les messages d'informations et les libellés d'interface (GUI). Vous être libre de laisser court à votre imagination pour en définir d'autres. Ces ressources standards se présent sous la forme de fichier au format « clef / valeur » (INI). Chargez les, ainsi que toutes vos ressources personnalisées, un surchargeant la méthode « loadResource() » de l'interface.

protected function loadResource($resourceBase, $local){
    $this->loadGuiResource( $resourceBase."emc-gui.resource");
    $this->loadErrorResource( $resourceBase."emc-error.resource");
    $this->loadMessageResource( $resourceBase."emc-message.resource");   
  }

Les ressources doivent être présentes dans tous les répertoires correspondant aux langues supportées. Nous nous contenterons du local « fr » qui a d'ailleurs été spécifié par défaut dans la configuration. Créez les fichiers de ressource dans le répertoire « MA_WEB_APP »/NOAS-INF/resource/fr. Internationalisons notre template. Notez que cela se fait généralement dans l'autre sens.

Définissez les deux seuls libellés dont nous avons besoin pour le moment dans le fichier GUI. Prenez soins de choisir des clefs significatives car pour un projet complet le nombre peut être conséquent.

EMC_TITLE="Noas PHP, extrême framework"

EMC_BOOK="programmez!"

Pour utiliser ces libellés dans la tempate, vous devez importer la librairie de tagues «noas.web.tag ». Elle contient des tagues classiques dont « resource » qui vous permet d'accéder aux valeurs des ressources standards. Lorsque vous importé une librairie vous devez spécifier l'espace de nomage à utiliser.

<%--
//////////////////////////////////////////////////////////////////////
//  File :    home.tpl
/////////////////////////////////////////////////////////////////////
--%>

<%@taglib uri="noas.web.tag" prefix="noas"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
  <meta content="text/html; charset=ISO-8859-1"
        http-equiv="content-type">
  <title>Démo - Noas PHP</title>
</head>
<body>
  <h1><noas:resource gui="EMC_TITLE"/></h1>
  <hr style="width: 100%; height: 2px;">
  <div style="text-align: center;">
    <noas:resource gui="EMC_BOOK"/>
  </div>
</body>
</html>

Inclusion statique

Il peut arriver que certaine partie des templates d'une application soient similaire. Par exemple le bloque contenue dans la balise « header » a de très forte chance de ne pas changer. Si vous souhaitez le modifier, le titre par exemple, vous seriez contraint de le propager sur l'ensemble des pages. Une astuce consiste à placer les bloques identiques dans des fichiers puis les inclure dans les templates. Ce mode d'inclusion est appelé « inclusion statique » car elle intervient avant la compilation de la template utilisatrice.

<%--
//////////////////////////////////////////////////////////////////////
//  File :    header.tpl
/////////////////////////////////////////////////////////////////////
--%>

<meta content="text/html; charset=ISO-8859-1" http-equiv="content-type">
<script src="<noas:base/>js/noas.js" type="text/javascript"></script>
<title>Démo - Noas PHP</title>

La tague « < :base/> » nous sert à obtenir l'adresse HTTP de l'application et le fichier « noas.js » sert à importer les scripts nécessaires à l'interactivé de l'interface.

<head><%@include file="header.tpl"%></head>

Événement

Nous avons précisez que les contextes étaient capables de « réagir » face à un événement. Un événement peut être généré par l'interface ou directement par programmation. Pour le capturer, un contexte doit implémenter une méthode de type « XXXEvent() » où XXX est le nom de l'événement. Les tagues capables d'en générer sont très facilement reconnaissable. Elles possèdent toutes l'attribut « event » à définir au nom de l'événement. La librairie de tague « noas.web.tag.html » en contient quelques-unes comme « href », « action », « form », « submit », etc.

Capturons l'événement « goToMessageEdit » en implémentant la méthode, vide pour le moment, « goToMessageEditEvent() ». Il sera généré par l'utilisateur en cliquant sur le lien « programmez ! ».

Importez la librairie « noas.web.tag.html »

<%@taglib uri="noas.web.tag.html" prefix="html"%>

Placez l'événement dans la template à l'aide de la balise « < :href/> ».

<html:href event="goToMessageEdit">
     <noas:resource gui="EMC_BOOK"/>
</html:href>

Message utilisateur

Le framework met à votre disposition un moyen de communiquer avec l'utilisateur en lui affichant des messages qui ne seront valide que pour une seule requête. Ces messages peuvent provenir de la validation d'un formulaire ou de votre propre volonté. Ils utilisent les ressources standards, donc supportent l'internationalisation. Envoyons un message d'erreur lorsque l'événement « goToMessageEdit » est généré.

Ajoutez une entré dans fichier de ressources d'erreur « emc-error.resource »

NO_IMPLEMENT_ERROR="Cette fonctionnalité est provisoirement indisponible !"

Composez le message dans la méthode événementielle.

public function goToMessageEditEvent(){   
    $message = new NoasMessage();
    $message->addErrorMessage("NO_IMPLEMENT_ERROR", array());
    self::getRequest()->getMessage()->addMessage($message);
}

Vous êtes libre de placer les messages utilisateur où vous le souhaitez dans la template. Vous pouvez ainsi personnaliser le rendu, par exemple les afficher en rouge.

<div style="color: red;">
     <html:errorMessage/>
</div>

Si vous ne voulez qu'afficher le message d'erreur « NO_IMPLEMENT_ERROR » placez plutôt le code suivant dans votre template.

<div style="color: red;">
    <html:errorMessage key= "NO_IMPLEMENT_ERROR"/>
</div>

Formulaire

Chaque champ d'un formulaire est toujours relié à une propriété d'un objet (modèle). Par défaut, il s'agit du contexte sur lequel s'exécute le script. Il est très souvent judicieux de spécifier directement un objet métier comme modèle. Vous bénéficiez au moins d'une mise à jour automatique ( sans passer par d'interminable « setXXX/getXXX » ) et d'une réutilisation des données recueillies. Optons pour cette solution. Mappons le formulaire sur l'objet métier « emc.business.EmcMessageEntity ». Rien n'oblige à ce qu'il dérive de « NoasObject ».

<?php
//////////////////////////////////////////////////////////////////////
//  File :    EmcMessageEntity.class.php
/////////////////////////////////////////////////////////////////////
 
class EmcMessageEntity extends NoasObject {
    private $entryId;
    private $surname;
    private $firstname;
    private $email;
    private $subject;
    private $message;
    private $response;
    private $service;
    private $dateRegister;
    private $dateSender;
    private $editable = false;

    public function getSurname() {       
        return $this->surname;
    }

    public function setSurname($_surname) {       
        $this->surname = $_surname;
    }

    public function getFirstname() {       
        return $this->firstname;
    }

    public function setFirstname($_firstname) {       
        $this->firstname = $_firstname;
    }

//...

    public function getEntryId() {       
        return $this->entryId;
    }

    public function setEntryId($_entryId) {       
        $this->entryId = $_entryId;
    }

    public function hasResponse() {       
        return isset($this->response);
    }   

    public function isEditable() {       
        return $this->editable;
    }

    public function setEditable($_editable) {       
        $this->editable = $_editable;
    }     
 }
?>

Implémentez le contexte de page d'édition du message dans la classe « emc.context.EmcEditPage ». Nous pouvons déjà prévoir les évènements « save » et « cancel ».

<?php
//////////////////////////////////////////////////////////////////////
//  File :    EmcEditPage.class.php
/////////////////////////////////////////////////////////////////////

Noas::import("noas.web.context.NoasPageDeviceContext");
Noas::import("emc.business.EmcMessageEntity");

class EmcEditPage extends NoasPageDeviceContext {
  private $businessObject;
 
  function __construct() {
    parent::__construct("edit.tpl"
                       ,"php/edit".NOAS_PHP_EXTENSION);
    $this->setInvalidPage("php/home".NOAS_PHP_EXTENSION);   
  }

  public function getBusinessObject(){
    if(!isset($this->businessObject)){
      $this->businessObject = $this->createBusinessObject();
    }
    return $this->businessObject;
  }

  public function setBusinessObject($_businessObject){
    $this->businessObject = $_businessObject;
  }

  public function saveEvent(){   
      // @TODO
  }

  public function cancelEvent(){   
    $this->setBusinessObject(null);
  }

  protected function createBusinessObject(){
    return new EmcMessageEntity();
  }
}
?>
<?php
//////////////////////////////////////////////////////////////////////
//  File :    edit.php
/////////////////////////////////////////////////////////////////////
 
include_once("../noas-emc-conf.inc");
Noas::loadPageContext("emc.context.EmcEditPage", false);
?>

Vous pouvez dés maintenant ajouter les libellés associés à cette template dans le fichier de ressource « emc-gui.resource ».
EMC_SAVE_BUTTON="Envoyer"
EMC_CANCEL_BUTTON="Annuler"

EMC_MESSAGE_ENTRYID="N°"
EMC_MESSAGE_SURNAME="Nom"
EMC_MESSAGE_FIRSTNAME="Prénom"
EMC_MESSAGE_EMAIL="Email"
EMC_MESSAGE_SUBJECT="Sujet"
EMC_MESSAGE_MESSAGE="Message"
EMC_MESSAGE_RESPONSE="Réponse"
EMC_MESSAGE_SERVICE="Service"
EMC_MESSAGE_DATE_REGISTER="Enregistré le"
EMC_MESSAGE_DATE_SENDER="Envoyé le"

La rédaction de la template n'est ni plus ni moins qu'un ensemble de tagues de type « input ». Leurs attributs sont quasiment identiques à leur homologue HTML, consultez la documentation pour plus de détails.

<%--
//////////////////////////////////////////////////////////////////////
//  File :    edit.tpl
/////////////////////////////////////////////////////////////////////
--%>

<%@taglib uri="noas.web.tag" prefix="noas"%>
<%@taglib uri="noas.web.tag.html" prefix="html"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head><%@include file="header.tpl"%></head>
<body>
  <h1><noas:resource gui="EMC_TITLE"/></h1>
  <hr style="width: 100%; height: 2px;">
  <html:form name="message">
      <table  cellspacing="1" cellpadding="0" class="list">
      <tr>
        <td style="vertical-align:middle;" width="50%">
        <noas:resource gui="EMC_MESSAGE_SURNAME"/>&nbsp;:&nbsp;
        </td>
        <td>
        <html:textfield model="businessObject" property="surname"
                        size="20" maxlength="200" />
        </td>
      </tr>
      <tr>
        <td style="vertical-align:middle;" width="50%">
        <noas:resource gui="EMC_MESSAGE_FIRSTNAME"/>&nbsp;:&nbsp;
        </td>
        <td>
        <html:textfield model="businessObject" property="firstname"
                        size="20" maxlength="200"/>
        </td>
      </tr>
      <tr>
        <td style="vertical-align:middle;" width="50%">
        <noas:resource gui="EMC_MESSAGE_EMAIL"/>&nbsp;:&nbsp;
        </td>
        <td>
        <html:textfield model="businessObject" property="email"
                        size="20" maxlength="200" />
        </td>
      </tr>
      <tr>
        <td style="vertical-align:middle;" colspan="2">
        <noas:resource gui="EMC_MESSAGE_SUBJECT"/>&nbsp;:&nbsp;<br/>
        <html:textfield model="businessObject" property="subject"
                        size="46" maxlength="300" />
        </td>
      </tr>
      <tr>
        <td style="vertical-align:middle;" colspan="2">
        <noas:resource gui="EMC_MESSAGE_MESSAGE"/>&nbsp;:&nbsp;<br/>
        <html:textarea model="businessObject" property="message"
                       rows="5" cols="35"/>
        </td>
      </tr>
      <tr>
        <td style="text-align: center; vertical-align:bottom;" colspan="2">
        <html:submit gui="EMC_SAVE_BUTTON" event="save"/>&nbsp;
        <html:button gui="EMC_CANCEL_BUTTON" event="cancel"/>
        </td>
      </tr>
      </table>
  </html:form>
</body>
</html>

Validation et Règles de gestion

Si vous testez l'exemple à ce niveau, vous constaterez que l'événement « save » est exécuté sans entrave même si les champs sont vides. Ceci n'est pas souhaitable car il est inadmissible de sauvegarder un message sans aucune information. Deux solutions se posent à nous. Soit nous effectuons manuellement les contrôles des valeurs dans la méthode « saveEvent() », soit nous utilisons les mécanismes du framework. Sachez que toutes les anomalies de valeurs détectables à l'aide d'une expression régulière sont interceptables.

Une règle `expression régulière' est associée à une clef unique. Vous devez ajouter une entré de même clef dans le fichier de ressource d'erreur correspondant au message à renvoyer à l'utilisateur lors de son occurrence, et une entrée de même clef dans un fichier de propriétés, correspondant à l'expression régulière associée.

Pour faire simple, énonçons que seul les messages non vide avec un sujet non vide et un mail valide seront acceptés. Ce qui nous conduit à définir deux règles, « NOTNULL_RULES » et « MAIL_RULES ». Ajouter les messages d'erreur correspondant.

NOTNULL_RULES="ce champ ne peut pas être nul !"

MAIL_RULES="ce champ doit être un e-mail valide !"

Un fichier de propriétés n'a besoin que d'être dans le chemin de classes. Créez le directement dans le répertoire « classes » de votre projet. Vous devez explicitement charger le fichier des règles lors de l'initialisation de l'application.

///////     emc-rules.properties       ///////////////////
NOTNULL_RULES="[^\s]+"
MAIL_RULES="/^[^@]+@(([\w\-]+\.){1,4}[a-zA-Z]{2,4}|(([01]?\d?\d|2[0-4]\d|25[0-5])\.){3}([01]?\d?\d|2[0-4]\d|25[0-5]))$/"

  public function initialize(){
    parent::initialize();
    $this->loadRules("emc-rules.properties", true);
  }

Vous pouvez à présent activer la validation automatique des règles, mais avant vous allez importer la librairie de tagues « noas.web.tag.logic » qui vous permettra d'utiliser des structures logiques dans vos temples ( if, else, while, etc.).

<%@taglib uri="noas.web.tag.logic" prefix="logic"%>
      <tr>
        <td style="vertical-align:middle;" width="50%">
        <noas:resource gui="EMC_MESSAGE_EMAIL"/>&nbsp;:&nbsp;
        <logic:ifError key="EMC_MESSAGE_EMAIL">
          <span title="<html:errorMessage key="EMC_MESSAGE_EMAIL"/>"
                style="color: red; cursor: hand;" >*</span>
        </logic:ifError>                        
        </td>
        <td>
        <html:textfield model="businessObject" property="email"
                        size="20" maxlength="200"
                        rules="NOTNULL_RULES" errorKey="EMC_MESSAGE_EMAIL"/>
        </td>
      </tr>
      <tr>
        <td style="vertical-align:middle;" colspan="2">
        <noas:resource gui="EMC_MESSAGE_SUBJECT"/>&nbsp;:&nbsp;
        <logic:ifError key="EMC_MESSAGE_SUBJECT">
          <span title="<html:errorMessage key="EMC_MESSAGE_SUBJECT"/>"
                style="color: red; cursor: hand;" >*</span>
        </logic:ifError>                                
        <br/>
        <html:textfield model="businessObject" property="subject"
                        size="46" maxlength="300"
                        rules="NOTNULL_RULES" errorKey="EMC_MESSAGE_SUBJECT"/>
        </td>
      </tr>
      <tr>
        <td style="vertical-align:middle;" colspan="2">
        <noas:resource gui="EMC_MESSAGE_MESSAGE"/>&nbsp;:&nbsp;
        <logic:ifError key="EMC_MESSAGE_MESSAGE">
          <span title="<html:errorMessage key="EMC_MESSAGE_MESSAGE"/>"
                style="color: red; cursor: hand;" >*</span>
        </logic:ifError>   
        <br/>
        <html:textarea model="businessObject" property="message"
                       rows="5" cols="35"
                       rules="NOTNULL_RULES" errorKey="EMC_MESSAGE_MESSAGE"/>
        </td>
      </tr>

Vous avez constatez que la règle « MAIL_RULES » n'a pas été utilisé. En réalité elle est trop complexe pour être utilisé en automatique (la fonction PHP ereg() est utilisée en interne ). Faite le manuellement de la manière suivante :

  public static function checkEmail(NoasMessage $_report, $_key, $_value){
    if(!preg_match (self::getApplication()->getRule("MAIL_RULES"), $_value)){
      $error = new NoasMessage();
      $error->addErrorMessage($_key, array("NOAS_INPUT_ERROR"=>"MAIL_RULES"));
      $_report->addMessage($error);     
    } 
  }

  public function saveEvent(){  
    $report = new NoasMessage();
    self::checkEmail($report, "EMC_MESSAGE_EMAIL", $this->getBusinessObject()->getEmail());
    if($report->hasErrorMessage()){
      self::getRequest()->getMessage()->addMessage($report);         
    } else {
            // @TODO
    }
 }

Tester l'application. Vous constaterez qu'apparaissent des étoiles rouges à côté des champs erronés. Passez le curseur au-dessus pour lire le message d'erreur associé.

Enumération

Sur le précédant formulaire, il manque la liste des services destinataires du message. Nous allons l'ajouter en utilisant un concept des « Enumérations ».

Les énumérations sont des associations clef/valeur où les clefs sont des codes et les valeurs des chaînes de caractères. Elles sont essentiellement utilisées pour représenter des ensembles finis de libellés, par exemple la liste des civilités (M., Mme, Mlle ). Le framework fournit une interface et des implémentations standards pour les modéliser.

Les énumérations supportent l'internationalisation. Lorsque vous les utilisez dans une template, sur une combo box par exemple, elles sont en premier lieu cherchées sur l'interface utilisateur, puis sur le modèle de la tague. Donc à moins d'une contrainte particulière, elles seront gérées par l'interface de votre application.

Pour notre exemple utilisons une énumération basée sur les fichiers (format INI). Comme nous vous l'avons précisez, les énumérations supportent l'internationalisation, vous placerez donc le fichier « emc-service.enum » dans le répertoire des ressources.

10="Direction Ressources Humaines"
20="Contentieux & Recouvrement"
30="Support Technique"
40="Commercial"
50="Marketing"
60="Autre"

L'implémentation à utiliser est « noas.util.enum.NoasFileEnum ». Son constructeur prend en argument le chemin complet du fichier à utiliser. Pour rendre cette énumération disponible pour les tagues de type « liste de choix » vous allez définir une propriété correspondante dans l'interface (sans oublier de l'importer).

Vous devez charger les données en même temps que le changement de langue.

<?php
//////////////////////////////////////////////////////////////////////
//  File :    EmcInterface.class.php
/////////////////////////////////////////////////////////////////////

Noas::import("noas.core.NoasUserInterface");
Noas::import("noas.util.enum.NoasFileEnum");

class EmcInterface extends NoasUserInterface {

  private $serviceEnum;

  function __construct() {
    parent::__construct(self::debug);
  }

    public function getServiceEnum(){
      if(!$this->serviceEnum->isLoad()){
        $this->serviceEnum->load();
      }
      return $this->serviceEnum;
   } 

  protected function loadResource($resourceBase, $local){
    $this->loadGuiResource( $resourceBase."emc-gui.resource");
    $this->loadErrorResource( $resourceBase."emc-error.resource");
    $this->loadMessageResource( $resourceBase."emc-message.resource");   
    $this->serviceEnum = new NoasFileEnum($resourceBase."emc-service.enum");
    $this->serviceEnum->setNullLabel(null);
  }
}
?>

Il ne vous reste plus qu'à ajouter la combo box dans le formulaire en spécifiant l'énumération utilisée.

<tr>
        <td style="vertical-align:middle;" colspan="2">
        <noas:resource gui="EMC_MESSAGE_SERVICE"/>&nbsp;:&nbsp;
        <html:combobox model="businessObject" property="service"
                        enum="serviceEnum" />
        </td>
</tr>

Persistance des données

Pour stocker nos informations nous n'avons besoin que d'une base de données et d'une table. Nous avons opté pour SQLite livré avec la distribution standard de PHP5. Créez la base de donnée « SQLDEMO ». Le fichier associer est placé dans le répertoire du projet sous le nom de « sqldemo.db ».

Chargez la définition de classe du moteur SQLite dans fichier d'implémentation de votre application.

Noas::import("sqlite.exchange.SQLiteDriver");

Définissez « demo » comme nom de référence à cette source de données en inscrivant les lignes suivantes dans la méthode d'initialisation de l'application.

public function initialize(){
    parent::initialize();
      $this->loadRules("emc-rules.properties", true);
      NoasExchangeManager::setReference("demo"
      ,"sqlite://[0666]"
      .NOAS_APPLICATION_HOME
      .NOAS_SEPARATOR."sqldemo.db");   
  }

Il faut à présent préparer la source de données. Créez la table nécessaire en exécutant l'instruction SQL suivante :

CREATE TABLE emc_entry (
  entry_id BIGINT NOT NULL PRIMARY KEY ,
  surname VARCHAR ( 150 ),
  firstname VARCHAR ( 100 ) ,
  email VARCHAR ( 60 ) NOT NULL ,
  subject VARCHAR ( 200 ) NOT NULL ,
  message TEXT NOT NULL ,
  response TEXT ,
  service INT NOT NULL ,
  date_register TIMESTAMP NOT NULL ,
  date_sender TIMESTAMP ) ;

Nous avons également besoin d'une séquence pour contrôler les numéros des enregistrements. Nous créons donc également la séquence « emc_entry_seq ».

CREATE TABLE noas_exchange_sequence (
  name     VARCHAR(100),
  value     BIGINT ) ;
INSERT INTO noas_exchange_sequence (name, value)
 VALUES
 ('emc_entry_seq', 1 ) ;

Nous vous rappelons que le processus de création de séquence dépend du SGBDR utilisée. Consultez la documentation pour plus d'information.

Pour effectuer les flux utilisons les mécanismes de persistances du framework. Pour mettre en relation la classe métier « EmcMessageEntity » et la table « emc_entry », utilisons un « proxy ». Ce « proxy », qui vous permettra de contrôler le transfert des objets. Il doit dériver de la classe « noas.exchange.NoasExchangeProxy ». Le framework fournir une variante, « noas.exchange.NoasExchangeProxyWizard », basé sur un fichier de description XML.Utilisons ce dernier pour plus d'aisance.

Vous devez rédiger un fichier de description pour chaque objet métier. La syntaxe est intuitive hors mis quelques éléments que nous allons préciser.

<?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>EmcMessageEntity</entity>
   <exchange>emc_entry</exchange>
   <code>EMC_ENTRY</code>
   <lazy>true</lazy>
   <desc>false</desc>
   <field>
      <type>INTEGER</type>
      <code>EMC_MESSAGE_ENTRYID</code>
      <property>entryId</property>
      <exchange>entry_id</exchange>
      <size>10</size>
      <notnull>true</notnull>
      <readonly>true</readonly>
      <writeonly>true</writeonly>
      <primary>true</primary>
      <sequence>emc_entry_seq</sequence>     
   </field>
   <field>
      <type>STRING</type>
      <code>EMC_MESSAGE_SURNAME</code>
      <property>surname</property>
      <exchange>surname</exchange>
      <size>150</size>
      <notnull>false</notnull>
      <readonly>true</readonly>
      <writeonly>true</writeonly>
   </field>
   <field>
      <type>STRING</type>
      <code>EMC_MESSAGE_FIRSTNAME</code>
      <property>firstname</property>
      <exchange>firstname</exchange>
      <size>120</size>
      <notnull>false</notnull>
      <readonly>true</readonly>
      <writeonly>true</writeonly>
   </field>
   <field>
      <type>STRING</type>
      <code>EMC_MESSAGE_EMAIL</code>
      <property>email</property>
      <exchange>email</exchange>
      <size>60</size>
      <notnull>true</notnull>
      <readonly>true</readonly>
      <writeonly>true</writeonly>
   </field>
   <field>
      <type>STRING</type>
      <code>EMC_MESSAGE_SUBJECT</code>
      <property>subject</property>
      <exchange>subject</exchange>
      <size>200</size>
      <notnull>true</notnull>
      <readonly>true</readonly>
      <writeonly>true</writeonly>
   </field>
   <field>
      <type>TEXT</type>
      <code>EMC_MESSAGE_MESSAGE</code>
      <property>message</property>
      <exchange>message</exchange>
      <size>1024</size>
      <notnull>true</notnull>
      <readonly>true</readonly>
      <writeonly>true</writeonly>
   </field>
   <field>
      <type>TEXT</type>
      <code>EMC_MESSAGE_RESPONSE</code>
      <property>response</property>
      <exchange>response</exchange>
      <size>1024</size>
      <notnull>false</notnull>
      <readonly>true</readonly>
      <writeonly>true</writeonly>
   </field>
   <field>
      <type>INTEGER</type>
      <code>EMC_MESSAGE_SERVICE</code>
      <property>service</property>
      <exchange>service</exchange>
      <size>10</size>
      <notnull>true</notnull>
      <readonly>true</readonly>
      <writeonly>true</writeonly>     
   </field>
   <field>
      <type>TIMESTAMP</type>
      <code>EMC_MESSAGE_DATE_REGISTER</code>
      <property>dateRegister</property>
      <exchange>date_register</exchange>
      <notnull>true</notnull>
      <readonly>true</readonly>
      <writeonly>true</writeonly>
   </field>
   <field>
      <type>TIMESTAMP</type>
      <code>EMC_MESSAGE_DATE_REGISTER</code>
      <property>dateSender</property>
      <exchange>date_sender</exchange>
      <notnull>false</notnull>
      <readonly>true</readonly>
      <writeonly>true</writeonly>
   </field>
</exchange-description>

L'élément « <type/> » correspond au type du champ dont les valeurs possibles sont "VERSION", "INTEGER", "NUMERIC", "STRING", "TEXT", "BLOB", "BOOLEAN", "TIMESTAMP".

L'élément « <code/> » sert à mieux distinguer les champs et peut être utilisé comme identifiant de libellé GUI.

L'élément « <property/> » correspond à la propriété de classe associée.

L'élément « < exchange /> » correspond à la colonne de table associée.

Les éléments« < readonly /> » et « < writeonly /> » correspondent aux sens d'écriture autorisés.

L'élément « < sequence /> » correspond à la séquence numérique à utiliser pour incrémenter automatiquement la valeur du champ.

Les fichiers de description ne se limite pas qu'aux simples champs. Vous pouvez également déclarer des agrégats ou des références qui seront gérées automatiquement ( mise à jour et suppression en cascades) en utilisant les éléments « <aggregate/> » et « <reference/>».

Notre proxy ne fera pas grand chose, si ce n'est que ce référencer au prés du manageur et définir la date de création.

<?php
//////////////////////////////////////////////////////////////////////
//  File :    EmcMessageEntityProxy.class.php
/////////////////////////////////////////////////////////////////////

Noas::import("noas.exchange.NoasExchangeProxyWizard");
Noas::import("emc.business.EmcMessageEntity");

class EmcMessageEntityProxy extends NoasExchangeProxyWizard  {

  function __construct() {
    parent::__construct(dirname(__FILE__)
               .NOAS_SEPARATOR."EmcMessageEntity.xml");
  }

  public function onCreate(NoasExchangeContext $_context, $_businessObject) {
    parent::onCreate($_context, $_businessObject);
    $_businessObject->setDateRegister(NoasTimestamp::getTimestamp()) ;
  } 
}
NoasExchangeManager::setProxy("EmcMessageEntity", "EmcMessageEntityProxy");
?>

Utilisons une transaction sur la référence « demo » pour effectuer l'enregistrement en base de données dans l'événement « save ».

Importer la définition de la transaction et du proxy dans la classe « EmcEditPage ».

Noas::import("noas.exchange.NoasExchangeTransaction");
Noas::import("emc.business.EmcMessageEntityProxy");

Vous devez modifier un temps soit peu la méthode « createBusinessObject() ». pour être en accord avec notre nouvelle logique.

  protected function createBusinessObject(){
    $newBusinessObject = new EmcMessageEntity();
    $transaction = new NoasExchangeTransaction("demo");
    $transaction->open();
    $transaction->beginTransaction(NOAS_SERIALIZABLE);
    $transaction->create($newBusinessObject);
    $transaction->commit();
    $transaction->close();       
    return $newBusinessObject;
  }

La modification apportée va nous permettre d'initialiser correctement l'objet et de ne pas tenter d'insérer des instances avec des identifiant nulles.

public function saveEvent(){  
    $report = new NoasMessage();
    self::checkEmail($report, "EMC_MESSAGE_EMAIL"
                   , $this->getBusinessObject()->getEmail());
      
   if($report->hasErrorMessage()){
      self::getRequest()->getMessage()->addMessage($report);         
    } else {
 
      $transaction = new NoasExchangeTransaction("demo");
      $transaction->open();
      $transaction->beginTransaction();
      try{
        if($transaction->exist($this->getBusinessObject())){
          $transaction->update($this->getBusinessObject());
        } else {
          $transaction->save($this->getBusinessObject());
        }
        $transaction->commit();
      } catch(Exception $ex){
        try { $transaction->rollback(); }catch (Exception $ex){ }
        Noas::log($ex);
      }
      $transaction->close();    
    }
 }

Pour une réelle application soyer plus rigoureux sur la gestion des exceptions. Les exceptions renvoyées par la couche de persistance sont assez riches pour réagire finement selon les cas. Penser également à renvoyer un message de confirmation pour les actions significatives réussies. Vous pouvez gagner en précision, au niveau de l'harmonisation des contraintes, en utilisant les tagues de la librairie « noas.web.tag.exchange ». Ses champs de formulaire vont directement utiliser les contraintes définis par les proxys. Vous n'aurez donc plus besoin de préciser la taille maximale des champs par exemple ou encore s'il ne doit pas être nul.

<tr>
        <td style="vertical-align:middle;" width="50%">
        <noas:resource gui="EMC_MESSAGE_SURNAME"/>&nbsp;:&nbsp;
        </td>
        <td>
        <exchange:textfield model="businessObject" property="surname" size="20"/>
        </td>
</tr>

Il existe différent moyen de naviguer sur les pages de votre application. Le moyen le plus simple, mais complètement hors gamme et qui ne doit servir qu'en cas d'extrême nécessité, est de placer directement les URL dans les templates. Une autre façon de procéder est de définir un événement qui va effectuer un « forward » sur un contexte. Sachez que dans ce cas, l'objet requête n'est pas réinitialiser. Le nombre de forward n'est pas limité. Utilisions cette dernière méthode pour accéder au formulaire de message depuis le contexte « EmcHomePage »

 public function goToMessageEditEvent(){   
    self::getRequest()->forward("emc.context.EmcEditPage");
  }

Vous pouvez transmettre plusieurs informations à un contexte en procédant ainsi ( un objet à modifier, un événement, etc.). Si cela n'est pas le cas, utilisez plutôt la tague « <:forward/> » depuis la template, elle effectue exactement le même travail.

<html:forward page="emc.context.EmcEditPage">
      <noas:resource gui="EMC_BOOK"/>
</html:forward>

Si vous souhaitez utiliser les URL plus évoluées, ce qui peut arriver si vous devez en créer dynamiquement, vous devez utiliser un mapping. Cela vous permet de changer le contenu d'une URL sans avoir à retoucher toutes les templates. Voici le même exemple en utilisant les URL.

<?php
//////////////////////////////////////////////////////////////////////
//  File :    EmcHomePage.class.php
/////////////////////////////////////////////////////////////////////
Noas::import("noas.web.context.NoasPageDeviceContext");
 
class EmcHomePage extends NoasPageDeviceContext {
  function __construct() {
    parent::__construct("home.tpl"
                       ,"php/home".NOAS_PHP_EXTENSION);
    $this->setInvalidPage("php/home".NOAS_PHP_EXTENSION);
 
    $this->URL("emc.context.EmcEditPage"
    , NoasURL::create("php/edit".NOAS_PHP_EXTENSION));
  }
}
?>
    <html:redirect page="emc.context.EmcEditPage">
      <noas:resource gui="EMC_BOOK"/>
    </html:redirect>

Authentification & Sécurité

Noas PHP se veut « intégral », par conséquent il se devait d'implémenter un mécanisme d'authentification. Le principe est simple et pas très nouveau, chaque session dispose d'un utilisateur qui lui-même possède un nom d'identification et des droits. Son initialisation et leurs définitions sont libres, ce qui permet d'imaginer une gestion des droits les plus complexe. Le framework dispose d'une implémentation rudimentaire, par rapport à ce qui être réalisé en la matière, basé sur XML. Pour un site avec plusieurs utilisateurs, il est préférable d'utiliser des données stockées en base.

Notre exemple est simple et la classe « noas.xml.user.NoasXmlRemoteUser » suffit amplement. Nous allons considérer qu'un utilisateur peut consulter les messages d'un service s'il possède le droit égale au code du service. Les autres actions seront soumises aux droit « UPDATE », « DELETE ». Pour la démo créez un « administrateur » et des « clients ».

Prenez soins de le protéger le répertoire « NOAS-INF » pour un usage une production. Créez le fichier de nom arbitraire « users.security.xml ».

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE user-description PUBLIC "-//Noas PHP, The Framework.//DTD User Description 1.0//FR" "http://noas.sourceforge.net/dtds/noas-user_1_0.dtd">
<user-description>
  <user>
   <name>admin</name>
   <login>admin</login>
   <password>adminpwd</password>
   <email>admin@admin.net</email>
   <profile>
      <right>10</right>
      <right>20</right>
      <right>30</right>
      <right>40</right>
      <right>50</right>
      <right>60</right>
      <right>UPDATE</right>
      <right>DELETE</right>
   </profile>
  </user>
  <user>
   <name>client1</name>
   <login>client1</login>
   <password>client1pwd</password>
   <email>client1@client.com</email>
   <profile>
      <right>10</right>
      <right>20</right>
      <right>30</right>
   </profile>
  </user>
  <user>
   <name>client2</name>
   <login>client2</login>
   <password>client2pwd</password>
   <email>client2@client.com</email>
   <profile>
      <right>40</right>
      <right>50</right>
      <right>60</right>
   </profile>
  </user>
</user-description>

A présent, nous nous intéressons à la page de connexion à la partie « back office » de l'application.

<?php
//////////////////////////////////////////////////////////////////////
//  File :  
Ce document intitulé « Noas php - mise en ?uvre » issu de CodeS SourceS (codes-sources.commentcamarche.net) est mis à disposition sous les termes de la licence Creative Commons. Vous pouvez copier, modifier des copies de cette page, dans les conditions fixées par la licence, tant que cette note apparaît clairement.
Rejoignez-nous