30
Accès aux bases de données relationnelles et ORM en PHP

Présentation de DBAL en PHP (Nantes)

Embed Size (px)

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).

Citation preview

Page 1: Présentation de DBAL en PHP (Nantes)

Accès aux bases de données

relationnelles et ORM en PHP

Page 2: Présentation de DBAL en PHP (Nantes)

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).

Page 3: Présentation de DBAL en PHP (Nantes)

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

Page 4: Présentation de DBAL en PHP (Nantes)

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

Page 5: Présentation de DBAL en PHP (Nantes)

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 :

Page 6: Présentation de DBAL en PHP (Nantes)

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é” :

Page 7: Présentation de DBAL en PHP (Nantes)

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': //...

Page 8: Présentation de DBAL en PHP (Nantes)

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é)

Page 9: Présentation de DBAL en PHP (Nantes)

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) */

Page 10: Présentation de DBAL en PHP (Nantes)

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();}

Page 11: Présentation de DBAL en PHP (Nantes)

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();

Page 12: Présentation de DBAL en PHP (Nantes)

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)

Page 13: Présentation de DBAL en PHP (Nantes)

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"}

Page 14: Présentation de DBAL en PHP (Nantes)

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

Page 15: Présentation de DBAL en PHP (Nantes)

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; }}

Page 16: Présentation de DBAL en PHP (Nantes)

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 :

Page 17: Présentation de DBAL en PHP (Nantes)

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

Page 18: Présentation de DBAL en PHP (Nantes)

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

Page 19: Présentation de DBAL en PHP (Nantes)

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'));

Page 20: Présentation de DBAL en PHP (Nantes)

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));

Page 21: Présentation de DBAL en PHP (Nantes)

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()

Page 22: Présentation de DBAL en PHP (Nantes)

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()

Page 23: Présentation de DBAL en PHP (Nantes)

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);

Page 24: Présentation de DBAL en PHP (Nantes)

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

Page 25: Présentation de DBAL en PHP (Nantes)

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();

Page 26: Présentation de DBAL en PHP (Nantes)

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;}

Page 27: Présentation de DBAL en PHP (Nantes)

Mickaël Perraud29 juin 2011 27

Doctrine\DBAL : schéma manager

listDatabases()

listSequences()

listTables()

listTableColumns()

listTableDetails()

listTableForeignKeys()

listTableIndexes()

listViews()

createSchema()

Page 28: Présentation de DBAL en PHP (Nantes)

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);

Page 29: Présentation de DBAL en PHP (Nantes)

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+)

Page 30: Présentation de DBAL en PHP (Nantes)

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