Projektując aplikację wykorzystującą strukturę drzewiastą o nieograniczonej liczbie zagłębień prędzej, czy później będziemy musieli zmierzyć się z rekurencją.
Pisanie funkcji rekurencyjnych w PHP nie jest rzeczą trudną, do chwili, gdy zapragniemy skorzystać z szablonów Smarty.
Twórcy pakietu Smarty - Monte Ort oraz Andrei Zmievski nie uwzględnili potrzeby stosowania rekurencji w swoich szablonach, ale będąc świadomymi ograniczeń swojego produktu pozostawili projektantom furtkę w postaci znaczników {php}{/php}, pomiędzy którymi można zamieścić dowolny kod PHP. Co jednak zrobić jeśli projektując aplikację przewidujemy możliwość edytowania szablonów przez przypadkowego użytkownika - tak jak ma to miejsce w blogach internetowych? Przede wszystkim ze względu bezpieczeństwa musimy zablokować możliwość korzystania w Smarty z bloków php i poszukać innego sposobu wyświetlenia drzewa w szablonie.
Rozwiązaniem od razu nasuwającym się na myśl jest zapisanie wygenerowanego kodu html w zmiennej i włączenie jej w takiej postaci do obiektu Smarty.
<?php $smarty->assign( 'full_tree', $tree ); ?>
Generowanie kodu html poza szablonami burzy logikę wzorca MVC i jest sprzeczne z samą ich ideą. Szablony powstały głównie po to, aby wyraźnie oddzielić część logiczną aplikacji od części wizualnej. Ponadto jeśli użytkownik ma jedynie dostęp do szablonów to stosując takie rozwiązanie odbiera mu się możliwość wpływania na sposób prezentowania drzewa.
Na szczęście, jak się nieoczekiwanie okazuje istnieje w Smarty składnia umożliwiająca zastosowanie rekurencji - choć wcale nie została do tego wymyślona.
Dla przykładu mamy tablicę:
$tree = array('element'=>array(array('name' => 'test1', 'element' => array(array('name' => 'test1.1'), array('name' => 'test1.2', 'element' => array(array('name' => 'test1.2.1'), array('name' => 'test1.2.2') ) ), array('name' => 'test1.3', 'element' => array(array('name' => 'test1.3.1') ) ) ) ) ) );
Włączamy ją do obiektu smarty:
<?php $smarty->assign('tree', $tree); ?>
I wyświetlamy szablon:
<?php $smarty->display('tree.tpl'); ?>
tree.tpl:
{foreach from=$tree.element item=element} <li>{$element.name}</li> {if $element.element} <ul>{include file="tree_recursion.tpl" element=$element.element}</ul> {/if} {/foreach}
Jak widać w szablonie tree.tpl - po spełnieniu określonego warunku - includowany jest kolejny szablon tree_recursion.tpl, który z kolei includuje samego siebie:
tree_recursion.tpl:
{foreach from=$element item=element} <li>{$element.name}</li> {if $element.element} <ul>{include file="tree_recursion.tpl" element=$element.element}</ul> {/if} {/foreach}
Wadą tego rozwiązania jest niestety spadek wydajności w stosunku do czystego kodu php.
Istnieje również inny, bardziej elegancki sposób rozwiązania trapiącego nas problemu. Pakiet Smarty daje możliwość zaawansowanym użytkownikom stworzenia własnych funkcji i filtrów i zarejestrowania ich w obiekcie smarty. Z mechanizmu tego korzysta miedzy innymi plugin compiler.defun, który służy do rekurencyjnego przetwarzania tablicy. Po pobraniu pliku compiler.defun.php wystarczy go umieścić w katalogu plugins znajdującym się w głównym katalogu pakietu smarty i już możemy korzystać z nowego bloku {defun} oraz znacznika {fun}. Możemy też plugin umieścić w dowolnym innym katalogu i poinformować o tej lokalizacji obiekt Smarty.
mySmarty:
<?php define('MYSMARTY_DIR',dirname(__FILE__)); include(MYSMARTY_DIR.'/../smarty/Smarty.class.php'); class mySmarty extends Smarty { public function __construct($templates_dir){ $this->Smarty(); $this->template_dir = $templates_dir; $this->compile_dir = MYSMARTY_DIR.'/templates_c/'; $this->config_dir = MYSMARTY_DIR.'/configs/'; $this->cache_dir = MYSMARTY_DIR.'/cache/'; // Wylancza m.in. mozliwosc uzycia blokow {php}{/php} $this->security = true; // W tablicy przechowywane sa lokalizacje katalogow z pluginami do Smarty // SMARTY_DIR.'/plugins/' – domyslny katalog pluginow Smarty // MYSMARTY_DIR.'/plugins/' – moj catalog pluginow do Smarty $this->plugins_dir = array(SMARTY_DIR.'/plugins/', MYSMARTY_DIR.'/plugins/'); } } $smarty = new mySmarty(MYSMARTY_DIR.'/templates/'); $smarty->assign('tree', $tree); $smarty->display('tree_defun.tpl'); ?>
tree_defun.tpl:
<ul> {defun name="testrecursion" list=$tree.element} {foreach from=$list item=element} <li>{$element.name}</li> {if $element.element} <ul> {fun name="testrecursion" list=$element.element} </ul> {/if} {/foreach} {/defun} </ul>
Zaletą korzystania z pluginu w stosunku do pierwszego rozwiązania jest lepsza wydajność oraz pojedynczy plik szablonu (a co za tym idzie mniejsza ilość kodu Smarty).
dla rozwiazania problemu drzewek n-poziomowy polecam ten artykul http://www.phpriot.com/d/articles...ion-design/nested-trees-2/index.html
naprawde dobre rozwiazanie, rekurencja jest tylko prz przeliczaniu drzewka (po jego modyfikacji) potem pobieramy juz ulozona strukture, dodam ze z dowolnego poziomu
I tak nie unikniemy skladni {php}{/php} w przypadku gdy
chce walodowac dane ktore sa w drzewie. Oczywiscie mozna zadbac o walidacje juz przy samej budowie drzewa, ale to nie rozwiazuje problemu. Smarty nie nadaje sie do rekurencyjnych danych, napewno nie na wszelkiego rodzaju grup-listy. Mozna w inny sposob trzymac sie MVC bez szablonu.
Temat jak się okazało (przynajmniej dla mnie) nie jest do końca taki prosty. Tutaj topic na forum: http://forum.php.pl/Drzewka-raz-jeszcze-t49772.html
hm, moze jestem leszcz ale jak zbudowac taka tablice majac do dyspozycji tabele mySql gdzie jest id, id_parent i nazwa