ITDumka
Делим код пополам или представление по шаблону в PHP

Зачем все это? - возмущенно спросил Артём, широко раскрыв глаза. У нас и так все прекрасно работает! Я знаю, где мне надо поменять код, чтобы новости отображались в три колонки, а не в две. А Тане я потом покажу, где поменять теги...

Если вы понимаете, какие проблемы у Тани с Артёмом, то это уже хорошо. Как некоторые догадываются Артём - PHP программист, Таня - дизайнер-верстальщик. У них есть общая проблема - файл, который формирует ленту новостей. Редактируют они его по очереди и сам черт ногу сломит в нём. А все потому, что PHP код Артёма уже давно зависит от HTML кода Татьяны и наоборот. И нет им покоя длительное время, если что-то надо поменять в этом файле. Если бы Артём с Татьяной знали о представлении по шаблону, то они бы не тратили кучу нервов и времени на столь простую функцию.

Итак, что же такое шаблонизация, шаблонизаторы, наконец, Template View, зачем они нужны, как их делать и как ими пользоваться? Все очень просто...

Представление по шаблону (Template View) или шаблонизация - это механизм, позволяющий заменять в статической HTML странице ее динамические части, то есть, простым языком говоря, в Таниной HTML страничке заменять данные, генерируемые PHP кодом Артёма. При этом Артём работает со своим файлом, в котором не видит ни одного Таниного тега, а Таня со своим. С этим механизмом Артёму и Тане очень удобно. Им больше не режет глаза чужой листинг, и их идеи в построении кода не зависят друг от друга. Таким образом, выделим два основных, почти смежных, достоинства шаблонизации:

  • Разделение и удобство работы дизайнера и кодера над одним приложением.
  • Отделение представления интерфейса пользователя от логики работы программы.

Даже если вы сами работаете над приложением, вам, может быть, намного удобнее будет разделять работу над видом вашего сайта и его логикой работы.

Для начала маленький пример:

Listing №1 (PHP)
  1. <?php
  2.   date_default_timezone_set('Europe/Kiev');
  3.   $UserMessage = 'Артём';
  4.   if (date('md') === '0509') {
  5.     $UserMessage .= ' С Днём Победы!';
  6.   }
  7. ?>
  8. <html>
  9. <head>
  10. <title>Пример1</title>
  11. <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  12. </head>
  13. <body>
  14. Привет, <?php echo $UserMessage; ?>
  15. </body>
  16. </html>

Поспешу вас обрадовать - перед вами простейший код, выполняющий шаблонизацию. Да, может вы и не знали, но PHP сам по себе отличный шаблонизатор. Как видно из примера, в результате выполнения сценария в статической части (шаблоне - код черного цвета) будет заменена ее динамическая часть, которую PHP сгенерирует с помощью команды echo. Это самый простой случай привожу для того, чтобы вы запомнили, что PHP - сам по себе шаблонизатор. Но как бы там ни было, такой подход не решает проблемы Артёма и Татьяны.

Разделим код на два файла: model.php и template.php. Первый отдадим Артёму, а второй Татьяне.

model.php

Listing №2 (PHP)
  1. <?php
  2.   date_default_timezone_set('Europe/Kiev');
  3.   $UserMessage = 'Артём';
  4.   if (date('md') === '0509') {
  5.     $UserMessage .= ' С Днём Победы!';
  6.   }
  7.   include 'template.php';
  8. ?>

template.php

Listing №3 (PHP)
  1. <html>
  2. <head>
  3. <title>Пример2</title>
  4. <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  5. </head>
  6. <body>
  7. Привет, <?php echo $UserMessage; ?>
  8. </body>
  9. </html>

Как же теперь обрадовался Артём - нет ни одного Татьяниного тега. Чистый PHP код. Татьяне тоже повезло. Хотя ее немного смущает кусочек PHP кода в теле страницы, но она не сильно обращает на это внимание, потому как знает, что в этом месте просто будет написано сообщение пользователю.

Заметьте, обычным разделением кода мы намного облегчили работу Артёма и Татьяны. Теперь статическая часть страницы, то есть интерфейс пользователя отделен от логики работы приложения. Не только Татьяна, но и любой другой человек, не владеющий программированием на PHP, но имеющий опыт в дизайне и верстке, теперь легко сможет изменить внешний вид сайта. Артем в любой момент может поменять логику работы приложения, например, заменив ее на поздравление "С Новым Годом". При этом ему не будет мозолить глаза вся эта Татьянина писанина и, пока Таня ваяет дизайн, он быстренько все поменяет и пойдет домой смотреть Футураму.

Таким образом мы отделили интерфейс от логики работы скрипта. Мы создали статический шаблон (файл template.php), в который поместили маркер PHP кода (<?php echo $UserMessage; ?>), обращающийся к скрипту для получения динамической информации - строки с приветствием пользователя. Такой метод шаблонизации называется native (родной) или прямой, потому как команда include 'template.php' фактически вставила template.php в model.php и PHP шаблонизировал все сам по себе, заменив свой вызов в шаблоне значением переменной $UserMessage.

Раз уж у нас есть такой хороший способ шаблонизации, то спрячем логику его работы в простой класс:

class.view.php

Listing №4 (PHP)
  1. <?php
  2. /**
  3.  * Класс шаблонизатор
  4.  */
  5. class View {
  6.   /**
  7.    * Файл шаблона
  8.    * @var string
  9.    */
  10.   private $_File;
  11.   /**
  12.    * Массив переменных-маркеров шаблона
  13.    * @var array
  14.    */
  15.   private $_Vars;
  16.   /**
  17.    * Конструктор, инициализируем свойства класса
  18.    */
  19.   public function __construct()
  20.   {
  21.     $this->_File = '';
  22.     $this->_Vars = array();
  23.   }
  24.   /**
  25.    * Устанавливает файл шаблона
  26.    * @param string $File
  27.    */
  28.   public function SetTemplate($File)
  29.   {
  30.     $this->_File = $File;
  31.   }
  32.   /**
  33.    * Связывает переменные скрипта с переменными-маркерами
  34.    * @param string $VarName
  35.    * @param string $VarValue
  36.    */
  37.   public function AssignVar($VarName, $VarValue)
  38.   {
  39.     if ($VarName !== '') {
  40.       $this->_Vars[$VarName] = $VarValue;
  41.     }
  42.   }
  43.   /**
  44.    * Отображает шаблон
  45.    */
  46.   public function Display()
  47.   {
  48.     if (file_exists($this->_File)) {
  49.       extract($this->_Vars, EXTR_PREFIX_ALL, '');
  50.       include $this->_File;
  51.     }
  52.   }
  53. }
  54. ?>

Теперь попробуем его использовать. Поместим наш класс в файл class.view.php и перепишем классы model.php и template.php:


template.php

Listing №5 (PHP)
  1. <html>
  2. <head>
  3. <title>Пример3</title>
  4. <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  5. </head>
  6. <body>
  7. Привет, <?php echo $_UserMessage; ?>
  8. </body>
  9. </html>


model.php

Listing №6 (PHP)
  1. <?php
  2. require_once 'class.view.php';
  3. date_default_timezone_set('Europe/Kiev');
  4. $UserMessage = 'Артём';
  5. if (date('md') === '0509') {
  6.   $UserMessage .= ' С Днём Победы!';
  7. }
  8. //Создаем объект представления интерфейса
  9. $Veiw = new View;
  10. //Устанавливаем шаблон представления
  11. $Veiw->SetTemplate('template.php');
  12. //Установка значения переменной-маркера сообщения пользователю
  13. $Veiw->AssignVar('UserMessage', $UserMessage);
  14. //Отображение интерфейса
  15. $Veiw->Display();
  16. ?>


Из примера видно, что класс довольно легко использовать. Артём видит в нем логическое завершение работы приложения - подготовка данных к выводу и сам вывод данных на экран. Теперь он может подключать разные шаблоны, устанавливать различные и общие значения их динамических частей. Инкапсуляция или, попросту, упаковка шаблонизатора в класс позволяет нам увеличить функциональные возможности приложения не увеличивая сложность процесса кодирования и восприятия кода. Мы скрываем все сложности в классе и, тем самым, приобретаем мощный инструмент отделения логики представления от логики приложения.

Давайте немного улучшим класс View. В данном случае extract использовать не эффективно по ряду причин. Это - возможные пересечения переменных, засорение области видимости, увеличение расхода памяти. Хотя, в принципе можно все оставить и так. Но все же...

class.view.php

Listing №7 (PHP)
  1. <?php
  2. /**
  3.  * Класс шаблонизатор
  4.  */
  5. class View {
  6.   /**
  7.    * Файл шаблона
  8.    * @var string
  9.    */
  10.   private $_File;
  11.   /**
  12.    * Массив переменных-маркеров шаблона
  13.    * @var array
  14.    */
  15.   private $_Vars;
  16.   /**
  17.    * Конструктор, инициализируем свойства класса
  18.    */
  19.   public function __construct()
  20.   {
  21.     $this->_File = '';
  22.     $this->_Vars = array();
  23.   }
  24.   /**
  25.    * Устанавливает файл шаблона
  26.    * @param string $File
  27.    */
  28.   public function SetTemplate($File)
  29.   {
  30.     $this->_File = $File;
  31.   }
  32.   /**
  33.    * Связывает переменные скрипта с переменными-маркерами
  34.    * @param string $VarName
  35.    * @param string $VarValue
  36.    */
  37.   public function AssignVar($VarName, $VarValue)
  38.   {
  39.     if ($VarName !== '') {
  40.       $this->_Vars[$VarName] = $VarValue;
  41.     }
  42.   }
  43.   /**
  44.    * Отображает шаблон
  45.    */
  46.   public function Display()
  47.   {
  48.     if (file_exists($this->_File)) {
  49.       include $this->_File;
  50.     }
  51.   }
  52.   /**
  53.    * Функция доступа к элементу массива
  54.    * переменных-маркеров шаблона
  55.    * @param string $Varname
  56.    * @return mixed
  57.    */
  58.   public function __get($VarName)
  59.   {
  60.     if (array_key_exists($VarName, $this->_Vars)) {
  61.       return $this->_Vars[$VarName];
  62.     }
  63.     return NULL;
  64.   }
  65. }
  66. ?>


И немного изменим шаблон:


template.php

Listing №8 (PHP)
  1. <html>
  2. <head>
  3. <title>Пример4</title>
  4. <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  5. </head>
  6. <body>
  7. Привет, <?php echo $this->UserMessage; ?>
  8. </body>
  9. </html>

Все стало лучше и проще. Заметьте, в файле model.php мы ничего не меняем - опять же, благодаря тому, что мы скрыли сложности реализации шаблонизации в классе. Скрывайте сложность - это избавит вас от многих проблем и не только при программировании на PHP.

Итак, мы познакомились с native или прямым методом шаблонизации. Его особенность, как вы понимаете, в прямом инклуде файла шаблона, при парсинге которого PHP сам заменит свои маркеры-выводы на соответствующие значения переменных.

Теперь самое время вспомнить наших Артёма с Татьяной и познакомить вас с еще одним методом шаблонизации - методом замены. Как бы мы не старались угодить Артёму с его скриптом, Татьяну все же немного напрягает присутствие чужого кода в своем милом HTML 4.01 Transitional. Дело в том, что полностью Татьяну удовлетворить нельзя, но мы можем максимально приблизить такой момент как раз с помощью метода замены. Для этого нам понадобиться что? Правильно - новый класс.

class.view.php

Listing №9 (PHP)
  1. <?php
  2. /**
  3.  * Класс шаблонизатор
  4.  */
  5. class View {
  6.   /**
  7.    * Файл шаблона
  8.    * @var string
  9.    */
  10.   private $_File;
  11.   /**
  12.    * Массив переменных маркеров шаблона
  13.    * @var array
  14.    */
  15.   private $_Vars;
  16.   /**
  17.    * Конструктор, инициализируем свойства класса
  18.    */
  19.   public function __construct()
  20.   {
  21.     $this->_File = '';
  22.     $this->_Vars = array();
  23.   }
  24.   /**
  25.    * Устанавливает файл шаблона
  26.    * @param string $File
  27.    */
  28.   public function SetTemplate($File)
  29.   {
  30.     $this->_File = $File;
  31.   }
  32.   /**
  33.    * Связывает переменные скрипта с переменными-маркерами
  34.    * @param string $VarName
  35.    * @param string $VarValue
  36.    */
  37.   public function AssignVar($VarName, $VarValue)
  38.   {
  39.     if ($VarName !== '') {
  40.       $this->_Vars[$VarName] = $VarValue;
  41.     }
  42.   }
  43.   /**
  44.    * Отображает шаблон
  45.    */
  46.   public function Display()
  47.   {
  48.     echo $this->_Prepare();
  49.   }
  50.   /**
  51.    * Загружает шаблон и
  52.    * заменяет в нем маркеры замены
  53.    */
  54.   private function _Prepare()
  55.   {
  56.     $Content = '';
  57.     if (file_exists($this->_File)) {
  58.       $Content = file_get_contents($this->_File);
  59.       foreach ($this->_Vars as $VarName=>$VarValue) {
  60.         $Content = str_replace('{' . $VarName . '}', $VarValue, $Content);
  61.       }
  62.     }
  63.     return $Content;
  64.   }
  65. }
  66. ?>

Посмотрим теперь на наш шаблон:


template.php

Listing №10 (HTML)
  1. <html>
  2. <head>
  3. <title>Пример5</title>
  4. <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  5. </head>
  6. <body>
  7. Привет, {UserMessage}
  8. </body>
  9. </html>


Да он уже и не PHP совсем! Хотя в нем и нет вообще PHP кода, в нем присутствует некий псевдокод. Но для Татьяны, дизайнера-верстальщика, это просто набор символов {UserMessage}, который довольно прост в восприятии и практически ее не отвлекает. Разве что она знает, что там, в итоге, будет имя пользователя.

Стоит заметить, что данная конструкция не разрушает никакие HTML теги и ее удобно наблюдать в WYSIWYG редакторах, в отличие от шаблонов, реализованных для native метода, которые часто могут исказить вид шаблона в "неподготовленных" к этому редакторах. Заметно, что метод замены намного удобнее для Тани. Да и Артём, как обычно, не сильно напрягается. Таким образом, мы практически полностью отделили Танин HTML код от PHP кода Артёма, а также логику вида интерфейса от логики работы приложения. Теперь, с помощью такого шаблонизатора можно написать приложение на PHP, описать маркеры в шаблонах и тогда любой дизайнер, даже понятия не имеющий, что есть PHP, сможет эффективно поработать.

Но у всего хорошего всегда есть какие-то недостатки. И этот метод не исключение. Во-первых, метод замены несколько, а порой и намного, медленнее native метода. Это естественно - мы же считываем файл с диска, ищем и заменяем в нем одни значения на другие. Во-вторых, при написании всего кода, можно теоретически совершит больше ошибок. Например, не связав переменную скрипта с переменной-маркером, мы можем получить надпись "Привет , {UserMessage}", в то время как в предыдущем подходе мы просто увидим "Привет, ". Я думаю, первое выглядит похуже. Вот такими недостатками мы можем платить за комфорт Татьяны. Поэтому, Татьяны со знаниями базовых конструкций PHP обычно ценятся подороже обычных Татьян. Но не надо так сильно останавливать внимание на недостатках. В конце концов, решающим фактором является не количество недостатков, а относительная их доля в количестве достоинств. Существует масса классов использующих, именно этот метод. Одним из самых распространенных является Smarty. Его используют миллионы сайтов и не замечают данных недостатков.

То же самое относиться и к native методу. Он не избавляет нас от PHP кода в шаблоне. Зато он намного быстрее и предоставляет более эффективное управление логикой представления, благодаря именно PHP коду внутри шаблона. Не надо стараться извлечь весь PHP код из шаблона - главное разделить логику представления и приложения. И если PHP код в шаблоне управляет логикой представления, то это есть норма и ничего более менять уже не надо. Разве что необходимость в эффективной работе дизайнеров или требования заказчика или личные предпочтения заставят вас все же использовать метод замены.

На этом можно поставить первую точку. Спешу сообщить, что все персонажи вымышленные и не надо спрашивать меня кто они такие, если вы захотите оставить комментарий к статье.


Comments
By   jeckz
Published   08.07.2009

Спасибо за статейку. Как новичек, могу сказать что я все понял, за исключением:

в примере где первый раз используется класс, в файле temlate.php, в строке 7, поджопник не лишний? Просто, я тут чуть чуть не могу понять, и хотелось бы знать, это опечатка, или так надо?.

И т.к. блог новый, сразу хочу попросить, нумеровать примеры, что бы в комментах не было потом таких строк:

"в примере где первый раз используется класс, в файле temlate.php,"


ЗЫ. "Татьяны со знаниями базовых конструкций PHP обычно ценятся подороже обычных Татьян" - Посмеялся чуток :)

Published   08.07.2009

jeckz
в строке 7, поджопник не лишний?

Нет не лишний, смотри extract с ключом EXTR_PREFIX_ALL.

jeckz
хочу попросить, нумеровать примеры

Ну файлы template.php все пронумерованы в <title>

Listing №1 (PHP)
  1. <title>Пример5</title>

ну а вообще в следующий раз пронумерую.

By   jeckz
Published   08.07.2009

Ну так ведь в коде:

Listing №1 (PHP)
  1. extract($this->_Vars, EXTR_PREFIX_ALL, '');
,

а не:

Listing №2 (PHP)
  1.  extract($this->_Vars, EXTR_PREFIX_ALL, '_');


Поясни пожалуйста, если я что-то не понимаю.

Published   08.07.2009

Функция extract прибавляет префиксы через знак подчеркивания. Знак подчеркивания добавляется по умолчанию. Если префикс равен пустой строке, то будет прибавляться только знак подчеркивания. Можно было бы добавить префикс и с двумя знаками подчеркивания, как ты пишешь:

Listing №1 (PHP)
  1. extract($this->_Vars, EXTR_PREFIX_ALL, '_');
Вообще, сделано это для того, чтобы переменные $UserMessage в model.php и $_UserMessage в template.php не пересекались, потому что extract без параметров переназначил бы переменную $UserMessage. В данном примере это ни на что бы не повлияло. Но если бы шаблонизатор производил какие-либо дополнительные действия со своими переменными (экранирование, преобразование в HTML сущности), то эти действия применились бы к переменной $UserMessage, описуемой в model.php. Пример без использования extract, лишен заботы об этом.

By   jeckz
Published   08.07.2009

Спасибо, теперь уж точно все понятно, без слов "За исключением".

By   jeckz
Published   09.07.2009

Буду ждать новые статьи. Вот мне кажется у тебя бы получилась очень хорошая статья про класс работы с БД :)

By   Материалы для саморазвития
Published   17.08.2009

Было бы интересно узнать поподробнее

Published   17.08.2009

Материалы для саморазвития

Было бы интересно узнать поподробнее

Поподробнее я если буду рассказывать, то в других статьях. Вообще я хотел расписать подробнее каждый из методов, но решил освятить только принципы. Кстати, что вас конкретно интересует?


By   Cерега
Published   05.10.2009

Уважаемый Константин, хочу спросить, подойдет ли класс показанный Вами в этой статье для использования в небольшой cms, например на новостном сайте? Если нет, то что бы Вы посоветовали желательно кроме Smarty?

Published   05.10.2009

Cерега
Уважаемый Константин, хочу спросить, подойдет ли класс показанный Вами в этой статье для использования в небольшой cms, например на новостном сайте? Если нет, то что бы Вы посоветовали желательно кроме Smarty?

Думаю, Серега, что эти примеры не потянут cms. Для native метода шаблонизации необходимо как минимум возможность вставки шаблона в шаблон. А для метода замены еще минимум циклы и условия. Честно скажу, я особо не пользовался различными шаблонизаторами и использую свои реализации, поэтому конкретные библиотеки посоветовать не могу, да и не хочу, потому как это "расслабляет" мозг. Попробуй реализовать в этих классах, то, что я предлагаю. Если получится, то ты сам уже будешь себе советовать. А если нет, то смыл использовать чужой код, не понимая начальных принципов его работы я не вижу.

By   Cерега
Published   06.10.2009

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

P.S. Для чего именно мне нужен не сложный и надежный шаблонизатор я напишу Вам лично если Вы согласитесь мне помочь.

Published   06.10.2009

Ты так и не понял, что я хотел донести. Могу сказать, что за деньги тебе я писать ничего не буду, но вот в одной из следующих статей я планирую выложить улучшенную версию native-шаблонизатора. Когда это точно будет, сказать не могу.

By   Cерега
Published   07.10.2009

Хорошо, спасибо за эту статью, с нетерпением буду ждать новую.

By   MimeHeirm
Published   30.12.2009

Интересно написано....но многое остается непонятнымb

Published   30.12.2009

MimeHeirm
Интересно написано....но многое остается непонятным

Вы спрашивайте, не стесняйтесь. Для этого комментарии и нужны ))

By   Серега
Published   30.12.2009

Константин, я нашел в сети функцию по замене html частей на php переменные, в html-файлах шаблона нужная переменная указывается например так %var%

Посмотрите пожалуйста и напишите можно ли как то улучшить эту функцию и переделать так что бы в html-файлах прописывать не %var% а например {var} (так вроде симпатичнее)???

Listing №1 (unknown)
  1. function html ($path){
  2. $down = implode ("", @file ($path));
  3. preg_match_all ("/%(\w+)%/", $down, $matches);
  4. list ($tags, $vals) = $matches;
  5. $f = create_function (
  6. '$a',
  7. 'return isset ($GLOBALS[$a])?$GLOBALS[$a]:"???";'
  8. );
  9. $vals = array_map ($f, $vals);
  10. echo strtr ($down, array_combine ($tags, $vals) );
  11. }

Published   30.12.2009

Уважаемый Серега. Давайте не будем задавать такие глупые вопросы. Попробуйте так:

Listing №1 (PHP)
  1. preg_match_all("/{(\w+)}/", $down, $matches);

А вообще, если вы не можете сделать такое элементарное, то дальше вам планировать нечего. И пробуйте лучше то, что я советую, а не это дерьмо.


By   Серега
Published   31.12.2009

Напишите хотя бы почему вы считаете этот код дерьмом?

На счет фигурных скобок, я именно так и сделал, просто опасался не возникнет ли с ними каких либо ошибок при использовании этого кода в скрипте сайта.

By   Серега
Published   31.12.2009

Ведь не зря же в коде используется именно знак процента.

Published   31.12.2009

Серега, что же ты так. Зачем позоришь такое хорошее имя. Прости, если я тебя этим задеваю, но ты вообще не понимаешь сути и не знаешь элементарных функций. Если ты не будешь изучать документацию и задавать такие вопросы, но ничего хорошего из этого не получиться.

Вот в твоём примере, если это можно так назвать, после второй строки уже дальше читать ничего не хочется. Почему? Да потому, что тот, кто это писал вообще далёк от логики и вообще от этого мира )). Зачем считывать файл, разбивать его на строки, а потом эти строки опять объединять? Можешь мне сказать, зачем?

By   Cерега
Published   01.01.2010

Предмет цитирования
Серега, что же ты так. Зачем позоришь такое хорошее имя.

О чем Вы???


Предмет цитирования
Зачем считывать файл, разбивать его на строки, а потом эти строки опять объединять? Можешь мне сказать, зачем?

Если честно, то понятия не имею зачем, поэтому и спрашивал совета у Вас.


Константин, я понимаю что Вас сильно бесят глупые (с Вашей точки зрения) вопросы и все же не надо так злиться на человека, который хоть и с ошибками и неполным пониманием чего то пытается постичь азы php. Ведь кто как не Вы (опытный программист) например может помочь встать ему на верный путь?!


Давно хотел спросить, что за cms Вы используете на этом сайте.

Published   01.01.2010

Cерега, прости, если я тебя каким-то образом обидел, но я считаю, что так познавать язык нельзя. Лучше прочитай пару книг, по PHP. В твоем случае - любых. Почитай мануал по PHP. Прямо с самого начала. Вот ссылка на документацию по PHP на русском языке. Правда полностью не переведено, но ничего страшного. А брать какие-то непонятные примеры и что то с ними непонятное делать - это ни к чему хорошему не приведет.


Cерега
что за cms Вы используете на этом сайте

Об этом сказано в разделе ПРО САЙТ

By   Cерега
Published   01.01.2010

Cпасибо за понимание )))

By   MimeHeirm
Published   02.01.2010

Интересно написано....но многое остается непонятнымb

Published   02.01.2010

MimeHeirm

Ау... Читай выше, если не бот.

By   Серега
Published   24.01.2010

Здраствуйте Константин! Я последовал Вашему совету и с помощью различных мануалов и статей пытаюсь освоить создание php-классов.

Есть такой вопрос:

При использовании представленного Вами в этой статье класса перед выводом шаблона приходится указывать шаблонизатору каждую переменную

<?php

$Veiw->AssignVar('UserMessage', $UserMessage);

?>

можно ли как то сделать что бы шаблонизатор автоматически определял в шаблоне любую переменную?

Ведь в этих переменных может быть очень много.

Published   24.01.2010

Серега
автоматически определял в шаблоне любую переменную

В любом случае необходимо инициализировать те данные (так скажем, набор переменных), которые вы выводите уже в HTML странице. Если это будет не в шаблоне, то это будет в каком-то другом месте. Можно сделать чуть проще и через магический метод __set() инициализировать переменные шаблона:

Listing №1 (PHP)
  1. class View {
  2.   public function __set($VarName, $VarValue)
  3.   {
  4.     $this->_Vars[$VarName] = $VarValue;
  5.   }
  6. //.....
  7. }
  8.  
  9. $View->UserMessage = $UserMessage;

Можно передавать шаблонизатору набор готовых(инициализированных) переменных:


Listing №2 (PHP)
  1. class View {
  2.   public function setVars($Vars)
  3.   {
  4.     $this->_Vars = $Vars;
  5.   }
  6. //......
  7. }
  8.  
  9. $ViewVars = array();
  10. $ViewVars['UserMessage'] = $UserMessage;
  11. $View->setVars($ViewVars);


В любом случае шаблонизатор запрашивает данные у модели приложения и эти данные должны быть где-то инициализированны. Даже если это будет происходить в модели, то всё равно вам придется это написать, или, если как-то это автоматизировать, то написать сам процесс. Но это уже зависит от модели, которую вы делаете уже исходя из архитектуры приложения и ваших потребностей. Поэтому, конкретнее чем "это можно как-то сделать", сказать не могу. В моих разработках это происходит не автоматом. Если вы теряетесь среди большого количества таких вызовов, то, возможно, вам надо пересмотреть другие участки, например, логику выборки и формирования данных, необходимость отображения каких-то данных в определенные моменты, на определенных страницах и т. д.


By   Серега
Published   25.01.2010

Предмет цитирования
Можно сделать чуть проще и через магический метод __set() инициализировать переменные шаблона

Ну хотя бы так. Уже меньше чем $Veiw->AssignVar('UserMessage', $UserMessage); :)

Спасибо большое!!!

By   Серега
Published   25.01.2010

Константин, посоветуйте еще пожалуйста как реализовать в данном классе обработку и вывод в шаблон массивов?

Например вывести из базы данных список пользователей по определенным параметрам и мне бы тогда было вполне достаточно такого шаблонизатора для своих проектов.

Published   25.01.2010

Слушай, давай не будем юлить и задавать такие вопросы. У тебя голова зачем? Подумай сам. Напиши алгорим на бумажке, реализуй. А такие вопросы обсуждаются обычно на форумах.

Вот пример обработки массива в шаблоне: http://itdumka.com.ua/index.php?cmd=shownode&node=10#listing7

By   Серега
Published   25.01.2010

Юлить не собирался, просто Вы мне кажетесь опытным программистом, а так же являетесь автором представленного здесь класса, вот и обращаюсь сначало к Вам.

Предмет цитирования
Вот пример обработки массива в шаблоне: http://itdumka.com.ua/index.php?cmd=shownode&node=10#listing7

Слишком много php-кода в файле шаблона. Ладно, если долго мучаться, что нибудь получится ))

By   Серега
Published   25.01.2010

И вообще Константин, что Вы злой такой постоянно???

Будьте немного проще и люди к Вам потянуться.

Published   25.01.2010

Серега

И вообще Константин, что Вы злой такой постоянно???

Будьте немного проще и люди к Вам потянуться.

Да ладно. Какой я злой? Я обычный кусок мяса, на сколько опытным конечно судить не мне. Но я говорю так, потому, что мне не особо нравиться объяснять какие-то вещи, которые должны рождаться у тебя в голове, понимаешь? Вот ты говоришь, слишком много php-кода и не можешь обработать массив в шаблоне - это наталкивает на мысль, что ты лукавишь.


Listing №1 (PHP)
  1. <div>
  2.   <?php foreach($Template->UserNames as $UserName):?>
  3.   <span style="color: #FF0000;"><?=$UserName;?></span><br>
  4.   <?php endforeach;?>
  5. </div>


Относительно, меньше кода не будет при native подходе.

By   Серега
Published   26.01.2010

Попробывал вот так сделать:

Listing №1 (PHP)
  1. $res=mysql_query("SELECT id,login,email FROM users");
  2. if (mysql_affected_rows() != 0) {
  3.     while ($q=mysql_fetch_array($res)) {
  4.         $uid=$q['id'];
  5.         $login=$q['login'];
  6.         $email=$q['email'];
  7.         $ArrUserInfo.='<tr><td>'.$uid.'</td><td>'.$login.'</td><td>'.$email.'</td></tr>';
  8.     }
  9. }
  10.  
  11. $View = new View;
  12. $View->SetTemplate('index.tpl');
  13.  
  14. $View->ArrUserInfo = $ArrUserInfo;
  15.  
  16. $View->Display();


А файле шаблона соответственно:

Listing №2 (HTML)
  1. <table border="1">
  2.   {ArrUserInfo}
  3. </table>


Но к сожалению теги ячеек таблицы остаются в скрипте, а не в шаблоне.

By   Серега
Published   26.01.2010

Еще вариант, например с навигацией:

Listing №1 (PHP)
  1. $page=$_SERVER["PHP_SELF"];
  2.  
  3. $menu=array(
  4. '<a href="index.php">Главная</a>',
  5. '<a href="news.php">Новости</a>',
  6. '<a href="contacts.php">Контакты</a>');
  7.  
  8. $menucount=count($menu);
  9. for($i=0;$i<$menucount;$i++) {
  10.     if (strstr($menu[$i], "$page")) {
  11.         $Navigate.="<li class='active'>$menu[$i]</li>";
  12.     } else {
  13.         $Navigate.="<li>$menu[$i]</li>";
  14.     }
  15. }
  16. $View = new View;
  17. $View->SetTemplate('index.tpl');
  18.  
  19. $View->Navigate = $Navigate;
  20.  
  21. $View->Display();


шаблон:

Listing №2 (HTML)
  1. <div class="sidebar">
  2.     <ul>
  3.         {Navigate}
  4.     </ul>
  5. </div>

Вообщем весь смысл в том, что обрабатывать цыклы и массивы еще до вывода в шаблон, а задать стиль оставшимся в скрипте тегам дизайнер-верстальщик может через css.

Published   26.01.2010

Ты используешь метод замены, а не native. Я не юзаю замену, поэтому могу посоветовать посмотреть в каком-то движке, типа Smarty, как там реализованы циклы. Обычно пишется какая-то конструкция, типа:


Listing №1 (HTML)
  1. <div class="sidebar">
  2.     <ul>
  3.         {for Item in Items}
  4.             <li>{Item}</li>
  5.         {/for}
  6.     </ul>
  7. </div>


, и парситься с помощью регулярных выражений.

Относительно любого метода твои примеры не верны, так как, например, "<li class='active'>$menu[$i]</li>" надо писать в шаблоне, потому что как CSS класс элемента ("active"), так и тип ("<li>") должен задаваться в шаблоне. В твоем примере, если список менюх надо будет вывести не через "<li>", то надо будет лезть в логику приложения, также как и надо будет лезть туда же, чтобы поменять класс "active".


By   Серега
Published   26.01.2010

Предмет цитирования
Обычно пишется какая-то конструкция, типа:

Да, я как раз думал об этом, должны быть какие то дополнительные параметры показывающие шаблонизатору что это цикл.

Предмет цитирования
В твоем примере, если список менюх надо будет вывести не через "<li>", то надо будет лезть в логику приложения, также как и надо будет лезть туда же, чтобы поменять класс "active".

Это верно, полной свободы для дизайнера тут не получается (

Тем не менее это лучше чем

Listing №1 (HTML)
  1. <div>
  2. <?php foreach($Template->UserNames as $UserName):?>
  3. <span style="color: #FF0000;"><?=$UserName;?></span><br>
  4. <?php endforeach;?>
  5. </div>

Published   26.01.2010

Серега
Это верно, полной свободы для дизайнера тут не получается

я надеюсь ты понимаешь, что можно сделать так, чтобы получалось. Как в Smarty, например.

Серега
Тем не менее это лучше чем

Это, я тебе скажу, ужасно спорный вопрос. Тут довольно много факторов в пользу и нет обоих методов. Если ты пишешь и верстаешь сам, то native дает больше гибкости, скорости, ну конечно относительно. Метод замены предназначен прежде всего на разработку шаблонов с помощью веб-дизайнеров, которые не владеют базовыми навыками php.

Вообще я схожусь во мнении, что если не надо делать "отдельно" вёрстку, то тут уж дело вкуса программиста, какой метод выбрать.


By   Серега
Published   02.02.2010

Продолжаю мусолить начатую мной тему на счет обработки циклов и массивов ))

Думал, думал и сделал пока так:

Listing №1 (PHP)
  1. private function _Prepare() {
  2.    
  3.     $Content = '';
  4.    
  5.     if (file_exists($this->_File)) {
  6.        
  7.         $Content = file_get_contents($this->_File);
  8.        
  9.         preg_match_all('|{StartCicle}(.*){EndCicle}|Uis', $Content, $out);
  10.        
  11.         foreach ($out[0] as $k => $replace) {
  12.             $cicle='';
  13.             for ($i=0; $i<$this->_Vars[CountRes]; $i++) {
  14.                 $cicle.=$out[1][$k];
  15.             }
  16.             $Content=str_replace($replace,$cicle,$Content);
  17.         }
  18.        
  19.         foreach ($this->_Vars as $VarName=>$VarValue) {
  20.            
  21.             $Content = str_replace('{' . $VarName . '}', $VarValue, $Content);
  22.            
  23.         }
  24.        
  25.     }
  26.    
  27.     return $Content;
  28.    
  29.   }


Listing №2 (PHP)
  1. $View = new View;
  2. $View->SetTemplate('cicle.tpl');
  3.  
  4. $CountRes='10';
  5. $View->CountRes = $CountRes;
  6.  
  7. $User = 'Серега';
  8. $View->User = $User;
  9.  
  10. $View->Display();


Listing №3 (HTML)
  1. {StartCicle}
  2.     <p>Цикл 1, определен {User} !!!</p>
  3. {EndCicle}
  4. <br />
  5. {StartCicle}
  6.     <p>Цикл 2, определен {User} !!!</p>
  7. {EndCicle}
  8. <br />
  9. {StartCicle}
  10.     <p>Цикл 3, определен {User} !!!</p>
  11. {EndCicle}


Заменит блоки {StartCicle}.....{EndCicle} и выведет $CountRes раз, при этом понимает несколько цыклов в шаблоне. И в файле шаблона остается красота ))

Но нужна еще обработка массивов, например что бы вывести результаты из базы и построчно записать их в $cicle.

Listing №4 (PHP)
  1. $res=mysql_query("SELECT id,login,email FROM users");
  2. for ($DataCicle=array(); $row=mysql_fetch_assoc($res) ; $DataCicle[]=$row);
  3. $View->DataCicle = $DataCicle;

Как дальше в шаблонизаторе обработать этот массив для меня пока темный лес.

Published   02.02.2010

Нужно более гибкое решение, чем то которое ты написал в _Prepare(). Ты написал, логику программы под шаблон. А надо наоборот. Я таким детально не занимался, поэтому рекомендую посмотреть в готовых шаблонных движках.

By   Серега
Published   02.02.2010

Да, там надо будет заменить $View->CountRes = $CountRes; на $View->CountRes = count($DataCicle);

Послать массив $DataCicle шаблонизатору и наверно как то разбить его по строкам, затем расставить данные согласно шаблону и вывести их $this->_Vars[CountRes]; раз, при этом в каждой новой строке менять данные на данные из следующей строки в массиве.

Listing №1 (HTML)
  1. <table border="1">
  2. <tr><td>id</td><td>login</td><td>email</td></tr>
  3.   {StartCicle}
  4.         <tr>
  5.             <td>{Uid}</td>
  6.             <td>{Login}</td>
  7.             <td>{Email}</td>
  8.         </tr>
  9.     {EndCicle}
  10. </table>


На теории вроде можно сделать, а вот на практике пока даже не знаю как начать.

By   Серега
Published   02.02.2010

Предмет цитирования
Нужно более гибкое решение, чем то которое ты написал в _Prepare(). Ты написал, логику программы под шаблон. А надо наоборот. Я таким детально не занимался, поэтому рекомендую посмотреть в готовых шаблонных движках.


Ну а как Вам вообще такой подход?

Published   02.02.2010

Еще раз повторяю. Тебе надо писать код, который ничего не знает про шаблон. Ты делаешь наоборот.

В идеале, тебе надо в шаблонизатор передавать какую-то структуру данных, причем эта структура не должна как-то отображать структуру расположения данных в шаблоне, хотя такое можно и выглядит и реализуется проще, как в твоем первом примере. Ты должен отдать массив в шаблон (без разницы какой там массив будет, какой вложенности, длинны, структура лишь показывает взаимосвязь данных). Шаблонизатор впоследствии исходя из шаблона отображает на него структуру полученных данных, с помощью тех же маркеров: название переменной в шаблоне -> название переменной в массиве переменных шаблонизатора -> значение переменной.

При разработке ты должен установить граничные условия в работе шаблонизатора (метод _Prepare(), только скорее всего его лучше назвать _Parse()), таким образом, чтобы определить, какой функционал он может поддерживать: вывод данных из массива, поддержка вложенности, тупо из одной переменной и только. А потом описать отображение в виде метода _Prepare(), учитывающим эти все условия, которые определяют гибкость описания шаблона.

Серега
Ну а как Вам вообще такой подход?

Я использую другой, он гибче, относительно проще и быстрее.

By   Серега
Published   02.02.2010

Предмет цитирования
Я использую другой, он гибче, относительно проще и быстрее.

Поделитесь на примере пожалуйста ))

Published   02.02.2010

Серега
Поделитесь на примере пожалуйста ))

Мы же это уже прошли:

#comment140


By   Серега
Published   03.02.2010

Ясно, а как лучше таким способом http://www.itdumka.com.ua/index.php?cmd=shownode&node=3#comment140 вывести например не только логин, но и еще допустим целую кучу данных пользователя и не одного пользователя, а целой базы??? Мне кажется в шаблоне получится настоящий винегрет.


И еще Константин, сделайте чтоб ник записывался в кукисы что ли, а то приходится каждый раз при написании поста его указывать.

Published   03.02.2010

Серега
И еще Константин, сделайте чтоб ник записывался в кукисы что ли, а то приходится каждый раз при написании поста его указывать.

Да я знаю, руки не доходят никак...

Серега
и не одного пользователя, а целой базы???

Ну да, а если всю базу данных вывести, то будет даже хуже чем винегрет.

Надо шаблоны простыми делать, делить на общие куски. Конечно если всё подряд выводить то будет черт знает что.

Ну вот такой пример, например )) :

Listing №1 (PHP)
  1. <table border="1">
  2.   <?php foreach($Data->Users as $User): ?>
  3.         <tr>
  4.         <?php if(!$User->IsLoggedIn()): ?>
  5.             <td>Привет, гость!</td>
  6.             <td>&nbsp;</td>
  7.         <?php else: ?>
  8.             <td>Привет, <?php echo $User->Login ?></td>
  9.             <td>Ваш email: <?php echo $User->Email ?></td>
  10.         <?php endif; ?>
  11.         </tr>
  12.    <?php endforeach; ?>
  13. </table>

By   Серега
Published   03.02.2010

Да уж, посмотришь на такой шаблон и уже не видишь смысла в шаблонизаторе.

Published   03.02.2010

Каждому свое, дело вкуса.

Create comment
 
Formatting
Comment can not be edited. Please, use the button "Preview"
By
  (Enter prev char)
Comment
Categories
PHP
12
articles
Прочее
4
articles
Delphi
0
article
C/C++
0
article
C#
0
article
Java
0
article
Perl
0
article
Python
0
article
Enter
Cookie must be "ON"
Login
Password
 
Popular tags
PHP
9
articles
паттерн
5
articles
framework
5
articles
шаблон
5
articles
Template View
3
articles
Facade
3
articles
Service Stub
3
articles
Page Controller
2
articles
Singleton
2
articles
Gateway
2
articles
MySQL
2
articles
Registry
2
articles
Command
2
articles
Front Controller
2
articles
Action
2
articles
Abstract Factory
2
articles
типовое решение
2
articles
шаблоны проектирования
2
articles
Iterator
2
articles
Transaction Script
2
articles
Rambler's Top100 Правильный CSS!