В предыдущей статье мы построили простейший MVC каркас и убедились в легкости и элегантности этого архитектурного шаблона. У нас был всего один контроллер — контроллер главной страницы с единственным действием (метод index) для отрисовки вида. В этой статье мы добавим класс для работы с маршрутами и модифицируем класс Load для разделения вида на общий для всех страниц шаблон и шаблон контента.
Реализация роутинга
Первый шаг, который нам нужно сделать, для создания удобной и практичной маршрутизации – записать следующий код в .htaccess:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule .* index.php [L]
Этот код перенаправит обработку всех страниц на index.php, что нам и нужно. Помните в первой части мы говорили о Front Controller?!
Маршрутизацию мы поместим в отдельный файл routing.php в корневой директории (application).
В этом файле опишем следующий класс, который будет запускать методы контроллеров, отрисовывающие вид страниц.
<?php
class Routing
{
private $default_controller = 'Main';
private $default_action = 'index';
private $controller_prefix = 'Controller_';
private $action_prefix = 'action_';
private $model_prefix = 'Model_';
function __construct ()
{
//echo "route: ".$_SERVER['REQUEST_URI']."<br>";
$this->routs = explode('/', $_SERVER['REQUEST_URI']);
if (count($this->routs)>3) die('Непредусмотренный маршрут!');
// получаем имя контроллера
if ( !empty($this->routs[1]) )
{
$this->controller_name = $this->routs[1];
}
else
{
$this->controller_name = $this->default_controller;
}
// получаем имя экшена
if ( !empty($this->routs[2]) )
{
$this->action_name = $this->routs[2]; // vj;tv
}
else
{
$this->action_name = $this->default_action;
}
// добавляем префиксы
$this->model_name = $this->model_prefix . $this->controller_name;
$this->controller_name = $this->controller_prefix . $this->controller_name;
$this->action_name = $this->action_prefix.$this->action_name;
$this->run();
}
function run()
{
// подцепляем файл с классом модели
$model_file = strtolower($this->model_name).'.php';
$model_path = "application/models/".$model_file;
if(file_exists($model_path)) include "models/".$model_file;
// файла модели может и не быть
// подцепляем файл с классом контроллера
$controller_file = strtolower($this->controller_name).'.php';
$controller_path = "application/controllers/".$controller_file;
if(file_exists($controller_path)) include "controllers/".$controller_file;
else die('Такого контроллера не существует!'); // здесь можно кинуть исключение
// создаем контроллер и вызываем экшн
$controller = new $this->controller_name;
$action = $this->action_name;
if(method_exists($controller, $action))
$controller->$action();
else die('Такого экшена не существует!'); // здесь можно кинуть исключение
}
}
?>
Несмотря на объемный код, логика класса очень проста. В элементе глобального массива $_SERVER['REQUEST_URI'] содержится полный адрес по которому обратился пользователь.
Например: tinymvc.ru/contacts/feedback
В конструкторе, c помощью функции explode производится разделение адреса на составлющие. В результате мы получаем имя контроллера, для приведенного примера, это контроллер contacts и имя действия, в нашем случае — feedback.
После этого добавляются необходимые префиксы, описанные в свойствах класса и запускается метод run, в котором подключается файл модели (модель может отсутствовать) и файл контроллера.
Наконец, создается экземпляр контроллера и вызывается действие, если такое было описано в классе контроллера.
Как видите, это очень упрощенная реализация, но в качестве тренировочного примера думаю вполне подходит. Примеры маршрутизации показаны в разделе «Результат».
Модификация видов
Сейчас мы усовершенствуем работу с видами, а именно, выделим общий для всех страниц шаблон, который будет содержать в себе другие виды, в частности виды отображающие контент страниц. Возможно, в дальнейшем мы добавим виды для сайдбара.
Сказано — сделано. Файл общего шаблона template_view.php, содержит следующий код:
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<title>Главная</title>
</head>
<body>
<?php include 'application/views/'.$content_file; ?>
</body>
</html>
Как мы видим, в общий шаблон встраивается некий контент.
Создадим вид для его отображения — файл contacts_view.php
<h1>Контакты</h1>
<p>
Email: <a href="mailto:yourname@company.com">yourname@company.com</a><br/>
ICQ: 199199538<br/>
</p>
Хорошо, теперь нужно подправить файл load.php, содержащий метод view для формирования видов.
Его код будет таким:
<?php
class Load {
public $template;
function view($content_file, $template_file, $data = null)
{
// динамически подключаем шаблон отображения (вид) и добавляем контент
include 'application/views/'.$template_file;
}
}
?>
В методе view подключается файл общего шаблона template_view.php, внутри которого подключается вид контента, ранее созданный файл contacts_view.php. Имена файлов передаются, как значения параметров метода view.
Ну что же, осталось написать контроллер, который «рулит» всем этим.
Класс контроллера будет следующим (файл controller_contacts):
<?php
class Controller_Contacts {
public $load;
public $model;
function __construct()
{
$this->load = new Load();
}
function action_index()
{
$this->load->view('contacts_view.php', 'template_view.php');
}
}
?>
Здесь я думаю все понятно. В метод view экземпляра класса Load передаются названия общего вида и вида c контентом страницы. После чего контент будет встроен в общий вид и отображен пользователю.
Помимо индексного действия мы можем описать в контроллере, скажем действие feedback, вызвав вид с формой обратной связи.
Теперь при обращении по адресу: tinymvc.ru/contacts/
или tinymvc.ru/contacts/index
пользователь увидит следующую страницу:
При обращении к несуществующим контроллерам или видам пользователь получит соответствующие уведомления. Например, при обращении по адресу tinymvc.ru/ufo пользователь увидит это:
Эту логику мы задавали в файле routing.php и позже мы сделаем редирект на 404 страницу.
Мы почти закончили. Создадим еще несколько контроллеров и видов для формирования полноценного сайта-визитки. Я не привожу здесь код этих файлов, т.к. он аналогичен приведенному выше.
Структура веб-приложения теперь выглядит следующим образом:
Результат
А вот, что получилось в результате:
Создание прототипа мы закончим в следующей статье.
Исходные файлы
Ссылка на GitHub: https://github.com/vitalyswipe/tinymvc/zipball/part2
Полезные ссылки:
- Маршрутизация на сайте средствами PHP
http://kdpsite.ru/sozdanie-routinga-na-php/ - Роутинг PHP и с чем его едят
http://codable.ru/routing-php-i-s-chem-ego-edyat.html - Роутинг на PHP
http://www.askdev.ru/php/4584/Роутинг-на-PHP/
Автор: vitalyswipe
мне кажется слишком большая привязка к структуре