Использование шаблонизатора при формировании представления (view) в контроллере

При формирования представления, по моему мнению, удобно использовать шаблонизаторы. У них есть много удобных и полезных возможностей, например, наследование шаблонов, фильтрация, функции, виджеты, переопределение блоков и так далее. Два из рассматриваемых нами фреймворков, а именно: Symfony и Laravel для формирования представлений предоставляют шаблонизаторы «из коробки», а вот Yii по умолчанию для этих целей использует чистый php, хотя не запрещает нам подключить и использовать в рамках фреймворка подходящий шаблонизатор.

Рассмотрим сначала как формируются шаблоны в Yii, а затем перейдём к рассмотрению вопроса использования в представлениях шаблонизаторов в рамках фреймворков Symfony и Laravel.

Yii view & native PHP

Итак, как мы уже сказали, «из коробки» Yii предлагает в шаблонах использовать чистый PHP. Представления, которые мы будем использовать в контроллерах приложении располагаются в папке frontend/views. Для каждого контроллера в данной директории создаётся отдельная папка (например, для контроллера CompanyController нужно создать папку company), а внутри папки будут храниться шаблоны для экшенов, каждому экшену свой шаблон (для экшена actionShow создадим шаблон show.php,  для  actionIndexindex.php). Для базовых шаблонов существует папка frontend/views/layouts, по умолчанию ко всем шаблонам будет применяться базовый шаблон main.php. Чтобы задать другой базовый шаблон для контроллера, надо переопределить в нём свойство $layout, например:

public $layout = 'new_layout';

Чтобы задать иной базовый шаблон по умолчанию для всех контроллеров, надо добавить параметр в «корень» конфигурации frontend/config/main.php:

[
     ...
     'layout' => 'my_new_layout',
     ...
]

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

Внесём изменения в CompanyController.php в методе actionIndex():

<?php
... 
class CompanyController extends Controller {
    public function actionIndex(): string 
    { 
        $companies = Company::find()->orderBy('name')->all();
	$result = [];
		
	foreach ($companies as $company) {
	    $result[] = 'ID: ' . $company->id . '. Company name: ' . $company->name;
	}
		
	return $this->render('index', [
            'companies' => $result,
        ]);
    }
...
}

И создадим файл frontend/views/company/index.php с содержимым:

<?php

/* @var $this yii\web\View */
/* @var $companies array */

use yii\helpers\Html;

$this->title = 'Список компаний';
$this->params['breadcrumbs'][] = $this->title;
?>
<div>
    <h1><?= Html::encode($this->title) ?></h1>
    <?php foreach ($companies as $company): ?>
         <?= $company ?><br>
    <?php endforeach; ?>
</div>

Хорошей практикой является указывать в начале view-файла в комментариях переменные, которые доступны для использования.

В базовом шаблоне main.php, который был у нас «из коробки», есть каркас html страницы, в котором используется поле title, а также реализованы breadcrumbs («хлебные крошки»). В нашем частном шаблоне index.php мы помимо вывода непосредственно списка компаний задаём также title и дополняем цепочку breadcrumbs ещё одним элементом (текущей страницей).

Теперь страница со списком компаний приобрела более благородный вид. Осталось создать подобные представления для остальных страниц нашего приложения.

Более подробно о представлениях в Yii можно узнать из документации: http://www.yiiframework.com/doc-2.0/guide-structure-views.html

Перейдём к рассмотрению шаблонизатора, используемого во фреймворке Symfony.

Шаблонизатор Twig в Symfony

Фреймворк Symfony «из коробки» предлагает нам использовать в приложении довольно мощный шаблонизатор Twig. Согласно документации, каждый шаблон компилируется в PHP класс и затем кешируется, что обеспечивает довольно высокую скорость отображения шаблона.

Шаблоны, общие для всего приложения, хранятся в директории app/Resources/views/, шаблоны бандлов сторонних библиотек хранятся в vendor/path/to/CoolBundle/Resources/views/, а шаблоны бандла FunnyAppBundle будут храниться в директории src/Funny/AppBundle/Resources/veiws/. Для каждого контроллера в данной папке создаётся подпапка с названием контроллера, например, Company, в которой для каждого экшена создаётся отдельный шаблон с расширением *.html.twig, например для экшена listAction мы создадим шаблон list.html.twig.

Расширение базового шаблона app/Resources/views/base.html.twig происходит не средствами фреймворка, как в Yii, а средствами самого шаблонизатора, то есть чтобы расширить (extends) базовый шаблон, нужно в шаблоне src/Funny/AppBundle/Resources/veiws/Company/list.html.twig в начале файла указать:

{% extends 'base.html.twig' %}

Полностью файл list.html.twig будет выглядеть следующим образом:

{% extends 'base.html.twig' %}

{% block title %}Список компаний{% endblock %}

{% block body %}
    <h2>Список компаний</h2>
    {% for company in companies %}
        <p>ID: {{ company.getId() }}. Company name: {{ company.getName() }}</p>
    {% endfor %}
{% endblock %}

Мы переопределяем два блока, определённые в базовом шаблоне: {% block title %} — текст из тега <title> и {% block body %} — основной контент страницы.

В данном примере, помимо возможности наследования шаблонов, продемонстрировано использование цикла {% for %}, который аналогичен циклу foreach в PHP. Для вывода значения переменной или функции используется конструкция {{  }}.

Подобным образом нам необходимо создать шаблоны и для остальных экшенов.

О том, как работать с шаблонами в рамках Symfony, и базовую информацию о шаблонизаторе Twig можно узнать из официальной документации по Symfony: http://symfony.com/doc/current/templating.html

Более подробно с синтаксисом и возможностями шаблонизатора можно ознакомиться на сайте http://twig.sensiolabs.org/.

Рассмотрим теперь, как работает с шаблонами Laravel.

Laravel & шаблонизатор Blade

Laravel предлагает нам использовать шаблонизатор Blade, который поставляется вместе с фреймворком. Согласно документации в шаблонизаторе Blade есть два основных преимущества: наследование шаблонов и секции. Рассмотрим данные преимущества на примере базового шаблона (layout) и шаблона списка компаний, который будет «расширять» базовый шаблон.

Все шаблоны будут располагаться в папке resources/views/, базовый шаблон будет располагаться непосредственно в данной директории, а шаблоны экшенов в поддиректориях.

Создадим базовый шаблон layout.blade.php:

<html>
    <head>
        <title>FunnyApp - @yield('title')</title>
    </head>
    <body>
        @section('header')
            <h1>FunnyApp</h1>
        @show

        <div class="container">
            @yield('content')
        </div>
    </body>
</html>

Отличие директивы @yield от @section в том, что @yield будет выводить контент из секции, которую получит из шаблона наследника, а директива @section сразу задаёт некоторый контент, который в шаблоне-наследнике может быть переопределён или дополнен.

Шаблон для экшена CompanyController@index, который будет располагаться в директории resources/views/company/, мы назовём index.blade.php и поместим туда следующий код:

@extends('layout')

@section('title', 'Список компаний')

@section('header')
    @parent

    <h2>Список компаний</h2>
@endsection

@section('content')
    @foreach ($companies as $company)
        ID: {{ $company->id }} Company name: {{ $company->name }} <br>
    @endforeach
@endsection

Директивой @extends мы задаём от какого шаблоны мы наследуемся, а дерективы @section задают контент для тех мест, где базовый шаблон будет запрашивать данные от шаблона наследника, а также расширяют контент в том месте, где он уже задан в базовом шаблоне. Чтобы получить данные из секции базового шаблона используется директива @parent.

Также в данном шаблоне продемонстрирована возможность использования циклов, в данном случае мы проходим по массиву, используя директиву @foreach. Для вывода значения переменных используется конструкция {{ … }}.

Осталось поправить экшен index в контроллере CompanyController:

<?php
namespace App\Http\Controllers;

use App\Models\Company;
use Illuminate\Contracts\View\View;
use Illuminate\Routing\Controller;

class CompanyController extends Controller
{
    /**
    * Displays list of companies.
    *
    * @return View
    */
    public function index(): View
    {
        $companies = Company::all();

        return view('company.index', ['companies' => $companies]);
    }
}

Для вывода данных с помощью шаблона используется глобальная функция (хелпер) view(), которая принимает в качестве первого параметра идентификатор шаблона, а вторым параметром при необходимости можно передать массив переменных, которые будут доступны в данном шаблоне.

Идентификатор шаблона состоит из двух частей: названия директории (относительно resources/views), в которой находится данный шаблон, и названия самого шаблона (без суффикса blade.php). Разделяются данные части точкой. В случае, если шаблон имеет более одного уровня вложенности, то названия директорий, составляющих путь до шаблона, также разделяются с помощью точки.