Autor: Tony Marston
Data publikacji: 22.05.2003, 21:28 | Ostatnia modyfikacja: 22.10.2006, 17:10
ArtykuÅ‚ ten przeznaczony jest dla programistów, którzy zainteresowani sÄ… eksportowaniem danych ze swojej bazy do pliku XML tak, by mogÅ‚y być one Å‚atwo przetwarzane w inny sposób, np. poprzez użycie arkuszy XSL do ich wyÅ›wietlenia jako dokumenty hipertekstowe. Metoda tu opisana pozwala caÅ‚kowicie oddzielić warstwÄ™ prezentacji (np. generowanie dokumentów HTML) od warstwy przetwarzania (aplikacja przetwarza dane wedle okreÅ›lonych reguÅ‚ zaprogramowanych w jÄ™zyku np. PHP), dziÄ™ki czemu modyfikacja jednej nie powoduje koniecznoÅ›ci dokonywania zmian w drugiej.
W poniższych przykÅ‚adach kod zostaÅ‚ tak napisany, że nie ma w nim zapisanych "na twardo" żadnych nazw tabel, pól itd. Dane pobierane sÄ… z bazy jako tablica asocjacyjna (pary "nazwa-wartość"). Każdy jej element jest zamieniany i zapisywany w pliku XML, a jej zawartość pobierana przy pomocy zapytania SQL "SELECT".
Na poczÄ…tku zaprezentujÄ™ sposób zapisania informacji z pojedynczej tabeli, a nastÄ™pnie z dwóch różnych powiÄ…zanych wzajemnymi relacjami. Na koÅ„cu znajdzie siÄ™ krótki opis wprowadzenia dodatkowych atrybutów do utworzonych znaczników w celu uzupeÅ‚nienia/skomentowania danych.
Wszystkie przykÅ‚ady wymagajÄ… zainstalowanego w PHP rozszerzenia "DomXML". Zalecana jest także znajomość budowy plików XML.
Poniższy kod ma za zadanie pobrać wszystkie rekordy z $dbresult (każde pole w rekordzie jest jedną parą "nazwa-wartość") i zapisać je jako znaczniki XML'a. W zależności od woli programisty/użytkownika mogą one być potem prosto zachowane w postaci pliku, lub przetworzone na kod HTML za pomocą rozszerzenia PHP "Sablotron XSLT processor". Pierwsza część kodu po prostu łączy się z bazą i przygotowuje odpowiednie zapytanie:
<?php if (!$dbconnect = mysql_connect('localhost', 'user', 'pass')) { echo "Nie mogę się połączyć z hostem 'localhost'."; exit; } // if if (!mysql_select_db('test')) { echo "Nie mogę się połączyć z bazą 'test'"; exit; } // if $table_id = 'jakas_tabela'; $dbresult = mysql_query("SELECT * FROM $table_id", $dbconnect);
Teraz, mając dane, możemy zacząć je wyświetlać. Na początku utworzymy nowy dokument DOM. Za pomocą poniższej komendy ustalamy, że chodzi nam o wersję 1.0 XML'a, po czym zwracamy referencję do wirtualnego dokumentu.
// Utworz nowy dokument DOM $doc = dom_new_doc('1.0');
Pierwszy utworzony element (zwany też wÄ™zÅ‚em) jest tak zwanym elementem gÅ‚ównym, który musi znaleźć siÄ™ raz i tylko raz w każdym dokumencie XML'a. W tym przykÅ‚adzie nazwaÅ‚em go po prostu "root", lecz możesz oczywiÅ›cie użyć takiej nazwy, jaka Ci siÄ™ podoba (np. nazwa wykonywanego skryptu). PamiÄ™taj, że element ten musisz wprowadzić za pomocÄ… dwóch funkcji:
// Utwórz element (węzeł) ROOT $root = $doc->create_element('root'); $root = $doc->append_child($root);
JesteÅ›my teraz gotowi do rozpoczÄ™cia zapisu danych, które mamy w bazie. Zauważ, że zwracam każdy wiersz jako tablicÄ™ asocjacyjnÄ…, dziÄ™ki czemu praktycznie bez problemu odczytam wszystkie pary "nazwa-wartość".
// załaduj wiersz z bazy while ($row = mysql_fetch_assoc($dbresult)) {
Pierwszym zadaniem, jakie muszÄ™ zrealizować dla każdego rekordu, jest utworzenie nowego wÄ™zÅ‚a tworzonym dokumencie XML, który zapisywany jest bezpoÅ›rednio w "roocie". W przykÅ‚adzie jego nazwa tworzona jest od nazwy tabeli, z której pobieram dane.
// utwórz element/wezel dla kazdego rekordu $occ = $doc->create_element($table_id); $occ = $root->append_child($occ);
Teraz za pomocÄ… pÄ™tli FOREACH pobieram nazwÄ™ oraz wartość każdego pola rekordu i zapisujÄ™ je do pliku. Jak widać, tablice asocjacyjne potrafiÄ… bardzo urpoÅ›cić życie. Nie muszÄ™ zastanawiać siÄ™ ani nad tym, ile pól majÄ… rekordy, ani w jakiej sÄ… one zapisywane kolejnoÅ›ci - wszystko zaÅ‚atwia za nas PHP.
// Dodaj węzeł potomny dla każdego z elementów foreach ($row as $fieldname => $fieldvalue) {($occ);
Zauważ, że każda kolumna (zwana też polem) zapisywana jest jako element potomny węzła oznaczającego rekord, tutaj identyfikowanego za pomocą zmiennej $occ.
$child = $doc->create_element($fieldname); $child = $occ->append_child($child);
Muszę zapisać do utworzonego węzła wartość aktualnie przetwarzanego pola.
$value = $doc->create_text_node($fieldvalue); $value = $child->append_child($value);
PÄ™tle sÄ… wykonywane dopóty, dopóki nie zostanÄ… zapisane wszystkie rekordy i kolumny.
} // foreach } // while
Ostatni fragment kodu zwraca gotowy dokument XML
// pobierz gotowy dokument XML $xml_string = $doc->dump_mem(true);
W tym przykÅ‚adzie po prostu wyÅ›wietlam caÅ‚ość na ekranie przeglÄ…darki, lecz w zależnoÅ›ci od potrzeb można tutaj zaprogramować parÄ™ dodatkowych metod przetwarzania, np. przepuszczenie wyniku przez procesor XSLT, który dziÄ™ki arkuszowi XSL wygeneruje nam kod HTML z osadzonymi w nim danymi z bazy.
echo $xml_string; ?>
Zawartość utworzonego dokumentu XML będzie prezentowała się mniej więcej następująco, rozpoczynając od deklaracji XML oraz elementu ROOT.
<?xml version="1.0"?> <root>
Każdy z rekordów w bazie bÄ™dzie posiadaÅ‚ swój element w dokumencie, bÄ™dÄ…cy potomkiem elementu ROOT. BÄ™dzie on zawieraÅ‚ w sobie znaczniki reprezentujÄ…ce pojedyncze kolumny. Zauważ, iż każdy taki znacznik posiada wÄ™zeÅ‚ tekstowy, w którym przechowuje dane, podczas gdy znacznik rekordu takiego wÄ™zÅ‚a nie posiada. Po ostatniej kolumnie dostrzec możesz tag zamykajÄ…cy, po którym jest on znów otwierany dla kolejnego rekordu pobranego z bazy.
<jakas_tabela> <kolumna1>wartosc1</kolumna1> <kolumna2>wartosc2</kolumna2> ........................ <kolumnaX>wartosc3</kolumnaX> </jakas_tabela> <jakas_tabela> ........................ </jakas_tabela>
W ostatniej linii zamykany jest znacznik ROOT.
</root>
Wszystkie znaczniki użyte w tym dokumencie posiadajÄ… zarówno formÄ™ otwierajÄ…cÄ…, jak i zamykajÄ…cÄ…: <element>...</element>. Znacznik taki jest pojedynczym wÄ™zÅ‚em pliku XML. Wszystko w nim zawarte zwane jest wÄ™zÅ‚ami potomnymi tego wÅ‚aÅ›nie elementu. MogÄ… one przyjąć postać zwykÅ‚ego wÄ™zÅ‚a tekstowego (<znacznik>blebleble</znacznik>), lub też być elementami podrzÄ™dnymi.
Czasami możesz dostrzec w dokumentach XML znacznik w formie <znacznik />. XML dopuszcza możliwość otwarcia i zamkniÄ™cia znacznika miÄ™dzy jednÄ… tylko klamrÄ… ukoÅ›nÄ…. Dla nas oznacza to, iż jest on pusty tzn. nie zawiera żadnych wÄ™zÅ‚ów potomnych.
W nastÄ™pnym przykÅ‚adzie generowany dokument XML bÄ™dzie zawieraÅ‚ dane z dwóch tabel połączonych relacjÄ… One-to-many (czyli kilka rekordów podrzÄ™dnych z jednej tabeli uzupeÅ‚nia dane w jednym wybranym z drugiej). Dane pobierane sÄ… z wyników dwóch zapytaÅ„: $resouter dla tabeli nadrzÄ™dnej i $resinner dla tabeli podrzÄ™dnej. Tym razem, by siÄ™ nie powtarzać, skomentujÄ™ tylko ten kod, gdzie nastÄ…piÅ‚y zmiany.
<?php if (!$dbconnect = mysql_connect('localhost', 'user', 'pass')) { echo "Nie można połączyć się z hostem 'localhost'."; exit; } // if if (!mysql_select_db('test')) { echo "Nie można połączyć się z bazą danych 'test'"; exit; } // if
Tutaj widać dwa oddzielne zapytania dla każdej z tabel:
$outer_table = 'tabela_nadrzedna'; $resouter = mysql_query("SELECT * FROM $outer_table WHERE column='wartosc'", $dbconnect); $inner_table = 'tabela_podrzedna'; $resinner = mysql_query("SELECT * FROM $inner_table WHERE column='wartosc'", $dbconnect);
Następnie tworzymy nowy dokument DOM i dodajemy element ROOT:
// utwórz dokument XML $doc = domxml_new_doc('1.0'); // dodaj element ROOT $root = $doc->create_element('root'); $root = $doc->append_child($root);
Pobieramy JEDEN rekord z tabeli nadrzędnej i tworzymy dla niego węzeł w dokumencie.
// dodaj wezel rekordu z tabeli nadrzednej $outer = $doc->create_element($outer_table); $outer = $root->append_child($outer); // pobierz jeden rekord z tabeli $row = mysql_fetch_assoc($resouter);
Nie możemy zapomnieć o wyeksportowaniu kolumn do pliku:
// utwórz węzeł potomny dla każdej kolumny z rekordu foreach ($row as $fieldname => $fieldvalue) { $child = $doc->create_element($fieldname); $child = $outer->append_child($child); $value = $doc->create_text_node($fieldvalue); $value = $child->append_child($value); } // foreach
Tutaj dodajemy węzły dla każdego rekordu pobranego z tabeli podrzędnej. Zauważ, że każdy z nich jest potomkiem elementu $outer, a nie $root. Wszystkie kolumny węzła $inner będą jego potomkami.
// przetworz dane wezlow potomnych while ($row = mysql_fetch_assoc($resinner)) { // utworz wezel $inner = $doc->create_element($inner_table); $inner = $outer->append_child($inner); // utworz wezly dla kolumn foreach ($row as $fieldname => $fieldvalue) { $child = $doc->create_element($fieldname); $child = $inner->append_child($child); $value = $doc->create_text_node($fieldvalue); $value = $child->append_child($value); } // foreach } // while // pobierz tresc dokumentu XML $xml_string = $doc->dump_mem(true); echo $xml_string; ?>
Kod ten wyprodukuje dokument XML o takiej strukturze:
<?xml version="1.0"?> <root> <tabela_nadrzedna> <kolumna1>wartosc1</kolumna1> <kolumna2>wartosc2</kolumna2> <kolumna3>wartosc3</kolumna3> ............... <tabela_podrzedna> <kolumna1>wartosc1</kolumna1> <kolumna2>wartosc2</kolumna2> <kolumna3>wartosc3</kolumna3> ............... </tabela_podrzedna> <tabela_nadrzedna> ............... </tabela_podrzedna> ............... </tabela_podrzedna> </root>
Jak widać, <tabela_podrzedna> zagnieżdżona jest w <tabela_nadrzedna>, a ta w <root>. <tabela_nadrzedna> posiada zarówno znaczniki reprezentujÄ…ce kolumny tegoż, jak i te identyfikujÄ…ce poszczególne rekordy z tabeli podrzÄ™dnej.
Możliwe, że czasami zechcesz wstawić do znaczników dodatkowe informacje. Może to być zrealizowane za pomocÄ… atrybutów. Atrybut posiada nazwÄ™ i wartość, a każdy znacznik może posiadać dowolnÄ… ich ilość. W DomXML dodajemy je za pomocÄ… metody '->set_attribute' wstawionej pomiÄ™dzy '->append_child', oraz '->create_text_node', np:
$child = $doc->create_element($fieldname); $child = $outer->append_child($child); // dodaj atrybuty $child->set_attribute('attr1', 'attrval1'); $child->set_attribute('attr2', 'attrval2'); // kontynuuj dodawanie danych $value = $doc->create_text_node($fieldvalue); $value = $child->append_child($value);
Wygeneruje to mniej więcej taki dokument XML:
<?xml version="1.0"?> <root> <tabela> <kolumna1 attr1="jakas_wartosc" attr2="jakas_wartosc">wartosc1</kolumna1> <kolumna2 attr1="jakas_wartosc" attr2="jakas_wartosc">wartosc2</kolumna2> <kolumna3 attr1="jakas_wartosc" attr2="jakas_wartosc">wartosc3</kolumna3> </tabela> </root>
Atrybuty możesz dodać zarówno do elementów reprezentujÄ…cych rekordy, jak i kolumny (pola).
W moich wÅ‚asnych aplikacjach używam atrybutów do okreÅ›lenia, jak dużo danych znajduje siÄ™ w każdej kolumnie. DziÄ™ki temu nie muszÄ™ tego zakodowywać "na twardo" w arkuszach XSL. Dla danych wielowierszowych tworzÄ™ atrybuty 'rows' (wiersze), oraz 'cols' (kolumny).
Używam także atrybutów do dołączania różnych komunikatów błędów. Wszystkie one sÄ… zapisywane w tablicy $errors, gdzie klucz jest nazwÄ… pliku, a wartość odpowiednim komunikatem. Jest on wstawiany jako atrybut do pola, które go wygenerowaÅ‚o, np.
if (isset($errors[$fieldname])) { $child->set_attribute("error", $errors[$fieldname]); } // if
DziÄ™ki użyciu tej metody byÅ‚em w stanie zaprojektować uniwersalny mechanizm tworzenia plików XML na podstawie danych z relacyjnych systemów baz. Wszystko, czego potrzebowaÅ‚em, to okreÅ›lić nazwy(Ä™) tabel(i), oraz ustalić kryteria wyboru odpowiednich informacji. Później za pomocÄ… arkusza XSL bez problemu wygenerowaÅ‚em statyczne dokumenty HTML.
Tony Marston jest weteranem w dziedzinie projektowania oprogramowania w jÄ™zykach drugiej, trzeciej i czwartej generacji; gÅ‚ównie jeÅ›li chodzi o aplikacje klient/serwer. Niedawno zainteresowaÅ‚ siÄ™ technologiÄ… PHP/MySQL jako doskonałą platformÄ… do ich tworzenia. Obecnie pracuje nad sieciowym systemem korzystajÄ…cym z PHP/MySQL, gdzie caÅ‚y kod HTML jest utworzony za pomocÄ… XML/XSL, oraz wÅ‚asnorÄ™cznie napisanych mechanizmów.
Możesz się z nim skontaktować poprzez jego stronę domową http://www.tonymartson.net.
Przetłumaczył ZyxwvU (zyxwvu@me2.pl)
Publikowane i tłumaczone za zgodą autora (wyłączność dla Webcity)
Tekst oryginalny: http://www.zend.com/zend/tut/tutorial-DOM-XML.php
Waszym zdaniem:
Nikt jeszcze nie dodał swojego komentarza. Możesz być pierwszy!