Rozdział 9. Zend_Db

Spis treści

9.1. Zend_Db_Adapter
9.1.1. Wprowadzenie
9.1.2. Zapobieganie atakom SQL Injection
9.1.3. Bezpośrednie zapytania
9.1.4. Transakcje
9.1.5. Wstawianie wierszy
9.1.6. Aktualizowanie wierszy
9.1.7. Usuwanie wierszy
9.1.8. Pobieranie wierszy
9.2. Zend_Db_Profiler
9.2.1. Wprowadzenie
9.2.2. Użycie profilera
9.2.3. Zaawansowane użycie profilera
9.3. Zend_Db_Select
9.3.1. Overview of the Select Object
9.3.2. Creating a Select Object
9.3.3. Building Select queries
9.3.4. Executing Select Queries
9.3.5. Other methods
9.4. Zend_Db_Table
9.4.1. Introduction to Table Class
9.4.2. Defining a Table Class
9.4.3. Creating an Instance of a Table
9.4.4. Inserting Rows to a Table
9.4.5. Updating Rows in a Table
9.4.6. Deleting Rows from a Table
9.4.7. Finding Rows by Primary Key
9.4.8. Querying for a Set of Rows
9.4.9. Querying for a Single Row
9.4.10. Retrieving Table Metadata Information
9.4.11. Caching Table Metadata
9.4.12. Customizing and Extending a Table Class
9.5. Zend_Db_Table_Row
9.5.1. Introduction
9.5.2. Fetching a Row
9.5.3. Writing rows to the database
9.5.4. Serializing and unserializing rows
9.5.5. Extending the Row class
9.6. Zend_Db_Table_Rowset
9.6.1. Introduction
9.6.2. Fetching a Rowset
9.6.3. Retrieving Rows from a Rowset
9.6.4. Retrieving a Rowset as an Array
9.6.5. Serializing and Unserializing a Rowset
9.6.6. Extending the Rowset class
9.7. Zend_Db_Table Relationships
9.7.1. Introduction
9.7.2. Defining Relationships
9.7.3. Fetching a Dependent Rowset
9.7.4. Fetching a Parent Row
9.7.5. Fetching a Rowset via a Many-to-many Relationship
9.7.6. Cascading Write Operations

9.1. Zend_Db_Adapter

9.1.1. Wprowadzenie

Zend_Db_Adapter jest uniwersalnym interfejsem dostępu do baz danych dla Zend Framework. Bazuje on na PDO. Używając interfejsu Zend_Db_Adapter możesz połączyć się z dowolnym wspieranym systemem bazodanowym używając tego samego API. Wspierane systemy to między innymi Microsoft SQL Server, MySQL, PostgreSQL, SQLite, i inne.

Aby utworzyć instancję interfejsu Zend_Db_Adapter dla określonego systemu bazodanowego, musisz wywołać metodę Zend_Db::factory() przekazując jej nazwę systemu bazodanowego oraz tablicę parametrów potrzebnych do połączenia. Na przykład, aby połączyć się z bazą MySQL nazwaną "camelot" znajdującą się pod adresem lokalnym, jako użytkownik "malory" możesz zrobić tak:

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

?>

Podobnie, aby połączyć się z bazą SQLite nazwaną "camelot.sq3":

<?php

require_once 'Zend/Db.php';

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

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

?>

Podobnie, aby połączyć się z bazą danych SQLite2 o nazwie "camelot.sq2": Dla bazy sqlite opartej na pamięci nie określaj prefiksu dsn i użyj bazy danych o nazwie ":memory:".

<?php

require_once 'Zend/Db.php';

$params = array ('dbname' => 'camelot.sq2',
                 'dsnprefix' => 'sqlite2');

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

?>

W każdym z tych przypadków będziesz mógł używać takiego samego API aby tworzyć zapytania do bazy.

9.1.2. Zapobieganie atakom SQL Injection

Powinieneś zawsze cytować wartości, które będą użyte w wyrażeniu SQL; zapobiega to atakom SQL Injection. Zend_Db_Adapter zapewnia dwie metody (poprzez obiekt PDO) aby pomóc Ci manualnie cytować wartości.

Pierwsza z nich to metoda quote(). Zacytuje ona wartość skalarną odpowiednio dla twojego adaptera bazy danych; jeśli spróbujesz cytować tablicę, metoda zwróci łańcuch znaków zawierający wartości elementów tablicy oddzielone przecinkami. Każda wartość zostanie odpowiednio zacytowana. (Jest to użyteczne dla funkcji przyjmujących parametr w postaci listy).

<?php

// tworzymy obiekt $db, zakładając, że adapter to Mysql

// cytujemy łańcuch znaków
$value = $db->quote('St John"s Wort');
// $value ma teraz wartość '"St John\"s Wort"' (zwróć uwagę na otaczające ją cudzysłowy)

// cytujemy tablicę
$value = $db->quote(array('a', 'b', 'c');
// $value ma teraz wartość '"a", "b", "c"' (elementy tablicy oddzielone przecinkami)

?>

Druga metoda to quoteInto(). Przekazujesz do niej podstawowy łańcuch znaków ze znacznikiem w postaci znaku zapytania, a następnie jedną wartość skalarną lub tablicę która ma być wstawiona. Jest to użyteczne przy tworzeniu zapytań i warunków po kolei. Wartości skalarne i tablice działają tak samo jak w metodzie quote().

<?php

// tworzymy obiekt $db, zakładając, że adapter to Mysql

// cytujemy łańcuch znaków dla warunku WHERE
$where = $db->quoteInto('id = ?', 1);
// $where ma teraz wartość 'id = "1"' (zwróć uwagę na otaczające ją cudzysłowy)

// cytujemy tablicę dla warunku WHERE
$where = $db->quoteInto('id IN(?)', array(1, 2, 3));
// $where ma teraz wartość 'id IN("1", "2", "3")' (elementy tablicy oddzielone przecinkami)

?>

9.1.3. Bezpośrednie zapytania

Gdy posiadasz instancję Zend_Db_Adapter, możesz wykonywać zapytania bezpośrednio jako SQL. Zend_Db_Adapter przekazuje te zapytania do obiektu PDO, który je przygotowuje i wykonuje, a następnie zwraca Ci obiekt PDOStatement abyś mógł manipulować danymi wynikowymi (jeśli jekieś są).

<?php

// tworzymy obiekt $db, a następnie wykonujemy
// odpowiednio zacytowane zapytanie SQL.
$sql = $db->quoteInto(
    'SELECT * FROM example WHERE date > ?',
    '2006-01-01'
);
$result = $db->query($sql);

// używamy PDOStatement aby pobrać wszystkie wiersze w postaci tablicy
$rows = $result->fetchAll();

?>

Możesz dołączać dane do zapytania automatycznie. Oznacza to, że możesz użyć dowolną ilość nazwanych znaczników w zapytaniu, a następnie przekazać tablicę danych które odpowiadają tym znacznikom. Odpowiadające wartości są automtycznie cytowane, lepiej zabezpieczając przed atakami SQL injection.

<?php

// tworzymy obiekt $db, a następnie wykonujemy zapytanie
// tym razem używając podmieniania znaczników
$result = $db->query(
    'SELECT * FROM example WHERE date > :placeholder',
    array('placeholder' => '2006-01-01')
);

// używamy PDOStatement aby pobrać wszystkie wiersze w postaci tablicy
$rows = $result->fetchAll();

?>

Opcjonalnie możesz przygotować i dołączyć dane do zapytań SQL ręcznie. Aby to zrobić użyj metody prepare() aby otrzymać przygotowane zapytanie PDOStatement, przy którym możesz bezpośrednio manipulować.

<?php

// tworzymy obiekt $db, a następnie wykonujemy zapytanie
// tym razem przygotowujemy PDOStatement do ręcznego dołączania.
$stmt = $db->prepare('SELECT * FROM example WHERE date > :placeholder');
$stmt->bindValue('placeholder', '2006-01-01');
$stmt->execute();

// używamy PDOStatement aby pobrać wszystkie wiersze w postaci tablicy
$rows = $stmt->fetchAll();

?>

9.1.4. Transakcje

Domyślnie, PDO (a więc i Zend_Db_Adapter) działają w trybie "auto-commit". Oznacza to, że wszystkie zapytania są realizowane natychmiast po ich wykonaniu. Jeśli chcesz je wykonać wewnątrz transakcji, w prosty sposób wywołak metodę beginTransaction(), a następnie commit() aby zrealizować zapytania lub rollBack() aby je cofnąć. Zend_Db_Adapter wraca wtedy do trybu "auto-commit" dopóki znów nie wywołasz metody beginTransaction().

<?php

// utwórz obiekt $db, a następnie rozpocznij transakcję
$db->beginTransaction();

// próbujemy wykonać zapytanie
// jeśli uda się, zatwierdzamy zapytanie;
// jeśli nie uda się, cofamy zapytanie.
try {
    $db->query(...);
    $db->commit();
} catch (Exception $e) {
    $db->rollBack();
    echo $e->getMessage();
}

?>

9.1.5. Wstawianie wierszy

Dla wygody możesz użyć metody insert(), która utworzy zapytanie INSERT oraz dołączy do niego dane, które mają wstawione. (Dołączone dane zostaną automatycznie zacytowane aby pomóc zapobiegać atakom SQL injection.)

Zwracana wartość nie jest ostatnio wstawionym numerem ID, ponieważ tabela nie musi posiadać automatycznie inkrementowanej kolumny; zwracana wartość jest liczbą wstawionych wierszy (najczęściej 1). Jeśli chcesz otrzymać wartość ID ostatnio wstawionego rekordu, wywołaj metodę lastInsertId() po wstawieniu rekordu.

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

// tworzymy obiekt $db, a następnie...
// dane do wstawienia w postaci nazwa_kolumny => wartość
$row = array (
    'noble_title'    => 'King',
    'first_name'     => 'Arthur',
    'favorite_color' => 'blue',
);

// nazwa tabeli do której ma być wstawiony wiersz
$table = 'round_table';

// wstawiamy wiersz i pobieramy jego ID
$rows_affected = $db->insert($table, $row);
$last_insert_id = $db->lastInsertId();

?>

9.1.6. Aktualizowanie wierszy

Dla wygody możesz użyć metody update(), która utworzy zapytanie UPDATE oraz dołączy do niego dane które mają być uaktualnione. (Dołączone dane zostaną automatycznie zacytowane aby pomóc zapobiegać atakom SQL injection.)

Możesz przekazać do metody opcjonalny warunek WHERE aby określić, które wiersze chcesz zaktualizować.

<?php

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

// tworzymy obiekt $db, a następnie...
// dane do aktualizowania w postaci nazwa_kolumny => wartość
$set = array (
    'favorite_color' => 'yellow',
);

// nazwa tabeli w której ma być aktualizowany wiersz
$table = 'round_table';

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

// uaktualniamy tabelę i pobieramy liczbę uaktualnionych wierszy
$rows_affected = $db->update($table, $set, $where);

?>

Jeśli masz kilka warunków WHERE, które powinny być dołączone do zapytania UPDATE, możesz przekazać tablicę tych warunków do metody update(). Takie warunki zostaną połączone za pomocą operatora AND. Łączenie warunków za pomocą operatora OR musi być przeprowadzone ręcznie:

<?php
$set = array('favorite_color' => 'yellow');

// kilka warunków połączonych za pomocą AND
$where   = array(
    $db->quoteInto('first_name = ?', $firstName),
    $db->quoteInto('noble_title = ?', $nobleTitle)
    );
$count   = $db->update('round_table', $set, $where);

// kilka warunków połączonych za pomocą OR
$firstNameOne = $db->quote($firstNameOne);
$firstNameTwo = $db->quote($firstNameTwo);
$where        = "first_name = $firstNameOne OR first_name = $firstNameTwo";
$count        = $db->update('round_table', $set, $where);

?>
[Notatka] Notatka

Wartości i identyfikatory użyte w warunkach WHERE nie są automatycznie cytowane. Jeśli masz wartości lub identyfikatory wymagające cytowania, powinieneś sam to zrobić. Użyj metod quote(), quoteInto(), oraz quoteIdentifier() adaptera bazy danych.

9.1.7. Usuwanie wierszy

Dla wygody możesz użyć metody delete(), która utworzy zapytanie DELETE; możesz przekazać do metody opcjonalny warunek WHERE aby określić, które wiersze chcesz usunąć. (Zauważ, że warunek WHERE nie jest dołączanym parametrem, więc musisz sam zacytować jego wartości.)

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

// tworzymy obiekt $db, a następnie...
// nazwa tabeli z której usuwamy wiersz
$table = 'round_table';

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

// usuwamy wiersz i pobieramy liczbę usuniętych wierszy
$rows_affected = $db->delete($table, $where);

?>

Tak jak w metodzie update(), możesz użyć tablicy warunków WHERE dla argumentu $where metody delete().

[Notatka] Notatka

Wartości i identyfikatory użyte w warunkach WHERE nie są automatycznie cytowane. Jeśli masz wartości lub identyfikatory wymagające cytowania, powinieneś sam to zrobić. Użyj metod quote(), quoteInto(), oraz quoteIdentifier() adaptera bazy danych.

9.1.8. Pobieranie wierszy

Chociaż możesz wykonać zapytanie bezpośrednio za pomocą metody query(), często wszystko czego potrzebujesz to wybrać wiersze i pobrać wyniki. Seria metod fetch*() robi to dla ciebie. Do każdej z metod fetch*(), przekazujesz zapytanie SQL SELECT; jeśli użyjesz nazwanych znaczników w zapytaniu, możesz przekazać tablicę wartości, które mają być zacytowane i zamienione w zapytaniu. Metody fetch*() to:

  • fetchAll()

  • fetchAssoc()

  • fetchCol()

  • fetchOne()

  • fetchPairs()

  • fetchRow()

<?php
	
// tworzymy obiekt $db, a następnie...

// pobieramy wszystkie kolumny wszystkich wierszy jako sekwencyjną tablicę
$result = $db->fetchAll(
    "SELECT * FROM round_table WHERE noble_title = :title",
    array('title' => 'Sir')
);

// pobieramy wszystkie kolumny wszystkich wierszy jako tablicę
// asocjacyjną; pierwsza kolumna jest używana jako klucz tablicy
$result = $db->fetchAssoc(
    "SELECT * FROM round_table WHERE noble_title = :title",
    array('title' => 'Sir')
);

// pobieramy pierwszą kolumnę wszystkich zwróconych wierszy
$result = $db->fetchCol(
    "SELECT first_name FROM round_table WHERE noble_title = :title",
    array('title' => 'Sir')
);

// pobieramy tylko pierwszą zwróconą wartość
$result = $db->fetchOne(
    "SELECT COUNT(*) FROM round_table WHERE noble_title = :title",
    array('title' => 'Sir')
);

// pobieramy serię par klucz-wartość; pierwsza kolumna jest
// kluczem tablicy, a druga jest wartością
$result = $db->fetchPairs(
    "SELECT first_name, favorite_color FROM round_table WHERE noble_title = :title",
    array('title' => 'Sir')
);

// pobieramy tylko pierwszy zwrócony wiersz
$result = $db->fetchRow(
    "SELECT * FROM round_table WHERE first_name = :name",
    array('name' => 'Lancelot')
);

?>