Kapitel 6. Zend_Db
Zurück |  | Weiter

## Kapitel 6. Zend_Db

## 6.1. Zend_Db_Adapter

### 6.1.1. Einführung

`Zend_Db_Adapter` ist die Datenbankabstraktionsschicht des Zend-Frameworks. Sie basiert auf PDO und erlaubt es, über eine einheitliche API (Schnittstelle) Verbindungen zu allen von PDO unterstützten Datenbanksystemen aufzubauen und mit diesen zu arbeiten. Unterstützte Datenbanken sind unter anderem: Microsoft SQL Server, MySQL, PostgreSQL und SQLite.

Um eine Instanz von `Zend_Db_Adapter` für ein spezielles Datenbank-Backend zu erstellen muss man `Zend_Db::factory()` mit dem Namen des Adapters und einem Array von Parametern aufrufen, welche die Verbindung beschreiben. Hier ein Beispiel für einen Verbindungsaufbau zu einer MySQL-Datenbank namens "camelot" auf einem lokalen Server unter Verwendung des Benutzernamens "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);

    ?>

Ähnlich erfolgt der Verbindungsaufbau zu einer SQlite-Datenbank namens "camelot.sq3":

    <?php

    require_once 'Zend/Db.php';

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

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

    ?>

Ähnlich erfolgt der Verbindungsaufbau zu einer SQLite2-Datenbank namens "camelot.sq2":

    <?php

    require_once 'Zend/Db.php';

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

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

    ?>

Wenn eine im RAM liegende SQlite-Datenbank benutzt werden soll wird kein DSN-Präfix angegeben und der Datenbankname lautet ":memory:".

So oder so kann immer die gleiche API verwendet werden um Abfragen auf die Datenbank durchzuführen.

### 6.1.2. Quoting[1] gegen SQL-Injection

Das so genannte Quoting sollte auf alle Werte angewendet werden, die in SQL-Anweisungen eingesetzt werden; dies hilft dabei, Angriffen per SQL-Injection vorzubeugen. `Zend_Db_Adapter` stellt (über das zugrunde liegende PDO Objekt) zwei hilfreiche Methoden für diesen Zweck bereit.

Die erste davon ist die `quote()`-Methode. Sie nimmt die für den jeweils verwendeten Datenbank-Adapter nötigen Umwandlungen vor; der Methode kann auch ein Array übergeben werden, dann gibt sie einen durch Kommata getrennten String mit den Array-Werten zurück, wobei jeder Wert entsprechend umgeschrieben wird (dies ist hilfreich für Funktionen, die einen Listen-Parameter erwarten).

    <?php

    // Erstelle ein $db-Objekt (am Beispiel eines Mysql-Adapters)

    // Benutzung von quote() für einen einzelnen Wert
    $value = $db->quote('St John"s Wort');
    // $value ist nun '"St John\"s Wort"' (beachten Sie die umschließenden Anführungszeichen)

    // Benutzung von quote() für ein Array
    $value = $db->quote(array('a', 'b', 'c');
    // $value ist nun '"a", "b", "c"' (ein durch Kommata getrennter String)

    ?>

Die Zweite ist die `quoteInto()`-Methode. Sie erwarten einen String mit einem Fragezeichen als Platzhalter und entweder einen einzelnen Wert oder ein Array von Werten, der/die an dieser Stelle eingefügt werden soll/en. Dies ist nützlich um Abfragen oder Bedingungen auf die Schnelle zu generieren. Die Behandlung der Werte ist dabei identisch wie bei der Nutzung der `quote()`-Methode.

    <?php

    // Erstelle ein $db-Objekt (am Beispiel eines Mysql-Adapters)

    // setze einen einzelnen Wert in eine WHERE-Bedingung ein
    $where = $db->quoteInto('id = ?', 1);
    // $where ist nun 'id = "1"' (beachte die umschließenden Anführungszeichen)

    // setze die Inhalte eines Arrays in eine WHERE-Bedingung ein
    $where = $db->quoteInto('id IN(?)', array(1, 2, 3));
    // $where ist nun 'id IN("1", "2", "3")' (ein durch Kommata getrennter String)

    ?>

### 6.1.3. Direkte Abfragen

Nachdem man eine Instanz von `Zend_Db_Adapter` hat, kann man direkt SQL-Abfragen ausführen. `Zend_Db_Adapter` übergibt diese Abfragen an das zugrunde liegende PDO-Objekt, welches sie aufbereitet und ausführt, und gibt ein PDOStatement-Objekt zurück, damit man die Ergebnisse (soweit vorhanden) weiter verarbeiten kann.

    <?php

    // Erstelle ein $db-Objekt und stelle dann eine
    // sicher formatierte SQL-Abfrage an die Datenbank.
    $sql = $db->quoteInto(
        'SELECT * FROM example WHERE date > ?',
        '2006-01-01'
    );
    $result = $db->query($sql);

    // verwende das PDOStatement $result, um alle Zeilen als Array abzurufen
    $rows = $result->fetchAll();

    ?>

Man kann Daten automatisch in eine Abfrage einbinden. Das bedeutet, dass man mehrere benannte Platzhalter in der Abfrage unterbringen und dann ein Array mit den Daten übergeben kann, welche diese Platzhalter ersetzen. Die ersetzten Werte werden dabei automatisch umgeschrieben, um größere Sicherheit gegen Angriffe per SQL-Injection zu bieten.

    <?php

    // Erstelle ein $db-Objekt und frage dann die Datenbank ab.
    // Dieses Mal: Verwende Platzhalter.
    $result = $db->query(
        'SELECT * FROM example WHERE date > :placeholder',
        array('placeholder' => '2006-01-01')
    );

    // verwende das PDOStatement $result, um alle Zeilen als Array abzurufen
    $rows = $result->fetchAll();

    ?>

Optional ist es auch möglich, Daten manuell aufzubereiten und mit der SQL-Abfrage zu verbinden. Hierfür verwendet man die `prepare()`-Methode, um ein vorbereitetes `PDOStatement` zu erhalten, welches man direkt bearbeiten kann.

    <?php

    // Erstelle ein $db-Objekt und frage dann die Datenbank ab.
    // Dieses Mal: Bereite ein PDOStatement für das manuelle Verbinden auf.
    $stmt = $db->prepare('SELECT * FROM example WHERE date > :placeholder');
    $stmt->bindValue('placeholder', '2006-01-01');
    $stmt->execute();

    // verwende das PDOStatement $result, um alle Zeilen als Array abzurufen
    $rows = $stmt->fetchAll();

    ?>

### 6.1.4. Transaktionen

Standardmäßig sind PDO (und damit auch `Zend_Db_Adapter`) im "auto-commit" Modus. Das heißt, dass alle Abfragen sofort unwiderruflich ausgeführt werden, wenn sie gestellt werden. Wenn sie innerhalb einer Transaktion ausgeführt werden sollen, kann einfach die Methode `beginTransaction()` aufgerufen werden, dann besteht die Möglichkeit, Änderungen entweder mit `commit()` zu bestätigen oder mit `rollBack()` rückgängig zu machen. `Zend_Db_Adapter` kehrt in den "auto-commit" Modus zurück, bis `beginTransaction()` wieder aufgerufen wird.

    <?php

    // Erstelle ein $db-Objekt und starte dann eine Transaktion
    $db->beginTransaction();

    // Versuche eine Abfrage
    // wenn erfolgreich, verarbeite die Änderungen
    // wenn nicht erfolgreich, setze Änderungen zurück
    try {
        $db->query(...);
        $db->commit();
    } catch (Exception $e) {
        $db->rollBack();
        echo $e->getMessage();
    }

    ?>

### 6.1.5. Zeilen einfügen

Der Einfachheit halber kann man die `insert()`-Methode verwenden, um eine INSERT Anweisung zu erstellen und mit den einzufügenden Daten zu verknüpfen. (Die verknüpften Daten werden automatisch umgeschrieben, um Angriffe per SQL-Injection zu vermeiden.)

Der Rückgabewert ist nicht die zuletzt eingefügte ID, da eine Tabelle keine automatisch inkrementierte Spalte haben muss. Stattdessen enthält der Rückgabewert die Zahl der beeinflussten Zeilen (normalerweise 1). Um die ID des zuletzt eingefügten Datensatzes zu ermitteln, kann man die `lastInsertId()`-Methode nach dem Einfügen aufrufen.

    <?php

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

    // Erstelle ein $db-Objekt und dann...
    // die einzufügenden Daten im Spalte => Wert Format
    $row = array (
        'noble_title'    => 'King',
        'first_name'     => 'Arthur',
        'favorite_color' => 'blue',
    );

    // die Tabelle, in der die Daten eingefügt werden sollen
    $table = 'round_table';

    // füge die Daten ein und hole die ID der eingefügten Zeile
    $rows_affected = $db->insert($table, $row);
    $last_insert_id = $db->lastInsertId();

    ?>

### 6.1.6. Zeilen aktualisieren

Der Einfachheit halber kann man die `update()`-Methode verwenden, um eine UPDATE-Anweisung zu erstellen und mit den zu aktualisierenden Daten zu verknüpfen. (Die verknüpften Daten werden automatisch umgeschrieben, um Angriffe per SQL-Injection zu vermeiden.)

Es ist möglich, eine WHERE-Klausel zu übergeben um festzulegen, welche Zeilen aktualisiert werden sollen. (Man beachte, dass die WHERE-Klausel kein verknüpfter Parameter ist, so dass die enthaltenen Werte manuell aufbereitet werden müssen, um Angriffe per SQL-Injection zu vermeiden.)

    <?php

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

    // Erstelle ein $db-Objekt und dann...
    // die neuen Werte, die aktualisiert werden sollen, im Spalte => Wert Format.
    $set = array (
        'favorite_color' => 'yellow',
    );

    // die zu aktualisierende Tabelle
    $table = 'round_table';

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

    // aktualisiere die Tabelle und hole die Anzahl der beeinflussten Zeilen
    $rows_affected = $db->update($table, $set, $where);

    ?>

### 6.1.7. Zeilen löschen

Der Einfachheit halber kann man die `delete()`-Methode verwenden, um eine DELETE Anweisung zu erstellen. Es ist möglich, eine WHERE-Klausel zu übergeben um anzugeben, welche Zeilen gelöscht werden sollen. (Man beachte, dass die WHERE-Klausel kein verknüpfter Parameter ist, so dass die enthaltenen Werte manuell aufbereitet werden müssen, um Angriffe per SQL-Injection zu vermeiden.)

    <?php

    //
    // DELETE FROM round_table
    //     WHERE first_name = "Patsy";
    //

    // Erstelle ein $db-Objekt und dann...
    // die Tabelle, aus der gelöscht werden soll
    $table = 'round_table';

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

    // aktualisiere die Tabelle und hole die Anzahl der beeinflussten Zeilen
    $rows_affected = $db->delete($table, $where);

    ?>

### 6.1.8. Zeilen abrufen

Obwohl man die Datenbank direkt mit der `query()`-Methode abfragen kann, will man meistens nur Zeilen selektieren und die Ergebnisse zurückerhalten. Diese Aufgabe erledigen die `fetch*()`-Methoden. Für jede der `fetch*()`-Methoden übergibt man eine SQL-SELECT-Anweisung; wenn man benannte Platzhalter in der Anweisung verwendet, kann man zudem ein Array mit zu verknüpfenden Werten übergeben, die dann zum Schutz gegen SQL-Injection aufbereitet und in die Anweisung eingesetzt werden. Diese `fetch*()`-Methoden gibt es:

- `fetchAll()`

- `fetchAssoc()`

- `fetchCol()`

- `fetchOne()`

- `fetchPairs()`

- `fetchRow()`

    <?php

    // Erstelle ein $db-Objekt und dann...

    // hole alle Spalten aller Zeilen als sequentielles Array
    $result = $db->fetchAll(
        "SELECT * FROM round_table WHERE noble_title = :title",
        array('title' => 'Sir')
    );

    // hole alle Spalten aller Zeilen als assoziatives Array
    // die erste Spalte wird als Array-Schlüssel verwendet
    $result = $db->fetchAssoc(
        "SELECT * FROM round_table WHERE noble_title = :title",
        array('title' => 'Sir')
    );

    // hole die erste Spalte aller zurück gelieferten Zeilen
    $result = $db->fetchCol(
        "SELECT first_name FROM round_table WHERE noble_title = :title",
        array('title' => 'Sir')
    );

    // hole nur den ersten Wert
    $result = $db->fetchOne(
        "SELECT COUNT(*) FROM round_table WHERE noble_title = :title",
        array('title' => 'Sir')
    );

    // hole eine Sequenz von Schlüssel-Wert-Paaren; die erste Spalte ist
    // der Array-Schlüssel, die zweite Spalte ist der Array-Wert
    $result = $db->fetchPairs(
        "SELECT first_name, favorite_color FROM round_table WHERE noble_title = :title",
        array('title' => 'Sir')
    );

    // hole nur die erste der zurück gelieferten Zeilen
    $result = $db->fetchRow(
        "SELECT * FROM round_table WHERE first_name = :name",
        array('name' => 'Lancelot')
    );

    ?>

[1] Der englische Begriff "quoting" bezeichnet das Entfernen einer Sonderbedeutung eines Zeichens durch Voranstellen oder Umklammern mit speziellen Zeichen (Backslash, Anführungsstriche usw.) Die genauen Regeln für das jeweilige Quoting sind abhängig vom Zielsystem, an welches die Daten übergeben werden.

Zurück |  | Weiter
5.7. Standardrouten | Zum Anfang | 6.2. Zend_Db_Profiler