Подключаем шаблонизатор Twig для работы с разметкой более грамотно
Бэкенд / Подключение процессора шаблонов Twig
Идем дальше по дорожке развития PHP приложения.
Современные веб приложения очень трепетно относятся к организации кода.
За что в первую очередь критиковали PHP, а за то что он по сути не отделяет вёрстку от кода А при росте сложности приложения превращается в лапшу.
Для решения этой проблему решили, что пусть php файлы содержат только код. А разметка пусть где-то отдельно лежит.
Давайте попробуем так сделать.
Создадим новый проект.
то есть две папки public
и views
. Папка views
пустая, а в папке public
наша точка входа index.php
.
Мы этот проект создаем чисто в целях разобраться. Потом надо будет вернуться к предыдущему. Так что вы его не в коем случае не удаляйте. Вам придется повторить на нем все что мы тут сейчас сделаем.
Переключаемся на новый проект
И так, мы решили, что в php файлов не должно быть верстки. Значит верстку будем пихать в папку views
, будем использовать просто html файлы.
Возьмем картинки mermaid.jpg uranus.png, и положим их в папку images
Ну и файлики сделаем
и вот вроде бы как все хорош. Отдельно файлики, отдельно код.
Но теперь возникает другая проблема. А как мне сделать навигацию, да так чтобы поменьше кода писать? Или как мне например передать данные в html файл и чтобы он их обработал, но чтобы php код писать не пришлось.
В общем, для решения этой проблемы были придуманы так называемый Templates engines
, то есть шаблонизаторы по-русски.
Помимо того, что шаблонизаторы упрощают написание динамических элементов (как например добавление класс active в прошлой задачке) плюс ко всему они добавляют возможность строить иерархию шаблонов, то есть реализовывать наследование.
Ну типа, есть у вас базовый шаблон страницы где присутствует допустим навигация, блок под общее содержимое и футер. И вот вы хотите сделать страницу с новостями. Вы просто создаете файлик указываете в нем что наследуетесь от базового шаблона и переопределяете отображение блока под содержимое. И усе =О
Twig
Мы будем использовать один из самых популярных шаблонизаторов, а именно Twig https://twig.symfony.com
чтобы начать его использовать, надо его сначала установить. Для установки дополнительных пакетов в php используется специальная утилита composer
. Она скачивает дополнительные библиотечки из интернета и кладет их в папку vendor
В общем, тыкаем на Terminal
это запустит нам консольку вместе с настроенными переменными окружения
тут есть важный момент, путь который написан в этой строке должен соответствовать папке в которой лежит ваш проект
если по какой-то причине там какой-то другой путь. То вам надо перейти в консольке в папку с вашим проектом.
Для этого найдите папку с вашим проектом. Скопируйте путь к этой папке
и введите в консольке команду
cd П:\уть\к\папке\с\вашим\проектом
в моём случае будет
cd C:\Users\m\Desktop\php_01
Теперь можно попробовать написать composer и посмотреть, что выведет:
тут информация как библиотечкой управлять, вдруг кому полезна будет
Тут нам важно одно, так как composer по умолчанию ставит пакеты в текущую папку, то в целях безопасности надо обязательно выйти из папки public, если у вас путь оканчивается на public
то вызовете следуюущую команду чтобы подняться на уровень выше:
cd ..
теперь идем на сайт twig https://twig.symfony.com/doc/3.x/intro.html#installation и смотрим как правильно установить twig
там вот что написано:
то есть надо ввести команду.
К сожалению, версия composer, которая идет с laragon, устаревшая, так что придется ее обновить. Идем на сайт https://getcomposer.org/download/, скролим вниз и качаем последнюю версию и сохраняем в папку laragon-portable\bin\composer
после того как скачали, проверяем в консольке laragon версию
composer -V
должно выдать более менее актуальную дату
Ну теперь можно попробовать запустить команду установки
composer require "twig/twig:^3.0"
правда в политехе интернет работает через прокси, поэтому сначала надо настроить прокси в консоли, для этого введите сначала команду утсановки переменной окружения, а потом уже команду установки
set http_proxy=172.27.100.5:4444
composer require "twig/twig:^3.0"
и ждем пока установится
и смотрим что у нас появилось в папке
давайте теперь попробуем воспользоваться twig.
Открываем index.php и пишем
<?php
// подключаем пакеты которые установили через composer
require_once '../vendor/autoload.php';
// создаем загрузчик шаблонов, и указываем папку с шаблонами
// \Twig\Loader\FilesystemLoader -- это типа как в C# писать Twig.Loader.FilesystemLoader,
// только слеш вместо точек
$loader = new \Twig\Loader\FilesystemLoader('../views');
// создаем собственно экземпляр Twig с помощью которого будет рендерить
$twig = new \Twig\Environment($loader);
$url = $_SERVER["REQUEST_URI"];
// ..
кстати если у вас подсвечивается красным имена классов поставьте плагин PHP Intelephense
и после установки, закройте и откройте студию чтобы он подцепил классы Twig.
Рендерим с помощью twig
идея в общем такая, мы вместо того чтобы подключить файлы с помощью require будем вызывать команды twig а он будет уже собственно ренедрить за нас файлы, делается так:
<?php
// ...
$url = $_SERVER["REQUEST_URI"];
if ($url == "/") {
// это убираем require "../views/main.html";
echo $twig->render("main.html");
} elseif (preg_match("#/mermaid#", $url)) {
// и это тоже require "../views/mermaid.html";
echo $twig->render("mermaid.html");
} elseif (preg_match("#/uranus#", $url)) {
// и вот это require "../views/uranus.html";
echo $twig->render("uranus.html");
}
пока особых преимуществ не видно, ну только что ../views/
не надо писать
можно потыкать как работает, по идее должно работать как обычно:
сразу ставим себе плагин для работы с twig
Теперь, сделаем базовый шаблон разметки, назовем его __layout.twig
загоняем туда стандартную разметку html (через ! + Tab
)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!-- но с небольшой правкой, это будет место под индивидуальный код страниц -->
{% block content %}
пустота
{% endblock %}
</body>
</html>
теперь давайте наследуем этот шаблон нашими страницами. Сначала идем в main.html
и правим там:
{% extends "__layout.twig" %}
<a href="/mermaid">Русалка</a>
<a href="/uranus">Уран</a>
проверяем:
получаем ошибку что у нас в main.html разметка находится во вне какого-нибудь блока который определен в шаблоне. Давайте запихаем ее вот так:
{% extends "__layout.twig" %}
{% block content %}
<a href="/mermaid">Русалка</a>
<a href="/uranus">Уран</a>
{% endblock %}
ну рендерится теперь
но все равно пока не впечатляет… о чем сыр-бор то?
Терпенье мой друг! =О
Давайте теперь тоже самое сделаем с остальными файлами, с mermaid.html
{% extends "__layout.twig" %}
{% block content %}
<img src="/images/mermaid.jpg" style="width: 300px;"/>
{% endblock %}
и с uranus.html
{% extends "__layout.twig" %}
{% block content %}
<img src="/images/uranus.png" style="width: 300px;"/>
{% endblock %}
а теперь сделаем магию, возьмем и в нашем базовом шаблоне добавим навигацию:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div><!-- добавил -->
<a href="/mermaid">Русалка</a>
<a href="/uranus">Уран</a>
</div>
{% block content %}
пустота
{% endblock %}
</body>
</html>
смотрим:
Ооо! Меню теперь везде есть, на главной странице даже дважды, давайте там чего-нибудь напишем:
{% extends "__layout.twig" %}
{% block content %}
Я главная страница
{% endblock %}
а в шаблоне добавим ссылку на главную страницу
<div>
<a href="/">Главная</a> <!-- добавил -->
<a href="/mermaid">Русалка</a>
<a href="/uranus">Уран</a>
</div>
{% block content %}
пустота
{% endblock %}
О как:
Передаем данные в шаблон
Прежде чем что-то делать, давайте переименуем наши шаблоны в twig формат, чтобы нормально работал плагин для Twig
и в index.php поменяем:
$url = $_SERVER["REQUEST_URI"];
if ($url == "/") {
echo $twig->render("main.twig");
} elseif (preg_match("#/mermaid#", $url)) {
echo $twig->render("mermaid.twig");
} elseif (preg_match("#/uranus#", $url)) {
echo $twig->render("uranus.twig");
}
в любой шаблон мы можем передать данные в виде словарика. Ну, например, у нас страница всегда называется Document
давайте будем передавать название страницы в нашем роутере (я роутером называю наши ifы которые решают какой шаблон рендерить)
if ($url == "/") {
echo $twig->render("main.twig", [
"title" => "Главная" // в квадратных скобках создаем словарик с ключем title, значением "Главная"
]);
} elseif (preg_match("#/mermaid#", $url)) {
echo $twig->render("mermaid.twig", [
"title" => "Русалка"
]);
} elseif (preg_match("#/uranus#", $url)) {
echo $twig->render("uranus.twig", [
"title" => "Уран"
]);
}
а теперь подключим эту значение в шаблоне, идем в __layout.twig
и вместо Document вставляем:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ title }}</title> <!-- заменил Document на {{ title }}-->
</head>
<body>
<!-- ... -->
то есть в двойных фигурных скобках мы указываем ключ значение, которого мы хотели бы вывести.
Смотрим на название вкладки
красота! =)
вообще у нас сейчас много копипасты в index.html, давайте немного отрефакторим наш код:
// ...
$url = $_SERVER["REQUEST_URI"];
// добавил две переменные
$title = "";
$template = "";
// тут теперь просто заполняю значение переменных
if ($url == "/") {
$title = "Главная";
$template = "main.twig";
} elseif (preg_match("#/mermaid#", $url)) {
$title = "Русалка";
$template = "mermaid.twig";
} elseif (preg_match("#/uranus#", $url)) {
$title = "Уран";
$template = "uranus.twig";
}
// рендеринг делаем один раз по заполненным переменным
echo $twig->render($template, [
"title" => $title
]);
Обобщаем страницы с картинками
Давайте теперь поглядим на страницы с картинками, они в принципе ничем не отличаются, кроме как адресом картинки. А раз так, давайте сделаем один шаблон под картинки:
вопрос только что в адресе писать?
Вообще, можно завести переменную под картинку, вот так:
$title = "";
$template = "";
$image = ""; // добавил переменную
if ($url == "/") {
$title = "Главная";
$template = "main.twig";
} elseif (preg_match("#/mermaid#", $url)) {
$title = "Русалка";
$template = "base_image.twig"; // используем шаблон base_image теперь
$image = "/images/mermaid.jpg"; // заполняю
} elseif (preg_match("#/uranus#", $url)) {
$title = "Уран";
$template = "base_image.twig"; // и тут тоже base_image
$image = "/images/uranus.png"; // и здесь заполняю
}
echo $twig->render($template, [
"title" => $title,
"image" => $image, // передаю
]);
теперь можно в шаблоне base_image.twig
подцепить:
{% extends "__layout.twig" %}
{% block content %}
<img src="{{ image }}" style="width: 300px;"/>
{% endblock %}
тестируем:
можно удалить отдельные шаблоны под русалку и уран:
вроде все ок, единственное у нас получается, что image передается и на главную страницу хотя там не нужен. Это не очень грамотно с точки зрения архитектуры.
Поэтому сделаем немного по-другому. Мы создадим пустой словарик который будет заполнятся нужными ключ-значениями по необходимости, вот так:
// ...
$url = $_SERVER["REQUEST_URI"];
$title = "";
$template = "";
// $image = ""; убираем
$context = []; // наш словарик, данные для шаблона принято называть контекстом
if ($url == "/") {
$title = "Главная";
$template = "main.twig";
} elseif (preg_match("#/mermaid#", $url)) {
$title = "Русалка";
$template = "base_image.twig";
$context['image'] = "/images/mermaid.jpg"; // передаем в контекст ключ image
} elseif (preg_match("#/uranus#", $url)) {
$title = "Уран";
$template = "base_image.twig";
$context['image'] = "/images/uranus.png"; // и тут передаем в контекст ключ image
}
// название не пихаю в контекст в роутере,
// потому что это отдельная сущность, общая для всех
$context['title'] = $title;
// ну и рендерю
echo $twig->render($template, $context);
Ну не прекрасно ли! =)