Toute application Web dite dynamique nécessite une base de données ainsi que des outils qui permettront de manipuler ces données.Dans la palette des outils à la disposition des développeurs PHP, on trouve entre autres les DBAL (DataBase Abstraction Layer ou couche d'abstraction de base de données) ou les ORM (Object Relational Mapping ou mapping objet-relationnel).
2. Accès aux bases de données
relationnelles et ORM en PHP
Toute application Web dite dynamique nécessite une
base de données ainsi que des outils qui permettront de
manipuler ces données.
Dans la palette des outils à la disposition des
développeurs PHP, on trouve entre autres les DBAL
(DataBase Abstraction Layer ou couche d'abstraction de
base de données) ou les ORM (Object Relational
Mapping ou mapping objet-relationnel).
29 juin 2011 Mickaël Perraud 2
3. Accès aux bases de données
relationnelles et ORM en PHP
3 interventions :
Présentation de différents DBAL
Présentation de l'ORM Doctrine2
Présentation de Pomm
29 juin 2011 Mickaël Perraud 3
4. Accès aux bases de données relationnelles
Contributeur ZF depuis 2007 (Zend_Db, Zend_Barcode)
Responsable documentation française
Donne des webinars sur ZF en partenariat avec Zend
Travaille sur l'aide à la traduction et propose les versions déconnectées
de la documentation PDF / CHM
Vice-trésorier AFUP 2011
@mikaelkael / http://mikaelkael.fr
29 juin 2011 Mickaël Perraud 4
5. Retournons en arrière
On a commencé par tout écrire en dur :
$lien = mysql_connect('localhost', 'mysql_user', 'mysql_password');
if (!$lien) {
die('Impossible de se connecter : ' . mysql_error());
}
$db = mysql_select_db('foo', $lien);
if (!$db) {
die ('Impossible de sélectionner la base de données : ' . mysql_error());
}
$requete = 'SELECT * FROM maTable WHERE id = ' . $_GET['id'];
$resultat = mysql_query($requete);
while($ligne = mysql_fetch_assoc($resultat)) {
echo $ligne['id'].': '.$ligne['valeur'];
}
29 juin 2011 Mickaël Perraud 5
6. Retournons en arrière
Puis on a ”amélioré” :
//config.php
define('DB_HOST', 'localhost');
define('DB_USERNAME', 'mysql_user');
define('DB_PASSWORD', 'mysql_password');
define('DB_DATABASE', 'mysql_base');
//db.php
require_once 'config.php';
$lien = mysql_connect(DB_HOST, DB_USERNAME, DB_PASSWORD);
if (!$lien) {
die('Impossible de se connecter : ' . mysql_error());
}
$db = mysql_select_db(DB_DATABASE, $lien);
if (!$db) {
die ('Impossible de sélectionner la base de données : ' . mysql_error());
}
29 juin 2011 Mickaël Perraud 6
7. Retournons en arrière
Puis les classes sont arrivées :
class BDD {
var $connexion;
function BDD() {
$this->connexion = $this->connecte(DB_TYPE);
}
function connecte($type = 'mysql') {
switch($type) {
case 'mysql':
return mysql_connect(DB_HOST, DB_USERNAME, DB_PASSWORD);
break;
case 'oci8':
//...
29 juin 2011 Mickaël Perraud 7
8. PDO
PDO = PHP Data Object
Ecrit en C
Introduit en PHP 5.0 en 2004
Activé par défaut avec PHP 5.1
Fournit une interface d'abstraction à l'accès aux
données
Plus sécurisé (si bien utilisé)
29 juin 2011 Mickaël Perraud 8
9. PDO : quelles bases de données ?
Demandez à phpinfo() :
Demandez à PDO :
print_r(PDO::getAvailableDrivers());
/*
Array
(
[0] => sqlite
[1] => dblib
[2] => mysql
[3] => oci
[4] => odbc
[5] => pgsql
[6] => sqlite2
)
*/
29 juin 2011 Mickaël Perraud 9
10. PDO : reprenons notre exemple
La connexion :
try {
$dbh = new PDO('mysql:host=localhost;dbname=' . DB_DATABASE, DB_USER, DB_PASSWORD);
echo 'Connected!';
} catch (PDOException $e) {
echo $e->getMessage();
}
En changeant de driver :
try {
$dbh = new PDO('oci:dbname=' . DB_DATABASE, DB_USER, DB_PASSWORD);
echo 'Connected!';
} catch (PDOException $e) {
echo $e->getMessage();
}
Ce qui va suivre est désormais indépendant du driver
29 juin 2011 Mickaël Perraud 10
11. PDO : requêtes préparées
PDO peut être utilisée avec ou sans requêtes
préparées
Pour des raisons de sécurité, préférez les requêtes
préparées :
$stmt = $dbh->prepare('SELECT nom, prenom FROM utilisateurs WHERE id_utilisateur = :id');
$stmt->bindParam('id', $_GET['id'], PDO::PARAM_INT);
$stmt->execute();
$resultat = $stmt->fetchAll();
L'assignation peut être nommée (ci-dessus) ou
numérique
29 juin 2011 Mickaël Perraud 11
12. PDO : lecture des résultats
Il existe plusieurs manières de récupérer les résultats via
PDO :
$resultat = $stmt->fetchAll(PDO::FETCH_...); // Toutes les lignes
//ou
$resultat = $stmt->fetch(PDO::FETCH_...); // Ligne par ligne
Et plusieurs mode de récupération (PDO::FETCH_*) :
PDO::FETCH_ASSOC :
Array
(
[nom] => Perraud
[prenom] => Mickael
)
29 juin 2011 Mickaël Perraud 12
14. PDO : lecture des résultats
Le meilleur pour la fin ?
PDO::FETCH_CLASS
Prend un résultat et le retourne sous la forme d'une
classe
On peut instancier la classe directement par PDO
29 juin 2011 Mickaël Perraud 14
15. PDO::FETCH_CLASS
class Utilisateur {
private $_nom;
private $_prenom;
public function __set($attribut, $valeur)
{
$this->{"set".ucfirst($attribut)} = $valeur;
}
public function setNom($nom)
{
Notre classe : $this->_nom = $nom;
}
public function getNom()
class Utilisateur { {
public $nom; return $this->_nom;
public $prenom; }
} public function setPrenom($prenom)
{
$this->_prenom = $prenom;
}
public function getPrenom()
{
return $this->_prenom;
}
public function __toString()
{
return $this->_prenom . ' ' . $this->_nom;
}
}
29 juin 2011 Mickaël Perraud 15
16. PDO::FETCH_CLASS
Interrogeons la base :
$stmt = $dbh->prepare('SELECT * FROM utilisateurs');
$resultat = $stmt->fetchAll(PDO::FETCH_CLASS, 'Utilisateur');
foreach($resultat as $class) {
echo $class;
// Affiche par exemple : Mickael Perraud
}
29 juin 2011 Mickaël Perraud 16
17. Ce que PDO ne fait pas
Ne fournit pas une abstraction de base de données :
il ne réécrit pas le SQL
Il n'émule pas des fonctionnalités manquantes
29 juin 2011 Mickaël Perraud 17
18. Zend_Db
Composant d'accès aux bases de données de Zend Framework
Contient différents sous composants :
Zend_Db_Adapter : abstraction de base de données
Zend_Db_Select : abstraction de requête de type ”SELECT”
Zend_Db_Table : ”Table Data Gateway” -
http://martinfowler.com/eaaCatalog/tableDataGateway.html
Zend_Db_Table_Row : ”Row Data Gateway” -
http://martinfowler.com/eaaCatalog/rowDataGateway.html
29 juin 2011 Mickaël Perraud 18
19. Zend_Db_Adapter
Surcharge PDO et certaines extensions (MySQLi, Oci8, Db2, Sqlsrv)
en fournissant une interface commune
Instanciation via la fabrique de Zend_Db :
$db = Zend_Db::factory('Pdo_Mysql', array('host' => 'localhost',
'username' => 'mysql_user',
'password' => 'mysql_password',
'dbname' => 'mysql_database'));
29 juin 2011 Mickaël Perraud 19
21. Zend_Db : lecture des résultats
Outre fetchAll() ou fetch() de PDO (renommé en fetchRow()),
on dispose de :
fetchAssoc()
fetchCol()
fetchOne()
fetchPairs()
29 juin 2011 Mickaël Perraud 21
22. Zend_Db : autres fonctions
Gestion du schéma :
listTables()
describeTable()
Interface générique de gestion des transactions
(beginTransaction(), commit(), rollback())
Abstraction de la clause limit()
29 juin 2011 Mickaël Perraud 22
23. Zend_Db_Select
Abstraction DQL : permet de construire des requêtes de type
”SELECT” en PHP
// Construire cette requête :
// SELECT produit_id, produit_nom, prix
// FROM "produits"
// WHERE (prix > 100.00)
// AND (prix < 500.00)
$prixminimum = 100;
$prixmaximum = 500;
$select = $db->select()
->from('produits',
array('produit_id', 'produit_nom', 'prix'))
->where('prix > ?', $prixminimum)
->where('prix < ?', $prixmaximum);
29 juin 2011 Mickaël Perraud 23
24. DoctrineDBAL
Partie de Doctrine destinée à l'abstraction des bases de données :
Plusieurs sous-composants :
DoctrineDBALDriver : surcouche de PDO et quelques drivers (pas de SQL)
DoctrineDBALPlatform : abstraction de la génération de requêtes et de
fonctionnalités (SQL)
DoctrineDBALSchema : abstraction de la gestion du schéma
DoctrineDBALType : abstraction du typage avec mapping PHP
29 juin 2011 Mickaël Perraud 24
25. DoctrineDBAL
Connexion :
$connexion = DriverManager::getConnection(array('dbname' => 'mysql_database',
'user' => 'mysql_user',
'password' => 'mysql_password',
'host' => 'localhost',
'driver' => 'pdo_mysql'));
Exécution de requêtes préparées :
$sql = "SELECT * FROM utilisateurs WHERE id = ? AND status = ?";
$stmt = $connexion->prepare($sql);
$stmt->bindValue(1, $id);
$stmt->bindValue(2, $status);
$stmt->execute();
On retrouve une API de récupération de données très similaire à
ce qui précède pour Zend_Db
29 juin 2011 Mickaël Perraud 25
26. DoctrineDBAL : transation
Transaction imbriquées :
// $connexion instanceof DoctrineDBALConnection
$connexion->beginTransaction(); // 0 => 1, transaction "réelle" démarrée
try {
//...
// nested transaction block, this might be in some other API/library code that is
// unaware of the outer transaction.
$connexion->beginTransaction(); // 1 => 2
try {
//...
$connexion->commit(); // 2 => 1
} catch (Exception $e) {
$connexion->rollback(); // 2 => 1, transaction marquée pour annulation
throw $e;
}
//...
$connexion->commit(); // 1 => 0, transaction "réelle" confirmée
} catch (Exception $e) {
$connexion->rollback(); // 1 => 0, transaction "réelle" annulée
throw $e;
}
29 juin 2011 Mickaël Perraud 26
28. DoctrineDBAL : schéma génération
Création table utilisateur :
$schema = new DoctrineDBALSchemaSchema();
$maTable = $schema->createTable("utilisateurs");
$maTable->addColumn("id_utilisateur", "integer", array("unsigned" => true));
$maTable->addColumn("nom", "string", array("length" => 50));
$maTable->addColumn("prenom", "string", array("length" => 50));
$maTable->setPrimaryKey(array("id_utilisateur"));
$schema->createSequence("utilisateurs_seq");
$myForeign = $schema->createTable("commentaires");
$myForeign->addColumn("id_commentaire", "integer");
$myForeign->addColumn("utilisateur_id", "integer");
$myForeign->addForeignKeyConstraint($myTable,
array("utilisateur_id"),
array("id_utilisateur"),
array("onUpdate" => "CASCADE"));
// Récupérer les requêtes pour générer le schéma
$queries = $schema->toSql($myPlatform);
// Récupérer les requêtes pour effacer le schéma
$dropSchema = $schema->toDropSql($myPlatform);
29 juin 2011 Mickaël Perraud 28
29. Ceux qu'il ne faut pas oublier
ADOdb : 5.11 (PHP 5)
PEAR::MDB2 : 2.5.0 en beta (PHP 5.3+)
29 juin 2011 Mickaël Perraud 29
30. ZendDb 2.0
ZendDbAdapter : ajout plugin (pre- post-connect),
suppression du SQL pur
ZendDbQuery : abstraction DML, DQL, ainsi que DDL
(”alter”, ”create”, ”drop”) et DCL (”commit”, ”rollback”,
”savepoint”), supporte ANSI ainsi que les dialectes des SGBD
ZendDbResultSet : modélisation des résultats
ZendDbMetadata : gestion du schéma
http://framework.zend.com/wiki/display/ZFDEV2/Zend+Db+2.0+Requirements
29 juin 2011 Mickaël Perraud 30