Использование шаблонов при программировании WEB-приложений

ЗАЧЕМ ВООБЩЕ ИСПОЛЬЗОВАТЬ ШАБЛОНЫ?


Может, кто-то скажет, а зачем выносить HTML код в отдельный файл если его можно спокойно писать в скрипте? Да конечно в скрипте можно писать HTML код, но просмотреть полностью «собранную», или частично «собранную» страницу которую выводит скрипт, возможно только при его нормальной работе, а во время отладки скрипта, как правило, возникают некоторые трудности. Потом при смене дизайна, опять же, править HTML код в скрипте сущая каторга, сколько раз я слышал о нареканиях со стороны Web-мастеров, что понять какие куски кода и в какой последовательности выносятся невозможно, так как, по большей части, они не программисты и вникать в код скрипта не намерены. И так далее и тому подобное, поэтому, все-таки будем разделять скрипты от HTML кода…

КАКАЯ ДОЛЖНА БЫТЬ СТРУКТУРА ШАБЛОНОВ


Сначала, вынося HTML код из скриптов, я каждый кусок записывал в отдельный файл. Да когда нет особых сложностей в сборке страницы (шапка, основа, подвал), то трудностей нет, но когда страница имеет сложные внедрения, такие как списки, причем одновременно разных видов (например список страниц и список товаров текущей страницы), количество файлов начинало возрастать с неимоверной быстротой (у меня дошло до того, что страница стала собираться из 28 файлов!!!), и опять возникли трудности, неимоверное количество кусков кода, настолько усложнило их обработку и редактирование, что я пришел опять к изначальной точке…
Потом пришла идея – использовать в качестве шаблона, всего 1 файл, который легко редактировать и который легко предварительно просмотреть визуально. То есть использовать в качестве шаблона уже практически готовую страницу, а разбивать её на куски будет сам скрипт.

ПРИМЕР ВЫВОДА СТРАНИЦЫ СПИСКА ТОВАРОВ С ИСПОЛЬЗОВАНИЕМ ОДНОГО ШАБЛОНА


<html>
<
head>
   <
title>Шаблон списка товаров</title>
   <
meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
</
head>
<
body>
<
table width=”100%” border=”0”>
<
tr>
       <
td>
            
Список товаров категории 1
      
</td>
</
tr>
<
tr>
       <
td>
          
Страницы &lt;
           <
font color=”red”><b>1</b></font>
           <
a href=?category=1&page=1”>2</a>
           <
a href=?category=1&page=1”>3</a>
           <
a href=?category=1&page=1”>4</a>
           &
gt;
       </
td>
</
tr>
<
tr>
       <
td>
              <
table width=”98%” border=”0” >
              <
tr>
                     <
td>№ п.п.</td>
                     <
td>Наименование товара</td>
                     <
td>Цена товара</td>
              </
tr>
              <
tr>
                     <
td>1</td>
                     <
td>Товар № 1</td>
                     <
td>100 р.</td>
              </
tr>
              <
tr>
                     <
td>2</td>
                     <
td>Товар № 2</td>
                     <
td>150 р.</td>
              </
tr>
              <
tr>
                     <
td>3</td>
                     <
td>Товар № 3</td>
                     <
td>200 р.</td>
              </
tr>
              </
table>
       </
td>
</
tr>
<
tr>
       <
td>
             
Страницы &lt;
              <
font color=”red”><b>1</b></font>
              <
a href=?category=1&page=1”>2</a>
              <
a href=?category=1&page=1”>3</a>
              <
a href=?category=1&page=1”>4</a>
              &
gt;
       </
td>
</
tr>
</
table>
</
body>
</
html>

Выглядит наша таблица как:

Список товаров категории 1
Страницы < 1 2 3 4 >
№ п.п.Наименование товараЦена товара
1Товар 1100 р.
2Товар 2150 р.
3Товар 3200 р.
Страницы < 1 2 3 4 >

Какие блоки кода нам понадобятся:

- Страницы, причем вся строка, так как в зависимости от того, сколько у нас будет товаров, у нас будет либо постраничный вывод, либо все товары уместятся на одной странице. Отдельно из этой строки нам понадобится текущая страница, и ссылка на другую страницу;
- Строка с выводом товара;

Что нужно будет изменять на странице:

- Заголовок страницы;
- Заголовок таблицы (название категории);
- Количество и номера страниц;
- № п.п., Наименование товара и Цена товара

После обработки получаем такой HTML-код:

<html>
<
head>
   <
title>Список товаров категории %name_category%, страница %n_page%</title>
   <
meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
</
head>
<
body>
<
table width=”100%” border=”0” >
<
tr>
       <
td>
             
Список товаров категории %name_category%
       </
td>
</
tr>
<!--
1 --> <tr>
<!--
1 --> <td>
<!--
1 --> Страницы &lt;
<!--
2 --> <font color=”red”><b>%n_page%</b></font>
<!--
3 --> <a href=?category=%id_category%&page=%n_page%>%n_page%</a>
<!--
1 --> <!-- pages -->
<!--
1 --> &gt;
<!--
1 --> </td>
<!--
1 --> </tr>
<!--
list_pages -->
<
tr>
       <
td>
              <
table width=”98%” border=”0” >
              <
tr>
                     <
td>№ п.п.</td>
                     <
td>Наименование товара</td>
                     <
td>Цена товара</td>
              </
tr>
<!--
4 --> <tr>
<!--
4 --> <td>
<!--
4 --> %npp%
<!--
4 --> </td>
<!--
4 --> <td>
<!--
4 --> %name_goods%
<!--
4 --> </td>
<!--
4 --> <td>
<!--
4 --> %price_goods%
<!--
4 --> </td>
<!--
4 --> </tr>
<!--
list_goods -->
              </
table>
       </
td>
</
tr>
<!--
list_pages -->
</
table>
</
body>
</
html>

Что получается? Перед каждой строкой определенного блока кода мы вставили небольшой комментарий в виде <!-- цифра -->, и в тех местах, где мы должны вставить код мы добавили по дополнительному комментарию (<!--pages -->, <!-- list_pages -->, <!-- list_goods -->). Причем комментарий <!--pages --> у нас получился в середине куска кода который мы отметили как <!-- 1 -->!
Почему мы использовали именно комментарии? Да потому, что при просмотре этого шаблона мы увидим все как положено, без лишних вещей:

Список товаров категории %name_category%
Страницы < %n_page% %n_page% >
№ п.п.Наименование товараЦена товара
%npp%%name_goods%%price_goods%

Мы можем проверить стили и общий дизайн «не отходя от кассы».
Почему мы поставили комментарии напротив каждой строки, а не, скажем, просто метки начала и конца блока? Да потому, что в случае вывода строки списка номеров страниц, нам требуется «выкусить» часть блока, а потом, после обработки, вставить обратно, нам бы пришлось этот блок разбивать не на 3 части а на 4 (начало блока, текущая страница, ссылка на другую страницу, конец блока). Да и потом, мне лично визуально гораздо проще видеть эти блоки в коде.

Ну теперь когда мы сделали шаблон, попробуем его обработать:

У нас есть переменная $category – название категории;
У нас есть переменная $id_category – идентификатор категории;
У нас есть переменная $num_goods – количество товаров на странице;
У нас есть переменная $on_page – текущая страница;
У нас есть массив @goods – наши товары в виде - Наименование|цена|

Пишем код:

# Загружаем шаблон

$file = “list.html”;
open (TMP, “$file”);
@
template = <TMP>;
close ($file);

# Разрезаем его на блоки:

foreach (@template) {
      if (
m/<!-- 1 -->/) {
            
$_ =~s /<!-- 1 -->//g;                                                 #Избавляемся от ненужного комментария
            
@line_pages = (@line_pages, $_);                                  #Собираем блок
            
$_ = "";                                                                      #Обнуляем строку шаблона
      
}
      if (
m/<!-- 2 -->/) {
            
$_ =~s /<!-- 2 -->//g;                                                 #Избавляемся от ненужного комментария
            
@on_page = (@on_page, $_);                                         #Собираем блок*
            
$_ = "";                                                                      #Обнуляем строку шаблона
      
}
      if (
m/<!-- 3 -->/) {
            
$_ =~s /<!-- 3 -->//g;                                                 #Избавляемся от ненужного комментария
            
@off_page = (@off_page, $_);                                        #Собираем блок*
            
$_ = "";                                                                     #Обнуляем строку шаблона
      
}
      if (
m/<!-- 3 -->/) {
            
$_ =~s /<!-- 3 -->//g;                                                 #Избавляемся от ненужного комментария
            
@line_goods = (@line_goods, $_);                                  #Собираем блок
            
$_ = "";                                                                      #Обнуляем строку шаблона
      
}
}

# Собираем строку с номерами страниц

$number = @goods;
# Если количество товаров меньше или равно количеству товаров выводимых
# на странице, то обнуляем блок где выводится список номеров страниц
if ($number <= $number_page) {@line_pages = ();}
else {
      
$number_page = $number/$num_goods;
      for (
$i = 0; $i < $number_page; $i++) {
            
$page = $i + 1;
# Проверяем текущую страницу, и в зависимости от этого присваиваем
# временному массиву соответствующий блок
            
if ($page eq $on_page) {@temp_line = @on_page;}
            else {@
temp_line = @off_page;}
# Обрабатываем текущий массив
          
foreach (@temp_line) {
               
$_ =~s /%id_category%/$id_category/gi;
               
$_ =~s /%n_page%/$page/gi;
           }
# Прибавляем полученный массив к списку номеров старниц
          
@temp_list = (@temp_list, @temp_line);
      }
# Обрабатываем блок с линией списка номеров страниц*
   
foreach (@line_pages) {
         
$_ =~s /<!-- pages -->/@temp_list/gi;
     }
}

# Собираем таблицу с товарами

$first_goods = ($page – 1)*$num_goods;
$last_goods = ($page)*$num_goods;
if (
$last_goods > $number) {$last_goods = $number;}

for (
$i = $first_goods; $i < $last_goods; $i++) {
     (
$name, $price) = split(/|/,@goods[$i]);
# присваиваем временному массиву соответствующий блок
    
@temp_line = @line_goods;
    
$npp = $i + 1;
    foreach (@
temp_line) {
         
$_ =~s /%npp%/$npp/gi;
         
$_ =~s /%name_goods%/$name/gi;
         
$_ =~s /%price_goods%/$price/gi;
     }
# Прибавляем полученный массив к таблице товаров
      
@list_goods = (@list_goods, @temp_line);
}

# Окончательная обработка шаблона

foreach (@template) {
    
$_ =~s /<!-- list_goods -->/@list_goods/gi;
    
$_ =~s /<!-- list_pages -->/@line_pages/gi;
    
# $_ =~s /<!-- pages -->/@temp_list/gi;*
}

# Выводим результат на экран

print "Content-type: text/html; charset=windows-1251nn";
print
"@template";
exit;

ЗАКЛЮЧЕНИЕ


Конечно использование данного метода может немного замедлить работу скрипта, так как проводится довольно много циклов, но убрав HTML-код из скрипта мы, тем самым, уменьшили его размер, а собрав шаблон в один файл, мы упростили дальнейшую работу с ним.

Автор: Сергей Томулевич