Dans ce TP nous allons créer une application web Utilisant le modèle MVC sans utiliser de framework. Néanmoins, nous allons sélectionner quelques packages disponibles afin de gérer la partie ‘frontend’ plus facilement.
Nous allons mettre en oeuvre une architecture de notre projet qui facilite la recherche des classes de notre application et qui permette l’intégration de classes de librairies tiers.
Dans le modèle MVC on retrouve :
Le modèle qui donne une représentation des données utilisées dans notre application. Les données sont généralement stockées dans une base de données. Dans notre TP nous allons utiliser le système de gestion de base de données MySQL et l’ORM Doctrine pour établir le dialogue entre notre application et la base de données. Doctrine est la librairie utilisée par Symfony.
Les vues qui affichent dans le navigateur les réponses aux requêtes. La librairie Twig sera utilisée comme librairie tiers qui génère des pages HTML à partir de template. Cela permet de séparer l’aspect visuel et le code pour le traitement. Twig est une version équivalente du moteur de template Twig utilisé dans Symfony
Le contrôleur analyse les requêtes, aiguille vers le code (Contrôleur spécifique) pour traiter la requête. Le contrôleur récupère les données nécessaires pour répondre à la requête et construit la vue qui sera renvoyée au navigateur. Dans notre cas le contrôleur utilisera le moteur de template Twig L’acteur qui permet facilement d’aiguiller la requête vers le bon contrôleur s’appelle un routeur. La librairie miladrahimi/phprouter
va nous permettre de mettre un routeur autonome et disposant de nombreuses possibilités.
A l’aide de PhpStorm, créez un nouveau projet ProjetBiblio
et créez à la racine un répertoire src
.
Créez à la racine de votre projet un fichier composer.json
à l’aide de la commande composer init
.
Ajoutez au fichier composer.json
une référence au code source de votre projet (choisir un préfixe) qui se trouvera dans le répertoire src
.
Voici un exemple de propriété autoload :
Vous devez fixer un préfixe (_vendor_) pour votre code. Par exemple \IutLens\Biblio
Ajouter à votre projet les dépendances à l’aide de la commande composer require
:
twig/twig
doctrine/orm
symfony/yaml
miladrahimi/phprouter
twbs/bootstrap
,components/jquery
,components/font-awesome
etmichelf/php-markdown
Dans cette section nous allons mettre en place la couche modèle. Notre application assurera toutes les opérations de base d’une application CRUD (_Create_, Read, Update, Delete).
Notre modèle est essentiellement composé de la classe Auteur
et Livre
dont voici le listing :
La classe Auteur
<?php
namespace IutLens\Biblio\Modele;
/**
* Class Auteur
* @package IutLens\Biblio\Modele
* @Entity @Table(name="auteurs")
*/
class Auteur {
/** @Id @Column(type="integer") @GeneratedValue **/
protected $id;
/** @Column(type="string") **/
protected $nom;
/** @Column(type="string") **/
protected $prenom;
/**
* @return mixed
*/
public function getNom() {
return $this->nom;
}
/**
* @param mixed $nom
*/
public function setNom($nom): void {
$this->nom = $nom;
}
/**
* @return mixed
*/
public function getPrenom() {
return $this->prenom;
}
/**
* @param mixed $prenom
*/
public function setPrenom($prenom): void {
$this->prenom = $prenom;
}
/**
* @return mixed
*/
public function getId() {
return $this->id;
}
La classe Livre
<?php
namespace IutLens\Biblio\Modele;
/**
* Class Livre
* @package IutLens\Biblio\Modele
* @Entity @Table(name="livres")
*/
class Livre {
/** @Id @Column(type="integer") @GeneratedValue **/
protected $id;
/** @Column(type="string") **/
protected $titre;
/** @Column(type="string") **/
protected $editeur;
/** @Column(type="date", name="date_publication") */
protected $datePublication;
/**
* @return mixed
*/
public function getId() {
return $this->id;
}
/**
* @return mixed
*/
public function getTitre() {
return $this->titre;
}
/**
* @param mixed $titre
*/
public function setTitre($titre): void {
$this->titre = $titre;
}
/**
* @return mixed
*/
public function getEditeur() {
return $this->editeur;
}
/**
* @param mixed $editeur
*/
public function setEditeur($editeur): void {
$this->editeur = $editeur;
}
/**
* @return mixed
*/
public function getDatePublication() {
return $this->datePublication;
}
/**
* @param mixed $datePublication
*/
public function setDatePublication($datePublication): void {
$this->datePublication = $datePublication;
}
}
Vous aurez noté les annotations doctrine qui permettent de faire le mapping entre le monde objet de PHP et le monde des bases de données relationnelles.
A l’aide de doctrine, il est possible de créer les tables dans la base de données.
Création d’un fichier bootstrap.php
à la racine du projet qui définit la base de données utilisée
<?php
use Doctrine\ORM\Tools\Setup;
require_once "vendor/autoload.php";
$isDevMode = true;
$config = Setup::createAnnotationMetadataConfiguration(array(__DIR__."/src"), $isDevMode);
$conn = array(
'driver' => 'pdo_mysql',
'user' => 'duchmol',
'password' => 'secret',
'host' => 'localhost',
'dbname' => 'biblio',
);
// obtaining the entity manager
$entityManager = \Doctrine\ORM\EntityManager::create($conn, $config);
Création d’un fichier cli-config.php
à la racine du projet
Doctrine met à votre disposition des commandes pour créer, modifier, supprimer les tables de la base de données.
Création des tables dans la base
Suppression des tables de la base
Modification des tables de la base
L’option --dump-sql
affiche les requêtes exécutées à l’écran.
Avec mysql
CREATE TABLE livres (id INT AUTO_INCREMENT NOT NULL, titre VARCHAR(255) NOT NULL,
editeur VARCHAR(255) NOT NULL, date_publication DATE NOT NULL,
PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB;
CREATE TABLE auteurs (id INT NOT NULL, nom VARCHAR(255) NOT NULL,
prenom VARCHAR(255) NOT NULL,
PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB;
Avec pgsql
CREATE TABLE livres (id INT NOT NULL, titre VARCHAR(255) NOT NULL,
editeur VARCHAR(255) NOT NULL,
date_publication DATE NOT NULL,
PRIMARY KEY(id));
CREATE TABLE auteurs (id INT NOT NULL, nom VARCHAR(255) NOT NULL,
prenom VARCHAR(255) NOT NULL,
PRIMARY KEY(id));
CREATE SEQUENCE livres_id_seq INCREMENT BY 1 MINVALUE 1 START 1;
CREATE SEQUENCE auteurs_id_seq INCREMENT BY 1 MINVALUE 1 START 1;
Dans le répertoire src/Modele
, ajoutez les fichiers Auteur.php
et Livre.php
.
A la racine de votre projet, créez le fichier bootstrap.php
et cli-config.php
.
A l’aide d’une commande doctrine, créez les tables auteurs
et livres
dans votre base de données.
Nous allons placer dans les tables créées des données aléatoires. Pour cela vous allez ajouter dans votre projet deux librairies supplémentaires à l’aide des commandes suivantes :
Pour automatiser la génération, nous allons utiliser un fichier de commandes load-fixtures.php
qui sera plaçé dans le répertoire racine.
<?php
/**
* Auteur: F. Hémery 20/10/2018 12:02
*
*/
require_once 'bootstrap.php';
use Doctrine\Common\DataFixtures\Executor\ORMExecutor;
use Doctrine\Common\DataFixtures\Loader;
use Doctrine\Common\DataFixtures\Purger\ORMPurger;
$loader = new Loader();
// Répertoire qui contient les fichiers de génération de données
$loader->loadFromDirectory(__DIR__.'/src/DataFixtures');
$purger = new ORMPurger();
$executor = new ORMExecutor($entityManager, $purger);
$executor->execute($loader->getFixtures());
Et créer les classes qui génèrent de façon aléatoire, des enregistrements dans les tables :
LoadAuteursData.php
dans le répertoire src/DataFixtures
<?php
namespace IutLens\Biblio\DataFixtures;
use Doctrine\Common\DataFixtures\FixtureInterface;
use Doctrine\Common\Persistence\ObjectManager;
use IutLens\Biblio\Modele\Auteur;
class LoadAuteursData implements FixtureInterface {
const NB_AUTEURS = 10;
/**
* Load data fixtures with the passed EntityManager
*
* @param ObjectManager $manager
*/
public function load(ObjectManager $manager) {
$generator = \Faker\Factory::create('fr_FR');
for ($i = 0; $i < self::NB_AUTEURS; $i++) {
$auteur = new Auteur();
$auteur->setNom($generator->lastName);
$auteur->setPrenom($generator->firstName);
$manager->persist($auteur);
}
$manager->flush();
}
}
LoadLivresData.php
dans le répertoire src/DataFixtures
<?php
namespace IutLens\Biblio\DataFixtures;
use Doctrine\Common\DataFixtures\FixtureInterface;
use Doctrine\Common\Persistence\ObjectManager;
use IutLens\Biblio\Modele\Auteur;
use IutLens\Biblio\Modele\Livre;
class LoadLivresData implements FixtureInterface {
const NB_LIVRES = 10;
/**
* Load data fixtures with the passed EntityManager
*
* @param ObjectManager $manager
*/
public function load(ObjectManager $manager) {
$generator = \Faker\Factory::create('fr_FR');
for ($i = 0; $i < self::NB_LIVRES; $i++) {
$livre = new Livre();
$livre->setTitre($generator->sentence(2,true));
$livre->setEditeur($generator->sentence(2, true));
$livre->setDatePublication($generator->dateTimeInInterval(
$startDate = '-6 months',
$interval = '+ 180 days',
$timezone = date_default_timezone_get()
));
$manager->persist($livre);
}
$manager->flush();
}
}
Ajouter des données dans les tables auteurs
et livres
en utilisant la commande `load-fixtures.php.
Nous avons de ce projet deux entités : les auteurs et les livres, nous allons ajouter une association entre ces deux entités : EcritPar
. Nous allons considérer qu’un livre est écrit par un auteur et qu’un auteur peut écrire plusieurs livres. C’est une association de type “Un vers plusieurs”. Voici le modèle de notre système d’informations :
A l’aide de l’ORM doctrine il est possible de gérer ce type d’association et d’autres, voir la documentation de doctrine.
Nous allons modifier les classes modèles pour intégrer l’association qui relie les deux entités.
Dans la classe Auteur nous allons :
Ajouter une propriété $livres
/**
* @OneToMany(targetEntity="\IutLens\Biblio\Modele\Livre", mappedBy="auteur", cascade={"persist"})
*/
protected $livres;
/**
* Auteur constructor.
*/
public function __construct() {
$this->livres = new ArrayCollection();
}
On indique que l’association est de type OneToMany, que l’association est gérée par l’autre entité et que si on enregistre l’auteur dans la table
auteurs
alors les livres associés à l’auteur seront eux aussi enregistrés dans la tablelivres
. Les livres associés à l’auteur forme une collection qui est créée par le constructeur.
Ajouter les fonctions getter
et setter
Dans la classe Livre
nous allons :
Ajouter une propriété $auteur
/**
* @ManyToOne(targetEntity="\IutLens\Biblio\Modele\Auteur", inversedBy="livres")
*/
protected $auteur;
On indique que l’association est de type ManyToOne, que l’association est visible par l’autre entité par la propriété
$livres
.
Ajouter les fonctions getter
et setter
Dans les classes qui génèrent les auteurs et les livres nous allons associer aléatoirement les livres aux auteurs.
Modifier la classe LoadLivresData
<?php
namespace IutLens\Biblio\DataFixtures;
use Doctrine\Common\DataFixtures\DependentFixtureInterface;
use Doctrine\Common\DataFixtures\FixtureInterface;
use Doctrine\Common\Persistence\ObjectManager;
use IutLens\Biblio\Modele\Livre;
class LoadLivresData implements FixtureInterface,
DependentFixtureInterface {
/**
* Load data fixtures with the passed EntityManager
*
* @param ObjectManager $manager
*/
public function load(ObjectManager $manager) {
$generator = \Faker\Factory::create('fr_FR');
$auteurs = $manager->getRepository('\IutLens\Biblio\Modele\Auteur')
->findAll();
foreach ($auteurs as $auteur) {
$nbLivres = random_int(1, 5);
for ($i = 0; $i < $nbLivres; $i++) {
$livre = new Livre();
$livre->setTitre($generator->sentence(2, true));
$livre->setEditeur($generator->sentence(2, true));
$livre->setDatePublication(
$generator->dateTimeInInterval(
$startDate = '-6 months',
$interval = '+ 180 days',
$timezone = date_default_timezone_get()
)
);
$livre->setAuteur($auteur);
$manager->persist($livre);
}
}
$manager->flush();
}
/**
* This method must return an array of fixtures classes
* on which the implementing class depends on
*
* @return array
*/
public function getDependencies() {
return ['IutLens\Biblio\DataFixtures\LoadAuteursData'];
}
}
- Ajout d’une dépendance : il faut que les auteurs existent avant de créer les livres (fonction
getDependencies()
)- Modification de la méthode
load()
. Pour chaque auteur on associe un nombre aléatoire de livres (entre 1 et 5).
Modifiez le modèle des tables auteurs
et livres
.
Ajouter des données aléatoires dans les tables auteurs
et livres
en utilisant la commande load-fixtures.php
.
Pour lister les auteurs on pourra écrire un script liste_auteurs.php
à la racine du projet qui contient le code
<?php
// liste_auteurs.php
require_once "bootstrap.php";
$auteursRepository = $entityManager->getRepository('IutLens\Biblio\Modele\Auteur');
$auteurs = $auteursRepository->findAll();
foreach ($auteurs as $auteur) {
echo sprintf("%d %s %s\n",$auteur->getId(), $auteur->getNom(), $auteur->getPrenom());
}
<?php
// update_auteur.php <id> <new-nom> <new-prenom>
require_once "bootstrap.php";
$id = $argv[1];
$newNom = $argv[2];
$newPrenom = $argv[3];
$auteur = $entityManager->find('IutLens\Biblio\Modele\Auteur', $id);
// ou
// $auteursRepository = $entityManager->getRepository('IutLens\Biblio\Modele\Auteur');
// $auteur = $auteursRepository->findOneBy(['id' => $id]);
if ($auteur === null) {
echo "Auteur $id does not exist.\n";
exit(1);
}
$auteur->setNom($newNom);
$auteur->setPrenom($newPrenom);
$entityManager->flush();
<?php
// ajoute_livre_auteur.php <auteur_id> <titre> <editeur>
use IutLens\Biblio\Modele\Livre;
require_once "bootstrap.php";
$id = $argv[1];
$titre = $argv[2];
$editeur = $argv[3];
$auteursRepository = $entityManager->getRepository('IutLens\Biblio\Modele\Auteur');
$auteur = $auteursRepository->findOneBy(['id' => $id]);
if ($auteur === null) {
echo "Auteur $id does not exist.\n";
exit(1);
}
$livre = new Livre();
$livre->setTitre($titre);
$livre->setEditeur($editeur);
$livre->setDatePublication(new DateTime('now'));
$livre->setAuteur($auteur);
$entityManager->persist($livre);
$entityManager->flush();
L’interpréteur PHP dispose d’un serveur web de développement, nous allons l’utiliser dans la suite du TP pour développer notre application web. Ce serveur web s’exécute sur votre machine (localhost
) et il attente des requêtes sur le port 8080 par défaut. C’est une version simple d’un serveur d’applications comme apache
ou encore nginx
.
Pour lancer ce serveur de développement, utilisez la commande suivante :
ou
Lance un serveur web dont la racine est le répertoire courant (le symbole ‘.
’ après l’option -t
) qui écoute sur le port 8080 ici. L’Url http://localhost:8080 va exécuter le code présent dans le fichier index.php
.
Ecrire un script listeAuteursWeb.php
qui affiche l’ensemble des auteurs contenues dans la base dans une page web.
Tester votre script http://localhost:8080/listeAuteursWeb.php.
La réalisation des vues représente un travail important lors du développement d’une application web. Ce travail se décompose en deux parties
Un site web utilise, en général, une charte, un style qui est partagé par l’ensemble des pages. Il existe des librairies qui mettent en oeuvre ce qu’on appelle un moteur de templates. L’idée est de définir une charte dans un template (un modèle) et de permettre l’ajout de paramètres (similaire au format de la fonction sprintf()
déjà utilisée, mais ici pour une page HTML).
Twig
Parmi les moteurs de templates nous avons retenu Twig car c’est celui que sera utilisé dans le framework Symfony.
Il faut ajouter le moteur Twig
dans votre projet
Configurer l’exécution du moteur de templates. Comme cette opération ne doit être faite qu’une seule fois, nous allons créer une classe Twig
qui met en oeuvre le patron de conception Singleton. Voir le listing suivant pour un exemple d’implémentation de la classe Twig
.
<?php
namespace IutLens\Biblio\Singleton;
use Twig_Environment;
use Twig_Loader_Filesystem;
class Twig {
private static $instance;
private static $twig;
private function __construct() {
$templates = __DIR__.'/../../templates';
$cache = __DIR__.'/../../cache';
$loader = new Twig_Loader_Filesystem($templates);
self::$twig = new Twig_Environment(
$loader, array(
'cache' => $cache,
)
);
}
public static function getTwig(): Twig_Environment {
if (!isset(self::$instance)) {
self::$instance = new Twig();
}
return self::$twig;
}
/**
* rend impossible le clonage
*/
private function __clone() {
}
}
Dans le code on précise que les vues (templates) seront placées dans le répertoire templates
(ici comme la classe se trouve dans le répertoire src/Singleton
pour respecter le namespace, les vues seront placées dans le répertoire templates
à la racine du projet).
Créer un template.
Un template est une page HTML classique contenant des directives Twig
qui seront interprétées par le moteur de templates. Pour avoir la liste compléte des directives, voir la documentation Twig.
Voici un exemple de template auteurs.html
qui affiche une liste des auteurs.
<!doctype html>
<html lang="fr">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="/vendor/twbs/bootstrap/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="/public/css/style.css">
<link rel="stylesheet" href="/vendor/components/font-awesome/css/fontawesome-all.css">
<title>Liste des auteurs</title>
</head>
<body>
<main role="main" class="container">
<table class="table">
<thead style="background-color: #ddd; font-weight: bold;">
<tr>
<td class="header">Id</td>
<td class="header">Nom</td>
<td class="header">Prénom</td>
</tr>
</thead>
<tbody>
{% for auteur in auteurs %}
<tr>
<td>{{ auteur.id}}</td>
<td>{{ auteur.nom}}</td>
<td>{{ auteur.prenom }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</main>
<script src="/vendor/components/jquery/jquery.min.js"></script>
<script src="/vendor/twbs/bootstrap/dist/js/bootstrap.min.js"></script>
</body>
</html>
Dans la page on utilise la directive
{% for ... %}
qui fonctionne comme l’instructionforeach
en PHP et la syntaxe{{ }}
qui permet d’afficher des données PHP dans notre template en utilisant la notation pointée.Le fichier
style.css
dans le répertoirepublic/css
contient les instructions suivantes :
Utilisation du template.
Pour utiliser le template, on appelle la méthode render
avec deux paramètres :
Voici un exemple qui utilise le template auteurs.php
Twig
Les variables permettent les échanges entre le langage PHP et le moteur de template. Il est possible d’accéder aux propriétés d’un objet en utilisant la notation pointée ou la notation clé/valeur d’un tableau associatif.
{{obj.prop}}
{{obj['prop']}}
Il est possible d’affecter une valeur à une variable dans un bloc de code :
{% set var = 'valeur' %}
{% set tab = [1,3,5] %}
Twig introduit la notion de filtre qui permet de modifier la valeur d’une variable. Les filtres peuvent s’enchaîner
{{ nom | upper }} {# Transforme en majuscule #}
{{ liste | join(', ') }} {# Transforme un tableau d'éléments en une chaîne de caractères d'éléments séparés par des virgules #}
{% filter upper %}
Ce texte sera transformé en majuscules.
{% endfilter %}
Twig propose des fonctions prédéfinies comme range()
. Voir la documentation pour avoir la liste.
Le for
<h1>Members</h1>
<ul>
{% for user in users %}
<li>{{ user.username|e }}</li>
{% endfor %}
</ul>
Le if
{% if users|length > 0 %}
<ul>
{% for user in users %}
<li>{{ user.username|e }}</li>
{% endfor %}
</ul>
{% endif %}
On inclut le fichier header.html dans le document courant. Le document inclus récupère le contexte et donc les variables.
La variable est accessible dans le composant.
Dans un template il est possible de définir des blocs. Ces blocs pourront éventuellement être remplacés dans le cas d’un héritage pour extension.
Dans le listing base.html
qui suit, les blocs head
, title
, content
et footer
sont définis.
<!DOCTYPE html>
<html>
<head>
{% block head %}
<link rel="stylesheet" href="style.css" />
<title>{% block title %}{% endblock %} - My Webpage</title>
{% endblock %}
</head>
<body>
<div id="content">{% block content %}{% endblock %}</div>
<div id="footer">
{% block footer %}
© Copyright 2018 by <a href="http://domain.invalid/">you</a>.
{% endblock %}
</div>
</body>
</html>
Le listing du fichier index.html
qui suit donne un hétitage du template base.html
.
extends
permet d’indiquer le template hétité.title
aura pour contenu Index
head
aura pour contenu celui défini dans base.html
({{ parent() }}
)content
aura pour contenu celui indiqué ici sans reprendre le contenu héritéfooter
restera le même que celui défini dans base.html
car il n’a pas été redéfini ici.{% extends "base.html" %}
{% block title %}Index{% endblock %}
{% block head %}
{{ parent() }}
<style type="text/css">
.important { color: #336699; }
</style>
{% endblock %}
{% block content %}
<h1>Index</h1>
<p class="important">
Welcome to my awesome homepage.
</p>
{% endblock %}
Nous allons mettre en place notre page standard. * Le fichier master.html
<!doctype html>
<html lang="fr">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="/vendor/twbs/bootstrap/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="/public/css/style.css">
<link rel="stylesheet" href="/vendor/components/font-awesome/css/fontawesome-all.css">
<title>Gestion de contacts</title>
</head>
<body>
{{ include('header.html') }}
<main role="main" class="container">
{% block main %}
<h1>Gestion des auteurs</h1>
{% endblock %}
</main>
{{ include('footer.html') }}
<script src="/vendor/components/jquery/jquery.min.js"></script>
<script src="/vendor/twbs/bootstrap/dist/js/bootstrap.min.js"></script>
</body>
</html>
Le fichier d’en-tête header.html
<nav class="navbar navbar-expand-md navbar-light fixed-top bg-light">
<a class="navbar-brand" href="/">Mon Application</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarCollapse" aria-controls="navbarCollapse" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarCollapse">
<ul class="navbar-nav mr-auto">
<li class="nav-item">
<a class="nav-link" href="#">A Propos</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Les contacts</a>
</li>
</ul>
</div>
</nav>
Le fichier de pied de page footer.html
Le fichier liste_auteurs.html
va permettre d’afficher la liste des auteurs.
La directive {% extends "master.html" %}
indique que l’on prend comme base le template master.html
et la bloc main
sera remplacé par le code compris entre {% block main %}
et {% endblock %}
. Le reste est inchangé.
{% extends "master.html" %}
{% block main %}
<table class="table">
<thead style="background-color: #6fb90a; font-weight: bold;">
<tr>
<td class="header">Id</td>
<td class="header">Nom</td>
<td class="header">Prénom</td>
</tr>
</thead>
<tbody>
{% for auteur in auteurs %}
<tr>
<td>{{ auteur.id}}</td>
<td>{{ auteur.nom}}</td>
<td>{{ auteur.prenom }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
Mettre à jour les dépendances dans votre projet pour permettre l’utilisation de Twig, bootstrap, ….
Voir les librairies qui sont indiquées en introduction du TP en laissant de côté pour le moment les librairies miladrahimi/phprouter
.
Créez les fichiers :
master.html
header.html
footer.html
Dans le répertoire templates
à la racine de votre projet.
Le fichier master.html
devra contenir une partie variable.
Vous pouvez partir des fichiers exemples donnés plus haut dans le texte et les adapter à votre goût
En utilisant les fichiers templates que vous venez de créer répondre aux questions suivantes :
Ecrire le script listeAuteursTwig.php
qui liste les auteurs présents dans la base de données
Ecrire le script creeAuteur.php
qui ajoute un auteur dans la base de données
Dans cette section on supposera que l’utilisateur saisit un formulaire et que l’action déclenchée valide sans contrôle l’insertion du nouvel auteur dans la base de données.
Un exemple de code :
<?php
use IutLens\Biblio\Singleton\Twig;
use IutLens\Biblio\Modele\Auteur;
require "bootstrap.php";
if (isset($_POST['ajouter'])) {
// récupèrer les données du formulaire
// créer une instance de la classe Auteur
// insérer la tâche dans la base de données
// redirige vers la page qui liste les tâches
header('location: /listeAuteursTwig.php');
} else {
$auteur = new Auteur();
echo Twig::getTwig()->render("createAuteur.html", ["auteur" => $auteur]);
}
Un extrait du script create.html
{% extends "master.html" %}
{% block main %}
<h1>Création d'un auteur</h1>
<fieldset>
<legend>Saisir d'un nouvel auteur</legend>
<form method="post" action="/creeAuteur.php">
<div class="form">
<div class="form-group">
<label for="nom">Nom</label>
<input type="text" name="nom" value="{{auteur.nom}}" class="form-control"
id="nom" placeholder="Nom">
</div>
</div>
// ... les autres champs de l'auteur
<button type="submit" class="btn btn-success" name="ajouter" value="valide">Ajouter</button>
</form>
{% endblock %}
Une application web qui met en oeuvre un modèle MVC utilise une entrée unique qui dispatche les requêtes vers la bonne partie du code à exécuter. Pour dispatcher les requêtes, un mécanisme de routage est utilisé. La encore, il existe plusieurs librairies qui propose une implémentation d’un routeur. Dans la suite du TP nous allons utiliser la librairie miladrahimi/phprouter
.
Un mécanisme de communication normalisée entre le routeur et le code à exécuter permettra de gérer les paramètres et le contenu des requêtes. La librairie zendframework/zend-diactoros
implémente la norme PSR-7 qui propose une interface pour représenter les messages (requêtes et réponses) HTTP.
Nous allons créer un script index.php
dans le répertoire racine du projet qui sera le point d’entrée de notre application.
Dans ce script nous allons créer une instance de la classe Router
proposée par la librairie miladrahimi/phprouter
. L’instance de la classe Router
nous permettra de créer des routes (aiguillages) entre une URL et une action. Par exemple :
<?php
require "vendor/autoload.php";
require_once 'src/Config.php';
use MiladRahimi\PhpRouter\Exceptions\RouteNotFoundException;
use MiladRahimi\PhpRouter\Router;
use Zend\Diactoros\Response\EmptyResponse;
$router = new Router();
/** Création des routes servies par l'application */
$router->get(
'/',
'IutLens\Biblio\Controleur\Welcome@index'
);
$router->get(
'/welcome',
'IutLens\Biblio\Controleur\Welcome@welcome'
);
try {
$router->dispatch();
} catch (RouteNotFoundException $e) {
echo "Route non connue : ".$router->currentRouteName();
$router->publish(new EmptyResponse(404));
}
Dans l’exemple deux routes ont été déclarées :
/
qui exécutera la fonction index
de la classe Welcome
./welcome
qui exécutera la fonction welcome
de la classe Welcome
.Les fonctions exécutées ont pour rôle de produire la réponse à la requête, en général une page HTML. Voici le code de ces fonctions :
<?php
namespace IutLens\Biblio\Controleur;
use IutLens\Biblio\Singleton\Twig;
use Michelf\MarkdownExtra;
class Welcome {
function index() {
return Twig::getTwig()->render("hello.html", array("visiteur" => "Robert Duchmol"));
}
public function welcome() {
$file = file_get_contents(__DIR__.'/../../public/md/welcome.md');
$parser = new MarkdownExtra();
$html = $parser->transform($file);
return Twig::getTwig()->render("info", array("content" => $html));
}
}
Dans le code donné, on utilise le moteur de templates Twig
pour produire le code HTML.
La fonction index
utilise la vue hello.html
qui utilise une variable visiteur
.
Voici le code de la vue hello.html
qui utilise les fichiers master, header, footer utilisés dans la section précédente :
La fonction welcome
utilise la vue infohtml
qui utilise une variable content
.
Ici on utilise un fichier au format markdown dans le répertoire public à la racine du projet. Le contenu du fichier welcome.md
peut être récupéré ici. On utilise la librairie michelf/php-markdown
et la classe MarkdownExtra
pour transformer le fichier welcome.md
en code HTML.
Le code du script info.html
est le suivant :
Il est possible de donner un paramètre dans la route. Ce paramètre pourra devenir un argument de la fonction associée à la route. Par exemple :
Utilise le paramètre id
qui pourra être un argument de la fonction show
Il est possible de transmettre le contenu de la requête comme argument de la fonction associée à une route. Par exemple :
Utilise le paramètre id
qui pourra être un argument de la fonction show
function update(ServerRequest $request, $id) {
return new JsonResponse(
[
'method' => $request->getMethod(),
'uri' => $request->getUri(),
'body' => $request->getBody(),
'parsedBody' => $request->getParsedBody(),
'headers' => $request->getHeaders(),
'attributes' => $request->getAttributes(),
]
);
}
Ici l’argument $request
contient les données de la requête. La fonction affiche une réponse au format Json.
Mettre à jour les dépendances dans votre projet pour permettre l’utilisation du routeur, de l’interface des messages HTTP, ….
Créer une classe AuteursController
dans le fichier AuteursController.php
du répertoire src/Controleur
Dans la classe AuteursController
créer un constructeur qui fait le lien entre la base de données et la classe
Dans la classe AuteursController
créer les fonctions :
index()
qui liste les auteurs de la base de données.show($id)
qui affiche les détails d’un auteur La fonction sera utilisée pour valider une demande de suppression d’un auteur.create()
qui affiche le formulaire de création d’un auteur.store($request)
qui vérifie la saisie du formulaire (à l’aide de la fonction valideAuteur
), ajoute l’auteur dans la base de données et renvoie vers l’affichage de la liste des auteurs si la saisie est correcte ou renvoie vers la création d’un auteur sinon.edit($id)
qui affiche le formulaire de modification d’un auteur.update($request, $id)
qui vérifie la saisie du formulaire (à l’aide de la fonction valideAuteur
), modifie l’auteur dans la base de données, renvoie vers l’affichage de la liste des auteurs si la saisie est correcte ou renvoie vers la modification d’un auteur sinon.destroy($id)
qui supprime un auteur de la base de données.valideAuteur($request, &$auteur)
qui vérifie que les champs du formulaire ont été saisis correctement (présence obligatoire des champs) et affecte les valeurs saisies dans le formulaire à la variable $auteur
donnée en argument de la fonction.Dans le répertoire templates
créer les templates :
create.html
qui affiche un formulaire de saisie d’un auteur.edit.html
qui affiche un formulaire de modification d’un auteur.show.html
qui affiche un auteur et les livres qu’il a écrit.auteurs.html
qui affiche la liste des auteurs dans la base de données.IUT de Lens Département Informatique
2018