Доработать свое приложение, путем реализации контроллеров для всех страниц.
Бэкенд / Создание контроллеров
Как я уже говорил, практически все современные веб приложения работают на базе архитектуры MVC, у нас из нее сейчас только более менее выделился пункт V то бишь View – представление, ну или шаблон если более понятно.
Если вы писали код примерно, как я, то у вас должна была бы получится двухуровневая структура в роутере
как вариант вы могли обойтись без вложенных if-ов но тогда вам бы приходилось дублировать код, ответственный за название галактики, общее описание объекта и картинку
в общем оба подхода одинаково плохи. Их неудобно читать и не приятно писать.
Мы же возьмем лучшее от этих двух подходов. Плоскую структуру из второго и иерархию из первого. Для этого мы воспользуемся возможности создавать классы в php.
Классы позволят нам утащить код из if-ов а также строить иерархию наследования. Ну типа у общей страницы андромеды название “Галактика Андромеды”, а у страница с картинкой то же название, но плюс там еще появляется информация о картинки.
Создаем контроллеры
И так, создаем папку controllers и в ней файл BaseController
Пишем в него
<?php
// класс абстрактный, чтобы нельзя было создать экземпляр
abstract class BaseController {
// так как все вертится вокруг данных, то заведем функцию,
// которая будет возвращать контекст с данными
public function getContext(): array {
return []; // по умолчанию пустой контекст
}
// с помощью функции get будет вызывать непосредственно рендеринг
// так как рендерить необязательно twig шаблоны, а можно, например, всякий json
// то метод сделаем абстрактным, ну типа кто наследуем BaseController
// тот обязан переопределить этот метод
abstract public function get();
}
Создаем еще один класс и назовем его TwigBaseController
<?php
require_once "BaseController.php"; // обязательно импортим BaseController
class TwigBaseController extends BaseController {
public $title = ""; // название страницы
public $template = ""; // шаблон страницы
protected \Twig\Environment $twig; // ссылка на экземпляр twig, для рендернига
// теперь пишем конструктор,
// передаем в него один параметр
// собственно ссылка на экземпляр twig
// это кстати Dependency Injection называется
// это лучше чем создавать глобальный объект $twig
// и быстрее чем создавать персональный $twig обработчик для каждого класс
public function __construct($twig)
{
$this->twig = $twig; // пробрасываем его внутрь
}
// переопределяем функцию контекста
public function getContext() : array
{
$context = parent::getContext(); // вызываем родительский метод
$context['title'] = $this->title; // добавляем title в контекст
return $context;
}
// функция гет, рендерит результат используя $template в качестве шаблона
// и вызывает функцию getContext для формирования словаря контекста
public function get() {
echo $this->twig->render($this->template, $this->getContext());
}
}
И вероятно, может возникнуть вопрос, а нафига столько кода, какие-то абстракции, и все такое?
А мы сейчас используя эти базовые классы создадим контроллеры под все наши виды и причем код в роутере у нас станет в разы меньше, а читать и поддерживать все станет многократно проще.
Добавляем контроллер главной страницы
Создаем контроллер MainController под main страницу
и пишем в нем… а почти ничего и не пишем =О
<?php
require_once "TwigBaseController.php"; // импортим TwigBaseController
class MainController extends TwigBaseController {
public $template = "main.twig";
public $title = "Главная";
}
далее идем и правим в роутере:
<?php
require_once "../vendor/autoload.php";
require_once "../controllers/MainController.php"; // добавим в самом верху ссылку на наш контроллер
// ...
$context = [];
$controller = null; // создаем переменную под контроллер
if ($url == "/") {
$controller = new MainController($twig); // создаем экземпляр контроллера для главной страницы
} elseif (preg_match("#^/andromeda#", $url)) {
// пока не трогаем ...
} elseif (preg_match("#^/orion#", $url)) {
// и это тоже ...
}
/* УБИРАЕМ
$context['title'] = $title;
echo $twig->render($template, $context);
*/
// проверяем если controller не пустой, то рендерим страницу
if ($controller) {
$controller->get();
}
тестим:
работает так же, но в самом роутере, то есть внутри if-а всего одна строчка – создание экземпляра контроллера. Что можно считать своего рода достижением!)
Но мизерным, там ведь и раньше особо ничего не было…
Добавляем контроллер страницы Андромеды
Теперь попробуем создать контроллер AndromedaController
под вывод инфы о андромеде.
Делаем по аналогии, по сути копипастим то, что было в роутере.
То есть указали title, указали шаблон, в getContext() указали специфичные для страницы ключи/значения
теперь идем обратно в index.php
и там пишем:
<?php
require_once "../vendor/autoload.php";
require_once "../controllers/MainController.php";
require_once "../controllers/AndromedaController.php"; // не забываем добавить импорт
// ...
$controller = null;
if ($url == "/") {
$controller = new MainController($twig);
} elseif (preg_match("#^/andromeda#", $url)) {
$controller = new AndromedaController($twig); // тут просто контроллер создаем
// image и info пока не трогаем
if (preg_match("#^/andromeda/image#", $url)) {
// ...
} elseif (preg_match("#^/andromeda/info#", $url)) {
// ...
}
} elseif (preg_match("#^/orion#", $url)) {
// ...
}
// ...
проверяем:
то есть работает так же! =О
Добавляем контроллер страницы изображения Андромеды
Сейчас самое интересное. У нас шаблон страницы Андромеды с картинкой, это по сути базовая страница с Андромедой, плюс дополнительные свойства.
Ну то есть в рамках twig
у нас уже есть двухуровневая иерархия. Теперь такую же иерархию мы делаем на уровне контроллера, то есть создаем контроллер, который наследует уже не TwigBaseController
, а AndromedaController
. Оот так:
опять подправляем index.php
<?php
require_once "../vendor/autoload.php";
require_once "../controllers/MainController.php";
require_once "../controllers/AndromedaController.php";
require_once "../controllers/AndromedaImageController.php"; // добавил
// ...
$controller = null;
if ($url == "/") {
$controller = new MainController($twig);
} elseif (preg_match("#^/andromeda#", $url)) {
$controller = new AndromedaController($twig);
if (preg_match("#^/andromeda/image#", $url)) {
$controller = new AndromedaImageController($twig); // теперь тут AndromedaImageController
} elseif (preg_match("#^/andromeda/info#", $url)) {
// ...
}
} elseif (preg_match("#^/orion#", $url)) {
// ...
}
// ...
тестим:
работает! =)
Добавляем контроллер страницы информации об Андромеде
по аналогии делаем AndromedaInfoController
<?php
require_once "AndromedaController.php";
class AndromedaInfoController extends AndromedaController {
public $template = "andromeda_info.twig";
public function getContext(): array
{
$context = parent::getContext();
// ...
return $context;
}
}
и подключаем в index.php
:
<?php
require_once "../vendor/autoload.php";
require_once "../controllers/MainController.php";
require_once "../controllers/AndromedaController.php";
require_once "../controllers/AndromedaImageController.php";
require_once "../controllers/AndromedaInfoController.php"; // добавил
// ...
$controller = null;
if ($url == "/") {
$controller = new MainController($twig);
} elseif (preg_match("#^/andromeda#", $url)) {
$controller = new AndromedaController($twig);
if (preg_match("#^/andromeda/image#", $url)) {
$controller = new AndromedaImageController($twig);
} elseif (preg_match("#^/andromeda/info#", $url)) {
$controller = new AndromedaInfoController($twig); // теперь и тут только AndromedaInfoController
}
} elseif (preg_match("#^/orion#", $url)) {
// ...
}
// ...
И вот, если посмотреть, то вложенный if нам для андромеды теперь не нужен!
И мы вполне можем сделать структуру роутера плоской, по крайне мере для андромеды, вот так:
if ($url == "/") {
$controller = new MainController($twig);
} elseif (preg_match("#^/andromeda/image#", $url)) {
$controller = new AndromedaImageController($twig);
} elseif (preg_match("#^/andromeda/info#", $url)) {
$controller = new AndromedaInfoController($twig);
} elseif (preg_match("#^/andromeda#", $url)) {
$controller = new AndromedaController($twig);
} elseif (preg_match("#^/orion#", $url)) {
// ...
}
Единственное, обратите внимание что я поменял порядок проверки на совпадение с url, так чтобы более точный шаблоны (с image или c info) шел раньше, чем более общий (типа #^/andromeda#)
Ну для второго объекта я думаю вы сами сделаете. Но прежде чем начнете,
Создаем 404 страницу
давайте еще сделаем 404 страницу, то есть это страница, когда ссылка на страницу не существует (ну в нашем случае если, например, контроллер не определен).
Создадим контроллер и называем его Controller404.php
и пишем в него простой код:
<?php
require_once "TwigBaseController.php";
class Controller404 extends TwigBaseController {
public $template = "404.twig";
public $title = "Страница не найдена";
}
я там прописал шаблон 404.twig
давайте его создадим в папке views
:
и загоним в него что-то такое
{% extends "__layout.twig" %}
{% block content %}
<div class="text-center">
<img src="/images/025_picture-47918.gif" alt="">
<div>
Страница не найдена! Шо делать!!!111 =О
</div>
</div>
{% endblock %}
так, и подключим в index.php
<?php
// ...
require_once "../controllers/AndromedaInfoController.php";
require_once "../controllers/Controller404.php"; // добавил
// ...
$context = [];
$controller = new Controller404($twig); // теперь 404 будут нашем контроллером по умолчанию
if ($url == "/") {
// ...
так как для Ориона я еще контроллеры не делал, то по идее тыкнув на страницу Ориона, я как раз должен вызвать этот контроллер. Пробуем:
тут еще один момент, у нас 404 страница не совсем настоящая получается. И не потому что я не написал на ней 404, а потому что код ответа, который приходит вместе с этой страницей не 404, а 200.
Код ответа можно посмотреть следующим образом. Нажмите в браузере F12
или Ctrl+Shift+I
найдите вкладку Network (или Сеть), и там найдите самый первый запрос:
вот то что в столбце Status – это и есть код возврата страницы. Чтобы сделать его настоящим 404, надо добавить в контроллер вызов специальной функции http_response_code:
<?php
require_once "TwigBaseController.php";
class Controller404 extends TwigBaseController {
public $template = "404.twig";
public $title = "Страница не найдена";
public function get()
{
http_response_code(404); // с помощью http_response_code устанавливаем код возврата 404
parent::get(); // вызываем базовый метод get(), который собственно уже отрендерит страницу
}
}
запустим еще раз, и снова глянем что в столбце статус:
вот теперь другое дело!)