Objectifs
L’objectif de cette activité est de travailler sur la couche modèle / données d’une application MVC, et d’expérimenter l’utilisation d’un ORM qui suit le principe des conventions.
Travail à faire
- Prendre le temps d’étudier les points communs et différence entre modèle objet et relationnel.
- Déployer le code de départ.
- Initialiser l’ORM.
- Créer les classes métiers.
- Modifier les contrôleurs.
Étude
- Étudier le cours sur l’ORM.
- Analyser le prototype des méthodes publiques (pas le code), c’est à dire
l’API de la classe
ORMdu cadriciel TeachFrame :- indiquer pour chaque opération CRUD quelle méthode utiliser et quels sont ses paramètres ;
- indiquer ce qui permet de savoir que le cadriciel suit le principe des conventions plutôt que celui des annotations.
- indiquer (et justifier) si le chargement des objets est anticipé ou paresseux.
Bien que la persistance soit assurée par une base de données relationnelle, la programmation dans le contrôleur est orientée objets.
Diagramme UML de domaine (modèle objet)
Remarque : IslandCollection correspond à Array<Island>.
Shéma relationnel
Island (id, name, population, area, idArchipelago)
clé primaire : id
clé étrangère : idArchipelago référence Archipelago(id)
Archipelago (id, name)
clé primaire : id
- Créer un nouveau dossier
ormet y extraire le code de départ, de sorte à ce que l’URL de l’application soit de la formehttps://web.sio.local/p.nom4/orm. - Corriger la directive
RewriteBasedu fichier.htaccess. - Adapter la configuration du fichier
config.yaml. - Ajouter les classes
ORMetCollectiondu cadriciel TeachFrame. - Vérifier le bon fonctionnement de l’application avant de continuer.
L’ORM contient des fonctions permettant de simplifier le point d’entrée (fichier
index.php) de l’application ; remplacer le code de ce fichier par :
<?php
require 'lib/autoloader.php';
use teachframe\Router;
use teachframe\ORM;
ORM::setConfigPDO('config.yaml');
$router = new Router();
$router->loadRoutes('routes.yaml');
$router->dispatch($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI']);
La classe ORM du cadriciel “teachframe” est importée, et toutes les instructions
de chargement du fichier de configuration et d’initialisation de PDO
sont prises en charge par la méthode setConfigPDO de l’ORM.
Créer, dans le dossier (namespace) model, les classes métiers Island
et Archipelago ; elles doivent avoir les mêmes propriétés que les champs
des tables sous-jacentes de la base de données, à ces exceptions près :
- les objets de la classe
Islandon une propriété$archipelagode typeArchipelago, contrairement à la table qui a un champidArchipelagode typeint; - les objets de la classe
Archipelagoont une propriété (supplémentaire)islandsqui doit (l’ORM l’impose) être de typeIslandCollection.
Ajouter les accesseurs pour les propriétés (getId, getName…).
Code de la classe model\IslandCollection :
<?php
namespace model;
use teachframe\Collection;
class IslandCollection extends Collection {}
Modifier les contrôleurs pour qu’ils utilisent l’ORM plutôt que des requêtes SQL pour extraire les objets de la base de données.
Ajouter un minimum de présentation (en attendant le développement de la vue) avec des liens pour la navigation entre îles et archipels.
Islandcontroller
<?php
namespace controller;
use teachframe\ORM;
use model\Island;
class IslandController {
function getAll() {
$islands = ORM::getAll(Island::class);
echo '<p>Liste des îles :</p>';
echo '<ul>'; //présentation minimaliste en attendant la vue
foreach ($islands as $island) { //pour chaque île
echo '<li><a href="' . Router::getURL('/island/' .$island->getId()) . '">' . $island->getName() . '</a></li>';
}
echo '</ul>';
}
function getOne (string $id) {
$island = ORM::getOne(Island::class, $id);
if (null == $island) {
http_response_code(404);
die('error: \'/island/' . $id . '\' not found');
}
$archipelago = $island->getArchipelago();
echo '<dl>'; //présentation minimaliste en attendant la vue
echo '<dt>Île :</dt><dd>' . $island->getName() . '</dd>';
echo '<dt>Archipel :</dt><dd><a href="' . Router::getURL('/archipelago/' . $archipelago->getId()) . '">' . $archipelago->getName() . '</a></dd>';
echo '</dl>';
echo '<p><a href="' . Router::getURL('/island/') . '">Liste des îles.</a></p>';
}
}
Dans les méthodes getOne et getAll des contrôleurs, les requêtes (query,
prepare, exec…) sont remplacés par l’utilisation des méthodes éponymes
(de même nom) de l’ORM qui renvoient des objets ou tableaux d’objets ; les
instructions de parcours de jeu d’enregistrements (fetch et fetchAll)
ne sont plus nécessaires.
Une collection (tableau) peut être parcourue par l’itérative foreach (par
exemple), et les objets sont manipulés via leurs méthodes (accesseurs notamment).
L’approche objet permet de naviguer d’une classe à l’autre (cf $island->getArchipelago())
sans avoir à gérer les concepts relationnels : clés étrangères, jointures…
Pour information, l’ORM utilisé effectue un chargement anticipé des objets
liés lorsque la méthode getOne est utilisée ; le chargement paresseux (à
la demande) n’est pas pris en charge ; par exemple, dans getAll, $island->getArchipelago()
renverrait null.
La méthode getURL de la classe Router permet de corriger les URLs
pour prendre en compte de façon transparente le dossier dans lequel l’application
est installée (p.nom4/orm par exemple) ; exemple : Router::getURL('/island/')
renvoie /p.nom4/orm/island.
ArchipelagoController
Pour la route archipelago_getone, le contrôleur doit afficher la liste des
îles de l’archipel.