Обо мне

Простые MVC-приложения

Я хотел бы затронуть тему правильной архитектуры приложений на PHP. Статья будет посвящена паттерну проектирования MVC. Написать про MVC меня сподвиг именно тот факт, что понимание этого паттерна является ключевым в становлении PHP-программиста. Так что если вы новичок и хотите начать писать программы правильно, читайте дальше.

Теория

Если коротко, то MVC (model view controller) это такой способ написания программы, когда код отвечающий за вывод данных, пишется в одном месте, а код который эти данные формирует, пишется в другом месте. В итоге получается так, что если вам надо подправить вывод вы сразу знаете в каком месте искать. Сейчас все популярные фреймворки используют такую архитектуру.

Также стоит упомянуть тот факт, что существует два лагеря: один пишет логику в контроллерах, второй в моделях. В тех фреймворках, которые знаю я (yii, laravel) логику пишут в контроллерах, а модели являются просто экземплярами ORM. У yii кстати в мануале написано, что писать логику надо в моделях, а потом они сами в примерах пишут её в контроллерах, довольно забавно.

С бизнес-логикой определились, пишем в контроллерах. Также в методах контроллера происходит вызов моделей, которые у нас по сути экземпляры ORM, чтобы с их помощью получить данные из базы над которыми будут производить изменения. Конечный результат отправляется в виды. Виды cодержат HTML-разметку и небольшие вставки PHP-кода для обхода, форматирования и отображения данных.

Читайте также:  Математические основы программирования алгоритмы

Ещё можно упомянуть, что есть два вида MVC Page Controller и Front Controller. Page Controller’ом пользуются редко, его подход заключается в использовании нескольких точек входа (запросы к сайту осуществляются к нескольким файлам), и внутри каждой точки входа свой код отображения. Мы будем писать Front Controller с одной точкой входа.

Практика

Начать надо с настройки сервера для переадресации на нашу единую точку входа. Если у нас apache, то в файле .htaccess пишем следующее

RewriteEngine on RewriteCond % !-f RewriteCond % !-d RewriteRule .* index.php [L] 

Дальше в папке нашего проекта создаём папку, которую можно назвать App например. В ней будет следующее содержимое.

Наш Service Locator. Файл App.php

 public static function bootstrap() < static::$router = new App\Router(); static::$kernel = new App\Kernel(); static::$db = new App\Db(); >public static function loadClass ($className) < $className = str_replace('\\', DIRECTORY_SEPARATOR, $className); require_once ROOTPATH.DIRECTORY_SEPARATOR.$className.'.php'; >public function handleException (Throwable $e) < if($e instanceof \App\Exceptions\InvalidRouteException) < echo static::$kernel->launchAction('Error', 'error404', [$e]); >else< echo static::$kernel->launchAction('Error', 'error500', [$e]); > > > 

Сервис локатор нужен чтобы хранить в нём компоненты нашего приложения. Поскольку у нас простое mvc приложение, то мы не используем паттерн registry (как например в yii сделано). А просто сохраняем компоненты приложения в статические свойства, чтобы обращаться к ним было проще. Ещё App регистрирует автозагрузчик классов и обработчик исключений.

Роутер. Файл Router.php

 $route = is_null($route) ? $_SERVER['REQUEST_URI'] : $route; $route = explode('/', $route); array_shift($route); $result[0] = array_shift($route); $result[1] = array_shift($route); $result[2] = $route; return $result; > > 

В простом mvc приложении роутер содержит всего один метод. Он парсит адрес из $_SERVER[‘REQUEST_URI’]. Я ещё не сказал, что все наши ссылки на страницы сайта должны быть вида www.ourwebsite.com/%controller%/%action%, где %controller% — имя файла контроллера, а %action% — имя метода контроллера, который будет вызван.

Файл Db.php

getPDOSettings(); $this->pdo = new \PDO($settings['dsn'], $settings['user'], $settings['pass'], null); > protected function getPDOSettings() < $config = include ROOTPATH.DIRECTORY_SEPARATOR.'Config'.DIRECTORY_SEPARATOR.'Db.php'; $result['dsn'] = ":host=;dbname=;charset="; $result['user'] = $config['user']; $result['pass'] = $config['pass']; return $result; > public function execute($query, array $params=null) < if(is_null($params))< $stmt = $this->pdo->query($query); return $stmt->fetchAll(); > $stmt = $this->pdo->prepare($query); $stmt->execute($params); return $stmt->fetchAll(); > > 

Этот класс юзает файл конфига, который возврашает массив при подключении

Файл Config/Db.php

 'mysql', 'host' => 'localhost', 'dbname' => 'gotlib', 'charset' => 'utf8', 'user' => 'root', 'pass' => '' ];

Наше ядро. Файл Kernel.php

resolve(); echo $this->launchAction($controllerName, $actionName, $params); > public function launchAction($controllerName, $actionName, $params) < $controllerName = empty($controllerName) ? $this->defaultControllerName : ucfirst($controllerName); if(!file_exists(ROOTPATH.DIRECTORY_SEPARATOR.'Controllers'.DIRECTORY_SEPARATOR.$controllerName.'.php')) < throw new \App\Exceptions\InvalidRouteException(); >require_once ROOTPATH.DIRECTORY_SEPARATOR.'Controllers'.DIRECTORY_SEPARATOR.$controllerName.'.php'; if(!class_exists("\\Controllers\\".ucfirst($controllerName))) < throw new \App\Exceptions\InvalidRouteException(); >$controllerName = "\\Controllers\\".ucfirst($controllerName); $controller = new $controllerName; $actionName = empty($actionName) ? $this->defaultActionName : $actionName; if (!method_exists($controller, $actionName)) < throw new \App\Exceptions\InvalidRouteException(); >return $controller->$actionName($params); > > 

Ядро обращается к роутеру, а потом запускает действия контроллера. Ещё ядро может кинуть исключение, если нет нужного контроллера или метода.

Файл Controller.php

Ещё нам нужно создать базовый класс для наших контроллеров, чтобы потом наследоваться от него. Наследовать методы нужно для того, чтобы вы могли рендерить (сформировать вывод) виды. Методы рендеринга поддерживают использование лэйаутов — шаблонов, которые содержат общие для всех видов компоненты, например футер и хэдер.

 public function render ($viewName, array $params = []) < $viewFile = ROOTPATH.DIRECTORY_SEPARATOR.'Views'.DIRECTORY_SEPARATOR.$viewName.'.php'; extract($params); ob_start(); require $viewFile; $body = ob_get_clean(); ob_end_clean(); if (defined(NO_LAYOUT))< return $body; >return $this->renderLayout($body); > >

Файл index.php

Не забываем создать индексный файл в корне:

Создаём контроллеры и виды

Работа с нашим приложением (можно даже сказать минифреймворком) теперь сводится к созданию видов и контроллеров. Пример контроллера следующий (в папке Controllers):

 my_photo

Привет

Меня зовут Глеб и я - веб-разработчик.

Мои контакты:
8-912-641-3462
goootlib@gmail.com

В папке Views/Layout создаём Layout.php:

         

Заключение

Если решите пользоваться кодом приложения, которое описал я, не забудьте создать контроллер Error с методами error404 и error500. Класс для работы с бд, описанный мной, подходит для написания запросов руками, вместо него можно подключить ORM и вас будут настоящие модели.

Источник

Пишем мини MVC фреймворк на PHP #1

Привет, хабр! В этой статье я хочу написать о том, как сделать PHP-приложение с помощью схемы разделения данных приложения MVC.

Для работы нам нужна функция под названием autoload. Она избавляет нас от бесконечных require’ов. Мы можем вручную написать скрипт, но эта функция есть у знаменитого пакетного менеджера composer.

После установки инициализируем его в главной папке нашего мини-фреймворка командой composer init. на всех вопросах нажимаем ENTER.

Далее заходим в появившийся файл composer.json. Удаляем все и добавляем это:

И наконец, выполняем команду composer update. На этом настройка composer завершена.

Теперь, создадим папку core и файл .htaccess. Еще нужно создать папку public и создать в ней файл index.php — точку входа в приложение.

В файл .htaccess нужно вписать следующее:

RewriteEngine on RewriteRule .* public/index.php

Все, что делает этот файл — переадресовывает любые запросы в index.php.

Потом, в папке core создаем класс Application. Помещаем в него этот код:

Здесь мы создаем пока что пустой класс и пространством имен app\core.

На этом этапе структура должна выглядеть так:

│ .htaccess
│ composer.json
│ composer.lock

├───core
│ Application.php

├───public
│ index.php

└───vendor

Отлично. Теперь давайте выполним первую задачу: маршрутизацию.

Маршрутизация

В папке core создаем класс Router.php и начнем писать код:

routes['get'][$path] = $callback; > public function post($path, $callback) < $this->routes['post'][$path] = $callback; > >

Мы создали переменную routes, в котором будут храниться все маршруты в таком формате:

Теперь нужно создать класс Request.php для получения урлов, методов запроса и так далее:

 public function getMethod() < return strtolower($_SERVER['REQUEST_METHOD']); >>

Тут все просто: метод getPath служит для получения url без GET-параметров, а getMethod просто возвращает HTTP-метод. Модифицируем класс Роутера:

request = new Request(); > public function get($path, $callback) < $this->routes['get'][$path] = $callback; > public function post($path, $callback) < $this->routes['post'][$path] = $callback; > public function resolve() < $path = $this->request->getPath(); $method = $this->request->getMethod(); $callback = $this->routes[$method][$path] ?? false; if ($callback === false) < return "404"; >return call_user_func($callback); > >

Тут мы создаем экземпляр Request’а и метод resolve, который возвращает то, что вернул callback.

Возвратимся в класс Application и создаем метод run, который запустит Роутер:

router = new Router(); > public function run() < echo $this->router->resolve(); > >

Теперь, можем протестировать текущий функционал. Создадим папку public и в ней файл index.php:

router->get('/', function () < return "Hello, habr!"; >); $app->run();

В этой же папке запустим команду php -S localhost:8080 (или любой другой порт). Зайдем на localhost:8080 в браузере и о чудо! Мы увидим надпись Hello, habr!

Итоги

На этом первая часть подходит к концу. В ней мы задали каркас нашего будущего фреймворка. В следующей части мы реализуем View и Контроллеры

Это моя первая статья на Хабре, поэтому буду рад любой критике. Спасибо за прочтение и удачи!

Источник

Оцените статью