Witaj, Gościu O nas | Kontakt | Mapa
Wortal Forum PHPEdia.pl Planeta Kubek IRC Przetestuj się!

Wprowadzenie do OPT cz. 1

Wielopoziomowe listy

OPT zezwala na zagnieżdżanie sekcji, co umożliwia tworzenie wielopoziomowych list. Od strony szablonu sprawa jest oczywista - umieszczamy jedną sekcję w drugiej i nie martwimy się o resztę. Parę ważnych decyzji czeka nas natomiast od strony kodu PHP. Musimy zdecydować się na to, w jakim formacie dostarczymy dane do sekcji zagnieżdżonych. Mamy dwa wyjścia:

  • Wiele sekcji - wiele tablic - domyślne ustawienie OPT. Każda zagnieżdżona sekcja to osobna tablica, osobno przypisywana metodą assign(). Jedyna różnica to większa liczba indeksów pozwalająca na skojarzenie elementów podrzędnych z nadrzędnymi. Przykład: jeśli kategoria 5 znajduje się w $kategorie[5], to czwarty produkt leżący w tej kategorii znajdzie się w $produkty[5][4].
  • Wiele sekcji - jedna tablica - dane sekcji podrzędnych trafiają do tablicy sekcji głównej. Posługując się przykładem z kategoriami i produktami, naszą kategorię 5 będziemy mieli po dawnemu w $kategorie[5] (zauważ, że w kwestii obsługi sekcji pojedynczych oba sposoby niczym się nie różnią!), natomiast produkt 4 będzie już w $kategorie[5]['produkty'][4].

Wyboru najlepiej dokonać przed rozpoczęciem pisania aplikacji, choć warto też dodać, że gdy już się zdecydujemy, wcale nie jesteśmy skazani do samego końca na korzystanie z niego. Zawsze istnieje możliwość skorzystania z drugiego wariantu, lecz wymaga to poczynienia pewnych zmian po stronie szablonu.

Ale przejdźmy do rzeczy. Do demonstracji zagadnienia wykorzystamy tym razem tabele SQL dołączone do przykładów OPT. Odpowiedni plik znajduje się w katalogu /examples/samples.sql i jest dołączony do każdego wydania biblioteki. Po jego wgraniu pojawią nam się tabelki "products" oraz "categories" połączone prostą relacją jeden-do-wielu. Poniżej podam teraz szablon z zagnieżdżonymi sekcjami, który umożliwi nam wyświetlenie naszych dwóch tabelek. Następnie zaprezentowane zostaną dwa wykorzystujące go skrypty demonstrujące użycie każdego z formatów danych. Oczywiście nie będziemy w nich wykonywać zapytań rekurencyjnych; i tak nam nie pozwoli na to biblioteka PDO użyta do komunikacji z bazą. Z tego powodu trzeba jeszcze zadbać o mechanizm pamiętający, jaki ID kategorii powiązany jest z jakim indeksem tablicy $kategorie; nie możemy w tym celu wykorzystać tego samego ID, ponieważ OPT wymaga ciągłej numeracji zaczynającej się od 0, podczas gdy identyfikatory bazy danych nie spełniają ani jednego, ani drugiego warunku.

<html>
<head>
  <title>Open Power Template: lista produktów</title>
</head>
<body>
	<h3>Moja lista produktów</h3>
	
	<ul>
	{section=kategoria}
	<li><i>{$kategoria.nazwa}</i><br/>
	<table width="60%" border="1">
	 <tr>
	  <td width="30"><b>#</b></td>
	  <td width="20%"><b>Nazwa</b></td>
	  <td width="*"><b>Opis</b></td> 
	 </tr>
	 {section=produkt}
	 <tr>
	  <td width="30">{$produkt.id}</td>
	  <td width="20%">{$produkt.nazwa}</td>
	  <td width="*">{$produkt.opis}</td>
	 </tr>
	 {/section}
	</table>
	</li>
	{/section}
	</ul>
</body>
</html>

Tak jak mówiłem, szablon nie jest szczytem wyrafinowania. Przyjrzyjmy się zatem pierwszemu skryptowi, który potrafi generować dane na jego użytek.

<?php
	define('OPT_DIR', './lib/');
	require('./lib/opt.class.php');
 
	try
	{	
		$tpl = new optClass;
		$tpl -> root = './templates/';
		$tpl -> compile = './templates_c/';
		$tpl -> gzipCompression = true;
		$tpl -> httpHeaders(OPT_HTML);
		
		$pdo = new PDO('mysql:host=localhost;port=3305;dbname=test',
			 'root', 'root');
 
		$stmt = $pdo -> query('SELECT id, name FROM
			categories ORDER BY id');
		$categories = array();
		$categoryMatch = array();
		$i = 0;
		while($row = $stmt -> fetch())
		{
			$categories[$i] = array(
				'id' => $row['id'],
				'nazwa' => $row['name']			
			);
			$categoryMatch[$row['id']] = $i; // 1
			$i++;		
		}
		$stmt -> closeCursor();
		unset($stmt);
 
		$stmt = $pdo -> query('SELECT id, name, description,
			category FROM products ORDER BY category, id');
		$products = array();
		
		while($row = $stmt -> fetch())
		{
			$products[$categoryMatch[$row['category']]][] = array( // 2
				'id' => $row['id'],
				'nazwa' => $row['name'],
				'opis' => $row['description']
			);
		}
		$stmt -> closeCursor();
		
		$tpl -> assign('kategoria', $categories); // 3
		$tpl -> assign('produkt', $products);
		
		$tpl -> parse('szablon5.tpl'); 
	}
	catch(optException $exception)
	{ 
		optErrorHandler($exception); 
	}
?>

Oto opis jego działania:

  1. $categoryMatch jest tablicą pamiętającą, jaki ID kategorii znajduje się w którym elemencie listy. Będziemy ją potem wykorzystywać, aby połączyć produkty z kategoriami.
  2. Tutaj musimy skonstruować nieco większą tablicę produktów uwzględniającą fakt, iż te są zagnieżdżone wewnątrz kategorii. Posiada ona dwa indeksy - pierwszy pokazuje, do jakiej kategorii należy produkt (tu wstawiamy wynik z tablicy $categoryMatch), a drugi jest indeksem produktu w obrębie tej kategorii.
  3. Do szablonu przekazujemy dwie tablice, osobno dla każdej z sekcji.

Jak wspomniałem, jest to domyślny sposób przekazywania danych do sekcji zagnieżdżonych, lecz OPT posiada jeszcze jeden. Należy go ręcznie włączyć, ustawiając dyrektywę sectionStructure na OPT_SECTION_SINGLE. Wtedy zamiast dwóch tablic, będziemy generowali tylko jedną - dla sekcji nadrzędnej. Dane sekcji podrzędnych zostaną w niej umieszczone jako poszczególne bloki. Od strony szablonu nic się nie zmienia. Od strony PHP, musimy zmienić trzy linijki powyższego skryptu. Na początek dodaj w części inicjacyjnej informację, że używamy innej struktury sekcji:

$tpl -> sectionStructure = OPT_SECTION_SINGLE;

Następnie w pobieraniu produktów zamień linijkę:

$products[$categoryMatch[$row['category']]][] = array(

na:

$categories[$categoryMatch[$row['category']]]['produkt'][] = array(

Zauważ, że teraz w obrębie kategorii tworzymy blok "produkt", lecz zamiast tekstu czy liczby, umieszczamy w nim dane dla sekcji produkt powiązane z aktualnie przetwarzaną kategorią.

Ponieważ nie tworzymy już tabeli $products, poniższa linijka jest nam zbędna i należy ją usunąć:

$tpl -> assign('produkt', $products);

Usuń z /templates_c skompilowaną wersję dotychczasowego szablonu i odpal zmodyfikowany skrypt. To wszystko, właśnie poznałeś dwie metody tworzenia zagnieżdżonych sekcji. Pamiętaj, że powinieneś na początku prac zdecydować, którego z nich będziesz używać, jednak jeżeli nagle przyjdzie Ci użyć drugiego, nie panikuj - wprawdzie wymaga to pewnych modyfikacji po stronie szablonów, ale jest wykonalne. Sekcje posiadają dodatkowy parametr, datasource, za pomocą których możesz powiadomić kompilator, gdzie dokładnie znajdują się dane dla nich. I tak: jeżeli korzystasz z pierwszego sposobu, a potrzebny Ci jest drugi, powinieneś połączyć sekcję produktów z kategoriami w następujący sposób:

{section name="produkt" datasource="$kategoria.produkt"}

W przeciwną stronę, musimy zrobić tak:

{section name="produkt" datasource="$produkt"}

Osobną sprawą jest wyświetlanie zawartości drzew z użyciem OPT. W wersjach 1.0.x wymaga to sporego nakładu pracy i wyposażenia sekcji w cały zestaw instrukcji warunkowych, lecz podcząwszy od wersji 1.1.x biblioteka ma już posiadać specjalne instrukcje realizujące to zadanie schludnie i elegancko. Pokażę zatem tutaj sposób uzyskania drzewka dla wersji 1.0.x. Zakładam, że generujesz je algorytmem "modified preorder tree traversal", który generuje liniową listę elementów z dołączonym do nich parametrem depth określającym ich głębokość. Zanim prześlemy takie drzewo do OPT, musimy przepuścić je przez dodatkową funkcję:

<?php
	function prepareTree($tree)
	{
		foreach($tree as $id => &$item)
		{
			if($item['depth'] == @$tree[$id+1]['depth'])
			{
				$item['leaf'] = 1;
			}
			if($item['depth'] < @$tree[$id+1]['depth'])
			{
				$item['opening'] = 1;
			}
			if($item['depth'] > @$tree[$id+1]['depth'])
			{
				$item['closing'] = 1;
				if(isset($tree[$id+1]))
				{
					$item['toclose'] = ($item['depth']
						- @$tree[$id+1]['depth']);
				}
			}
		}
		return $tree;
	} // end prepareTree();
?>

Umieści ona w każdym elemencie drzewa następujące bloki:

  1. leaf - podany element jest liściem drzewa.
  2. opening - po aktualnym elemencie należy otworzyć nową listę dla jego dzieci.
  3. closing - dany element jest ostatnim na danym poziomie.
  4. toclose - umieszczany razem z closing. Informuje, ile list należy teraz zamknąć, aby powrócić do poziomu następnego elementu na liście.

Teraz przechodzimy do strony szablonu. Tutaj będziemy musieli pobawić się nieco instrukcjami warunkowymi i pętlami, aby algorytm zadziałał prawidłowo. Oto i on:

<ol>
	{section=tree}
		{if $tree.leaf} {* 1 *}
		<li>{$tree.title}</li>
		{/if}
		{if $tree.opening} {* 2 *}
		<li>{$tree.title}<ol>
		{/if}
		{if $tree.closing} {* 3 *}
		<li>{$tree.title}</a>
			{for=@i is 0; @i < $tree.toclose; @i is @i+1} {* 4 *}
			</li></ol>
			{/for}
		</li>
		{/if}
	{/section}
<ol>

Znaczenie poszczególnych fragmentów:

  1. Wygląd liścia drzewa.
  2. Wygląd elementu otwierającego nowy poziom listy.
  3. Wygląd elementu zamykającego aktualny poziom listy.
  4. Pętla zamykająca listy do nowego poziomu.

Teraz jesteśmy już w stanie wyświetlać struktury drzewiaste za pomocą Open Power Template.

Informacje na podobny temat:
Wasze opinie
Wszystkie opinie użytkowników: (11)
Dużej różnicy w stosunku do Smarty nie widać
Sobota 16 Kwiecień 2011 12:28:13 am - neosatan <pawel.kuznik_at_gmail.com>

Niestety, z wielkim rozczarowaniem doczytałem do końca arta. OPT był mi zachwalany jako genialna alternatywa dla SMARTY, lecz przynajmniej po tym artykule muszę stwierdzić, że OPT nie rozwiązuje problemów, które są w SMARTY (lub nie zostały takowe wspomniane). Moje wrażenie jest mniej więcej takie, że OPT jest bardzo podobne do SMARTY. Twórcy OPT zamiast tworzyć nowy, bardzo podobny silnik szablonów, mogli zaproponować współpracę twórcą SMARTY, lub wydanie alternatywnej wersji.
Muszę powiedzieć, że SMARTY (przynajmniej na razie) góruje nad OPT tym, że jest akceptowany za granicą, co jest plusem dla programistów pracujących również z zagranicznymi firmami.
Tak, czy owak artykuł skłonił mnie do bliższego przyjrzenia się OPT.

OPT
Wtorek 30 Czerwiec 2009 4:19:12 pm - pearl1985

Zrobiłem sobie dzisiaj porównanie szablonów między smarty a opt i szczerze powiem, że o ile obydwa systemy szablonów generują pliki php i je później includują do zdobycia danych do wyjścia, to smarty robi to znacznie szybciej (<!-- Skrypt wykonał się w 0.0013339519500732 sekund --> dla smarty, <!-- Skrypt wykonał się w 0.032589912414551 sekund --> dla opt). Reasumując jaki z tego wniosek? Moim zdaniem lepiej napisać prostą klasę, która będzie składowała dane i potem robiła include już napisanego przez nas szablonu w php. A w tym php odpowiednie odwoływanie się do zmiennych przechowywanych przez klasę np. template.

ciekawe
Sobota 19 Styczeń 2008 2:02:05 pm - potreb

Jestem pod wrażeniem, jakby nie było jest to dość wielka alternatywa dla smarty

Ankieta
Czwartek 18 Październik 2007 6:59:19 pm - grzesk98 <grzegorz.kowalewski_at_gmail.com>

Jeśli moglibyście wypełnić ankietę na temat systemów szablonów:

http://ankiety.boby.pl/index.php?module=polls&action=fillup&poll_id=74

z góry dzięki.

OPT
Poniedziałek 08 Październik 2007 2:28:35 pm - Diabl0

Czy OPT w pełni obsługuje obiekty w szablonach? Chodzi mi o kostrukcje takie jak:
{$zmienna->metoda1()->metoda2();}

Smarty sobie niestety nie radzi z takim czymś a znacznie ułatwiło by mi to pracę.

Brak porównań do Smarty
Niedziela 01 Lipiec 2007 11:10:05 pm - Diabl0

Wszystko bardzo fajnie i ładnie, ale brakuje mi w treści porównań względem Smarty'ego. Jak wiadomo przyzwyczajenie jest silne i dla kogoś kto już używa (używał) Smarty i szuka czegoś innego/lepszego takie porównania i wyjaśnienia były by bardzo wygodne.
Przy porównywaniu chętnie bym także zobaczył informację o wydajności - Smarty jest fajne i baaardzo elastyczne ale z wydajnością nie jest wesoło i informacje o wydajności OPT w stosunku do Smarty mogły by sporo osób skusić do sprawdzenia tego w praktyce.

ee tam
Czwartek 10 Maj 2007 3:30:38 am - yacho

i tak odchodzi sie od znacznikow w szablonach na rzecz plain php templates - nie oszukujmy sie - kazdy szablon ma jakas logike prezentacji i nie ma co sie oszukiwac ze kiedys maksymalnie to sie uprosci - co jest wazne to separacja i enkapsulacja warstw - a sama skladnia juz nie tak mocno.

Odp dla sh4dow
Piątek 16 Marzec 2007 7:57:59 pm - slump <slump_ilawa_at_wp.pl>

sh4dow jeśli oczekujesz odp na te pytania napisz do zyxa lub skorzystaj z openpb.net/forum :) celowo nie daję jako link aby nie było za dużego spamu w tym komentarzu.

Pytanie
Czwartek 01 Marzec 2007 9:34:32 am - sh4dow

Tak sie zastanawiam, jak mozna krytykowac sposob nazewnictwa metod/funkcji. No w sumie i dlaczego camel style ma byc "eleganckim" nazewnictwem ? Wiem czepiam sie ale tak jakos nie daje mi to spokoju.
A co do wsparcia wielojęzykowego to tutaj bym mocno polemizowal i zastanawial sie raczej jak mozna by to raczej poprawic. Bo sorki ale jesli ja mam baze gdzie jest okolo 5000 kluczy i do tego 6 jezykow to nie chcial bym widziec jak te pliki i tablice z tlumaczeniami by wygladaly.

koment
Środa 28 Luty 2007 1:40:27 pm - mrm

dobry art

koment
Środa 28 Luty 2007 1:39:19 pm - mrm

bardzo fajny artykul

Mentax.pl    NQ.pl- serwery z dodatkiem świętego spokoju...   
O nas | Kontakt | Mapa serwisu
Copyright (c) 2003-2024 php.pl    Wszystkie prawa zastrzeżone    Powered by eZ publish Content Management System eZ publish Content Management System