Pliki XML generowane z bazy danych

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.

Wymagania

Wszystkie przykÅ‚ady wymagajÄ… zainstalowanego w PHP rozszerzenia "DomXML". Zalecana jest także znajomość budowy plików XML.

Do góry

Pobieranie danych z pojedynczej tabeli

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.

Do góry

Dwie tabele połączone wzajemnymi relacjami

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.

Do góry

Dodawanie atrybutów

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 

Do góry

Zakończenie

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.

Do góry

O autorze

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

Do góry

Waszym zdaniem:

Nikt jeszcze nie dodał swojego komentarza. Możesz być pierwszy!


Twoim zdaniem:

Reklama

banner

Partnerzy

CityDesign.pl
phpSolutions