présentation de dbal en php (nantes)
DESCRIPTION
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).TRANSCRIPT
Accès aux bases de données
relationnelles et ORM en PHP
Mickaël Perraud29 juin 2011 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).
Mickaël Perraud29 juin 2011 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
Mickaël Perraud29 juin 2011 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
Mickaël Perraud29 juin 2011 5
Retournons en arrière
$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'];}
On a commencé par tout écrire en dur :
Mickaël Perraud29 juin 2011 6
Retournons en arrière
//config.phpdefine('DB_HOST', 'localhost');define('DB_USERNAME', 'mysql_user');define('DB_PASSWORD', 'mysql_password');define('DB_DATABASE', 'mysql_base');
//db.phprequire_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());}
Puis on a ”amélioré” :
Mickaël Perraud29 juin 2011 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': //...
Mickaël Perraud29 juin 2011 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é)
Mickaël Perraud29 juin 2011 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) */
Mickaël Perraud29 juin 2011 10
PDO : reprenons notre exemple
La connexion :
En changeant de driver :
Ce qui va suivre est désormais indépendant du driver
try { $dbh = new PDO('mysql:host=localhost;dbname=' . DB_DATABASE, DB_USER, DB_PASSWORD); echo 'Connected!';} catch (PDOException $e) { echo $e->getMessage();}
try { $dbh = new PDO('oci:dbname=' . DB_DATABASE, DB_USER, DB_PASSWORD); echo 'Connected!';} catch (PDOException $e) { echo $e->getMessage();}
Mickaël Perraud29 juin 2011 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 :
L'assignation peut être nommée (ci-dessus) ou
numérique
$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();
Mickaël Perraud29 juin 2011 12
PDO : lecture des résultats
Il existe plusieurs manières de récupérer les résultats via
PDO :
Et plusieurs mode de récupération (PDO::FETCH_*) :
PDO::FETCH_ASSOC :
$resultat = $stmt->fetchAll(PDO::FETCH_...); // Toutes les lignes//ou $resultat = $stmt->fetch(PDO::FETCH_...); // Ligne par ligne
Array( [nom] => Perraud [prenom] => Mickael)
Mickaël Perraud29 juin 2011 13
PDO : lecture des résultats
Et plusieurs mode de récupération (PDO::FETCH_*) :
PDO::FETCH_NUM :
PDO::FETCH_BOTH (par défaut) :
PDO::FETCH_OBJ :
Array( [0] => Perraud [1] => Mickael)
Array( [nom] => Perraud [0] => Perraud [prenom] => Mickael [1] => Mickael)
object(stdClass)#1 (2) { ["nom"]=> string(7) "Perraud" ["prenom"]=> string(7) "Mickael"}
Mickaël Perraud29 juin 2011 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
Mickaël Perraud29 juin 2011 15
PDO::FETCH_CLASS
Notre classe :
class Utilisateur { public $nom; public $prenom;}
class Utilisateur { private $_nom; private $_prenom; public function __set($attribut, $valeur) { $this->{"set".ucfirst($attribut)} = $valeur; } public function setNom($nom) { $this->_nom = $nom; } public function getNom() { return $this->_nom; } public function setPrenom($prenom) { $this->_prenom = $prenom; } public function getPrenom() { return $this->_prenom; } public function __toString() { return $this->_prenom . ' ' . $this->_nom; }}
Mickaël Perraud29 juin 2011 16
PDO::FETCH_CLASS
$stmt = $dbh->prepare('SELECT * FROM utilisateurs');$resultat = $stmt->fetchAll(PDO::FETCH_CLASS, 'Utilisateur');
foreach($resultat as $class) { echo $class; // Affiche par exemple : Mickael Perraud}
Interrogeons la base :
Mickaël Perraud29 juin 2011 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
Mickaël Perraud29 juin 2011 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
Mickaël Perraud29 juin 2011 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'));
Mickaël Perraud29 juin 2011 20
Zend_Db_Adapter
Exécution de requêtes préparées :
Abstraction DML (”INSERT”, ”UPDATE”, ”DELETE”) :
$stmt = $db->query('SELECT * FROM utilisateurs WHERE id_utilisateur = ?', array($_GET['id']));
$id = $db->insert('utilisateurs', array('nom' => 'Doe', 'prenom' => 'John'));
$db->update('utilisateurs', array('nom' => 'Doe', 'prenom' => 'Jane'), array('id_utilisateur = ?' => 2));
$db->delete('utilisateurs', array('id_utilisateur = ?' => 2));
Mickaël Perraud29 juin 2011 21
Zend_Db : lecture des résultats
Outre fetchAll() ou fetch() de PDO (renommé en fetchRow()),
on dispose de :
fetchAssoc()
fetchCol()
fetchOne()
fetchPairs()
Mickaël Perraud29 juin 2011 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()
Mickaël Perraud29 juin 2011 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);
Mickaël Perraud29 juin 2011 24
Doctrine\DBAL
Partie de Doctrine destinée à l'abstraction des bases de données :
Plusieurs sous-composants :
Doctrine\DBAL\Driver : surcouche de PDO et quelques drivers (pas de SQL)
Doctrine\DBAL\Platform : abstraction de la génération de requêtes et de
fonctionnalités (SQL)
Doctrine\DBAL\Schema : abstraction de la gestion du schéma
Doctrine\DBAL\Type : abstraction du typage avec mapping PHP
Mickaël Perraud29 juin 2011 25
Doctrine\DBAL
Connexion :
Exécution de requêtes préparées :
On retrouve une API de récupération de données très similaire à
ce qui précède pour Zend_Db
$connexion = DriverManager::getConnection(array('dbname' => 'mysql_database', 'user' => 'mysql_user', 'password' => 'mysql_password', 'host' => 'localhost', 'driver' => 'pdo_mysql'));
$sql = "SELECT * FROM utilisateurs WHERE id = ? AND status = ?";$stmt = $connexion->prepare($sql);$stmt->bindValue(1, $id);$stmt->bindValue(2, $status);$stmt->execute();
Mickaël Perraud29 juin 2011 26
Doctrine\DBAL : transation
Transaction imbriquées :// $connexion instanceof Doctrine\DBAL\Connection$connexion->beginTransaction(); // 0 => 1, transaction "réelle" démarréetry {
//...
// 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;}
Mickaël Perraud29 juin 2011 27
Doctrine\DBAL : schéma manager
listDatabases()
listSequences()
listTables()
listTableColumns()
listTableDetails()
listTableForeignKeys()
listTableIndexes()
listViews()
createSchema()
Mickaël Perraud29 juin 2011 28
Doctrine\DBAL : schéma génération
Création table utilisateur :
$schema = new \Doctrine\DBAL\Schema\Schema();$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);
Mickaël Perraud29 juin 2011 29
Ceux qu'il ne faut pas oublier
ADOdb : 5.11 (PHP 5)
PEAR::MDB2 : 2.5.0 en beta (PHP 5.3+)
Mickaël Perraud29 juin 2011 30
Zend\Db 2.0
Zend\Db\Adapter : ajout plugin (pre- post-connect),
suppression du SQL pur
Zend\Db\Query : abstraction DML, DQL, ainsi que DDL
(”alter”, ”create”, ”drop”) et DCL (”commit”, ”rollback”,
”savepoint”), supporte ANSI ainsi que les dialectes des SGBD
Zend\Db\ResultSet : modélisation des résultats
Zend\Db\Metadata : gestion du schéma
http://framework.zend.com/wiki/display/ZFDEV2/Zend+Db+2.0+Requirements