Przeglądarka bazy danych przy użyciu PHP-Gtk2.pdf

(1072 KB) Pobierz
8140284 UNPDF
Dla zaawansowanych
Przeglądarka bazy danych
przy użyciu PHP-Gtk2
Christian Weiske
Stopień trudności: lll
Bardzo mocną stroną PHP są wbudowane funk-
cje obsługi baz danych. Z kolei aplikacje okien-
kowe wiodą prym dzięki intuicyjnym interfej-
som, doskonale reagującym na działania użyt-
kownika. W tym artykule opiszemy, jak połączyć
zalety tych dwóch aspektów programistycznych,
budując przeglądarkę bazy danych, którą łatwo
będzie można udoskonalać oraz dostosowywać
do różnych systemów bazodanowych.
za danych” oraz “przeglądarka”,
na myśl przychodzi nam zazwy-
czaj phpMyAdmin lub podobne jemu na-
rzędzie. Wygląd (ang. layout ) tych apli-
kacji jest w większości przypadków bar-
dzo do siebie zbliżony: w górnym lewym
rogu mamy menu rozwijane z możliwo-
ścią wyboru bazy danych, na której bę-
dziemy pracować, zaś poniżej wyświetla-
ne są jej tabele.
Cała zawartość prawej części apli-
kacji jest przeznaczona na wyświetla-
nie zawartości i struktury aktualnie za-
znaczonej tabeli. Często zawiera także
elementy nawigacyjne, pozwalające na
zmianę struktury, właściwości czy za-
wartości tabeli.
Ponieważ takie rozmieszczenie jest
popularne i dobrze znane większości
użytkowników, nasza przeglądarka ba-
zy danych będzie także miała taki wy-
gląd. Oto lista wszystkich funkcjonalno-
ści, które omówimy w ramach artykułu,
wraz z ich reprezentacją koloru w wido-
ku strukturalnym (patrz Rysunek 1):
• możliwość wyboru bazy danych z me-
nu rozwijanego (pomarańczowy),
• wyświetlanie nazw i ilości wierszy
tabel w zaznaczonej bazie danych
(zielony),
• wyświetlanie zawartości (wierszy) za-
znaczonej tabeli (ioletowy),
• numeracja i sortowanie, nawigacja
wierszy za pomocą przycisków w to-
olbarze (niebieski).
W SIECI
http://www.gnope.org
– pakiet PHP5 + Gtk2 dla
Windows,
http://gtk.php.net
– strona główna projektu
PHP-Gtk2 wraz
z repozytorium,
http://cweiske.de/iles/
download/phpgtk2/
DatabaseBrowser.tar.bz2
– pełen kod aplikacji
zbudowanej w tym artykule,
http://pear.php.net/package/
MDB2
– pakiet MDB2 dla PEAR,
http://pear.php.net/package/
Gtk2_ExceptionDump
– pakiet Gtk_ExceptionDump
dla PEAR,
http://pear.php.net/package/
Structures_Form_Gtk2
– pakiet Structures_Form_Gtk2
dla PEAR.
Co należy wiedzieć...
Przydatna będzie znajomość podstaw
PHP-Gtk oraz podstawowych zapytań
SQL.
Co obiecujemy...
Dowiesz się, jakie nowe funkcje przynosi
druga wersja PHP-Gtk. Zbudujesz prze-
glądarkę baz danych z funkcją sortowa-
nia i podziału na strony.
44
www.phpsolmag.org
PHP Solutions Nr 2/2007
S łysząc kombinację słów „ba-
 
 
PHP-Gtk2 Dla zaawansowanych
Na Rysunku 1. pokazaliśmy, jakie wid-
gety GTK+ zostaną użyte oraz, w jaki
sposób będą zagnieżdżone.
Ponieważ GtkWindow może mieć
jedynie jeden element potomny, a my
potrzebujemy użyć kilku widgetów, sko-
rzystamy z GtkContainer. GtkHPaned
z kolei idealnie będzie pasował do na-
szych wymagań: dzieli bowiem okno
na dwie sekcje, których rozmiar może
zmieniać użytkownik.
Dla lewego panelu użyjemy kla-
sycznego GtkVBox , w którym zagnieź-
dzimy nasze rozwijane menu ( Gtk-
ComboBox ze specjalnym GtkListStore
funkcjonującym jako jego model), oraz
dla listy tabel – także GtkTreeView
z odpowiednim GtkListStore jako mo-
delem.
Prawa strona naszej aplikacji jest
podobnie zamknięta w GtkVBox . Prze-
trzymujący element GtkTreeView , będzie
pokazywał wszystkie wiersze. Będzie
on także zawierał element GtkToolbar z
przyciskami nawigacyjnymi, usprawnia-
jącymi poruszanie się po liście: pierw-
sza i ostatnia strona, odśwież, następna
i ostatnia strona.
Kilka słów na temat stylu kodo-
wania i używania klas: z doświadcze-
nia wiem, że bardzo pomocne okazu-
je się rozszerzanie klas głównych wid-
getów Gtk, tworząc własne klasy i osa-
dzając je według kolejności zagnież-
dżenia. To może powiększyć ilość linii
kodu przy małych projektach, jednak
jest absolutnie niezbędne przy budo-
waniu wielkoskalowych projektów – in-
aczej szybko zgubimy się we własnym
kodzie. Ta metoda pozwala także na
łatwe przystosowanie kodu do ponow-
nego użycia, ponieważ wystarczy za-
łączyć klasę i zainicjować ją komendą
new MyClassName() – bez porówna-
nia jest to mniej kodu, niż w przypad-
ku wycinania i kopiowania kodu każdej
funkcji (co z kolei utrudnia znalezienie
i korektę błędów).
Podłączamy się do bazy
Wykonanie połączenia do bazy danych,
używając MDB2 jest relatywnie proste:
wystarczy stworzyć cią DSN (ang. Data
Source Name ), który odpowiada nasze-
mu środowisku, i wywołać metodę sin-
gleton klasy MDB2:
require_once 'MDB2.php';
$dsn = 'mysql://username:
password@hostname/databasename';
$mdb2 = MDB2::singleton($dsn, array());
Jeśli otrzymujesz błąd podobny do
"MDB2 Error: not found", prawdopo-
dobnie nie masz zainstalowanych odpo-
wiednich sterowników MDB2. Wystarczy
wykonać polecenie pear install MDB2_
Driver_mysql , by całość zaczęła działać
(należy zamienić “ mysql ” na nazwę na-
szego systemu bazodanowego).
Rysunek 1. Struktura przeglądarki bazy danych
Rozprawiamy się z błędami
W przypadku dużego projektu, zaprzęgli-
byśmy do pracy wyjątki, sprawdzając, czy
zwracany obiekt w $mdb2 to PEAR_Error
i rzucalibyśmy wyjątek w przypadku praw-
dy. Ponieważ tego typu zabiegi są bardzo
kosztowne pod kątem np. czasu wykony-
wania, czy pisania aplikacji, użyjemy za-
let pakietu PEAR : Gtk2_ExceptionDump .
Tworzy on graiczny zrzut wszelkiego ro-
dzaju błędów: wyjątków, obiektów PEAR_
Error, kiedy zostaną utworzone oraz błę-
dów PHP, w przypadku ich pojawienia się
(patrz Rysunek 2).
Dobrą wiadomością jest, że ten skom-
plikowany z pozoru proces reprezentują w
kodzie zaledwie dwie linijki:
Wymagania
Przygotujmy najpierw nasze stanowisko. Niezbędne będą:
• PHP 5.1.x
• zainstalowany moduł PHP-Gtk2
• zainstalowany PEAR z pakietami MDB2 i Gtk2_ExceptionDump ( patrz ramka W Sieci )
• dostęp do bazy danych
Ja wybrałem MDB2, ponieważ oferuje najłatwiejszy sposób dostępu do różnych typów baz
danych, w tym Oracle, DB2 i MySQL. Jeśli pracujesz w systemie Windows, polecam uży-
cie pakietu Gnope (patrz ramka “ W Sieci ”), który zawiera wszystkie niezbędne składniki,
pozwalające rozpocząć pracę z PHP5 i Gtk2. Jeśli używasz Linuxa lub Macintosha, powi-
nieneś pobrać PHP-Gtk2 Alpha (patrz ramka “W Sieci”) i skompilować go samodzielnie. W
manualu załączonym do pakietu znajdziesz instrukcję, jak przeprowadzić kompilację dla
obu systemów. Mamy już zainstalowane narzędzia pracy, możemy więc przejść do właści-
wego rzemiosła.
require_once 'Gtk2/ExceptionDump.php';
Gtk2_ExceptionDump::setupAllHandlers();
Nie musimy już martwić się o błędy i
możemy skupić się na programowaniu
funkcjonalności naszej aplikacji. Taki
PHP Solutions Nr 2/2007
www.phpsolmag.org
45
 
Dla zaawansowanych
PHP-Gtk2
Listing 1. Kod klas wyświetlających bazy danych
zabieg jest bardzo przydatny przy two-
rzeniu prototypu aplikacji, jednak je-
śli zamierzasz rozprzestrzeniać swój
kod, powinieneś zaimplementować wła-
sny system obsługi błędów, by klient
otrzymywał bardziej przydatne i bar-
dziej czytelne informacje o ewentual-
nych błędach.
class DatabaseBrowser_DatabaseList extends GtkListStore
{
public function __construct ()
{
parent::__construct ( Gtk::TYPE_STRING ) ;
$this - > loadDatabases () ;
}
Wyświetlamy tabele
Pierwsze zapytanie SQL, jakiego bę-
dziemy używać, to naturalnie " SHOW
DATABASES ". Jego wynikiem wypeł-
nimy GtkComboBox . By być przygoto-
wanym na wprowadzanie zmian i udo-
skonaleń w przyszłości, skorzystamy
z naszej własnej klasy, która będzie
rozszerzała GtkListStore . Widget ten
posiada jedynie jedną kolumnę typu
Gtk::TYPE_STRING , która przechowu-
je nazwy dostępnych baz danych. Kod
klas zamieściliśmy na Listingu 1, wynik
jego działania widać na Rysunku 3.
GtkListStore oferuje kilka metod do-
dawania wierszy. W tym przypadku za-
stosujemy append() , ponieważ jest dla
nas najłatwiejsza w użyciu. Pobiera
ona jako parametr tablicę, która powin-
na mieć tyle wartości, ile nasza lista ma
kolumn.
Zauważmy, że stosujemy obiekt pu-
bliczny $mdb2 typu static naszej głów-
nej klasy, DatabaseBrowser , do wykony-
wania zapytań do bazy danych...
W połączeniu z dwoma kolejnymi
linijkami, otrzymujemy działające me-
nu rozwijane z listą dostępnych baz da-
nych.
Kiedy użytkownik wybierze bazę
danych, powinniśmy wyświetlić mu Gtk-
TreeView (Listing 2). Element ten bę-
dzie zawierał nazwy tabel oraz ilość
wierszy w nich dostępnych. Podobnie,
jak poprzednio, stworzymy własną kla-
sę rozszerzając GtkListStore . Tym ra-
zem musimy pamiętać, że lista musi
być czyszczona i ponownie uzupełnia-
na przy każdym kolejnym wyborze ba-
zy danych przez użytkownika. Tym sa-
mym, klasa otrzyma własną metodę lo-
adTables() , która będzie wymagała ja-
ko jedynego parametru, nazwy bazy
danych. Wynik jej działania będziemy
wyświetlać w dwóch kolumnach: jednej
dla nazwy tabeli, drugiej dla ilości re-
kordów w niej istniejących.
Wypełnienie elementu GtkTreeView
będzie wymagało tym razem więcej
pracy, niż w przypadku baz danych w
protected function loadDatabases ()
{
$res = DatabaseBrowser:: $mdb2 - > query ( 'SHOW DATABASES' ) ;
while (( $row = $res - > fetchRow ())) {
$this - > append ( array ( $row [ 0 ])) ;
}
}
}
$cmbDatabase = GtkComboBox::new_text () ;
$cmbDatabase - > set_model ( new DatabaseBrowser_DatabaseList ()) ;
Listing 2. Wyświetlamy dostępne tabele
class DatabaseBrowser_TableList extends GtkListStore
{
public function __construct ()
{
parent::__construct ( Gtk::TYPE_STRING, Gtk::TYPE_LONG ) ;
}
public function loadTables ( $strDatabase )
{
$this - > clear () ;
DatabaseBrowser:: $mdb2 - > setDatabase ( $strDatabase ) ;
$res = DatabaseBrowser:: $mdb2 - > query ( 'SHOW TABLES' ) ;
while (( $row = $res - > fetchRow ())) {
$strTable = $row [ 0 ] ;
$nRows = DatabaseBrowser:: $mdb2 - > queryOne (
t 'SELECT COUNT(*) FROM ' . $strTable ) ;
$this - > append ( array ( $strTable , $nRows )) ;
}
}
}
$lstTables = new GtkTreeView ( new DatabaseBrowser_TableList ()) ;
$renderer = new GtkCellRendererText () ;
$col = new GtkTreeViewColumn ( 'Table' , $renderer , 'text' , 0 ) ;
$lstTables - > append_column ( $col ) ;
$numberRenderer = new GtkCellRendererText () ;
$col - > pack_end ( $numberRenderer , false ) ;
$col - > add_attribute ( $numberRenderer , 'text' , 1 ) ;
$cmbDatabase - > connect ( 'changed' , array ( $this , 'onSelectDatabase' ) , $lstTables ) ;
public function onSelectDatabase ( $cmbDatabase , $lstTables )
{
$strDatabase = $cmbDatabase - > get_active_text () ;
$lstTables - > get_model () - > loadTables ( $strDatabase ) ;
}
$scrTables = new GtkScrolledWindow () ;
$scrTables - > set_policy ( Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC ) ;
$scrTables - > add ( $lstTables ) ;
46
www.phpsolmag.org
PHP Solutions Nr 2/2007
Dla zaawansowanych
PHP-Gtk2
GtkComboBox , ponieważ nie posiada
on odpowiedniego konstruktora, który
mógłby ustawić wszystkie kolumny.
Użyjemy więc następujących kompo-
nentów:
Ostatnie zadanie, to umożliwie-
nie przewijania listy, co staje się trywial-
ne przy użyciu widgeta GtkScrolledWin-
dow . Miejmy na uwadze, że GtkTreeView ,
w odróżnieniu od wielu innych widge-
tów, posiada swój własny system wido-
ku. Oznacza to, że jeśli będziemy przewi-
jać zawartość widgetu, tytuły kolumn bę-
dą zawsze widoczne. Dodatkowo, upew-
nijmy się, że paski przewijania nie będą
widoczne, jeśli nie są konieczne. To za-
danie spełni proste odwołanie do set_
policy .
Po połączeniu kodu, otrzymujemy
aplikację widoczną na Rysunku 4.
Pokazujemy dane
Zmapowanie zawartości całej tabeli z ba-
zy danych do jednego widgetu GtkList-
Store jest możliwe, acz nie trywialne.
Będziemy musieli zmierzyć się z dwoma
problemami:
Po pierwsze, lista kolumn każdej ta-
beli nie jest znana podczas progra-
mowania naszej przeglądarki baz da-
nych. Oznacza to, że nie możemy zaini-
GtkTreeView jako główny widget, ma-
jący dostęp do modelu Database-
Browser_TableList,
GtkTreeViewColumn , wyświetlający
zawartość kolumn,
GtkCellRendererText , by narysować
nazwy tabel w wierszach,
GtkScrolledWindow , by wyświetlone
zostały suwaki na wypadek, gdyby li-
sta tabel nie mieściła się w oknie.
Listing 3. Tworzymy menu nawigacyjne
$this - > tbl = new DatabaseBrowser_Table ( $strDatabase , $strTable ) ;
Konstruktor GtkTreeView potrzebuje,
jako pierwszego i jedynego parame-
tru modelu, podajemy więc mu instan-
cję naszej klasy DatabaseBrowser_
TableList .
GtkTreeViewColumn potrzebuje z
kolei czterech parametrów: jako pierw-
szego, tytułu nagłówka kolumny. Ja-
ko drugiego – obiektu GtkCellRenderer ,
który zajmuje się rysowaniem danych
modelu. Trzeci parametr determinuje,
który atrybut GtkCellRenderer zostanie
ustawiony (np. GtkCellRendererText ja-
ko atrybut “text”, ale już GtkCellRende-
rerPixbuf jako "pixbuf", bowiem determi-
nuje obraz). Czwarty i ostatni, parametr,
to numer kolumny modelu, który prze-
trzymuje dane zawarte w atrybucie.
Moglibyśmy teraz po prostu dodać
drugą kolumnę, by wyświetlać ilości
wierszy, lecz istnieje także inna metoda:
dodanie drugiego elementu GtkCellRen-
dererText do tej samej kolumny. Ponie-
waż klasa GtkTreeViewColumn imple-
mentuje interfejs GtkCellLayout , może-
my użyć jej podobnie, jak w przypadku
GtkBox , używającego pack_start() oraz
pack_end() , w celu dodania większej ilo-
ści rendererów.
Jeśli uważnie śledziłeś dotychcza-
sową treść artykułu, zapewne zastana-
wiasz się, skąd lista tabel “wie”, kiedy
zachodzą zmiany w liście baz danych.
Odpowiedź jest prosta: jeszcze nie wie.
Musimy więc uzupełnić kod, dodając
uchwyt (ang. handler ) do sygnału Gtk-
ComboBox changed (reakcja na zmia-
nę). Przekazujemy objekt tabeli do funk-
cji pełniącej rolę callbacka (połączenie
zmiany z jej efektem), bowiem będziemy
odnosili się do metody loadTables() mo-
delu, który wcześniej zdeiniowaliśmy.
$scrwnd = new GtkScrolledWindow () ;
$scrwnd - > set_policy ( Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC ) ;
$scrwnd - > add ( $this - > tbl ) ;
$this - > pack_start ( $scrwnd ) ;
$tb = new GtkToolbar () ;
$arButtons = array (
Gtk::STOCK_GOTO_FIRST = > 'irst' ,
Gtk::STOCK_GO_BACK = > 'back' ,
Gtk::STOCK_REFRESH = > 'refresh' ,
Gtk::STOCK_GO_FORWARD = > 'forward' ,
Gtk::STOCK_GOTO_LAST = > 'last'
) ;
foreach ( $arButtons as $nStockItem = > $strAction ) {
$btn = GtkToolButton::new_from_stock ( $nStockItem ) ;
$btn - > connect_simple ( 'clicked' , array ( $this - > tbl, 'navigate' ) , $strAction ) ;
$tb - > insert ( $btn , -1 ) ;
$this - > arButtons [ $strAction ] = $btn ;
}
Rysunek 2. Wynik działania Gtk2_ExceptionDump po stworzeniu obiektu PEAR_Error
przez MDB2
48
www.phpsolmag.org
PHP Solutions Nr 2/2007
Zgłoś jeśli naruszono regulamin