Ostatnio w ramach sportów wyczynowych majstruję trochę z zapytaniami do bazy danych w Zendzie. Sprawdzam co można wycisnąć z obiektów Zend_Db_Select. Ten wpis zdecydowałem się podzielić na dwie części. W pierwszej pokażę jak wygenerować dosyć złożone zapytanie do bazy danych za pomocą kilku obiektów Zend_Db_Select, a w drugiej zajmiemy się stworzeniem formularza i odebraniem danych.
Naszym celem będzie utworzenie alfabetycznej listy polityków, których wszyscy lubimy wraz z ich powszechnie znanymi pseudonimami, np.
<select>
<option value="1">Donald Tusk</option>
<option value="2">Jareczek (Jarosław Kaczyński)</option>
<option value="3">Jarosław Kaczyński</option>
<option value="4">Słońce Peru (Donald Tusk)</option>
</select>
Najpierw musimy mieć skąd brać naszych wybrańców narodu. Stworzymy sobie bazę danych mniej więcej taką: Obrazek bazy danych polityków zrobiłem fajnym narzędziem online WWW SQL Designer. Schemat jest oczywiście maksymalnie uproszczony. Nie czepiać się, że imiona i nazwiska trzymam w jednym polu. Chodzi nam o relację jeden polityk ma wiele pseudonimów. Macie poniżej trochę kodu SQL wyeksportowanego przez phpMyAdmin plus kilka przykładowych wartości.
CREATE TABLE IF NOT EXISTS `politycy` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`imie_nazwisko` varchar(50) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='tabela zawierajaca imiona i nazwiska politykow' AUTO_INCREMENT=3 ;
INSERT INTO `politycy` (`id`, `imie_nazwisko`) VALUES
(1, 'Donald Tusk'),
(2, 'Jarosław Kaczyński');
CREATE TABLE IF NOT EXISTS `politycy_ksywki` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`ksywka` varchar(20) NOT NULL,
`polityk_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `polityk_id` (`polityk_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='zawiera popularne pseudonimy politykow' AUTO_INCREMENT=3 ;
INSERT INTO `politycy_ksywki` (`id`, `ksywka`, `polityk_id`) VALUES
(1, 'Słońce Peru', 1),
(2, 'Jareczek', 2);
ALTER TABLE `politycy_ksywki`
ADD CONSTRAINT `politycy_ksywki_ibfk_1` FOREIGN KEY (`polityk_id`) REFERENCES `politycy` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
Następnie spróbujemy sobie wygenerować dosyć spore zapytanie SQL, które za jednym zamachem wypisze wszystkich polityków, wszystkie pseudonimy i dodatkowo w nawiasie wypisze którego polityka który pseudonim dotyczy. Wszystko oczywiście w Zend Framework.
// adapter bazy umieszczony w rejestrze
$dbAdapter = Zend_Registry::get('db');
$select1 = $dbAdapter->select()
// podstawowa tabela z politykami
->from(array('p' => 'politycy'),
// ktore pola (lacznie z aliasem)
array('p.id','p.imie_nazwisko'));
// podzapytanie dla ksywek
$subSelect = $dbAdapter->select()
->from(array('pp' => 'politycy'),
array('pp.imie_nazwisko'))
// warunek dla podzapytania
->where('pp.id = pk.polityk_id')
// powinno przyspieszyc duze bazy
->limit(1);
$select2 = $dbAdapter->select()
// tabela z ksywkami
->from(array('pk' => 'politycy_ksywki'),
array('pk.polityk_id',
// Zend_Db_Expr konieczny przy wywolywaniu funkcji SQL
new Zend_Db_Expr("CONCAT(pk.ksywka, ' (', ({$subSelect}) , ')')")));
$selectUnion = $dbAdaptery->select()
// konstruujemy zapytanie typu union
->union(array($select1, $select2))
// szeregujemy wyniki
->order('imie_nazwisko ASC');
$stmt = $select->query();
$rowset = $stmt->fetchAll();
Powyższy kod stworzył niczego sobie zapytanie:
SELECT `p`.`id`, `p`.`imie_nazwisko` FROM `politycy` AS `p` UNION SELECT `pk`.`polityk_id`, CONCAT(pk.ksywka, ' (', (SELECT `pp`.`imie_nazwisko` FROM `politycy` AS `pp` WHERE (pp.id = pk.polityk_id) LIMIT 1) , ')') FROM `politycy_ksywki` AS `pk` ORDER BY `imie_nazwisko` ASC
Zmienna $rowset
zawiera wynik zapytania w postaci tablic:
array(4) {
[0] => array(2) {
["id"] => string(1) "1"
["imie_nazwisko"] => string(11) "Donald Tusk"
}
[1] => array(2) {
["id"] => string(1) "2"
["imie_nazwisko"] => string(31) "Jareczek (Jarosław Kaczyński)"
}
[2] => array(2) {
["id"] => string(1) "2"
["imie_nazwisko"] => string(20) "Jarosław Kaczyński"
}
[3] => array(2) {
["id"] => string(1) "1"
["imie_nazwisko"] => string(27) "Słońce Peru (Donald Tusk)"
}
}
Na koniec uwaga: obiekty typu Zend_Db_Select są przydatne tylko wtedy, gdy nasze zapytanie nie ma charakteru stałego, tzn. różne czynniki wpływają na jego kształt, przez co musi być tworzone w locie. Jeżeli wiemy, że zapytanie zawsze będzie takie samo to jest to zwykłe marnotrawstwo zasobów serwera, aczkolwiek ładnie wygląda i szybko się pisze.