Module M2105 - RT web dyna - TD/P 2
- Lancer le bash setup-xampp.bat du dossier de xampp
- Démarrer xampp-control
- Démarrer le serveur Apache
- Tester la réponse du serveur en allant à l'adresse http://127.0.0.1 dans un navigateur client
Fichier bat à modifier/exécuter :
set PATH=%PATH%;d:\xampp\php;%USERPROFILE%\AppData\Roaming\Composer\vendor\bin
Notions abordées
- Programmation orientée Objet
- Prise en main d'un framework
- Contrôleurs
- Formulaires/vues Twig
- Tableaux associatifs
- Utilisation de la Session Http (USession)
Installations
- php 7.1 ou supérieur (vérifier avec php -v en invite de commande)
- composer (Téléchargement)
- PhpStorm ou Eclipse PHP
Installer Ubiquity-devtools
composer global require phpmv/ubiquity-devtools
En cas de problèmes, consulter Procédures pour install de Xampp, composer, Ubiquity
Quick-start
Pour se familiariser avec le framework, faire les 2 quick-start
Création du projet
Créer le projet tp2 en invite du commande à partir du dossier htdocs de XAMPP
cd htdocs ubiquity new tp2 -a
Ouvrir/créer ce projet avec votre IDE (Eclipse ou PHPStorm)
Vérifier que les fichiers .htaccess et app/config.php font bien référence au dossier adéquat à partir du dossier htdocs de xampp :
AddDefaultCharset UTF-8 <IfModule mod_rewrite.c> RewriteEngine On RewriteBase /tp2/ RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{HTTP_ACCEPT} !(.*images.*) RewriteRule ^(.*)$ index.php?c=$1 [L,QSA] </IfModule>
<?php return array( "siteUrl"=>"http://127.0.0.1/tp2/", ...
Intro
Contrôleur et action par défaut
- Créer le contrôleur Messages, à partir de la console d'administration http://127.0.0.1/tp2/admin.
- Afficher le texte Hello world dans la méthode index() :
Tester en allant à l'adresse http://127.0.0.1/tp2/Messages : l'action par défaut d'un contrôleur est la méthode index.
Il est également possible de tester une action à partir de l'interface d'administration, en cliquant sur le bouton GET en face de l'action.
Action avec paramètre
- Créer l'action hello dans le contrôleur Messages avec le paramètre destinataire
Tester le résultat aux adresses :
- http://127.0.0.1/tp2/Messages/hello/world
- http://127.0.0.1/tp2/Messages/hello/you !
Paramètre par défaut d'une action
Ajouter une valeur par défaut au paramètre $destinataire de la méthode hello
Tester le résultat aux adresses :
- http://127.0.0.1/tp2/Messages/hello
- http://127.0.0.1/tp2/Messages/hello/world
- http://127.0.0.1/tp2/Messages/hello/you !
Création d'une action et affichage dans une vue
Créer l'action display dans le contrôleur Messages, et lui passer les paramètres message, type='info', icon='info circle',
cocher la case create associated view à la création :
Modification de l'action :
L'action doit passer les paramètres à la vue (sous forme d'un tableau associatif) :
Modifier la vue pour qu'elle affiche un message produit par Semantic-ui
Tester la page en allant aux adresses : Tester le résultat aux adresses :
- http://127.0.0.1/tp2/Messages/display/Hello
- http://127.0.0.1/tp2/Messages/display/Ceci est un message d'avertissement/warning/warning circle
Il est également possible de passer les variables en utilisant la fonction compact :
Application
Il s'agit de créer un jeu permettant de deviner un nombre généré aléatoirement.
Partie 1
Template de base
- Créer dans un seul fichier app/views/base.html un template structuré en 4 blocks de la façon suivante :
header et menu
Le block menu affichera la liste des opérations possibles :
<div class="ui secondary menu"> <a href="RandomNumberGame/index" class="active item"> Home </a> {% block menu %} {% endblock %} </div> {% block header %} <h1 class="ui header"> <i class="search icon"></i> Random Number Game </h1> <div class="ui message"> <div class="header"> Random number game </div> <p>Trouvez le nombre aléatoire.</p> </div> {% endblock %} ...
body
Le block body affichera l'opération en cours
... {% block body %} {% endblock %} ...
footer
... <div class="ui inverted segment"> {% block footer%} <a href="RandomNumberGame/termine" class="ui inverted red button"> <i class="icon stop"></i> Arrêter le jeu </a> {% endblock %} </div>
Contrôleur/actions
Contrôleur | Action | Vue | Comportement |
---|---|---|---|
RandomNumberGame | index | index.html | Appelle la méthode propose si un nombre est déjà généré en Session ou affiche le bouton nouvelle partie |
propose | propose.html | Affiche le formulaire pour effectuer une proposition | |
genere | Génère un nombre aléatoire, le sauvegarde en session, puis appelle la méthode index | ||
soumet | soumet.html | Analyse la réponse envoyée par l'utilisateur et affiche le message de réponse dans la vue | |
termine | Détruit la variable de session et appelle la méthode index |
Le contrôleur définit la constante SESSION_KEY, clé qui permettra de sauvegarder le nombre aléatoire en session :
/** * Controller RandomNumberGame **/ class RandomNumberGame extends ControllerBase{ const SESSION_KEY="random";
Notions
Pour générer un nombre aléatoire entre 1 et 10 :
$number=\mt_rand(1,10);
Pour sauvegarder le nombre en session :
USession::set(self::SESSION_KEY, $number);
Pour récupérer la variable de session :
$number=USession::get(self::SESSION_KEY);
Pour vérifier que la variable existe en session :
if(USession::exists(self::SESSION_KEY)){ //Faire quelque chose si la variable existe }
Pour récupérer la variable number postée :
$number=URequest::post("number");
Pour plus d'informations :
Ecrans
RandomNumberGame/index
Contrôleur | Action | Vue | Comportement |
---|---|---|---|
RandomNumberGame | index | index.html | Appelle la méthode propose si un nombre est déjà généré en Session ou affiche le bouton nouvelle partie |
Si aucun nombre n'est généré en session :
La vue index.html hérite de base.html et redéfinit certains blocks :
{% extends "base.html" %} {% block body %} {% endblock %} {% block footer%} <a href="RandomNumberGame/genere" class="ui inverted teal button"> <i class="ui plus icon"></i> Nouvelle partie </a> {% endblock %}
RandomNumberGame/propose
Contrôleur | Action | Vue | Comportement |
---|---|---|---|
RandomNumberGame | propose | propose.html | Affiche le formulaire pour effectuer une proposition |
RandomNumberGame/soumet
Contrôleur | Action | Vue | Comportement |
---|---|---|---|
RandomNumberGame | soumet | soumet.html | Analyse la réponse envoyée par l'utilisateur et affiche le message de réponse dans la vue |
Partie 2
On souhaite ajouter de nouvelles fonctionnalités au jeu :
- Ajout d'une limite en nombre d'essais pour deviner le nombre
- Création de niveaux de jeu :
- Facile : génération entre 1 et 10 en 5 essais
- Intermédiaire : génération entre 1 et 15 en 6 essais
- Difficile : génération entre 1 et 20 en 7 essais
- Mémorisation et affichage des parties réalisées
Classe métier
On dispose maintenant d'une classe permettant de gérer le jeu, à intégrer dans le dossier app/services du projet :
<?php namespace services; class Game { const TROUVE="trouvé"; const ABANDON="abandon"; const PERDU="perdu"; const ICONS=[self::TROUVE=>"thumbs green up",self::PERDU=>"thumbs red down",self::ABANDON=>"window orange close"]; private $number; private $min; private $max; private $essais; private $maxEssais; private $statut; private $messages; public function __construct($min,$max,$maxEssais){ $this->min=$min; $this->max=$max; $this->maxEssais=$maxEssais; $this->messages=[]; $this->essais=[]; } protected function addMessage($content,$type="error",$icon=""){ $this->messages[]=\compact("type","content","icon"); } /** * Génére un nouveau nombre aléatoire */ public function generate(){ $this->number=\mt_rand($this->min,$this->max); $this->addMessage("Un nombre aléatoire a été généré","info","info circle"); } /** * Permet de proposer un nombre, et de vérifier la proposition * @param int $number * @return boolean retourne vrai si la partie est gagnée */ public function propose($number){ if($this->reste()>0){ if(\array_search($number, $this->essais)!==false){ $this->addMessage("Vous avez déjà proposé le nombre {$number}.","info","info circle"); }else{ $this->essais[]=$number; if($this->number==$number){ $this->statut=self::TROUVE; $this->addMessage("Vous avez gagné la partie !","success","star"); return true; }else{ $this->addMessage("Ce n'est pas la bonne réponse !","error","warning circle"); } } } if($this->reste()==0){ $this->statut=self::PERDU; $this->addMessage("Il ne vous reste plus aucun essai, vous avez perdu !","info","info circle"); }else{ $this->addMessage("Il vous reste {$this->reste()} essai(s) sur {$this->maxEssais}.","info","info circle"); } return false; } /** * Retourne le nombre aléatoire généré * @return number */ public function getNumber() { return $this->number; } /** * Retourne la liste au format HTML des propositions effectuées * @return string */ public function getEssais() { $result=[]; foreach ($this->essais as $essai){ if($essai==$this->number){ $result[]="<u>{$essai}</u>"; }else{ $result[]=$essai; } } return \implode(" . ", $result); } /** * Retourne le nombre d'essais restant * @return number */ public function reste(){ return $this->maxEssais-\sizeof($this->essais); } /** * Termine la partie par abandon */ public function terminate(){ $this->statut=self::ABANDON; $this->addMessage("Vous avez abandonné.","","talk"); } /** * Retourne la liste des messages émis * @return array */ public function getMessages():array{ return $this->messages; } /** * Retoune les messages et les marque comme étant lus * @return array */ public function readMessages():array{ $result=[]; foreach ($this->messages as &$msg){ if(!isset($msg["read"])){ $msg["read"]=true; $result[]=$msg; } } return $result; } /** * Retourne le statut de la partie (gagné, perdu, abandon) * @return string */ public function getStatut():string{ if(isset($this->statut)) return $this->statut; return "?"; } public function getMaxEssais():int{ return $this->maxEssais; } /** * Retourne le nombre d'essais réalisés * @return int */ public function getNbEssais():int{ return \sizeof($this->essais); } /** * @return int */ public function getMin():int { return $this->min; } /** * @return int */ public function getMax():int { return $this->max; } public function perdu(){ return $this->statut===self::PERDU; } public function getStatutIcon(){ return self::ICONS[$this->statut]; } }