MVC приложение на php. Часть 1

Из паттернов меня устраивало mvc, registry. Для запросов я написал небольшой слой абстракции, для роутинга — свою функцию парсинга запроса.
Структура веб-приложения будет такой

Папка application

Входной файл index.php подключает bootstrap.php. Тот в свою очередь подключает ядро, конфиг-файл, некоторые библиотеки и запускает маршрутизатор.

application/bootstrap.php


use Core\Route;

require_once 'lib/registry.php';
require_once 'config.php';
require_once 'lib/datebase.php';
require_once 'core/model.php';
require_once 'core/view.php';
require_once 'core/controller.php';
require_once 'core/route.php';
$router = new Route();
$router->start(); // запускаем маршрутизатор

Реестр устроен просто:

application/lib/registry.php


namespace Lib;

class Lib_Registry {
    static private $data = array();
    static public function set($key, $value) {
        self::$data[$key] = $value;
    }
    static public function get($key) {
        return isset(self::$data[$key]) ? self::$data[$key] : null;
    }
    static public function remove($key) {
        if ( isset(self::$data[$key]) ) {
            unset(self::$data[$key]);
        }
    }
}

Здесь геттеры и сеттеры для сохранения глобальных значений.

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

application/config.php


use Lib\Lib_Registry;

 define('PATH_SITE', $_SERVER['DOCUMENT_ROOT']);
 define('HOST', 'localhost');
 define('USER', 'root');
 define('PASSWORD', 'mypass');
 define('NAME_BD', 'articles');
 define ('DS', DIRECTORY_SEPARATOR);
 $mysqli = new mysqli(HOST, USER, PASSWORD,NAME_BD)or die("Невозможно установить соединение c базой данных".$mysqli->connect_errno());
 Lib_Registry::set('mysqli',$mysqli);
 $mysqli->query('SET names "utf8"');   //база устанавливаем кодировку данных в базе

Далее роутинг.

Запрос вида http://domen.ru/articles/index преобразуется в Контролллер — Экшн. Название контролллера и экшна задается в стиле Zend framework, camel case — Controller_Name, function action_name(). Файл контролллера, модели, въюхи должен совпадать с названием контроллера в нижнем регистре без префикса Controller_ или Model_

Класс модели задается так же — Model_Name, файл вьюхи мы уже выяснили — по имени экшна или явно в методе generate(name)

Так как в перспективе у нас создание админки — создадим папки client и admin. Кстати наш маршрутизатор будет учитывать вложенные папки, т.е. можно будет создать подпапки в контроллерах (напр. /about/contacts/contacts.php ) и обратиться к нему по его пути /about/contacts/
Итак, мы запустили маршрутизатор


    /**
     * 
     */
    public function start()
    {
        // catch AJAX request
        if ($this->getIsAjaxRequest()) {
            
        }
        session_start();
        $this->dispatch();
     }

Работает диспетчер

application/core/route.php


    /**
     *
     */
    public function dispatch(){
        // диспетчер получает файл совпадающий с названием контроллера, экшн и аргументы 
        $this->getDirections($file, $controller, $action, $args);
        /* ************* include Controller - Model  */
        if (is_readable($file) == false) {
            die ("File $file 404 Not Found");
        }
        // подключили контроллер
        include ($file);
        $model = str_replace("controller", "model", $file);
        // Model additional 
        if(is_readable($model)){
            // подключаем модель
            include($model);
        } 
        /* ****** получаем класс ** */
        $controller = ucfirst($controller);
        $class = ucfirst($this->namespace).'\Controller_' . $controller;
        // создаем экземпляр
        $controller = new $class($this->controller_path_folder);
        if (is_callable(array($controller, $action)) == false) {
            die ("Action $action 404 Not Found");
        }
        // вызываем экшн
        $controller->$action($args);
    }

Диспетчер вызывает метод getDirections(), т.е. получить директивы запроса. По умолчанию дефолтный контроллер — articles, экшн — index.


    /**
     * @param $file
     * @param $controller
     * @param $action
     * @param $args
     */
    private function getDirections(&$file, &$controller, &$action, &$args) {

        $route = (empty($_SERVER['REQUEST_URI'])) ? '' : $_SERVER['REQUEST_URI'];
        unset($_SERVER['REQUEST_URI']);
        $route = trim($route, '/\\');
        $controller_path = $this->path;
        if (empty($route)) {
        /* ******************* Default directions ******** */
            $controller = 'articles';
            $action = 'action_index';
            $controller_path = $this->controller_path_folder =  "application/controllers/$this->namespace/";
            $file = $controller_path.$controller.".php";
        }
        else
        {
            $parts = explode('/', $route);
            /* ************** namespace ********** */
            if($parts[0] == "admin") {
                $this->namespace =  "admin";
                array_shift($parts);
            }
            /* ***************** folders & subfolders ******* */
            $fullpath = $this->controller_path_folder = $controller_path . $this->namespace;
            foreach ($parts as $part) {
                $fullpath .= DS . $part;
                if (is_dir($fullpath)) {
                    array_shift($parts);
                    continue;
                }
                if (is_file($fullpath . '.php')) {
                    array_shift($parts);
                    $file = "$fullpath.php";
                    break;
                }
            }
            /* *************** Controller, Action, Params ******** */
            if(!isset($part))
                $part = "articles";
            $controller = $part;
            if(!$file)
                $file = $fullpath."/$part.php";
            $action = array_shift($parts);
            if(!$action)
                $action = 'action_index';
            else
                $action = "action_$action";
            $args = $parts;
        }
    }

В следующем уроке рассмотрим создание базовых контроллера, моделей и вьюх, напишем запросы.

Файлы 1-го урока здесь

https://github.com/vaajnur/create_php_application
Ветка мастер будет 1-м уроков, далее для каждого урока одноименная ветка — lesson1, lesson2, 3..

3 Comments

  1. Уроки из темы, сжечь к херам и удалить сайт…

  2. Спасибо,вот только бы побольше комментариев в коде. А так то, что надо.

Leave a comment

Your email address will not be published.


*