Upload
mickael-perraud
View
3.191
Download
3
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
{
Accès aux bases de
données relationnelles
et ORM en PHP
30/03/2011 Mickaël Perraud 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).
30/03/2011 Mickaël Perraud 3
{ Accès aux bases de données relationnelles et ORM en PHP
3 interventions :
Présentation de différents DBAL
Présentation de 2 ORM :
PropelDoctrine2
30/03/2011 Mickaël Perraud 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
30/03/2011 Mickaël Perraud 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'];}
30/03/2011 Mickaël Perraud 6
{ Retournons en arrière
Puis on a ”amélioré” :
//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());}
30/03/2011 Mickaël Perraud 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': //...
30/03/2011 Mickaël Perraud 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é)
30/03/2011 Mickaël Perraud 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) */
30/03/2011 Mickaël Perraud 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();}
30/03/2011 Mickaël Perraud 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();
30/03/2011 Mickaël Perraud 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)
30/03/2011 Mickaël Perraud 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"}
30/03/2011 Mickaël Perraud 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
30/03/2011 Mickaël Perraud 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; }}
30/03/2011 Mickaël Perraud 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 :
30/03/2011 Mickaël Perraud 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
30/03/2011 Mickaël Perraud 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
30/03/2011 Mickaël Perraud 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'));
30/03/2011 Mickaël Perraud 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));
30/03/2011 Mickaël Perraud 21
{ Zend_Db : lecture des résultats
Outre fetchAll() ou fetch() de PDO (renommé en fetchRow()), on dispose de :
fetchAssoc()
fetchCol()
fetchOne()
fetchPairs()
30/03/2011 Mickaël Perraud 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()
30/03/2011 Mickaël Perraud 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);
30/03/2011 Mickaël Perraud 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
30/03/2011 Mickaël Perraud 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();
30/03/2011 Mickaël Perraud 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;}
30/03/2011 Mickaël Perraud 27
{ Doctrine\DBAL : schéma manager
listDatabases()
listSequences()
listTables()
listTableColumns()
listTableDetails()
listTableForeignKeys()
listTableIndexes()
listViews()
createSchema()
30/03/2011 Mickaël Perraud 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);
30/03/2011 Mickaël Perraud 29
{ Ceux qu'il ne faut pas oublier
ADOdb : 5.11 (PHP 5)
PEAR::MDB2 : 2.5.0 en beta (PHP 5.3+)
30/03/2011 Mickaël Perraud 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