И так начнем. Паттерн MVC подразумевает одну точку входа – index.php, через это скрипт будут проходить все запросы, через него будет работать вся логика проекта. Для того чтобы реализовать такой подход необходимо настроить сервер, подразумевается, что сайт работает на сервере apache, поэтому нам достаточно создать файл.htaccess, в котором мы укажем правила маршрутизации URL. Помимо определения точки входа, маршрутизация позволяет создавать ЧПУ(человеко-понятные урлы). То есть после правильной настройки, адреса страниц буду выглядеть вот так site.ru/article/new.
Для начала, давайте составим.htaccess, который перенаправит обработку всех страниц на скрипт index.php. Код выглядит вот так:
RewriteEngine on RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ index.php?route=$1
Файл.htaccess должен лежать в корневой папке сайта, тут же необходимо создать скрипт index.php, который является точкой входа. Давайте запишем в index.php одну строку, для проверки работы перенаправления:
echo "test";
Теперь можно проверять работу перенаправления, введите любой адрес и посмотрите, что получиться: test-mvc.web/sdf/sdf/ или test-mvc.web/sdf/sdf/2342/не важно, на экране в любом случае, должно появиться «Test». Если Вы увидели эту надпись, значит, у нас все получилось.
Продолжим, давайте для удобства создадим в корне сайта файл config.php, в котором будем задавать различные константы, облегчающие своим существование настройку сайта. Это могут быть различные пути к скриптам, подступы к базе данных и так далее. Сейчас в конфиге давайте зададим следующее:
// Задаем константы:
define ("DS", DIRECTORY_SEPARATOR); // разделитель для путей к файлам
$sitePath = realpath(dirname(__FILE__) . DS);
define ("SITE_PATH", $sitePath); // путь к корневой папке сайта
// для подключения к бд
define("DB_USER", "root");
define("DB_PASS", "");
define("DB_HOST", "localhost");
define("DB_NAME", "blog_mvc");
Для того, чтобы константы и другие данные конфига мы могли использовать во всем проекте, в файле index.php необходимо подключить скрипт config.php.
Помимо подключения файла с настройками, в index.php нужно создать подключение к базе данных, подключить скрипт с ядром сайта и запустить роутер, в котором будет происходить маршрутизация.
Теперь по порядку, создание соединения с базой данных будет находиться в index.php для того, чтобы соединение открывалось только один раз. Единожды открыв соединение, мы сможем использовать его во всех контроллерах и моделях, но об этом чуть позже. Сейчас просто создадим соединение с базой. Для работы с бд я решил использовать PDO. Подробнее почитать про PDO можно тут.
Ядро сайта расположим в папке core и назовем скрипт core.php, тут мы напишем функцию, которая будет сама подключать, необходимы для работы классы. Такая функция очень облегчит и упростит нам работу с контролерами, моделями и тд. Поскольку, забегая вперед скажу, что каждый контролер и каждая модель будут представлять собой отдельный класс.
Помимо авто подключения классов, добавим в ядро создания хранилища (реестра), в котором будем хранить все необходимые объекты и переменные, которые могут пригодиться в любом месте проекта.
Роутер тоже подключим в индексном файле, он будет анализировать URL и подключать необходимый контроллер и экшен. Что такое контролер я писал в предыдущей статье, а информацию про экшен я пропустил умышленно, не став нагружать лишней информацией. Так что же такое экшен?
Контролер это класс, в котором заключены различные методы, при MVC подходе каждый метод будет являться экшеном. То есть экшен(action) – это метод класса, который будет обрабатывать данные и передавать их в представление (в шаблон). Может быть, пока не совсем понятно, но после примера все станет на свои места.
На данном этапе теории достаточно, давайте перейдем к практике. Приведу код файлов, работу которых, я описывал выше.
Код скрипта index.php:
// включим отображение всех ошибок
error_reporting (E_ALL);
// подключаем конфиг
include ("/config.php");
// Соединяемся с БД
$dbObject = new PDO("mysql:host=" . DB_HOST . ";dbname=" . DB_NAME, DB_USER, DB_PASS);
// подключаем ядро сайта
include (SITE_PATH . DS . "core" . DS . "core.php");
// Загружаем router
$router = new Router($registry);
// записываем данные в реестр
$registry->set ("router", $router);
// задаем путь до папки контроллеров.
$router->setPath (SITE_PATH . "controllers");
// запускаем маршрутизатор
$router->start();
Скрипт core.php:
// Загрузка классов "на лету"
function __autoload($className) {
$filename = strtolower($className) . ".php";
// определяем класс и находим для него путь
$expArr = explode("_", $className);
if(empty($expArr) OR $expArr == "Base"){
$folder = "classes";
}else{
switch(strtolower($expArr)){
case "controller":
$folder = "controllers";
break;
case "model":
$folder = "models";
break;
default:
$folder = "classes";
break;
}
}
// путь до класса
$file = SITE_PATH . $folder . DS . $filename;
// проверяем наличие файла
if (file_exists($file) == false) {
return false;
}
// подключаем файл с классом
include ($file);
}
// запускаем реестр (хранилище)
$registry = new Registry;
Класс хранилища Registry.php, будет находиться в папке /classes/
// Класс хранилища
Class Registry {
private $vars = array();
// запись данных
function set($key, $var) {
if (isset($this->vars[$key]) == true) {
throw new Exception("Unable to set var `" . $key . "`. Already set.");
}
$this->vars[$key] = $var;
return true;
}
// получение данных
function get($key) {
if (isset($this->vars[$key]) == false) {
return null;
}
return $this->vars[$key];
}
// удаление данных
function remove($var) {
unset($this->vars[$key]);
}
}
Код файла router.php, который находиться в папке /classes/
// класс роутера
Class Router {
private $registry;
private $path;
private $args = array();
// получаем хранилище
function __construct($registry) {
$this->registry = $registry;
}
// задаем путь до папки с контроллерами
function setPath($path) {
$path = trim($path, "/\\");
$path .= DS;
// если путь не существует, сигнализируем об этом
if (is_dir($path) == false) {
throw new Exception ("Invalid controller path: `" . $path . "`");
}
$this->path = $path;
}
// определение контроллера и экшена из урла
private function getController(&$file, &$controller, &$action, &$args) {
$route = (empty($_GET["route"])) ? "" : $_GET["route"];
unset($_GET["route"]);
if (empty($route)) {
$route = "index";
}
// Получаем части урла
$route = trim($route, "/\\");
$parts = explode("/", $route);
// Находим контроллер
$cmd_path = $this->path;
foreach ($parts as $part) {
$fullpath = $cmd_path . $part;
// Проверка существования папки
if (is_dir($fullpath)) {
$cmd_path .= $part . DS;
array_shift($parts);
continue;
}
// Находим файл
if (is_file($fullpath . ".php")) {
$controller = $part;
array_shift($parts);
break;
}
}
// если урле не указан контролер, то испольлзуем поумолчанию index
if (empty($controller)) {
$controller = "index";
}
// Получаем экшен
$action = array_shift($parts);
if (empty($action)) {
$action = "index";
}
$file = $cmd_path . $controller . ".php";
$args = $parts;
}
function start() {
// Анализируем путь
$this->getController($file, $controller, $action, $args);
// Проверка существования файла, иначе 404
if (is_readable($file) == false) {
die ("404 Not Found");
}
// Подключаем файл
include ($file);
// Создаём экземпляр контроллера
$class = "Controller_" . $controller;
$controller = new $class($this->registry);
// Если экшен не существует - 404
if (is_callable(array($controller, $action)) == false) {
die ("404 Not Found");
}
// Выполняем экшен
$controller->$action();
}
}
Теперь необходимо создать папки для хранения контроллеров, шаблонов и моделей – в корне создадим три папки controllers, views и models. И создадим несколько тестовых файлов /controllers/index.php, /views/index/index.php и /models/model_users.php, а теперь заполним файлы:
Для контроллера:
// контролер
Class Controller_Index Extends Controller_Base {
// шаблон
public $layouts = "first_layouts";
// экшен
function index() {
$model = new Model_Users();
$userInfo = $model->getUser();
$this->template->vars("userInfo", $userInfo);
$this->template->view("index");
}
}
Для отображения(/views/index/index.php)
Test view
id: =$userInfo["id"];?>
name: =$userInfo["name"];?>
И модель:
// модель
Class Model_Users{
public function getUser(){
return array("id"=>1, "name"=>"test_name");
}
}
Как вы могли заметить, класс контролера наследуется от родительского класса Controller_Base. Это сделано, для того, чтобы упростить класс контролера. Поскольку нам еще необходимо подключать класс для работы с шаблонами, его подключение вынесено в Controller_Base.
Приведу его код, он расположен в папке /classes/ и называется controller_base.php:
// абстрактый класс контроллера
Abstract Class Controller_Base {
protected $registry;
protected $template;
protected $layouts; // шаблон
public $vars = array();
// в конструкторе подключаем шаблоны
function __construct($registry) {
$this->registry = $registry;
// шаблоны
$this->template = new Template($this->layouts, get_class($this));
}
abstract function index();
}
Теперь осталось только разобраться с шаблонами. В абстрактном классе Controller_Base мы вызываем класс Template и передаем ему имя шаблона и имя контроллера.
Код класса Template, который лежит тут /classes/ и называется template.php
// класс для подключения шаблонов и передачи данных в отображение
Class Template {
private $template;
private $controller;
private $layouts;
private $vars = array();
function __construct($layouts, $controllerName) {
$this->layouts = $layouts;
$arr = explode("_", $controllerName);
$this->controller = strtolower($arr);
}
// установка переменных, для отображения
function vars($varname, $value) {
if (isset($this->vars[$varname]) == true) {
trigger_error ("Unable to set var `" . $varname . "`. Already set, and overwrite not allowed.", E_USER_NOTICE);
return false;
}
$this->vars[$varname] = $value;
return true;
}
// отображение
function view($name) {
$pathLayout = SITE_PATH . "views" . DS . "layouts" . DS . $this->layouts . ".php";
$contentPage = SITE_PATH . "views" . DS . $this->controller . DS . $name . ".php";
if (file_exists($pathLayout) == false) {
trigger_error ("Layout `" . $this->layouts . "` does not exist.", E_USER_NOTICE);
return false;
}
if (file_exists($contentPage) == false) {
trigger_error ("Template `" . $name . "` does not exist.", E_USER_NOTICE);
return false;
}
foreach ($this->vars as $key => $value) {
$$key = $value;
}
include ($pathLayout);
}
}
Если вы внимательно прочитали код, то наверняка поняли, что для отображения на страницах у нас используется шаблон first_layouts и вьюха(отображение) index.php – ее код я приводил чуть выше. Все что нам осталось, это создать файл шаблона first_layouts. Расположим его в папке /views/layouts/first_layouts.php
Шаблон будет содержать вот такой код:
Добро пожаловать во вторую статью о MVC и PHP . В ней мы обсудим некоторые принципы, которыми следует руководствоваться при использовании архитектуры MVC .
Реализация MVC с помощью PHP в интернете может оказаться немного сложнее. Первая проблема связана с URL -маршрутизацией. URL -маршрутизация - это один из аспектов, которые не учитывались, когда разрабатывался MVC .
Так какие возможности у нас есть для решения проблемы URL -маршрутизации? Одна из них заключается в предопределении всех URL -адресов, которые нужны веб-приложению. А затем в их сохранении в хранилище вместе с соответствующими данными, которые шаблоны «Модели », «Представления » и «Контроллера » загружают для каждой страницы или раздела.
Затем система получает URL -адрес, запрошенный пользователем, и загружает компоненты, назначенные для этой страницы. Это вполне осуществимое решение, если вы создаете сайт-визитку или статический сайт, который не работает с динамическими URL . Например:
array("model" => "AboutModel", "view" => "AboutView", "controller" => "AboutController"), "portfolio" => array("model" => "PortfolioModel", "view" => "PortfolioView", "controller" => "PortfolioController")); foreach($data as $key => $components){ if ($page == $key) { $model = $components["model"]; $view = $components["view"]; $controller = $components["controller"]; break; } } if (isset($model)) { $m = new $model(); $c = new $controller($model); $v = new $view($model); echo $v->output(); } }
URL -адреса будут выглядеть следующим образом:
example.com/index.php?page=about
example.com/index.php?page=portfolio
MVC PHP пример загружает конкретный набор «Модели », «Представления » и «Контроллера » для запрашиваемой страницы. Если параметр URL -страницы - это “about ”, то будет отображаться страница About . Если “portfolio ”, то будет отображаться страница Portfolio .
Этот пример статической маршрутизации даже при таких простых настройках имеет несколько недостатков. Одним из них является тот факт, что масштабируемость проекта затруднена, поскольку объем сайта ограничен заданным массивом страниц.
Можно открыть доступ к определениям классов «Модели », «Представления » и «Контроллера », и разрешить URL -адресам определять их параметры. В статическом примере маршрутизации мы извлекаем идентификатор класса из массива, который содержит данные для маршрутизации, поступающие из постоянного хранилища. Замена массива с элементами превращает статическую маршрутизацию в динамическую.
Мы помещаем ключ для каждого связанного элемента в массив с переменной URL -адреса, но взаимодействия с соответствующими классами уже предопределены; мы не могли сравнивать значения каждого ключа со статической маршрутизацией. Но почему бы нам не попробовать сделать это? Ну, для начала, нам не стоит жестко закреплять в коде каждый раздел системы.
Можно создать разделы или страницы, создавая взаимосвязи между «Моделью », «Представлением » и «Контроллером ». Например:
output(); }
Наш новый URL -адрес будет выглядеть так:
example.com/index.php?controller=controllername;model=modelname&view=viewname&action=actionname
Текущая переменная URL -адреса сообщает системе, какую функцию нужно вызвать в «Контроллере ». Когда эта функция передает данные в «Модель », она пересылает и часть данных, которые указывают, какое именно «Представление » и «Контроллер » нужно загрузить.
Это может быть переменная URL -адреса события, отдельная переменная или данные, собранные контроллером. Не забывайте, «Контроллер » никогда не должен загружать данные или непосредственно передавать их в «Представление »; он должен взаимодействовать только с «Моделью » и вводимыми пользователем данными.
Оба подхода имеют свои плюсы и минусы. Статическая маршрутизация более устойчива, быстрее реализуется и дает разработчикам больше контроля над системой. Динамическая маршрутизация позволяет создать более эффективную систему, с большим потенциалом масштабируемости и переносимости.
Однако при динамической маршрутизации «Контроллер » может получить больше функций, чем при статической. Динамическую маршрутизацию можно рассматривать как модификацию традиционной MVC архитектуры. Тем не менее, если она реализована правильно и эффективно, «Контроллер » может стать более важным элементом, чем при статической маршрутизации.
Добавление Front Controller позволит вашей системе динамически загружать разделы, в зависимости от того, что вам нужно.
Для меня одним из главных аргументов в пользу использования MVC является возможность сделать всю систему как можно более организованной. Любой разработчик согласится, что самое худшее для любого приложения – это повторение одного и того же кода. Принцип поддержания простоты кода и многократного использования компонентов получила название философия DRY - Don’t Repeat Yourself (не повторяйтесь ).
Принципы DRY гласят: "Каждый фрагмент информации должен быть представлен в системе единожды, однозначно и понятно ". Цель DRY - расширить и исследовать все возможные способы, доступные разработчикам, чтобы сделать систему динамичной и оптимизированной насколько это возможно. DRY подразумевает, что, если вам нужно написать один и тот же фрагмент кода во многих местах, то вместо повторения этого кода создайте отдельный метод и используйте его, где это необходимо.
Такой подход делает систему более оптимизированной и предусматривает возможность кэширования для сокращения общего времени обработки.
Корректная реализация DRY - это когда при изменении одного элемента системы несвязанные с ним элементы не меняются. Поэтому DRY так важен при разработке с использованием MVC паттерна.
Слово "шаблон " может вызвать некоторые вопросы у тех, кто работал ранее с MVC веб-фреймворками, так как большинство людей отождествляют шаблон с «Представлением ». Как мы уже обсуждали ранее, это некорректно с точки зрения традиционной архитектуры MVC .
В идеале, «Представление » должно обрабатывать данные после их получения от «Модели ». Поэтому имеет смысл организовать все так, чтобы компонент «Представление » только выбирал шаблон и передавал данные в этот шаблон.
Таким образом, данные будут готовы для отображения с помощью структуры блочного кода, или с помощью echo , print . Главное помнить, что ваши данные должны быть готовы для вывода через шаблон. Если у вас в шаблоне данные обрабатываются по-другому, скорее всего, у вас неверно задана архитектура MVC .
Вот простой пример того, как представление загружает шаблон и передает в него данные:
tstring = "The string has been loaded through the template."; $this->template = "tpl/template.php"; } } controller = $controller; $this->model = $model; } public function output(){ $data = "
" . $this->model->tstring ."
"; require_once($this->model->template); } }Шаблон PHP MVC передается через «Модель », которая может назначать шаблон в зависимости от того, для чего предназначено каждое конкретное «Представление ». Этот метод шаблонов позволяет создавать эффективные и расширяемые системы, предоставляя возможность разделения back-end и front-end разработки. В этом и заключается основная цель MVC .
MVC претерпел серьезные изменения с тех пор, как он впервые был использован для программирования. Он был и до сих пор остается одной из самых обсуждаемых тем в среде разработчиков. Эти споры стали еще более интенсивными после того, как MVC стал использоваться для разработки на PHP для Веб.
Перевод статьи «The MVC Pattern and PHP. Part 2 » был подготовлен дружной командой проекта Сайтостроение от А до Я.
Давайте поговорим о интересных полезностях, которые рано или поздно пригодятся любому web-developer`у.
Начнем с Роутинга (eng. “Routing” – маршрутизация). Сразу же появляется вопрос, зачем же нужна на сайте маршрутизация, если и раньше все хорошо работало и устраивало? Все просто. Если вы хотите получить более гибкую систему и уменьшить время на настройку/перенастройку сайта, то маршрутизация вам необходима. К тому же централизованное управление сайтом упростит работу с кодом.
Когда то я сам пользовался обычной switch-case маршрутизацией и думал, что это удобно (на самом деле даже такая маршрутизация куда лучше, чем ее отсутствие).
Все это было на старом функциональном php даже без какого-либо намека на ООП. Такого рода система получалась очень не гибкой, т.к. появлялись трудности в передаче данных в модули, код был очень громоздким, так как приходилось держать логику вместе с маршрутизацией, а потом меня осенило – почему бы не сделать следующее:
В этой статье многие не найдут какой-либо новизны,но я не стремлюсь вводить инновацию, я просто хочу открыть глаза тем, кто еще спит и пользуется менее удобными способами:)
Первый шаг, который нам нужно сделать, для создания удобной и практичной маршрутизации – записать следующий код в. htaccess:
Этот код перенаправит обработку всех страниц на index. php, что даст нам возможность упростить управление контентом. Для увеличения понимания советую обратить внимание на статью DarkKemper`a
Теперь, когда у нас есть перенаправление, перейдем к созданию файла-маршрутизатора. Я пользуюсь xml-форматом, но это не особо важно, главное, что бы вам было удобно.
Все довольно просто: в файле-маршрутизаторе вы можете указывать настройки как для отдельных URL, так и для каких-либо статичных частей страниц. В примере я указал модули для шапки сайта, так как она у меня не меняется. Замечу, что кроме модулей, вы можете указывать настройки, какие-либо передаваемые параметры, правила… В общем все, что угодно.
Последний и самый важный шаг – пишем класс Router, который и будет заведовать всей маршрутизацией.
Для начала нам нужно разобрать xml-файл:
Метод parse() получает путь к вашему конфигу (можно использовать не только для маршрутизации) и возвращает SimpleXML object.
Теперь вы можете обращаться к каким либо настройкам следующим образом: $xml->header->modules …
Итак, для начала мы парсим xml-файл, после чего обрабатываем регулярным выражением строку из
Если мы находим соответствие с URL текущей страницы, то подключаем модули, указанные в файле-маршрутизаторе. Если не находим – то подключаются модули 404 страницы.
Ну, вот и все. Ничего сложного тут нет.
Выводы можно сделать следующие:
Вы можеть быть даже слышали о шаблонах проектирования и даже листали эти прекрасные книги:
Представленная статья будет полезна в первую очередь новичкам. Во всяком случае, я надеюсь что за пару часов вы сможете получить представление о реализации MVC паттерна, который лежит в основе всех современных веб-фреймворков, а также получить «пищу» для дальнейших размышлений над тем — «как стоит делать». В конце статьи приводится подборка полезных ссылок, которые также помогут разобраться из чего состоят веб-фреймворки (помимо MVC) и как они работают.
Прожженные PHP-программисты вряд ли найдут в данной статье что-то новое для себя, но их замечания и комментарии к основному тексту были бы очень кстати! Т.к. без теории практика невозможна, а без практики теория бесполезна, то сначала будет чуть-чуть теории, а потом перейдем к практике. Если вы уже знакомы с концепцией MVC, можете пропустить раздел с теорией и сразу перейти к практике.
Рассмотрим концептуальную схему шаблона MVC (на мой взгляд — это наиболее удачная схема из тех, что я видел):
В архитектуре MVC модель предоставляет данные и правила бизнес-логики, представление отвечает за пользовательский интерфейс, а контроллер обеспечивает взаимодействие между моделью и представлением.
Типичную последовательность работы MVC-приложения можно описать следующим образом:
Вид
— используется для задания внешнего отображения данных, полученных из контроллера и модели.
Виды cодержат HTML-разметку и небольшие вставки PHP-кода для обхода, форматирования и отображения данных.
Не должны напрямую обращаться к базе данных. Этим должны заниматься модели.
Не должны работать с данными, полученными из запроса пользователя. Эту задачу должен выполнять контроллер.
Может напрямую обращаться к свойствам и методам контроллера или моделей, для получения готовых к выводу данных.
Виды обычно разделяют на общий шаблон, содержащий разметку, общую для всех страниц (например, шапку и подвал) и части шаблона, которые используют для отображения данных выводимых из модели или отображения форм ввода данных.
Контроллер
— связующее звено, соединяющее модели, виды и другие компоненты в рабочее приложение. Контроллер отвечает за обработку запросов пользователя. Контроллер не должен содержать SQL-запросов. Их лучше держать в моделях. Контроллер не должен содержать HTML и другой разметки. Её стоит выносить в виды.
В хорошо спроектированном MVC-приложении контроллеры обычно очень тонкие и содержат только несколько десятков строк кода. Чего, не скажешь о Stupid Fat Controllers (SFC) в CMS Joomla. Логика контроллера довольно типична и большая ее часть выносится в базовые классы.
Модели, наоборот, очень толстые и содержат большую часть кода, связанную с обработкой данных, т.к. структура данных и бизнес-логика, содержащаяся в них, обычно довольно специфична для конкретного приложения.
Надеюсь, вы уже успели заметить, что у разных сайтов могут быть совершенные разные форматы построения адресной строки. Каждый формат может отображать архитектуру web-приложения. Хотя это и не всегда так, но в большинстве случаев это явный факт.
Рассмотрим два варианта адресной строки, по которым показывается какой-то текст и профиль пользователя.
Первый вариант:
Второй вариант:
Подход с множеством точек взаимодействия вы можете наблюдать на форумах с движком phpBB. Просмотр форума происходит через сценарий viewforum.php , просмотр топика через viewtopic.php и т.д. Второй подход, с доступом через один физический файл сценария, можно наблюдать в моей любимой CMS MODX, где все обращения проходят черезindex.php .
Эти два подхода совершенно различны. Первый — характерен для шаблона контроллер страниц (Page Controller), а второй подход реализуется паттерном контроллер запросов (Front Controller). Контроллер страниц хорошо применять для сайтов с достаточно простой логикой. В свою очередь, контроллер запросов объединяет все действия по обработке запросов в одном месте, что даёт ему дополнительные возможности, благодаря которым можно реализовать более трудные задачи, чем обычно решаются контроллером страниц. Я не буду вдаваться в подробности реализации контроллера страниц, а скажу лишь, что в практической части будет разработан именно контроллер запросов (некоторое подобие).
К примеру, для обычной страницы, отображающей форму обратной связи, URL мог бы выглядеть так:
http://www.example.com/contacts.php?action=feedback
Приблизительный код обработки в таком случае:
switch
($_GET
["action"
])
{
case
"about"
:
require_once
("about.php"
); // страница "О Нас"
break
;
case
"contacts"
:
require_once
("contacts.php"
); // страница "Контакты"
break
;
case
"feedback"
:
require_once
("feedback.php"
); // страница "Обратная связь"
break
;
default
:
require_once
("page404.php"
); // страница "404"
break
;
}
Думаю, почти все так раньше делали.
С использованием движка маршрутизации URL вы сможете для отображения той же информации настроить приложение на прием таких запросов:
http://www.example.com/contacts/feedback
Здесь contacts представляет собой контроллер, а feedback — это метод контроллера contacts, отображающий форму обратной связи и т.д. Мы еще вернемся к этому вопросу в практической части.
Также стоит знать, что маршрутизаторы многих веб-фреймворков позволяют создавать произвольные маршруты URL (указать, что означает каждая часть URL) и правила их обработки.
Теперь мы обладаем достаточными теоретическими знаниями, чтобы перейти к практике.
Забегая вперед, скажу, что в папке core будут храниться базовые классы Model, View и Controller.
Их потомки будут храниться в директориях controllers, models и views. Файл index.php
это точка в хода в приложение. Файлbootstrap.php
инициирует загрузку приложения, подключая все необходимые модули и пр.
Будем идти последовательно; откроем файл index.php и наполним его следующим кодом:
ini_set("display_errors"
, 1
);
require_once
"application/bootstrap.php"
;
Тут вопросов возникнуть не должно.
Следом, сразу же перейдем к фалу bootstrap.php
:
require_once
"core/model.php"
;
require_once
"core/view.php"
;
require_once
"core/controller.php"
;
require_once
"core/route.php"
;
Route::start(); // запускаем маршрутизатор
Первые три строки будут подключать пока что несуществующие файлы ядра. Последние строки подключают файл с классом маршрутизатора и запускают его на выполнение вызовом статического метода start.
Маршрутизацию мы поместим в отдельный файл route.php в директорию core. В этом файле опишем класс Route, который будет запускать методы контроллеров, которые в свою очередь будут генерировать вид страниц.
Содержимое файла route.php
class Route { static function start () { // контроллер и действие по умолчанию $controller_name = "Main" ; $action_name = "index" ; $routes = explode("/" , $_SERVER ["REQUEST_URI" ]); // получаем имя контроллера if (!empty ($routes )) { $controller_name = $routes ; } // получаем имя экшена if (!empty ($routes )) { $action_name = $routes ; } // добавляем префиксы $model_name = "Model_" .$controller_name ; $controller_name = "Controller_" .$controller_name ; $action_name = "action_" .$action_name ; // подцепляем файл с классом модели (файла модели может и не быть) $model_file = strtolower($model_name ).".php" ; $model_path = "application/models/" .$model_file ; if (file_exists($model_path )) { include "application/models/" .$model_file ; } // подцепляем файл с классом контроллера $controller_file = strtolower($controller_name ).".php" ; $controller_path = "application/controllers/" .$controller_file ; if (file_exists($controller_path )) { include "application/controllers/" .$controller_file ; } else { /* правильно было бы кинуть здесь исключение, но для упрощения сразу сделаем редирект на страницу 404 */ Route::ErrorPage404(); } // создаем контроллер $controller = new $controller_name ; $action = $action_name ; if (method_exists($controller , $action )) { // вызываем действие контроллера $controller ->$action (); } else { // здесь также разумнее было бы кинуть исключение Route::ErrorPage404(); } } function ErrorPage404 () { $host = "http://" .$_SERVER ["HTTP_HOST" ]."/" ; header("HTTP/1.1 404 Not Found" ); header("Status: 404 Not Found" ); header("Location:" .$host ."404" ); } }
В элементе глобального массива $_SERVER["REQUEST_URI"] содержится полный адрес по которому обратился пользователь.
Например: example.ru/contacts/feedback
С помощью функции explode производится разделение адреса на составлющие. В результате мы получаем имя контроллера, для приведенного примера, это контроллер contacts и имя действия, в нашем случае — feedback .
Далее подключается файл модели (модель может отсутствовать) и файл контроллера, если таковые имеются и наконец, создается экземпляр контроллера и вызывается действие, опять же, если оно было описано в классе контроллера.
Таким образом, при переходе, к примеру, по адресу:
example.com/portfolio
или
example.com/portfolio/index
роутер выполнит следующие действия:
Напомню, что они будут содержать базовые классы, к написанию которых мы сейчас и приступим.
Содержимое файла model.php
class
Model
{
public
function
get_data
()
{
}
}
Класс модели содержит единственный пустой метод выборки данных, который будет перекрываться в классах потомках. Когда мы будем создавать классы потомки все станет понятней.
Содержимое файла view.php
class
View
{
//public $template_view; // здесь можно указать общий вид по умолчанию.
function
generate
($content_view
, $template_view
, $data
= null)
{
/*
if(is_array($data)) {
// преобразуем элементы массива в переменные
extract($data);
}
*/
include
"application/views/"
.$template_view
;
}
}
Не трудно догадаться, что метод generate
предназначен для формирования вида. В него передаются следующие параметры:
В нашем случае общий шаблон будет содержать header, menu, sidebar и footer, а контент страниц будет содержаться в отдельном виде. Опять же это сделано для упрощения.
Содержимое файла controller.php
class
Controller
{
public
$model
;
public
$view
;
function
__construct
()
{
$this
->view = new
View();
}
}
}
Метод action_index
— это действие, вызываемое по умолчанию, его мы перекроем при реализации классов потомков.
На предыдущем рисунке отдельно выделен файл template_view.php
— это шаблон, содержащий общую для всех страниц разметку. В простейшем случае он мог бы выглядеть так:
<html
lang
="ru"
>
<head
>
<meta
charset
="utf-
8
">
<title
>
Главнаяtitle
>
head
>
<body
>
$content_view
; ?>
body
>
html
>
Для придания сайту презентабельного вида сверстаем CSS шаблон и интегририруем его в наш сайт путем изменения структуры HTML-разметки и подключения CSS и JavaScript файлов:
<link
rel
="stylesheet"
type
="text/css"
href
="/css/style.css"
/>
<script
src
="/js/jquery-1.6.2.js"
type
="text/javascript"
>
script
>
В конце статьи, в разделе «Результат», приводится ссылка на GitHub-репозиторий с проектом, в котором проделаны действия по интеграции простенького шаблона.
Файл с общим видом мы рассмотрели ранее. Рассмотрим файл контента main_view.php
:
<h1
>
Добро пожаловать!h1
>
<p
>
<img
src
="/images/office-small.jpg"
align
="left"
>
<a
href
="/"
>
ОЛОЛОША TEAMa
>
- команда первоклассных специалистов в области разработки веб-сайтов с многолетним опытом коллекционирования мексиканских масок, бронзовых и каменных статуй из Индии и Цейлона, барельефов и изваяний, созданных мастерами Экваториальной Африки пять-шесть веков назад...
p
>
Здесь содержиться простая разметка без каких либо PHP-вызовов.
Для отображения главной странички можно воспользоваться одним из следующих адресов:
Класс контроллера модели содержится в файле controller_portfolio.php
, вот его код:
class
Controller_Portfolio
extends
Controller
{
function
__construct
()
{
$this
->model = new
Model_Portfolio();
$this
->view = new
View();
}
function
action_index
()
{
$data
= $this
->model->get_data();
$this
->view->generate("portfolio_view.php"
, "template_view.php"
, $data
);
}
}
В переменную data
записывается массив, возвращаемый методом get_data
, который мы рассматривали ранее.
Далее эта переменная передается в качестве параметра метода generate
, в который также передаются: имя файла с общим шаблон и имя файла, содержащего вид c контентом страницы.
Вид содержащий контент страницы находится в файле portfolio_view.php
.
Год | Проект | Описание | " .$row ["Year" ]." | " .$row ["Site" ]." | " .$row ["Description" ]." | " ; }