Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Котеров Д. В., Костарев А. Ф. - PHP 5. 2-е издание (В подлиннике) - 2008

.pdf
Скачиваний:
6114
Добавлен:
29.02.2016
Размер:
11.36 Mб
Скачать

984

Часть VII. Приемы программирования на PHP 5

Листинг 46.5. Файл mvc/model.php

<?php ## MVC. Модель (ядро) гостевой книги.

//Загружает гостевую книгу с диска. Возвращает содержание книги. function LoadBook($fname) {

if (!file_exists($fname)) return array(); $Book = unserialize(file_get_contents($fname)); return $Book;

}

//Сохраняет содержимое книги на диске.

function SaveBook($fname, $Book) {

file_put_contents($fname, serialize($Book));

}

?>

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

Говорят, что заголовки (прототипы) функций ядра составляют прикладной программный интерфейс (Application Program Interface, API) ядра, а сами тела функций — реализацию этого интерфейса. Модель при этом определяет содержание системы.

Выделение низкоуровневых функций в отдельную абстракцию позволяет легко менять реализацию прикладного интерфейса в будущем, не модифицируя при этом остальных участков кода. Например, мы можем переделать гостевую книгу так, чтобы она использовала базу данных, а не файл. Для этого достаточно изменить всего лишь две функции: LoadBook() и SaveBook() (т. е. заменить реализацию API на другую). Ни Контроллер, ни, тем более, Шаблон это не затронет.

Вывод: элемент Модель (Model) позволяет прикладному коду Контроллера удобно работать с базой данных системы (в нашем случае — загружать содержимое гостевой книги с диска и сохранять его в файл). Чаще всего Модель реализуется в виде библиотеки функций (или же библиотеки классов, если используется объектноориентированный подход).

Взаимодействие элементов

Обычно взаимодействие между Моделью, Контроллером, Шаблоном и браузером пользователя изображают схемой, представленной на рис. 46.1.

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

Из схемы можно сделать следующие выводы.

Контроллер может в своей работе использовать несколько различных Шаблонов для формирования одной и той же страницы. Здесь имеется в виду как выбор Шаблона из набора (например, HTML-представление страницы, PDF-представ- ление, RTF-представление и т. д.), так и одновременное использование нескольких Шаблонов для страниц, состоящих из двух и более логических частей (например, "баннер", "новости", "карта раздела" и "текст").

Глава 46. Код и шаблон страницы

985

Шаблон 1

 

(View 1)

Модель

Контроллер

(Model)

(Controller)

 

Шаблон 2

 

(View 2)

 

...

 

Браузер

Рис. 46.1. Взаимосвязь элементов MVC

Шаблоны напрямую не взаимодействуют с Моделью, они могут получать данные только через посредничество Контроллера. Аналогично, Модель не может воздействовать на Шаблоны. В этом смысле Контроллер выступает в роли "клея" между Моделью и Шаблоном, сосредотачивая в себе бизнес-логику сценария.

С точки зрения пользователя (браузера) Шаблоны вторичны, Контроллер первичен.

Пользователь не может напрямую взаимодействовать ни с Шаблонами, ни с Моделью. Все, что он "видит," — это страницы, предоставляемые Контроллером.

Активные и пассивные шаблоны

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

Итак, вернемся немного назад и взглянем на листинг 46.3, в котором приведен код Шаблона гостевой книги. Выделим один особенный участок программы, который выглядит так:

<?foreach ($Book as $id=>$e) {?>

Имя человека: <?=$e['name']?><br>

Его комментарий:<br> <?=$e['text']?><hr>

<?}?>

Ранее мы все время ратовали за то, чтобы упростить работу дизайнера и сделать синтаксис шаблонов максимально простым и понятным. Но разве это упрощение? Посмотрите, сколько знаков препинания! Использовано почти все, что только есть

986

Часть VII. Приемы программирования на PHP 5

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

Активные шаблоны

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

Существует всего лишь один способ упростить синтаксис языка активных шаблонов: "замаскировать" инструкции foreach, if и т. д. специальными псевдотегами (которые, как это ни удивительно, гораздо лучше воспринимаются дизайнерами), чтобы код выглядел примерно так:

<foreach src="Book">

Имя человека: $name<br>

Его комментарий:<br>$text<hr>

</foreach>

Данный текст достаточно несложно автоматически перевести в обычный код на PHP, в дальнейшем исполняемый непосредственно. Сделать это может, например, специальный скрипт-транслятор на PHP, который необходимо написать.

Рассмотрим следующее важное представление активных шаблонов:

{foreach from="$Book" item="e"}

Имя человека: {$e.name}<br>

Его комментарий:<br>{$e.text}<hr>

{/foreach}

Последний пример представляет собой шаблон на языке системы Smarty, которую мы подробнее рассмотрим в гл. 47. Пока скажем только, что Smarty — очень популярный в настоящее время транслятор с языка активных шаблонов в PHP-код. Система "умеет" не только транслировать код, но также и выполнять его под собственным управлением, отслеживая многие типичные ошибки программирования.

Еще один недостаток активных шаблонов заключается в том, что их, как правило, нельзя "напрямую" открывать в браузере, минуя Контроллер. И это несмотря на то, что мы храним их в HTM-файлах. Например, при попытке открыть код листинга 46.3, просто щелкнув на файле в Проводнике, мы получим мешанину символов в прямом смысле этого слова. Те же свойства имеют и Smarty-шаблоны.

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

Пассивные шаблоны

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

Глава 46. Код и шаблон страницы

987

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

Существует множество библиотек поддержки пассивных шаблонов, например:

QuickTemplate, FastTemplate, а класс HTML_Template_IT даже входит в дистрибутив PEAR, поставляемый вместе с PHP. Давайте рассмотрим пример Шаблона с использованием последней библиотеки (листинг 46.6).

Хотя модуль HTML_Template_IT и поставляется вместе с PHP, перед началом работы его необходимо установить. А именно, если вы работаете в Windows, запустите BAT-файл go-pear.bat, расположенный в каталоге PHP. После того, как PEAR проинициализируется (это может потребовать подключения к Интернету), установите модуль командой: pear.bat install HTML_Template_IT. Также проверьте, что директива include_path

в файле php.ini содержит путь к PEAR-каталогу (например, /usr/local/php5/PEAR).

Листинг 46.6. Файл mvc/passive/view.htm

<!-- MVC. Шаблон гостевой книги (пассивный). --> <html><head><title>Гостевая книга</title></head> <body>

<h2>Добавьте свое сообщение:</h2>

<form action="controller.php" method="post">

...

</form>

<h2>Гостевая книга:</h2> <!-- BEGIN book_element -->

Имя человека: {NAME}<br>

Его комментарий:<br>{TEXT}<hr> <!-- END book_element --> </body></html>

Как видите, шаблон не содержит ни строчки кода и выглядит для дизайнера явно проще и понятнее, чем все примеры, приводившиеся до этого!

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

блоки, заданные парными тегами <!-- BEGIN имя --> ... <!-- END имя -->;

Обратите внимание, что теги выглядят, как HTML-комментарии. Это обозначение условно и специфично для HTML_Template_IT (и, кстати, большинства других систем пассивных шаблонов), мы вернемся к нему позже.

подстановки текста, выглядящие как {ПЕРЕМЕННАЯ}.

988

Часть VII. Приемы программирования на PHP 5

Для того чтобы Шаблон был корректно обработан, Контроллер должен об этом дополнительно позаботиться. В нашем примере (листинг 46.7) необходима организация цикла в коде Контроллера (опять же, пример для HTML_Template_IT).

Листинг 46.7. Файл mvc/passive/controller.php

<?php ## MVC. Контроллер системы с пассивным Шаблоном. require_once "HTML/Template/IT.php";

require_once "../model.php";

//Инициализируем систему шаблонов. $tpl = new HTML_Template_IT(".");

$tpl->loadTemplatefile("view.htm", true, true);

//Загружаем данные гостевой книги.

$Book = LoadBook("gbook.dat");

//В цикле генерируем HTML-код книги. foreach ($Book as $id=>$e) {

$tpl->setCurrentBlock("book_element"); $tpl->setVariable("NAME", $e['name']); $tpl->setVariable("TEXT", nl2br($e['text'])); $tpl->parseCurrentBlock();

}

//Выводим результат.

$tpl->show(); ?>

Мы не будем подробно рассматривать систему HTML_Template_IT, потому что в данной главе нас, прежде всего, интересуют активные, а не пассивные шаблоны.

Сравнив фрагмент кода Контроллера с кодом активного Шаблона из листинга 46.3, мы можем заметить, что цикл foreach никуда не исчез, он просто "перекочевал" из Шаблона в Контроллер. С одной стороны, это хорошо: Шаблон выглядит проще, код концентрируется только в Контроллере, как ему и положено.

Однако у систем пассивных шаблонов есть один очень значительный недостаток. Речь идет о смешении элементов дизайна и программирования в коде Контроллера. Да, конечно, напрямую Контроллер не печатает HTML, однако он полностью управляет порядком вывода различных блоков Шаблона. В частности, если мы захотим выводить четные записи гостевой книги серым цветом, а нечетные — белым (так называемая "зебра"), нам придется изменять и Контроллер (логика определения четности записи), и Шаблон (вывод соответствующего атрибута bgcolor="{COLOR}"). Более того, мы вынуждены будем указать в коде Контроллера, какие цвета следует использовать в "зебре", а это уже совсем плохо (ибо цвет — явный элемент оформления, подвластный лишь дизайнеру).

Хотя использование каскадных таблиц стилей (Cascading Style Sheets, CSS) и дает частичное решение "цветовой" проблемы, оно все же не позволяет гибко и удобно управлять порядком вывода блоков страницы.

Глава 46. Код и шаблон страницы

989

Достоинство пассивных шаблонов заключается в том, что их, как правило, можно "напрямую" открывать в браузере. Попробуйте щелкнуть в Проводнике на файле view.htm из листинга 46.6. За счет того, что управляющие конструкции HTML_Template_IT оформлены в виде HTML-комментариев, вы увидите вполне "презентабельную" страницу, в которой каждый блок будет выведен по одному разу.

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

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

Иногда говорят, что активные (pull) шаблоны позволяют разделить презентационную и бизнес-логику сценария, а пассивные (push) — код и шаблон скрипта.

Недостатки MVC

У любой медали есть оборотная сторона и, как часто бывает, от ее качества зависит довольно много. Имеется она и у MVC-схемы построения сценариев. Давайте перечислим основные недостатки и постепенно будем их исправлять. На основе рассуждений мы позже синтезируем новую, модифицированную схему.

Главная проблема — идеологическая. Что такое для пользователя "гостевая книга"? Конечно же, это прежде всего страница (View). А для разработчика сценария? Разумеется, программный код (Controller). Получается, что взгляды пользователя несколько отличаются от воззрений разработчика. Посмотрим на нашу систему "Контроллер—Шаблоны" со стороны. Что мы видим? Контроллер загружает данные из Модели, а затем обращается к Шаблонам, чтобы те их вывели. Но пользователь хочет иметь перед глазами, прежде всего, заполненные соответствующим образом Шаблоны! Итак, мы же заставляем человека явно запускать программу даже в том случае, если он имеет дело с обычной, неинтерактивной страницей без форм.

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

990

Часть VII. Приемы программирования на PHP 5

ся к этому вопросу чуть позже). Следовательно, внося в Контроллер информацию об используемых им Шаблонах, мы тем самым заставляем его содержать сведения об оформлении сайта, которые должны бы содержаться в Шаблоне (View). Итак, имеем смешение кода и оформления, с которым мы как раз пытаемся бороться.

Частное следствие: хотя Шаблон схемы MVC и является подчиненным элементом, он все же вынужден ссылаться на имя генератора данных через атрибут action тега <form> (а также, вероятно, "знать" что-то о других контроллерах сайта, чтобы иметь возможность на них ссылаться). Это вносит довольно серьезную неразбериху в процесс верстки.

Еще одно следствие: адресация страниц производится относительно URL Контроллера, а не URL Шаблона. Поясним это на примере. Представьте, что дизайнер правит Шаблон /views/gbook.htm для Контроллера /controllers/gbook.php (обычно шаблоны отделяют от контроллеров, чтобы их можно было независимо копировать и изменять). Пусть дизайнер захотел вставить на страницу картинку и "положил" GIF-файл рядом с Шаблоном — в каталог /views. Затем он вставил в HTML-код нечто вроде <img src="image.gif">, открыл, как и положено, URL http://example.com/controllers/gbook.php и с удивлением обнаружил, что на месте картинки ему показывается "крестик". Но удивляться, по сути, нечему: ведь в момент работы шаблона браузер пользователя находится в каталоге /controllers, т. е. там же, где и скрипт Контроллера (вспомните, что Контроллер единолично решает, какой Шаблон ему использовать, и подгружает соответствующий файл). Но откуда дизайнеру на этапе верстки знать, где в недрах дерева сайта затерялась программа-контроллер? И куда же ему, наконец, положить свою картинку?..

Использование абсолютных путей типа /img/image.gif не всегда подходит, например, не всегда хочется "захламлять" каталог картинок изображениями, которые не будут использоваться нигде, кроме как в определенном разделе сайта. Кроме того, в некоторых случаях "привязка" к абсолютному пути вообще нежелательна (например, если скрипт может бесконтрольно копироваться в другие каталоги).

Контроллер единолично определяет порядок вывода различных Шаблонов, если страница имеет блочную структуру. Пример такой страницы мы уже обсуждали выше ("баннер сверху, новости справа, меню слева, текст в центре"). И хотя мы можем легко менять внешний вид отдельных Шаблонов-блоков, у нас не получится переставлять их в другом порядке, не изменяя кода Контроллера (например, переставить меню в правую колонку, а баннер — вообще убрать).

Четвертый способ: компонентный подход

Как вы думаете, кто из команды разработчиков менее сообразителен в технических вопросах: программист или дизайнер/верстальщик?.. Конечно же, дизайнер. А потому программист должен стараться всячески упрощать ему жизнь. Например, раз хочет дизайнер поместить картинку в тот же каталог, что и Шаблон (или даже скопировать Шаблон страницы под другим именем в надежде, что он заработает) — быть по сему.

Глава 46. Код и шаблон страницы

991

URL страниц сайта — это во многом элемент, определяемый фантазией дизайнера.

Если дизайнер хочет, чтобы подраздел "Новости сайта"

находился в разделе

"О компании", он волен сменить его URL, например, с /news на /company/news (в "идеальной" системе управления сайтом он смог бы это сделать, просто перетащив соответствующий элемент мышью). И вообще, карту сайта составляет дизайнер (еще на самой начальной стадии разработки проекта), а не программист. Дизайнеру же подвластны и Шаблоны сайта.

Блочная структура Web-страниц

Давайте подробно рассмотрим одну очень важную закономерность построения Webстраниц. (Кстати, мы уже вскользь касались ее выше.) Речь идет о блочной структуре большинства страниц (иногда употребляется фраза "компонентная структура контента"). Блоки могут иметь самую разную природу и содержать различные данные. Вот несколько примеров часто встречающихся блоков:

баннер;

новости сайта;

меню раздела;

основной текст страницы;

форма регистрации (ввод имени пользователя и пароля);

"хлебные крошки" — перечень имен "родительских" по отношению к текущей странице разделов. По каждому из таких имен можно щелкнуть мышью, чтобы перейти в соответствующий раздел.

Мы рассматриваем здесь блоки как с точки зрения данных, которые в них содержатся, так и с точки зрения их оформления. Но внешний вид блоков, конечно же, должен задаваться отдельными Шаблонами, которые можно в любой момент изменить. Итак, договоримся в дальнейшем называть Блоком некоторый автономный участок страницы (или Шаблона), а Компонентом — соответствующие ему данные и код, который их генерирует.

Иными словами, понятие Компонент носит программистский характер, а понятие Блок — скорее дизайнерский. Компонент иногда называют источником данных (data source) или

генератором данных.

Проблема расположения Блоков на каждой странице сайта — вопрос исключительно дизайна, но не программирования. Дизайнер должен иметь возможность "перекомпоновать" страницы с целью изменения порядка Блоков, а также добавления новых и удаления ненужных.

Итак, с концептуальной точки зрения Шаблон страницы выглядит как изначально пустой "холст", "палитра" (терминология из различных прикладных систем, поддерживающих визуальное редактирование данных). На нее "набрасываются" (в идеале — перетаскиваются мышью) различные элементы оформления, ссылки и, что самое главное, сведения о том, в каких Компонентах нуждается данный Шаблон. То есть

992

Часть VII. Приемы программирования на PHP 5

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

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

В различной литературе Компоненты и Блоки фигурируют под разными названиями. Например, в портальных Java-системах сходные сущности иногда называют "портлетами". Мы не будем пользоваться этим термином, потому что он во многом специфичен для Java; сейчас же речь о PHP. Кроме того, необходимо понимать, что с точки зрения MVC Компонент представляет собой некоторый частный Контроллер, только он генерирует не содержание всей страницы сразу, а лишь некоторого ее "кусочка" (Блока). В этом смысле Компоненты можно называть "Контроллерами компонентной модели". Компонентный подход также иногда называют просто портальной технологией.

Взаимодействие элементов

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

положение страниц в дереве сайта, а также их взаимодействие между собой посредством гиперссылок (в том числе ссылок на изображения);

состав и порядок Блоков, используемых на странице;

используемые данным Шаблоном Компоненты.

Изобразим новую схему взаимодействия элементов компонентного подхода (рис. 46.2).

Мы видим, что, как и ожидалось, внимание переключилось с Контроллера на Шаблон. Давайте рассмотрим другие особенности схемы.

Один и тот же Шаблон в своей работе волен использовать несколько независимых Компонентов. Например, первый Компонент может генерировать меню текущего раздела сайта, второй — блок новостей, а третий — главный текст страницы. Это достаточно естественно: страницы как раз обычно и состоят из разнородных Блоков, каждому из которых соответствует свой Компонент.

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

С точки зрения пользователя (браузера) Шаблон первичен, Компоненты вторичны и выступают в виде кода, предоставляющего некоторые данные Шаблону.

Пользователь не может напрямую взаимодействовать ни с Компонентами, ни с Моделью. Все, что он "видит", — это страницы, предоставляемые Шаблоном.

Элемент Модель компонентного подхода и элемент Модель MVC идентичны.

Глава 46. Код и шаблон страницы

993

Компонент 1

(Component 1)

Шаблон

(View)

Модель

(Model)

Компонент 2 (Component 2)

...

Браузер

Рис. 46.2. Взаимосвязь элементов компонентного подхода

К вопросу о первичности Шаблона. Представьте, что вы сидите перед монитором и пытаетесь попасть мышью в полосу прокрутки окна. С чем вы тогда общаетесь — с окном и линейкой прокрутки, или же с той программой, которая обрабатывает движения курсора, перерисовывает линейку прокрутки и подкачивает данные с диска? Иными словами, на интуитивном уровне вы ведь "разговариваете" именно с интерфейсом объекта (Шаблоном), а не с его "внутренностями" (Контроллером или Компонентом), не так ли? Так что идея использования Шаблона в качестве первичного элемента вполне естественна.

Но позвольте, довольно сухой теории! Проиллюстрируем, как можно реализовать нашу гостевую книгу с использованием компонентного подхода. Мы приведем код Шаблона книги и ее единственного Компонента. Что касается Модели, то она не меняется, и ее код уже был приведен в листинге 46.5.

Шаблон (View)

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

http://example.com/comp/view.htm

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

Тут вы можете оставить комментарий к выбранному абзацу или сообщить об ошибке.

Оставленные комментарии видны всем.