Vous découvrirez dans cet article comment réaliser une petite application Jelix basée sur un CRUD.
ATTENTION : ce tutoriel est assez vieux, et n'utilise pas le controlleur jControllerDaoCrud fourni depuis jelix 1.0Beta3.1 qui simplifie beaucoup la tâche (Voir l'autre tutoriel).
Le code comprend :
Limites actuelles
Étant donné qu'il y a des bugs dans jForms de la version 1.0 beta 2.1 de Jelix (rappel: jForms est étiqueté comme étant experimental dans cette version !), les exemples de ce tutoriel ne fonctionnent qu'avec la version en cours de développement de jelix 1.0 beta 3 (la version dites nightly). Par contre, il faudra faire quelques adaptations si vous utilisez jelix 1.0.
Vous devez avoir fait le Mini tutoriel et le Tutoriel principal ou être à l'aise avec Jelix. Nous utiliserons le même type d'installation que celle du Mini tutoriel. Nous vous conseillons fortement la lecture des articles concernant le formulaire et le DAO du manuel de Jelix.
Veillez suivre la démarche décrite dans l'article Étapes lors de la création d'une application Jelix en tenant compte des informations suivantes:
Nom de l'application : news
Nom du module : news
basePath = “/jelix/news/www” ou selon votre configuration préférée.
Il se peut que cette table existe déjà dans votre base de données, sinon créez là.
CREATE TABLE `news` ( `id_news` INT(11) NOT NULL AUTO_INCREMENT, `sujet` VARCHAR(255) NOT NULL, `texte` text NOT NULL, `news_date` DATE NOT NULL, PRIMARY KEY (`id_news`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
Ressource jDao : news~news
Fichier DAO : news
Table : news
Créez dans le répertoire jelix/news/modules/news/forms/, le fichier news.form.xml.
<?xml version="1.0" encoding="utf-8"?> <form xmlns="http://jelix.org/ns/forms/1.0"> <input ref="id_news" > <label>Id</label> </input> <input ref="sujet" > <label>Votre sujet</label> </input> <input ref="texte" > <label>Votre texte</label> </input> <input ref="news_date" > <label>Votre date</label> </input> </form>
Précisons l'ouvrage qu'il nous reste à faire pour compléter notre programme :
Et nous aurons une fois terminé une application de base pouvant évoluer vers un projet informatique.
Le but de créer une réponse HTML personnalisée est de généraliser dans l'application :
Enregistrez le code suivant dans le fichier jelix/news/responses/myHtmlResponse.class.php
<?php require_once (JELIX_LIB_RESPONSE_PATH.'jResponseHtml.class.php'); class myHtmlResponse extends jResponseHtml { public $bodyTpl = 'news~main'; // modifications communes aux actions utilisant cette reponses protected function _commonProcess(){ $this->body->assignIfNone('MAIN','<p>Aucun contenu</p>'); $this->body->assignIfNone('CRUDtpl',''); $this->addCSSLink($GLOBALS['gJConfig']->urlengine['basePath'].'screen.css'); } } ?>
Consulter le manuel pour obtenir plus de détail sur la classe jReponseHTML.
Pour indiquer à Jelix que vous avez créé une réponse personnalisée, vous devez ouvrir le fichier jelix/news/var/config/index/config.ini.php et ajouter dans la section [responses] la ligne suivante:
html = myhtmlResponse
Le fichier screen.css doit être enregistré dans jelix/news/www/ ou dans un de ses sous-répertoires. Ce fichier est le CSS contenu dans l'application testapp de Jelix.
body { font-family: Verdana, Arial, Sans; font-size:0.8em;} a img { border:0;} pre { padding-left:1em; border-left:4px solid #efe03b; color:black; } #header { margin:20px 0 0 190px; width:730px; padding:15px; background-color:#1e92af; -moz-border-radius:10px 10px 0 0; /*height:35px;*/ color:white; font-weight:bold; font-size:1.5em; } #main {clear:both; margin:0 0 0 190px; padding:30px 15px 15px 15px; max-width:730px; color : #002830; border-left:1px solid #d8e1e4; border-right:1px solid #d8e1e4; max-width:728px; } #main a { color:#0594c8; text-decoration:underline; } #main a:visited { color : #002830;} #main a:hover { color: white; background-color: #0594c8; text-decoration:none;} #main a.wikilink2 { color:#ed5a02;} #main h1 { color: #3c90af; border-bottom:4px solid #ed5a02; margin:0; } #main h2 { color: #3c90af; border-bottom:2px solid #002830; margin:1em 0 0.5em 0; } #main h3 { color: #002830; border-bottom:1px solid #002830; margin:1em 0 0.5em 0; } #main h4 { color: #002830; } #main input.button{ background-color:#4397b6; border-left: 1px solid #99cbde; border-right: 1px solid #225365; border-top: 1px solid #99cbde ; border-bottom: 1px solid #225365; -moz-border-radius:3px; color:white; cursor: pointer; } .fail { color: red; } .pass { color: green; } pre { background-color: lightgray; } .resultfail { padding: 8px; margin-top: 1em; background-color: red; color: white; } .resultsuccess { padding: 8px; margin-top: 1em; background-color: green; color: white; } div.exception { margin-top:4px; margin-bottom:4px; background-color: #ff9186; border: 1px solid red; } div.exception strong {color: red;} div.exception p { font-size:0.9em; } .diff { background: white; border: 1px solid black; } .diff .block { background: #ccc; padding-left: 1em; } .diff .context { background: white; border: none; } .diff .block tt { font-weight: normal; font-family: monospace; color: black; margin-left: 0; border: none; } .diff del, .diff ins { font-weight: bold; text-decoration: none; } .diff .original, .diff .deleted, .diff .final, .diff .added { background: white; } .diff .original, .diff .deleted { background: #fcc; border: none; } .diff .final, .diff .added { background: #cfc; border: none; } .diff del { background: #f99; } .diff ins { background: #9f9; } #sidemenu { position:absolute; top:0; left:0; width:185px; padding-left:10px; } #sidemenu a {color:#0594c8; text-decoration:none;} #sidemenu a:hover { text-decoration: underline; } #sidemenu h1 { text-align:center; } #sidemenu h1 a {text-decoration:none;} #sidemenu h1 a:hover { text-decoration:none; } #sidemenu ul { margin:0 0 0 1em; padding:0 0 0 1em; color: #3c90af; } #sidemenu ul li { margin:0; padding:0 0 0 0em; } #sidemenu .userinfo { margin:2em 0; } #sidemenu p { text-align:center;} #footer { clear:both; margin:0 0 0 190px; padding:15px; max-width:728px; color : #002830; text-align:center; min-height:2em; background-color:#d8e1e4; border-left:1px solid #d8e1e4; border-right:1px solid #d8e1e4; border-bottom:1px solid #d8e1e4; -moz-border-radius:0px 0px 15px 15px; }
Le classe baseCRUD contient tout le code directement lié à la manipulation des données d'une table.
Au début de la classe on retrouve le nom des sélecteurs et des templates utilisés par le CRUD mais initialisés par la classe fille.
Viens par après les messages qu'on peut considérer comme par défaut, puisque la classe fille peut les modifier.
Le CRUD crée un template qui contiendra les messages et les données. Puis ce template sera inséré dans le template principal par le biais de la variable de template CRUDtpl.
Vous devez enregistrer ce code dans le répertoire jelix/news/modules/news/controllers/ et le fichier se nomme baseCrudController.php.
<?php class baseCrudController extends jController{ // Les sélecteurs protected $CRUD_sel_jDao; protected $CRUD_sel_jForm; // Redirections protected $CRUD_sel_direct_browse; protected $CRUD_sel_direct_update; protected $CRUD_sel_direct_create; // Les templates du CRUD protected $CRUD_tpl_browse; protected $CRUD_tpl_edit; protected $CRUD_tpl_view; // Les messages du CRUD protected $CRUD_txt_browse = array( 'title' => 'La liste des fiches ',); protected $CRUD_txt_create = array( 'title' => 'Ajout d\'une fiche ',); protected $CRUD_txt_update = array( 'title' => 'Edition d\'une fiche ', 'errID' => 'L\'enregistrement que vous recherchez n\'existe pas. ',); protected $CRUD_txt_save = array( 'errTitle' => 'Erreur de session! ou session fermé', 'errMessage' => 'Les données session recherchées n\'existent pas',); protected $CRUD_txt_view = array( 'title' => 'La fiche ',); // Le réponse de l'action est-elle du type HTML ou est-ce une redirection? private $__typeReponseHTML = false; public function __construct($request){ parent::__construct($request); } /** * Récupère plusieurs enregistrements de la db */ protected function crud_browse(){ // Prépare la réponse $rep = $this->getResponse('html'); // Crée un template CRUD qui contiendra les données $tpl = new jTpl(); $tpl->assign('title', $this->CRUD_txt_browse['title']); // On récupère les données de la db $fact = jDao::get($this->CRUD_sel_jDao); $tpl->assign('liste', $fact->findAll()); // La validation d'une liste vide se fait dans la vue. // Insertion du template CRUD dans le template de l'action $rep->body->assign('CRUDtpl', $tpl->fetch($this->CRUD_tpl_browse)); return $rep; } /** * Ajoute un nouvel enregistrement dans la db */ protected function crud_create(){ // Si le jForms n'existe pas on le crée $form = jForms::get($this->CRUD_sel_jForm); if($form == null){ $form = jForms::create($this->CRUD_sel_jForm); } // Prépare la réponse $rep = $this->getResponse('html'); // Crée un template CRUD qui contiendra les données $tpl = new jTpl(); $tpl->assign('title', $this->CRUD_txt_create['title']); // Insère les données du formulaire dans le template CRUD $tpl->assign('form', $form->getContainer()); // Insertion du template CRUD dans le template de l'action $rep->body->assign('CRUDtpl', $tpl->fetch($this->CRUD_tpl_edit)); return $rep; } /** * Préparation de la saisie de données du formulaire html * Paramètres : * id : la clé du formulaire qui est aussi la clé primaire de notre enregistrement */ protected function crud_update(){ // Préparation de la réponse $rep = $this->getResponse('html'); // Si le jForms n'existe pas on le crée $form = jForms::get($this->CRUD_sel_jForm, $this->param('id')); if($form == null){ $form = jForms::create($this->CRUD_sel_jForm, $this->param('id')); } // On récupère les données de la db try { $form->initFromDao($this->CRUD_sel_jDao); }catch(Exception $e){ //quand initFromDao ne trouve pas l'enregistrement, il y a une exception $rep->title = $this->CRUD_txt_update['title']; $rep->body->assign('CRUDtpl', $this->CRUD_txt_update['errID']); return $rep; } // Un nouveau template qui contiendra les données $tpl = new jTpl(); $tpl->assign('title', $this->CRUD_txt_update['title']); // Insère les données du formulaire dans le template CRUD $tpl->assign('form', $form->getContainer()); // Insertion du template dans le template de l'action $rep->body->assign('CRUDtpl', $tpl->fetch($this->CRUD_tpl_edit)); return $rep; } /** * Enregistre le formulaire (jForm) * paramètres : * id : la clé du formulaire qui est aussi la clé primaire de notre enregistrement * La validation de données sera complétée dans la version beta 3... */ protected function crud_save(){ // On récupère les données de la session courante $form = jForms::fill($this->CRUD_sel_jForm, $this->param('id')); if(!$form){ // pas de formulaire ! // Préparation de la réponse de type html $rep = $this->getResponse("html"); $__typeReponseHTML = true; // Un nouveau template qui contiendra les données $tpl = new jTpl(); $tpl->assign('title', $this->CRUD_txt_save['errTitle']); $tpl->assign('message', $this->CRUD_txt_save['errMessage']); // Insertion du template dans le template de l'action $rep->body->assign('CRUDtpl', $tpl->fetch($this->CRUD_tpl_index)); return $rep; } //normalement, on devrait utiliser $form->check() pour vérifier le contenu du formulaire //mais cette méthode n'est pas complètement implanté. Donc on vérifie à la main if($form->getData('sujet') != '' && $form->getData('texte') != ''){ $form->saveToDao($this->CRUD_sel_jDao); }else{ // la vérification a échouée, // Retourne les données vers le client pour correction $rep = $this->getResponse("redirect"); $rep->params['id'] = $this->param('id'); if($this->param('id')){ $rep->action = $this->CRUD_sel_direct_update; }else{ $rep->action = $this->CRUD_sel_direct_create; } return $rep; } // La sauvegarde des données à réussi, // on va maintenant effacer le formulaire en session jForms::destroy($this->CRUD_sel_jForm, $this->param('id')); // on retourne à la liste $rep = $this->getResponse("redirect"); $rep->action = $this->CRUD_sel_direct_browse; return $rep; } /** * Affiche un enregistrement de la db * paramètres : * id : la clé de notre enregistrement */ protected function crud_view(){ // Préparation de la réponse $rep = $this->getResponse('html'); // On récupère les données de la db $fact = jDao::get($this->CRUD_sel_jDao); $rec = $fact->get($this->param('id')); // La validation d'une liste vide se fera dans la vue. // Un nouveau template qui contiendra les données $tpl = new jTpl(); $tpl->assign('title', $this->CRUD_txt_view['title']); $tpl->assign('rec', $rec); // Insertion du template dans le template de l'action $rep->body->assign('CRUDtpl', $tpl->fetch($this->CRUD_tpl_view)); return $rep; } /** * Efface l'enregistrement * paramètres : * id : la clé de notre enregistrement */ protected function crud_destroy(){ // Récupère un objet jDao $dao = jDao::get($this->CRUD_sel_jDao); // Détruit l'enregistrement correspondant à l'id $dao->delete($this->param('id')); // on retourne à la liste $rep = $this->getResponse("redirect"); $rep->action = $this->CRUD_sel_direct_browse; return $rep; } /** * Dans une action retourne vrai si la réponse utilisé est du type HTML */ protected function checkReponseHTML(){ return $this->__typeReponseHTML; } } ?>
Nous voici rendu au controleur qui utilise notre CRUD, la classe fille, la classe qui hérite du CRUD. C'est aussi cette classe qu'on appelle les fonctions qui manipulent les enregistrements de la table.
Au début du controleur on retrouve un message qui sera affiché par l'action index.
Remarquez le constructeur qui a exactement la même signature que sa classe parente qui elle aussi à la même signature que sa classe parente. Également le constructeur initialise les sélecteurs et les templates du CRUD. De plus le choix du template principal et du css a été fait dans la réponse personnalisée. Voir une section précédente.
Vous devez copier le code suivant dans le fichier jelix/news/modules/news/controllers/default.classic.php
<?php global $gJCoord; include $gJCoord->getModulePath('news').'controllers/baseCrudController.php'; class defaultCtrl extends baseCrudController{ private $my_msg_index = 'Jelix vous présente un exemple de CRUD inscrit dans une classe de base.<br /><br /> Veillez noter que les exemples de ce tutoriel ne fonctionnent qu’avec la version en cours de développement de jelix 1.0 beta 3.<br /><br /> Le code comprend : <ul> <li>La classe baseCrud</li> <li>Un exemple d\'utilisation d\'un CRUD</li> <li>Personnalisation complète du CRUD :: ressources Jelix du CRUD et des messages</li> <li>Une réponse html personnalisé</li> <li>Utilisation d\'un css</li> <li>Un template principal et quatres templates du CRUD correspondant à chacune des actions</li> <li>Un code bien documenté</li> <li>Un code facilement adaptable</li> </ul> <br /> <strong>Limites présentement</strong><br /> <ul> <li>Ne gère qu\'une seule table via le jDao</li> <li>Les templates du CRUD sont créés manuellement</li> </ul> '; public function __construct($request){ parent::__construct($request); $this->common(); } /** * Initialisation du CRUD */ private function common(){ $this->CRUD_sel_jDao = 'news~news'; $this->CRUD_sel_jForm = 'news'; $this->CRUD_sel_direct_browse = 'news~default_browse'; $this->CRUD_sel_direct_update = 'news~default_update'; $this->CRUD_sel_direct_create = 'news~default_create'; $this->CRUD_tpl_browse = 'news~crudbrowse'; $this->CRUD_tpl_edit = 'news~crudedit'; $this->CRUD_tpl_view = 'news~crudview'; } /** * Action par défaut... */ public function index(){ $rep = $this->getResponse('html'); $rep->body->assign('MAIN', $this->my_msg_index); return $rep; } /** * Exemple d'utilisation du CRUD * Récupère plusieurs enregistrements de la db */ public function browse(){ // CRUD $rep = $this->crud_browse(); // Votre formulaire $rep->body->assign('MAIN', 'Template d\'utilisation du CRUD action browse....'); return $rep; } /** * Exemple d'utilisation du CRUD * Préparation de la saisie de nouvelles données du formulaire html */ public function create(){ // CRUD $rep = $this->crud_create(); // Votre formulaire $rep->body->assign('MAIN', 'Template d\'utilisation du CRUD action create....'); return $rep; } /** * Exemple d'utilisation du CRUD * Préparation de la modification de données du formulaire html */ public function update(){ // CRUD $rep = $this->crud_update(); // Votre formulaire $rep->body->assign('MAIN', 'Template d\'utilisation du CRUD action update....'); return $rep; } /** * Exemple d'utilisation du CRUD * Ajoute ou modifie un enregistrement dans la db */ public function save(){ // CRUD $rep = $this->crud_save(); // Votre formulaire // Les réponses "html et redirect" n'ont pas les mêmes propriétés if($this->checkReponseHTML()){ $rep->body->assign('MAIN', 'Template d\'utilisation du CRUD action save....'); } return $rep; } /** * Exemple d'utilisation du CRUD * Affiche un enregistrement de la db */ public function view(){ // CRUD $rep = $this->crud_view(); // Votre formulaire $rep->body->assign('MAIN', 'Template d\'utilisation du CRUD action view....'); return $rep; } /** * Exemple d'utilisation du CRUD * Efface l'enregistrement dans la db */ public function destroy(){ // CRUD $rep = $this->crud_destroy(); return $rep; } } ?>
Il s'agit du template principal qui affiche les templates du CRUD par le biais de la variable de template $CRUDtpl.
Vous devez copier le code ci-dessous dans le fichier template nommé main.tpl du répertoire /jelix/news/modules/news/templates/.
<div id="header"> Tutoriel CRUD de base </div> <div id="main"> {$MAIN} {$CRUDtpl} </div> <div id="sidemenu"> <h2>Sommaire</h2> <ul> <li><a href="{jurl 'news~default_index'}">Index</a></li> <li><a href="{jurl 'news~default_browse'}">browse</a></li> <li><a href="{jurl 'news~default_create'}">create</a></li> </ul> </div> <div id="footer"> <p>page HTML générée par {@jelix~jelix.framework.name@}</p> </div>
Il s'agit du template du CRUD qui affiche un enregistrement.
Vous devez copier le code ci-dessous dans le fichier template nommé crudview.tpl du répertoire /jelix/news/modules/news/templates/.
<h3>{$title}</h3> <br /> {if $rec} <dl> <dt>id_news :</dt> <dd>{$rec->id_news}</dd> <dt>Sujet :</dt> <dd>{$rec->sujet|escxml}</dd> <dt>Texte :</dt> <dd>{$rec->texte|escxml}</dd> <dt>Date :</dt> <dd>{$rec->news_date}</dd> </dl> {else} <p>Cette fiche n'existe pas!</p> {/if}
Il s'agit du template du CRUD qui permet l'édition d'un enregistrement.
Vous devez copier le code ci-dessous dans le fichier template nommé crudedit.tpl du répertoire /jelix/news/modules/news/templates/.
<h3>{$title}</h3> <br /> <form action="{jurl 'news~default_save'}" method="POST"> <fieldset> <legend>Votre identité<legend> <p><label for="sujet"> Sujet : </label> <input type="text" name="sujet" id="sujet" value="{$form->datas['sujet']|escxml}" /></p> <p><label for="texte">Texte: </label> <input type="text" name="texte" id="texte" value="{$form->datas['texte']|escxml}" /></p> <p><label for="news_date">date: </label> <input type="text" name="news_date" id="news_date" value="{$form->datas['news_date']}" /> </fieldset> <input type="hidden" name="id" value="{$form->formId}" /> <p><input type = "submit" value="ok" /></p> </form>
Il s'agit du template du CRUD qui affiche plusieurs enregistrements.
Vous devez copier le code ci-dessous dans le fichier template nommé crudbrowse.tpl du répertoire /jelix/news/modules/news/templates/.
<h3>{$title}</h3> <br /> {if $liste->rowCount()} <table border="1"> {foreach $liste as $rec} <tr> <td>{$rec->id_news}</td> <td>{$rec->sujet|escxml}</td> <td>{$rec->texte|escxml}</td> <td>{$rec->news_date}</td> <td> <a href="{jurl 'news~default_view',array('id'=>$rec->id_news)}">voir</a> <a href="{jurl 'news~default_update',array('id'=>$rec->id_news)}">éditer</a> <a href="{jurl 'news~default_destroy',array('id'=>$rec->id_news)}">détruire</a> </tr> {/foreach} </table> {else} <p>Il y a aucune fiches d'enregistrés</p> {/if}