PHP et accès à une base de données

Contexte

Architecture PHP/BD

Exemple

Exemple d'architecture 3-tiers : PHP/BD

Exercice

TODO

PHP Data Objects

Définition

« 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 quelque soit la base de données utilisée. »

http://www.php.net/manual/fr/intro.pdo.php

SyntaxeConnexion à PostgreSQL avec PDO en PHP

$conn = new PDO('pgsql:host=hostname;port=5432;dbname=db', 'user', 'pass');

SyntaxeExécution de requête SQL

$sql = '...';
$resultset = $connexion->prepare($sql);
$resultset->execute();

SyntaxeTraitement de résultat de requête SQL

while ($row = $resultset->fetch(PDO::FETCH_ASSOC)) {
	... $row['...'];
	}

Exercice

TODO

Requêtes préparées et paramétrées

Conseil

Utiliser des requêtes paramétrées au niveau des langages applicatifs.

ExempleRequête INSERT préparée en PHP

$result = $connexion->prepare("INSERT INTO medicament (nom) VALUES ('Nouveau')");
$result->execute();

ExempleRequête INSERT préparée et paramétrée en PHP (bindValue)

$result = $connexion->prepare("INSERT INTO medicament (nom) VALUES (:param1)");
$result->bindValue('param1', $name, PDO::PARAM_STR);
$result->execute();

ExempleRequête INSERT préparée et paramétrée en PHP (bindParam)

$result = $connexion->prepare("INSERT INTO medicament (nom) VALUES (:param1)");
$result->bindParam(':param1', $name, PDO::PARAM_STR);
$name = "Nouveau1";
$result->execute();
$name = "Nouveau2";
$result->execute();

ComplémentRequête préparée en PHP

À l'école de musique

[30 min]

Une école de musique souhaite gérer les inscriptions aux différentes classes d'instrument et aux orchestres à l'aide d'une base de données.

Schéma conceptuel
Famille (#nom:chaîne)
Instrument (#lib:chaîne, lib_long:chaîne, famille=>Famille(nom))
Professeur (#num:entier, nom:chaîne, prénom:chaîne, tâche:chaîne)
Eleve (#num:entier, nom:chaîne, prénom:chaîne, date_naissance:date, inst=>Instrument(lib))
Orchestre (#lib:chaîne, min:entier, max:entier)
Enseigne (#num=>Professeur(num), #lib=>Instrument(lib))
Participe (#num=>Eleve(num), #lib=>Orchestre(lib))

Créer la base de données permettant de gérer les élèves et les instruments (sans les familles). Insérer des données exemple.

CREATE TABLE instrument (
lib VARCHAR PRIMARY KEY,
lib_long VARCHAR
);
CREATE TABLE eleve (
num INTEGER PRIMARY KEY,
nom VARCHAR NOT NULL,
prenom VARCHAR,
date_naissance DATE,
inst VARCHAR REFERENCES instrument(lib)
);
INSERT INTO instrument (lib) VALUES ('Violon');
INSERT INTO instrument (lib) VALUES ('Guitare');
INSERT INTO eleve (num, nom, inst) VALUES (1, 'Jimmy', 'Guitare');
INSERT INTO eleve (num, nom, inst) VALUES (2, 'Robby', 'Guitare');

Écrire le code SQL qui permet d'afficher la liste des élèves (nom et prénom) triés par instrument.

SELECT E.nom, E.prenom, E.inst 
FROM Eleve E 
ORDER BY E.inst ;

Écrire le code SQL qui permet d'afficher la liste des instruments avec le nombre d'élèves associés (on affichera même les instruments dont personne ne joue).

SELECT I.lib, COUNT(E.inst)
FROM Instrument I LEFT JOIN Eleve E ON E.inst=I.lib
GROUP BY lib ;

Écrire le code SQL qui permet d'afficher la liste des instruments avec le nombre de places restantes par instruments, sachant qu'il y a 20 places disponibles par instrument.

SELECT I.lib, 20 - COUNT(E.inst)
FROM Instrument I LEFT JOIN Eleve E ON E.inst=I.lib
GROUP BY lib ;

Compléter le code PHP ci-dessous pour afficher le nombre de places disponibles par instruments listés par leur libellé long.

On utilisera une requête paramétrée.

<html xmlns="http://www.w3.org/1999/xhtml">
	<head>
		<title>École de musique</title>
		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
	</head>
	
	<body>
	<h2>Liste des places disponibles par instrument</h2>
	<table border="1">
	<tr> <td><b>Instruments</b></td> <td><b>Places disponibles</b></td> </tr>
	
	<?php
	/* Nombre maximum d'élèves autorisé par instrument */
    $max_eleves = 20; 
    
    /* Nombre maximum d'élèves autorisé par instrument */
    $connexion = new PDO('pgsql:host=localhost;port=5432;dbname=musique', 'me', 'mypassword');	
    
    /** Préparation et exécution de la requête **/
    $sql = 'SELECT I.lib AS lib, ? - COUNT(E.inst) AS dispo ...';
    $resultset = $connexion->prepare($sql);
    $resultset->bindParam(1, ...);
    $resultset->execute();            
        
    while ($row = $resultset->fetch(PDO::FETCH_ASSOC)) {
        echo '<tr>';
        echo '<td>' . $row[...] . '</td>';
        echo '<td>' . $row[...] . '</td>';
        echo '</tr>';
        }
        
    /** Déconnexion **/
    $connexion=null;    
    ?>
    
    </table>
    </body>
</html>
<html xmlns="http://www.w3.org/1999/xhtml">
	<head>
		<title>École de musique</title>
		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
	</head>
	
	<body>
	<h2>Liste des places disponibles par instrument</h2>
	<table border="1">
	<tr> <td><b>Instruments</b></td> <td><b>Places disponibles</b></td> </tr>
	
	<?php
	/* Nombre maximum d'élèves autorisé par instrument */
    $max_eleves = 20; 
    
    /* Nombre maximum d'élèves autorisé par instrument */
    $connexion = new PDO('pgsql:host=localhost;port=5432;dbname=test', 'test', 'test');	
    
    /** Préparation et exécution de la requête **/
    $sql = 'SELECT I.lib AS lib, :max - COUNT(E.inst) AS dispo 
            FROM Instrument I LEFT JOIN Eleve E ON E.inst=I.lib 
            GROUP BY lib';
    $resultset = $connexion->prepare($sql);
    $resultset->bindParam('max', $max_eleves);
    $resultset->execute();            
        
    while ($row = $resultset->fetch(PDO::FETCH_ASSOC)) {
        echo '<tr>';
        echo '<td>' . $row['lib'] . '</td>';
        echo '<td>' . $row['dispo'] . '</td>';
        echo '</tr>';
        }
        
    /** Déconnexion **/
    $connexion=null;    
    ?>
    
    </table>
    </body>
</html>

Accès à une BD en écriture (INSERT, UPDATE, DELETE)

Exemple

<?php 
/** Connexion **/
$connexion = new PDO('pgsql:host=localhost;port=5432;dbname=test', 'test', 'test');	
/** Préparation et exécution de la requête **/
$sql = 'INSERT INTO medicament (nom) VALUES (\'Nouveau\')';
$result = $connexion->prepare($sql);
$result->execute();
/** Traitement du résultat **/
if ($result) {
  echo 'Nouveau inséré';
}
else {
  echo 'Erreur lors de l\'insertion';
}
/** Déconnexion **/
$connexion=null;
?>

Super-transferts

L'entreprise de ventes de figurines de super-héros GARVEL a monté un partenariat avec les deux sites de ventes en ligne makemoney.com et dobusiness.com. Chaque entreprise lui demande de mettre à disposition respectivement un fichier CSV et un fichier XML pour le transfert du catalogue, stocké dans une base de données PostgreSQL.

Le code devra être exécuté et testé.

Fichier CSV et fichier XML requis

Superman;15
Batman;12
Superchild;12
...
<catalogue>
  <figurine designation='Superman' prix='1555'/>
  <figurine designation='Batman' prix='12'/>
  <figurine designation='Superchild' prix='12'/>
  ...
</catalogue>

Modèle de la base de données

Modèle UML Figurines GARVEL (extrait)

Créer la base de données correspondant à ce modèle.

Créer une vue vfigurine permettant de retourner les champs designation et prix.

Créer un utilisateur customer permettant de lire la vue figurine.

CREATE TABLE Personnage (
designation VARCHAR PRIMARY KEY,
prix DECIMAL NOT NULL,
identite VARCHAR,
genre VARCHAR(12) CHECK (genre='superhéros' OR genre='supervillain')
);
INSERT INTO Personnage (designation, prix) VALUES ('Superman',15);
INSERT INTO Personnage (designation, prix) VALUES ('Batman',12);
INSERT INTO Personnage (designation, prix) VALUES ('Superchild',12);
CREATE VIEW vfigurine AS
SELECT designation, prix FROM Personnage;
CREATE USER customer WITH ENCRYPTED PASSWORD 'public';
GRANT SELECT ON vfigurine TO customer;

Réaliser un script PHP csv.php permettant de se connecter à la base PosgreSQL et d'afficher la désignation et le prix au format CSV, en suivant l'exemple ci-après.

<?php 
/** Connexion **/
$connexion = new PDO('pgsql:host=localhost;port=5432;dbname=test', 'test', 'test');	
/** Préparation et exécution de la requête **/
$sql = 'SELECT designation, prix FROM vfigurine';
$resultset = $connexion->prepare($sql);
$resultset->execute();
/** Traitement du résultat **/
while ($row = $resultset->fetch(PDO::FETCH_ASSOC)) {
	echo $row['designation'] . ";" . $row['prix'] . "\n";
}
/** Déconnexion **/
$connexion=null;
?>

Réaliser un script PHP permettant de se connecter à la base et d'afficher la désignation et le prix selon un schéma XML, en suivant l'exemple ci-après.

<?php 
/** Connexion **/
$connexion = new PDO('pgsql:host=localhost;port=5432;dbname=garvel', 'customer', 'public');	
/** Préparation et exécution de la requête **/
$sql = "SELECT designation, prix FROM vfigurine";
$resultset = $connexion->prepare($sql);
$resultset->execute();
/** Traitement du résultat **/
echo "<catalogue>";
while ($row = $resultset->fetch(PDO::FETCH_ASSOC)) {
	echo "<figurine designation='" . $row['designation'] . "' prix='" . $row['prix'] . "'/>";
}
echo "</catalogue>";
/** Déconnexion **/
$connexion=null;
?>

Accès à une BD en lecture (SELECT)

Exemple

<?php 
/** Connexion **/
$connexion = new PDO('pgsql:host=localhost;port=5432;dbname=test', 'test', 'test');	
/** Préparation et exécution de la requête **/
$sql = 'SELECT nom FROM medicament';
$resultset = $connexion->prepare($sql);
$resultset->execute();
/** Traitement du résultat **/
while ($row = $resultset->fetch(PDO::FETCH_ASSOC)) {
	echo $row['nom'];
	echo ' ';
}
/** Déconnexion **/
$connexion=null;
?>

Auto-évaluation

Exercice final

Quiz - Culture

...

A

B

Quiz - Méthode

...

A

B

Quiz - Code

...

A

B

Quiz - Culture

...

A

B

Quiz - Méthode

...

A

B

Quiz - Code

...

A

B

Devoirs en ligne

[45 min]

Une université propose des formations pour ses étudiants via une plate-forme d'enseignement en ligne. Plusieurs devoirs sont proposés, chacun ayant une description et une même date de rendu pour tous les étudiants souhaitant le faire. Le but de cet exercice est de réaliser un site Web permettant aux étudiants de consulter leurs notes.

Base de données

On vous donne ci-dessous le code SQL LDD de la base de données sous PostgreSQL ainsi qu'un exemple de code SQL LMD pour l'insertion de données.

CREATE TABLE etudiant(
login CHAR(8) PRIMARY KEY,
nom TEXT,
prenom TEXT
);
CREATE TABLE devoir(
num INTEGER PRIMARY KEY,
daterendu DATE UNIQUE NOT NULL,
description TEXT NOT NULL
);
CREATE TABLE note(
etudiant CHAR(8) REFERENCES etudiant(login),
devoir INTEGER REFERENCES devoir(num),
valeur INTEGER NOT NULL,
PRIMARY KEY(etudiant, devoir),
CHECK (valeur BETWEEN 0 AND 20)
);
INSERT INTO etudiant(login, nom, prenom) VALUES ('bfrankli', 'Franklin', 'Benjamin');
INSERT INTO devoir(num, daterendu, description) VALUES (1, '10-05-2013','Structures de donnees en C');
INSERT INTO note(etudiant, devoir, valeur) VALUES ('bfrankli', 1, 15);

Site Web

Le site Web sera composé :

  • d'une page d'accueil (accueil.html) comportant un formulaire où l'étudiant doit entrer son login ;

  • d'une page présentant les notes d'un étudiant pour chaque devoir qu'il a rendu ainsi que sa moyenne générale (notes.php).

La seconde page (notes.php) est appelée à partir de la première (accueil.html). On supposera dans le reste de l'exercice que tous les fichiers sont situés directement dans le répertoire public_html du serveur (sans sous-répertoires).

Exemple d'affichage pour notes.php

Écrivez le formulaire HTML de la page d'accueil (accueil.html). On utilisera la méthode HTTP POST.

Requête GET ou POST par formulaire HTML (balise <form>)

<form method="post" action="notes.php">
  <p>Login : </p>
  <input type="text" name="login"/>
  <input type="submit"/>
</form>
<html>
<body>
<h1>Accueil</h1>
<form method="post" action="notes.php">
  <p>Login : </p>
  <input type="text" name="login"/>
  <input type="submit"/>
</form>
</body>
</html>

Dans les questions suivantes, on supposera que le login spécifié dans le formulaire de accueil.html existe effectivement dans la base de données (on ne fera donc pas de test pour le vérifier) et on se référera à l'exemple d'affichage pour notes.php pour le résultat souhaité.

Écrivez le code PHP permettant de générer le code HTML du titre de la page "Notes de l'étudiant..." pour le login en question.

echo "<h1>Notes de l'étudiant $vRow[prenom] $vRow[nom] (<i>$vRow[login]</i>)</h1>";
<?php
// Connexion à la base de données
$vConn = new PDO('pgsql:host=localhost;port=5432;dbname=devoirs', 'test', 'test');
// Écriture, préparation et exécution de la requête 1
$vSql = 'SELECT login, nom, prenom FROM etudiant WHERE login=:login';
$vResultSet = $vConn->prepare($vSql);
$vResultSet->bindValue(':login',$_POST['login'],PDO::PARAM_STR);
$vResultSet->execute();
// Traitement du résultat
$vRow = $vResultSet->fetch(PDO::FETCH_ASSOC);
echo "<h1>Notes de l'étudiant $vRow[prenom] $vRow[nom] (<i>$vRow[login]</i>)</h1>";
// Clôture de la connexion
$vConn=null;
?>

Écrivez le code PHP permettant de générer le code HTML du tableau des notes de l'étudiant. Les devoirs seront affichés par ordre croissant de date de rendu.

On ajoutera une vue à la base de données préalablement à l'écriture du code PHP, afin d'éviter que la couche PHP ne contiennent du code SQL avancé.

SQL

CREATE VIEW v_devoir AS
SELECT d.description, d.daterendu, n.etudiant, n.valeur 
FROM devoir d JOIN note n ON n.devoir=d.num 
ORDER BY d.daterendu;

PHP

// Écriture, préparation et exécution de la requête 2
$vSql = 'SELECT * FROM v_devoir WHERE etudiant=:login';
$vResultSet = $vConn->prepare($vSql);
$vResultSet->bindValue(':login',$_POST['login'],PDO::PARAM_STR);
$vResultSet->execute();
// Traitement du résultat
echo "<table border='1'>";
echo "<tr><th>Devoir</th><th>Date de rendu</th><th>Note</th></tr>";
while ($vRow = $vResultSet->fetch(PDO::FETCH_ASSOC)) {
	echo "<tr><td>$vRow[desc]</td><td>$vRow[date]</td><td>$vRow[note]</td></tr>";
}
echo "</table>";

Écrivez le code PHP permettant de calculer la moyenne générale de l'étudiant.

SQL

CREATE VIEW v_moy AS
SELECT ROUND(AVG(valeur),1) AS moy, etudiant
FROM note
GROUP BY etudiant;
// Écriture, préparation et exécution de la requête 3
$vSql = 'SELECT * FROM v_moy WHERE etudiant=:login';
$vResultSet = $vConn->prepare($vSql);
$vResultSet->bindValue(':login',$_POST['login'],PDO::PARAM_STR);
$vResultSet->execute();
// Traitement du résultat
$vRow = $vResultSet->fetch(PDO::FETCH_ASSOC);
echo "<p>Moyenne générale : <b>$vRow[moy]</b></p>";

Écrivez le code HTML du lien hypertexte "Retour à l'accueil" et finalisez la page notes.php.

<a href="accueil.html">Retour à l'accueil</a>
<?php
// Connexion à la base de données
$vConn = new PDO('pgsql:host=localhost;port=5432;dbname=test', 'test', 'test');
// Écriture, préparation et exécution de la requête 1
$vSql = 'SELECT login, nom, prenom FROM etudiant WHERE login=:login';
$vResultSet = $vConn->prepare($vSql);
$vResultSet->bindValue(':login',$_POST['login'],PDO::PARAM_STR);
$vResultSet->execute();
// Traitement du résultat
$vRow = $vResultSet->fetch(PDO::FETCH_ASSOC);
echo "<h1>Notes de l'étudiant $vRow[prenom] $vRow[nom] (<i>$vRow[login]</i>)</h1>";
// Écriture, préparation et exécution de la requête 2
$vSql = 'SELECT * FROM v_devoir WHERE etudiant=:login';
$vResultSet = $vConn->prepare($vSql);
$vResultSet->bindValue(':login',$_POST['login'],PDO::PARAM_STR);
$vResultSet->execute();
// Traitement du résultat
echo "<table border='1'>";
echo "<tr><th>Devoir</th><th>Date de rendu</th><th>Note</th></tr>";
while ($vRow = $vResultSet->fetch(PDO::FETCH_ASSOC)) {
	echo "<tr><td>$vRow[desc]</td><td>$vRow[date]</td><td>$vRow[note]</td></tr>";
}
echo "</table>";
// Écriture, préparation et exécution de la requête 3
$vSql = 'SELECT * FROM v_moy WHERE etudiant=:login';
$vResultSet = $vConn->prepare($vSql);
$vResultSet->bindValue(':login',$_POST['login'],PDO::PARAM_STR);
$vResultSet->execute();
// Traitement du résultat
$vRow = $vResultSet->fetch(PDO::FETCH_ASSOC);
echo "<p>Moyenne générale : <b>$vRow[moy]</b></p>";
// Lien de retour 
echo "<p><a href='accueil.html'>Retour à l'accueil</a></p>";
// Clôture de la connexion
$vConn=null;
?>

Requêtes préparées et paramétrées

Conseil

Utiliser des requêtes paramétrées au niveau des langages applicatifs.

ExempleRequête INSERT préparée en PHP
$result = $connexion->prepare("INSERT INTO medicament (nom) VALUES ('Nouveau')");
$result->execute();
ExempleRequête INSERT préparée et paramétrée en PHP (bindValue)
$result = $connexion->prepare("INSERT INTO medicament (nom) VALUES (:param1)");
$result->bindValue('param1', $name, PDO::PARAM_STR);
$result->execute();
ExempleRequête INSERT préparée et paramétrée en PHP (bindParam)
$result = $connexion->prepare("INSERT INTO medicament (nom) VALUES (:param1)");
$result->bindParam(':param1', $name, PDO::PARAM_STR);
$name = "Nouveau1";
$result->execute();
$name = "Nouveau2";
$result->execute();
ComplémentRequête préparée en PHP

Accès à une BD en lecture (SELECT)

Exemple
<?php 
/** Connexion **/
$connexion = new PDO('pgsql:host=localhost;port=5432;dbname=test', 'test', 'test');	
/** Préparation et exécution de la requête **/
$sql = 'SELECT nom FROM medicament';
$resultset = $connexion->prepare($sql);
$resultset->execute();
/** Traitement du résultat **/
while ($row = $resultset->fetch(PDO::FETCH_ASSOC)) {
	echo $row['nom'];
	echo ' ';
}
/** Déconnexion **/
$connexion=null;
?>

Requête GET ou POST par formulaire HTML (balise <form>)

DéfinitionFormulaire

On appelle formulaire une interface permettant à un utilisateur humaine de saisir des données en vue dans une application informatique.

DéfinitionContrôle

On appelle contrôle un élément d'un formulaire permettant d'effectuer une action : saisir une donnée, exécuter une requête...

La balise form du langage HTML permet de :

  • créer un formulaire avec des contrôles,

  • envoyer le contenu du formulaire à un serveur web grâce à une requête GET ou POST.

ExempleContrôle en HTML
  • étiquette

  • cases à cocher

  • champs de saisie

  • boutons radio

  • listes à choix multiples

  • ...

ExempleFormulaire
Exemple de formulaire HTML
<form metho="get" action="test.php">
	<p><label>Nom</label> <input type="text" name="nom"></p>
	<p><label>Prénom</label> <input type="text" name="prenom"></p>
	<p><label>Age</label> <input type="text" name="age"></p>
	<p><input type="submit"></p>		
</form>
Un exemple de fichier XHTML
ExempleCorps
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title>Exemple de fichier XHTML</title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    </head>
    <body>
        <p>Hello world !</p>
    </body>
</html>

Traiter les requêtes HTTP avec un serveur PHP

Lorsqu'une requête HTTP envoie des données au serveur web, par exemple grâce à un lien <a> ou un formulaire <form> en HTML, les données envoyées doivent être traitées par un programme que l'on écrit spécifiquement sur le serveur.

Fondamental

Un serveur web/PHP peut gérer les données envoyées par une requête HTTP.

Lors de son chargement une page PHP contient un tableau de variables pour les données envoyées par la méthode GET et un autre pour les données envoyées par POST.

Syntaxe

On accède à ses données en utilisant la syntaxe :

$_GET["var1"]

$_GET["var2"]

ou

$_POST["var1"]

$_POST["var2"]

var1 et var2 sont des noms de données dans la requête HTTP (par exemple le nom des contrôles dans le formulaire HTML à l'origine de la requête).

Exemple
<?php
echo 'Hello ' . $_POST["name"] ;
?>
Liste des raccourcis clavier

Liste des fonctions de navigation et leurs raccourcis clavier correspondant :

  • Bloc Suivant : flèche droite, flèche bas, barre espace, page suivante, touche N
  • Bloc Précédent : flèche gauche, flèche haut, retour arrière, page précédente, touche P
  • Diapositive Suivante : touche T
  • Diapositive Précédente : touche S
  • Retour accueil : touche Début
  • Menu : touche M
  • Revenir à l'accueil : touche H
  • Fermer zoom : touche Échap.