JustPaste.it

Open Power Template w praktyce

W tym artykule omówione zostało wykorzystanie oferowanych przez OPT możliwości w różnych praktycznych sytuacjach oraz metodyka pracy z systemami szablonów.

W tym artykule pragnę pokazać, jak korzystać z systemu szablonów Open Power Template w codziennej praktyce. Szczególny nacisk położony zostanie na strukturę kodu źródłowego, a także użycie najważniejszych cech tego systemu do osiągnięcia pożądanych rezultatów. Ze względu na objętość materiału, nie jestem w stanie zaprezentować tutaj w pełni kompletnego, działającego kodu. Stąd też czytelnik powinien wiedzieć, czym jest np. MVC tak, aby móc w stanie wyobrazić sobie na podstawie opisu brakujące fragmenty oraz prawidłowo zinterpretować pseudokod. Jeśli nie znasz jeszcze Open Power Template, zapoznaj się najpierw z artykułami "Wprowadzenie do OPT" cz. 1 i 2, które omawiają podstawy pracy z nim.

Struktura kodu źródłowego

Dostępne w OPT przykłady mają na celu przeważnie zaprezentowanie różnych jego cech, przez co słabo pokazują metodykę korzystania z szablonów do osiągnięcia pożądanych rezultatów. Szablony służą bowiem nie tylko do separacji kodu HTML od PHP. Odpowiednio użyte, mogą znacząco zmienić nasze podejście do projektowania aplikacji WWW - rzeczy, o której wielu przeciwników tego typu rozwiązań po prostu zapomina. Przyjrzymy się tutaj hipotetycznemu projektowi niewielkiej witryny WWW z artykułami, aktualnościami oraz systemem komentarzy. Jak wynika z opisu, witryna posiada elementy interaktywne, z których może skorzystać internauta, a także zwyczajne prezentowanie mu różnych treści. Działanie systemu oparte jest o prosty model MVC, w którym model opdowiada za pobieranie danych z bazy, kontroler za uruchomienie akcji, natomiast widokiem jest sam system szablonów wspomagany dodatkowo przez kod akcji do wykonania. Pierwszym krokiem, jakiemu przyjrzymy się dokładniej, jest inicjacja biblioteki. Ze względu na specyfikę projektu, musi ona zostać rozbita na kilka etapów podzielonych pomiędzy parę plików.

Pierwsza rzecz, na jaką powinniśmy zwrócić uwagę, to raportowanie błędów. OPT wszystkie problemy zgłasza jako wyjątki, które możemy (a raczej powinniśmy) przechwycić i odpowiednio sformatować. W naszym CMS-ie cała inicjacja zawarta jest w rozmaitych filtrach.

public function initSystem()
{
$errorLog = errorLog::getInstance();
try
{
$this -> loadFilters();
$this -> runAction();
$this -> view -> display();
}
catch(optException $exception)
{
$errorLog -> setCustomReporting();
$errorLog -> process($exception);
}
catch(appException $exception)
{
$errorLog -> process($exception);
}
} // end initSystem();

Procedura inicjacji zrealizowana jest w następujący sposób:

  1. Na początku ładujemy obiekt uniwersalnej klasy do rejestracji i obsługi błędów.
  2. Wewnątrz bloku wyjątków dokonujemy inicjacji oraz uruchomienia systemu. Na początku ładujemy filtry, wśród których jest również ten odpowiadający za utworzenie oraz skonfigurowanie klasy optClass.
  3. Uruchamiamy akcję, która wykonuje to, co ma się na danej podstronie wykonywać.
  4. Na końcu uruchamiamy metodę display().

Pora wyjaśnić, gdzie i w jaki sposób będziemy wykonywać szablony. Zakładamy, że projekt posiada pliki header.tpl oraz footer.tpl reprezentujące odpowiednio nagłówek i stopkę. Pomiędzy nimi wyświetlamy szablon aktualnej podstrony. Cała sztuczka polega na tym, aby najpierw wykonać caluteńki skrypt i przesłać dane do parsera i dopiero w ostatnich trzech linijkach nakazać przetworzenie szablonów. Zilustuję to pewnym pseudokodem:

<?php
 
// Tutaj inicjujemy system

initSystem();


// Tutaj wykonujemy wszystko i ładujemy dane do parsera:

processPage();

// A na samym końcu dajemy:

$tpl -> parse('header.tpl');
$tpl -> parse($pageTemplate);
$tpl -> parse('footer.tpl');

die(); // tu już jest koniec, dalej nic nie robimy, bo wysłaliśmy już HTML
 
?>

Takie podejście daje nam niesamowicie dużo zalet. Po pierwsze, gdyby w trakcie wykonywania pojawił się jakiś krytyczny błąd, jesteśmy w stanie cofnąć wszystko poprzez zatrzymanie skryptu i przekierowanie działania do procedury obsługi błędów, która może w ten sposób bez trudu wygenerować od zera własny kod HTML. Ponadto, mamy pełną swobodę w wysyłaniu ciastek, aczkolwiek w przypadku korzystania z OPT nie jest to problemem, ponieważ parser ma wbudowane buforowanie wyjścia. Ostatnia zaleta to zwiększenie palety możliwości wpływania na treść przez samą podstronę. Jeżeli ta stwierdzi w połowie wykonywania, że kod HTML powinien mieć inny tytuł, nic nie stoi na przeszkodzie, by go zmienić, ponieważ nic jeszcze do przeglądarki nie poleciało. Podobnie jeśli mamy kilka wersji nagłówków strony - skrypt może w DOWOLNYM momencie swego wykonywania zdecydować, którego użyć.

Kod odpalający szablony powinien znajdować się w metodzie display(), której zadaniem jest także wprowadzenie do parsera danych mających pokazać się w nagłówku i stopce strony. Poniżej zaprezentowana została przykładowa implementacja klasy view:

<?php
 
class view
{
private $title;
private $templates = array();

// Tym ustawiamy tytul strony
public function setTitle($title)
{
$this -> title = $title;
} // end setTitle();

// Tym dodajemy szablony, ktore podstrona chce wykorzystywac
public function setTemplate($template)
{
$this -> templates[] = $template;
} // end setTemplate();

public function resetTemplates()
{
$this -> templates = array();
} // end resetTemplates();

public function message($message, $redirect = 'index.php')
{
global $tpl;

$tpl -> assign('message', $message);
$tpl -> assign('redirect', $redirect);

$this -> setTitle('Wiadomość');
$this -> resetTemplates();
$this -> setTemplate('message.tpl');

$this -> display();
} // end message();

public function display()
{
global $tpl, $main;

// Tutaj ustawiamy tytul strony.
// Z $main->config->pageTitle ciagniemy tytul witryny
// ktory automatycznie jest doklejany do tytulu
$tpl -> assign('title', $this -> title.' - '.$main -> config -> pageTitle);

// Adres strony, tez z konfiguracji - moze sie przydac
$tpl -> assign('address', $main -> config -> address);

// Tu mozemy dac np. generowanie menusow, nawigacji itd.

// Tu przetwarzamy wszystkie szablony jeden za drugim
$tpl -> parse('header.tpl');
foreach($this -> templates as $template)
{
$tpl -> parse($template);
}
$tpl -> parse('footer.tpl');

// Dalej juz nic sie nie wykona, bo i po co?
die();
} // end display();
}
?>

Podsumowując, klasa view pełni rolę bufora między skryptem, a systemem szablonów. Zarządza kolejnością ich odpalania, zajmuje się automatycznym dołączeniem nagłówka, stopki itd. oraz udostępnia kilka dodatkowych metod, jak np. message() pozwalającą na szybkie wyświetlenie jakiegoś komunikatu dla internauty. Naturalnie jest to jedynie podstawowy schemat, który należy dostosować do własnych potrzeb oraz struktury własnego kodu. Niemniej zawarte w nim wskazówki mają charakter uniwersalny i co ważniejsze, naprawdę sprawdzają się w praktyce!

Formularze

Oficjalny dodatek, Open Power Forms wciąż jest w przygotowaniu i dlatego obsługę formularzy należy sobie w OPT dodać we własnym zakresie. Jednak system szablonów nie pozostawia nas zupełnie bezradnych w obliczu tego żywiołu, ponieważ posiada on wsparcie dla tzw. komponentów. Są to niewielkie klasy wykonywane razem z szablonem i osadzane w nim, które generują kod HTML, mogą odbierać od szablonu różne dane i generować proste zdarzenia. Co ważniejsze, obiekty tych klas mogą być zarówno tworzone automatycznie przez szablon, jak i przez programistę w obrębie skryptu, a później wyświetlane np. za pomocą sekcji. Teraz wystarczy, że stworzymy sobie komponenty obsługujące najważniejsze kontrolki, np. pole tekstowe, checkboksy, listy rozwijane i poumieszczamy je zamiast statycznego kodu HTML. Dzięki temu, że komponent napisany jest w PHP, może automatycznie pobrać dane z systemu kontroli wejścia (a niech już stracę - walidatora), a także zrobić wiele innych rzeczy. Proces tworzenia takiego komponentu został szczegółowo opisany w artykule "Wprowadzenie do OPT cz. 2" i do niego też odsyłam po dalsze szczegóły, natomiast tu pragnę skupić się na pokazaniu ich praktycznego użycia.

Pierwsza rzecz to dobra organizacja kodu. Komponenty mogą wykorzystywać zdarzenia do wyświetlania pod sobą jakiegoś komunikatu, jeśli zostały nieprawidłowo wypełnione. Oto przykład:

{inputComponent name="nazwisko"}
{onError message="msg" position="down"}
<span class="error">{@msg}</span>
{/onError}
{/inputComponent}

Teraz, jeżeli formularz zostanie źle wypełniony, komponent automatycznie zadba o wywołanie zdarzenia "onError" pod sobą, a treść komunikatu zostanie przypisana do zmiennej @msg. Sęk w tym, że jak formularzy jest pięćdziesiąt, a w każdym dodatkowo po przynajmniej pięć pól, to nagle robi nam się z tego 250 kodów praktycznie tego samego zdarzenia. Aby tego uniknąć, musimy skorzystać z kolejnych atrakcji. Pierwsza rzecz to stworzenie zdarzenia ogólnego - takie zdarzenie można sobie podpiąć w prosty sposób do dowolnego komponentu. Jeśli przyjdzie nam wyedytować jego kod HTML, zmiana zostanie uwzględniona w całym projekcie. Poniżej podany jest kod:

{bindEvent id="errorev" name="onError" message="msg" position="down"}
<span class="error">{@msg}</span>
{/bindEvent}
 
<table class="form">
<tr>
<td>Imię:</td>
<td>{inputComponent name="imie"}{load event="errorev"/}{/inputComponent}</td>
</tr>
<tr>
<td>Nazwisko:</td>
<td>{inputComponent name="nazwisko"}{load event="errorev"/}{/inputComponent}</td>
</tr>
<tr>
<td>Wiek:</td>
<td>{inputComponent name="wiek"}{load event="errorev"/}{/inputComponent}</td>
</tr>
</table>

Już rzecz wygląda o wiele prościej, jednak wciąż pozostaje jedna kwestia związana z działaniem samego parsera szablonów. W OPT, podobnie jak w większości innych systemów, szablony są raz kompilowane do postaci kodu PHP, który jest później używany wielokrotnie, aby zaoszczędzić na czasie. Instrukcja bindEvent jest specyficzna, ponieważ działa tylko w momencie kompilacji szablonu, a w wynikowym kodzie PHP nie pozostawia żadnego śladu swego istnienia. Może teraz dojść do niebezpiecznej sytuacji: umieszczamy takie zdarzenie ogólne w pliku header.tpl i później sukcesywnie wczytujemy je do wszystkich formularzy. Uruchamiamy naszą stronę pierwszy raz, szablony są kompilowane i jest fajnie - zdarzenia działają. Jednak później musimy wyedytować szablon jednego z formularzy. Po uruchomieniu jest on na nowo kompilowany, lecz... bez kodu obsługi zdarzenia! Co się stało? Po prostu kompilator nie kompilował już nam pliku header.tpl z definicją zdarzenia ogólnego, zatem nie wiedział w ogóle, co ma w formularzu podpiąć! Takie zachowanie w OPT 1.0.0 zmuszało programistę do powielania kodu zdarzeń ogólnych we wszystkich szablonach z formularzami, a tego przecież pragniemy uniknąć. Sprawę tę rozwiązała dopiero wersja 1.1.0, wraz z wprowadzeniem tzw. szablonów kompilacyjnych. Są to dodatkowe szablony, które kompilator ładuje sobie automatycznie w momencie swojej inicjacji. Wystarczy powiadomić OPT, jakie pliki są szablonami kompilacyjnymi, a następnie umieścić w nich nasze zdarzenia ogólne. Teraz, kiedy będzie trzeba przekompilować obojętnie jaki szablon, mamy stuprocentową pewność, że wszystkie dodatkowe dane zostaną automatycznie załadowane i będziemy mogli z nich bez przeszkód skorzystać. Oto, jak wygląda cała procedura:

$tpl -> setMasterTemplate('master.tpl');

Tę jedną linijkę należy dodać do kodu inicjacji biblioteki, aby powiadomić o istnieniu szablonu kompilacyjnego master.tpl. Umieszczamy w nim następującą treść:

{bindEvent id="errorev" name="onError" message="msg" position="down"}
<span class="error">{@msg}</span>
{/bindEvent}

I to wszystko - nasze zdarzenie ogólne będzie automatycznie ładowane za każdym razem, gdy zajdzie potrzeba skompilowania czegokolwiek.

Tworzenie całkowicie dynamicznych formularzy dzięki OPT także jest całkiem proste. Biblioteka umożliwia osadzanie w szablonach tzw. komponentów niezdefiniowanych. Są to zwyczajne porty, w które możemy wstawić dowolny obiekt komponentu stworzony bezpośrednio przez skrypt PHP. Dla OPT nie ma znaczenia, czy umieścimy tam listę, czy pole tekstowe, bowiem wszystkie komponenty posiadają jednolity interfejs i biblioteka wie, jak z każdym z nich się komunikować, nawet jeżeli otrzyma je ze skryptu. Poniżej zamieszczony został szablon w pełni dynamicznego formularza (korzystamy z trybu kompatybilności z XML).

<table class="form">
<opt:section name="form">
<tr>
<td><h3>{$form.title}</h3><span>{$form.description}</span></td>
<td><opt:component id="$form.component"><opt:load event="errorev"/></opt:component></td>
</tr>
</opt:section>
<tr>
<td colspan="2"><input type="submit" value="Wyślij"/></td>
</tr>
</table>

Przypominam, że sekcja to taki wysokopoziomowy odpowiednik pętli i jest ona używana do tworzenia wszelkiego rodzaju list. Tu gwarantuje nam ona, że możemy prosto utworzyć sobie tak wielki formularz, jak tylko skrypt będzie sobie tego życzył. Zwróćmy uwagę, jak ładnie się to komponuje z mechanizmem zdarzeń omówionym wyżej.

Załóżmy teraz, że w naszej aplikacji jest edytor konfiguracji, do której administrator może dodawać sobie własne pola (to tłumaczy, dlaczego formularza nie mogliśmy zrobić na sztywno). Informacje o dostępnych polach zapisane są w bazie danych w tabeli config. Wyposażona jest ona w takie pola, jak "title", "description" i "componentType". Ostatnie z nich przechowuje informację o komponencie, który ma być używany do jego edycji. Zapoznajmy się zatem z naszym skryptem PHP:

$stmt = $pdo -> query('SELECT `id`, `title`, `codename`, `description`, `componentType` FROM `config` ORDER BY `order`');
 
$form = array();
while($row = $stmt -> fetch())
{
if(class_exists($row['componentType']))
{
$component = $row['componentType'];
$form[] = array(
'title' => $row['title'],
'description' => $row['description',
// Tworzymy obiekt komponentu recznie
'component' => new $component($row['codename'])
);
}
}
$stmt -> closeCursor();
 
$tpl -> assign('form', $form);

Jeżeli pisałeś kiedyś w pełni autorski system generowania dynamicznych formularzy, porównaj go sobie z rezultatem osiągniętym za pomocą OPT. Naturalnie rozwiązanie to należy jeszcze rozbudować o jakieś źródła danych dla komponentów, aby dało się łatwo ustawiać ich domyślną wartość, a w przypadku list - ustalać ich zawartość. Wszystko to jest zaimplementowane w komponentach, które będą dostarczane razem z pakietem Open Power Forms.

Dynamiczny układ strony

W wielu CMS-ach w skład witryny wchodzą różne moduły uruchamiane w pewnych konkretnych miejscach i wyświetlające tam swoją zawartość. Ponadto, administrator przeważnie może wpływać na ich aktywność oraz miejsca, w jakich się one znajdują. Pokażę teraz, jak można wykorzystać OPT do uzyskania podanej funkcjonalności.

Oczywistą rzeczą jest, że każdy z modułów posiada swój unikalny szablon, który skrypt musi automatycznie załadować. Najprostsze z rozwiązań polega na dość drobnym rozbiciu kodu HTML na poszczególne fragmenty i wywoływaniu metody parse() dla każdej z nich. Poniższa lista przedstawia przykładowy podział na szablony (kursywą zaznaczone są szablony modułów):

  1. Nagłówek strony - sekcja HEAD, logo, początek sekcji modułów
  2. Moduł 1
  3. Moduł 2
  4. Moduł 3
  5. Kod HTML oddzielający sekcję modułów od treści strony
  6. Treść strony
  7. Kod HTML oddzielający treść strony od kolejnej sekcji modułów
  8. Moduł 4
  9. Moduł 5
  10. Stopka

Aby rozwiązać problem konfliktu nazw bloków między modułami, możemy uciec się do pewnej sztuczki, a mianowicie rozszerzyć metodę setTemplate() klasy view.

public function setTemplate($template, $tplData = array())
{
$this -> templates[] = array(
'tpl' => $template,
'data' => $tplData
);
} // end setTemplate();

Teraz do klasy widoku, oprócz informacji, jakiego szablonu życzy sobie strona, trafia też tablica "sprzężonych z nim" bloków charakterystycznych tylko dla niego. Podczas przetwarzania wszystkich szablonów musimy wykonać następującą operację:

$tpl -> parse('header.tpl');
foreach($this -> templates as $template)
{
$tpl -> assign('tplData', $template['data']);
$tpl -> parse($template['tpl']);
}
$tpl -> parse('footer.tpl');

To wszystko - każdy szablon może już odwoływać się do specjalnej tablicy \$tplData, dzięki której uzyska dostęp do swoich unikalnych danych bez obawy, że powstanie jakiś konflikt nazw między modułami. Tablica ta, jak widać, jest inicjowana osobno dla każdego z przetwarzanych szablonów.

Na dłuższą metę rozwiązanie to staje się uciążliwe właśnie z powodu porozrzucania kodu po wielu małych plikach. Musimy tu też pamiętać o szybkości operacji dyskowych. Mając identyczny tekst podzielony między trzy pliki oraz między dziesięć, znacznie szybciej uzyskamy pełen dostęp do mniej zdefragmentowanej wersji. Dlatego powinniśmy wykorzystać dodatkowe narzędzia składni oferowane przez OPT, aby liczbę szablonów zmniejszyć - wprawdzie osobnych plików dla modułów raczej się nie pozbędziemy, ale nie ma większych trudności z wyeliminowaniem szablonów z kodem oddzielającym od nich treść strony.

Mając do wyświetlenia grupę modułów, nasuwa się dość oczywisty wniosek, żeby obsługę modułów zrealizować za pomocą sekcji. Wewnątrz jej elementów przekazywana będzie nazwa szablonu do wykonania oraz unikalne dane dla modułu.

<div id="modulesleft">
<opt:section name="modulesleft">
<div class="module">
<opt:include file="$modulesleft.filename"/>
</div>
</opt:section>
</div>

Może tu rodzić się pytanie, co z sekcjami wewnątrz modułu - czy nie będą one kolidować z sekcją główną tak, że będziemy musieli kombinować, jak rozwiązać problem zagnieżdżonych sekcji? Okazuje się, że nie. Pojęcie "sekcji zagnieżdżonych" istnieje tylko na etapie kompilacji szablonu, ponieważ wtedy kompilator widzi istniejące zależności. Jeśli korzystamy z include, plik, jaki za jego pomocą ładujemy, jest formalnie w trakcie kompilacji głównego szablonu niezdefiniowany. Szablony modułów kompilowane są znacznie później, już podczas jego wykonywania, w związku z czym powiązanie między naszą sekcją główną, a sekcjami modułów nie istnieje - funkcjonują one niezależnie od siebie i nie są w żaden sposób powiązane. Oznacza to jednocześnie, że wewnątrz szablonu dołączonego przez include, nie widać danych niesionych w sekcji "modulesleft", aczkolwiek istnieje możliwość przekazania "parametrów".

<div id="modulesleft">
<opt:section name="modulesleft">
<div class="module">
<opt:include file="$modulesleft.filename" data="$modulesleft.data"/>
</div>
</opt:section>
</div>

Teraz w dołączanym szablonie dostępna będzie zmienna (zmienna, nie blok!) @data dająca dostęp do zawartości pola sekcji $modulesleft.data.

Podsumowując, zaprojektowanie mechanizmu przekazywania danych z modułu do systemu szablonów wymaga przewidzenia kilku czynników i poruszanie się tu na oślep może spowodować więcej złego, niż dobrego. Na szczęście OPT posiada odpowiednie środki do zrealizowania naszych planów, musimy być jedynie świadomi ich istnienia oraz pewnych specyficznych właściwości związanych ze sposobem działania samego parsera.

Chciałbym zaprezentować jeszcze jedną metodę rozwiązania modułów, tym razem z użyciem komponentów. Chociaż dokumentacja promuje je jako doskonałe narzędzie do projektowania formularzy, możliwych zastosowań są dziesiątki. Trzeba tylko uruchomić wyobraźnię. Szczegółowy poradnik opisujący tworzenie komponentów znajduje się w dokumentacji lub w artykule "Wprowadzenie do OPT cz. 2". Tutaj przyjmiemy, że każdy moduł, jaki może być uruchomiony na stronie, jest klasą implementującą interfejs ioptComponent. Zawiera on dwie metody: begin() oraz end(), które muszą wygenerować kod HTML komponentu. Proste komponenty formularzy korzystały tam z prostego echo do wyświetlenia kodu HTML odpowiednich kontrolek. My pójdziemy krok dalej i wewnątrz jednej z nich odpalimy... cały szablon. Zaskoczenie? Dlaczego? Przecież na dobrą sprawę robimy tu to samo, co instrukcja include, tyle że za pomocą programowalnych komponentów. Poniżej wymieniona jest lista spraw, o których musimy pamiętać przy korzystaniu w ten sposób z komponentów:

  1. Każdy komponent można statycznie osadzić w szablonie lub utworzyć samodzielnie jego obiekt w skrypcie i przekazać go metodą assign(). To samo będzie dotyczyć wykonanych tym sposobem modułów.
  2. Istnieje spory arsenał środków umożliwiających przekazanie do komponentu różnych parametrów: poczynając od znaczników param oraz listItem, na parametrze datasource skończywszy. Jako że komponent napisany jest w PHP, może też samodzielnie pobierać sobie dane np. z bazy danych.
  3. Metody begin i end wykonywane są w momencie przetwarzania szablonu. Dlatego jeśli umieścimy w nich dodatkowo całą logikę przetwarzania danych, musimy być świadomi, że część kodu HTML poleciała już w tym momencie do internauty, co nieco zawęża nam pole manewru. Alternatywą może być przeniesienie logiki do konstruktora i pozostawienie w ww. metodach tylko obsługi szablonów.
  4. Komponenty posiadają mechanizm zdarzeń. Moduły mogą zrobić z niego jakiś ciekawy użytek.

Aby można było namacalnie zobaczyć całość rozwiązania w akcji, poniżej prezentowany jest w pełni kompletny i gotowy do uruchomienia kod. Zaczniemy od pliku PHP, w którym najpierw utworzymy sobie jeden przykładowy moduł w oparciu o interfejs komponentów:

<?php
 
define('OPT_DIR', './opt/');
require(OPT_DIR.'opt.class.php');
 
 
class dateModule implements ioptComponent
{
private $dateFormat = 'd.m.Y, H:i';
private $tpl;
 
public function __construct($name = '')
{
} // end __construct();
 
public function begin()
{
$this -> tpl -> assign('date', date($this->dateFormat));
$this -> tpl -> parse('dateModule.tpl');
} // end begin();
 
public function end()
{
} // end end();
 
public function setOptInstance(optClass $tpl)
{
$this -> tpl = $tpl;
} // end setOptInstance();
 
public function set($name, $value)
{
if($name == 'dateFormat')
{
$this -> dateFormat = $value;
}
} // end set();
 
public function push($name, $value, $selected = false)
{
} // end push();
 
public function setDatasource(&$datasource)
{
} // end setDatasource();
}

Moduł ten wyświetla po prostu na stronie aktualną datę i nic więcej. Dodatkowo możliwe jest zdefiniowanie własnego formatu daty. Zwróćmy uwagę na metodę begin(), w której dokonujemy odpalenia szablonu w celu wygenerowania kodu HTML komponentu oraz przekazania doń różnych danych. Metoda setOptInstance() odbiera aktualnie używany obiekt parsera, z którego moduł-komponent powinien korzystać. Dalsza część skryptu to tradycyjna inicjacja OPT oraz stworzenie tablicy z modułami dla sekcji umieszczonej wewnątrz szablonu:

	try
{
$tpl = new optClass;
$tpl -> root = './templates/';
$tpl -> compile = './templates_c/';
$tpl -> xmlsyntaxMode = true;
$tpl -> registerComponent('dateModule');
$tpl -> httpHeaders(OPT_HTML);
 
$modules[0] = array(
'component' => new dateModule
);
 
$modules[1] = array(
'component' => new dateModule
);
 
$tpl -> assign('modules', $modules);
$tpl -> parse('website.tpl');
}
catch(optException $exception)
{
optErrorHandler($exception);
}
?>

Przypominam o konieczności zarejestrowania nowego komponentu w parserze, gdyż inaczej nie będziemy mogli go osadzić statycznie wewnątrz szablonu.

Oto szablon modułu dateModule.tpl:

<p>Dzisiaj jest {$date}</p>

A oto plik website.tpl:

<html>
<head>
<title>Moduly w OPT</title>
<style type="text/css">{literal}
#modules{
border: 1px #dddddd solid;
width: 168px;
padding: 2px;
}

.m{
border: 1px #aaaaaa solid;
width: 166px;
}
{/literal}</style>
</head>
<body>

<div id="modules">
<opt:section name="modules">
<div class="m">
<opt:component id="$modules.component"></opt:component>
</div>
</opt:section>
</div>

<h3>Ponizej osadzamy modul na stronie w sposob statyczny:</h3>
<opt:dateModule>
<opt:param name="dateFormat" value="`d M Y`"/>
</opt:dateModule>
</body>
</html>

I gotowe - wewnątrz sekcji modułów stworzyliśmy miejsce, w którym odpalane mają być komponenty, a dodatkowo szablon pokazuje, w jaki sposób statycznie osadzić jakiś moduł w kodzie HTML. Dalsze rozwinięcie zaprezentowanego tu kodu pozostawiam czytelnikowi.

Sztuka szablonowania

"Szablony to jeszcze jeden język programowania. Skoro poza składnią nie ma różnicy między zrobieniem instrukcji warunkowej w szablonie, a w PHP, to jaki jest sens korzystania z nich?" - to jeden z koronnych argumentów przeciwników wykorzystywania systemów szablonów. Takie podejście prawdopodobnie wynika z metodyki forsowanej przez popularny parser Smarty, faktycznie stawiający w dokumentacji i proponowanych przykładach na swoje programistyczne właściwości. Jednak nawet Smarty jest biblioteką, która da się oraz można rozbudowywać o nowe funkcje, bardziej odpowiadające naszym potrzebom i pozwalające wyrugować wiele programistycznych konstrukcji z szablonów. Wykorzystując każdą bibliotekę, nie możemy ograniczać się do propozycji oferowanych nam przez jego twórców, ponieważ siłą rzeczy muszą być one dość ogólne i dopasowane do jak największej liczby możliwych sytuacji.

Mimo wszystko udostępnianie nowej składni przez systemy szablonów nadal nie jest w pełni uzasadnione. Czy rzeczywiście programista oraz webmaster mają jakieś korzyści z tego, że piszą zamiast foreach($costam as $id => $value)? Odpowiedź brzmi: tak. Zostawmy w spokoju Smarty i skupmy się na OPT. Jego mechanizm kompilacji wzorowany jest na rozwiązaniach stosowanych w języku XML. W trakcie przetwarzania szablonu na kod PHP, tworzone jest ogromne drzewo a'la XML, którym można dowolnie manipulować. Instrukcje mogą zadecydować np. o nieprzetwarzaniu znajdującej się wewnątrz nich treści, obsłużeniu tylko wybranych znaczników lub nawet przeniesieniu fragmentu drzewa w zupełnie inne miejsce. Stwarza to znacznie większe możliwości manipulacji danymi - zastanów się sam. Czy kod PHP jest w stanie przenieść w trakcie wykonywania część samego siebie w inne miejsce? Nawet jeśli opakujemy wszystko w funkcje czy klasy, czytelność i prostota takiego rozwiązania pozostawia wiele do życzenia. Kompilacja to także okazja do poczynienia szeregu optymalizacji oraz utworzenie dodatkowej warstwy abstrakcji gwarantującej większą przenośność. Przyjrzyjmy się dostępnym w OPT sekcjom:

<ul>
<opt:section name="kategorie">
<li>{$kategorie.tytul} <ul>
<opt:section name="produkty">
<li>{$produkty.tytul}</li>
</opt:section>
</ul></li>
</opt:section>
</ul>

Na przykładzie mamy dwie zagnieżdżone sekcje wyświetlające listę kategorii i powiązanych z nimi produktów. W szablonie wskazujemy tylko: co ma się powtarzać i jak się to nazywa. Tymczasem skrypt PHP ma do wyboru aż cztery różne formaty, w jakich przekazane zostaną do takiej sekcji dane:

  1. Sposób standardowy - osobna tablica dla każdej z sekcji, z uwzględnieniem poziomu zagłębienia każdej z nich.
  2. Sposób alternatywny - pojedyncza mega-tablica dla sekcji głównej oraz dla wszystkich sekcji zagnieżdżonych.
  3. Sekcje dynamiczne prekompilowane (od OPT 1.1.0) - do szablonu przekazujemy tylko funkcję, która generuje dane dla sekcji w momencie jej wywołania. Obsługa tej funkcji jest na sztywno wkompilowana w szablon tylko dla tych sekcji, które z tego korzystają.
  4. Sekcje dynamiczne runtime (od OPT 1.1.0) - do tej samej sekcji możemy przekazać albo gotową tablicę z danymi, albo funkcję do automatycznego wygenerowania takowej.

Pisząc szablon, w ogóle nie trzeba przejmować się mechaniką silnika strony, formatami danych itd. W jednym miejscu powyższy kod może korzystać z kilku tablic, a skopiowany w inne miejsce, bez żadnych zmian może zacząć pracować jako sekcja dynamiczna. O wszystko dba kompilator, odpowiednio manipulując drzewem XML-owym oraz generując stosowny do danej sytuacji kod PHP. Przejrzyj jeszcze raz artykuł - zaprezentowaliśmy tutaj mechanizm dynamicznego składania strony z modułów oraz porady, jak zarządzać dynamicznymi formularzami. Nigdzie nie wyszliśmy poza sekcje oraz komponenty (ewentualnie instrukcję include). Z tych dwóch, trzech podstawowych klocków można uzyskać dziesiątki różnych zastosowań ograniczonych tylko fantazją i zdolnościami programisty. A przecież OPT to także automatyczne buforowanie wejścia, kompresja gZip, obsługa nagłówków HTTP wystarczająca do większości zastosowań, cache'owanie wyników działania, wszystko w pełni zunifikowane i dopasowane do siebie. W ostateczności, jeśli komuś nie pasuje taka warstwa abstrakcji, zawsze może na początku i końcu każdego szablonu umieścić znacznik {php} i już może tworzyć szablon bezpośrednio w kodzie PHP.

Do rozprawienia pozostał jeszcze jeden mit, mianowicie wydajność. Wśród części programistów pokutuje przekonanie, że im więcej opcji ma system szablonów, tym jest on powolniejszy. Jest to błędne rozumowanie. Widziałem już wiele niewielkich systemów, które Smarty i OPT rozkładały na łopatki przy pierwszej praktyczniejszej próbie ich wykorzystania. Tak naprawdę siła leży w odpowiednio skonstruowanym mechaniźmie przetwarzania oraz umiejętnym decydowaniu, które fragmenty kodu są aktualnie potrzebne, a które nie. Oba wymienione tu systemy wcale nie zaprzeczają powiedzeniu "najlepszym systemem szablonów jest samo PHP", ponieważ w rzeczywistości wszystkie szablony kompilowane są raz, a dobrze, do postaci kodu PHP i przy następnych żądaniach HTTP wykonywany jest jedynie zwykły include. Kompilator oraz większość kodu obu bibliotek jest ładowana tylko na specjalne okazje. Dla uwidocznienia tego faktu podam, że podczas normalnej pracy w pamięci rezydują tylko pliki opt.class.php oraz opt.functions.php (i ewentualnie opt.components.php, jeśli się ich nie usunęło za pomocą OPT Toolsetu). OPT mógłby mieć wobec tego nawet i tysiąc różnych instrukcji zaprogramowanych w pliku opt.instructions.php, których załadowanie faktycznie spowalniałoby samą kompilację, ale już później takowe co najwyżej okupowałyby tylko miejsce na twardym dysku. Podsumowując: Smarty i OPT wyraźnie udowadniają, że ogromne możliwości wcale nie muszą iść w parze z powolnością. Nie liczby się bowiem ilość kodu, tylko jego jakość.

Zakończenie

Każde narzędzie, obojętnie jak dobrze wyposażone, nie przyda się nam na wiele, jeśli nie będziemy potrafili z niego korzystać. Artykuł ten powstał, aby odpowiedzieć na zapotrzebowanie i pokazać praktyczne użycie różnych elementów systemu szablonów Open Power Template do realizacji praktycznych zadań. Mam nadzieję, że wszyscy znaleźli w nim coś dla siebie. Ostatni z rozdziałów był swoistym podsumowaniem, które objaśniało filozofię stojącą za opracowaną przeze mnie metodyką korzystania nie tylko z OPT, ale systemów szablonów w ogóle. Pytania i uwagi proszę kierować na oficjalne forum projektu znajdujące się pod adresem http://openpb.net/forum. Samo OPT można pobrać ze strony http://opt.openpb.net.

Autor: Tomasz "Zyx" Jędrzejewski, www.zyxist.com

 

Źródło: http://artykuly.zyxist.com/czytaj.php/opt_w_praktyce

Licencja: Creative Commons - użycie niekomercyjne - bez utworów zależnych