Zend_Db_Select
Wprowadzenie
Zend_Db_Select jest narzędziem pomagającym w budowaniu zapytań SQL
SELECT w sposób niezależny od rodzaju bazy danych. Oczywiście nie
może to być perfekcyjne, ale pomaga w tym, aby zapytania były
przenośne pomiędzy różnymi systemami bazodanowymi. Dodatkowo
pomaga to w uodpornieniu zapytań na ataki SQL injection.
Najprostszy sposób utworzenia instancji Zend_Db_Select to użycie
metody Zend_Db_Adapter::select().
'127.0.0.1',
'username' => 'malory',
'password' => '******',
'dbname' => 'camelot'
);
$db = Zend_Db::factory('PDO_MYSQL', $params);
$select = $db->select();
// $select jest teraz obiektem Zend_Db_Select skonfigurowanym do użycia z adapterem PDO_MYSQL.
?>]]>
Wtedy konstruujesz zapytanie SELECT używając tego obiektu i jego metod,
a następnie generujesz łańcuch znaków który przekazujesz spowrotem
do obiektu Zend_Db_Adapter w celu wykonania zapytania.
from('round_table');
$select->where('noble_title = ?', 'Sir');
$select->order('first_name');
$select->limit(10,20);
// ...lub stylu łańcuchowego:
$select->from('round_table')
->where('noble_title = ?', 'Sir')
->order('first_name')
->limit(10,20);
// pobieramy dane:
$sql = $select->__toString();
$result = $db->fetchAll($sql);
// druga alternatywa: możesz przekazać sam obiekt $select;
$result = $db->fetchAll($select);
// trzecia alternatywa: tworzysz obiekt Zend_Db_Statement
// lub PDOStatement bezpośrednio z obiektu $select:
$stmt = $select->query();
$result = $stmt->fetchAll();
?>]]>
Możesz także użyć parametrów wstawianych w miejsce nazwanych
znaczników, zamiast cytowania parametrów po kolei.
from('round_table', '*')
->where('noble_title = :title')
->order('first_name')
->limit(10,20);
// pobieramy wyniki używająć parametrów wstawianych w miejsce znaczników
$params = array('title' => 'Sir');
$result = $db->fetchAll($select, $params);
?>]]>
Kolumny z tabel (FROM)
Aby wybrać kolumny z określonej tabeli, użyj metody from(),
określając tabelę oraz kolumny których potrzebujesz. Możesz
użyć aliasów dla tabel oraz kolumn, i możesz używać metody from()
tyle razy ile potrzebujesz.
select();
// SELECT some_table.`a`, some_table.`b`, some_table.`c`
// FROM `some_table` some_table
$select->from('some_table', array('a', 'b', 'c'));
// SELECT bar.`col`
// FROM `foo` bar
$select->from(array('foo' => 'bar'), 'col');
?>]]>
Drugi argument do metody from() jest wartością skalarną dla
pojedynczej nazwy kolumny, lub tablicą dla wielu nazw kolumn.
Wartość skalarna lub element tablicy może być łańcuchem znaków,
który jest interpretowany jako prosta nazwa kolmny i jest ona
cytowana, a na początek nazwy dołączany jest alias tabeli. Jeśli
pominiesz drugi argument, przyjęta zostanie jego domyślna wartość
'*', co nie jest cytowane, ale do tej wartości też
zostanie dołączony alias tabeli. Jeśli nie chcesz by kolumny z tej
tabeli znajdowały się w zestawie wyników, użyj pustej tablicy
array().
Nie określaj wielu kolumn jako pojedynczego łańcucha znaków z
nazwami kolumn oddzielonymi przecinkami. Taka składnia była używana
we wcześniejszych wydaniach Zend_Db i nie jest ona już obsługiwana.
Użyj zamiast tego tablicy.
Alias tabeli jest dołączany do każdego elementu będącego łańcuchem
znaków znajdującym się w drugim argumencie, ale jeśli element jest
obiektem typu Zend_Db_Expr, to jego wartość w postaci łańcucha
znaków jest używana bez cytowania oraz dodawania alisu tabeli.
select();
// SELECT foo.col AS col1, bar.col AS col2
// FROM foo, bar
$select->from('foo', array(
new Zend_Db_Expr('foo.col AS col1'),
new Zend_Db_Expr('CURDATE()')
);
$select->from('bar', new Zend_Db_Expr('bar.col AS col2'));
?>]]>
Użyj tablicy asocjacyjnej aby zadeklarować aliasy dla tabel. Klucz
tablicy jest nazwą tabeli, a wartośc jest aliasem. Jeśli zamiast
tego określisz nazwę tabeli jako zwykły łańcuch znaków, a nie jako
asocjacyjną tablicę, to Zend_Db_Select wygeneruje alias dla tabeli.
Domyślnie generowany jest alias o tej samej nazwie jak tabela, czyli
tak jak na przykład w zapytaniu SQL
"SELECT foo.* FROM `foo` foo". Jeśli dodasz tę samą
tabelę więcej niż raz, na przykład w złączeniu SELF JOIN,
Zend_Db_Select zadeklaruje unikalny alias dla każdej instancji
tabeli, jak np. "foo_1", "foo_2", itd.
Kolumny ze złączonych tabel (JOIN)
Aby wybrać kolumny używając złączonych tabel, użyj metody join().
Wpierw określ nazwę złączanej tabeli, następnie wyrażenie łączące,
a na końcu kolumny które potrzebujesz dołączyć. Możesz użyć metody
join() tyle razy ile potrzebujesz.
select();
// SELECT foo.*, bar.*
// FROM `foo` foo
// JOIN `bar` bar ON foo.id = bar.id
$select->from('foo');
$select->join('bar', 'foo.id = bar.id');
?>]]>
Pierwszy argument dla metody join() określa tabelę. Działa to
analogicznie jak pierwszy argument metody from(), w którym nazwa
tabeli może być łańcuchem znaków lub asocjacyjną tablicą mapującą
nazwę tabeli do aliasu.
Drugi argument dla metody join() jest wyrażeniem tworzącym warunek
złączenia. Możesz tu użyć aliasów tabel, ale pamiętaj, że jeśli nie
określiłeś aliasów tabel, to Zend_Db_Select sam je wygeneruje.
Jeśli pominiesz warunek złączenia, będzie to odpowiadało użyciu
złączenia krzyżowego CROSS JOIN lub iloczynu kartezjańskiego.
Trzeci argument dla metody join()jest listą kolumn ze złączonej
tabeli, które mają być dołączone do listy wyników. Działa to
analogicznie jak drugi argument dla metody from(), w którym może
być wartość skalarna z nazwą tabeli, lub tablica wartości
skalarnych w celu wybrania wielu kolumn. Każda wartość skalarna może
być łańcuchem znakow lub obiektem typu Zend_Db_Expr. Jeśli pominiesz
ten argument, jako wartość domyślna zostanie przyjęty znak
'*'. Jeśli nie chcesz by kolumny z dołączonej
tabeli znajdowały się w zestawie wyników, użyj pustej tablicy
array().
Nie określaj wielu kolumn jako pojedynczego łańcucha znaków z
nazwami kolumn oddzielonymi przecinkami. Taka składnia była używana
we wcześniejszych wydaniach Zend_Db i nie jest ona już obsługiwana.
Użyj zamiast tego tablicy.
Obsługiwane są następujące typy złączeń:
INNER JOIN za pomocą metody
join() lub joinInner().
Wszystkie rodzaje RDBMS obsługują ten typ złączeń.
LEFT JOIN za pomocą metody
joinLeft().
Wszystkie rodzaje RDBMS obsługują ten typ złączeń.
RIGHT JOIN za pomocą metody
joinRight().
Niektóre rodzaje RDBMS nie obsługują tego typu złączeń.
FULL JOIN za pomocą metody
joinFull().
Niektóre rodzaje RDBMS nie obsługują tego typu złączeń.
CROSS JOIN za pomocą metody
joinCross().
Nie ma żadnego parametru dla tej metody do określania
warunku złączenia.
Niektóre rodzaje RDBMS nie obsługują tego typu złączeń.
NATURAL JOIN za pomocą metody
joinNatural().
Nie ma żadnego parametru dla tej metody do określania
warunku złączenia; przy złączeniu naturalnym zakładane jest,
że jest tworzone równościowe złączenie kolumn o tej samej
nazwie w obu tabelach.
Obsługiwane jest jedynie złączenie NATURAL INNER JOIN.
Warunki WHERE
Aby dodać warunki WHERE, użyj metody where(). Możesz przekazać
zwykły łańcuch znaków lub możesz przekazać łańcuch znaków
ze znacznikiem w postaci znaku zapytania oraz wartość która
ma być zacytowana. (wartość będzie zacytowana za pomocą
metody Zend_Db_Adapter::quoteInto).
Kolejne wywołania where() będą łączyć warunki za pomocą AND;
jeśli chcesz je złączyć za pomocą OR, użyj metody orWhere().
select();
// SELECT r.*
// FROM `round_table` r
// WHERE noble_title = 'Sir'
// AND favorite_color = 'yellow'
$select->from(array('round_table' => 'r'));
$select->where("noble_title = 'Sir'"); // osadzona wartość
$select->where('favorite_color = ?', 'yellow'); // cytowana wartość
// SELECT foo.*
// FROM `foo` 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));
?>]]>
Zend_Db_Select nie stosuje cytowania oraz aliasów tabeli dla
nazwanych kolumn w wyrażeniach WHERE. Jeśli zmiennych PHP używasz
do tworzenia łańcucha znaków dla wyyrażenia WHERE, powinienes użyć
metody Zend_Db_Adapter quoteIdentifier() aby ochronić twoje
zapytania przed niedozwoloną składnią.
quoteIdentifier($columnName) . ' = ?';
$select->where($whereExpr, 'baz');
?>]]>
Warunek GROUP BY
Aby grupować wiersze użyj metody group() tyle razy ile potrzebujesz.
select();
// SELECT COUNT(id)
// FROM `foo` foo
// GROUP BY `bar`, `baz`
$select->from('foo', new Zend_Db_Expr('COUNT(id)'));
$select->group('bar');
$select->group('baz');
// wywołanie metody group():
$select->group(array('bar', 'baz'));
?>]]>
Nie określaj wielu kolumn jako pojedynczego łańcucha znaków z
nazwami kolumn oddzielonymi przecinkami. Taka składnia była używana
we wcześniejszych wydaniach Zend_Db i nie jest ona już obsługiwana.
Użyj zamiast tego tablicy.
Cytowanie jest stosowane do każdego elementu będącego łańcuchem
znakow, znajdującym się argumencie dla group(), ale jeśli element
ten jest obiektem typu Zend_Db_Expr, jego wartość jest używana bez
cytowania.
Warunki HAVING
Aby dodać warunki HAVING dla wybranych wyników użyj metody having().
Ta metoda jest identyczna w użyciu jak metoda where().
Kolejne wywołania having() będą łączyć warunki za pomocą AND;
jeśli chcesz je złączyć za pomocą OR, użyj metody orHaving().
select();
// SELECT COUNT(id) AS count_id
// FROM `foo` foo
// GROUP BY `bar`, `baz`
// HAVING count_id > '1'
$select->from('foo', new Zend_Db_Expr('COUNT(id) AS count_id'));
$select->group(array('bar', 'baz'));
$select->having('count_id > ?', 1);
?>]]>
Zend_Db_Select does not apply quoting or table aliases to
columns named in HAVING clauses. If you combine PHP
variables into the string for a HAVING expression, you
should use the Zend_Db_Adapter quoteIdentifier() method
to protect against illegal syntax.
quoteIdentifier($columnName) . ' > ?';
$select->having($havingExpr, 1);
?>]]>
Warunek ORDER BY
Aby sortować dane użyj metody order() tyle razy ile potrzebujesz.
select();
// SELECT r.*
// FROM `round_table` r
// ORDER BY `noble_title` DESC, `first_name` ASC
$select->from('round_table');
$select->order('noble_title DESC');
$select->order('first_name');
// wywołanie metody order():
$select->order(array('noble_title DESC', 'first_name'));
?>]]>
Nie określaj wielu kolumn jako pojedynczego łańcucha znaków z
nazwami kolumn oddzielonymi przecinkami. Taka składnia była używana
we wcześniejszych wydaniach Zend_Db i nie jest ona już obsługiwana.
Użyj zamiast tego tablicy.
Cytowanie jest stosowane do każdego elementu będącego łańcuchem
znakow, znajdującym się argumencie dla order(), ale jeśli element
ten jest obiektem typu Zend_Db_Expr, jego wartość jest używana bez
cytowania.
LIMIT w oparciu o ilość wyników i offset
Zend_Db_Select oferuje obsługę bazodanowej składni LIMIT.
Dla wielu baz danych, np. MySQL czy PostgreSQL, jest to
relatywnie proste, ponieważ obsługują one składnię "LIMIT :count
[OFFSET :offset]".
Dla niektórych innych baz danych, nie jets to takie proste, ponieważ
nie obsługują one składni LIMIT. Microsoft SQL Server posiada
składnię TOP, która daje taki sam rezultat. Oracle oraz DB2 wymagają
zapytań napisanych w specjalny sposób aby emulować składnię LIMIT.
Zend_Db_Select może przepisać zapytanie SELECT odpowiednio dla
każdego ze sterowników bazy danych aby umożliwiać obsługę
funkcjonalności LIMIT.
Aby limitować zwracane wyniki na podstawie ilości i offsetu użyj
metody limit() podając ilość oraz opcjonalny offset.
select();
$select->from('foo');
$select->order('id');
$select->limit(10);
// W MySQL/PostgreSQL/SQLite odpowiada to zapytaniu:
//
// SELECT foo.*
// FROM "foo" foo
// ORDER BY "id" ASC
// LIMIT 10
//
// A w Microsoft SQL Server odpowiada to zapytaniu:
//
// SELECT TOP 10 foo.*
// FROM [foo] foo
// ORDER BY [id] ASC
// a teraz bardziej złożony "LIMIT :count OFFSET :offset"
$select = $db->select();
$select->from('foo', '*');
$select->order('id');
$select->limit(10, 20);
// W MySQL/PostgreSQL/SQLite odpowiada to zapytaniu:
//
// SELECT foo.*
// FROM "foo" foo
// ORDER BY "id" ASC
// LIMIT 10 OFFSET 20
//
// Microsoft SQL Server nie obsługuje offset, więc odpowiada to zapytaniu:
//
// SELECT * FROM (
// SELECT TOP 10 * FROM (
// SELECT TOP 30 *
// FROM [foo] foo
// ORDER BY [id] DESC
// ) ORDER BY id ASC
// )
//
// Zend_Db_Adapter automatycznie tłumaczy zapytanie.
?>]]>
LIMIT w oparciu o ilość wyników i numer strony
Zend_Db_Select oferuje limitowanie wyników oparte na stronach. Jeśli
chcesz pobrać pewną stronę wyników, użyj metody limitPage();
wpierw przekaż numer strony którą potrzebujesz, a nąstępnie ilość
wierszy jaka ma się pojawiać na każdej ze stron.
select();
$select->from('foo');
$select->order('id');
// ... i limitujemy do strony 3 gdzie każda strona ma 10 wierszy
$select->limitPage(3, 10);
// W MySQL/PostgreSQL/SQLite, odpowiada to zapytaniu:
//
// SELECT foo.*
// FROM `foo` foo
// ORDER BY `id` ASC
// LIMIT 10 OFFSET 20
?>]]>
Inne metody
Metoda distinct() pozwala dodać słowo kluczowe DISTINCT do twojego
zapytania SQL.
select();
$select->distinct();
$select->from('foo', 'non_unique_column');
?>]]>
Metoda forUpdate() pozwala dodać ci słowa kluczowe FOR UPDATE do
twojego zapytania SQL.
select();
$select->forUpdate();
$select->from('foo');
?>]]>
Metoda query() działa podobnie jak metoda query() obiektu klasy
Zend_Db_Adapter. Zwraca ona obiekt typu Zend_Db_Statement lub
PDOStatement, zależnie od typu używanego sterownika.
select();
$select->from('foo');
$stmt = $select->query();
$result = $stmt->fetchAll();
// Jest to równoznaczne z poniższym:
$select = $db->select();
$select->from('foo');
$stmt = $db->query($select);
$result = $stmt->fetchAll();
?>]]>
Metoda getPart() zwraca dane, ktore przypisałeś do części zapytania
SQL. Klasa Zend_Db_Select zawiera definicje stałych, których możesz
użyć dla części zapytania SQL.
select();
$select->from('foo');
$select->order('keyColumn');
print_r( $select->getPart( Zend_Db_Select::ORDER ) );
?>]]>
Metoda reset() pozwala ci na wyczyszczenie jednej określonej części
zapytania SQL, lub na wyczyszczenie wszystkich części zapytania SQL,
jeśli pominiesz argument podczas wywołania.
select();
$select->from('foo');
$select->order('column1');
// Chcemy zmienić kryteria sortowania
//
// SELECT foo.*
// FROM `foo` foo
// ORDER BY `column2`
// Czyścimy jedną część aby zdefiniować ją ponownie
$select->reset( Zend_Db_Select::ORDER );
$select->order('column2');
// Czyścimy wszystkie części zapytania
$select->reset();
?>]]>