You are on page 1of 43

Programmation Web côté serveur

Support de cours (Ebauche)

Tamba KOLANI
Master Génie Logiciel
Email : koltemb@gmail.com
Programme
Éléments de syntaxe langage PHP
Base du langage
PHP orienté Objet
PDO (PHP Data Objects) et bases de données
Architecture d’un site
Les PSR
PHP et Design patterns
modèle MVC, contrôleur unique
Introduction a AJAX
Les frameworks (Symfony, Laravel, ...)
Application Web Restful

Evaluation
Examen final : 40%
Projet : 60%
Éléments de syntaxe langage PHP
Architecture client-serveur
Une architecture client-serveur représente l’environnement dans lequel des
applications de machines clientes communiquent avec des applications de machines
de type serveurs.
L’exemple classique est le navigateur Web d’un client qui demande (on parle de
“requête”) le contenu d’une page Web à un serveur Web qui lui renvoie le résultat
(on parle de “réponse”).
 Le client : C’est le visiteur d’un site Web. Il demande la page Web au serveur.
En pratique, vous êtes des clients quand vous surfez sur le Web. Plus
précisément c’est votre navigateur Web (Firefox, Chrome, Safari, IE, Edge,
…) qui est le client car c’est lui qui demande la page Web.
 Le serveur : Ce sont les ordinateurs qui délivrent les sites Web aux
internautes, c’est-à-dire aux clients.

Figure 1: Architecture client-serveur

Ce type d’échange utilise le protocole http (HyperText Transfer Protocol).


 L’utilisateur saisit une URL (Uniform Resource Locator) dans le navigateur
qui transmet la requête vers le serveur en utilisant le protocole HTTP.
 Le serveur analyse la requête, la traite et renvoie vers le navigateur sa réponse,
le plus souvent dans un format texte structuré (HTML, JSON, TEXT, XML,
etc.). La réponse peut contenir aussi des images, du son, des vidéos et du code
dans le langage JavaScript qui sera exécuté par un interpréteur dans le
navigateur.
Pour réaliser une application web, on utilise des langages qui s’exécutent coté serveur.
Il s’agit par exemple de PHP, Java (JavaEE), python (Django), Perl, Ruby etc. Dans
le cadre de notre cours nous utiliserons PHP 7 ou une version ultérieure.
Syntaxe dans PHP
Notion de variables
Il est pratique d'avoir parfois des noms de variables qui sont variables. C'est-à-dire
un nom de variable qui est affecté et utilisé dynamiquement. Une variable classique
est affectée avec l'instruction suivante
<?php
$a = "Bonjour";

echo $a; // Affichera Bonjour

?>

Une variable dynamique prend la valeur d'une variable et l'utilise comme nom d'une
autre variable. On utilise pour cela "$$"
<?php
$a = "Bonjour";
$$a = "SUPINFO";

echo $a. ' '.$Bonjour; // Affichera Bonjour SUPINFO

?>

Notez qu’e PHP, les variables sont typées dynamiquement.

Notion de fonction
Une fonction peut être définie en utilisant la syntaxe suivante
<?php
function mafonction($arg_1, $arg_2, /* ..., */ $arg_n)
{
echo "Exemple de fonction.\n";
return $valeur;
}

?>

Ici on peut typer les variables passées en argument afin de forcer à entrer les valeurs
convenable aux paramètres lors de l’appel de la fonction
<?php
function mafonction(int $arg_1, int $arg_2){
$valeur = $arg_1 + $arg_2;
return $valeur;
}
?>

Notion de tableau
Les tableaux en PHP peuvent s’indexer par des entiers ou des chaînes de caractères
Pour créer un tableau vide, on utilise la syntaxe
<?php
$mon_tableau = array();
?>

On peut créer le tableau case par case :


<?php
$coordonnees['prenom'] = 'François';
$coordonnees['nom'] = 'Dupont';
?>

Note : Le tableau $coordonnees contient plusieurs associations. Par exemple, il


associe à la chaîne de caractères 'prenom' la chaîne de caractères 'François'.
Dans cette association, 'prenom' s’appelle la clé (ou index) et 'François' la valeur.
On peut aussi initialiser le tableau comme ceci
<?php
$coordonnees = array (
'prenom' => 'François',
'nom' => 'Dupont'
);
?>

On peut rajouter facilement un élément “à la fin” d’un tableau avec


<?php
$mon_tableau[] = "Nouvelle valeur";
?>

Notez l’existence des boucles foreach pour parcourir les paires clé/valeur des
tableaux.
<?php
foreach ($mon_tableau as $cle => $valeur){
//commandes
}
?>

La boucle foreach va boucler sur les associations du tableau. Pour chaque association,
foreach va mettre la clé de l’association dans la variable $cle et la valeur dans $valeur
puis exécuter les commandes. Par exemple
<?php
foreach ($coordonnees as $cle => $valeur){
echo "$cle : $valeur\n";
}
?>

Remarque : La boucle foreach est indispensable pour parcourir les indices et valeurs
d’un tableau indexé par des chaînes de caractères.
Il existe aussi bien sûr une boucle for classique si le tableau est indexé uniquement
par des entiers
<?php
for ($i = 0; $i < count($mon_tableau); $i++) {
echo $mon_tableau[$i];
}
?>

Pour comprendre foreach autrement, le code suivant


<?php
foreach ($mon_tableau as $cle => $valeur){
//commandes
}
?>
est équivalent à
<?php
for ($i = 0; $i < count(array_keys($mon_tableau)); $i++) {
$cle = array_keys($mon_tableau)[$i];
$valeur = $mon_tableau[$cle];
//commandes
}
?>
La programmation objet en PHP
PHP était initialement conçu comme un langage de script, mais est passé Objet à
partir de la version 5.
<?php
class Voiture {

private $marque;
private $couleur;
private $immatriculation;

// un constructeur
public function __construct($m, $c, $i) {
$this->marque = $m;
$this->couleur = $c;
$this->immatriculation = $i;
}

// un getter
public function getMarque() {
return $this->marque;
}

// un setter
public function setMarque($m) {
$this->marque = $m;
}

/*
Les autres getters et setters
*/

// une methode d'affichage.


public function afficher() {
// À compléter
}
}
?>

A la différence avec Java par exemple :


 Pour accéder à un attribut ou une fonction d’un objet, on utilise le -> au lieu
du point (.)
 Le constructeur ne porte pas le nom de la classe, mais s’appelle __construct().
 En PHP, on ne peut pas avoir deux fonctions avec le même nom, même si
elles ont un nombre d’arguments différent. En particulier, il ne peut y avoir au
maximum qu’un constructeur.

Interaction avec un formulaire


Créer deux fichiers .php : index.php et reponse.php. Tester l’envoie de données via
un formulaire avec les méthodes GET et POST
Méthode GET

Méthode POST

PDO (PHP Data Objects) et bases de données


L'extension PHP Data Objects (PDO) définit une excellente interface pour accéder
à une base de données depuis PHP. Chaque pilote de base de données implémenté
dans l'interface PDO peut utiliser des fonctionnalités spécifiques de chacune des
bases de données en utilisant des extensions de fonctions. Notez que vous ne pouvez
exécuter aucune fonction de base de données en utilisant l'extension PDO par elle-
même ; vous devez utiliser un driver PDO spécifique à la base de données pour
accéder au serveur de base de données.
PDO fournit une interface d'abstraction à l'accès de données, ce qui signifie que vous
utilisez les mêmes fonctions pour exécuter des requêtes ou récupérer les données
quel que soit la base de données utilisée. PDO est fourni avec PHP.

Connexions et gestionnaire de connexion


Les connexions sont établies en créant des instances de la classe de base de PDO.
Peu importe quel pilote vous voulez utiliser ; vous utilisez toujours le nom de la classe
PDO. Le constructeur accepte des paramètres pour spécifier la source de la base de
données (connue en tant que DSN) et optionnellement, le nom d'utilisateur et le mot
de passe (s'il y en a un).
<?php
$db = new PDO('mysql:host=localhost;dbname=test', $user, $pass);
?>
S'il y a des erreurs de connexion, un objet PDOException est lancé. Vous pouvez
attraper cette exception si vous voulez gérer cette erreur de la manière suivante :
<?php
try {
$db = new PDO('mysql:host=localhost;dbname=test', $user, $pass);
foreach($dbh->query('SELECT * from FOO') as $row) {
print_r($row);
}
$db = null;
} catch (PDOException $e) {
print "Erreur !: " . $e->getMessage() . "<br/>";
die();
}
?>

Lorsque la connexion à la base de données a réussi, une instance de la classe PDO


est retournée à votre script. La connexion est active tant que l'objet PDO l'est. Pour
clore la connexion, vous devez détruire l'objet en vous assurant que toutes ses
références sont effacées. Vous pouvez faire cela en assignant null à la variable gérant
l'objet. Si vous ne le faites pas explicitement, PHP fermera automatiquement la
connexion lorsque le script arrivera à la fin.
Beaucoup d'applications web utilisent des connexions persistantes aux serveurs de
base de données. Les connexions persistantes ne sont pas fermées à la fin du script,
mais sont mises en cache et réutilisées lorsqu'un autre script demande une connexion
en utilisant les mêmes paramètres. Le cache des connexions persistantes vous permet
d'éviter d'établir une nouvelle connexion à chaque fois qu'un script doit accéder à
une base de données, rendant l'application web plus rapide.
<?php
$db = new PDO('mysql:host=localhost;dbname=test', $user, $pass, array(
PDO::ATTR_PERSISTENT => true
));
?>

Requêtes préparées
La plupart des bases de données supportent le concept des requêtes préparées. C’est
une sorte de modèle compilé pour le SQL que vous voulez exécuter, qui peut être
personnalisé en utilisant des variables en guise de paramètres. Les requêtes préparées
offrent deux fonctionnalités essentielles :
 La requête ne doit être analysée (ou préparée) qu'une seule fois, mais peut être
exécutée plusieurs fois avec des paramètres identiques ou différents. Lorsque
la requête est préparée, la base de données va analyser, compiler et optimiser
son plan pour exécuter la requête. Pour les requêtes complexes, ce processus
peut prendre assez de temps, ce qui peut ralentir vos applications si vous devez
répéter la même requête plusieurs fois avec différents paramètres. En utilisant
les requêtes préparées, vous évitez ainsi de répéter le cycle
analyse/compilation/optimisation. Pour résumer, les requêtes préparées
utilisent moins de ressources et s'exécutent plus rapidement.
 Les paramètres pour préparer les requêtes n'ont pas besoin d'être entre
guillemets ; le pilote gère cela pour vous. Si votre application utilise
exclusivement les requêtes préparées, vous pouvez être sûr qu'aucune
injection SQL n'est possible (Cependant, si vous construisez d'autres parties
de la requête en vous basant sur des entrées utilisateurs, vous continuez à
prendre un risque).
Les requêtes préparées sont tellement pratiques que c'est l'unique fonctionnalité que
PDO émule pour les pilotes qui ne les prennent pas en charge. Ceci assure de pouvoir
utiliser la même technique pour accéder aux données, sans se soucier des capacités
de la base de données.
Exemples
<?php
$stmt = $dbh-
>prepare("INSERT INTO REGISTRY (name, value) VALUES (:name, :value)");
$stmt->bindParam(':name', $name);
$stmt->bindParam(':value', $value);

// insertion d'une ligne


$name = 'one';
$value = 1;
$stmt->execute();

// insertion d'une autre ligne avec des valeurs différentes


$name = 'two';
$value = 2;
$stmt->execute();
?>

L’exemple suivant substitue un nom et une valeur pour les marqueurs point
d’interrogation (?).
<?php
$stmt = $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (?, ?)");
$stmt->bindParam(1, $name);
$stmt->bindParam(2, $value);

// insertion d'une ligne


$name = 'one';
$value = 1;
$stmt->execute();

// insertion d'une autre ligne avec différentes valeurs


$name = 'two';
$value = 2;
$stmt->execute();
?>

Cet exemple récupère des données basées sur la valeur d'une clé fournie par un
formulaire. L'entrée utilisateur est automatiquement échappée, il n'y a donc aucun
risque d'attaque par injection SQL.
<?php
$stmt = $dbh->prepare("SELECT * FROM REGISTRY where name = ?");
$stmt->execute([$_GET['name']]);
foreach ($stmt as $row) {
print_r($row);
}
?>

Les erreurs et leur gestion


PDO vous offre 3 façons différentes de gérer les erreurs afin de mieux s'adapter à
votre application.
 PDO::ERRMODE_SILENT
Antérieur à PHP 8.0.0, c'est le mode par défaut. PDO définit simplement le code
d'erreur à inspecter grâce aux méthodes PDO::errorCode() et PDO::errorInfo() sur
les objets représentant les requêtes, mais aussi ceux représentant les bases de
données; si l'erreur résulte d'un appel à l'objet représentant la requête, vous pouvez
appeler la méthode PDOStatement::errorCode() ou la méthode
PDOStatement::errorInfo() sur l'objet. Si l'erreur résulte d'un appel sur l'objet
représentant une base de données, vous pouvez également appeler ces deux mêmes
méthodes sur l'objet.
 PDO::ERRMODE_WARNING
En plus de définir le code d'erreur, PDO émettra un message E_WARNING
traditionnel. Cette configuration est utile lors des tests et du débogage, si vous voulez
voir le problème sans interrompre l'application.

 PDO::ERRMODE_EXCEPTION
À partir de PHP 8.0.0, c'est le mode par défaut. En plus de définir le code d'erreur,
PDO lancera une exception PDOException et y définit les propriétés afin de
représenter le code d'erreur et les informations complémentaires. Cette configuration
est également utile lors du débogage, car elle va « contourner » le point critique de
votre code, vous montrer rapidement le problème rencontré (souvenez-vous : les
transactions sont automatiquement annulées si l'exception fait que votre script se
termine).
Le mode "exception" est également très utile car ainsi, vous pouvez structurer votre
gestionnaire d'erreur plus clairement qu'avec les alertes traditionnelles PHP et, ce,
avec moins de code que lorsque vous exécutez votre code en mode silence, et que
vous vérifiez systématiquement les valeurs retournées après chaque appel à la base
de données.
Exemples
<?php
$dsn = 'mysql:dbname=testdb;host=127.0.0.1';
$user = 'dbuser';
$password = 'dbpass';

try {
$dbh = new PDO($dsn, $user, $password);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
echo 'Échec lors de la connexion : ' . $e->getMessage();
}
?>

Création d’une instance PDO et définition du mode d'erreur depuis le constructeur


PDO::__construct() va toujours lancer une exception PDOException si la
connexion échoue suivant la configuration de PDO::ATTR_ERRMODE. Les
exceptions non attrapées deviennent des erreurs fatales.
<?php
$dsn = 'mysql:dbname=test;host=127.0.0.1';
$user = 'user';
$password = 'password';

/*
L'utilisation des blocs try/catch autour du constructeur est toujours
valide
même si nous définissons le ERRMODE à WARNING sachant que PDO::__construct
va toujours lancer une exception PDOException si la connexion échoue.
*/
try {
$dbh = new PDO($dsn, $user, $password, array(PDO::ATTR_ERRMODE => PDO:
:ERRMODE_WARNING));
} catch (PDOException $e) {
echo 'Échec de la connexion : ' . $e->getMessage();
exit;
}

// Ceci fera que PDO lancera une erreur de niveau E_WARNING au lieu d'une
exception (lorsque la table n'existe pas)
$dbh->query("SELECT wrongcolumn FROM wrongtable");
?>

L'exemple ci-dessus va afficher

Warning: PDO::query(): SQLSTATE[42S02]: Base table or view not found:


1146 Table 'test.wrongtable' doesn't exist in /tmp/pdo_test.php on line 18

Exercice
Créer une classe Etudiant et réaliser le CRUD avec PDO. On réaliser une interface
web pour cela.

La recommandation standard PHP (PSR)


La PHP Standard Recommendation (PSR) est une spécification PHP publiée par le
PHP Framework Interop Group. Semblable à Java Specification Request for Java, il
sert à la standardisation des concepts de programmation en PHP. L'objectif est de
permettre l'interopérabilité des composants et de fournir une base technique
commune pour la mise en œuvre de concepts éprouvés pour des pratiques de
programmation et de test optimales. Le PHP-FIG est formé par plusieurs fondateurs
de frameworks PHP.

Chaque PSR est suggéré par les membres et voté selon un protocole établi pour agir
de manière cohérente et conformément aux processus convenus.

Nombre Nom La description Éditeur Coordinateur Parrainer Statut

Il décrit les exigences


Norme de obligatoires qui doivent être Obsolète et
PSR-0 chargement respectées pour N / A N/A N/A remplacée
automatique l'interopérabilité du chargeur par PSR-4
automatique.
Il comprend ce qui devrait
être considéré comme les
Norme de éléments de codage standard
PSR-1 codage de qui sont nécessaires pour N / A N/A N/A Accepté
base assurer un niveau élevé
d'interopérabilité technique
entre le code PHP partagé.
Il prend en compte le PSR-1
et vise à réduire les frictions
cognitives lors de la
Guide de numérisation de code de
PSR-2 style de différents auteurs. Il le fait en N / A N/A N/A Obsolète
codage énumérant un ensemble
partagé de règles et d'attentes
sur la façon de formater le
code PHP.
Il décrit une interface
Interface de commune pour les Jordi
PSR-3 N/A N/A Accepté
l'enregistreur bibliothèques de Boggiano
journalisation.
Il décrit une spécification
pour le chargement
automatique de classes à
partir de chemins de
fichiers. Il est entièrement
interopérable et peut être
Norme de
utilisé en plus de toute autre Paul M. Phil Larry
PSR-4 chargement Accepté
spécification de chargement Jones Sturgeon Garfield
automatique
automatique, y compris PSR-
0. Ce PSR décrit également
où placer les fichiers qui
seront chargés
automatiquement selon les
spécifications.
PSR-1 - Standard de base de codage
Cette section liste ce qui doit être condidéré comme un standard de codage qui est
requis pour garantir un haut niveau d'intéropérabilité pour le code PHP partagé.

Les mots-clés “DOIT”, “NE DOIT PAS”, “REQUIS”, “PEUT”, “POURRAIT NE


PAS”, “DEVRAIT”, “NE DEVRAIT PAS”, “RECOMMANDE” et
“OPTIONNEL” dans ce document correspondent à la terminologie décrite dans la
RFC 2119.
Résumé
 Les fichiers DOIVENT utiliser uniquement les balises <?php et <?= .
 Les fichiers DOIVENT utiliser uniquement du code PHP en UTF-8 sans
BOM.
 Les fichiers DEVRAIENT déclarer des symboles (classes, fonctions,
constantes, etc.) OU causer un effet de bord (générer une sortie, modifier les
paramètres .ini, etc.) mais ne DEVRAIENT PAS faire les deux.
 Les Namespaces et classes DOIVENT suivre un PSR “autoloading” [PSR-0,
PSR-4].
 Les noms de classes DOIVENT être déclarés en UpperCamelCase.
 Les constantes de classes DOIVENT être déclarées en majuscules avec un
séparateur underscore.
 Les noms de Méthodes DOIVENT être déclarés en camelCase.

Fichiers
Balises PHP
Le code PHP DOIT utiliser la balise longue <?php ?> ou la balise courte echo <?=
?> ; Il NE DOIT PAS utiliser d'autre variation.
Encodage de caractères
Le fichier PHP DOIT utiliser UNIQUEMENT l'encodage UTF-8 sans BOM.
Effets de bord
Un fichier DEVRAIT déclarer de nouveaux symboles (classes, fonctions, constantes,
etc.) et ne pas causer d'autre effet de bord, ou il DEVRAIT exécuter de la logique
avec effet de bord, mais NE DEVRAIT PAS faire les deux.
Les termes “effet de bord” signifient l'exécution de logique qui n'est pas directement
liée à une déclaration de classe, fonction, constante...etc., seulement en incluant le
fichier.
“Effets de bord” inclut mais n'est pas limité à : génération de sortie, utilisation
explicite de require ou include, se connecter à un service externe, modifier les
paramètres ini, émettre des erreurs ou exceptions, modifier des variables globales ou
statiques, lire ou écrire dans un fichier...
L'exemple ci-dessous est un exemple contenant déclaration et effet de bord, un
exemple de ce qu'il faut éviter :
<?php
// effet de bord : modification des paramètres ini
ini_set('error_reporting', E_ALL);
// effet de bord : chargement d'un fichier
include "file.php";
// effet de bord : génère une sortie
echo "<html>\n";
// déclaration
function foo(){
// code de la fonction
}

PHP
L'exemple suivant correspond à un fichier qui contient des déclarations mais pas
d'effets de bord :
<?php
// déclaration
function foo()
{
// code de la fonction
}
// déclaration conditionnelle n'est *pas* un effet de bord
if (! function_exists('bar')) {
function bar()
{
// code de la fonction
}
}
PHP
Noms de Namespace et Class
Les Namespaces et classes DOIVENT suivre un PSR "autoloading” [PSR-0, PSR-
4].
Cela signifie que chaque classe est un fichier par lui-même, et se trouve dans un
namespace d'au moins un niveau, un nom de "vendor".
Les noms de classes DOIVENT être déclarés en UpperCamelCase.
Par exemple:

<?php
namespace Vendor\Model;
class Foo
{
}

PHP
Constantes, Propriétés et Méthodes de classes
Le terme “classe” se réfère à toutes les classes, interfaces et traits.
Constantes
Les constantes de classes DOIVENT être déclarées en majuscules avec séparateur
underscore. Par exemple :
<?php
namespace Vendor\Model;
class Foo
{
const VERSION = '1.0';
const DATE_APPROVED = '2012-06-01';
}

PHP
Propriétés
Le guide officiel évite de façon volontaire toute recommandation concernant
l'utilisation de $UpperCamelCase, $camelCase, ou $under_score pour les noms de
propriétés.
Méthodes
Les noms de méthodes DOIVENT être déclarés en camelCase().

PSR-2
Il existait le PSR-2, qui indiquait quelques bonnes pratiques de codage pour les
variables et fonctions. Ce standard a été déprécié à ce stade.

PSR-4 - Autoloader
Les mots-clés “DOIT”, “NE DOIT PAS”, “REQUIS”, “PEUT”, “POURRAIT NE
PAS”, “DEVRAIT”, “NE DEVRAIT PAS”, “RECOMMANDE” et
“OPTIONNEL” dans ce document correspondent à la terminologie décrite dans la
RFC 2119.
Résumé
Cette PSR décrit une spécification pour mettre en place l'autoloading de classes
depuis un chemin de fichiers. Il est totalement interopérable, et peut être utilisé en
complément de tout autre standard d'autoloading, incluant PSR-0. Cette PSR décrit
également où placer les fichiers qui seront chargés automatiquement en accord avec
la spécification.
Spécification
Le terme “classe” se réfère à toutes les classes, interfaces et traits, et autres structures
similaires.
Un nom de classe totalement qualifié a la forme suivante :
\<NamespaceName>(\<SubNamespaceNames>)*\<ClassName>
1. Un nom de classe totalement qualifié DOIT avoir un namespace principal,
également appelé “vendor namespace”.
2. Un nom de classe totalement qualifié PEUT avoir un ou plusieurs noms de
"sous namespaces".
3. Un nom de classe totalement qualifié DOIT se terminer par un nom de classe.
4. Les underscores n'ont pas de signification dans le nom de classe totalement
qualifié.
5. Les caractères alphabétiques dans le nom de classe totalement qualifié
PEUVENT être toute combinaison de majuscules et minuscules.
6. Tous les noms de classes DOIVENT être référencés dans un modèle sensible
à la casse.
7. Lors du chargement d'un fichier qui correspond à un nom de classe totalement
qualifié…
8. Une série continue d'un ou plusieurs namespaces et sous namespaces, sans
compter le séparateur d'espace de noms principal, dans le nom de classe
pleinement qualifié (un «préfixe d'espace de noms») correspond à au moins un
«répertoire de base».
9. Les noms de sous-namespaces contigus après le «préfixe d'espace de noms»
correspondent à un sous-répertoire dans un «répertoire de base», dans lequel
les séparateurs d'espaces de noms représentent des séparateurs de répertoires.
Le nom du sous-répertoire DOIT correspondre à la casse des noms de sous-
namespaces.
10. Le nom de la classe correspond à un nom de fichier se terminant par .php. Le
nom de fichier DOIT correspondre à la casse du nom de classe.
11. Les implémentations d'autoloader NE DOIVENT PAS lancer d'exceptions,
NE DOIVENT PAS déclencher d'erreurs de quelque niveau que ce soit et
NE DEVRAIENT PAS renvoyer de valeur.

PHP et Design patterns (modèle MVC)


Les patrons de conception sont des solutions classiques à des problèmes récurrents
de la conception de logiciels. Ce sont des sortes de plans ou de schémas que l’on peut
personnaliser afin de résoudre un problème récurrent dans notre code.
Vous ne pouvez pas vous contenter de trouver un patron et de le recopier dans votre
programme comme vous le feriez avec des fonctions ou des librairies prêtes à
l’emploi. Un patron, ce n’est pas un bout de code spécifique, mais plutôt un concept
général pour résoudre un problème précis. Vous pouvez suivre le principe du patron
et implémenter une solution qui convient à votre propre programme.
Que trouve-t-on dans un patron de conception ?
La majorité des patrons sont présentés de façon très générale, afin qu’ils soient
reproductibles dans tous les contextes. Voici les différentes sections que vous
retrouverez habituellement dans la description d’un patron :
 L’Intention du patron permet de décrire brièvement le problème et la solution.
 La Motivation explique en détail la problématique et la solution offerte par le
patron.
 La Structure des classes montre les différentes parties du patron et leurs
relations.
 L’Exemple de code écrit dans un des langages de programmation les plus
populaires facilite la compréhension générale de l’idée derrière le patron.
De manière générale il existe trois grands groupes de patron de conception :
Patrons de création : ils fournissent des mécanismes de création d’objets qui
augmentent la flexibilité et la réutilisation du code. Il s’agit du design fabrique,
fabrique abstraite, monteur, prototype et singleton
Les patrons structurels : ils guident pour assembler des objets et des classes en
de plus grandes structures tout en gardant celles-ci flexibles et efficaces. Il
s’agit du design adaptateur, pont, composite, décorateur, façade, poids
mouche et procuration
Les patrons comportementaux : ils s’occupent des algorithmes et de la
répartition des responsabilités entre les objets. Il s’agit du design itérateur,
chaîne de responsabilité, commande, médiateur memento, observateur, état,
stratégie, patron de méthode et visiteur.

Le modèle MVC (Modèle-Vue-Contrôleur)


Le modèle MVC décrit une manière d'architecturer une application informatique en
la décomposant en trois sous-parties :
 la partie Modèle ;
 la partie Vue ;
 la partie Contrôleur.
Ce modèle de conception (« design pattern ») a été imaginé à la fin des années 1970
pour le langage Smalltalk afin de bien séparer le code de l'interface graphique de la
logique applicative

Rôle des composants


La partie Modèle d'une architecture MVC encapsule la logique métier (« business
logic ») ainsi que l'accès aux données. Il peut s'agir d'un ensemble de fonctions
(Modèle procédural) ou de classes (Modèle orienté objet).
La partie Vue s'occupe des interactions avec l'utilisateur : présentation, saisie et
validation des données.
La partie Contrôleur gère la dynamique de l'application. Elle fait le lien entre
l'utilisateur et le reste de l'application.
Figure 2: Modèle MVC

Le modèle MVC fonctionne selon le principe suivant :


1. Une demande de l'utilisateur (exemple : requête HTTP) est reçue et interprétée
par le Contrôleur.
2. Celui-ci utilise les services du Modèle afin de préparer les données à afficher.
3. Ensuite, le Contrôleur fournit ces données à la Vue, qui les présente à
l'utilisateur (par exemple sous la forme d'une page HTML).
Une application construite sur le principe du MVC se compose toujours de trois
parties distinctes. Cependant, il est fréquent que chaque partie soit elle-même
décomposée en plusieurs éléments. On peut ainsi trouver plusieurs modèles,
plusieurs vues ou plusieurs contrôleurs à l'intérieur d'une application MVC.

Le routeur

Dans la structure MVC, un seul et unique fichier est le point d'entrée de l'application,
quelle que soit la page affichée. Il est systématiquement appelé, et envoie la demande
au bon contrôleur. Il est chargé de trouver le bon chemin pour que l'utilisateur
récupère la bonne page, d'où le nom de routeur.

Cas pratique

Notre aura la structure suivante :

Figure 3: Structure de projet

 .htaccess et index.php : ces fichiers vont servir à créer notre routeur


 app : ce dossier contiendra le cœur de l'application
 controllers : contiendra, comme son nom l'indique, les contrôleurs, dont le
nom commencera par une majuscule, par convention. Les contrôleurs sont
des classe php
 models : contiendra nos modèles, leur nom commencera également par une
majuscule. Ce sont également les classes php.
 views : contiendra nos fichiers de vues, dans des dossiers, un dossier par
contrôleur.

Le point d’entrée : le routeur

Comme indiqué précédemment, le point d'entrée de notre application est le routeur.


Il s'agit "tout simplement" de notre fichier « index.php » situé à la racine publique de
notre projet. Ce routeur va nous servir à identifier quel contrôleur doit être utilisé
pour générer la page demandée.

Voici un routeur simple, qui contient les adresses comme ci-dessous.

http://url_du_site/controleur/methode
Cette url permettra à notre routeur de comprendre qu'il doît pointer vers le
contrôleur mentionné en premier paramètre et la méthode de ce contrôleur
mentionnée en deuxième paramètre.

Le fichier .htaccess

Afin de parvenir au fonctionnement élucidé ci-haut, nous devons utiliser la réécriture


d'URL proposée par les serveurs Apache au moyen d'un fichier « .htaccess ». Il faut
pour cela créer ce fichier à la racine publique de notre projet, comme « index.php ».

Dans ce fichier nous ajouter ces lignes

RewriteEngine On

RewriteRule ^([a-zA-Z0-9\-\_\/]*)$ index.php?p=$1

 RewriteEngine On : permet de démarrer la réécriture d'URL


 RewriteRule : permet de définir une règle de réécriture d'URL et fonctionne
comme suit
^([a-zA-Z0-9\-\_\/]*)$ : il s'agit des différents caractères
(REGEX) pris en compte dans l'URL pour sa réécriture
a-z : caractères minuscules
A-Z : caractères majuscules
0-9 : chiffres
\-\_\/ : tiret, underscore et / (caractère \ pour l'échappement)
Tout ceci entre ^( pour le début de chaîne et )$ pour la fin de
chaîne
index.php?p=$1 : $1 contiendra le résultat de la réécriture notre
chaîne

Au finale, une URL de la forme :http://url_du_site/articles/lire

Donnera : http://url_du_site/index.php?p=articles/lire

Le fichier index.php

Dans ce fichier il faut gérer les données de l'URL. Ce fichier sera mis à jour au fur et
à mis de ce cours

Les classes principales

Avant d'écrire notre routeur, nous allons appeler dans ce fichier le contrôleur et le
modèle principaux qui serviront de base commune à tous les fichiers.
Afin d'assurer la portabilité du projet sur toutes les configurations, nous allons baser
nos appels sur un chemin généré automatiquement. Nous allons le sauvegarder dans
une constante que nous appellerons "ROOT".

Cette constante sera générée depuis une des informations stockées dans la variable
super globale "$_SERVER" qui contient le chemin complet vers notre fichier.

Nous y enlèverons uniquement le nom de fichier "index.php" au moyen de la


fonction php "str_replace".

Dans le fichier index.php, ajouter le code suivant :

define('ROOT', str_replace('index.php','',$_SERVER['SCRIPT_FILENAME']));

On peut maintenant utiliser "ROOT" pour appeler nos fichiers comme ceci :

require_once(ROOT.'app/Model.php');

require_once(ROOT.'app/Controller.php');

Les paramètres d'URL


Commençons par la gestion des paramètres d'URL. Dans l'exemple ci-dessus nous
avons deux paramètres qui sont envoyés à notre routeur, un contrôleur et une
méthode ou action.

Il faudra garder en tête qu'une action dans l'URL correspondra à une méthode
(fonction) dans un contrôleur.

Pour commencer, nous devons récupérer chacun des paramètres, s’ils existent, et les
affecter à des variables. En effet, la page d'accueil n'aura pas de paramètre par
exemple (http://nom_dusite/).

Nous allons utiliser la fonction explode de php pour séparer chacun des paramètres
et générer un tableau. Puis nous allons tester les valeurs et enfin affecter les variables,
si besoin.

Le code ainsi écrit sera le suivant :


<?php
// On sépare les paramètres et on les met dans le tableau $params
$params = explode('/', $_GET['p']);
// Si au moins 1 paramètre existe
if($params[0] != ""){
// On sauvegarde le 1er paramètre dans $controller en mettant sa 1ère
lettre en majuscule
$controller = ucfirst($params[0]);
// On sauvegarde le 2ème paramètre dans $action si il existe, sinon in
dex
$action = isset($params[1]) ? $params[1] : 'index';
}else{
// Ici aucun paramètre n'est défini
}

?>

Le routeur est maintenant capable de lire une URL. Il faut maintenant diriger la
demande vers le bon contrôleur et dans ce contrôleur vers la bonne méthode. Etant
donné que nous développons en PHP orienté objet, chaque contrôleur correspondra
à une classe.

Dans la suite, nous manipulerons des articles. La page d'accueil des articles en
affichera la liste complète. Nous aurons donc une méthode "index" qui accèdera à
cette liste.

Si nous souhaitions accéder à cette méthode, sans routeur, nous écririons le code
suivant

<?php
require_once(ROOT.'controllers/Articles.php');
$articles = new Articles();
$articles->index();

?>

Ces 3 lignes permettent d'instancier la classe "Articles" et d'appeler la méthode


"index" de la classe Articles().

Avec notre routeur, nous avons ces informations dans des variables :

 $controller correspond à "Articles"


 $action correspond à "index"

Nous pourrions donc écrire

<?php
require_once(ROOT.'controllers/'.$controller.'.php');
$controller = new $controller();
$controller->$action();
?>

Notre routeur devient donc :


<?php
// On sépare les paramètres et on les met dans le tableau $params
$params = explode('/', $_GET['p']);
// Si au moins 1 paramètre existe
if($params[0] != ""){
// On sauvegarde le 1er paramètre dans $controller en mettant sa 1ère
lettre en majuscule
$controller = ucfirst($params[0]);
// On sauvegarde le 2ème paramètre dans $action si il existe, sinon in
dex
$action = isset($params[1]) ? $params[1] : 'index';
// On appelle le contrôleur
require_once(ROOT.'controllers/'.$controller.'.php');
// On instancie le contrôleur
$controller = new $controller();

// On appelle la méthode
$controller->$action();
}else{
// Ici aucun paramètre n'est défini
}
?>

Il nous reste maintenant à gérer les erreurs et l'absence de paramètres. Une erreur,
c'est un contrôleur ou une action qui n'existent pas.

Nous avons une fonction php qui nous permet de vérifier si une méthode existe dans
une classe. Cette fonction s'appelle "method_exists()" et prend deux paramètres,
la classe ou une instance de la classe (ici le contrôleur) et la méthode (action). Intégrée
à notre routeur, on a donc :

<?php
if(method_exists($controller, $action)){
// On appelle la méthode
$controller->$action();
}else{
// On envoie le code réponse 404
http_response_code(404);
echo "La page recherchée n'existe pas";
}
?>

En cas d'absence de paramètres, nous appellerons un contrôleur par défaut que nous
appellerons "Main" et sa méthode "index".

Nous ajouterons donc en fin de fichier, dans le "else", les lignes suivantes
<?php
// Ici aucun paramètre n'est défini
// On appelle le contrôleur par défaut
require_once(ROOT.'controllers/Main.php');

// On instancie le contrôleur
$controller = new Main();
// On appelle la méthode index
$controller->index();
?>

A cet stade le contenu de notre routeur est le suivant :


<?php
// On génère une constante contenant le chemin vers la racine publique du
projet
define('ROOT', str_replace('index.php','',$_SERVER['SCRIPT_FILENAME']));
// On appelle le modèle et le contrôleur principaux
require_once(ROOT.'app/Model.php');
require_once(ROOT.'app/Controller.php');
// On sépare les paramètres et on les met dans le tableau $params
$params = explode('/', $_GET['p']);
// Si au moins 1 paramètre existe
if($params[0] != ""){
// On sauvegarde le 1er paramètre dans $controller en mettant sa 1ère
lettre en majuscule
$controller = ucfirst($params[0]);
// On sauvegarde le 2ème paramètre dans $action si il existe, sinon in
dex
$action = isset($params[1]) ? $params[1] : 'index';
// On appelle le contrôleur
require_once(ROOT.'controllers/'.$controller.'.php');
// On instancie le contrôleur
$controller = new $controller();
if(method_exists($controller, $action)){
// On appelle la méthode
$controller->$action();
}else{
// On envoie le code réponse 404
http_response_code(404);
echo "La page recherchée n'existe pas";
}
}else{
// Ici aucun paramètre n'est défini
// On appelle le contrôleur par défaut
require_once(ROOT.'controllers/Main.php');
// On instancie le contrôleur
$controller = new Main();
// On appelle la méthode index
$controller->index();
}
?>

Les contrôleurs
Ils se situent entre la base de données et les vues et sont chargés de demander et
traiter les données avant de les envoyer vers les vues.
Le contrôleur principal
Le contrôleur principal est le contrôleur qui contiendra les méthodes nécessaires à
tous les autres. Ceci nous évitera de répéter les mêmes méthodes plusieurs fois.
Il sera situé dans le dossier "app" et s'appellera "Controller.php".
Nous allons commencer par un contrôleur vide mais nous devrons rapidement y
inclure du contenu.
<?php
abstract class Controller{
}
?>

Cette classe étant abstraite, on ne pourra donc pas l’instancier, mais elle sera utilisée
par héritage dans tous les autres contrôleurs.
Le contrôleur "Articles"

Ce contrôleur permettra de gérer les pages des articles. Il héritera du contrôleur


principal et se chargera de traiter les informations et de les passer aux vues.

Créons ce contrôleur avec une action par défaut qui est la méthode "index"

<?php
class Articles extends Controller{
/**
* Cette méthode affiche la liste des articles
* @return void
*/
public function index(){
echo "Ici nous aurons la liste des articles";
}
}
?>
Accéder ensuite à l’url suivant : http://url_du_site/articles
Nous allons donc créer les contrôleurs en suivant ce modèle, mais nous aurons
besoin de données, nous devrons donc charger des fichiers "model" pour pouvoir y
accéder.
Les modèles

Les modèles nous permettront d'accéder à la base de données. La première chose à


faire est donc d'initialiser une connexion avec cette base. Nous allons le faire dans le
modèle principal.

Le modèle principal

Le modèle principal sera une classe abstraite "Model" qui sera incluse par héritage
dans tous nos modèles. Il servira principalement à initialiser la connexion à la base
de données.

<?php
abstract class Model{
// Informations de la base de données
private $host = "localhost";
private $db_name = "nom_de_la_base";
private $username = "nom_utilisateur";
private $password = "mot_de_passe";
// Propriété qui contiendra l'instance de la connexion
protected $_connexion;
// Propriétés permettant de personnaliser les requêtes
public $table;
public $id;
/**
* Fonction d'initialisation de la base de données
* @return void
*/
public function getConnection(){
// On supprime la connexion précédente
$this->_connexion = null;
// On essaie de se connecter à la base
try{
$this->_connexion = new PDO("mysql:host=".$this-
>host . ";dbname=" . $this->db_name, $this->username, $this->password);
$this->_connexion->exec("set names utf8");
}catch(PDOException $exception){
echo "Erreur de connexion : " . $exception->getMessage();
}
}
}
?>
Les méthodes communes

Il peut être utile de créer des méthodes communes à tous les modèles. Par
exemple, il sera fréquent de vouloir obtenir un unique enregistrement ou au
contraire tous les enregistrements d'une table donnée. Nous avons 2 propriétés
publiques (protégé de préférence) "table" et "id" qui nous permettront de créer ces
requêtes.

Nous allons les appeler "getOne" pour un enregistrement et "getAll" pour tous les
enregistrements.

Voici le code correspondant

/**
* Méthode permettant d'obtenir un enregistrement de la table choisie en f
onction d'un id
*
* @return void
*/
public function getOne(){
$sql = "SELECT * FROM ".$this->table." WHERE id=".$this->id;
$query = $this->_connexion->prepare($sql);
$query->execute();
return $query->fetch();
}
/**
* Méthode permettant d'obtenir tous les enregistrements de la table chois
ie
*
* @return void
*/
public function getAll(){
$sql = "SELECT * FROM ".$this->table;
$query = $this->_connexion->prepare($sql);
$query->execute();
return $query->fetchAll();
}

Ces méthodes seront donc disponibles depuis tous les modèles.

Le modèle "Article"

Une classe "Articles" existant déjà pour un contrôleur, nous allons nommer notre
modèle "Article.php" et le stocker dans "models". Cette classe "Article" va hériter
du modèle principal.
Nous allons devoir utiliser le constructeur pour instancier la base de données dès
que le modèle sera lui-même instancié. Pour ce faire nous allons ajouter la méthode
magique "__construct"

public function __construct(){


// Nous définissons la table par défaut de ce modèle
$this->table = "articles";
// Nous ouvrons la connexion à la base de données
$this->getConnection();
}

Interaction entre contrôleurs et modèles

Notre contrôleur "Articles" nécessite l'affichage des derniers articles lors de l'appel
de la méthode "index".

Nous allons donc devoir demander à notre contrôleur d'utiliser le modèle "Article"
et de récupérer tous les enregistrements.

Par extension, il sera courant de devoir instancier un modèle depuis un


contrôleur. Nous allons donc commencer par ajouter une méthode dans
notre contrôleur principal qui nous permettra de charger n'importe quel
modèle à tout moment.

Cette méthode sera appelée "loadModel" et contiendra ce code

/**
* Permet de charger un modèle
*
* @param string $model
* @return void
*/
public function loadModel(string $model){
// On va chercher le fichier correspondant au modèle souhaité
require_once(ROOT.'models/'.$model.'.php');
// On crée une instance de ce modèle. Ainsi "Article" sera accessible
par $this->Article
$this->$model = new $model();
}

Dans tous les contrôleurs il nous suffira d'appeler un modèle de cette façon
$this->loadModel('Article');

Notre contrôleur "Articles" va maintenant devoir demander à notre modèle la liste


de tous les articles.
Nous allons donc instancier le modèle "Article" puis utiliser la méthode "getAll"
pour obtenir cette liste. Nous allons juste, pour l'instant, faire un "var_dump" de
cette liste pour vérifier que ça fonctionne.

Le code sera le suivant

/**
* Cette méthode affiche la liste des articles
*
* @return void
*/
public function index(){
// On instancie le modèle "Article"
$this->loadModel('Article');

// On stocke la liste des articles dans $articles


$articles = $this->Article->getAll();

// On affiche les données


var_dump($articles);
}

Les vues

L'intérêt de toute application web est d'afficher les données de façon propre et
structurée aux utilisateurs. Nous allons donc devoir générer du code html qui
contiendra les données de la base de données.

Pour ce faire, nous allons créer des vues. Ces fichiers contiendront principalement
du HTML mais également un peu de PHP pour afficher les données.

Pour commencer, nous allons créer un dossier "articles" dans "views" et un premier
fichier "index.php" à l'intérieur. Nous utiliserons comme "convention" de nommer
les fichiers de vue de la même façon que la méthode qui les appellera.

Ce fichier index.php devra donc être appelé par le contrôleur.

Révision du contrôleur principal

Pour simplifier les choses, nous allons créer une méthode d'affichage des
vues dans le contrôleur principal. Cette méthode permettra de charger toutes les
vues, quel que soit le contrôleur qui va y faire appel.
Pour y parvenir nous utiliserons la fonction php "get_class" qui permet de récupérer
la classe du contrôleur. Nous la passerons en minuscule, convention utilisée pour les
noms de dossiers, et l'intègrerons au chemin d'accès au fichier de vues.

La méthode d'affichage s'appelle assez souvent "render", nous utiliserons donc ce


nom. Elle devra également récupérer en entrée le nom de la vue, et les données,
s’il y en a.

Cette méthode s'écrira donc comme ceci

/**
* Afficher une vue
*
* @param string $fichier
* @param array $data
* @return void
*/
public function render(string $fichier, array $data = []){
// Récupère les données et les extrait sous forme de variables
extract($data);

// Crée le chemin et inclut le fichier de vue


require_once(ROOT.'views/'.strtolower(get_class($this)).'/'.$fichier.'
.php');
}

La fonction php "extract" permet de prendre un tableau et de l'éclater en


différentes variables.
Ainsi, avec ce tableau
<?php
$data = [
'id' => 1,
'contenu' => 'Ceci est le contenu'
];
?>

Après avoir fait un "extract" nous aurons


<?php
$id = 1;
$contenu = 'Ceci est le contenu';
?>
Nous pourrons donc utiliser ces variables dans notre vue.

Appeler la vue depuis le contrôleur

Revenons sur le contrôleur "Articles" et sa méthode "index".

Cette méthode devra appeler la vue "index" et lui passer les articles sous forme de
tableau.

Nous allons donc appeler la méthode "render" et lui faire passer les informations.

Les données devront être passées sous forme de tableau. Nous pourrons utiliser la
fonction php "compact" pour créer le tableau.

Sans la fonction compact, nous écrirons

<?php
$this->render('index', ['articles' => $articles]);
?>

Son équivalent avec la fonction compact() est :

<?php
$this->render('index', compact('articles'));
?>

La méthode index devient donc

<?php
/**
* Cette méthode affiche la liste des articles
*
* @return void
*/
public function index(){
// On instancie le modèle "Article"
$this->loadModel('Article');
// On stocke la liste des articles dans $articles
$articles = $this->Article->getAll();

// On envoie les données à la vue index


$this->render('index', compact('articles'));
}
?>
Le fichier de vue

Passons maintenant à l'affichage de nos données. Nous devons créer du code HTML
et y insérer les données envoyées par le contrôleur.

Les articles contiennent un titre, un texte et un "slug"(version simplifiée du titre


utilisé dans les urls).

Pour afficher les données, nous allons devoir boucler sur la variable "articles" et
afficher les informations à chaque boucle.

On peut procéder comme ceci

<?php foreach($articles as $article): ?>


<h2><?= $article['titre'] ?></h2>
<p><?= $article['contenu'] ?></p>
<?php endforeach ?>

A ce stade, la liste des articles présents en base de données est affichée.

Cependant, il nous manque une chose, le lien pour aller lire l'article. Nous allons donc
devoir créer une nouvelle "route" pour y accéder, et cette route devra prendre un
paramètre complémentaire. Pour la lecture d'un article, nous définirons la route
comme suit

http://url_du_site/articles/lire/slug-de-l-article

Notre vue finalisée sera donc la suivante

<?php foreach($articles as $article): ?>


<h2><a href="/articles/lire/<?= $article['slug'] ?>"><?= $article['titre']
?></a></h2>
<p><?= $article['contenu'] ?></p>
<?php endforeach ?>

Les routes avec paramètres

Pour accéder à la lecture d'un article, nous avons besoin de passer un paramètre
supplémentaire à notre routeur, le "slug" de l'article.

Le routeur

Le problème est que notre routeur actuel ne prend que 2 paramètres. Comment
faire pour en ajouter un 3ème ?
Nous allons utiliser la fonction php "call_user_func_array" qui permet d'appeler
une fonction en lui faisant passer des paramètres sous forme de tableau. Cette
fonction prend en premier paramètre une classe et une méthode sous forme de
tableau.

Au niveau du routeur on aura :

<?php
...
call_user_func_array([$controller,$action], $params);
// la ligne ci-dessus remplacera la ligne ci-dessous
$controller->$action();
...
?>

Le problème qui se pose maintenant est que les paramètres sont en doublon, pour
les 2 premiers. Il faudra donc commencer par les supprimer. On utilise pour cela la
méthode unset() qui permet de supprimer un élément du tableau.
Notre routeur devient donc :
<?php
// On génère une constante contenant le chemin vers la racine publique du
projet
define('ROOT', str_replace('index.php','',$_SERVER['SCRIPT_FILENAME']));
// On appelle le modèle et le contrôleur principaux
require_once(ROOT.'app/Model.php');
require_once(ROOT.'app/Controller.php');
// On sépare les paramètres et on les met dans le tableau $params
$params = explode('/', $_GET['p']);
// Si au moins 1 paramètre existe
if($params[0] != ""){
// On sauvegarde le 1er paramètre dans $controller en mettant sa 1ère
lettre en majuscule
$controller = ucfirst($params[0]);
// On sauvegarde le 2ème paramètre dans $action si il existe, sinon
index
$action = isset($params[1]) ? $params[1] : 'index';
// On appelle le contrôleur
require_once(ROOT.'controllers/'.$controller.'.php');
// On instancie le contrôleur
$controller = new $controller();

if(method_exists($controller, $action)){
// On supprime les 2 premiers paramètres
unset($params[0]);
unset($params[1]);

// On appelle la méthode $action du contrôleur $controller


call_user_func_array([$controller,$action], $params);
}else{
// On envoie le code réponse 404
http_response_code(404);
echo "La page recherchée n'existe pas";
}
}else{
// Ici aucun paramètre n'est défini
// On appelle le contrôleur par défaut
require_once(ROOT.'controllers/Main.php');

// On instancie le contrôleur
$controller = new Main();

// On appelle la méthode index


$controller->index();
}

?>

Le modèle

Nous pouvons donc maintenant créer la méthode "lire" avec un paramètre "slug"
pour aller chercher un article.

Mais nous n'avons pas encore de méthode pour aller chercher un article à partir de
son slug, ce qui nous pose problème. Créons pour cela cette méthode dans le
modèle "Article". Cette méthode prendra en entrée un "slug" et retournera un
enregistrement de la base de données.

Le modèle "Article" sera donc mis à jour comme ceci

<?php
class Article extends Model{
public function __construct()
{
// Nous définissons la table par défaut de ce modèle
$this->table = "articles";

// Nous ouvrons la connexion à la base de données


$this->getConnection();
}
/**
* Retourne un article en fonction de son slug
*
* @param string $slug
* @return array()
*/
public function findBySlug(string $slug){
$sql = "SELECT * FROM ".$this->table." WHERE `slug`='".$slug."'";
$query = $this->_connexion->prepare($sql);
$query->execute();
return $query->fetch(PDO::FETCH_ASSOC);
}

Le contrôleur

Notre modèle étant à jour, nous pouvons maintenant créer la méthode "lire" dans
le contrôleur "Articles".

Cette méthode s'écrira comme suit

<?php
/**
* Méthode permettant d'afficher un article à partir de son slug
*
* @param string $slug
* @return void
*/
public function lire(string $slug){
// On instancie le modèle "Article"
$this->loadModel('Article');

// On stocke l'article dans $article


$article = $this->Article->findBySlug($slug);

// On envoie les données à la vue lire


$this->render('lire', compact('article'));
}
?>

Notre contrôleur devient :


<?php
class Articles extends Controller{
/**
* Cette méthode affiche la liste des articles
*
* @return void
*/
public function index(){
// On instancie le modèle "Article"
$this->loadModel('Article');
// On stocke la liste des articles dans $articles
$articles = $this->Article->getAll();
// On envoie les données à la vue index
$this->render('index', compact('articles'));
}
/**
* Méthode permettant d'afficher un article à partir de son slug
*
* @param string $slug
* @return void
*/
public function lire(string $slug){
// On instancie le modèle "Article"
$this->loadModel('Article');

// On stocke l'article dans $article


$article = $this->Article->findBySlug($slug);

// On envoie les données à la vue lire


$this->render('lire', compact('article'));
}
}

La vue

Dans notre projet, notre vue s'appelle "lire" et elle utilisera la variable "article". Le
fichier "lire.php" sera créé dans le dossier "views/articles" et contiendra

<?php
<h2><?= $article['titre'] ?></h2>
<p><?= $article['contenu'] ?></p>

Travail à faire : Continuer le reste du projet pour créer le CRUD


Utilisation des « templates » pour la vue

Il est très courant d'avoir des éléments qui s'intègrent dans toutes les pages (entête,
pied de page...), il serait donc utile de pouvoir les intégrer automatiquement dans
toutes nos vues.

Sur le principe, notre "template" contiendra tous les éléments répétitifs des
pages, et nos vues viendront uniquement injecter leur contenu dans l'emplacement
réservé à cet effet

Pour ce faire, préparons ce "template" et le stockons le dans un dossier appelé


"layout" dans le dossier "views".

Ce fichier contiendra tout ce que nous voulons intégrer dans toutees nos pages, et
un espace réservé sous la forme d'une variable php (ici $content) pour le contenu
envoyé par les vues.

Ce fichier s'appellera "default.php" et contiendra

<header>
<h1>Bannière de haut de page du site</h1>
</header>
<main>
<?= $content ?>
</main>
<footer>
<p>Pied de page du site Copyright 2021</p>
</footer>

Il faut maintenant gérer ce "template" dans la fonction "render" du contrôleur


principal. On fait pour cela la "temporisation de sortie", c'est à dire, la mise en
cache du code généré le temps qu'on décide ce qu'on veut en faire.

Nous allons demander à la vue de générer son contenu, puis le stocker dans une
variable et enfin l'envoyer au "template"

Pour ce faire nous utiliserons les fonctions php "ob_start()" et "ob_get_clean()".

ob signifie "Output Buffer".

"ob_start" crée ce "buffer" et stocke tout le code de sortie généré.


"ob_get_clean" va vider ce buffer dans la variable de notre choix.

Le contrôleur devient :
<?php
abstract class Controller{
/**
* Afficher une vue
*
* @param string $fichier
* @param array $data
* @return void
*/
public function render(string $fichier, array $data = []){
extract($data);

// On démarre le buffer de sortie


ob_start();

// On génère la vue
require_once(ROOT.'views/'.strtolower(get_class($this)).'/'.$fichi
er.'.php');
// On stocke le contenu dans $content
$content = ob_get_clean();
// On fabrique le "template"
require_once(ROOT.'views/layout/default.php');
}
/**
* Permet de charger un modèle
*
* @param string $model
* @return void
*/
public function loadModel(string $model){
// On va chercher le fichier correspondant au modèle souhaité
require_once(ROOT.'models/'.$model.'.php');
// On crée une instance de ce modèle. Ainsi "Article" sera
accessible par $this->Article
$this->$model = new $model();
}
}

PHP et AJAX

Le langage PHP peut être couplé avec le JavaScript pour réaliser les applications
web dynamique. Les pages ici sont rafraîchies sans que l’url ne change. Le client
(navigateur) va pouvoir faire une requête vers le serveur et modifier la page
courante sans que l’url de la page courante ne change. On utilise pour cela la
technologie AJAX.
AJAX signifie Asynchronous Javascript and Xml. Il permet de créer une interface
fonctionnant sur un navigateur, avec le langage JavaScript, devenu extrêmement
rapide sur les dernières versions des navigateurs.

PHP fonctionne sur le serveur donc peut consulter une base de donnée et la mettre
à jour avec les données entrées par l'interface utilisateur, et exécute aussi les
traitements demandés par l'utilisateur qui requièrent des données stockées sur le
serveur.

Comment opère l'interaction entre l'interface en Ajax et les scripts en PHP sur le
serveur ?

Interaction entre Ajax et PHP avec GET

Nous allons utiliser l’objet « XmlhttpRequest() » disponible dans la plupart des


navigateurs récents. Pour la compatibilité avec les anciens navigteur notamment IE
6 et antérieurs on utilise le code suivant

// ancien code de compatibilité, aujourd’hui inutile


if (window.XMLHttpRequest) { // Mozilla, Safari, IE7+...
httpRequest = new XMLHttpRequest();
}
else if (window.ActiveXObject) { // IE 6 et antérieurs
httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
}

Pratique

Créer deux fichiers : index.html et ajax.php

 Dans le fichier index.html


 function createInstance()
 {
 var req = null;
 if(window.XMLHttpRequest) {
 req = new XMLHttpRequest();
 }
 else if (window.ActiveXObject) {
 try {
 req = new ActiveXObject("Msxml2.XMLHTTP");
 } catch (e) {
 try {
 req = new ActiveXObject("Microsoft.XMLHTTP");
 } catch (e) {
 alert("XHR not created");
 }
 }
 }
 return req;
 }

 function submitForm()
 {
 var req = createInstance();
 req.onreadystatechange = function()
 {
 if(req.readyState == 4)
 {
 if(req.status == 200)
 {
 document.ajax.dyn.value="Received:" + req.responseText;
 }
 else
 {
 document.ajax.dyn.value="Error: returned status code " +
req.status + " " + req.statusText;
 }
 }
 };
 req.open("GET", "ajax.php", true);
 req.send(null);
 }

 Dans le fichier ajax.php

You might also like