Построение приложения на базе Zend Framework. Часть 1. Zend_Controller, Zend_Db, Zend_Layout
|
Опубликовано: 08.06.2008
|
- Введение
- Старт
- Общий алгоритм работы сайта
- Zend_Loader
- Zend_Registry
- Zend_Config
- Управление маршрутами Zend_Controller_Router_Rewrite
- Front контроллер
- Работа с исключениями и обработчик ошибок
- Работа с контроллером
- Работа с моделью
- Работа с базой данных
- Работа с видом Zend_View и Zend_Layout
- Заключение
Данная статья является первой частью серии статей под названием "Построение приложения на базе Zend Framework". Каждая часть сопровождается кодом. Ранее эта статья была доступна под названием "Построение простого сайта на базе Zend Framework (с кодом)".
Если вы работаете с Zend Framework 1.8.0 и старше прочтите это.
В тексте затронуты следующие вопросы:
- Паттерн MVC - на основе Zend_Controller, Zend_View, Zend_Layout и Zend_Db
- Управление конфигурационными файлами - Zend_Config
- Работа с базой данных - Zend_Db, Zend_Db_Select, Zend_Db_Table
- Отделение представления (вида) с помощью системы шаблонов - Zend_View
- Двухэтапное представление - Zend_Layout
- Управление маршрутами - Zend_Controller_Router_Rewrite
- Автоматическая загрузка классов - Zend_Loader
- Работа с исключениями и обработчик ошибок
Для получения базовых навыков прочтите статью. Как и в той статье, мы будем располагать php файлы вне document root директории (за исключением точки входа index.php), для обеспечения большей безопасности.
Итак, скачиваем последнюю стабильную версию фреймворка, разархивируем и выбираем классы для работы. Почти все классы Zend Framework можно использовать независимо, поэтому выберем только нужные. Вот так выглядит наш список файлов и папок библиотеки Zend:

А вот так выглядит дерево папок сайта, в нашем случае папка public это document root :

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

С помощью файла .htaccess все запросы к сайту перенаправляются на входную точку - файл index.php лежащий в корне document root.
RewriteEngine on
RewriteCond %{SCRIPT_FILENAME} !-f
RewriteCond %{SCRIPT_FILENAME} !-d
RewriteRule ^(.*)$ index.php/$1
Не перенаправляются только запросы на реально существующие файлы.
Рассмотрим код файла index.php
<?php
// Подключаем файл настроек
require '../application/settings/config.php';
// Создаем строку путей
$paths = implode(PATH_SEPARATOR,
array (
$config['path']['library'],
$config['path']['models'],
$config['path']['system']
));
// Пути по которым происходит поиск подключаемых файлов, это папка библиотек, моделей и системных файлов
set_include_path($paths);
// Подключение загрузчика
require 'Bootstrap.php';
// Запуск приложения
$bootstrap = new Bootstrap();
$bootstrap->run($config);
Так как мы не хотим каждый раз указывать полный путь к нужным файлам - задаем set_include_path. Подключаемые файлы это библиотеки Zend, системные классы, а также классы модели. Но не стоит указывать слишком много путей, иначе это может отразиться на производительности. Также старайтесь что бы путь к библиотекам Zend шел первым. index.php запускает $bootstrap->run() - основной метод нашего загрузочного класса Bootstrap.php. Этот класс предназначен для инициализации и настройки окружения. Приведу его код полностью:
<?php
require 'Zend/Loader.php';
class Bootstrap
{
/**
* Объект конфигурации
*
* @var Zend_Config
*/
private $_config = null;
/**
* Запуск приложения
*
* @var array $config Конфигурация
*/
public function run($config)
{
try {
// Настройка загрузчика
$this->setLoader();
// Настройка конфигурации
$this->setConfig($config);
// Настройка Вида
$this->setView();
// Подключение к базе данных
$this->setDbAdapter();
// Подключение маршрутизации
$router = $this->setRouter();
// Создание объекта front контроллера
$front = Zend_Controller_Front::getInstance();
// Настройка front контроллера, указание базового URL, правил маршрутизации
$front->setBaseUrl($this->_config->url->base)
->throwexceptions(true)
->setRouter($router);
// Запуск приложения, в качестве параметра передаем путь к папке с контроллерами
Zend_Controller_Front::run($this->_config->path->controllers);
}
catch (Exception $e) {
// Перехват исключений
Error::catchException($e);
}
}
/**
* Настройка загрузчика
*/
public function setLoader()
{
// Запуск автозагрузки
Zend_Loader::registerAutoload();
}
/**
* Настройка конфигурации
*
* @param array $config Настройки
*/
public function setConfig($config)
{
$config = new Zend_Config($config);
$this->_config = $config;
Zend_Registry::set('config', $config);
}
/**
* Настройка вида
*/
public function setView()
{
// Инициализация Zend_Layout, настройка пути к макетам, а также имени главного макета.
// Параметр layout указан лишь для примера, по умолчанию имя макета именно "layout"
Zend_Layout::startMvc(array(
'layoutPath' => $this->_config->path->layouts,
'layout' => 'layout',
));
// Получение объекта Zend_Layout
$layout = Zend_Layout::getMvcInstance();
// Инициализация объекта Zend_View
$view = $layout->getView();
// Настройка расширения макетов
$layout->setViewSuffix('tpl');
// Задание базового URL
$view->baseUrl = $this->_config->url->base;
// Задание пути для view части
$view->setBasePath($this->_config->path->views);
// Установка объекта Zend_View
$layout->setView($view);
// Настройка расширения view скриптов с помощью Action помощников
$viewRenderer = new Zend_Controller_Action_Helper_ViewRenderer();
$viewRenderer
->setView($view)
->setViewSuffix('tpl');
Zend_Controller_Action_HelperBroker::addHelper($viewRenderer);
}
/**
* Установка соединения с базой данных и помещение его объекта в реестр.
*/
public function setDbAdapter()
{
// Подключение к БД, так как Zend_Db "понимает" Zend_Config,
// нам достаточно передать специально сформированный объект конфигурации в метод factory
$db = Zend_Db::factory($this->_config->db);
// Задание адаптера по умолчанию для наследников класса Zend_Db_Table_Abstract
Zend_Db_Table_Abstract::setDefaultAdapter($db);
// Занесение объекта соединения c БД в реестр
Zend_Registry::set('db', $db);
}
/**
* Настройка маршрутов
*/
public function setRouter()
{
// Подключение файла правил маршрутизации
require($this->_config->path->system . 'routes.php');
// Если переменная router не является объектом Zend_Controller_Router_Abstract,
// выбрасываем исключение
if (!($router instanceof Zend_Controller_Router_Abstract)) {
throw new Exception('Incorrect config file: routes');
}
return $router;
}
}
Практически каждая строчка откомментирована и уже сейчас вы можете полностью представить себе схему работы.
Хочу отметить, что существует другой вариант настройки окружения для работы сайта - . Вместо класса Bootstrap мы могли бы создать плагин с названием вроде My_Plugin_Initialize и в нем использовать перехватчик routeStartup для настройки окружения. Получился бы такой же результат.
Далее, предположим мы обратились к следующей странице нашего сайта http://ilovezf/pages/1/ В этом случае будет вызван IndexController и его действие PageAction. (Такое поведение происходит потому что я добавил специальное правило в файл маршрутов routes.php, по умолчанию по такому запросу вызовется контроллер PagesController и действие 1, конечно если они существуют)
В контроллере происходит подключение модели и получение данных необходимых запрошенной странице. Затем происходит определение переменных для вида.
Наконец в главный макет application/views/scripts/layout.tpl подставляются нужные нам данные и весь полученный результат отправляется в браузер.
Далее я более подробно остановлюсь на ключевых моментах построения нашего приложения.
Zend_Loader
Этот класс позволяет использовать одну из замечательных возможностей php5 - автоматическую загрузку объектов, иначе называемую autoload. Если вы пишите ООП приложение то у вас, должно быть, не один десяток различных классов, и на некоторых страницах нужно подключить 5-10 классов. Если таких страниц много, то это очень неприятно. С Zend_Loader вам достаточно всего лишь указать название класса в нужном формате, и он сам произведет подключение.
То есть, в коде например пишем $news = new Text_News_Home();
Символ "_" используется как разделитель, Zend_Loader будет пытаться найти указанный класс по адресу Text/News/Home.php. (В рассматриваемом примере в index.php я указал относительно каких директорий будет происходить поиск)
Для автоматической загрузки можно использовать метод Zend_Loader::loadClass() или же выполнять Zend_Loader::registerAutoload(); что бы не писать каждый раз Zend_Loader::loadClass(). Правда registerAutoload() требует наличие модуля spl_autoload. С другими подходами к автозагрузке классов вы можете ознакомиться в статье. К сожалению, такая гибкость в подгрузке классов идет в ущерб производительности, если вопрос производительности стоит для вас очень остро, рекомендую ознакомится с к мануалу.
Zend_Registry
Класс реализует паттерн singleton и таким образом позволяет нам обращаться к нужным объектам из любого места кода. Это альтернатива использованию глобальных переменных. Класс оперирует реестром, в который заносятся объекты. Ключевые методы класса это get и set, которые заносят и получают объекты в реестр соответственно. В нашей статье в реестр заносятся объекты соединения с БД и объект конфигурации. Также важен метод isRegistery проверяющий существование объекта в реестре.
Zend_Config
Класс позволяет в ООП стиле работать с конфигурационными файлами. Поддерживаются конфигурационные файлы различного формата: php-массив, ini - файл, xml - файл. Для рассматриваемого примера я выбрал самый простой вариант - хранить настройки в php массиве. Вы можете использовать любой. В объекте Zend_Config все данные представлены в виде ассоциативного массива. Например таким образом: $config->path->system получаем значения физического пути папки с системными файлами.
Например вот так может выглядеть ваш конфигурационный файл:
<?php
$config = array (
// Настройки соединения с БД
'db' => array (
'adapter' => 'PDO_MYSQL',
'params' => array(
'host' => 'localhost',
'username' => 'root',
'password' => 'xxxx',
'dbname' => 'ilovezf',
'driver_options'=> array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES UTF8'),
'profiler' => false,
),
),
);
Управление маршрутами Zend_Controller_Router_Rewrite
Маршрутизация - это процесс принятия URI (той части URI, которая идет после базового URL) и ее разложения на части для определения того, какой контроллер и какое действие этого контроллера должны получить запрос и выполниться. Другими словами процесс маршрутизации дает нам возможность сопоставить адрес запрошенной страницы с конкретными контроллерами и действиями в нашем коде. С помощью Zend_Controller_Router_Rewrite можно задавать различные правила маршрутизации.
<?php
$router = new Zend_Controller_Router_Rewrite();
$router->addRoute('articles',
new Zend_Controller_Router_Route('articles/:articleId',
array('controller' => 'articles', 'action' => 'view'))
);
$router->addRoute('pages',
new Zend_Controller_Router_Route('pages/:pageId',
array('controller' => 'index', 'action' => 'page'))
);
Благодаря этим правилам мы добиваемся, например, того, что по запросу http://ilovezf/articles/1/ будет вызван контроллер articlesController и действие viewAction, кроме того, значение идентификатора статьи (в указанном запросе это 1) будет доступно по параметру articleId. Кроме Zend_Controller_Router_Route который используется для динамических маршрутов, есть еще Zend_Controller_Router_Route_Static для постоянных, неизменных маршрутов, например для URL типа http://ilovezf/exit/. Также существует Zend_Controller_Router_Route_Regex основанный на регулярных выражениях, это самый гибкий способ определения маршрутов.
Front контроллер
Zend_Controller_Front реализует паттерн Front Controller, используемый в приложениях MVC. Его назначение состоит в инициализации окружения запроса, прокладывании маршрута приходящего запроса и последующем запуске выявленных действий. Он агрегирует все ответы и возвращает их по завершении процесса. Другими этот класс выполняет роль некоторого директора управляющего всеми процессами приложения:
// Создание объекта front контроллера
$front = Zend_Controller_Front::getInstance();
// Настройка front контроллера, указание базового URL, правил маршрутизации
$front->setBaseUrl($this->_config->url->base)
->throwexceptions(true)
->setRouter($router);
// Запуск приложения, в качестве параметра передаем путь к папке с контроллерами
Zend_Controller_Front::run($this->_config->path->controllers);
Работа с исключениями и обработчик ошибок
Zend Framework предлагает несколько вариантов работы с исключениями. Во первых это плагин Zend_Controller_Plugins_ErrorHandler, он дает возможность перехватывать исключения вызванные отсутствием контроллера или действия, кроме того он перехватывает исключения выброшенные в контроллерах. Никакие другие исключения, например исключения маршрутизации, или исключения выброшенные в плагинах он не перехватывает. Этот плагин хорош именно для работы с ошибками типа 404, он позволяет в рамках общей структуры макетов отобразить страницу 404 с необходимыми сообщениями. За плагин отвечает контроллер ErrorController и действие errorAction.
Для того же что бы все таки обрабатывать остальные ошибки в рассматриваемом примере используется еще один обработчик ошибок /application/system/Error.php.
<?php
class Error
{
/**
* Управление ошибками
*
* @param exception $exception Перехватываемое исключение
*/
public static function catchException(Exception $exception)
{
// Получение текста ошибки
$message = $exception->getMessage();
// Получение трейса ошибки как строки
$trace = $exception->getTraceAsString();
$str = 'ERROR: ' . $message . "\n" . $trace;
$config = Zend_Registry::get('config');
// Если включен режим отладки отображаем сообщение о ошибке на экран
if($config->debug->on) {
Zend_Debug::dump($str);
}
// Иначе выводим сообщение об ошибке
else {
// Здесь может происходить логирование ошибки, уведомление вебмастера и т д
die('System error! Please try later');
}
}
}
В Bootstrap.php с помощью try и catch все ошибки перехватываются и передаются этому обработчику. Далее в зависимости от настроек режима дебага в конфигурационном файле, либо показывается информация об ошибке в браузере, либо выдается сообщение о временных неполадках.
Для того что бы все ошибки, включая ошибки не найденных контроллеров и действий, перехватывать своим обработчиком, необходимо при настройке front контроллера выполнить $front->throwexceptions(true).
Работа с контроллером
Контроллер является связующим звеном между видом и моделью. Основная задача контроллера это проверить данные что пришли от пользователя, выполнить те или иные действия с данными, используя методы модели, и наконец, определить переменные для вида. Для иллюстрации привожу откоментированный код контроллера ArticlesController
<?php
class ArticlesController extends Zend_Controller_Action
{
/**
* Список статей
*/
public function indexAction()
{
$modelArticles = new Articles();
$articles = $modelArticles->getArticles();
$this->view->articles = $articles;
}
/**
* Выбранная статья
*/
public function viewAction()
{
// Получение параметра пришедшего от пользователя
$articleId = $this->_getParam('articleId');
// Создание объекта модели, благодаря autoload нам нет необходимости подключать класс через require
$modelArticles = new Articles();
// Выполнения метода модели по получению информации о статье
$article = $modelArticles->getArticles($articleId);
// Определение переменных для вида
$this->view->article = $article;
}
}
Работа с моделью
Работа с моделью практически не освещена в мануале, в файловой структуре предполагается папка models но не дается конкретных рекомендаций по ее использованию. И это не спроста, на самом деле отвечать за реализацию этой части MVC должен сам разработчик. Я рассматриваю модель как набор классов сохраняющих состояние системы и оперирующих с данными, это может быть получение данных, изменения данных, различные манипуляции и вставка данных в базу данных или другое хранилище. Я не допускаю составления и выполнения sql-запросов в классах контроллеров. Такие модели называются толстыми. О толстых моделях и худых контроллерах вы можете прочесть здесь. В рамках текущей статьи суть модели сводится к получению данных из базы. В рассматриваемом сайте-примере существует два класса модели, это Pages и Articles. В первом классе собраны функции работающие с сущностью "страница", во втором с сущностью "статьи". Как вы видите названия классов моделей отражают суть данных которыми они оперируют. Классы модели являются наследниками Zend_Db_Table_Abstract. Методы работы с базой и пример класса модели смотрите ниже.
Работа с базой данных
Для гибкой работы с БД Zend Framework предлагает несколько классов. Нам понадобятся Zend_Db_Table, Zend_Db_Select, Zend_Db_Table_Select. В Bootstrap.php задается соединение с БД, задается адаптер используемый по умолчанию.
// Подключение к БД, так как Zend_Db "понимает" Zend_Config,
//нам достаточно передать специально сформированный объект конфигурации в метод factory
$db = Zend_Db::factory($this->_config->db);
// Задание адаптера по умолчанию для наследников класса Zend_Db_Table_Abstract
Zend_Db_Table_Abstract::setDefaultAdapter($db);
// Занесение объекта соединения c БД в реестр
Zend_Registry::set('db', $db);
На самом деле создание экземпляра класса адаптера не приводит к немедленному соединению с сервером базы данных. Адаптер сохраняет параметры соединения и производит подключение, когда нужно произвести первый запрос к базе данных. Это значит, что само по себе создание объекта адаптера производится быстро и занимает мало ресурсов
Отмечу что если вы используете Zend_Cache вам пригодится метод Zend_Db_Table_Abstract::setDefaultMetadataCache, с его помощью вы можете закешировать метаданные таблиц. Таким образом запрос describe table не будет выполнятся при каждом создании объекта Zend_Db_Table.
Далее вся работа с базой данных у нас будет собрана в классах моделей.
Для примера рассмотрим код класса Articles
<?php
class Articles extends Zend_Db_Table_Abstract
{
// Имя таблицы
protected $_name = 'articles';
/**
* Получить все статьи или одну
*
* @param int $articleId Идентификатор статьи
* @return array
*/
public function getArticles($articleId = null)
{
// Создаем объект Zend_Db_Select
$select = $this->getAdapter()->select()
// Таблица из которой делается выборка
->from($this->_name)
// Добавление таблицы с помощью join, указывается поле связи
->join('users','users.id = articles.author_id',array('name'))
// Порядок сортировки
->order('id DESC')
// Количество возвращаемых записей
->limit(2)
;
if (!is_null($articleId)) {
// Условие на выборку
$select->where("articles.id = ?",$articleId);
// Выполнение запроса
$stmt = $this->getAdapter()->query($select);
// Получение данных в виде объекта, по умолчанию в виде ассоциативного массива
$result = $stmt->fetch(Zend_Db::FETCH_OBJ);
}
else {
$stmt = $this->getAdapter()->query($select);
// Получение данных в виде массива объектов, по умолчанию в виде массива ассоциативных массивов
$result = $stmt->fetchAll(Zend_Db::FETCH_OBJ);
}
return $result;
}
}
Класс Articles наследует абстрактный класс Zend_Db_Table_Abstract. Таким образом, мы получаем возможность использовать его функционал по работе с базой. Zend_Db_Table реализует паттерн Table Data Gateway. Идея в том что для каждой таблицы создается отдельный класс который предназначен для работы именно с этой таблицей. Таким образом, работа с БД делится на логические части, с которыми, в случае большого объема проекта, работать гораздо удобнее. Например, вся работа со статьями в сайте-примере собрана в классеArticles, а работа со страницами собрана в классе Pages.
Все что нам нужно, это указать название таблицы с которой мы работаем: protected $_name = 'articles';
Для различных сложных выборок из базы используется класс Zend_Db_Select, он предоставляет гибкий функционал для выполнения select запросов. Особенно отмечу объектно-ориентированный подход "покусочного" построения sql-запросов. Например:
$select = $this->getAdapter()->select()
->from('articles')
->where("articles.id = ?",$articleId)
->join('users', 'users.id = articles.author_id', array('name'))
->order('id DESC')
->limit(2);
"Куски" могут располагаться в произвольном порядке, это очень удобно когда у вас запросы строятся динамически.
Для защиты от sql-инъекций в select запросах предлагается механизм плейсхолдеров where('id = ?',$pageId);
В случае же update, delete запросов использующих условие where c данными полученными от пользователя, необходимо поступать немного по другому. Для защиты от sql-инъекций в таких случаях нужно экранировать спец символы и брать данные в кавычки самому. Я использую следующий метод:
$id = $this->getAdapter()->quote($id);
Если запросы на выборку у вас достаточно простые и не требуют данных из других таблиц, вы можете использовать методы fetchRow, fetchAll класса Zend_Db_Table_Select (Вы также можете использовать аналогичные методы класса Zend_Db_Table но они являются устаревшими и к использованию более не рекомендуются), пример:
<?php
/**
* Работа с страницами
*/
class Pages extends Zend_Db_Table_Abstract
{
// Имя таблицы
protected $_name = 'pages';
/**
* Получить одну страницу (альтернативой этому методу может быть встроенный метод find)
*
* @param int $pageId Идентификатор страницы
* @return array
*/
public function getPage($pageId)
{
// Создание объекта Zend_Db_Table_Select,
// Нам не нужно указывать название таблицы как в Zend_Db_Select
$select = $this->select()
// Накладываем условие
->where('id = ?', $pageId);
// Выполняем запрос и получаем объект Zend_Db_Table_Row в результате
// Нам не нужно предварительно выполнять запрос методом query, как в Zend_Db_Select
$result = $this->fetchRow($select);
return $result;
}
}
Итого, если вам необходимы запросы на вставку, удаление, изменение данных - нужно использовать Zend_Db_Table. Если вам необходимы простые запросы на выборку данных результатом которых есть данные только одной таблицы - можно использовать Zend_Db_Table_Select (а можно и Zend_Db_Select). Если же вам нужны сложные запросы с получением данных из других таблиц - лучше использовать Zend_Db_Select.
Работа с видом Zend_View и Zend_Layout
Для работы с видом Zend Framework предлагает два класса, это Zend_View и Zend_Layout.
При создании объектов этих классов в Bootstrap.php я произвел небольшую настройку. Изменил расширение этих файлов на привычное tpl.
Вот так выглядит скрипт вида, отвечающий за список статей.
<ul>
<?php foreach ($this->articles as $article): ?>
<li><a href = "<?php echo $this->url(array('articleId' => $article->id),'articles'); ?>">
<?php echo $article->title; ?></a></li>
<?php endforeach; ?>
</ul>
PHP код здесь отделен от html кода, кроме того используется pascal-подобный синтаксис php для лучшего визуального восприятия.
Zend_Layout реализует паттерн "двухэтапное представление" (Two Step View pattern). Данный паттерн предназначен для гибкой работы с макетами вида. Создается некий главный макет дизайна сайта. Обычно он содержит каркас html страницы
// Doctype
<html>
<head>
// Мета теги
// Подключение css и т д
</head>
<body>
...
</body>
</html>
А вот все что находится между <body></body> разбивается на блоки, причем у каждого блока есть свой маленький макет. Это может быть меню, новости, блок авторизации и т д. Таким образом, если мы хотим изменить html код меню, мы меняем всего лишь один макет. Вот так выглядит код главного макета index.tpl
<?php echo $this->doctype(Zend_View_Helper_Doctype::XHTML1_TRANSITIONAL); ?>
<html>
<head>
<?php echo $this->headTitle('I LOVE ZEND FRAMEWORK!'); ?>
<?php echo $this->headMeta()->appendHttpEquiv('Content-Type', 'text/html; charset=UTF-8'); ?>
<?php echo $this->headLink()->appendStylesheet($this->baseUrl.'public/design/css/style.css'); ?>
<?php echo $this->headScript(); ?>
</head>
<body>
<div id="menu">
<?php echo $this->partial('menu.tpl'); ?>
</div>
<div id="main">
<div id="left">
<div style = "margin:5px;">Лучшие статьи:</div>
<?php echo $this->action('index', 'articles'); ?>
<div id = "copy">(c) San <a href = "http://zendframework.ru">http://zendframework.ru</a></div>
</div>
<div id="content"><?php echo $this->layout()->content; ?></div>
</div>
</body>
</html>
Кроме двухэтапного представления Zend_Layout дает помощников. Это doctype, headTitle, headMeta, headLink, headScript. Эти помощники выручают если например на какой то особой странице вы захотите подключить особый css или js файл.
С помощью помощника partial можно подключать другие макеты - блоки, например в вышеприведенном коде так подключается меню. Ключевое отличие между отображением partial и обычного скрипта вида (render()) в том что partial скрипты имеют свою собственную область видимости. Они будут видеть только те переменные, что были переданы непосредственно им. Если же в этом нет необходимости, с точки зрения производительности, лучше использовать render(). Подробнее с помощниками вида вы можете ознакомиться в статье
Кроме того можно вызывать различные действия прямо из макета с помощью помощника action(). Это дает нам возможность разбить весь шаблон сайта на блоки. Таким образом, я подключаю блок "Лучшие статьи", и когда я захочу убрать его со всех страниц, я удалю только одну строчку, а если я захочу изменить его формат, я отредактирую всего один view скрипт. Однако я рекомендую для этих целей использовать собственные помощники вида которые будут напрямую обращаться к модели, в обход контроллера. Пример такого помощника можно найти по ссылке
Также отмечу помощник Zend_View_Helper_Url который используется для построения URL на основании маршрутов. Теперь вам не нужно жестко вписывать ваши адреса в шаблоны. Достаточно сделать вызов вроде: <?php echo $this->url(array('articleId' => $article['id']),'articles'); ?> и вы получите корректный адрес вашей статьи. И самое главное, когда вы захотите что либо изменить в ваших URL, например для улучшения SEO эфекта, вам не прийдется править все ваши шаблоны. А только настроить маршруты.
Возможно в качестве шаблонизатора вы захотите использовать Smarty или что-нибудь в этом роде. Интеграция с Zend Framework не вызовет большого труда. Но хочу отметить что Zend_View и Zend_Layout уже сами по себе отличный шаблонизатор, причем не требующий использования нового специфического языка шаблонизатора.
Скачать код без библиотек Zend (17 kb)
Скачать код с библиотеками Zend (418 kb)
Для запуска кода необходимо:
- Не забыть что Zend Framework требует php 5.1.4 или выше
- Установить расширение PDO с поддержкой драйвера mysql (если оно не установлено)
- Скачать архив, разархивировать его, document root виртуального хоста должен указывать на папку public. Обязательно убедитесь что у вас установлен модуль mod_rewrite веб сервера apache, если он не установлен переходя по ссылкам вы будете получать 404 ошибку.
- Создать базу данных в кодировке UTF-8
- Выполнить sql dump расположенный в корне архива
- Отредактировать конфигурационный файл application/settings/config.php Необходимо указать название БД, имя пользователя и пароль
Если же у вас нет возможности создать отдельный виртуальный хост для сайта вы можете попробовать запустить сайт в папке. Для этого вам прийдется выполнить следующие действия:
- Разместите код в нужной папке;
- Отредактируйте applications/settings/config.php изменив $baseUrl = '/'; на $baseUrl = '/dir_name/public/'; с обязательным слешом в конце. dir_name это название вашей директории;
- После этих настроек сайт станет доступен по адресу /dir_name/public/
Результат работы разработанного сайта примера можно увидеть по адресу http://ilovezf.demo.zendframework.ru
Скриншот главной страницы сайта примера:

P.S.
Весь вышеизложенный подход построения приложения на базе Zend Framework есть субъективным мнением автора и не претендует на полную объективность и оптимальность. Главной целью данной статьи было показать, как можно использовать Zend Framework для построения web приложения. Не весь код приведенный в статье присутствует именно в таком виде в архиве, в некоторых местах в статье он не существенно изменен, для лучшей наглядности. В общем не забывайте что у вас своя голова на плечах.
P.P.S.
Приношу благодарность Grisha Kostyuk aka naspeh за помощь в написании статьи.
P.P.P.S.
Если статья показалась вам полезной, вы можете помочь развитию русскоязычному сообществу zend framework, поставив ссылку на ресурс http://zendframework.ru. В следующей статье серии я рассмотрю работу с Zend_Form и связанных с нею компонент.
- Спрашивать почему у вас не работает код, для этого есть тема форума закрепленная за статьей.
- Спрашивать как реализовать ту или иную функциональность, для этого необходимо создать свою тему на форуме.
Комментарии для того чтобы: высказать свое аргументированное мнение о статье, указать какие участки вызывают непонимание, что нужно исправить/улучшить, просто сказать спасибо.
Комментарии имеют древовидную структуру.
Если вы хотите ответить на определенный комментарий - нажмите на ссылку "Ответить" возле этого комментария.

ZFConf 2010
