Aller au menu - Aller au contenu

Icône TP : Création d'un CRUD

Avatar
Mise à jour : 04/09/2010
Difficulté : Intermédiaire Intermédiaire Creative Commons BY-NC-SA
487 visites depuis 7 jours, dont 29 sur ce chapitre classé 233/786
Nous allons maintenant passer un peu plus à la pratique et créer un CRUD. Pour ceux qui ne connaissent pas, un CRUD est un système de manipulation des données de la base : ça signifie Create, Read, Update, Delete (Créer, Lire, Mettre à jour, Supprimer).

Autrement dit, on va faire une interface pour gérer nos données. Cela peut entre autres servir à créer une interface d'administration pour votre site.

Attention, ce TP est là uniquement pour vous faire pratiquer un peu sur des choses simples, pour commencer à retenir un peu le fonctionnement de Doctrine et les méthodes utiles. Je ne traiterai pas, notamment, de la sécurité des scripts. Ceci est à intégrer à un projet, c'est pourquoi je vous donne un contrôleur frontal si basique.
Sommaire du chapitre :
Icône du chapitre
Chapitre précédent Sommaire Chapitre suivant

Préparation du terrain

Récapitulatif



Avant de commencer, récapitulons la structure de notre projet. Jusqu'à maintenant je vous ai simplement indiqué où placer quelques fichiers.

Nous utiliserons cette structure :

~/config/
----global.php
----schema.yml
~/html/
~/lib/
----models/
--------Article.php
--------ArticleTable.php
--------User.php
--------UserTable.php
--------generated/
------------BaseArticle.php
------------BaseUser.php
~/web/
----index.php


Si vous avez suivi le tutoriel en entier, vous ne devriez rien avoir à faire. Dans le cas contraire, je vous laisse vous reporter aux chapitres précédents pour connaître le contenu de ces fichiers.

Comme je vous l'ai dit, nous allons grandement simplifier le modèle MVC : nous aurons un contrôleur frontal (~/web/index.php) qui se chargera aussi d'inclure la page HTML.

Le but ici n'est pas d'expliquer le pattern MVC. Libre à vous d'utiliser votre propre système de templates/framework, d'adapter tout cela à un projet existant, etc. Je vous encourage d'ailleurs à le faire.


Le contrôleur



Voici la structure de notre contrôleur (tout pas beau tout moche, je sais :p ) :

Code : PHP - ~/web/index.php
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?php
require(dirname(__FILE__).'/../config/global.php');

// On récupère l'action à effectuer (Create, Read, Update ou Delete).
// Je compte sur vous pour utiliser un système plus perfectionné que ça. ^^
$action = 'read';
if(isset($_GET['action']) && in_array($_GET['action'], array('create', 'read', 'update', 'delete')))
{
  $action = $_GET['action'];
}

/* Nous ferons ici les traitements concernant la page. */
switch($action)
{
  case 'read':
    break;
  case 'create':
    break;
  case 'update':
    break;
  case 'delete':
    break;
}

/* Nous appellerons ici la page HTML appropriée. */
include(HTML_DIR.$action.'.php');


Données de test



Avant de commencer, il va bien falloir avoir quelques données pour tester. Insérez donc ceci :

Code : SQL - Données de base
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
INSERT INTO member
(login, password)
VALUES
('Jean', 'mypass'),
('Bernard', 'mysuperpass'),
('Superman', 'batman');

INSERT INTO article
(title, content)
VALUES 
("Mon premier article", "Voici le contenu de l'article, comme c'est simplement pour tester, je mets du texte tout bidon. D'ailleurs, je ne sais pas si vous avez remarqué, mais je n'ai pas beaucoup d'imagination !"),
("Mon second article", "Voici le contenu du deuxième article, comme c'est simplement pour tester, je mets du texte tout bidon. D'ailleurs, je ne sais pas si vous avez remarqué, mais je n'ai pas beaucoup d'imagination !"),
("Mon troisième et dernier article", "Voici le contenu du troisième article, comme c'est simplement pour tester, je mets du texte tout bidon. D'ailleurs, je ne sais pas si vous avez remarqué, mais je n'ai pas beaucoup d'imagination !");

Bien sûr, si vous avez des données plus intéressantes, utilisez-les. ^^

À partir d'ici, je vais vous donner le code au fur et à mesure que l'on avance. Je ne vous prends pas pour des gamins, si vous n'avez pas envie de réfléchir et voulez copier/coller directement, libre à vous. ^^ Mais dans ce cas, ce n'est presque pas la peine de lire jusqu'en bas, parce que je ne fais que reprendre ce que l'on a vu jusqu'ici. À vous de voir ce que vous préférez. ;)

Affichage de données

Ici, nous voulons afficher une liste de tous nos articles dans un tableau. Nous allons devoir récupérer les données dans la base de données, puis les afficher. Ce n'est vraiment pas compliqué, à vous de jouer.

Création de la requête



Ajoutez ceci dans le contrôleur :

Code : PHP - ~/web/index.php
1
2
3
4
5
6
<?php
/* ... */
  case 'read':
    $articles = Doctrine_Core::getTable('Article')->findAll();
    break;
/* ... */


Comme nous l'avons vu, findAll() nous retourne tous les articles de la table.

Affichage des données récupérées



Créons maintenant la vue pour l'action read :

Code : PHP - ~/html/read.php
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US" lang="en-US">
<head>
 <title>Tutoriel Doctrine</title>
 <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
</head>
<body>
 <h1>Liste des articles</h1>
 <a href="?action=create">Ajouter un nouvel article</a>
 <table>
  <tr>
   <th>Titre</th>
   <th>Contenu</th>
   <th>Auteur</th>
   <th><em>Action</em></th>
  </tr>
  <?php foreach($articles as $article): ?>
   <tr>
    <td><?php echo $article['title'] ?></td>
    <td><?php echo $article['content'] ?></td>
    <td><?php echo $article['User']['login'] ?></td>
    <td>
     <a href="?action=update&amp;id=<?php echo $article['id'] ?>">Modifier</a>
     <a href="?action=delete&amp;id=<?php echo $article['id'] ?>">Supprimer</a>
    </td>
   </tr>
  <?php endforeach; ?>
 </table>
</body>
</html>


Tout ceci est assez simple, je ne pense pas qu'il y ait de problème particulier. Nous utilisons simplement findAll() pour récupérer tous les articles. Comme on récupère un objet Doctrine_Collection (qui peut s'utiliser comme un tableau, rappelez-vous), nous utilisons foreach pour les parcourir.

La notation « tableau » est recommandée lorsque nous accédons à l'objet dans du code HTML. Ceci, simplement parce que si la personne qui s'occupe d'écrire le code HTML n'est pas la même que celle qui écrit le code PHP, elle n'est pas censée savoir comment PHP fonctionne. Cette notation lui permet de ne pas se soucier de la nature de la variable $article. Cela dit, si vous vous occupez de tout, vous faites bien comme vous voulez, vous avez quand même tout à fait le droit d'utiliser la notation objet si ça vous chante. ^^

Création d'objets

Cette fois, il va nous falloir afficher un formulaire nous permettant de renseigner les informations sur un nouvel article. Attention, pour l'utilisateur, je suppose que vous ne voulez pas laisser le choix à celui qui écrit l'article de « choisir qui écrit l'article » (logique, non ? ^^ ).

Le formulaire



Nous allons voir ici comment créer de nouveaux objets et les sauvegarder dans la base.

Nous allons avoir besoin d'un formulaire afin de rentrer les informations :

Code : PHP - ~/html/create.php
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<body>
	<h1>Création d'un article</h1>
	<form action="index.php?action=create" method="post">
		<label for="article_title">Titre : </label><br />
		<input type="text" name="title" id="article_title" /><br />
		<br />
		<label for="article_text">Texte : </label><br />
		<textarea name="content" id="article_text"></textarea><br />
		<br />
		<input type="submit" value="Sauvegarder" />
	</form>
	<a href="?action=read">Retour à la liste</a>
</body>


Nous ne mettons pas de champ 'Auteur'. Personnalisez tout cela à votre sauce, par exemple en récupérant l'id de l'auteur depuis une variable de session.

Traitement des informations



Code : PHP - ~/web/index.php
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
<?php
/* ... */
case 'create':
	if(!empty($_POST))
	{
		$article = new Article();
		$article->title = $_POST['title'];
		$article->content = $_POST['content'];
	
		// On indique l'auteur. Adaptez cela à votre projet.
		$article->User = $user;
	
		$article->save();
	}
	break;
/* ... */


Nous récupérons ici les données postées par le formulaire, et les affectons à un nouvel article, puis nous le sauvegardons.

Modification d'objets

La modification d'objets sera semblable à la création. La principale différence réside dans le fait que nous allons préremplir le formulaire avec les informations déjà existantes. Il nous faut donc récupérer l'article dans tous les cas. Et si on poste des informations le concernant, il faut le mettre à jour et sauvegarder en conséquence.

Le formulaire



Nous allons reprendre le formulaire de création, en ajoutant simplement les valeurs par défaut :

Code : PHP - ~/html/update.php
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<body>
 <h1>Modification d'un article</h1>
 <form action="index.php?action=update" method="post">
  <label for="article_title">Titre : </label><br />
  <input type="text" name="title" id="article_title" value="<?php echo $article['title'] ?>" /><br />
  <br />
  <label for="article_text">Texte : </label><br />
  <textarea name="content" id="article_text"><?php echo $article['content'] ?></textarea><br />
  <br />
  <input type="submit" value="Sauvegarder" />
  <!-- Pour se souvenir de quel article il s'agit -->
  <input type="hidden" name="id" value="<?php echo $article['id'] ?>" />
 </form>
 <a href="?action=read">Retour à la liste</a>
</body>


Traitement des informations



Code : PHP - ~/web/index.php
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?php
/* ... */
case 'update':
 $id = isset($_POST['id']) ? $_POST['id'] : $_GET['id'];
 // Il faut évidemment s'assurer que l'article existe
 if(!($article = Doctrine_Core::getTable('Article')->find($id)))
 {
  // À vous de mettre un traitement personnalisé !

  // Par exemple, on considère qu'il s'agit d'un nouvel article.
  $article = new Article();
  // Ou bien :
  exit();
 }
 
 if(!empty($_POST))
 {
  $article->title = $_POST['title'];
  $article->content = $_POST['content'];
 
  // On indique l'auteur. Adaptez cela à votre projet, par exemple si vous stockez l'id dans la session.
  $article->User = $user;
 
  $article->save();
 }
 break;
/* ... */


Petit exercice : trouvez-moi l'équivalent en construisant une requête (Doctrine_Query).

Correction :
Secret (cliquez pour afficher)
Code : PHP
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<?php
if(!empty($_POST))
{
 $q = Doctrine_Query::create()
 $q->update('Article a')
   ->set('a.title', '?', $_POST['title'])
   ->set('a.content', '?', $_POST['content'])
   ->where('a.id = ?', $id)
   ->execute();
}


Refactorisation du code



Ne voyez-vous pas quelque chose d'évident ?
Les pages create et update sont pratiquement identiques, il est donc intéressant de les fusionner en une seule.

Le formulaire



La page contenant le formulaire reste inchangée. Si l'article est « vide », alors les champs seront tout simplement préremplis avec des valeurs vides :

Code : PHP - ~/html/update.php
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
<body>
 <?php if($article->exists()): ?>
  <h1>Modification d'un article</h1>
 <?php else: ?>
  <h1>Création d'un article</h1>
 <?php endif; ?>

 <form action="index.php?action=<?php echo $action ?>" method="post">
  <label for="article_title">Titre : </label><br />
  <input type="text" name="title" id="article_title" value="<?php echo $article['title'] ?>" /><br />
  <br />
  <label for="article_text">Texte : </label><br />
  <textarea name="content" id="article_text"><?php echo $article['content'] ?></textarea><br />
  <br />
  <input type="submit" value="Sauvegarder" />
  <?php if($article->exists()): ?>
   <input type="hidden" name="id" value="<?php echo $article['id'] ?>" />
  <?php endif; ?>
 </form>
 <a href="?action=read">Retour à la liste</a>
</body>


Remarquez la méthode exists() de l'objet Doctrine_Record. Elle indique si l'objet existe dans la base de données, c'est-à-dire s'il est nouveau ou non.


Le contrôleur



Le traitement devra créer l'objet au cas où il n'existe pas :

Code : PHP - ~/web/index.php
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?php
/* ... */
case 'create':
case 'update':
 if(!isset($_GET['id']) && !isset($_POST['id']))
 {
  $article = new Article();
 }
 else
 {
  $id = isset($_POST['id']) ? $_POST['id'] : $_GET['id'];
  if(!($article = Doctrine_Core::getTable('Article')->find($id)))
  {
   $article = new Article();
  }
 }

 if(!empty($_POST))
 {
  $article->title = $_POST['title'];
  $article->content = $_POST['content'];
 
  // On indique l'auteur. Adaptez cela à votre projet, par exemple si vous stockez l'id dans la session.
  $article->User = $user;
 
  $article->save();
 }
 break;
/* ... */


Dans le cas où la page est appelée avec un id, on l'utilise pour récupérer l'article existant correspondant. Sinon, on en crée un nouveau.
Ensuite, peu importe que l'article soit un nouveau ou un existant, le traitement est le même pour modifier les valeurs et sauvegarder.

Par ailleurs, le fichier create.php ne nous sert maintenant plus à rien. ^^

Suppression de données

La suppression de données est très simple. Pour la vue, je vous invite juste à afficher un message de confirmation, par exemple, dans le fichier ~/html/delete.php, vous devriez y arriver sans problème. ^^

Traitement



Code : PHP - ~/web/index.php
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<?php
/* ... */
case 'delete':
 $id = isset($_POST['id']) ? $_POST['id'] : $_GET['id'];
 // On s'assure que l'article existe
 if($article = Doctrine_Core::getTable('Article')->find($id))
 {
  $article->delete();
 }
 break;
/* ... */


Trouvez-moi maintenant l'équivalent avec une Doctrine_Query :)

Secret (cliquez pour afficher)

Tout simplement :
Code : PHP
1
2
3
4
5
<?php
$q = Doctrine_Query::create()
$q->delete('Article a')
  ->where('a.id = ?', $id)
  ->execute();
Ce TP (extrêmement simple, non ? ^^ ) vous a permis de mettre en pratique simplement ce que nous avons vu jusque là. Bien sûr, comme je l'ai déjà dit, l'intérêt de Doctrine se révèle surtout dans les gros projets, et je ne vous encourage pas forcément à l'utiliser pour de petits projets simples.

Attention, comme je l'ai déjà dit dans l'introduction, ce TP n'est pas destiné à être un système complet. Je n'ai notamment pas mis l'accent sur la sécurité, mais il faut bien entendu vous assurer dans chaque action que les données correctes sont fournies.


Sur ce, et si vous voulez en savoir plus sur Doctrine, passons maintenant à des chapitres un peu plus complexes. Le suivant concerne les templates (ou behaviors).
Chapitre précédent Sommaire Chapitre suivant

Partager

Il n'y a pas encore de commentaire pour ce tuto.

Ce tutoriel a été corrigé par les zCorrecteurs.