framework.zend.com
Stable релиз 2.0 / 1.12

Внедрение модульной структуры. На базе первой части туториала.

К комментариям

Содержание

Введение

В связи с частыми случаями возникновения проблем в понимании и внедрении в свои проекты предлагаемой Zend Framework (ZF) модульной структуры приложения у людей начинающих осваивание ZF, было решено написать краткую инструкцию по первоначальной настройке приложения для работы в «модульном» контексте.

В качестве основы для этого мини руководства, был взят проект из статьи «Построение приложение на базе Zend Framework. Часть 1», написанной Александром Махометом для первоначального знакомства с Фреймворком на реальном проекте с зачатками функционала без которого не обходится ни одно современное Интернет приложение. Поэтому предполагается, что читателем уже изучена эта статья, код, прилагающийся к ней, установлен и корректно работает. Скачать его можно по ссылке.

Приложение было протестировано на Zend Framework 1.7.1, 1.6.1 и 1.5. php 5.2.4, mysql 5.0.45-community-nt, Apache/2.2.4

Проблема терминологии

Понятия «модульной структуры приложения» в целом и «модуля» в частности определить не так легко как это может показаться на первый взгляд. Вот как звучит определение модульной структуры в официальном руководстве к Zend Framework:

Определенная соглашением модульная структура директорий позволяет разделять различные приложения MVC в автономные единицы и повторно использовать их с различными фронт-контроллерами.

Пример такой структуры из руководства:


docroot/
index.php
application/
default/
controllers/
IndexController.php
FooController.php
models/
views/
scripts/
index/
foo/
helpers/
filters/
blog/
controllers/
IndexController.php
models/
views/
scripts/
index/
helpers/
filters/
news/
controllers/
IndexController.php
ListController.php
models/
views/
scripts/
index/
list/
helpers/
filters/


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

Однако, с другой стороны, на практике в разработке веб-приложений часто используется подход разделения функционала по принципу front-end/backend, когда управляющий код отделяется от остального (часто даже разрабатывается другой командой разработчиков). Такой же точно подход нередко применяется и при разработке проектов на основе ZF – весь backend проекта (часто так называемая «админ часть») оформляется в виде отдельного модуля, и содержит контроллеры, которые отвечают за "административную часть" модулей. Достоинство такого подхода – простота и очевидность реализации (не требуется никаких дополнительных действий), своеобразная «привычность» такой схемы. Недостаток – необходимость совершать массу дополнительных телодвижений при желании перенести код какого-то конкретного модуля целиком в другой проект.

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

Постановка задачи

Итак, в проекте из статьи «Построение простого сайта на базе Zend Framework» для демонстрации базовых возможностей создано два контроллера: один для статей (ArticlesController) и ещё один - контроллер по умолчанию (IndexController), в котором реализован функционал по выводу статических страниц из базы (pageAction). Мы создадим на нашем простом сайте модуль блога (blog), в который поместим контроллер статей (ArticlesController) и для наглядности добавим контролер «авторов статей» (AuthorsController), чтобы как-то обосновать отделение в модуль.

Структура директорий

В основном разработчики применяют один из двух способов организации директорий с модулями:

  1. все модули помещаются в один каталог
  2. все модули помещаются в один каталог, кроме модуля по умолчанию, он находится на уровень выше.

Так как в рассматриваемом нами проекте в каталоге приложения application несколько оригинально располагаются папки с конфигурациями и системными файлами, то чтобы не создавать дополнительную путаницу мы воспользуемся первым подходом и поместим все модули включая модуль по умолчанию в каталог modules в котором для каждого модуля создадим свою папку. Для основного модуля папка по умолчанию должна называться default. Для модуля блога папка будет называться blog. По этим каталогом нам необходимо распределить контроллеры, скрипты вида и модели из каталога application.

В вашем приложении может появится и обязательно появится необходимость использовать какие-то общие для всех модулей ресурсы, такие как модели или шаблоны макетов (layout’s). Главный макет в этом проекте как раз ресурс такого типа – его будет использовать и модуль по умолчанию и модуль блога. Поэтому, мы оставляем его и скрипт отображающий меню в каталоге /application/view/scripts. Остальные скрипты вида распределяются по соответствующим модулям.

Также нам понадобятся несколько новых файлов реализующих дополнительный функционал модуля «блог», о котором я говорил ранее. Это контроллер авторов Blog_AuthorsController и модель Authors (файлы /application/modules/blog/controllers/AuthorsController.php и /application/modules/blog/models/Authors.php соответственно). Код файла AuthorsController.php:


<?php

/**
* AuthorController
*
* Работа с авторами статей
*
* @author Александр Стешенко aka Lcf для http://zendframework.ru
*/
class Blog_AuthorsController extends Zend_Controller_Action
{

/**
* Список авторов
*/
public function indexAction()
{

$modelAuthors = new Authors();
$this->view->assign('authorsList', $modelAuthors->getAuthors());
}

}

Обратите внимание, что классы контроллера для модуля блога именуются по правилу ИмяМодуля_ИмяКонтроллера. Таким образом, мы должны поменять также имя класса контроллера статей с ArticlesController на Blog_ArticlesController. Это правило должно выполнятся для всех контроллеров всех модулей, кроме контроллеров модуля по умолчанию. Они не содержат префикса.

Код файла Authors.php


<?php

/**
* Authors
*
* Работа с авторами статей
*
* @author Александр Стешенко aka Lcf для http://zendframework.ru
*/
class Authors extends Zend_Db_Table_Abstract
{

/**
* Имя таблицы
* @var string
*/
protected $_name = 'users';

/**
* Получить список авторов
*
* @return array
*/
public function getAuthors()
{

// Создаем объект Zend_Db_Select
$select = $this->getAdapter()->select()
// Таблица из которой делается выборка
->from(array('u' => $this->_name))
// Добавление таблицы с помощью join, указывается поле связи
->join(array('a' => 'articles'),
'u.id = a.author_id',
array('articles' => new Zend_Db_Expr('COUNT(*)')))
->group('u.id');

// Получение массива данных
$result = $this->getAdapter()->fetchAll($select, null, Zend_Db::FETCH_OBJ);
return $result;

}

}

Новому контроллеру потребуется скрипт вида для действия по умолчанию indexAction. Код файла /application/modules/blog/views/scripts/authors/index.tpl:


<ul>
<?php foreach ($this->authorsList as $author): ?>

<li><b><?php print $author->name; ?></b>, Кол-во статей:<b><?php print $author->articles; ?></b></li>
<?php endforeach; ?>
</ul>

После изменений наш проект должен иметь следующую структуру директорий:

Изменение конфигурации

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


// Путь к контроллерам
// 'controllers' => $root . 'application/controllers/',
// Путь к видам
// 'views' => $root . 'application/views/',

Для того чтобы подключались модели из обоих модулей, мы преобразуем конфигурационную директиву models следующим образом:


// Пути к моделям для каждого модуля
'models' => array(
$root . 'application/modules/default/models/',
$root . 'application/modules/blog/models/'
),

В файле index.php необходимо внести изменений в строку формирования путей, следующим образом:


// Создаем строку путей
$paths = implode(
PATH_SEPARATOR,
array(
$config['path']['library'],
implode(PATH_SEPARATOR, $config['path']['models']),
$config['path']['system']
)
);

Очевидно что вопрос загрузки моделей нужно решать более универсально. Для автоматизации этих действий есть несколько готовых решений, в данной статье они не рассматриваются. Также добавим путь к каталогу модулей в $config[‘path’]:


// Путь к модулям
'modules' => $root . 'application/modules/',

Настройка Zend_Controller_Front

Сразу после получения объекта фронт-контролера в ядре системы добавим вызов метода устанавливающий путь к модулям. Также этот метод автоматически определяет пути к скриптам вида и контроллерам всех модулей:


// Создание объекта front контроллера
$front = Zend_Controller_Front::getInstance();

// Добавляем каталог с модулями
$front->addModuleDirectory($this->_config->path->modules);

Так как в определении пути к контроллерам теперь нет необходимости, запуск приложения будем осуществлять с помощью dispatch() метода фронт-контроллера вместо статического метода run(), принимающего в качестве аргумента строку путей:


//Zend_Controller_Front::run($config->path->controllers);

// Запуск приложения
$front->dispatch();

Маршрутизация применительно к модулям

Вот что говорится о маршрутизации в модульном приложении в официальном руководстве:

Маршрут, используемый по умолчанию в Zend_Controller_Router_Rewrite, является объектом типа Zend_Controller_Router_Route_Module. Этот маршрут использует один из следующих шаблонов маршрутизации:

:module/:controller/:action/*
:controller/:action/*

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

Для корректной работы маршрутизатора в нашем проекте нам нужно внести два изменения в файл с настройками маршрутов /application/system/routes.php – указать в маршруте 'articles' 'module' => 'blog' а в маршруте 'pages' 'module' => 'default'. Мы по прежнему можем обращаться к страницам используя маршрутизацию по умолчанию. Например по адресу http://ilovezf/blog/articles/view/articleId/2.

Заключительные штрихи

В общем скрипте вида, в шаблоне так называемого layout'а нам обязательно нужно указать, что действие по выводу статей должно браться из index контроллера модуля blog. Также добавим в левую колонку сайта вывод списка авторов статей.


<div id="left">
<div style = "margin:5px;">Лучшие статьи:</div>
<?php print $this->action('index', 'articles', 'blog'); ?>

<div style = "margin:5px;">Список авторов:</div>
<?php print $this->action('index', 'authors', 'blog'); ?>
<div id = "copy">(c) San <a href = "http://zendframework.ru">http://zendframework.ru</a></div>

</div>

Чтобы блок с копирайтами (<div id="copy">) не пропал из левой колонки, нам придется модифицировать css файл проекта, немного увеличив высоту div#left.

Итоги

В статье мы определили понятие модульной структуры приложения, предлагаемой Zend Framework, и полностью рассмотрели пример её внедрения в рабочий проект из статьи «Построение простого сайта на основе ZF».

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

Скачать код без библиотек Zend, можно по ссылке (примерно 21 kb)

Результат работы разработанного сайта примера можно увидеть по адресу http://ilovezf-modular.demo.zendframework.ru

Обсуждение на форуме

Лучший способ следить за обновлениями сайта это подписаться на RSS
Если информация была полезной для вас, вы можете поддержать сайт.
Комментарии:
Алексей 21.01.2009 08:51 #
Статья понравилась. Но есть один вопрос. Если мы в Bootstrap.php указываем основной шаблон (layout.tpl например), то для каждого модуля этот шаблон ищется в папке view/scripts модуля. Но как быть, если для определенной страницы модуля, а еще круче, если для группы страниц модуля, я не хочу использовать основной шаблон layout.tpl а хочу например использовать layout2.tpl? Каким образом указать в контроллере новый основной шаблон?
Ответить
Александр Махомет 24.01.2009 16:43 #
Подобная проблема обсуждалась несколько раз на форуме, поищите.
Ответить
Никита 09.03.2009 13:06 #
нужно для каждого контроллера создавать новую строчку в routes.php ?
Ответить
rusk 12.03.2009 06:04 #
Ну если Вы собираетесь поменять дефолтные пути, то да.
Ответить
Golden 22.03.2009 11:04 #
В статье возможно ошибка:

должно быть

// Добавляем каталог с модулями
$front-addModuleDirectory($this-_config-path-modules);

а не

// Добавляем каталог с модулями
$front-addModuleDirectory($config-path-modules);

Иначе вылетает ошибка "Notice: Trying to get property of non-object"
Ответить
lcf 22.03.2009 11:41 #
Да, спасибо, поправил.
Ответить
I-genic 06.07.2009 02:27 #
Отличная статья - спасибо!
Ответить
yriel 06.11.2009 21:39 #
Столкнулся с проблемой быстродействия Zend'a, Может кто подскажет как работать с Zend_Cache ?
Ответить
Павел 07.12.2009 23:36 #
Как быть, если мне необходимо в данном примере изменить главную страницу, т.е. по умолчанию запускается модуль default, а как его можно поменять?
К примеру я добавил модуль contacts и хочу чтоб он у меня был главной страницей. Я вижу только вариант переноса содержимого с папки contacts в default, но думаю что это не правильно и есть вариант как это сделать правильно.
Ответить
erik 30.10.2010 18:30 #
как в файле menu.tpl получить конфиг?
к примеру в предыдущем уроке я вытаскивал переменную root так:
$this->_config->path->root
а теперь $this-> не содержит массива _config
Ответить
Mozart 02.09.2011 23:37 #
При использовании Apache необходимо, чтоб параметр AllowOverride был в положении "ALL", если же хостинг позволяет только в "None", то в .htacces необходимо добавить после "RewriteEngine on" строку "RewriteBase /".
Автору спасибо за материал, легко и доступно.
Ответить
Alexander 25.09.2011 20:50 #
Некоторые полезные статьи по Zend Framework можно найти здесь http://plutov.by/tag/zf
Ответить
Уважаемые пользователи. Комментарии не для того чтобы:
  1. Спрашивать почему у вас не работает код, для этого есть тема форума закрепленная за статьей.
  2. Спрашивать как реализовать ту или иную функциональность, для этого необходимо создать свою тему на форуме.

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

Комментарии имеют древовидную структуру.
Если вы хотите ответить на определенный комментарий - нажмите на ссылку "Ответить" возле этого комментария.

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