Šablonovací systém Smarty
Transkript
Šablonovací systém Smarty
Šablonovací systém Smarty Josef Toman 31. března 2008 1 Úvod do problematiky Asi každý, kdo někdy vytvářel nějaký větší web s netriviálním zapojením php, dospěl do stavu, kdy byl jeho kód velmi nepřehledný a těžko čitelný. Bloky html značek se střídají s kusy php kódu, všude samé echo. Moudřejší programátoři v takové chvíli začínají používat postupy, které alespoň částečně zlepší situaci. Vzniká řada funkcí, které generují tu či onu část stránky, web se modularizuje. Výsledek, který se nakonec dostane do browseru, je tak rozmělněn v bezpočtu souborů a funkcí. Pokud v tuto chvíli přijde k projektu designér, aby dal webu novou tvář, nemá šanci na úspěch. Šikovní grafici většinou neumí a nechtějí programovat, programátoři na druhou stranu nezřídka grafickou stránku ignorují nebo jí přímo opovrhují. Při prostém míchání html a php se však obě role významně prolínají. Pokud by programátor a designér byly skutečně dvě různé osoby, museli by úzce spolupracovat a výsledná produktivita by byla nízká. Tento problém je potřeba řešit tzv. oddělením vzhledu od logiky. Ideální je takový stav, kdy programátor a designér mohou pracovat zcela nezávisle. Z teoretického hlediska totiž stačí specifikovat, jaké objekty bude stránka obsahovat, co má být jejich obsahem a jakou mají mít funkci. Úloha návrháře je potom následující. Vytvoří vzhled stránky a na konkrétní místa umístí data, o kterých v tuto chvíli pouze vágně prohlašme, že jsou „někde nachystanáÿ. Programátor na druhou stranu píše program, který v závislosti na vstupu (proměnná $_POST, URL, . . . ) generuje požadovaný výstup. To dokáže i za předpokladu, že v životě neviděl html a nerozezná růžovou barvu od zelené. 2 Dostupná řešení Od motivace se nyní posuneme k řešením, která jsou dnes k dispozici. Možným východiskem je využití formátu xml. Ten byl již od začátku navržen k uchovávání dat. Spojením s xslt šablonami dostaneme způsob, jak vzhled (xslt šablona) oddělit od dat (xml soubor). Je ovšem pravdou, že jazyk xsl nemůže bezezbytku nahradit php a tedy veškerou logiku, která řídí web v jeho úplnosti. Navíc vyjadřovací způsob jazyka xsl je eufemicky řečeno velmi exotický, obtížně pochopitelný a i po překonání těchto obtíží bývá výsledná šablona těžko čitelná. Další možností jsou šablonovací systémy, kterých dnes již existuje několik. Jedním z nich je systém Smarty, kterým se budeme zabývat ve zbytku članku. Mezi systémy stejného druhu 1 vyniká jak snadným a intuitivním použitím, tak i výběrem nabízených funkcí. Jeho základním stavebním kamenem jsou šablony, což není nic jiného než standardní (x)html soubor1 obohacený o Smarty tagy. Při návrhu šablony lze (mimo jiné) použít proměnné, které obsahují potřebná data. Podívejme se na jednoduchý příklad on-line novinových článků. Představme si novinový článek, který se skládá z titulku, úvodu, těla a jména autora. Tyto elementy implicitně neobsahují žádnou informaci o způsobu svého zobrazení. Jejich obsah je aplikací (php kód) předán do šablony2 . Designér potom navrhne šablonu zobrazující články s využitím běžných html tagů, kaskádových stylů atd. a na příslušná místa pomocí Smarty tagů vloží odpovídající obsah. Rozhodne-li se někdy programátor, že změní způsob generování obsahu, návrhář šablony to ani nepozná, natož aby musel cokoliv měnit. Naproti tomu když se jakkoliv změní šablona, a tím i vzhled, neznamená to pro programátora žádnou práci navíc. 3 Vlastnosti systému Systém Smarty je napsán v php3 . Lze na něj pohlížet jako na knihovnu, která implementuje objekt Smarty, se kterým může programátor pracovat. Vše funguje velmi rychle, jelikož šablony se před zobrazením kompilují4 . K opětovné kompilaci dochází pouze tehdy, dojde-li ke změně šablony. Systém je velmi konfigurovatelný. Lze definovat vlastní sekvenci znaků, která v šabloně uvozuje Smarty tag. Snadno lze vytvářet nové funkce, které jsou pak dostupné návrhářům šablon. Smarty umožňuje práci s konfiguračními soubory. Jedním z jejich možných použití je přehledná správa různých jazykových mutací webu. Šablony umožňují vkládání („includováníÿ) dalších šablon. Kromě proměnných, které mohou být jak atomické, tak strukturované, lze v šablonách používat mnoho dalších věcí. Kromě předdefinovaných funkcí jsou dostupné konstrukce typu if/then/else a primitivní cykly. Obojí s neomezeným zanořováním. Navíc lze přímo do šablon vkládat php kód, což je však na druhou stranu nedoporučované a většinou i zbytečné. 3.1 Instalace Celá procedura je velmi jednoduchá. Stačí stáhnout distribuci Smarty a příslušné soubory vybalit na vhodné místo. V php kódu je třeba nastavit konstantu SMARTY_DIR5 a vytvořit instanci objektu Smarty. Dále je možné konfigurovat adresáře se šablonami, zkompilovanými šablonami atd., systém ale funguje i bez toho. 3.2 Konkrétní příklad použití Podívejme se na jednoduchý příklad. Vytvořená stránka má za úkol pozdravit uživatele zvoleným jazykem. Začneme php kódem (Příklad 1). Čistě technicky může jít o jakýkoliv jiný obsah, důležité jsou pouze Smarty tagy. Pravděpodobně jako čtveřice pojmenovaných proměnných. 3 Vyžaduje verzi 4.0.6 nebo vyšší 4 Výsledkem kompilace je opět php skript, jak už ale napovídá slovo kompilace, není příliš čitelný. 5 To není nutné v případě, že potřebné soubory jsou „vidětÿ. 1 2 2 <? php require(’Smarty.class.php’); $smarty = new Smarty; $lang = get_lang(); $smarty->assign(’lang’, $lang); $smarty->display(’index.tpl’); ?> Příklad 1: obsah souboru index.php Takto bude vypadat konfigurační soubor greetings.cfg (Příklad 2), ve kterém jsou uloženy pozdravy v různých jazycích. [en] greeting [cs] greeting [fr] greeting [gr] greeting [hu] greeting = "Hello" = "Ahoj" = "Salut" = "Hallo" = "Szia" Příklad 2: obsah souboru greetings.cfg A nakonec šablona index.tpl (Příklad 3), která vyrobí výsledný vzhled. { config_load file="greetings.cfg" section=$lang scope="global" } <h1 style="color: red;">{ #greeting# }</h1> Příklad 3: obsah souboru index.tpl Jak je vidět, veškeré formátování se skutečně odehrává až v šabloně (červený nadpis úrovně 1) a jakákoliv změna by neměla vliv na ostatní soubory. 4 4.1 Psaní šablon Základní syntaxe Všímavější již jistě ví, že podobně jako se pro html tagy používají znaky <>, tak u Smarty jsou to složené závorky {}. Ještě všímavější čtenáři si mohou vzpomenout, že také lze stanovit jiné znaky či sekvence znaků. Navíc by ještě mohlo docházet ke konfliktům s úseky javascriptu nebo css, proto Smarty obsahuje tag literal, jehož obsah zůstává při interpretaci šablony (kompilaci) nedotčen. Vše, co se nachází mimo složené závorky, je ponecháno beze změny. V šablonách je možné psát i komentáře, a to tímto způsobem: {* Toto je komentář *} S proměnnými se ve Smarty pracuje velmi podobně jako v php. Rovněž jsou uvozeny znakem $, povoleno je indexování polí a lze přistupovat k metodám a proměnným objektů. 3 Výjimkou jsou proměnné z konfiguračních souborů, ty musí být obklopeny znakem # tak, jak je vidět v příkladu 3. Zápis funkcí je jednoduchý. Za otevírací závorkou je jméno volané funkce, následuje seznam parametrů oddělených mezerami tak, jak ukazuje toto schéma: { funcname attr1="val" attr2="val" } Přímo v šabloně je možné zapisovat i matematické výrazy, např.: { $foo->bar - $bar[1] * $baz->foo->bar() - 3 * 7 } 4.2 Proměnné Smarty rozeznává tři druhy proměnných. První typ tvoří proměnné předané z php společně s těmi, které byly definovány až v šabloně. Jejich chování je stejné. K dispozici jsou atomické proměnné, standardní i asociativní pole libovolné hloubky a v neposlední řadě vnitřní proměnné objektů. Použití těchto proměnných shrnuje příklad 4. <?php $smarty = new Smarty; $smarty->assign(’Contacts’, array(’fax’ => ’555-222-9876’, ’email’ => ’[email protected]’, ’phone’ => array(’555-444-3333’, ’555-111-1234’))); $smarty->display(’index.tpl’); ?> ... {$Contacts.fax}<br /> {$Contacts.email}<br /> {$Contacts.phone[0]}<br /> {$Contacts.phone[1]}<br /> ... 555-222-9876<br /> [email protected]<br /> 555-444-3333<br /> 555-111-1234<br /> Příklad 4: použití proměnných - php, šablona a výstup Proměnné z konfiguračních souborů jsme probrali již dříve a zbývá dodat pouze to, že k nim lze přistupovat pomocí speciální proměnné $smarty.config. Tím se dostáváme k vyhrazené proměnné $smarty, pomocí které lze přistupovat k řadě interních hodnot, hodnotám z polí GET, POST, SERVER atd. a několika dalším údajům. Jako sebevysvětlující příklady uveďme $smarty.now a $smarty.version. 4 4.3 Modifikátory Zajímavou oblastí návrhu šablon jsou modifikátory. Ve své podstatě jsou to pouze převlečené funkce, jejich zápis je však tímto způsobem intuitivní a přehledný narozdíl od typické závorkové notace. Modifikátory lze uplatnit na proměnné, funkce a řetězce. Za výraz určený k modifikaci napíšeme znak | následovaný jménem modifikátoru s případnými parametry, které jsou odděleny dvojtečkou. Modifikátory je možné skládat za sebe v libovolném pořadí. Jako příklad poslouží modifikátory k proložení mezerami6 a oříznutí (Příklad 5). $text = "Don’t worry, be happy!"; ... { $text|spacify|truncate:15 }<br /> { $text|truncate:15|spacify }<br /> ... D o n ’ t D o n ’ t w o<br /> w o r r y , b e</br> Příklad 5: použití modifikátorů - php, šablona a výstup 4.4 Interní funkce Smarty rozeznává dva druhy funkcí. Interní funkce jsou základními stavebními kameny systému a nelze je běžným způsobem měnit. Dále si každý programátor může vytvořit své uživatelské funkce a snadným způsobem je přidat k ostatním. Smarty již ve své základní distribuci obsahuje řadu uživatelských funkcí. Nyní si ukážeme několik interních a posléze uživatelských funkcí. config load(file, section, scope) Slouží k zavedení proměnných z konfiguračního souboru. Je možné určit konkrétní sekci (viz. příklad 2) a rozsah platnosti těchto proměnných. foreach(from, item, key, name), foreachelse Cyklus přes jedno asociativní pole určené parametrem from. Item je název proměnné, která bude v každém cyklu obsahovat příslušná data. Pokud je to zapotřebí, lze v každé iteraci použít i příslušný klíč - k tomu slouží parametr key. Tento cyklus lze použít pouze pro jednu úroveň pole, nicméně smyčky lze zanořovat do sebe. konkrétní použití ukazuje příklad 6. Celý cyklus lze pojmenovat a prostřednictvím proměnné $smarty potom přistupovat k dalším užitečným informacím o cyklu. Zmiňme třeba číslo aktuální iterace nebo celkový počet iterací celého cyklu7 . 6 7 Nebo jiným specifikovaným řetězcem Tento údaj je k dispozici, i když ještě cyklus neskončil 5 Nepovinný blok foreachelse je vykonán tehdy, když v předchozím bloku foreach nedošlo ani k jediné iteraci. $smarty->assign("contacts", array( array("phone" => "1", "fax" => "2", "cell" => "3"), array("phone" => "555-444", "fax" => "555-333", "cell" => "760-123"))); ... {foreach name=outer item=contact from=$contacts} {foreach key=key item=item from=$contact} {$key}: {$item}<br> {/foreach} {/foreach} .. phone: 1<br> fax: 2<br> cell: 3<br> phone: 555-4444<br> fax: 555-3333<br> cell: 760-1234<br> Příklad 6: příklad použití funkce foreach include(file, assign, [variable, ...]) Funkce vkládající jinou šablonu. Povinným parametrem je jméno souboru. Také je možné přidat parametr assign, který určí jméno proměnné, do které se uloží obsah šablony místo zobrazení na místě funkce include. Všechny proměnné aktuální šablony jsou viditelné i ve vkládané šabloně. Do volání funkce lze navíc zahrnout libovolné množství dalších parametrů, které budou vystupovat jako lokální proměnné ve vkládané šabloně a budou mít přednost před proměnnými stejného jména. if/elseif/else Příkaz if funguje stejně jako v php. Používá i stejné operátory a přidává několik dalších, které jsou užitečné při návrhu šablony. section(name, loop, start, step, max, show), sectionelse Druhá možnost, jak cyklem projít pole. Narozdíl od funkce foreach se více blíží příkazu for, jak ho známe z běžných programovacích jazyků. Povinnými parametry jsou name (jméno indexu) a loop (pole). Při běžném použití bez dalších parametrů smyčka projde všechny hodnoty daného pole, které tak jako jediné určuje, kolik iterací se vykoná. Parametry start a step mají očekávaný význam. Údaj max určuje maximální počet operací, a pokud show není nastaven na nepravdu, bude výsledek příkazu section normálně zobrazen. V opačném 6 případě (a nebo pokud nedošlo ani k jedné iteraci) se vykoná blok sectionelse. Možnosti této funkce ukazuje příklad 7. {section name=cust loop=$customers} <p>id: {$customers[cust].id}<br> name: {$customers[cust].name}<br> address: {$customers[cust].address}<br> {section name=contact loop=$contact_type[cust]} {$contact_type[cust][contact]}: {$contact_info[cust][contact]}<br> {/section} </p> {/section} ... <p>id: 1000<br> name: John Smith<br> address: 253 N 45th<br> home phone: 555-555-5555<br> cell phone: 555-555-5555<br> e-mail: [email protected]<br> </p> <p>id: 1001<br> name: Jack Jones<br> address: 417 Mulberry ln<br> home phone: 555-555-5555<br> cell phone: 555-555-5555<br> e-mail: [email protected]<br> </p> Příklad 7: příklad použití funkce section I v případě funkce section lze pomocí jejího jména a proměnné $smarty přistupovat k interním hodnotám o cyklu, a to jak během jeho provádění, tak i po skončení smyčky. 4.5 Uživatelské funkce assign(var, value) Slouží k přiřazení hodnoty proměnné přímo při zpracovávání šablony. Oba parametry jsou pochopitelně povinné. cycle(name, values, ...) Další funkce určená k procházení polí, která má ale narozdíl od ostatních jedno výhodné použití. Znázorňuje ho příklad 8. 7 {section name=rows loop=$data} <tr bgcolor="{cycle values="#eeeeee,#d0d0d0"}"> <td>{$data[rows]}</td> </tr> {/section} ... <tr bgcolor="#eeeeee"> <td>1</td> </tr> <tr bgcolor="#d0d0d0"> <td>2</td> </tr> <tr bgcolor="#eeeeee"> <td>3</td> </tr> Příklad 8: příklad použití funkce cycle eval(var, assign) Tato funkce interpretuje proměnnou var, jako by to byla součást šablony. To je nutné v případě, že obsahem proměnné jsou mimo jiné i Smarty tagy. Pokud je vyplněn parametr assign, bude do proměnné zvoleného jména přiřazen výsledek funkce místo normálního zobrazení. A kdy se něco takového hodí? Například může být potřeba umístit Smarty tagy do dat v konfiguračním souboru nebo do proměnných předávaných z php. Jiným příkladem je souběžné použití Smarty a xml+xslt. Výstupem xsl šablony může být text se Smarty tagy. Pokud je tento výstup předáván šabloně prostřednictvím php, musí být použita funkce eval8 . html checkboxes(name, values, output, selected, options, ...) Tato funkce vytvoří skupinu zaškrtávacích políček. Data lze zadat dvojím způsobem. Buď dvěma poli values a output nebo jedním asociativním polem options. Pomocí parametru selected lze zařídit, aby některá políčka byla rovnou zaškrtnuta. Výstup funkce je kompatibilní s xhtml. Použití viz. příklad 9 Smarty dále obsahuje i další funkce pro automatické vytváření formulářových prvků. Jsou to html_radios a html_options, jejichž výstupem je skupina přepínačů, respektive seznam možností pro element <select>. html table(loop, cols, rows, ...) Pomocí funkce html_table lze vytořit jednoduchou tabulku. Jediným povinným parametrem je loop, což je pole hodnot, které mají být zobrazeny v tabulce. Pozor, jde o jednorozměrné pole. Rozměry tabulky jsou dány parametry cols a rows. Stačí pouze druhý z nich, jelikož 8 Toto není umělý příklad, popsaná situace skutečně vznikla, když jsem vyvíjel jeden web. 8 $smarty->assign(’customers’, array( 1000 => ’Joe Schmoe’, 1001 => ’Jack Smith’, 1002 => ’Jane Johnson’, 1003 => ’Charlie Brown’)); $smarty->assign(’cust_id’, array(1001, 1003)); ... {html_checkboxes name="id" options=$customers selected=$cust_id separator="<br />"} ... <label><input type="checkbox" <label><input type="checkbox" Jack Smith</label><br /> <label><input type="checkbox" <label><input type="checkbox" name="id" value="1000" />Joe Schmoe</label><br /> name="id" value="1001" checked="checked" /> name="id" value="1002" />Jane Johnson</label><br /> name="id" value="1003" />Charlie Brown</label><br /> Příklad 9: použití funkce html checkboxes druhý údaj je implicitně dán počtem hodnot v poli. Funkce má mnoho dalších parametrů, pomocí nichž lze ovlivňovat formátování tabulky a umístění dat. Tato funkce není příliš vhodná pro tabulky se záhlavím a dalšími metadaty. V takovém případě jsou vhodnější funkce foreach a section. Použití html_table ukazuje příklad 10. 4.6 Konfigurační soubory Konfigurační soubory jsou užitečné pro správu globálních nastavení celého webu. Příkladem můžou být barevná schémata. Řekněme, že všechny tabulky mají být zobrazeny jednotným stylem. Bez konfiguračních souborů by každá změna znamenala úpravy mnoha šablon a nebo předávání barev z php, což však odporuje principu oddělení vzhledu od aplikační logiky. Jiným příkladem, o kterém jsme mluvili již dříve, jsou jazykové mutace (viz. příklad 2). Hodnoty v konfiguračním souboru mohou být umístěny v uvozovkách, ale není to nutné. Pouze pokud je hodnota proměnné zapsaná přes více řádků, je nutné ohraničit ji trojnásobnými uvozovkami. Cokoliv, co odporuje syntaxi, je považováno za komentář. Při psaní komentářů je však vhodné používat vhodnou formu, například začínat každý řádek komentáře znakem #. Data v souboru mohou být rozdělena do sekcí. Proměnné, které nejsou součástí žadné sekce, jsou globální. Při zavedení konfiguračního souboru se specifikovanou sekcí se do šablony dostanou proměnné z příslušné sekce společně se všemi globálními proměnnými. Konfigurační soubory se v šablonách používají pomocí interní funkce config_load. 5 Smarty a php Systém Smarty nabízí široké pole působnosti i php programátorům. K tomu slouží objekt Smarty, který jsme již mnohokrát zmiňovali. Je možné měnit hodnoty několika desítek pro9 $smarty->assign(’data’,array(1,2,3,4,5,6,7,8,9)); ... {html_table loop=$data} {html_table loop=$data cols=4} ... <table> <tr><td>1</td><td>2</td><td>3</td></tr> <tr><td>4</td><td>5</td><td>6</td></tr> <tr><td>7</td><td>8</td><td>9</td></tr> </table> <table> <tr><td>1</td><td>2</td><td>3</td><td>4</td></tr> <tr><td>5</td><td>6</td><td>7</td><td>8</td></tr> <tr><td>9</td><td> </td><td> </td><td> </td></tr> </table> Příklad 10: použití funkce html table měnných a využívat velké množství metod. Za pozornost stojí i filtry, které lze definovat a používat při zobrazování šablon. A pak jsou tady samozřejmě uživatelské funkce, jejichž tvorba může usnadnit práci návrháři šablon. Všechna tato témata jsou však pokročilejšího charakteru a (jak napovídá praxe) jejich použití je výrazně méně časté. Proto se jim v tomto textu nebudeme dále věnovat. 6 Poznámky závěrem Zdrojem informací pro tuto práci byla dokumentace systému Smarty a mé bohaté zkušenosti s ním, které zahrnují bankovní aplikaci, web jednoho univerzitního projektu, bakalářskou práci či stránky taneční skupiny. Je vidět, že šablonovací systém lze uplatnit skutečně kdekoliv. Tento text slouží jako vysvětlení principů šablonovacích systémů reprezentativní ukázka možností jednoho z nich. Zdaleka nejde o úplný popis. Vynechal jsem řadu funkcí dostupných při tvoření šablon a jen lehce jsem zmínil bohaté možnosti pro php programátory. Případní zájemci najdou všechny potřebné informace v dokumentaci. Vše o Smarty lze najít na adrese http://smarty.php.net/. 10