Простая MVC модель.

Статью писал для своего сайта, но по некоторым причинам решил все свои статьи перезалить сюда (что бы не утерять)!

Model View Controller позволяет разделить данные, представление и обработку действий пользователя на три отдельных компонента.

Модель (Model). Модель предоставляет данные (обычно для View), а также реагирует на запросы (обычно от контроллера), изменяя своё состояние.
Представление (View). Отвечает за отображение информации (пользовательский интерфейс).
Поведение (Controller). Интерпретирует данные, введённые пользователем, и информирует модель и представление о необходимости соответствующей реакции.
---------------------------------------------------------------------------------------------------
Как-то задался вопросом о работе MVC, и не имея представления о реализации, я искал в гугле решение своего вопроса!
Гугл привел меня к вот этой статье http://habrahabr.ru/blogs/php/31270/
Я поглядел на "это" все, и решил попробовать! Особого удовлетворения от работы с данной моделью я не ощущал, и решил проблему по своему...

Вот так выглядит структура моей модели:
|- /includes/
|- /includes/classes/
|- /includes/classes/MVC_BaseController.class.php
|- /includes/classes/MVC_Registry.class.php
|- /includes/classes/MVC_Router.class.php
|- /includes/classes/TestClass.class.php
|- /includes/controllers/
|- /includes/controllers/home/
|- /includes/controllers/home/index.cont.php
|- /includes/setting/
|- /includes/setting/core.inc.php
|- /.htaccess
|- /index.php

Приступим к .htaccess
# Указываем индексный файл.
DirectoryIndex index.php
Options -Indexes
# Указываем кодировку.
AddDefaultCharset UTF-8

RewriteEngine on
# modDir:  Узнаем папку к модели
# modFile: Узнаем файл модели.
# modVar:  Узнаем переменные.
RewriteRule ^([a-z]{3,10})($|/)($|[a-zA-Z0-9]{3,10})($|/)($|[a-z0-9;:_]{3,30})? index.php?modDir=$1&modFile=$3&modVar=$5 [L,QSA]
Т.е теперь у нас url будет выглядеть так: http://example.com/home/index/id:1

Переходим к index.php
<?php
// Включаем на время отладки вывод ошибок.
error_reporting(E_ALL);

// Узнаем корневую директорию.
$pathRoot = realpath(dirname(__FILE__)).'/';
// Для ОС Windows заменяем слеш, и записываем.
define('SITE_PATH_ROOT', str_replace('\\', '/', $pathRoot));

// Подключаем конфигурационный файл.
require_once('includes/settings/core.inc.php');
На этом мы и закончим пока что работу над index.php.

Теперь можно приступить к самой структуре MVC, и первое что мы будем делать, это класс для передачи значений в в контроллер.
Класс будет состоять из двух магических методов __set() и __get().

Создадим файл /includes/classes/MVC_Registry.class.php в котором создаем класс MVC_Registry
<?php

class MVC_Registry {

    // Массив с переменными.
    private $_vars = array();

    /**
    * __set
    *
    * Присваиваем переменным значения.
    *
    * @param  string $name    Имя переменной.
    * @param  mixed  $val     Значение переменной.
    * @return bool			
    **/	
    public function __set($name, $val) 
    {
	
        if (empty($this -> _vars[$name]) == false) {
            return false;
        }


        $this -> _vars[$name] = $val;
        return true;
    }


    /**
    * __get
    *
    * Возвращаем значение переменной.
    *
    * @param string $name    Имя переменной.
    * @return mixed			
    **/	
    public function __get($name) 
    {

        if (empty($this -> _vars[$name]) == true) {
            return null;
        }
		
        return $this -> _vars[$name];
    }
}

?>

Теперь этот класс надо подключить в index.php, и это мы сделаем при помощи магического метода __autoload().
Создадим файл /includes/setting/core.inc.php и пишем в нем следующее:
<?php

function __autoload($name) 
{
    // Добавляем прификс к названию класса.
    $fileName = $name.'.class.php';
    // Узнаем категорию в которой лежит класс.
    $fileDir  = SITE_PATH_ROOT.'/includes/classes/'.$fileName;

    // Проверяем существует ли такой класс, или нет.
    if (is_file($fileDir) == false) {
        return true;
    }

    // Подключаем файл с классом.
    include_once($fileDir);
}

// Теперь можно создать объект класса MVC_Registry, что бы не гадить в index.php.
$mvcRegistry = new MVC_Registry;
?>

Перейдем к самой главной части MVC, к маршрутизатору.
Маршрутизатор у нас отвечает за то, что нам должен выдать скрипт! если точнее, то он указывает в какой папке искать модель, какой файл подключать, и что выводить.
Класс будет содержать в себе 3 метода: __construct(), _getController(), getModul()
<?php

class MVC_Router {

    // Массив со значениями.
	private $_registry;

	// Папка с контроллерами.
	private $_path = '/includes/controllers/';

	// Массив со стандартным контроллером.
	private $_arrModul = array(
							  'dir' => 'home/',
							  'file' => 'index.cont.php'
							  ); 
	

	/**
	* __construct
	*
	* @param array $registry    Массив со значениями.
	*
	**/	
	public function __construct($registry) 
	{
		$this -> _registry = $registry;
	}
	

	 
	/**
	* _getController
	*
	* Узнаем путь к файлу модуля для дальнейшего его подключения.
	*
	**/	
	private function _getControllerClass() 
	{	
	
		// Проверяем указана ли папка с контроллером.
		if (!empty($_GET['modDir'])) {

			// Проверяем существует ли категория с указанным контроллером.
			if (is_dir($this -> _path . $_GET['modDir'] . '/') === true) {

				// Записываем указанную категорию.
				$this -> _arrModul['dir'] = $_GET['modDir'] . '/';
			
			}
			
			// Проверяем указан ли контроллер, если он не указан, то пытаемся указать как index.
			$_GET['modFile'] = empty($_GET['modFile']) ? 'index' : $_GET['modFile'];
				
			// Проверяем существует ли файл контроллера
			if (is_file($this -> _path . $this -> _arrModul['dir'] . '/' . $_GET['modFile'] . '.modul.php') === true) {
				
				// Записываем указанный контраллер.
				$this -> _arrModul['file'] = $_GET['modFile'] . '.modul.php';
			
			}
		}
		
		// Записываем путь к файлу контроллера.
		$this -> modulFile = SITE_PATH_ROOT . $this -> _path . $this -> _arrModul['dir'] . $this -> _arrModul['file'];

	}



	/**
	* getModul
	*
	* Подключаем класс модуля, и выводим результат.
	*
	**/
	 public function getController()
	 {

		// Узнаем путь к контроллеру.
		$this -> _getControllerClass();
		
		// Подключаем контроллер.
		include_once ($this -> modulFile);

		// Создать объект класса DisplayController
		$controller = new DisplayController($this -> _registry);
		// Выводим результат выполнения контроллера.
		$controller -> display();
	 }

}

?>

Теперь в файле index.php добавим еще пару строк? в которых мы запускаем маршрутизатор, и выводим контроллер.
<?php
// Создадим объект класса MVC_Router
$mvcRouter = new MVC_Router($mvcRegistry);
// Подключаем контроллер.
$mvcRouter -> getController();
?>

Все готово! Теперь можно создавать контроллер!
Думаю у вас возник вопрос "Неужели нам придется каждый раз создавать конструктор класса DisplayController?", конечно же нет!

Создаем файл /includes/classes/MVC_BaseController.class.php в котором мы создадим абстрактный класс MVC_BaseController который будет выполнять не только подгрузку значений реестра, но и разберать переменные полученные из $_GET.
<?php
abstract class MVC_BaseController {

	// Массив со значениями.
	protected static $registry = array();



	/**
	* __construct
	*
	* @param array $registry    Массив со значениями.
	*
	**/	
	public function __construct($registry) 
	{
		self :: $registry = $registry;

		// Проверяем указанны ли переменные.
		if (empty($_GET['modVar']) === false) {

			// Делим строку на части.
			$aExVar = explode(';', $_GET['modVar']);

			// Если в строке есть хоть одна переменная, то работаем дальше.
			if (empty($aExVar) === false) {

				$vars = array();

				foreach ($aExVar as $aKey) {

					// Делим строку на ключ и значение.
					$aExValue = explode(':', $aKey);
					
					// Проверяем есть ли в строке ключ и значение.
					if (empty($aExValue[0]) === false and empty($aExValue[1]) === false) {

						// Записываем переменную.
						$vars[$aExValue[0]] = $aExValue[1];

					}
				}
			}
			// Пишем полученные переменные в глобальный массив $_GET
			$_GET = array_map('trim', $vars);
		}
	}

	// Указываем метод displey() как абстрактный.
	abstract function display();
}
?>

Самое главное мы сделали, осталось дело за малым! Создаем файл контроллера /includes/controllers/home/index.cont.php
И в нем мы пишем следующее:
<?php
class DisplayController extends MVC_BaseController {

	public function display() 
	{ 

		echo 'Hello World';

	}

}
?>

Посмотрев на работу скрипта, результатом у нас будет: Hello World


Теперь можно глянуть применение:
/index.php
<?php
define('SITE_PATH_ROOT', str_replace('\\', '/', realpath(dirname(__FILE__))).'/');

include_once ('includes/settings/core.inc.php');

$mvcRegistry -> testClass = new TestClass();

$mvcRouter = new MVC_Router($mvcRegistry);
$mvcRouter -> getController();
?>
/includes/classes/TestClass.class.php
<?php
class TestClass {

	public function testMethod($str) 
	{
		return 'I love '.$str;
	}
}

?>
/includes/controllers/home/index.cont.php
<?php
class DisplayController extends MVC_BaseController {

	public function display() 
	{ 
		echo 'Hello World <br />' . self :: $registry -> testClass -> testMethod('Moscow');
	}

}
?>
Результат выполнения:
Hello World 
I love Moscow
------------------------------------
Подготовил статью: Nu3oN специально для http://7je.ru

URL: https://visavi.net/articles/365