Heavymind
Gdyby ludzie rozmawiali tylko o tym, co rozumieją, zapadłaby nad światem wielka cisza.

20/12/2007

Zend Framework Tutorial - Rozwijanie Zend View - Zend Layout

Opublikowane jako: Off topic — Kubek Bartosz @ 00:30

Zend Framework Tutorial

cześć III

Rozwijanie Zend View :

Zend Layout

Kubek Bartosz, www.heavymind.net
Wersja dokumentu 1.2
Copyright © 2007 - 2008

Witam w trzecim artykule z serii “Zend Framework Tutorial”. Dokument ten jest pierwszym z kilku, które tyczyć się będą szczególnie jednej z trzech składowych struktury MVC – Widoków. Tym razem zajmiemy się zagadnieniem kryjącym się pod nazwą “Two Step View”. Poznamy minimalistyczne podstawy teoretyczne i spróbujemy zastosować je w praktyce. Wiedzę i praktykę poznaną na łamach tego samouczka, będziemy mogli także zastosować w kolejnych częściach serii. Zapraszam serdecznie.

UWAGA: Ten samouczek wymaga znajomości zagadnień z pierwszej i drugiej części serii: Pierwsze kroki z Zend Framework oraz Zend Framework Tutorial - Rozwijanie Aplikacji, jak również posiadać należy działającą aplikację testową z drugiej części serii. Przykłady zawarte w tym dokumencie, przetestowane zostały w oparciu o Zend Framework w wersji 1.5.1. Najprawdopodobniej będą działały także z nowszymi wersjami, jednak jest mało prawdopodobne, by prawidłowo mogły być uruchamiane z wersjami wcześniejszymi niż wersja 1.5.0.

Spis treści:

Layout View – czyli jak dobrze się nie powtarzać

DRY – Dont Repeat Yourself (Nie powtarzaj się) – to zasada, którą od dawna kierują się programiści wszelkich języków. Była popularna już za czasów programowania proceduralnego, gdzie wielokrotnie wykonywanie tych samych akcji, zastępowano jedną funkcją, którą można było użyć w wielu miejscach. Współcześnie programowanie obiektowe w jego ogólnym zarysie jest pochodną tej zasady . W końcu po to dla przykładu, tworzymy obiekt “Instrument” po którym dziedziczą obiekty “Gitara” i “Skrzypce”, by wspólnej dla instrumentów tych cech (tj. akcja “zagraj()”, lub własność “kolor”) nie powtarzać i zaimplementować je w obiekcie rodzica “Instrument”. Naturalnym odruchem jest chęć stosowania DRY w wszelkich obszarach pisanego oprogramowania – również Widoku aplikacji MVC napisanej w PHP5/HTML. Omawiany w tym artykule “Two Step View” jest wzorcem w imię tej zasady.

Wyobraźmy sobie że tworzymy stronę www. Typowa strona www posiada część HEAD oraz BODY. I tak jak w sekcji BODY treść zmienia się bardzo wiele podczas przechodzenia poprzez poszczególne podstrony serwisu, tak w sekcji HEAD zmienia się bardzo mało, czasem nawet nic. Idąc dalej, bardzo często serwisy budowane są w taki sposób, by na wszystkich jego stronach, również i stopka wyświetla zawsze identyczną treść, np. prawa autorskie lub dane kontraktowe firmy. Celem Two-Step View jest wykorzystanie tego faktu w taki sposób, by programista nie musiał kopiować tych statycznych części naszej strony i mógł je zaimplementować w jednym wspólnym miejscu, z którego będą korzystały wszystkie podstrony.

Poprzez nasze dotychczasowe doświadczenia z poprzednich części Zend Framework Tutorial, znamy już pewien sposób na przeciwdziałanie kopiowaniu nagłówka i stopki strony do każdej z podstron. Robiliśmy to poprzez każdorazowe inkludowanie do aktualnie generowanej strony, osobnych plików zawierających odpowiednio: treść nagłówkową (w pierwszym) i stopkę (w drugim). Jest to bardzo popularne rozwiązanie.

Obrazek 1 – Inkliudowanie wspólnych nagłówka i stopki

W dość naturalny jednak sposób pojawia się pytanie, czy nie można by uniknąć tego każdorazowego inkludowania plików nagłówkowych na każdej z podstron? Otóż można, i to na wiele sposobów.

Jako że jesteśmy grupą programistów zainteresowanych rozwiązaniami pochodzącymi z narzędziowni Zend Framework, nie sposób wspomnieć przy tej okazji o popularnie używanym do tego celu przez programistów Zend Framework procesie post-dispatching’u. Jest to moment, gdy silnik Zend Controller wykonał już wszystkie akcje jakie miał zadane i na ich podstawie jest gotów do wygenerowania treści strony. To w tym właśnie momencie, większość programistów decyduje się na “wkradnięcie się” do wygenerowanej przez Zend View treści (wykonuje się to za pomocą pisania pluginów do Front kontrolera) i doklejenia do niej wspólnych stałych nagłówków i stopki.

Co więcej, wszelkie opisywane w tym i kolejnych artykułach serii rozwiązania, bazują na tej metodzie. Różnica jednak jest taka, że mając na uwadze cel, jaki ma spełniać “framework”, czyli udostępniać prostych i gotowych rozwiązań, opisywane w tym i kolejnych dokumentach biblioteki dodatkowe, mają właśnie udostępnić tego typu udogodnienia, by programista mógł z nich po prostu skorzystać i się skupić wyłącznie na logice i treści tworzonej aplikacji, nie musząc wnikać w strukturę silnika frameworka i go modyfikować na własne potrzeby (choć praktyka pokazuje zbyt często niestety, że bardzo trudno tego uniknąć tak czy inaczej).

Tak więc naszym sposobem na rozwiązanie tego zagadnienia będzie skorzystanie z wzorca Two Step View, poprzez bibliotekę Zend_Layout (która weszła na stałe do pakietu Zend Framework wraz z wersją 1.5.0). Dzięki wykorzystaniu tej biblioteki, będziemy mogli stworzyć jeden plik, którego treścią będzie nagłówek strony, oraz stopka, a treść poszczególnych generowanych stron w naszej aplikacji, zostanie automatycznie wklejana w wyznaczonym miejscu szablony Layout’u.

Obrazek 2 – wzorzec Layout View

Tworzenie głównego szablonu widoku

Pierwszym krokiem przygotowawczym jaki poczynimy, będzie stworzenie głównego szablonu widoku, czyli Layout’u.

Dotychczas opis nagłówka i stopki naszej strony mieliśmy zdefiniowany w dwóch plikach szablonu. Oto one:

plik: application/views/scripts/header.phtml

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
   <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
   <title><?php echo $this->escape($this->title); ?></title>
   <link rel="stylesheet" type="text/css" media="screen"
      href="<?php echo $this->baseUrl;?>/public/styles/style.css" />
</head>
<body>
   <div id="content">

plik: application/views/scripts/footer.phtml

   </div>
</body>
</html>

Jak już wcześniej sobie wyjaśniliśmy, szablon Layout’u zawiera w sobie całą “uwspólnioną dla wszystkich podstron” treść serwisu www. Tak więc naszym nowym plikiem będzie:

plik: application/views/scripts/layout.phtml

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
   <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
   <title><?php echo $this->escape($this->title); ?></title>
   <link rel="stylesheet" type="text/css" media="screen"
      href="<?php echo $this->baseUrl;?>/public/styles/style.css" />
</head>

<body>
   <div id="content">
      <?php echo $this->layout()->content; ?>
   </div>
</body>

</html>

Zwróćmy proszę uwagę na treść tego pliku. Jest to niemal złożenie treści dwóch wcześniej wymienionych plików w jeden, bez jakiejkolwiek zmiany. Jedyną nowością jaka zaistniała, to linia znajdująca się na “złożeniu” treści nagłówka i stopki:

<?php echo $this->layout()->content; ?>

Znienna “content” jest kontenerem, do którego silnik Zend Framework, a dokładnie Zend Layout będzie wkładał wygenerowaną treść HTML aktualnie wywołanej strony serwisu www. Efektem tego złożenia będzie kompletna strona składająca się z stałej treści nagłówkowo-stopkowej naszego Layoutu, oraz zmienna teść zależna od aktualnie wywołanej podstrony serwisu www. Aby jednak tak się stało, akcję przypisania dynamicznej treści do kontenera content musimy także “zaprogramować”. To jednak za chwilę.

Ostatecznie, jako że nowo stworzony przez nas plik layout.phtml zastępuje nam rolę dotychczasowych plików header.phtml oraz footer.phtml, usuńmy je z projektu. Nie będą już nam potrzebne.

Wskazówka: Zend_Layout jest w pełni konfigurowalną biblioteką, dzięki czemu możemy dla przykadu zmieniać lokację pliku layout’owego, jego nazwę, bądź także definiować wiele róźnych layoutów dla jednej aplikacji. Szczegóy w dokumnetacji Zend_Layout

Porządki w szablonach

Pozostała nam jeszcze jedna rzecz do uporządkowania z szablonami HTML. A mianowicie, dotychczas – w każdym generowanym przez naszą aplikację szablonie, załączaliśmy poprzez linkowanie szablon nagłówkowy header.phtml na samym początku pliku, oraz szablon stopki footer.phtml na jego końcu. Mowa tu o inkludowaniu plików, które przed chwilką powinniśmy byli usunąć z naszego projektu. Skoro to uczyniliśmy, pora przyszła na usunięcie także wszystkie ów podlinkowań.

Przejdźmy więc przez poniższą listę szablonów otwierając je :

/application/views/scripts/index/add.phtml
/application/views/scripts/index/delete.phtml
/application/views/scripts/index/edit.phtml
/application/views/scripts/index/index.phtml

i usuńmy następujące dwie linie w każdym z nich, każdorazowo zapisując zmiany:

<?php echo $this->render('header.phtml'); ?>

oraz :

<?php echo $this->render('footer.phtml'); ?>

Abyśmy mieli pełną jasność, przejrzyjmy treść poszczególnych w/w szablonów, by upewnić się że wszystko przygotowaliśmy poprawnie. Oto one:

plik: application/views/scripts/index/add.phtml

<h1><?php echo $this->escape($this->title); ?></h1>
<?php echo $this->render('index/_form.phtml'); ?>

plik: application/views/scripts/index/delete.phtml

<h1><?php echo $this->escape($this->title); ?></h1>

<?php if ($this->album) :?>
<form action="<?php echo $this->baseUrl ?>/index/delete" method="post">
   <p>Are you sure that you want to delete '
      <?php echo $this->escape($this->album->title); ?>' by '
      <?php echo $this->escape($this->album->artist); ?>'? </p>
   <div>
      <input type="hidden" name="id" value="<?php
         echo $this->album->id; ?>" />
      <input type="submit" name="del" value="Yes" />
      <input type="submit" name="del" value="No" />
   </div>
</form>

<?php else: ?>
<p>Cannot find album.</p>
<?php endif;?>

plik: application/views/scripts/index/edit.phtml

<h1><?php echo $this->escape($this->title); ?></h1>
<?php echo $this->render('index/_form.phtml'); ?>

plik: application/views/scripts/index/index.phtml

<h1><?php echo $this->escape($this->title); ?></h1>

<p><a href="<?php echo $this->baseUrl; ?>/index/add">Add new album</a></p>
<table>
   <tr>
      <th>Title</th>
      <th>Artist</th>
      <th> </th>
   </tr>

   <?php foreach($this->albums as $album) : ?>
   <tr>
      lt;td><?php echo $this->escape($album->title);?></td>
      <td><?php echo $this->escape($album->artist);?></td>
      <td>
         <a href="<?php echo $this->baseUrl; ?>/index/edit/id/
            <?php echo $album->id;?>">Edit</a>
         <a href="<?php echo $this->baseUrl; ?>/index/delete/id/
            <?php echo $album->id;?>">Delete</a>
      </td>
   </tr>
   <?php endforeach; ?>
</table>

Nadmienię jeszcze o jednym dodatkowym szablonie, który istnieje w naszym katalogu widoków dla kontrolera Index, o którym to pliku nie wspomnieliśmy dotychczas : _form.phtml. Nasze niezainteresowanie nim jest oczywiście celowe, ponieważ wiemy iż szablon ten służy jako pod-szablon wykorzystywany przez inne szablony (jest dołączany w add.phtml oraz edit.phtml). Innymi słowy, szablon ten nigdy nie miał zainkludowanych plików nagłówka i stopki, ponieważ nie był nigdy wykorzystywany do generowania strony samodzielnie. Tak więc niczego w nim nie zmieniamy.

Podsumowując zagadnienia szablonów – wykonaliśmy w nich wszystko co potrzeba do wykorzystania wzorca Layout’owego. Mam nadzieję że dotychczasowe nasze prace są wystarczająco przejrzyste, by móc przejść do ostatniego wymaganego kroku.

Inicjalizacja Zend Layout

Jak wcześniej już wspomniałem, musimy poinstruować teraz aplikację by korzystając z funkcjonalności dostarczonej przez biblioteki Zend Layout, umieszczała generowane strony w wskazane przez nas miejsce w pliku Layout’u.

Najlepszym miejscem na zapisanie takich instrukcji, będzie funkcja init() inicjująca nasz własny, główny kontroler akcji Album_Controller_Action (przypominam, że jest to kontroler, po którym w naszej aplikacji dziedziczą wszystkie kontrolery akcji, czyli ten w którym funkcja inicjalizacyjna init() jest uruchamiana jako pierwsza – przed wykonaniem jakiejkolwiek akcji w kontrolerach akcji. Jeśli czujesz się zagubiony w nadmienionych tutaj terminach, zapraszam do odświeżenia sobie wiedzy podczas lektury Pierwsze kroki z Zend Framework oraz Zend Framework Tutorial – Rozwijanie aplikacji).

Tak więc dodajmy tę oto linię kodu jako ostatnią w w/w metodzie.

plik: library/Album/Controller/Action.php

...
public function init() {
   // load configuration
   $this->obConfig = new Zend_Config_Ini('../application/config.ini', 'general');

   set_include_path('.' . PATH_SEPARATOR . $this->obConfig->path->models
      . PATH_SEPARATOR . get_include_path());

   // setup database
   $db = Zend_Db::factory(
      $this->obConfig->db->adapter,
      $this->obConfig->db->config->toArray() );
   Zend_Db_Table::setDefaultAdapter($db);

   //initialize Zend Layout
   Zend_Layout::startMvc();
}
…

Bardzo niewiele, a jednak wystarczająco by wszystko zadziałało. W tym oto momencie jesteśmy gotowi by uruchomić w przeglądarce naszą aplikację i przekonać się że nic się nie zmieniło w sposobie działania naszej aplikacji. Tyle że informacja o braku zmian, jest dla nas bardzo dobrą informacją :) Mówi ona bowiem o tym, że poprawnie wykorzystaliśmy możliwości Zend_Layout.

Podsumowanie

Artykuł ten tematyką swoją dotknął nowych możliwości, które udostępnione zostay wraz z wersją Zend Framework 1.5.0.

Mam nadzieję, iż przedstawiona koncepcja okaże się dla Ciebie - drogi czytelniku – wartościowa i przejrzysta.

Jeśli natomiast nie stało się tak, polecam przeglądnięcie ponowne treści tego artykułu od samego początku. Z pewnością całkowite zrozumienie zagadnienia, okaże się pomocne na łamach jednego z kolejnych artykułów: “Zend Framework Tutorial – Rozwijanie Zend View – Widoki Kompozytowe”.

Repozytorium SVN

Opisywana wyżej aplikacja testowa jest dostępna do pobrania za pośrednictwem systemu kontroli wersji Subversion (SVN), na serwerach udostępnionych przez usługę Google Code. Kod źródłowy kolejnych wersji tego samouczka, które to powstawały ze względu na kolejne wersje Zend Framework, został odpowiednio rozdzielony, na tzw. tagi. Ze względu na aktualność tego artykłu, jest nim póki co jeden:

Aby skorzystać z w/w repozytoriów, należy użyć jednego z istniejących klientów SVN, tj. np.: KSVN (dla KDE linux), TortoiseSVN (dla windows), lub po prostu CLI klienta SVN w *nix systemach.

Kod aplikacji zawarty w repozytorium można także przeglądać poprzez przeglądarkę internetową, umieszczając adres repozytorium w pasku adresu.

Pozdrawiam

Kubek Bartosz



Komentarze: 14 »

  1. […] Tyle słowem wstępu. Zapraszam więc do lektury pierwszego z serii artykułu, pt : „Zend Framework Tutorial – Rozwijanie Zend View - Zend Layout”. […]

    Pingback od Zend Framework Tutorial - Rozwijanie Zend View - Zend Layout - nowy artukuł | Heavymind — 20/12/2007 @ 00:34

  2. Tutaj chyba masz błąd? :)
    Zend_Layout::addLayout(’content’,’_layout.tpl’);

    powinno być,
    Zend_Layout::addLayout(’content’,’_layout.phtml’);
    W cały artykule używasz ‘_layout.phtml’.

    Cały artykuł fajny.
    Tylko ja inicjalizuje Zend_Layout w bootstrap i potem o nim zapominam w cały projekcie , ale każdy robi jak chce. :D
    Pozdrawiam.

    Komentarz od Sapper — 20/12/2007 @ 09:00

  3. @Sapper: Dziękuję za to sokole oko. Naturalnie jest to błąd. Nazbyt często jestem zmuszony korzystać z Smarty, stąd takie odruchowe efekty. Poprawka w drodze.

    Komentarz od Kubek Bartosz — 20/12/2007 @ 09:34

  4. W najnowszej rewizji (1.4 http://framework.zend.com/wiki/display/ZFPROP/Zend_Layout) trochę się pozmieniało. Za cholerę nie mogę odpalić…

    Komentarz od steamm — 11/01/2008 @ 17:39

  5. @steamm: Zaczekajmy może do momentu pojawienia się oficjalnej wersji 1.4 z testami. Z pewnością jednak dużo musiało się zmienić w Zend_Layout od czasu wersji którą opisuję. Jednak bardzo dobrze, że z propozycji biblioteki stała się elementem “jądra” Zend Framework.

    Komentarz od Kubek Bartosz — 11/01/2008 @ 19:44

  6. Dziwna polityka, a ta 1.4 nie jest oficjalna, przecież jest w trunku a nie w inkubatorze… z tego co widzę to nie tylko ja mam problemy z odpaleniem.

    Komentarz od steamm — 11/01/2008 @ 21:12

  7. […] poinformować, iż zaktualizowałem trzecią część serii Zend Framework Tutorial, pt.: “Rozwijanie Zend View - Zend Layout“. Artykuł ten jest w tym momencie dostosowany do zmian jakie zostały wprowadzone wraz z […]

    Pingback od Aktualizacja samouczka Zend Framework Tutorial - Rozwijanie Zend View - Zend Layout | Heavymind — 07/05/2008 @ 20:52

  8. Doczytałem się że można zmienić zmienną content na inną. Np.

    layout()->naglowek; ?>

    layout()->content; ?>

    layout()->stopka; ?>

    Jak zarządzać z poziomu kontrolera tymi zmiennymi?

    Komentarz od darek_s — 15/05/2008 @ 15:43

  9. Uwaga z mojej strony: bez Zend_Layout (czyli w ZF 1.0) również można było zorganizować tak szablony jak przedstawiłeś powyżej. Jednak trzeba było dopisać kilka linijek kodu (np wtyczce dispatchLoopShutdown). Idea polegała na tym, aby wynik danej akcji zapisywać do zmiennej (np pageContent) i renderować szablon główny. Wówczas w szablonie głównym zamiast:
    layout()->content; ?> piszesz pageContent; ?>
    Zasada jednak jest taka sama ;)

    Komentarz od Szymon — 31/05/2008 @ 15:51

  10. Może ktoś mi wyjaśnić dlaczego mam link do arkusza stylów w sekcji body zamiast head? Wiecęj tu: http://forum.zend-framework.pl/viewtopic.php?id=417 :/

    Komentarz od midnight — 22/07/2008 @ 13:45

  11. @midnight: wkleiłem podany przez Ciebie kod do swojego pliku layout.phtml i struktura HTML w firebug’u wyświetliłą się całkowicie poprawnie. Test wykonałem na najaktualniejszej wersji firebug’a i firefox’a.

    Komentarz od Kubek Bartosz — 22/07/2008 @ 13:53

  12. http://wklej.org/id/dc27bffc5c - nie działa :/

    Komentarz od midnight — 22/07/2008 @ 14:23

  13. W skryptach przez Ciebie podanych jest bład. Zamiast base_url ma być baseUrl :- ) pozdrawiam.

    Komentarz od Blask Ogina — 22/07/2008 @ 16:18

  14. Witam!
    Nie wiem czy nie nadążam czy może komentarze są jeszcze z “czasów” wcześniejszych wersji tego artykułu ale w powyższej treści artykułu nie widzę użycia wspomnianej przez Sapper’a metody Zend_Layout::addLayout(’content’,’_layout.phtml’);
    Więc jak z tym jest?

    Druga sprawa to kwestia nazwy pliku ze stylami. W pierwszej części tutoriala użyłeś nazwy site.css a w tej pojawia się style.css więc jakby ktoś przerabiał po kolei poszczególne części tutoriala i po wprowadzeniu zmian z tej części nagle przestanie się wyświetlać stylizowana strona to sprawdźcie właśnie nazwę pli css.

    A tak wogóle to wielkie dzięki za tą serię tutoriali; jest baardzo przydatna :)
    pozdrawiam

    Komentarz od Tomek — 27/09/2008 @ 22:34

Kanał RSS dla tego wpisu. TrackBack URL

Dodaj komentarz

Oparte na WordPress