Mini-cours de PHP - MySQL

Partie V

Ce chapitre est consacré à la gestion d'une table d'une base de données. Il n'est pas destiné aux débutants. Toutefois, ils auraient tort de ne pas essayer de comprendre, car cela leur facilitera considérablement leur travail de présentation de données.

SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET AUTOCOMMIT = 0;
START TRANSACTION;
SET time_zone = "+00:00";

DROP TABLE IF EXISTS voitures;
CREATE TABLE voitures (
  `id_voiture` smallint(5) UNSIGNED NOT NULL,
  `marque` varchar(15) NOT NULL,
  `modele` varchar(15) NOT NULL,
  `pays` varchar(15) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

INSERT INTO voitures (`id_voiture`, `marque`, `modele`, `pays`) VALUES
(1, 'Porsche', '911', 'Allemagne'),
(2, 'BMW', 'M5', 'Allemagne'),
(3, 'Ferrari', 'Testarossa', 'Italie'),
(4, 'Ford', 'Fiesta', 'USA'),
(5, 'Lamborghini', 'Aventador', 'Italie'),
(6, 'Fiat', '500', 'Italie');

ALTER TABLE voitures
  ADD PRIMARY KEY (`id_voiture`);

ALTER TABLE voitures
  MODIFY `id_voiture` smallint(5) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=7;

COMMIT;

voitures doit être préfixé par la valeur attribuée à la variable $prefixT du fichier maConnexion.php.

Pour réinitialiser la table "voitures", cliquez ici (TODO : modifier ce lien)

PHP Data Objects

PDO_MYSQL est un pilote qui implémente l'interface de PHP Data Objects (PDO) pour autoriser l'accès de PHP aux bases de données MySQL.

Ce pilote sera toujours utilisé (y compris avec les frameworks : w3.js et AngularJS)

Voir : Fichier PHP de connexion

PHP - MySQL

Les requêtes SQL relatives aux enregistrements peuvent se diviser en deux catégories.

query()

$sql = "SELECT nom AS a, prenom AS b FROM table";
$rep=$bdd->query($sql);
if ($rep===FALSE) die; /* code minimaliste */
$data=$rep->fetchAll(PDO::FETCH_ASSOC);
echo "{\"lignes\":".json_encode($data)."}"; /* retourne au format JSON */

exec()

$sql = "UPDATE table SET pwd=MD5('".$newPwd."') WHERE id=".$id;
$rep=$bdd->exec($sql); /* $rep = nombre de lignes affectées */
if($rep==1) {header("Location:index.php");}
else {die("échec");} /* code minimaliste */

PHP - MySQL - w3.js

w3.js est un framework JS qui simplifie le code nécessaire à l'affichage des données (souvent, dans un tableau). PHP - MySQL - JSON - w3.js forment un quarté gagnant.

Voir le résumé de w3P.js

PHP - JSON

  $file = 'tauxBe.json';
  $data = file_get_contents($file);
  $obj = json_decode($data);
  echo $obj[0]->b;

Source

PHP - MySQL - AngularJS

AngularJS est un framework JS qui simplifie le code nécessaire à l'affichage des données (souvent, dans un tableau). PHP - MySQL - JSON - AngularJS forment un quarté gagnant.

Bien que plus puissant que w3.js, AngularJS semble être en baisse de popularité

Afficher le contenu d'une table

mysql_01.htm (extrait)

  <head>
    ...
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>
    <script src="mysql_01.js"></script>
    ...
  </head>
  <body>
    ...
        <div data-ng-app="monApplication" data-ng-controller="monControleur">
          <p>
            <button data-ng-click="afficherTable()">Afficher le contenu de la table
            'voitures'</button>
          </p>
          <p>
            Affichage de contrôle de l'objet JSON voitures :
          </p>
          <p class="code">
            {{voitures}}<br />
          </p>
          <table border="1">
            <tr>
              <th>Marque</th>
              <th>Modèle</th>
              <th>Pays</th>
            </tr><!-- Placement des enregistrements dans le tableau HTML -->
            <tr data-ng-repeat="voiture in voitures">
              <td>{{voiture.marque}}</td>
              <td>{{voiture.modele}}</td>
              <td>{{voiture.pays}}</td>
            </tr>
          </table>
        </div>

Ce n'est plus le serveur qui crée le code HTML du tableau (via du code PHP), mais le navigateur web (via du code JavaScript). D'ailleurs, le fichier ne contient que du code HTML. Il possède l'extension .htm, il est donc envoyé tel quel au navigateur web par le serveur.

Après réception de ce fichier HTML, le navigateur appelle, dans l'ordre, deux JavaScript : AngularJS et mysql_01.js.

mysql_01.js appelle le fichier mysql_01.php qui répond par l'envoi d'un objet JSON (qui contient les données à afficher dans le tableau HTML)

Cet objet JSON est ensuite envoyé au controller de l'application Angular qui s'occupe de la présentation selon les instructions données dans le fichier HTML.

La "magie" d'AngularJS ne s'exerce que dans la zone (<div>) :

        <div data-ng-app="monApplication" data-ng-controller="monControleur">
           ...
        </div>

Toutes les instructions AngularJS doivent être placées dans cette zone. HTML5 autorise l'ajout d'attributs globaux sans signification en HTML dans une balise, pourvu que le nom de cet attribut débute par data-.

          <button data-ng-click="afficherTable()">
            Afficher le contenu de la table 'voitures'
          </button>

On comprend aisément que le bouton appelle la fonction afficherTable(), située dans le JavaScript, lorsqu'il est cliqué.

ng-click

          <p>
            Affichage de contrôle de l'objet JSON voitures :
          </p>
          <p class="code">
            {{voitures}}<br />
          </p>

Ce code HTML affiche le contenu de l'objet JSON. Il permet au développeur de comprendre. En production, ce code HTML ne signifiera rien au visiteur. Ce code devra donc être supprimé.

Le coeur de la "magie" se trouve donc ici :

            <tr data-ng-repeat="voiture in voitures">
              <td>{{voiture.marque}}</td>
              <td>{{voiture.modele}}</td>
              <td>{{voiture.pays}}</td>
            </tr>

ng-repeat

L'objet JSON voitures est un tableau d'objets.
L'objet voiture est un élément du tableau (qui représente un enregistrement de la table voitures). Ici, chaque élément dispose de trois propriétés dont les noms correspondent aux noms des colonnes de la table voitures (et dont la valeur représente la donnée contenue dans cette colonne pour cet enregistrement de la table voitures).

Le navigateur web exécute des instructions écrites dans différents langages (HTML, CSS, JS). Pour assurer l'affichage, il possède autant d'interprétateurs que de langages.

Cliquez ici pour voir la page permettant d'afficher la table dans un tableau.


mysql_01.js (code complet)

/* Ce JS retourne un objet JSON, nommé "voitures",
 créé après l'appel d'un fichier PHP */

// Déclaration du module nommé monApplication
var app = angular.module("monApplication", []);

// Déclaration du contrôleur nommé monControleur
app.controller("monControleur", function($scope, $http) {

  // Fonction d'affichage de la table
  $scope.afficherTable = function() {

    /* Via la fonction get()
      => URL sans paramètre ou passés dans l'URL)*/
      $http.get("mysql_01.php")
        .then(function(response) {
        $scope.voitures = response.data;
      });

  };

});

Ce code n'est pas expliqué ! Il est donné tel quel. Bonne chance ...
Néanmoins, pour plus d'info, cliquez ici
En réalité, pour un débutant, il n'est pas nécessaire de le comprendre pour l'utiliser. Pour conduire une voiture, il n'est pas nécessaire de comprendre le fonctionnement du moteur.

.voitures est l'objet JSON utilisé sur la page HTML
.data sont les données envoyées par le fichier PHP
Ces données s'appellent toujours "data". Ici, elles sont au format JSON


mysql_01.php (code complet)

<?php

  // Informe l'appelant (mysql_01.js) que la réponse sera de type JSON
  header("Content-Type: application/json");

  // Connexion à la DB
  require("maConnexion.php");

  // Création de la requête SQL
  $voitures=$prefixT."voitures";
  $sql = "SELECT * FROM $voitures ORDER BY pays";

  // Soumission de la requête SQL
  $rep=$bdd->query($sql);

  // Réponse du présent fichier
  echo json_encode($rep->fetchAll(PDO::FETCH_ASSOC));

  // Fermeture de la connexion MySQL
  $bdd=null;

?>

echo va envoyer des données. Cependant, ici les données ne seront pas intégrées dans un fichier PHP (page web), mais envoyées à l'appelant - le fichier mysql_01.js - au format JSON, car Content-Type: application/json.

Pour plus d'info sur json_encode(), cliquez ici.
Pour plus d'info sur fetchAll(), cliquez ici.
Pour plus d'info sur PDO::FETCH_ASSOC, cliquez ici.

Afficher le contenu d'une table

Afficher une liste déroulante

mysql_02.htm (extrait)

        <div data-ng-app="monApplication" data-ng-controller="monControleur">
          <p>
            <select>
              <option data-ng-repeat="nom in pays" value="{{nom.pays}}">
                {{nom.pays}}
              </option>
            </select>
          </p>
        </div>

Dans data-ng-repeat="nom in pays", pays est le nom de l'objet JSON (la réponse).
Dans {{nom.pays}}, pays est le nom de la propriété de l'objet nom.
Le code {{ }} permet d'afficher la valeur d'une variable de l'application Angular ou, comme ici, la valeur d'une propriété d'un objet.


mysql_02.js (code complet)

var app = angular.module("monApplication", []);
app.controller("monControleur", function($scope, $http) {
  $http.get("mysql_02.php")
    .then(function(response) {
    $scope.pays = response.data;
  });
});

mysql_02.php (code complet)

<?php
  header("Content-Type: application/json");
  require("maConnexion.php");
  $voitures=$prefixT."voitures";
  $sql = "SELECT DISTINCT pays FROM $voitures";
  $rep=$bdd->query($sql);
  echo json_encode($rep->fetchAll(PDO::FETCH_ASSOC));
  $bdd=null;
?>

La clause DISTINCT impose de ne retenir que des valeurs différentes.
Il est préférable de limiter le choix de l'utilisateur aux choix possibles.

Afficher une liste déroulante

Sélectionner des enregistrements

mysql_03.htm (extrait)

        <div data-ng-app="monApplication" data-ng-controller="monControleur">
          <p>
            <select data-ng-model="nomPays">
              <option data-ng-repeat="nom in pays" value="{{nom.pays}}">
                {{nom.pays}}
              </option>
            </select>
          </p>
          <p>
            <button data-ng-click="selectionner()">Afficher les enregistrements
            correspondant</button>
          </p>
          <table border="1">
            <tr>
              <th>
                Marque
              </th>
              <th>
                Modèle
              </th>
            </tr>
            <tr data-ng-repeat="voiture in voitures">
              <td>
                {{voiture.marque}}
              </td>
              <td>
                {{voiture.modele}}
              </td>
            </tr>
          </table>
        </div>

nomPays est le nom de la variable AngularJS dont la valeur représente le nom du pays choisi. Lors du clic, la fonction selectionner() (située dans le fichier mysql_03.js) est appelée. Cette fonction transmet au fichier mysql_03.php la valeur de la variable nomPays et gère la réponse du fichier PHP.


mysql_03.js (code complet)

var app = angular.module("monApplication", []);
app.controller("monControleur", function($scope, $http) {

  $http.get("mysql_02.php")
    .then(function(response) {
    $scope.pays = response.data;
  });

  $scope.selectionner = function() {

    $http.get("mysql_03.php?pays="+$scope.nomPays)
      .then(function(response) {
      $scope.voitures = response.data;
    });

  };

});

mysql_03.php (code complet)

<?php
  $pays=$_GET['pays'];
  header("Content-Type: application/json");
  require("maConnexion.php");
  $voitures=$prefixT."voitures";
  $sql = "SELECT marque, modele FROM $voitures WHERE pays='$pays';";
  $rep=$bdd->query($sql);
  echo json_encode($rep->fetchAll(PDO::FETCH_ASSOC));
  $bdd=null;
?>

Sélectionner des enregistrements

Insérer un enregistrement

mysql_04.htm (extrait)

        <div data-ng-app="monApplication" data-ng-controller="monControleur">
        <table border="1" style="margin:auto;">
            <tr>
              <th>Pays</th>
              <th>Marque</th>
              <th>Modèle</th>
            </tr>
            <tr data-ng-repeat="voiture in voitures">
              <td>{{voiture.pays}}</td>
              <td>{{voiture.marque}}</td>
              <td>{{voiture.modele}}</td>
            </tr>
          </table>
        </div>
        <form action="mysql_04.php" method="post">
          <p>
            Pays   : <input name="pays" /><br />
            Marque : <input name="marque" /><br />
            Modèle : <input name="modele" />
          </p>
          <p>
            <input type="submit">
          </p>
          </form>
        </div>

Tout le code lié à l'affichage du tableau a été vu précédemment.


mysql_04.js (code complet)

var app = angular.module("monApplication", []);
app.controller("monControleur", function($scope, $http) {
  $http.get("mysql_01.php")
    .then(function(response) {
    $scope.voitures = response.data;
  });
});

On réutilise le fichier "mysql_01.php" (car il renvoie un objet JSON qui représente toute la table "voitures").
Ce fichier ressemble au fichier mysql_01.js. Ici, le tableau s'affiche directement.


mysql_04.php (code complet)

<?php
  $pays=$_POST['pays'];
  $marque=$_POST['marque'];
  $modele=$_POST['modele'];
  require("maConnexion.php");
  $voitures=$prefixT."voitures";
  $sql="INSERT INTO $voitures (marque,modele,pays) VALUES ('$marque','$modele','$pays');";
  $rep=$bdd->exec($sql);
  $bdd=null;
  header("Location: mysql_04.htm");
?>

Grâce à la fonction header(), après la demande d'insertion, on retourne sur la page initiale (mysql_04.htm); ce qui permet de constater l'insertion.

Insérer un enregistrement

Insérer via data-ng-click

mysql_04b.htm (extrait)

        <div data-ng-app="monApplication" data-ng-controller="monControleur">
          <p>
            Pays   : <input data-ng-model="pays" /> {{pays}}<br />
            Marque : <input data-ng-model="marque" /><br />
            Modèle : <input data-ng-model="model" />
          </p>
          <p>
            <button data-ng-click="insert(pays,marque,model)">Insérer les données</button>
          </p>
        </div>

La directive AngularJS data-ng-model lie le champ d'un formulaire à une variable de l'application AngularJS. pays est le nom d'une variable. Tout ce qui tapé dans le champ "Pays" va directement dans la variable pays.

À droite du champ "Pays", le code {{pays}} permet d'afficher le contenu de la variable pays. On constate ainsi que le contenu de la variable pays est synchrone (correspond toujours au contenu du champ auquel elle est liée)

Lors du clic, le contenu des trois variables - pays, marque, model - est passé à la fonction insert() qui est appelée.


mysql_04b.js (code complet)

var app = angular.module("monApplication", []);
app.controller("monControleur", function($scope, $http) {
  $scope.insert = function(pays,marque,model) {
    $http({
      url:"mysql_04b.php",
      method:"POST",
      data:{marque:marque,modele:model,pays:pays},
      headers:{"Content-Type":"application/x-www-form-urlencoded"}
    })
  };
});

Ce code n'est pas expliqué ! Mais, pour un débutant, il n'est pas nécessaire de le comprendre pour l'utiliser.

Notez qu'il n'est pas nécessaire de construire l'objet JSON - {marque:...,modele:...,pays:...} - en plaçant les variables dans l'ordre où ils sont passés dans la fonction : pays,marque,model.

Notez aussi que le nom des propriétés de l'objet JSON ne doit pas nécessairement correspondre aux nom des paramètres de la fonction. Ainsi, ici, modele est le nom d'une propriété de l'objet JSON et model est le nom de la variable liée au champ HTML "Modèle"


mysql_04b.php (code complet)

<?php
  $paramApp=file_get_contents("php://input");
  $obj=json_decode($paramApp);

  $marque=$obj->marque;
  $modele=$obj->modele;
  $pays=$obj->pays;

  require("maConnexion.php");
  $voitures=$prefixT."voitures";
  $sql="INSERT INTO $voitures (marque,modele,pays) VALUES ('$marque','$modele','$pays');";

  $rep=$bdd->exec($sql);
  $bdd=null;
?>

Ce code n'est pas expliqué ! Mais, pour un débutant, il n'est pas nécessaire de le comprendre pour l'utiliser.

Insérer un enregistrement via data-ng-click

Supprimer un enregistrement

Avant de supprimer un enregistrement, il faut le sélectionner. On affichera donc le tableau avec une colonne contenant un lien pour supprimer l'enregistrement choisi.

mysql_05.htm (extrait)

            <tr data-ng-repeat="voiture in voitures">
              <td>{{voiture.marque}}</td>
              <td>{{voiture.modele}}</td>
              <td>{{voiture.pays}}</td>
              <td>
                <a href="mysql_05.php?id={{voiture.id_voiture}}">&#x274C;</a>
              </td>
            </tr>

Le code lié à l'affichage du tableau varie peu par rapport ce qu'on a vu précédemment.

Le code HTML &#x274C; affiche : ❌

Voir aussi : Encodage des caractères spéciaux

Analysons : mysql_05.php?id={{voiture.id_voiture}}
C'est une URL.

{{voiture.id_voiture}} représente la valeur de la propriété id.voiture de l'objet voiture (un nombre entier attribué automatiquement à un enregistrement dans la table "voitures"). En passant la souris sur ce lien s'affichera (en bas de la fenêtre du navigateur) la valeur (un nombre entier positif) qui sera envoyée au fichier "mysql_05.php". Par exemple, 1.


mysql_04.js (complet)

var app = angular.module("monApplication", []);
app.controller("monControleur", function($scope, $http) {
  $http.get("mysql_01.php")
    .then(function(response) {
    $scope.voitures=response.data;
  });
});

Ce code vu précédemment ne sert qu'à l'affichage du tableau.


mysql_05.php (complet)

<?php
  $id=$_GET['id'];
  require("maConnexion.php");
  $voitures=$prefixT."voitures";
  $sql = "DELETE FROM $voitures WHERE id_voiture=$id;";
  $rep=$bdd->exec($sql);
  $bdd=null;
  header("Location: mysql_05.htm");
?>

La fonction header() a été vue précédemment. Après la demande de suppression, on retourne sur la page initiale (mysql_05.htm); ce qui permet de constater sa suppression.

Supprimer un enregistrement

Mettre à jour un enregistrement

mysql_06.htm (extrait)

        <div data-ng-app="monApplication" data-ng-controller="monControleur">
          <table border="1" style="margin:auto;">
            <tr>
              <th>Pays</th>
              <th>Marque</th>
              <th>Modèle
              </th><th>Modifier</th>
            </tr>
            <tr data-ng-repeat="voiture in voitures">
              <td>{{voiture.pays}}</td>
              <td>{{voiture.marque}}</td>
              <td>{{voiture.modele}}</td>
              <td>
                <button data-ng-click="selectionner(voiture.id_voiture)">Modifier</button>
              </td>
            </tr>
          </table>
          <form action="mysql_06.php" method="post">
            <p>
              Pays : <input name="pays" data-ng-model="pays" /><br />
              Marque : <input name="marque" data-ng-model="marque" /><br />
              Modèle : <input name="modele" data-ng-model="modele" />
              <input type="hidden" name="id_voiture" data-ng-value="id_voiture" />
            </p><input type="submit" />
          </form>
        </div>

Quasi tout le code lié à l'affichage du tableau a été vu précédemment.

La dernière colonne du tableau contient des boutons. Un clic sur un bouton entraîne le remplissage du formulaire, situé sous le tableau, avec les données en regard du bouton cliqué.

        <button data-ng-click="selectionner(voiture.id_voiture)">
          Modifier
        </button>

La directive AngularJS ng-click a été vu précédemment. La différence est qu'ici la fonction contient un paramètre (l'ID de la voiture).

Attention ! On écrit selectionner(voiture.id_voiture)
et non selectionner({{voiture.id_voiture}}) ...

La directive AngularJS ng-model permet de lier le contenu d'un champ d'un formulaire à un nom de variable AngularJS.

Pour plus d'info sur la directive AngularJS ng-model, cliquez ici.

Un exemple de l'utilisation de cette directive a été présenté lorsque l'insertion d'un enregistrement a été abordé : Insérer via data-ng-click.
Bien que ce type d'insertion d'un enregistrement ne doit pas être compris par un débutant, le remplissage du champ "pays" montre le contenu de la variable AngularJS pays synchronisé.

La directive AngularJS ng-value permet de lier la valeur d'un champ d'un formulaire à un nom de variable AngularJS.

Pour plus d'info sur la directive AngularJS ng-value, cliquez ici.

La valeur de la variable AngularJS id_voiture est donné par la fonction selectionner() contenu dans le JavaScript lié à ce fichier HTML.


mysql_06.js (code complet)

var app = angular.module("monApplication", []);
app.controller("monControleur", function($scope, $http) {

  $http.get("mysql_01.php")
    .then(function(response) {
    $scope.voitures = response.data;
  });

  $scope.selectionner = function(id) {
    for (i = 0, len = $scope.voitures.length; i < len; i++) {
      if ($scope.voitures[i].id_voiture==id){
        $scope.pays=$scope.voitures[i].pays;
        $scope.marque=$scope.voitures[i].marque;
        $scope.modele=$scope.voitures[i].modele;
        $scope.id_voiture=$scope.voitures[i].id_voiture;
        break;
      }
    }
  };

});

On réutilise le fichier "mysql_01.php" (car il renvoie un objet JSON qui représente toute la table "voitures").
Ce fichier ressemble au fichier mysql_01.js. Ici, le tableau s'affiche directement.

La fonction selectionner() parcourt l'objet JSON voitures à la recherche d'un ID voiture correspondant à celui fourni lors du clic sur le bouton "Modifier". Une fois trouvé, il met à jour les champs du formulaire, y compris le champ caché; puis quitte la boucle (break;).


mysql_06.php (code complet)

<?php
  $id_voiture=$_POST['id_voiture'];
  $pays=$_POST['pays'];
  $marque=$_POST['marque'];
  $modele=$_POST['modele'];
  require("maConnexion.php");
  $voitures=$prefixT."voitures";
  $sql="UPDATE $voitures SET pays='$pays', marque='$marque', modele='$modele' WHERE id_voiture=$id_voiture;";
  $rep=$bdd->exec($sql);
  $bdd=null;
  header("Location: mysql_06.htm");
?>

La fonction header() a été vue précédemment. Après la demande de mise à jour, on retourne sur la page initiale (mysql_06.htm); ce qui permet de constater sa modification.

Mettre à jour un enregistrement

Afin de se centrer sur l'essentiel, les codes "complets" présentés ci-dessus sont "minimalistes". Aucune validation des données est faite avant leur envoi au serveur. Aucune vérification de l'identité de celui qui demande une modification de la base de données n'est faite. Aucune réponse du serveur MySQL n'affichée en cas d'une insertion, suppression ou modification d'un enregistrement. De plus, ces codes ne gèrent pas le cas des enregistrement identiques (doublons) ni les cas triviaux : Absence de table, de colonnes, table vide, ...

Voir aussi : Trier les colonnes