Шаблонизатор

Формирование HTML во фреймворках — отдельная нетривиальная тема. Конечно, никто не запрещает создавать HTML напрямую в виде строки, но такой способ просто перестает работать на реальных сайтах, где HTML одной страницы — это сотни, а то и тысячи строк.

<?php

$app->get('/courses/{name}', function ($request, $response, array $args) use ($courses) {
    $slug = $args['name'];
    $course = $courses[$slug];
    return $response->write("<h1>{$course->name}</h1>")
      ->write("<div>{$course->body}</div>");
});

У такого подхода масса недостатков:

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

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

Для начала необходимо установить пакет slim/php-view. Выполните соответствующую команду в корне проекта:

$ composer require slim/php-view php-di/php-di

Измените настройку Slim в начале файла public/index.php так как показано в примере:

<?php

// Контейнеры в этом курсе не рассматриваются (это тема связанная с самим ООП), но если вам интересно, то посмотрите DI Container
use Slim\Factory\AppFactory;
use DI\Container;

$container = new Container();
$container->set('renderer', function () {
    // Параметром передается базовая директория, в которой будут храниться шаблоны
    return new \Slim\Views\PhpRenderer(__DIR__ . '/../templates');
});
$app = AppFactory::createFromContainer($container);
$app->addErrorMiddleware(true, true, true);

В этих строчках происходит подключение шаблонизатора к Слиму, используя DI Container. Наши шаблоны будут храниться в директории templates в корне проекта.

Далее добавьте еще один обработчик:

<?php

$app->get('/users/{id}', function ($request, $response, $args) {
    $params = ['id' => $args['id'], 'nickname' => 'user-' . $args['id']];
    // Указанный путь считается относительно базовой директории для шаблонов, заданной на этапе конфигурации
    // $this доступен внутри анонимной функции благодаря https://php.net/manual/ru/closure.bindto.php
    // $this в Slim это контейнер зависимостей
    return $this->get('renderer')->render($response, 'users/show.phtml', $params);
});

Метод render() выполняет рендеринг указанного шаблона и добавляет результат в ответ. Сама функция принимает на вход три параметра:

  1. Объект ответа
  2. Путь до нужного шаблона внутри директории templates
  3. Набор параметров, которые будут доступны внутри шаблона. Сюда можно передавать все, что угодно.

И последний шаг, добавьте файл templates/users/show.phtml со следующим содержимым:

<h1>Hello, <?= $id ?></h1>

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

Теперь откройте браузер и попробуйте загрузить страницу http://localhost:8080/users/nick.

$ curl localhost:8080/users/nick
<h1>Hello, nick</h1>

Вместо кода <?= $id ?> на экране появилось значение переменной $id. Откуда она взялась в шаблоне? Наш шаблонизатор берет массив $params, который передается третьим параметром в метод render() и создает внутри шаблона локальную переменную на каждый ключ этого массива. Причем имя переменной и ключа совпадают. Этот принцип работает всегда. Если вам нужно вывести данные на странице, то добавляйте их в массив $params и обращайтесь к ним в шаблоне через переменные.

Этой тактики следует придерживаться и для суперглобальных массивов. По понятным причинам их можно использовать напрямую:

<h1><?= $_GET['name'] ?></h1>

Но делать так ни в коем случае нельзя. Такой код значительно сложнее в отладке и потенциально очень опасен (подробнее об этом дальше по курсу).

Собирая все вместе

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

Обработчик

<?php

$app->get('/courses', function ($request, $response) use ($courses) {
    $params = [
        'courses' => $courses
    ];
    return $this->get('renderer')->render($response, 'courses/index.phtml', $params);
});

Шаблон

<table>
  <?php foreach ($courses as $course): ?>
    <tr>
      <td>
          <?= $course['id'] ?>
      </td>
      <td>
          <?= $course['name'] ?>
      </td>
    </tr>
  <?php endforeach ?>
</table>
<!-- END -->

Самостоятельная работа

  1. Измените настройку Slim в начале файла public/index.php как описано в примере из теории. И добавьте обработчик для просмотра страницы пользователя (следующий после настройки пример).
  2. Создайте для него шаблон templates/users/show.phtml. Выведите в нем идентификатор пользователя и никнейм пользователя.
  3. Выполните запрос к странице /users/5. Попробуйте поменять число.

Дополнительные материалы

  1. Альтернативный синтаксис конструкций PHP
  2. $this в Slim