View в MVC

View в MVC

Сегодня мы сделаем компонент View, то самое “V” в архитектуре MVC. View – это представление, то есть та часть программы, которая формирует то, что видит пользователь.

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

Итак, давайте рассмотрим простейший пример и создадим для начала только шаблон. Путь до него будет следующим: templates/main/main.php

Шаблон main.php

Давайте запишем в него HTML-код для нашей будущей странички
templates/main/main.php

Код доступен только после покупки курса ООП в PHP7.

Теперь давайте откроем наш контроллер MainController и изменим его метод main()
src/MyProject/Controllers/MainController.php

Код доступен только после покупки курса ООП в PHP7.

Теперь откроем http://myproject.loc/ и полюбуемся результатом:

Результат вывода шаблона

Для начала давайте немного облегчим шаблон и вынесем стили в отдельный файл. Для этого в папке www создадим файл styles.css.
www/styles.css

Код доступен только после покупки курса ООП в PHP7.

Теперь подключим этот файл со стилями в шаблоне:

Код доступен только после покупки курса ООП в PHP7.

И снова убедимся, что всё работает.
Отображение при подключении внешних стилей

Давайте теперь попробуем передавать в шаблон переменные. Вместо явно заданных статей сделаем переменную со статьями:
src/MyProject/Controllers/MainController.php

Код доступен только после покупки курса ООП в PHP7.

А теперь выведем эти статьи в шаблоне:
templates/main/main.php

Код доступен только после покупки курса ООП в PHP7.

Если нам понадобится в другом контроллере или другом экшне добавить логику для работы с шаблонами, нам снова придется перечислять список переменных, а затем писать include с указанием полного пути для шаблона. Звучит не очень хорошо. Поэтому мы просто вынесем логику с подключением нужного шаблона в отдельный класс.

Создадим класс View.php по пути src/MyProject/View/View.php
Класс View

В конструкторе этого класса мы будем принимать путь до папки с шаблонами:
src/MyProject/View/View.php

Код доступен только после покупки курса ООП в PHP7.

Помимо этого давайте добавим метод, в который будем передавать имя конкретного шаблона и массив с переменными.

Код доступен только после покупки курса ООП в PHP7.

Функция extract извлекает массив в переменные. То есть она делает следующее: в неё передаётся массив ['key1' => 1, 'key2' => 2], а после её вызова у нас имеются переменные $key1 = 1 и $key2 = 2.

После этого мы просто подключаем файл с нужным шаблоном, получив путь до него, склеив пути до папки с шаблонами и именем конкретного шаблона.

Пришло время опробовать этот код в нашем контроллере. Создадим новый объект View в конструкторе контроллера, а затем внутри экшена вызовем renderHtml().
src/MyProject/Controllers/MainController.php

Код доступен только после покупки курса ООП в PHP7.

Теперь мы можем снова открыть сайт, и убедиться, что всё прекрасно работает.

Буфер вывода

В тот момент, когда мы подключаем файл c HTML-кодом, либо пишем в PHP-коде echo, либо совершаем какой-либо другой вывод данных, эти данные начинают сразу передаваться в поток вывода. И если что-то пойдёт не так, мы не сможем вернуть этот вывод и вывести вместо него какую-нибудь ошибку. Но в PHP есть возможность весь этот поток вывода положить во временный буфер вывода. Выглядит его использование следующим образом:
src/MyProject/View/View.php

Код доступен только после покупки курса ООП в PHP7.

Если вы сейчас попробуете запустить наш скрипт, то увидите пустую страницу. Дело в том, что все данные, которые должны были быть переданы в поток вывода, оказались в переменной $buffer.
Для того, чтобы передать эти данные в поток вывода, достаточно только вывести переменную $buffer.
src/MyProject/View/View.php

Код доступен только после покупки курса ООП в PHP7.

Откройте страничку снова, и убедитесь, что всё вернулось на свои места.

Так в чём же профит? А профит в том, что мы можем обрабатывать ошибки, возникшие в процессе работы с шаблоном. Пока мы с вами не знакомы с понятием «Исключения», давайте предположим, что у нас при подключении шаблона произошла какая-то ошибка. Тогда мы могли бы обработать эту ошибку и не выводить пользователю неправильно отрисованный шаблон. Мы могли бы сделать что-то типа такого:
src/MyProject/View/View.php

Код доступен только после покупки курса ООП в PHP7.

Чуть позже мы вернёмся к обработке возможных ошибок, когда познакомимся с исключениями. А пока оставим этот код в таком состоянии:
src/MyProject/View/View.php

Код доступен только после покупки курса ООП в PHP7.

Реиспользование шаблонов

Давайте в наш контроллер вернём экшн из прошлых уроков, который выводил приветствие.
src/MyProject/Controllers/MainController.php

Код доступен только после покупки курса ООП в PHP7.

Давайте изменим его, чтобы он работал через шаблон.

Код доступен только после покупки курса ООП в PHP7.

Ну и создадим сам шаблон для него.
templates/main/hello.php

Код доступен только после покупки курса ООП в PHP7.

Давайте теперь перейдём по адресу http://myproject.loc/hello/username и увидим, что всё прекрасно сработало:

Рендеринг другого шаблона

А теперь внимательно присмотритесь к нашим двум получившимся шаблонам. Согласитесь, у них всё абсолютно одинаковое, кроме текста, который мы выводим на странице. Давайте это исправим! Всё, что выше нашего контента – вынесем в один файл, всё что ниже – в другой. А в самих наших шаблонах будем эти два файла подключать.

Итак, выносим верхнюю часть (так называемую шапку сайта - хедер) в новый файл templates/header.php
templates/header.php

Код доступен только после покупки курса ООП в PHP7.

Затем выносим нижнюю часть (называемую футером или подвалом) в файл templates/footer.php
templates/footer.php

Код доступен только после покупки курса ООП в PHP7.

После чего редактируем наши шаблоны:
templates/main/main.php

Код доступен только после покупки курса ООП в PHP7.

templates/main/hello.php

Код доступен только после покупки курса ООП в PHP7.

Должна получиться вот такая структура в шаблонах:
Структура папки с шаблонами

После этого заходим на странички http://myproject.loc/hello/username и http://myproject.loc/ и радуемся результату :)

Согласитесь, теперь можно очень просто добавлять новые странички на сайте, а также изменять шапку и футер только в одном месте.

Давайте повторим последовательность шагов, которые необходимо сделать для добавления новой странички:

  1. Добавляем экшн в контроллер (либо создаём ещё и новый контроллер);
  2. Добавляем для него роут в routes.php;
  3. Описываем логику внутри экшена и в конце вызываем у компонента view метод renderHtml();
  4. Создаём шаблон для вывода результата.

Вот и весь View.

Читайте также
Комментарии


tomsonst
tomsonst
// MainController.php

<?php

namespace MyProject\Controllers;
use MyProject\View\View;
class MainController
{
    private $view;

    public function __construct()
    {
        $this->view = new View(__DIR__ . '/../../../templates');
    }

    public function main()
    {
        $articles = [
            ['name' => 'Статья 1', 'text' => 'Текст статьи 1'],
            ['name' => 'Статья 2', 'text' => 'Текст статьи 2'],
        ];
        $title = '';
        $this->view->renderHtml('main/main.php', ['articles' => $articles], $title);
    }

    public function sayHello(string $name)
    {
        $title = 'Страница приветствия';
        $this->view->renderHtml('main/hello.php', ['name' => $name], $title);
    }
}

// View.php 

<?php

namespace MyProject\View;
class View
{
    private $templatesPath;

    public function __construct(string $templatesPath)
    {
        $this->templatesPath = $templatesPath;
    }

    public function renderHtml(string $templateName, array $vars = [], string $title)
    {
        extract($vars);

        ob_start();
        include $this->templatesPath . '/' . $templateName;
        $buffer = ob_get_contents();
        ob_end_clean();

        echo $buffer;
    }
}

// header.php

    <?php 
    if (!empty($title)) {
        $title;
    } else {
    $title = 'Мой блог';
} ?>
    <title><?= $title ?></title>
ivashkevich
ivashkevich

А зачем вам еще один параметр? Можно же title положить в vars, просто еще одно значение.
Выводить можно проще:

<title><?= $title ?? 'Мой блог' ?></title>
Sparky
Sparky

Переменную $title передаю из MainController в renderHtml (в массив $vars). Все работает, но не получается вывести <title> при пустом значении $title. Вывожу так:
<title><?= $title ?? "Мой блог" ?></title>

Если делать проверку if (empty($title)), то выводит что надо. Почему <?= $title ?? "Мой блог" ?> не работает?

ivashkevich
ivashkevich

Оператор ?? работает только с null-ом. Значит там не null, попробуй через var_dump узнать, что в переменной лежит. Скорее всего у тебя там пустая строка =)

Sparky
Sparky

О, да, так и есть. Не знал про Null :)