Comment utiliser Doctrine et PHP pour se connecter aux bases de données

Doctrine est devenu l'ORM de référence utilisé dans le monde PHP. Pour bien débuter avec ce composant logiciel, je vous propose dans cet article un exemple d'utilisation simple de gestion de l'affichage des données d'une table MySQL.

L'interface de l'affichage utilisera Twitter Bootstrap pour avoir rapidement une page responsive.

Doctrine est un ORM ou Object Relational Mapping. C'est un composant logiciel qui permet de transformer les données contenues dans une table MySQL en objets utilisables dans votre application, et vice versa. Cette procédure est qualifiée de mapping.

Dans cette introduction, nous allons :

  • faire un tour rapide des composants Doctrine.
  • installer Doctrine sous Wamp ou sous Xampp.
  • créer une entité simple et mapper avec les annotations.
  • voir comment manipuler une entité avec EntityManager et Repository.
  • créer un gabarit HTML5 avec Twitter Bootstrap pour l'interface d'affichage.
  • afficher les données de l' entité dans une page PHP.

Vous pouvez aller plus loin dans l'utilisation de Doctrine en mettant en oeuvre un CRUD complet et en gérant un mapping complexe.

blog-doctrine-visuel.jpg

Dans un premier temps, nous allons présenter les éléments composant Doctrine, dont le package ORM. Ensuite nous verrons comment l'installer et l'utiliser pour afficher des données d'une table de base de données.

1- Composants Doctrine et package ORM

Sous le terme doctrine sont normalement regroupés 3 composants distincts, même si le package ORM est le composant le plus connu :

  • Le DBAL ou DataBase Access Layer : cette couche, assise sur PDO (PHP Data Access) propose un certain nombre de fonctionnalités pour accéder aux bases de données. Il s'agit d'une couche d'abstraction qui permet à votre code d'être découplé du type de base de données.
     
  • Le package ORM bien entendu : c'est celui dont nous parlons dans ce texte. Il utilise les packages DBAL et Common.
     
  • Le package Common : ce package contient du code commun aux 2 premiers packages.

Dans ce qui suit, nous allons installer l'ORM via Composer, puis nous créerons une entité simple pour illustrer le mappage avec des annotations.

2- Installer Doctrine sous Wamp ou Xampp

+ Je suppose que vous avez Wamp ou Xampp installé sous Windows, ou Mamp sous Mac, et que vous avez crée un dossier de projet nommé ProjetORM.

+ Créez ensuite les 5 sous-dossiers testDoctrine, images, includes, css et js.

Pour faciliter l'installation de l'ORM, nous allons utiliser Composer, dont vous pouvez télécharger la version sous Windows ici : télécharger Composer.

Installer Composer

L'installation est rapide depuis le fichier composer.exe que vous avez récupéré.

Installer Doctrine

+ Commencez par créer le fichier composer.json, disons dans le dossier testDoctrine créé auparavant. Ce fichier indique à Composer les ordres que vous allez lancer dans la ligne de commande. Ici, il s'agit d'installer le package ORM de Doctrine.

{
	"require": {
		"doctrine/orm": "2.*",
		"symfony/yaml": "2.*"
	},
	"autoload": {
		"psr-0": {"": "src/"}
	}
}

code de composer.json

+ Ensuite, en ligne de commande MS DOS, placez-vous dans le dossier testDoctrine puis lancez la commande Composer install.

+ Créer les dossiers testDoctrine/src et testDoctrine/config.

+ Créer dans phpMyAdmin la base de données testdoctrine.

C'est tout : vous pouvez maintenant utiliser l'ORM.

Fichier bootstrap.php et EntityManager

Les entités de votre application (utilisateurs, produits, etc.) sont gérées par un objet dit EntityManager. Il est recommandé de créer ce manager dans un fichier séparé bootstrap.php qu'il suffira d'inclure dès qu'on voudra gérer des entités. Nous le plaçons dans le dossier testDoctrine.

<?php
	use Doctrine\ORM\Tools\Setup;
	use Doctrine\ORM\EntityManager;

	require_once "vendor/autoload.php";

	// Configuration par defaut Doctrine ORM avec avec Annotations
	$isDevMode = true;
	$config = Setup::createAnnotationMetadataConfiguration(array(__DIR__."/src"), $isDevMode);
	// En yaml ou en  XML
	//$config = Setup::createXMLMetadataConfiguration(array(__DIR__."/config/xml"), $isDevMode);
	//$config = Setup::createYAMLMetadataConfiguration(array(__DIR__."/config/yaml"), $isDevMode);

	// Parametres de la database
	$conn = array(
		'driver' => 'pdo_mysql',
		'user' => 'root',
		'password' => '',
		'dbname' =>'testdoctrine',
	);

	// Obtenir l'Entity Manager
	$entityManager = EntityManager::create($conn, $config);
?>

code du fichier bootstrap.php

+ createAnnotationMetadataConfiguration(.) : cette méthode est utilisée pour indiquer qu'on veut utiliser des annotations dans les classes représentant vos entités. Ces entités sont placées dans le dossier src.

+ createXMLMetadataConfiguration(.) : on utilisera un fichier XML pour préciser le mapping.

+ createYAMLMetadataConfiguration(.) : on utilisera un fichier YAML pour préciser le mapping.

+ create(.) : méthode pour créer l'EntityManager.

Doctrine en ligne de commande

Pour lancer des ordres Doctrine en ligne de commande, on va créer le fichier cli-config.php contenant l'appel de la console. Nous le plaçons dans le dossier testDoctrine. On utilise la méthode createHelperSet() qui reçoit l'EntityManager. Il faut donc inclure le fichier de bootstrap.php qui créé l'EntityManager.

<?php
    //Inclure le fichier de bootstrap
	require_once "bootstrap.php";
	
	return \Doctrine\ORM\Tools\Console\ConsoleRunner::createHelperSet($entityManager);

?>

code du fichier cli-config.php

3- Créer une entité, mapper avec des annotations

Nous souhaitons gérer des entités Livres qu'il faudra persister en base de données MySQL. Votre base de données est testdoctrine comme indiqué dans votre fichier bootstrap.php.

Pour une entité Livres, je vous propose de gérer les informations suivantes : livreid, titre, auteur, editeur, prix, description et enfin photo qui sera l'image de couverture d'une livre.

a/ Créez la classe Livres ci-dessous avec le mapping associé. Il correspondra à cette classe une table en base de données dont on peut donner le nom avec l'annotation : @Entity @Table(name="livres").

Pour simplifier, les colonnes sont en type string sauf le prix qui sera decimal et livreid qui est un entier (généré automatiquement : AUTO_INCREMENT donc en langage de base de données).

b/ Ajouter des getters et setters et sauvegarder votre classe sous Livres.php dans le dossier testDoctrine/src.

c/ Dans le terminal, supprimer le schéma et re-créez le en tapant les 2 ordres :
testDoctrine>vendor\bin\doctrine.bat orm:schema-tool:drop--force
testDoctrine>vendor\bin\doctrine.bat orm:schema-tool:create

L’entité est créée maintenant en BDD.

 

<?php
	/**
	 * @Entity @Table(name="livres")
	 **/
	class Livres
	{
	  //Attributs
	  /** @Id @Column(type="integer") @GeneratedValue **/
	  private $livreid; 
	  
	  /** @Column(type="string", length = 255, unique = true) **/
	  private $titre;
	  
	  /** @Column(type="string") **/
	  private $auteur; 
	  
	  /** @Column(type="string") **/
	  private $editeur;
	  
	  /** @Column(type="decimal") **/
	  private $prix;
	  
	  /** @Column(type="string") **/
	  private $description;
	  
	  /** @Column(type="string") **/
	  private $photo;
	  
	  
	  // Getters
	  public function getLivreid() {return $this->livreid;}
	  public function getTitre(){return $this->titre;}
	  public function getAuteur() {return $this->auteur;}
	  public function getEditeur(){return $this->editeur;}
	  public function getPrix() {return $this->prix;}
	  public function getDescription(){return $this->description;}
	  public function getPhoto(){return $this->photo;}
	  
	
	  //Setters
	  public function setLivreid($livreid){
		$livreid = (int) $livreid;
		
		if ($livreid > 0){$this->livreid = $livreid;}
	  }
	  
	  public function setTitre($titre){
		if (is_string($titre)){$this->titre = $titre;}
	  }
	  
	  public function setAuteur($auteur){
		if (is_string($auteur)){$this->auteur = $auteur;}
	  }
	  
	  public function setEditeur($editeur){
		if (is_string($editeur)){$this->editeur = $editeur;}
	  }
	  
	  public function setPrix($prix){
		  $prix = (float) $prix;
		  $this->prix = $prix;
	  }
	  
	  public function setDescription($description){
		if (is_string($description)){$this->description = $description;}
	  }
	  
	  public function setPhoto($photo){
		if (is_string($photo)){$this->photo = $photo;}
	  }
	}
?>

code de la classe Livres.php

4- Manipuler des entités : EntityManager, Repository

L'Entity Manager ou EM est l'objet qui permet de manipuler l’entité, par exemple enregistrer en base de données. Vous l'avez créé dans le fichier bootstrap.php.

Méthodes de l'EntityManager (EM)

persist($entite) : fait persister l'entité.
flush() : fait le commit du persist.
clear($nomentite) : annule tous les persists effectués.
detach($nomentite) : annule le persist sur l’entité en argument.
contains($entite) : donne true si $entite est géré par EM (s’il y a eu un persist sur $entite).
refresh($entite) : met à jour $entite dans l’état où elle est en BDD.
remove($entite) : suppression de la BDD.

L'Entity Repository permet de ramener les entités de la base de données, en vue de les afficher par exemple sur une page web.

Méthodes de l'Entity Repository

find($id) : trouver l’entité correspondant à un ID
findAll() : retourne toutes les entités en Array parcourable avec un foreach.

5- Créer un gabarit Twitter Bootstrap pour l'affichage.

Pour obtenir tout de suite un affichage responsive de nos livres, nous allons créer un gabarit Bootstrap simple composé de quelques lignes bootstrap.

Ce gabarit sera composé de plusieurs parties qu'il suffira d'inclure avec la directive PHP include() pour composer rapidement une nouvelle page.

+ header.php contenant la navigation,

+ bandeau.php pour ajouter un slider, et un

+ footer.php terminant chaque page.

Tous ces fichiers peuvent être placés dans un dossier includes, les fichiers images dans le dossier images, les styles dans le dossier css et les javascript dans le dossier js (voir figure).

Les fichiers CSS personnalisé et JS peuvent être téléchargés : css.css et bootstrap.min.js.

Le header.php

Il contient le HEAD de chaque page avec références aux fichiers de CSS comme bootstrap.min.css et aux fichiers Javascript. Il contient aussi la navigation Bootstrap de chaque page.

<!doctype html>
<html>
	<head>
		<meta charset="utf-8">
		<title><?php echo $titre;?></title>
		<meta name="viewport" content="width=device-width,initial-scale=1.0"/>
		<meta name="description" content="Bookstore : choix de livres."/>

		<link rel="stylesheet" 
		      href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" 
			  media="screen">
			  
		<link href="css/css.css" rel="stylesheet" media="screen"/>
		<script src="http://code.jquery.com/jquery-2.1.0.min.js"></script>
	</head>

<body>
    <nav class="navbar navbar-default navbar-fixed-top">
		<div class="container-fluid">
			<div class="navbar-header">
			  <button class="navbar-toggle" data-toggle="collapse" 
			          data-target=".navHeaderCollapse">
					<span class="icon-bar"></span> 
			  </button>
			  <a class="navbar-brand" href="index.php">
				<span class="navbar-logo">
					<img src="images/logo.png" width="50%">
				</span>
			  </a>
			</div>

			<div>  
			  <ul class="nav navbar-nav collapse navbar-collapse n
			           avHeaderCollapse navbar-right">
				<li><a class="lien" href="index.php">Accueil</a></li>
				<li><a class="lien" href="livres.php">Livres</a></li>
				<li><a class="lien" href="contact.php">Contact</a></li>
			  </ul>
			</div>
		</div>
    </nav>

code du header.php

Le bandeau.php

Le bandeau est un emplacement sur une ligne Bootstrap réservé à placer un contenu comme du texte ou un slider par exemple.

<div class="bandeau">
	<div class="container">   
		<br><br><br>
		<div class="row">
			<div class="col-md-12 col-sm-12">
				<div class="accroche">
					<p><span>
					   MEILLEURS LIVRES
					</span></p>

					<p class="sous-accroche">
						<span>
						  Nouvelles technologies.
						</span>
					</p>
				</div>
			</div>
		</div>
	</div> 
</div>

code du bandeau.php

Le footer.php

Le footer termine chaque page. Il contient par exemple un menu du bas, un copyright ou toute autre information que l'on souhaite mettre en footer. Il peut faire aussi référence à du code Javascript : bootstrap.min.js, code de suivi Facebook, Twitter, Google Analytics ou autre.

	    <footer class="footer">
			<div>
				<div class="copyright">
					Copyright : Bookstore 2016.
				</div>
			</div>
	    </footer>

		<script src="js/bootstrap.min.js"></script>
	</body>
</html>

code du footer.php

6- Afficher les données - la page livres.php

Il est possible de créer maintenant la page d'affichage utilisant Doctrine. Son interface peut être améliorée en gérant aussi la visualisation des détails d'un livre, la suppression d'un ou de plusieurs livres, la mise à jour d'un livre et l'ajout d'un livre (CRUD).

La page livres.php pourra utiliser le code qui suit :

<?php
	$titre = "Bookstore : liste de livres";//Titre de la page
	include('includes/header.php');//Le header
	include('includes/bandeau.php');//Le bandeau
?>
<div class="container-fluid marges background" style="margin-top:0px;margin-bottom:0px;">
	<div class="container">    
		<div class="row">
			<div class="col-md-12 titre">
				Liste de livres
			</div>
		</div>

		<div class="row">
			<div class="col-md-12 argu">
				ICI ON AFFICHERA
			</div>
		</div>
	</div>
</div>

<?php
	include('includes/footer.php');//Footer
?>

code de livres.php

Créer et utiliser un Repository

Pour ramener les livres à afficher, il faut créer un Repository à partir de l'EntityManager. Cela se fait avec la méthode getRepository() de l'EntityMananger. Toutes les entités sont obtenues avec la méthode findAll() du Repository.

<?php 
	// Require de la classe
	require_once "testDoctrine/bootstrap.php";

	// Obtenir l'objet Repository
	$livreRepository = $entityManager->getRepository('Livres');
	
	// Obtenir les livres avec findAll(.)
	$livres = $livreRepository->findAll();

	//Titre de la page
	$titre = "Bookstore : meilleurs livres.";
	
	//le header
	include("includes/header.php");

	// Bandeau
	include("includes/bandeau.php");
?>

code de livres.php

$livreRepository = $entityManager->getRepository('Livres') : création d'un objet Repository avec la méthode getRepository de l'EntityManager.

$livres = $livreRepository->findAll() : obtenir les données à afficher.

L'affichage des données dans le corps de la page consistera juste à extraire les livres de l'objet $livres, en utilisant un for each, comme dans le code ci-dessous.

L'affichage des données au sein de la page

Pour afficher les bonnes informations, on peut tester le nombre de lignes renvoyées avec la fonction count().

Nous plaçons ce code PHP dans une ligne bootstrap, dans plusieurs DIV en utilisant les classes CSS row, container et container-fluid.

	<div class="container-fluid marges background">
		<div class="container">    
			<div class="row">
				<div class="col-md-12 titre">
					Liste de livres avec Doctrine
				</div>
			</div>

			<div class="row">
				<div class="col-md-12 argu">
					<?php
						if(count($livres) != 0){							
							echo "<table id='tablivres'>
									  <tr style='background-color:yellow;font-weight:bold;'>
										   <td>Titre</td>
										   <td>Auteur</td>
										   <td>Editeur</td>
									  </tr>";

							//Parcours du tableau des objets
							foreach($livres as $data){
								echo "<tr> 
										  <td><a href=detail.php?livreid=" . $data->getLivreid() . ">". $data->getTitre() . "</a></td>
										  <td>". $data->getAuteur() . "</td>
										  <td>". $data->getEditeur() . "</td>
									</tr>";
							}
							
							// Fermer le tableau
							echo "</table>";
							
							//Total de livres et affichage
							echo "<br><b>Total livres : </b>" . count($livres) . "<br>";
						}
						else{
							echo "Aucun livre dans la base.<br><br>";
						}
					?>
				</div>
			</div>
		</div>
	</div>


<?php 
	//Le footer
	include("includes/footer.php");
?>

code du fetch

par-titre-45845878588

Créateur de ReCONVERT, chef de projet web et e-commerce, formateur de +2000 stagiaires en présentiel et en ligne (LIVE et VOD). Actuellement, je développe le Digital Learning.

N'hésitez pas à me suivre sur les réseaux sociaux.