Fichier bat à modifier/exécuter :
set PATH=%PATH%;d:\xampp\php;%USERPROFILE%\AppData\Roaming\Composer\vendor\bin
Avant de commencer faire:
composer global update
Créer le projet tp3 en invite du commande à partir du dossier htdocs de XAMPP
cd htdocs ubiquity new tp3 -a
Ouvrir/créer ce projet avec votre IDE (Eclipse ou PHPStorm)
Démarrer le serveur Mysql à partir de Xampp et démarrer le serveur Ubiquity à partir du dossier tp3 :
Ubiquity serve
Créer un repository php-rt-tp3 sur votre compte github (https://github.com) :
git --version
Si git n'est pas installé, téléchargez et installez le : https://git-scm.com/downloads
Activer la rubrique git d'UbiquityMyAdmin :
Les classes métiers (models) permettent de faire la passerelle avec la base de données :
Il est possible de les générer automatiquement à partir d'UbiquityMyAdmin :
Aller dans models puis cliquer sur (Re-)create Models
Exemple de la classe Organization :
Cette classe correspond à la table organization de la base de données. Les informations de mapping Objet/relationnel sont définies grâce aux annotations (voir Models).
<?php namespace models; class Organization{ /** * @id * @column("name"=>"id","nullable"=>"","dbType"=>"int(11)") */ private $id; /** * @column("name"=>"name","nullable"=>"","dbType"=>"varchar(100)") */ private $name; /** * @column("name"=>"domain","nullable"=>"","dbType"=>"varchar(255)") */ private $domain; /** * @column("name"=>"aliases","nullable"=>1,"dbType"=>"text") */ private $aliases; /** * @oneToMany("mappedBy"=>"organization","className"=>"models\\Groupe") */ private $groupes; /** * @oneToMany("mappedBy"=>"organization","className"=>"models\\Organizationsettings") */ private $organizationsettingss; /** * @oneToMany("mappedBy"=>"organization","className"=>"models\\User") */ private $users; public function getId(){ return $this->id; } public function setId($id){ $this->id=$id; } public function getName(){ return $this->name; } public function setName($name){ $this->name=$name; } public function getDomain(){ return $this->domain; } public function setDomain($domain){ $this->domain=$domain; } public function getAliases(){ return $this->aliases; } public function setAliases($aliases){ $this->aliases=$aliases; } public function getGroupes(){ return $this->groupes; } public function setGroupes($groupes){ $this->groupes=$groupes; } public function getOrganizationsettingss(){ return $this->organizationsettingss; } public function setOrganizationsettingss($organizationsettingss){ $this->organizationsettingss=$organizationsettingss; } public function getUsers(){ return $this->users; } public function setUsers($users){ $this->users=$users; } public function __toString(){ return $this->domain; } }
Les données ne sont accessibles qu'à condition que les informations de mappage aient été mises en cache (pour des raisons de performances) :
A partir de l'interface d'administration, choisir (Re-)init Models cache :
Cliquer sur l'onglet Structure
Cliquer sur le bouton Class diagram
La méthode __toString des classes métier permet d'afficher un objet sous forme de chaîne ; le __toString généré par défaut n'affiche pas forcément les informations adéquates d'un objet, comme le montre l'écran suivant :
Modifier la méthode __toString des classes User, Group et Organizationsettings pour obtenir le résultat suivant :
Créer un contrôleur Organizations et sa vue associée (via l'interface d'administration)
Créer un template de base :
{% block header %} <style> a.active{ font-weight: bold; color: red; } a.active::after { content: " →"; } </style> <h1>Messagerie Administration</h1> {% endblock %} {% block message %} {{ message | raw }} {% endblock %} {% block body %} {% endblock %}
Dans le contrôleur : Chargement des organisations
namespace controllers; use Ubiquity\orm\DAO; use models\Organization; /** * Controller Organizations **/ class Organizations extends \Ubiquity\controllers\ControllerBase{ public function index(){ $organizations=DAO::getAll(Organization::class); $this->loadView("Organizations/index.html",["orgas"=>$organizations]); } }
Dans la vue : affichage des organisations
<h1>Organisations</h1> <ul> {% for orga in orgas %} <li>{{orga}}</li> {% endfor %} </ul>
Créer l'action display($idOrga) dans le contrôleur Organizations à partir de l'interface d'administration :
L'accès à l'adresse /Organizations/display/2 devra permettre d'afficher l'organisation d'id 2.
Dans le contrôleur : Chargement d'une organisation par son id :
namespace controllers; use Ubiquity\orm\DAO; use models\Organization; /** * Controller Organizations **/ class Organizations extends \Ubiquity\controllers\ControllerBase{ ... public function display($idOrga){ $orga=DAO::getOne(Organization::class, $idOrga); $this->loadView("Organizations/display.html",["orga"=>$orga]); } }
Dans la vue : affichage d'une organisation
{% extends "base.html" %} {% block body %} <h2 class="ui header"> {{orga.name}} <div class="sub header">{{orga.domain}}</div> </h2> {% endblock %}
Modifier l'appel de la méthode DAO::getOne pour charger les instances de type manyToOne et oneToMany :
La méthode getOne charge dans ce cas les utilisateurs users et les groupes de l'organisation :
namespace controllers; use Ubiquity\orm\DAO; /** * Controller Organizations **/ class Organizations extends \Ubiquity\controllers\ControllerBase{ ... public function display($idOrga){ $orga=DAO::getOne(Organization::class, $idOrga, true); $this->loadView("Organizations/display.html",["orga"=>$orga]); } }
Les groupes sont affichés dans la vue display.html :
{% extends "base.html" %} {% block body %} <h2 class="ui header"> {{orga.name}} <div class="sub header">{{orga.domain}}</div> </h2> <div class="ui two columns grid"> <div class="four wide column"> <h3 class="ui header"> <i class="ui users icon"></i> <div class="content">Groupes</div> </h3> <ul> {% for groupe in orga.groupes %} <li>{{ groupe }}</li> {% endfor %} </ul> </div> <div class="twelve wide column"> <div id="users"> </div> </div> </div> {% endblock %}
Création d'une méthode retournant une liste d'utilisateurs au format HTML :
class Organizations extends \Ubiquity\controllers\ControllerBase{ ... protected function users($users=null){ $title="Tous les utilisateurs"; return $this->loadView("Organizations/users.html",compact("users","title"),true); }
Vue affichant les utilisateurs :
<h3 class="ui header"> <i class="ui user icon"></i> <div class="content">{{title}}</div> </h3> <div class="ui four columns grid"> {% for user in users %} <div class="column"><span class="ui label"><i class="ui user icon"></i> {{ user | raw }}</span></div> {% endfor %} </div>
Modification de l'action display pour qu'elle affiche les utilisateurs (appel de la méthode users précédemment créée) :
class Organizations extends \Ubiquity\controllers\ControllerBase{ ... public function display($idOrga,$idGroupe=null){ $orga=DAO::getOne(Organization::class, $idOrga,true); $users=$this->users($orga->getUsers()); $this->jquery->renderView("Organizations/display.html",["orga"=>$orga,"users"=>$users]); } }
Modification de la vue display.html pour qu'elle affiche les utilisateurs (variable users) :
... <div class="twelve wide column"> <div id="users"> {{users | raw}} </div> </div> ...
Modifier la méthode users pour qu'elle affiche éventuellement les utilisateurs d'un groupe :
class Organizations extends \Ubiquity\controllers\ControllerBase{ ... protected function users($idOrga,$idGroupe=null,$users=null){ if(isset($idGroupe)){ $group=DAO::getOne("models\\Groupe",$idGroupe,true); $title=$group->getName(); $users=DAO::getManyToMany($group, "users"); }else{ $title="Tous les utilisateurs"; } return $this->loadView("Organizations/users.html",compact("users","title"),true); }
Modifier la méthode display pour qu'elle prenne en compte l'affichage des utilisateurs d'un groupe :
... public function display($idOrga,$idGroupe=null){ $orga=DAO::getOne("models\\Organization", $idOrga,true); $users=$this->users($idOrga,$idGroupe,$orga->getUsers()); $this->jquery->renderView("Organizations/display.html",["orga"=>$orga,"users"=>$users]); } ...
Ajouter enfin des liens pour afficher les utilisateurs de chaque groupe :
... <ul> {% for groupe in orga.groupes %} <li><a href="Organizations/display/{{orga.id}}/{{groupe.id}}" data-target="#users">{{ groupe }}</a></li> {% endfor %} </ul> ...
Ouvrir la console du navigateur (Bouton droit de la souris puis inspecter ou ctrl+MAJ+c au clavier)
Activer l'onglet Réseau :
Afficher les pages :
Puis en cliquant sur les liens des groupes :
Notez les temps de chargement : environ <fc #FF0000>450 ms</fc> pour chaque page (10 requêtes)
Chaque URL recharge complétement la page, sa présentation (HTML + CSS) et ses données (organization, Groupes, Users), alors que l'accès à l'URL /Organizations/display/1/1 permettant de charger les utilisateurs du groupe 1 de l'organisation 1 ne devrait charger que ces utilisateurs (le reste ayant déjà été affiché).
L'utilisation d'AJAX (acronyme d'asynchronous JavaScript and XML) va permettre d'éviter ce surplus de chargement, en effectuant des requêtes partielles, n'affichant que les informations nouvelles.
Introduction d'ajax sur les liens vers les utilisateurs des groupes dans le contrôleur :
... public function display($idOrga,$idGroupe=null){ if(URequest::isAjax()){ echo $this->users($idOrga,$idGroupe); }else{ $orga=DAO::getOne("models\\Organization", $idOrga,true,true); $users=$this->users($idOrga,$idGroupe,$orga->getUsers()); $this->jquery->getHref("a","#users"); $this->jquery->renderView("Organizations/display.html",["orga"=>$orga,"users"=>$users]); } } ...
Dans la vue :
{% extends "base.html" %} {% block body %} <h2 class="ui header"> {{orga.name}} <div class="sub header">{{orga.domain}}</div> </h2> <div class="ui two columns grid"> <div class="four wide column"> <h3 class="ui header"> <i class="ui users icon"></i> <div class="content">Groupes</div> </h3> <ul> {% for groupe in orga.groupes %} <li><a href="Organizations/display/{{orga.id}}/{{groupe.id}}" data-target="#users">{{ groupe }}</a></li> {% endfor %} </ul> </div> <div class="twelve wide column"> <div id="users"> {{users | raw}} </div> </div> </div> {{ script_foot | raw }} {% endblock %}
Afficher à nouveau les pages :
Puis en cliquant sur les liens des groupes :
Notez les temps de chargement : pas de changements pour la première page mais environ <fc #008000>60 ms</fc> pour chaque page suivante (1 seule requête)
L'accès direct à l'url (dans la barre de navigation) peut provoquer des erreurs
Créer une vue message.html permettant d'afficher un message semantic ui :
Créer une méthode dans la classe ControllerBase permettant d'afficher un message à partir du chargement de cette vue :
... public function message($type,$header,$body,$icon="info"){ //A implémenter } ...
Modifier le template base.html pour qu'il affiche éventuellement un message :
{% block header %} <h1>Messagerie Administration</h1> {% endblock %} {% block message %} {{ message | raw }} {% endblock %} {% block body %} {% endblock %}
Ajouter la navigation de l'adresse /Organizations vers /Organizations/display/{idOrga} sur le click d'une organisation (sans Ajax).