Blog ENI : Toute la veille numérique !
Dernière chance (fin le 29/02) : -25€ dès 75€ sur les livres en ligne, vidéos... code FUSEE25. J'en profite !
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici
  1. Livres et vidéos
  2. PHP et MySQL
  3. Corrigé 3
Extrait - PHP et MySQL Entraînez-vous à développer une application collaborative
Extraits du livre
PHP et MySQL Entraînez-vous à développer une application collaborative Revenir à la page d'achat du livre

Corrigé 3

Prérequis

1.

b.

La réponse b est évidemment la bonne. À part les propositions fantaisistes a et c, la réponse d correspond en fait au langage Java dont PHP s’inspire en partie pour définir son modèle objet.

2.

c.

La réponse c est juste, les autres sont fausses ou empruntées à d’autres langages.

3.

a., b. et d.

Trois réponses sont satisfaisantes. L’indexation des moteurs de recherche a été l’une des premières applications de ce mécanisme, en rendant les URL compréhensibles. Un exemple couramment cité est le classement d’articles pour un journal en ligne. Les URL explicitent certains mots-clés (international, sciences…) ainsi que la date de parution (2019/08/24) et même le nom de l’article (le-langage-php-prepare-sa-revolution). Dans un domaine plus technique, les URL contiennent des paramètres pour accéder à des objets dans des collections indexées retournées par des services web. Elles peuvent également intégrer des jetons de session ou d’authentification qui évitent le recours à des cookies. La réponse c ne correspond pas à la question, la forme des URL n’ayant aucun lien avec les performances du site.

4.

c.

Le vocabulaire consacre le terme « action » pour désigner les traitements situés...

Corrigé 3.1 Affichage de la page d’accueil en MVC

 La ligne est située en début de fichier avec d’autres constantes :

FPLGlobal::$namespace = "teamup\\controllers\\"; 

 Nous avons défini un contrôleur implémentant une action. Cette action redirige vers la vue index et lui transmet l’objet $model comme paramètre d’entrée.

/* 
 * homeController.php 
 */ 
namespace teamup\Controllers\general; 
 
use comfpl\controllers\BaseController; 
use comfpl\views\PartialViewResult; 
 
/** 
 * 
 */ 
class homeController extends BaseController 
{ 
   public function index($model) 
   { 
       $model->message = "Bienvenue"; 
       return $this->View($model, "index"); 
   } 
} 

 Par rapport aux TP précédents, l’utilisation du template de présentation est beaucoup plus simple. Il n’y a pas à copier-coller de contenu, on se trouve directement dans la section <body> :

<?php //{layout:../_mainLayout.php}?><?php $model=FPLGLobal:: 
$view_result->model; ?> 
<h2>Accueil</h2> 
<div><?php echo $model->message?></div> 

Le résultat de ces premiers...

Corrigé 3.2 Réaliser la page de connexion en MVC

 Voici le code complet du contrôleur. Les lignes importantes sont la déclaration du namespace, de la classe et de l’action, faute de quoi le routeur ne réussira pas à appeler le traitement spécifié dans l’URL :

<?php 
namespace testapp\Controllers\usermgt; 
 
use comfpl\controllers\BaseController; 
require_once 'models/loginmodel.php'; 
 
class userController extends BaseController 
{ 
   function Login($view_model) { 
        return $this->View($model,"login"); 
   } 
} 

 Il n’y a qu’à insérer dans le script login.php le code sans besoin de le personnaliser.

 Le framework FPL analyse les annotations de la classe loginModel. Comme les champs sont marqués par l’attribut Required, le framework vérifie si ces champs sont valorisés.

images/C03TP02.png

 La route par défaut est située en haut du fichier config.php.

    FPLGlobal::$default_route="general-home-index.do"; 

Les logins de démonstration se trouvent plutôt à la fin du fichier de configuration :

    FPLGlobal::$mockup_credentials=array(new Credentials
('Geeck','go'),new Credentials('John','doe')); 

Après...

Corrigé 3.3 Activer l’authentification

 L’annotation étant appliquée au niveau de la classe contrôleur, toutes les actions suivent la même règle sauf à la modifier ponctuellement. Autrement dit, l’utilisateur ne peut pas être anonyme pour exécuter une action du contrôleur home.

/** 
 * @Autorisation(access_control_mode=Autorisation::DENY_ANONYMOUS) 
 */ 
class homeController extends BaseController 

 Si le code du TP 2.5 peut être repris quasiment sans modification, il faut bien faire correspondre les routes pour d’une part afficher la page listusers.php et d’autre part invoquer le service web dans le bon contrôleur. Les lignes importantes sont en gras dans les extraits de code qui suivent.

Voici d’abord le contrôleur :

/** 
 * @Autorisation(access_control_mode=Autorisation::DENY_ANONYMOUS) 
 */ 
class homeController extends BaseController 
{ 
   public function index($model) 
   { 
       $model->message = "Bienvenue"; 
       return $this->View($model, "index"); 
   } 
    
   /** 
    * Obtient la liste paginée des utilisateurs 
    * @param int $page 
    * @param int $limit ...

Corrigé 3.4 Brancher l’authentification sur la base de données

 D’autres méthodes de la classe Security pouvant accéder à la base de données, le constructeur est adapté sur le modèle de UserDAO pour récupérer la chaîne de connexion. En revanche, ce n’est pas une bonne chose que de s’appuyer sur la classe UserDAO pour implémenter la vérification des crédits utilisateurs. En effet, UserDAO est un composant du répertoire application alors que la classe Security dépend du framework.

private $db_connection; 
 
public function __construct() { 
   $this->db_connection = get_default_connection(); 
} 

Dans le corps de la méthode check_credentials(), l’instruction try / catch / finally a été utilisée pour faciliter la libération des ressources et la fermeture de la connexion à la base de données. Le bloc finally est toujours appelé, qu’une exception soit déclenchée ou non. En l’occurrence, même si l’utilisateur est reconnu, le bloc finally sera exécuté avant le return $id. Ce TP étant prévu pour PHP 7, c’est bien la classe Throwable qui doit être utilisée dans le bloc catch et non plus Exception (valable en PHP 5).

/** 
 * 
 * {@inheritdoc} ...

Corrigé 3.5 Tables SQL pour les demandes et les classes de données

 Pas de difficulté pour créer les deux tables à l’aide de l’interface de gestion phpMyAdmin.

images/C03TP07.png

 Les deux entités reprennent fidèlement la structure des tables :

/* 
 * typedemandeentity.php 
 */ 
class typeDemandeEntity { 
   public $id_type_demande; 
   public $type_demande_label; 
} 

et :

/* 
 * demandeentity.php 
 */ 
class demandeEntity { 
   public $id_demande; 
   public $demande_objet; 
   public $demande_texte; 
   public $demande_date_creation; 
   public $demande_date_echeance; 
   public $id_type_demande; 
   public $id_utilisateur; 
} 

 Seule la classe TypeDemandeDAO est reproduite ici :

/* 
 * typedemandedao.php 
 */ 
require_once 'config.php'; 
require_once 'models/typedemandeentity.php'; 
 
class TypeDemandeDAO { 
   private $db_connection; 
 
   public function __construct() { 
       $this->db_connection = get_default_connection(); 
   } 
 
   /** 
    * Obtient la liste des types de demandes 
    * @return typeDemandeEntity[] 
    */ 
   public function gettypedemandelist(){ 
       $cx = mysqli_connect($this->db_connection["cx_server"], 
           $this->db_connection["cx_login"], 
           $this->db_connection["cx_pwd"], 
 ...

Corrigé 3.6 Saisir des demandes

 Une zone est matérialisée par un répertoire à son nom sous controllers et sous views.

images/C03TP08.png

 À la création du contrôleur, l’espace de noms doit être défini en fonction de la zone. Nous avons également ajouté l’annotation @autorisation pour requérir l’authentification des utilisateurs.

/* 
 * demandeController.php 
 */ 
namespace teamup\Controllers\demandes; 
 
use comfpl\controllers\BaseController; 
 
/** 
 * @Autorisation(access_control_mode=Autorisation::DENY_ANONYMOUS) 
 */ 
class demandeController extends BaseController 
{ 
   public function index($model) 
   { 
       return $this->View($model, "index"); 
   } 
} 

 Grâce au template, le contenu de la vue est très concis :

<?php //{layout:../_mainLayout.php}?><?php $model=FPLGLobal:: 
$view_result->model; ?> 
<h2>Liste des demandes</h2> 

La copie d’écran ci-après montre que la mise en page est appliquée avec très peu de code :

images/C03TP09.png

 L’action est définie par une méthode à son nom :

public function adddemande($view_model){ 
   return $this->View($view_model, "adddemande"); 
} 

La vue adddemande.php reprend la référence du template de mise en page :

<?php //{layout:../_mainLayout.php}?><?php $model=FPLGLobal:: 
$view_result->model; ?> 
<h4>Nouvelle demande</h4> 

Pour insérer le lien, nous avons utilisé la méthode html::action_link() qui génère une balise <a href> à partir des paramètres indiqués :

<div class="container"> 
   <div class="row"> 
       <div class="col"><h4>Liste des demandes</h4></div> 
       <div class="col"><?php html::action_link('demandes', 
'demande', 'adddemande', 'Nouvelle demande')?></div> 
   </div> 
</div> 

 En PHP, le type...

Corrigé 3.7 Affichage d’une demande

 Le code reprend la logique de l’action adddemande :

public function editdemande($view_model) { 
   $tdservice = new \TypeDemandeService(); 
 
   $vm = new \demandeViewModel(); 
   $vm->demande = new \demandeEntity(); 
   $vm->demande->demande_date_creation = date("d/m/Y"); 
 
   $vm->typeDemandelist = array(); 
   foreach($tdservice->gettypedemandelist() as $td) { 
       $vm->typeDemandelist[$td->id_type_demande]=$td->type_demande_label; 
   } 
 
   return $this->View($vm,"editdemande"); 
} 

 La déclaration de l’annotation @Route précède la définition de la méthode :

/** 
 * @Route(parameters="id-{id}") 
 */ 
public function editdemande($view_model) { 

Et nous devons ajouter un peu plus bas :

$vm->id = $view_model->id; 

 Le code de la vue editdemande.php...

<?php //{layout:../_mainLayout.php}?><?php $model=FPLGLobal:: 
$view_result->model; ?> 
<h4>ID <?php echo $model->id ?></h4>   

… restitue bien la valeur du paramètre passé dans l’URL :

images/C03TP14.png

 Nous avons d’abord implémenté...

Corrigé 3.8 Liste des demandes

 Ces deux fonctions sont désormais un classique du genre, et voilà une variation sur le DAO à utiliser.

function getdemandedata($page=null,$limit=null){ 
   $demandeservice = new \DemandeService(); 
   $p = $demandeservice->getlistdemandes(); 
 
   $r=array(); 
   if($page!=null && $limit!=null) 
   { 
       $start = ($page - 1) * $limit; 
   } else { 
       $start = 0; 
       $limit = count($p); 
   } 
   for($i = $start; $i < $start + $limit && $i < count($p); $i++) 
       $r[] = $p[$i]; 
 
   return array("records" => $r, "total" => count($p)); 
} 
 
/** 
* @return \comfpl\views\JsonResult 
*/ 
public function listdemandedata($model) { 
   $page = isset($model->page) ? $model->page : null; 
   $limit = isset($model->limit) ? $model->limit : null; 
 
   $p = $this->getdemandedata($page,$limit); 
   $json_dde_list = json_encode($p); 
 
   return new JsonResult($json_dde_list); 
} 

 Attention car les fonctions d’encodage JSON sont très...

Corrigé 3.9 Pour aller plus loin : gérer un workflow

 Après avoir ajouté la propriété dans l’entité demandeEntity, c’est la requête SQL qui doit être adaptée en réalisant une jointure à gauche. En effet, certains enregistrements de la table demande ont NULL pour valeur id_utilisateur. Avec une jointure de type inner join, ces enregistrements auraient été écartés.

// construit la requête 
$query = "select demande.*,utilisateur.utilisateur_nom from demande left 
join utilisateur on demande.id_utilisateur = utilisateur.id_utilisateur"; 
 
// exécute la requête 
$result = mysqli_query($cx, $query); 
 
$list = array(); 
while( ($row=mysqli_fetch_assoc($result))!=null) { 
   $demande = new DemandeEntity(); 
   $demande->id_demande = $row["id_demande"]; 
   $demande->demande_objet = $row["demande_objet"]; 
   $demande->demande_texte = $row["demande_texte"]; 
   $demande->demande_date_creation = $row["demande_date_creation"]; 
   $demande->demande_date_echeance = $row["demande_date_echeance"]; 
   $demande->id_type_demande = $row["id_type_demande"]; 
   $demande->id_utilisateur = $row["id_utilisateur"]; 
   $demande->utilisateur_nom = $row["utilisateur_nom"]; 

 L’ajout de la colonne tient dans sa déclaration :

{ field: 'utilisateur_nom',title:'Utilisateur' }, 

La quatrième colonne indique maintenant le nom de l’utilisateur assigné à la demande....

Corrigé 3.10 Finaliser la page de liste des demandes

 Copiez-collez dans le fichier listdemandes.php le contenu de la vue index.php, hors référencement de la page template :

<div class="row"> 
   <div class="col"><h4>Liste des demandes</h4></div> 
   <div class="col"><?php html::action_link('demandes', 'demande', 
'adddemande', 'Nouvelle demande')?></div> 
</div> 

 La route comprend la zone, le contrôleur et l’action :

{"route":"demandes-demande-listdemandes.do","label":
"Demandes","tag":"","icon":"collective"} 

 Le script s’ajoute à la collection en instanciant la classe ScriptItemBundle :

new scriptItemBundle("https://unpkg.com/gijgo@1.9.13/js/messages/
messages.fr-fr.js") 

 La propriété locale s’insère dans le constructeur grid() :

   grid = $('#grid').grid({ 
  primaryKey: 'id_utilisateur', 
 locale: 'fr-fr', 
  dataSource: '<?php html::action_href("demandes" , "demande", 
"listdemandedata")?>', 
  uiLibrary: 'bootstrap4', 
  columns: [ 
  { field:...