Способ разделения frontend/backend-частей в Yii
О чем это я?
Хочу немного рассказать, как я разделяю фронтэнд/бекэнд-части сайта во всех своих проектах на Yii. Не претендую на авторство этого способа, просто хочу немного объяснить, что и как организуется и работает.
Собственно, почти всегда проект не может состоять только из фронтэнда (тоесть того, что видит посетитель). Очень часто требуется организовать административную часть, при этом иметь безболезненный доступ ко всем компонентам проекта (модели, расширения и т.п.).
Есть несколько способов добиться этого. Не буду вдаваться в подробности каждого из них, если вам интересно — можете поискать на форуме yiiframework.com.
Самым элегантным и удобным мне показался вариант с использованием своего “поведения” запуска приложения. Он позволяет очень удобно разграничить приложение в плане директорией, использовать разные конфиги для каждой части сайта и т.п.
Если вы знакомы с Yii, то должны знать, что в нем реализованы так называемые behaviors, которые позволяют изменять или дополнять функционал и поведение разных компонентов. Чаще всего они применяются с связке с моделями, но в нашем случае предлагается использовать поведения непосредственно для старта приложения.
Директории
Сначала давайте определимся с директориями. Сойдемся на том, что контроллеры пользовательской части сайта (фронтэнд) хранятся в папке “protected/controllers/frontend”. Логично, что контроллеры админки мы будем хранить в папке “protected/controllers/backend”.
Тоже самое с отображениями. Для фронтэнда — это папка “protected/views/frontend”, для бэкенда — “protected/views/backend”.
На деле это выглядит примерно так:
Контроллеры
Очевидно, что мы не хотим, чтобы контроллеры нашей админки работали также, как внешние контроллеры. Например, мы хотим поставить в админке фильтры доступа только для определенных ролей пользователей.
Чтобы не разводить лапшы и не повторяться в каждом контроллере с фильтрами (DRY же!) — создадим промежуточные контроллеры — FrontEndController и BackEndController, которые унаследуем от базового контроллера и поместим их в папку “protected/components”.
Выглядить эти контроллеры должны примерно так:
FrontEndController.php:
class FrontEndController extends BaseController { // лейаут public $layout = 'application'; // меню public $menu = array(); // крошки public $breadcrumbs = array(); }
BackendEndController.php:
<?php class BackEndController extends BaseController { // лейаут public $layout = 'application'; // меню public $menu = array(); // крошки public $breadcrumbs = array(); /* Фильтры */ public function filters() { return array( 'accessControl', ); } /* Права доступа */ public function accessRules() { return array( // даем доступ только админам array( 'allow', 'roles'=>array('admin'), ), // всем остальным разрешаем посмотреть только на страницу авторизации array( 'allow', 'actions'=>array('login'), 'users'=>array('?'), ), // запрещаем все остальное array( 'deny', 'users'=>array('*'), ), ); } }
Вообщем-то они идентичны, разве что BackEndController реализует фильтры, которые скрывают от любопытных глаз наш бекэнд, разрешая лишь попытаться пройти авторизацию. При этом они оба могут обладать общим функционалом и поведением, который при необходимости можно создать в BaseController, который они наследуют.
Лично я в BaseController делаю удобные шорткаты для разных ф-ий, которые очень часто используются:
BaseController.php:
<?php class BaseController extends CController { // флеш-нотис пользователю public function setNotice($message) { return Yii::app()->user->setFlash('notice', $message); } // флеш-ошибка пользователю public function setError($message) { return Yii::app()->user->setFlash('error', $message); } }
Ну а дальше все понятно — все контроллеры, которые лежат в “protected/controllers/backend” — наследуют BackEndController. Те, что в “protected/controllers/frontend” — FrontEndController.
Поведение
Теперь непосредственно расскажу, как же все это будет работать. Для этого мы создаем в папке “protected/behaviors” новое поведение — WebApplicationEndBehavior. Код его достаточно, как говорится, straightforward. Но в комментариях все-таки поясню, что и зачем.
WebApplicationEndBehavior.php
<?php class WebApplicationEndBehavior extends CBehavior { // имя нужной нам части сайта private $_endName; // геттер $_endName; public function getEndName() { return $this->_endName; } // запуск приложения public function runEnd($name) { $this->_endName = $name; // обрабатываем событие создания модуля $this->onModuleCreate = array($this, 'changeModulePaths'); $this->onModuleCreate(new CEvent ($this->owner)); $this->owner->run(); } // обработчик события onModuleCreate public function onModuleCreate($event) { $this->raiseEvent('onModuleCreate', $event); } // подменяем пути к файлам protected function changeModulePaths($event) { // добавляем название части сайта (frontend или backend) в путь, по которому фреймворк будет искать контроллеры и вьюшки $event->sender->controllerPath .= DIRECTORY_SEPARATOR.$this->_endName; $event->sender->viewPath .= DIRECTORY_SEPARATOR.$this->_endName; } }
Весьма просто. Вызов функции runEnd($name) с названием части сайта (frontend/backend) вешает обработчик события onModuleCreate, затем в обработчике мы подменяем путь до контроллеров и отображений, добавив туда нужные нам “суффиксы”.
Осталось только добавить этот компонент в путь, по которому фреймворк будет искать классы для автозагрузки.
В «protected/config/main.php» добавьте:
<?php ... // используемые приложением поведения 'behaviors'=>array( 'runEnd'=>array( 'class'=>'application.behaviors.WebApplicationEndBehavior', ), ), ...
Конфигурация
Весьма полезным будет отделить наши части сайта не только на уровне размещения папок, но и на уровне конфигурации приложения. Для этого создадим отдельные файлы конфигурации в папке “protected/config” — backend.php и frontend.php.
Выглядят они примерно так:
backend.php
<?php return CMap::mergeArray( require_once(dirname(__FILE__).'/main.php'), array( // стандартный контроллер 'defaultController' => 'posts', // компоненты 'components'=>array( // пользователь 'user'=>array( 'loginUrl'=>array('/users/login'), ), // mailer 'mailer'=>array( 'pathViews' => 'application.views.backend.email', 'pathLayouts' => 'application.views.email.backend.layouts' ), ), ) );
С фронтендом точно также.
Вот например так выглядит папка с конфигами у меня:
Bootstrap
Теперь нам осталось только настроить наши входные скрипты. Здесь я обычно создаю два файла в корне приложения — index.php и backend.php. Код они содержат следующий:
index.php
// путь до фреймворка и нужного нам конфига $yii = dirname(__FILE__).'/../yii/framework/yii.php'; $config = dirname(__FILE__).'/protected/config/frontend.php'; // включать дебаг? defined('YII_DEBUG') or define('YII_DEBUG', true); defined('YII_TRACE_LEVEL') or define('YII_TRACE_LEVEL', 3); // подключаем фреймворк require_once($yii); // стартуем приложение с помощью нашего WebApplicaitonEndBehavior, указав ему, что нужно загрузить фронтенд Yii::createWebApplication($config)->runEnd('frontend');
backend.php
// путь до фреймворка и нужного нам конфига $yii = dirname(__FILE__).'/../yii/framework/yii.php'; $config = dirname(__FILE__).'/protected/config/backend.php'; // включать дебаг? defined('YII_DEBUG') or define('YII_DEBUG', true); defined('YII_TRACE_LEVEL') or define('YII_TRACE_LEVEL', 3); // подключаем фреймворк require_once($yii); // стартуем приложение с помощью нашего WebApplicaitonEndBehavior, указав ему, что нужно загрузить бекэнд Yii::createWebApplication($config)->runEnd('frontend');
Все, теперь обращаясь к http://localhost/yourapp/index.php (или как вас там) — вы попадете на фронтэнд сайта, обращаясь к http://localhost/yourapp/admin.php — приложение загрузит административную часть.
На мой взгляд, выходит очень удобно — все четко разграничено, каждая часть имеет свои настройки, хранит свои контроллеры и вьюхи в отдельных папках, не создавая мешанины и путаницы в файлах.
Собственно, на этом все, что я хотел вам рассказать по этому очень часто возникающему у многих вопросу.
Вообще, на хабре довольно не важно освещен Yii. Хотя, лично для меня — это явный номер №1 среди php-фреймворков.