9.3. Zend_Db_Select

9.3.1. Einführung

Zend_Db_Select ist ein Werkzeug, dass dich beim Erstellen von datenbankunabhängigen SQL SELECT Abfragen unterstützt. Offensichtlich kann dies nicht perfekt sein, dennoch reicht die Unterstützung soweit, um deine Abfragen zwischen Datenbanken portierbar zu machen. Zusätzlich hilft es dir dabei deine Abfragen, gegen Attacken per SQL Injection, resistent zu machen.

Der einfachste Weg, um eine Instanz von Zend_Db_Select zu erstellen, ist die Verwendung der Zend_Db_Adapter::select() Methode.

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

$select = $db->select();
// $select ist nun ein Zend_Db_Select Objekt, das nur für die Verwendung des PDO_MYSQL 
// Adapters konfiguriert wurde.

?>

Du kannst deine SELECT Anfrage durch Verwendung des Objektes und seiner Methoden erstellen, und danach einen String generieren, um ihn für Abfragen zurück an den Zend_Db_Adapter zu übergeben.

<?php
	
//
// SELECT *
//     FROM round_table
//     WHERE noble_title = "Sir"
//     ORDER BY first_name
//     LIMIT 10 OFFSET 20
//

// Du kannst einen schrittweisen Stil verwenden...
$select->from('round_table', '*');
$select->where('noble_title = ?', 'Sir');
$select->order('first_name');
$select->limit(10,20);

// ...oder einen "fließenden" Stil:
$select->from('round_table', '*')
       ->where('noble_title = ?', 'Sir')
       ->order('first_name')
       ->limit(10,20);

// ungeachtet dessen, hole die Ergebnisse
$sql = $select->__toString();
$result = $db->fetchAll($sql);

// alternativ kannst du das $select Objekt selber übergeben;
// Zend_Db_Adapter ist smart genug , um __toString() auf die 
// Zend_Db_Select Objekte aufzurufen, um den Abfrage String zu erhalten.
$result = $db->fetchAll($select);

?>

Du kannst auch eingebundene Parameter in deinen Abfragen verwenden statt alles in schrittweise Anführungszeichen setzen.

<?php
	
//
// SELECT *
//     FROM round_table
//     WHERE noble_title = "Sir"
//     ORDER BY first_name
//     LIMIT 10 OFFSET 20
//

$select->from('round_table', '*')
       ->where('noble_title = :title')
       ->order('first_name')
       ->limit(10,20);

// ungeachtet dessen, hole die Ergebnisse unter Verwendung von eingebundenen Parametern
$params = array('title' => 'Sir');
$result = $db->fetchAll($select, $params);

?>

9.3.2. Spalten einer Tabelle

Um Spalten einer bestimmten Tabelle zu selektieren, verwende die from() Methode, unter Angabe der Tabelle und der Spalten, die du erhalten möchtest. Du kannst Tabellen- und Spaltenpseudonyme verwenden und du kannst from() so oft wie nötig verwenden.

<?php
	
// Erstelle ein $db Objekt unter der Annahme von Mysql als Adapter.
$select = $db->select();

// SELECT a, b, c FROM some_table
$select->from('some_table', 'a, b, c');
// entsprechend:
$select->from('some_table', array('a', 'b', 'c');

// SELECT bar.col FROM foo AS bar
$select->from('foo AS bar', 'bar.col');

// SELECT foo.col AS col1, bar.col AS col2 FROM foo, bar
$select->from('foo', 'foo.col AS col1');
$select->from('bar', 'bar.col AS col2');

?>

9.3.3. Spalten von verbundenen Tabellen

Um Spalten von verbundenen Tabellen zu selektieren, verwende die join() Methode. Zuerst gib den zu verbindenen Tabellennamen an, dann die Verbundbedingung und zuletzt die Spalten, die du vom Verbund erhalten möchtest. Du kannst join() so oft wie nötig verwenden.

<?php
	
// Erstelle ein $db Objekt unter der Annahme von Mysql als Adapter.
$select = $db->select();

//
// SELECT foo.*, bar.*
//     FROM foo
//     JOIN bar ON foo.id = bar.id
//
$select->from('foo', '*');
$select->join('bar', 'foo.id = bar.id', '*');

?>

Derzeit wird nur die JOIN Syntax unterstützt, keine LEFT JOINs, RIGHT JOINs, usw. In zukünftigen Versionen wird dies in datenbankneutraler Art und Weise unterstützt werden.

9.3.4. WHERE Bedingungen

Um WHERE Bedingungen hinzuzufügen, verwende die where() Methode. Du kannst entweder einen normalen String oder einen String mit Fragezeichen als Platzhalter und einen zu ersetzenden Wert übergeben (der Wert wird mittels Zend_Db_Adapter::quoteInto in Anführungszeichen gesetzt).

Mehrfache Aufrufe von where() verbinden die Bedingungen mit einem logischen AND; wenn du einen Verbund mit logischem OR benötigst, verwende orWhere().

<?php
	
// Erstelle ein $db Objekt, dann hole ein SELECT Werkzeug.
$select = $db->select();

//
// SELECT *
//     FROM round_table
//     WHERE noble_title = "Sir"
//     AND favorite_color = "yellow"
//
$select->from('round_table', '*');
$select->where('noble_title = "Sir"'); // eingebundener Wert
$select->where('favorite_color = ?', 'yellow'); // einzubindener Wert

//
// SELECT *
//     FROM foo
//     WHERE bar = "baz"
//     OR id IN("1", "2", "3")
//
$select->from('foo', '*');
$select->where('bar = ?', 'baz');
$select->orWhere('id IN(?)', array(1, 2, 3);

?>

9.3.5. GROUP BY Klauseel

Um Zeilen zu gruppieren, verwende die group() Methode so oft du möchtest.

<?php
	
// Erstelle ein $db Objekt, dann hole ein SELECT Werkzeug.
$select = $db->select();

//
// SELECT COUNT(id)
//     FROM foo
//     GROUP BY bar, baz
//
$select->from('foo', 'COUNT(id)');
$select->group('bar');
$select->group('baz');

// eine gleichwertiger Aufruf von group():
$select->group('bar, baz');

// ein weiterer gleichwertiger Aufruf von group():
$select->group(array('bar', 'baz'));

?>

9.3.6. HAVING Bedingungen

Um HAVING Bedingungen zu den selektierten Ergebnissen hinzuzufügen, verwende die having() Methode. Diese Methode ist identisch in der Funktion zur where() Methode.

Wenn du having() mehrfach aufrufst, werden die Bedingungen per AND miteinander verbunden; wenn du einen OR Verbunde benötigst, verwende orHaving().

<?php
	
// Erstelle ein $db Objekt, dann hole ein SELECT Werkzeug.
$select = $db->select();

//
// SELECT COUNT(id) AS count_id
//     FROM foo
//     GROUP BY bar, baz
//     HAVING count_id > "1"
//
$select->from('foo', 'COUNT(id) AS count_id');
$select->group('bar, baz');
$select->having('count_id > ?', 1);

?>

9.3.7. ORDER BY Klausel

Um Spalten zu sortieren, verwenden die order() Methode so oft du möchtest.

<?php
	
// Erstelle ein $db Objekt, dann hole ein SELECT Werkzeug.
$select = $db->select();

//
// SELECT * FROM round_table
//     ORDER BY noble_title DESC, first_name ASC
//
$select->from('round_table', '*');
$select->order('noble_title DESC');
$select->order('first_name');

// eine gleichwertiger Aufruf von order():
$select->order('noble_title DESC, first_name');

// ein weiterer gleichwertiger Aufruf von order():
$select->order(array('noble_title DESC', 'first_name'));

?>

9.3.8. LIMIT Begrenzung per Anzahl und Offset

Zend_Db_Select bietet Unterstützung für datenbankabstrahierte LIMIT Klauseln. Für viele Datenbanken, wie z.B. MySQL und PostgreSQL, ist dies relativ einfach, da sie die "LIMIT :count [OFFSET :offset]" Syntax unterstützen.

Für andere Datenbanken, wie z.B. Microsoft SQL und Oracle, ist dies nicht so einfach, da sie die LIMIT Klausel überhaupt nicht unterstützen. MS-SQL hat nur eine TOP Klausel and Oracle verlangt, dass eine Abfrage in bestimmter Art und Weise geschrieben wird, um LIMIT zu emulieren. Aufgrund der internen Arbeitsweise von Zend_Db_Select, können wir den SELECT "on-the-fly" umschreiben, um die LIMIT Funktionalität für die vorgenannten open-source Datenbanksysteme zu emulieren.

Um die erhaltenen Ergebnisse zu limitieren, per Anzahl und Offset, verwende die limit() Methode mit einer Anzahl und optionalem Offset.

<?php
	
// zuerst ein einfacher "LIMIT :count"
$select = $db->select();
$select->from('foo', '*');
$select->order('id');
$select->limit(10);

//
// In MySQL/PostgreSQL/SQLite wird dies wie folgt übersetzt:
//
// SELECT * FROM foo
//     ORDER BY id ASC
//     LIMIT 10
//
// Aber in Microsoft SQL wird es so übersetzt:
//
// SELECT TOP 10 * FROM FOO
//     ORDER BY id ASC
//
//

// nun einer etwas komplexeres "LIMIT :count OFFSET :offset"
$select = $db->select();
$select->from('foo', '*');
$select->order('id');
$select->limit(10, 20);

//
// In MySQL/PostgreSQL/SQLite wird dies wie folgt übersetzt:
//
// SELECT * FROM foo
//     ORDER BY id ASC
//     LIMIT 10 OFFSET 20
//
// Aber in Microsoft SQL, dass keine Offset Unterstützung hat, wird dies wie folgt übersetzt:
//
// SELECT * FROM (
//     SELECT TOP 10 * FROM (
//         SELECT TOP 30 * FROM foo ORDER BY id DESC
//     ) ORDER BY id ASC
// )
//
// Zend_Db_Adapter führt die Abfrageübersetzung automatisch durch.
//

?>

9.3.9. LIMIT Begrenzung per Seite und Offset

Zend_Db_Select bietet auch seitenbasierte Limitierung. Wenn du einen bestimmte "Seite" eines Ergebnisses erhalten möchtest, verwende die limitPage() Methode; zuerst übergebe die gewünschte Seitennummer und dann die Anzahl der Zeilen, die auf jeder Seite erscheinen sollen.

<?php
	
// erstelle den grundlegenden Select...
$select = $db->select();
$select->from('foo', '*');
$select->order('id');

// ... und begrenze auf Seite 3, wobei jede Seite 10 Zeilen hat
$select->limitPage(3, 10);

//
// In MySQL/PostgreSQL/SQLite wird dies wie folgt übersetzt:
//
// SELECT * FROM foo
//     ORDER BY id ASC
//     LIMIT 10 OFFSET 20
//

?>