Chapitre 3. Zend_Db

Table des matières

3.1. Zend_Db_Adapter
3.1.1. Introduction
3.1.2. Se protéger des injections SQL
3.1.3. Requêtes Directes
3.1.4. Transactions
3.1.5. Insertion de Lignes
3.1.6. Mise à jour de lignes
3.1.7. Suppression de Lignes
3.1.8. Récupération de lignes
3.2. Zend_Db_Profiler
3.2.1. Introduction
3.2.2. Utiliser le profileur
3.2.3. Utilisation avancée du profileur
3.3. Zend_Db_Select
3.3.1. Introduction
3.3.2. Sélectionner les colonnes d'une table : FROM
3.3.3. Sélectionner les colonnes de tables jointes : JOIN
3.3.4. Conditions WHERE
3.3.5. Clause GROUP BY
3.3.6. Conditions HAVING
3.3.7. Clause ORDER BY
3.3.8. Limiter le résultat par un décompte et un offset
3.3.9. Limiter le résultat par pages
3.4. Zend_Db_Table
3.4.1. Introduction
3.4.2. Mise en Route
3.4.3. Nom de la table et clé primaire
3.4.4. Insertion de lignes
3.4.5. Mise à jour de lignes
3.4.6. Suppression de lignes
3.4.7. Trouver des lignes par clé primaire
3.4.8. Aller chercher une ligne
3.4.9. Aller chercher plusieurs lignes
3.4.10. Ajouter votre propre logique
3.5. Zend_Db_Table_Row
3.5.1. Introduction
3.5.2. Aller chercher une ligne
3.5.3. Modifier les valeurs
3.6. Zend_Db_Table_Rowset
3.6.1. Introduction
3.6.2. Aller chercher un ensemble de lignes
3.6.3. Itérer sur l'ensemble de lignes

3.1. Zend_Db_Adapter

3.1.1. Introduction

Zend_Db_Adapter est l'API d'abstraction pour le Framework Zend. Basé sur PDO, vous pouvez utiliser Zend_Db_Adapter pour vous connecter sur tout les SGBD supportés, en utilisant la même API. Les SGB supportés sont entre autres : Microsoft SQL Server, MySQL, PostgreSQL, SQLite.

Pour créer une instance de Zend_Db_Adapter pour vous connecter à votre base de données, vous devez appeler Zend_Db::factory() avec le nom de l'adapteur et un tableau de paramètres décrivant la connexion. Par exemple, pour vous connecter à une base de données MySQL appelée "camelot" sur votre machine locale et avec le nom d'utilisateur "malory" :

<?php

require_once 'Zend/Db.php';

$params = array ('host'     => '127.0.0.1',
                 'username' => 'malory',
                 'password' => '******',
                 'dbname'   => 'camelot');

$db = Zend_Db::factory('PDO_MYSQL', $params);

?>

De la même manière, pour vous connecter à une base de données SQLite appelée "camelot" :

<?php

require_once 'Zend/Db.php';

$params = array ('dbname' => 'camelot');

$db = Zend_Db::factory('PDO_SQLITE', $params);

?>

Quelque soit le SGBD, vous aurez la possibilité d'utiliser exactement la même API pour interroger la base de données.

3.1.2. Se protéger des injections SQL

Vous devriez toujours échapper les valeurs destinées à être utilisées dans une requête SQL; cela permet de se prémunir des injections SQL. Zend_Db_Adapter fournies deux méthodes (via l'objet PDO) pour vous aider à échapper les valeurs.

La première est la méthode quote(). Elle va échapper une valeur scalaire conformément aux spécificités de la base de données. Si vous essayer d'échapper un tableau, la méthode va retourner une chaîne contenant chaque valeur du tableau, séparée par des virgules, chacune correctement protégée (c'est pratique dans le cas de fonctions qui prennent un paramètre de type liste).

<?php

// créé un objet $db, on suppose l'utilisation de MySQL

// échappement d'un scalaire
$value = $db->quote('St John"s Wort');
// $value is now '"St John\"s Wort"' (note the surrounding quotes)

// échappement d'un tableau
$value = $db->quote(array('a', 'b', 'c');
// $value vaut maintenant '"a", "b", "c"' (une chaîne séparée par des virgules)

?>

La seconde est la méthode quoteInto(). Vous fournissez une requête de base, avec des points d'interrogations comme caractère à remplacer, et un scalaire ou un tableau à protéger. C'est pratique pour construire rapidemant des requête et des clauses. Les scalaires et les tableaux sont traitées de la même façon que dans la méthode quote().

<?php
	
// créé un objet $db, on suppose l'utilisation de MySQL

// protection d'un scalaire dans une clause WHERE
$where = $db->quoteInto('id = ?', 1);
// $where vaut maintenant 'id = "1"' (notez bien les guillemets supplémentaires)

// protection d'un tableau dans la clause WHERE
$where = $db->quoteInto('id IN(?)', array(1, 2, 3));
// $where vaut maintenant 'id IN("1", "2", "3")' (une chaîne séparée par des virgules)

?>

3.1.3. Requêtes Directes

Une fois que vous avez une instance de Zend_Db_Adapter, vous pouvez exécuter directement des requêtes SQL. Zend_Db_Adapter passe cette requête dans l'objet PDO sous-jacent, qui les prépare et les exécute, et vous retourne ensuite une objet PDOStatement pour que vous puissez manipuler les résultats, s'il y en a.

<?php
        
// créé un objet $db et exécute ensuite la requête
// avec une requête SQL correctement protégée
$sql = $db->quoteInto(
    'SELECT * FROM example WHERE date > ?',
    '2006-01-01'
);
$result = $db->query($sql);

// utilisez l'objet de type PDOStatement $result pour récupérer toutes les lignes de résultat dans un tableau
$rows = $result->fetchAll();
?>

Vous pouvez associer automatiquement vos données à la requête. Cela signifie que vous pouvez définir de multiples marqueurs de remplacement réservés et nommés dans la requête, et ensuite passer un tableau de données qui vont se substituer à ces marqueurs. Les valeurs de remplacement sont automatiquement protégées, pour prévenir les attaques par injection SQL.

<?php
	
// créé un objet $db, et interroge la base de données.
// cette fois, utilisez un marqueur de remplacement.
$result = $db->query(
    'SELECT * FROM example WHERE date > :placeholder',
    array('placeholder' => '2006-01-01')
);

// utiliser le PDOStatement $result pour récupérer toutes les lignes de résultat dans un tableau
$rows = $result->fetchAll();

?>

Optionellement, vous pouvez préparer et lier manuellement une instruction SQL. Pour ce faire, utilisez la méthode prepare() pour obtenir un objet de type PDOStatement, que vous pouvez manipuler directement.

<?php
	
// créé un objet $db, et interroge la base de données.
// cette fois-ci, utilisez un objet PDOStatement pour une liaison manuelle.
$stmt = $db->prepare('SELECT * FROM example WHERE date > :placeholder');
$stmt->bindValue('placeholder', '2006-01-01');
$stmt->execute();

// utiliser le PDOStatement $result pour récupérer toutes les lignes de résultat dans un tableau
$rows = $stmt->fetchAll();

?>

3.1.4. Transactions

Par défaut, PDO (and ainsi Zend_Db_Adapter) sont en mode "auto-commit". Cela signifie que chaque requête est validée à chaque exécution. Si vous souhaitez les exécuter à l'intérieur d'une transaction, appelez simplement la méthode beginTransaction() vous pourrez alors valider (commit()) ou annuler (rollBack()) vos changements. Zend_Db_Adapter retourne en mode "auto-commit" jusqu'à ce que vous appeliez de nouveau beginTransaction().

<?php
	
// créé une objet $db, puis débute une transaction.
$db->beginTransaction();

// tente une requête
// si elle réussie, validez les changements avec commit()
// si elle échoue, annuler les changements avec rollback()
try {
    $db->query(...);
    $db->commit();
} catch (Exception $e) {
    $db->rollBack();
    echo $e->getMessage();
}

?>

3.1.5. Insertion de Lignes

Vous avez la possibilité d'utiliser la méthode insert() pour créer une instruction INSERT, et lui lier les données pour l'insertion. (Les données liées sont automatiquement protégées pour prévenir tout risque d'injection SQL).

La valeur retournée n'est pas le dernier identifiant d'insertion, la table ne contenant peut-être pas de colonne auto-incrémentée; à la place, la valeur retournée est le nombre de lignes affectées (habituellement 1). Si vous souhaitez l'ID du dernier identifiant généré, appelez la méthode lastInsertId() après l'insertion.

<?php
	
//
// INSERT INTO round_table
//     (noble_title, first_name, favorite_color)
//     VALUES ("King", "Arthur", "blue");
//

// créé un objet $db et ensuite...
// les données des colonnes à insérer en utilisant la syntaxe nom_colonne => valeur_colonne
$row = array (
    'noble_title'    => 'King',
    'first_name'     => 'Arthur',
    'favorite_color' => 'blue',
);

// la table dans laquelle la ligne doit être insérée
$table = 'round_table';

// insérez la ligne, et récupérez son identifiant
$rows_affected = $db->insert($table, $row);
$last_insert_id = $db->lastInsertId();

?>

3.1.6. Mise à jour de lignes

Vous avez la possibilité d'utiliser la méthode update() pour créer une instruction UPDATE et lier vos données pour la mise à jour. (Les données liées sont automatiquement protégées pour prévenir tout risque d'injection SQL).

Vous pouvez fournir une clause WHERE facultative pour spécifier quelle ligne mettre à jour. (Notez que la clause WHERE n'est pas un paramètre échappé, vous devez donc protéger vous-même sa valeur).

<?php
	
//
// UPDATE round_table
//     SET favorite_color = "yellow"
//     WHERE first_name = "Robin";
//

// crée un objet $db, en ensuite...
// les nouvelles valeurs à mettre à jour, en utilisant la syntaxe nom_colonne => valeur_colonne
$set = array (
    'favorite_color' => 'yellow',
);

// la table à mettre à jour
$table = 'round_table';

// la clause WHERE
$where = $db->quoteInto('first_name = ?', 'Robin');

// mise à jour de la table, et récupération du nombre de lignes affectées
$rows_affected = $db->update($table, $set, $where);

?>

3.1.7. Suppression de Lignes

Vous avez la possibilité d'utiliser la méthode delete() pour créer une instruction DELETE. Vous pouvez fournir optionellement une clause WHERE. (Notez que la clause WHERE n'est pas un paramètre échappé, vous devez donc protéger vous-même sa valeur).

<?php
	
//
// DELETE FROM round_table
//     WHERE first_name = "Patsy";
//

// crée un objet $db, en ensuite...
// la table dont on veut supprimer des données
$table = 'round_table';

// la clause WHERE
$where = $db->quoteInto('first_name = ?', 'Patsy');

// mise à jour de la table, et récupération du nombre de lignes affectées
$rows_affected = $db->delete($table, $where);

?>

3.1.8. Récupération de lignes

Bien que vous puissiez interroger la base de données directement avec la méthode query() tout ce que vous avez besoin de faire et des sélectionner des lignes et obtenir le résultat. La série de méthodes fetch*() vous permet de le faire. Pour chaque méthode fetch*(), vous passez une instruction SQL; si vous utilisez des marqueurs de remplacement dans cette instruction, vous devrez aussi passer un tableau contenant la valeur à échapper, pour qu'elle soient protégées et remplacées dans la requête. Les méthodes fetch*() sont :

  • fetchAll()

  • fetchAssoc()

  • fetchCol()

  • fetchOne()

  • fetchPairs()

  • fetchRow()

<?php
	
// crée un objet $db, en ensuite...

// ?? retrouve les colonnes de chaque ligne comme un tableau séquentiel ??
$result = $db->fetchAll(
    "SELECT * FROM round_table WHERE noble_title = :title",
    array('title' => 'Sir')
);

// ?? retrouve les colonnes de chaque ligne comme un tableau associatif ?? 
// la première colonne est utilisée comme la clé du tableau
$result = $db->fetchAssoc(
    "SELECT * FROM round_table WHERE noble_title = :title",
    array('title' => 'Sir')
);

// retrouve la première colonne de résultat
$result = $db->fetchCol(
    "SELECT first_name FROM round_table WHERE noble_title = :title",
    array('title' => 'Sir')
);

// retrouve uniquement la première valeur
$result = $db->fetchOne(
    "SELECT COUNT(*) FROM round_table WHERE noble_title = :title",
    array('title' => 'Sir')
);

// retrouve la série de paries clé/valeurs; la première colonne et le tableau de clé
// la seconde le tableau de valeurs 
$result = $db->fetchPairs(
    "SELECT first_name, favorite_color FROM round_table WHERE noble_title = :title",
    array('title' => 'Sir')
);

// retrouve uniquement la première ligne retournée
$result = $db->fetchRow(
    "SELECT * FROM round_table WHERE first_name = :name",
    array('name' => 'Lancelot')
);

?>