Використання templates з підстановкою даних
Що таке шаблони і навіщо вони потрібні
Що таке шаблон в мові програмування? Можна сказати, що шаблон - це текст із змінними усередині нього. При обробці шаблону відбувається заміна змінних на їх значення.
У одній з лекцій ми вже розглядали приклад шаблону. Це був шаблон для відображення документів. Користувач створював рядок тексту, розміченого за допомогою html-тегов, і вставляв в неї спеціальні метасимволи (вигляду <!имя елементу>), які наша програма згодом замінювала на значення відповідних елементів. Для чого нам був потрібний такий шаблон? Аби, наприклад, можна було змінити стиль відображення документа, не міняючи коди програми.
Найбільш поширена відповідь на питання, навіщо потрібні шаблони, звучить приблизно так: шаблони потрібні для того, щоб відокремити логіку роботи застосування від способу представлення даних, тобто від дизайну.
Отже, templates - це механізм, який дозволить вам більшою чи меншою мірою позбавитися від тісної прив'язки вашої коди до зовнішнього вигляду вашого сайту і допоможе вам полегшити завдання генерації динамічної HTML коди сторінок. Основна ідея цього механізму полягає в тому, аби мати безліч "шматочків" HTML коди з яких ви потім, як з кубиків в конструкторі, зберете будь-яку сторінку вашого сайту.
Прості templates
Простий спосіб використання tempates - це створення безлічі змінних, що містять шматочки HTML коди. Код самої сторінки при цьому самостійно займається об'єднанням HTML коди з цих змінних з необхідними даними для здобуття результату. Поглянемо, наприклад, як могла б виглядати генерація тієї ж самої сторінки за допомогою простих темплейтов. Тут я не став використовувати жоден з поширених пакетів, тому що просто хочу продемонструвати вам основну ідею.
Файл templates.php содежит опис всіх необхідних темплейтов. Якщо поглянути на вміст змінних, описаних в цьому файлі, то можна відмітити, що це просто та ж сама сторінка, але розбита на безліч частин, між якими мають бути вставлені дані.
<?php
// Початок заголовка сторінки
$pageHeaderStart = '<HTML><head><title>';
// Кінець заголовка сторінки
$pageHeaderEnd = '</title><head><body>';
// Початок меню
$menuStart = '<table width="100%" border="0" cellspacing="0" cellpadding="1"><tr>';
// Кінець меню
$menuEnd = '</tr></table>';
// Початок пункту меню
$menuItemCellStart = '<td>';
// Кінець пункту меню
$menuItemCellEnd = '</td>';
// Початок content'а сторінки
$pageContentStart = '<p>';
// Кінець content'а сторінки
$pageContentEnd = '</p>';
// Footer сторінки
$pageFooter = '<p>(с) 2006 Вася Пупкин</p></body></html>'; ?>
Файл index.php містить сам код побудови сторінки
<?php
// Заголовок сторінки
$title = 'Проста сторіночка';
// Вміст меню
$menu = array(
array('page1.php','Страница 1')
array('page2.php','Страница 2')
array('page3.php','Страница 3')
);
// Content сторінки
$content = 'Динамічний content сторінки';
// Підвантажуємо темплейты
include('templates.php');
// Виводимо заголовок
echo $pageHeaderStart.$title.$pageHeaderEnd;
// Виводимо меню
echo $menuStart; for($i=0;$i<sizeof($menu);$i++)
echo $menuItemCellStart.'<a href="'.$menu[$i][0].'">'.$menu[$i][1].'</a>'.$menuItemCellEnd;
echo $menuEnd;
// Виводимо content сторінки
echo $pageContentStart.$content.$pageContentEnd;
// Виводиться footer
echo $pageFooter; ?>
Звичайно цей код виглядає просто жахливо і так насправді ніхто не робить. Але основну ідею "збирання" HTML коди сторінки з шматочків цей приклад демонструє досить добре.
Насправді основна проблема приведеної вище коди полягає в тому, що він не дозволяє вам повністю позбавитися від HTML коди усередині PHP коди, адже тут кожна частинка HTML коди зберігається в окремій змінній. Уявіть, скільки довелося б мати подібних змінних для більш-менш складної сторінки. І, крім того, не дивлячись на те, що безпосередньо HTML код винесений в окремий файл, але його зв'язок з результатами роботи PHP коди жорстко заданий усередині самого PHP коди (адже всі об'єднання HTML і PHP коди жорстко прописані).
Більшість цих проблем можуть бути вирішені шляхом використання нескладної системи для підстановок даних в HTML темплейты.
Використання templates з підстановкою даних
Основною відмінністю систем, заснованих на підстановці даних, є те, що вони дозволяють, використовуючи певний синтаксис, визначати місця вставки даних в HTML темплейты. По суті все наявні системи роботи з темплейтами засновані саме на цьому принципі і єдине, що їх розрізняє - синтаксис, використовуваний для завдання темплейтов і набір можливостей, що надається системою.
Короткий опис синтаксису для опису темплейтов в BNF-подобной системі і потім дам необхідні пояснення.
"Ключ" для підстановки:
<Кеу> ::= '{'<Key name>[' '<Default value>]'}'
Ключем для підстановки тут називається частина тексту темплейта, яка буде згодом замінена на деякі дані, передані функції - обробникові темплейтов. Він складається з двох основних частин: імені (унікального в межах даного темплейта) і необов'язкового значення по-умовчанню. Воно використовуватиметься в разі, якщо при обробці темплейта для нього не було задано значення. В разі, якщо значення по-умовчанню також не було задане - цей ключ буде замінений на порожній рядок.
Значення по-умовчанню може також бути використане для завдання спеціальної обробки.
Нижче приведені 3 різних типа синтаксису, допустимих для значення по-умовчанню:
<Default value> ::= <Text>
<Default value> ::= '#'<Template name>[' '<Parameter name>' '<Parameter value>]*
<Default value> ::= '!'<Function name>[' '<Parameter name>' '<Parameter value>]*
Як бачите, тип обробки для значення по-умовчанню вказується в першому символі.
Якщо це символ '#', то все значення розглядається як "вставити результат обробки темплейта з ім'ям <Template name> із заданими параметрами як значення для цього ключа підстановки". Тобто обробник темплейтов буде викликаний рекурсивно для обробки тепмлейта із заданим ім'ям і заданим списком даних для підстановки, а результати обробки цього темплейты будуть використані як значення для підстановки.
Якщо це символ '!', то процес обробки схожий на попередній, з тією лише різницею, що замість виклику обробника темплейтов проводиться виклик призначеної для користувача функції із заданим ім'ям і їй як параметр передається масив даних, заданих в цьому ключі (структура масиву така ж, як і для самої функції обробки темплейтов). Результати роботи функції будуть використані як значення для підстановки.
Символи, що мають спеціальне значення можуть бути вставлені в текст, використовуючи їх ESC-последовательности (escaping sequences):
Поза ключами для підстановки
{ <=> {l}
} <=> {r}
Усередині ключів для підстановки
{ <=> {{
} <=> }}
Нижче приведений текст функції, яка безпосередньо займається обробкою темплейтов, використовуючи описаний вище синтаксис.
Файл templates.function.php
<?php
// Вставка в сторінку HTML коди на основі темплейтов
// Параметри:
// $template - темплейт з HTML кодом, який використовуватиметься як основа
// $params - масив з даними, які використовуватимуться для підстановки.
function insertTemplate($template,$params=array()){
// Прибираємо з тексту темплейта всі escaped символи (вони будуть замінені
// на необхідні значення пізніше) Це необхідно, аби полегшити завдання
// розбиття темплейтов за допомогою регулярних виразів
$template = strtr($template,array('{{'=>"\x03"'}}'=>"\x04"));
// Використовуємо регулярне вираження аби отримати масив всіх місць усередині темплейта
// які мають бути замінені на результати підстановки.
preg_match_all("/\{([^\}]+)\}/i",$template,$matches);
// Якщо не було знайдено жодного місця для підстановки -
// просто повертаємо вихідний текст темплейта.
if (sizeof($matches[0])==0)
return($template);
// У цей масив ми збиратимемо тексти, які будуть исползованы для
// підстановок в темплейт.
$replaces = array();
// Нам необхідно перетворити всі знайдені місця для підстановок усередині темплейта
// у регулярні вирази для їх пошуку. Тоді ми зможемо згодом виконати
// всі підстановки одночасно, використовуючи заміну по масиву регулярних виразів.
for ($i=0;$i<sizeof($matches[0]);$i++)
$matches[0][$i]= '/'.preg_quote($matches[0][$i],'/').'/';
// Тепер нам необхідно підготувати тексти для заміни
// Для цього нам необхідно обробити вміст кожного із знайдених
// місць для підстановок усередині темплейта.
for ($i=0;$i<sizeof($matches[1]);$i++)
{
// Перетворимо всі escaped символи в нормальних. Символ розділення ' ' при цьому
// замінюємо на символ з кодом 0x01, аби не переплутати.
$match = strtr(strtr'($matches[1][$i],array(' '=>"\x02"', '=>"\x01")),"\x02" ');
// Перевіряємо, що із себе вдає рядок, який ми намагаємося обробити
if (strpos($match,"\x01")!==false)
// Цей рядок містить в собі декілька частин. Це означає, що окрім імені ця
// рядок містить якісь параметри, які вимагають додаткової обробки.
{
// Оскільки основна синтаксична структура у нас складається з 2 частин - імені
// і значення по-умовчанню - отримуємо ці дві основні частини у вигляді окремих змінних
list($key,$default)= explode("\x01",$match,2);
// Виправляємо regular expression для подальшої заміни
$matches[0][$i]= "/\{$key\ [^\}]+\}/";
// Перевіряємо, чим є параметр, переданий усередині темплейта. Якщо він починається
// з одного із спеціальних символів, то необхідна додаткова обробка цього значення.
// Проте це необхідно робити толлько в разі, якщо в переданих у функцію даних для
// заміни немає тексту для цієї підстановки (тому що дані, передані в якості
// аргументу мають вищий пріоритет).
if ((in_array($default[0],array('#''!'))) && (!isset($params[$key])))
{
// Отримуємо список аргументів. Перший символ відкидаємо, тому що це ознака
// спеуиальной обробки і не відноситься до імені.
$words = explode("\x01",substr($default,1));
// Оскільки першим в отриманому списку коштує ім'я, яке використовуватиметься
// обробником - беремо його в окрему змінну і прибираємо з масиву аргументів.
// Тепер в масиві $words - лише список аргументів.
$name = array_shift($words);
// Перевіряємо, якщо кількість аргументів - непарне (тобто нам необхідний ще один, оскільки
// всі аргументи розглядаються як пари "ім'я-значення"), то додаємо порожній рядок.
if ((sizeof($words)%2)!=0)
$words[] = '';
// Формуємо масив параметрів. Він має бути в тому ж вигляді, в якому він передається
// у дану функцію (тобто ім'я параметра задається у вигляді ключа асоціативного масиву).
$params = array();
for ($j=0;$j<sizeof($words);$j+=2)
$params[$words[$j]]= $words[$j+1];
if ($default[0]=='#')
// Символ '#' вказує на необхідність вставки темплейта із заданим ім'ям
$default = insertTemplate($GLOBALS[$name],$params);
elseif ($default[0]=='!')
// Символ '#' вказує на необхідність вставки результатів роботи призначеною для користувача
// функції із заданим ім'ям
$default = call_user_func($name,$params);
};
// Якщо в списку текстів для підстановки, переданих як параметр в цю функцію
// є текст для підстановки з таким же ім'ям, то використовуємо його, тому що параметри
// передані як аргумент мають вищий пріоритет. Якщо ж такого тексту
// ні, то використовуємо текст, що є у нас як значення.
$replaces[] = (isset($params[$key]))?$params[$key]:$default;
}
elseif ($match=='l')
// Цей рядок - escaping для лівої фігурної дужки, що має спеціальне значення.
$replaces[] = '{';
elseif ($match=='r')
// Те ж саме для правої фігурної дужки
$replaces[] = '}';
else
// Цей рядок має лише ім'я. Якщо в списку текстів для підстановки, переданих
// як параметр в цю функцію, є текст для підстановки з таким ім'ям
// то використовуємо його, інакше використовуємо як заміну порожній рядок.
$replaces[] = (isset($params[$match]))?$params[$match]:"";
};
// Тепер у нас є всі необхідні дані і ми можемо виконати заміну. Оскільки все
// рядки, які необхідно замінити в даному темплейте сконвертированы в регулярних
// вирази - необхідно просто виконати заміну по наявних масивах. Крім того
// тут же ми повертаємо нормальні значення escaped символам, які ми прибирали на початку.
return(strtr(preg_replace($matches[0],$replaces,$template),array("\x03"=>'{',"\x04"=>'}')));
}; ?>
Тепер поглянемо, як можна згенерувати ту ж саму просту сторіночку, використовуючи приведену вище функцію.
Файл templates.php содежит опис всіх необхідних темплейтов.
<?php
// Основний темплейт для сторінки
$tplPage = <<<HTML
<html>
<head>
<title>{title}</title>
</head>
<body>
{menu #tplMenu}
{content #tplContent}
{footer #tplFooter}
</body>
</html>
HTML;
// Темплейт для меню сайту
$tplMenu = <<<HTML
<table width="100%" border="0" cellspacing="0" cellpadding="1">
<tr>
{menuItems !createMenu}
</tr>
</table>
HTML;
// Темплейт для пункту меню для меню сайту
$tplMenuItem = <<<HTML
<td><a href="{url}">{name}</a></td>
HTML;
// Темплейт для основного content'а сторінки
$tplContent = <<<HTML
<p>{content !createPageContent}</p>
HTML;
// Темплейт footer'а сайту
$tplFooter = <<<HTML
<p>{footer (с) 2001 Вася Пупкин}</p>
HTML;
?>
Файл index.php містить сам код побудови сторінки
<?php
// Підвантажуємо всі необхідні файли
include('templates.function.php'); include('templates.php');
// Заголовок сторінки
$title = 'Проста сторіночка';
// Вміст меню
$menu = array(
array('page1.php','Страница 1')
array('page2.php','Страница 2')
array('page3.php','Страница 3')
);
// Content сторінки
$content = 'Динамічний content сторінки';
// Функція генерації меню сайту. Вона викликається парсером темплейтов
// під час обробки темплейта $tplMenu.
function createMenu() {
global $menu;
$html = '';
// Вся генерація вмісту меню зводиться все до того ж викличу парсера темплейтов.
// При цьому як аргументи передаються дані для кожного з наявних пунктів меню.
foreach($menu as $item)
$html .= insertTemplate($GLOBALS['tplMenuItem'],array('url'=>$item[0],'name'=>$item[1]));
return($html);
};
// Функція генерації вмісту сторінки. У нашому випадку вона просто повертає змінну.
function createPageContent() {
return($GLOBALS['content']);
};
// Як бачите, після всіх підготовчих кроків весь код програми зводиться до однієї строчки :-)
// Ми просто викликаємо парсер темплейтов для обробки основного темплейта сторінки, а все
// необхідні зв'язки між темплейтами у нас прописані безпосередньо усередині них, що дозволить
// згодом легко змінити їх не міняючи коди. Що, власне, нам і потрібне.
echo insertTemplate($tplPage,array('title'=>$title)); ?>
Як бачите - код стає набагато компактнішим і логічнішим із застосуванням темплейтов. І, крім того, навіть така проста система обробки темплейтов значно спрощує вам роботу. Ви дістаєте можливість контролювати окремо логіку програми і окремо - її візуальну частину, чого ми, власне, і добивалися.
До речі, цю функцію можна застосовувати не лише для генерації HTML (все ж вона дуже проста для цього), а і для інших цілей. Наприклад таких, як генерація e-mail. Адже інколи буває необхідно згенерувати текст листа за шаблоном, додавши в нього якусь інформацію. Використання цієї простої функції допоможе вам вирішити це завдання швидко і легко.
<== предыдущая лекция | | | следующая лекция ==> |
Видалення змінних сесії | | | Завдання на лабораторну роботу. Гіперпосилання. Впровадження зображень. |
Дата добавления: 2016-04-02; просмотров: 831;