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

Содержание:


Введение

Blade — это простой но мощный шаблонизатор, который постовляется вместе с Ларавел. В отличие от других популряных шаблонизаторов, Блейд не запрещает вам использовать стандартный PHP код в ваших представлениях. По факту, все представления Blade собираются в код PHP, кэшируются и хранятся до момента модификации. Это означает, что фактически Blade не создаёт дополнительный слой нагрузки на ваше приложение. Файлы Blade используют расширение .blade.php и обычно хранятся в папке resources/views.


Наследование шаблонов

Определение шаблона

Есть два главных преимущества использования Blade: наследование шаблонов и секции. Чтобы начать, давайте взглянем на простой пример. Во-первых, мы проверим шаблон страницы "master". В силу того, что большое количество приложений имеют один главный шаблон для большого числа страниц, это удобно определить этот шаблон как одиночно представление Blade:

<!-- Stored in resources/views/layouts/app.blade.php -->

<html>
    <head>
        <title>App Name - @yield('title')</title>
    </head>
    <body>
        @section('sidebar')
                    This is the master sidebar.
                @show

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

Как вы можете видеть, этот файл содержит типовую разметку HTML. Обратите внимание на директивы @section и @yield. Директива @section подгружает содержимое, а директива @yield служит заменителем для содержимого секции.

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

Расширение шаблона

Для определиния представления дочерней страницы, используйте директиву Blade @extends, чтобы указать какой шаблон наследует страница. Представление, расширяющее шаблон Blade, может внедрить контент в секцию через директиву @section. Содержимое будет отображаться вместо директивы @yield:

<!-- Stored in resources/views/child.blade.php -->

@extends('layouts.app')

                @section('title', 'Page Title')

                @section('sidebar')
                    @parent

                    <p>This is appended to the master sidebar.</p>
                @endsection

                @section('content')
                    <p>This is my body content.</p>
                @endsection

В это примере, секция sidebar использует директиву @parent, чтобы дополнить содержание в sidebar шаблон. Директива @parent будет земенена содержимым шаблона при загрузке представления.

В противоположность к предыдущему примере, секция sidebar завершается директивой @endsection вместо @show. @endsection только определит секцию, в то время как @show определит и мгновенно дополнит секцию.

Директива @yield также принимает базовое значение как второй параметр. Значение будет отображаться, если секция для заполнения не определена:

@yield('content', View::make('view.name'))

Представления Blade могут быть возвращены из маршрутов используя глобальный помощник view:

Route::get('blade', function () {
    return view('child');
});

Компоненты и слоты

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

<!-- /resources/views/alert.blade.php -->

<div class="alert alert-danger">
    {{ $slot }}
</div>

Переменная {{ $slot }} будет включать содержимое, которое мы бы хотели внедрить в компонент. Теперь, чтобы построить компонент, мы можем использовать директиву Blade @component:

@component('alert')
                    <strong>Whoops!</strong> Something went wrong!
                @endcomponent

Чтобы научить Laravel загружать первое представление, которое существует из возможных представлений, вы можете использовать директиву componentFirst:

@componentFirst(['custom.alert', 'alert'])
    <strong>Whoops!</strong> Something went wrong!
@endcomponent

Иногда бывает полезно определить несоклько слотов для компонента. Давайте модифицируем наш компонент тревоги чтобы позволить внедрять заголовок. Именованные слоты могут отображаться путём "вывода" переменной, которая соответствует их имени:

<!-- /resources/views/alert.blade.php -->

<div class="alert alert-danger">
    <div class="alert-title">{{ $title }}</div>

    {{ $slot }}
</div>

Теперь мы можем внедрять контент в именованные слоты используя директиву @slot. Любой контент не входящий в директиву @slot будет передан в компонент в переменную $slot:

@component('alert')
                    @slot('title')
                        Forbidden
                    @endslot

                    You are not allowed to access this resource!
                @endcomponent

Передача дополнительных данных в комопоненты

Иногда может потребоваться передать дополнитлеьный данные в компонент. По этой причине, вы можете передать массив данных как второй аргумент в директиву @component. Все данные будут доступны в шаблоне компонента как переменные:

@component('alert', ['foo' => 'bar'])
                    ...
                @endcomponent

Присвоение псевдонимов компонентам

Если ваши компоненты Blade хранятся во внутренних папках, вы можете присвоить псевдоним для более легкого доступа. Например, представьте компонет Blade, который располагается в resources/views/components/alert.blade.php. Вы можете использовать метод component, чтобы обращаться к компоненту не components.alert, а просто alert. Обычно, это можно будет сделать в методе boot вашего AppServiceProvider:

use Illuminate\Support\Facades\Blade;

Blade::component('components.alert', 'alert');

После присвоения псевдонима компоненту, к нему можно обрращаться напрямую:

@alert(['type' => 'danger'])
    You are not allowed to access this resource!
@endalert

Вы можете опустить параметры компонента, если у компонента нет дополнительных слотов:

@alert
    You are not allowed to access this resource!
@endalert

Отображение данных

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

Route::get('greeting', function () {
    return view('welcome', ['name' => 'Samantha']);
});

Вы можете отобразить содержание переменной name таким образом:

Hello, {{ $name }}.
Выражения Blade {{ }} автоматически пропускаются через функцию PHP htmlspecialchars, чтобы предотвратить XSS атаки.

Вы можете показывать любой контент, а не только те переменные, что были переданы в представление. Вы можете также вывести результаты любой функции PHP. По факту, вы можете положить любой код PHP, который вы хотите, во внутрь выражения Blade:

The current UNIX timestamp is {{ time() }}.

Отображение нэкранированных данных

По умолчанию, синтаксис Blade {{ }} автоматически проводит через функцию PHP htmlspecialchars, чтобы предотвратить XSS атаку. Если вы не хотите, чтобы часть данных могли пропасть, вы можете использовать следующий синтаксис:

Hello, {!! $name !!}}.
Будьте осторожны при выводе контента пользователями вашего приложения. Двойные скобки надёжно защищают от XSS атак.

Отображение JSON

Иногда вы можете передавать массив в ваше представление с намерением отразить его как JSON для инициализации переменной JavaScript. Например:

<script>
    var app = <?php echo json_encode($array); ?>;
</script>

Вы можете использовать директиву Blade @json вместо функции PHP json_encode. Директива @json принимает такие же аргументы, как и функция PHP json_encode:

<script>
    var app = @json($array);

    var app = @json($array, JSON_PRETTY_PRINT);
</script>
Вы должны использовать директиву @json для отображения существующих перевенных как JSON. Шаблонизатор Blade основывается на регулярных выражениях и попытка передать сложное выражение в директиву может привести к неудаче.

Директива @json также полезна для наполнения компонентов Javascript фреймворка Vue или атрибутов data-*:

<example-component :some-prop='@json($array)'></example-component>
Использование @json в атрибутах элемента предполагает, чтобы он был окружен в одинарных кавычках.

Кодировка элементов HTML

По умолчанию, Blade проводит двойную кодировку элементов HTML. Еслиы вы хотите отменить двойное кодирование, вызовите метод Blade::withoutDoubleEncoding из метода boot вашего AppServiceProvider:

<?php

namespace App\Providers;

use Illuminate\Support\Facades\Blade;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        Blade::withoutDoubleEncoding();
    }
}

Blade и JavaScript фреймворки

В силу того, что фрейворки JavaScript также используют двойные скобки в своих целях, мы можем использовать символ @, чтобы проинформировать движок Blade, что данных элемент необходимо оставить нетронутым. Например:

<h1>Laravel</h1>

Hello, @{{ name }}.

В этом примере, символ @ будет убран Blade. Однако, выражение {{ name }} не будет тронутов Blade, что позволяет выражению быть обработанным фреймворком JavaScript.

Директива @verbatim

Если вы показываете переменные JavaScript в значительной части вашего шаблона, вы можете обернуть HTML в директиву @verbatim, поэтому вам не придётся прописывать символ @ для каждого вывода:

@verbatim
                    <div class="container">
                    Hello, {{ name }}.
                    </div>
                @endverbatim

Структуры контроля

В дополнение к функциям отображения данных, Blade также даёт удобные инструменты для типовых структур котнтроля PHP таких как условия и циклы. Механика инфструментов похожа на одноимённые функции PHP, самое главное эти инструменты делают код чистым и понятным.

Оператор If

Вы можете сконструировать выражение if используя директивы @if, @elseif, @else, и @endif. Эти директивы функционируют идентично своим PHP-аналогам:

@if (count($records) === 1)
                    I have one record!
                @elseif (count($records) > 1)
                    I have multiple records!
                @else
                    I don't have any records!
                @endif

Для удобства, Blade также предоставляет директиву @unless:

@unless (Auth::check())
                    You are not signed in.
                @endunless

В дополнение к директивам условий, которые мы только что обсудили, есть директивы @isset и @empty, которые выполняют те же функции, что одноимённые аналоги PHP функций:

@isset($records)
                    // $records is defined and is not null...
                @endisset

                @empty($records)
                    // $records is "empty"...
                @endempty

Директивы аутентификации

Директивы @auth и @guest могут быть использованы для быстрого определения, что текущий пользователь аутенцифицирован или является гостем:

@auth
                    // The user is authenticated...
                @endauth

                @guest
                    // The user is not authenticated...
                @endguest

Если необходимо, вы можете указать защитника аутентификации который должен быть выбран при использовании директив @auth и @guest:

@auth('admin')
                    // The user is authenticated...
                @endauth

                @guest('admin')
                    // The user is not authenticated...
                @endguest

Директивы секции

Вы можете проверить, что у секции есть контент, используя директиву @hasSection:

@hasSection('navigation')
                    <div class="pull-right">
                    @yield('navigation')
                    </div>

                    <div class="clearfix"></div>
                @endif

Оператор Switch

Выражения Switch могут быть сконструированы используя директивы @switch, @case, @break, @default и @endswitch:

@switch($i)
                    @case(1)
                    First case...
                    @break

                    @case(2)
                    Second case...
                    @break

                    @default
                    Default case...
                @endswitch

Циклы

В дополнение к выражениям условий, Blade предоставляет простые директивы для работы с циклами. Опять же, для каждой директивы Blade есть аналог функции PHP:

@for ($i = 0; $i < 10; $i++)
                    The current value is {{ $i }}
                @endfor

                @foreach ($users as $user)
                    <p>This is user {{ $user->id }}</p>
                @endforeach

                @forelse ($users as $user)
                    <li>{{ $user->name }}</li>
                @empty
                    <p>No users</p>
                @endforelse

                @while (true)
                    <p>I'm looping forever.</p>
                @endwhile
При использовании цикла, вы можете использовать переменные цикла, чтобы получить информацию о цикле, такую как первая или последняя это итерация цикла.

При использовании циклов вы можете завершить цикл или пропустить текущую итерацию:

@foreach ($users as $user)
                    @if ($user->type == 1)
                        @continue
                    @endif

                    <li>{{ $user->name }}</li>

                    @if ($user->number == 5)
                        @break
                    @endif
                @endforeach

Вы можете также включить условия с декларацией условия в одну строку:

@foreach ($users as $user)
                    @continue($user->type == 1)

                    <li>{{ $user->name }}</li>

                    @break($user->number == 5)
                @endforeach

Переменные цикла

При использовании цикла, переменная $loop будет доступна внутри вашего цикла. Эта переменная даёт доступ к полезной информации, такой как интекс текущей итерации, первая или последняя это итерация:

@foreach ($users as $user)
                    @if ($loop->first)
                        This is the first iteration.
                    @endif

                    @if ($loop->last)
                        This is the last iteration.
                    @endif

                    <p>This is user {{ $user->id }}</p>
                @endforeach

При использовании вложенных циклов, вы можете получить доступ к родительской переменной $loop через свойство parent:

@foreach ($users as $user)
                    @foreach ($user->posts as $post)
                        @if ($loop->parent->first)
                            This is first iteration of the parent loop.
                        @endif
                    @endforeach
                @endforeach

Переменная $loop также содержит набор других полезный свойств:

Свойство Описание
$loop->index Индекс текущей итерации цикла (начинается с 0).
$loop->iteration Итерация текущего цикла (начинается с 1).
$loop->remaining Итерации, оставшиеся в цикле.
$loop->count Общее число элементов в массиве под итерацией.
$loop->first Первая ли это итерация.
$loop->last Последняя ли это итерация в цикле.
$loop->even Является ли это четной итерацией цикла.
$loop->odd >Является ли это нечетной итерацией цикла.
$loop->depth Уровень вложенности текущего цикла.
$loop->parent Для вложенных циклов, переменная родительского цикла.

Комментарии

Blade также позволяет делать комментарии для ваших представлений. Но в отличие от HTML комментариев, комментарии Blade не включаются в отдаваемый приложением HTML:

{{-- This comment will not be present in the rendered HTML --}}

Чистый PHP

В некоторых ситуациях, бывает полезно внедрить чистый PHP код в ваше представление. Вы можете использовать директиву Blade @php, чтобы выполнить блок кода PHP внутри шаблона:

@php
    //
                @endphp
Blade предоставляет возможность использовать PHP код напрямую. Но слишком частое использование может свидетельствовать о том, что вы слишком много логики передаёте в шаблон.

Формы

Поле CSRF

Каждый раз определяя HTML форму в вашем приложении, вы должны включать скрытое поле CSRF токена в форму так, чтобы посредник смог проверить запрос. Вы можете использовать директиву Blade @csrf, чтобы сгенерировать поле токена:

<form method="POST" action="/profile">
    @csrf

    ...
</form>

Поле Method

В силу того, что HTML формы не могут сделать запросы PUT, PATCH, или DELETE вам будет необходимо добавить скрытое поле _method, чтобы подделать эти методы. Директива Blade @method может создать это поле за вас:

<form action="/foo/bar" method="POST">
    @method('PUT')

    ...
</form>

Ошибки валидации

Директива @error может быть использована для быстрой проверки на наличие ошибок валидации для данного атрибута. С директивой @error, вы можете вывести переменую $message, чтобы показать сообщение ошибки:

<!-- /resources/views/post/create.blade.php -->

<label for="title">Post Title</label>

<input id="title" type="text" class="@error('title') is-invalid @enderror">

@error('title')
    <div class="alert alert-danger">{{ $message }}</div>
@enderror

Вы можете передать имя ошибки, как второй параметр в директиву @error, чтобы получить сообщение ошибки валидации на страницах, которые содержат несколько форм:

<!-- /resources/views/auth.blade.php -->

<label for="email">Email address</label>

<input id="email" type="email" class="@error('email', 'login') is-invalid @enderror">

@error('email', 'login')
    <div class="alert alert-danger">{{ $message }}</div>
@enderror

Вставка других представлений

Директива Blade @include позволяет вам включать в представление другое представления. Все переменные, доступные для родительского представления, будут доступны и для включаемого представления:

<div>
    @include('shared.errors')

    <form>
        <!-- Form Contents -->
    </form>
</div>

Кроме данных родителя, доступных дочернему представлению, вы также можете передать дополнительные данные во включаемое представление:

@include('view.name', ['some' => 'data'])

Если вы пытаетесь включить представление, которое не существует, Ларавел выкинет ошибку. Если вы хотите включить представление, которого может не быть, вам следует использовать директиву @includeIf:

@includeIf('view.name', ['some' => 'data'])

Если вы хотите включить представление в зависимости от данных логических условий, вы можете использовать директиву @includeWhen:

@includeWhen($boolean, 'view.name', ['some' => 'data'])

Чтобы включить первое представление, которое существует из представленного массива, вы можете использовать директиву includeFirst:

@includeFirst(['custom.admin', 'admin'], ['some' => 'data'])
Вам стоит избегать использования __DIR__ и __FILE__ в вашем представлении Blade, в силу того, что они будет ссылаться на закешированное, собранное представление.

Присвоение псевдонимов включениям

Если вы включаете в шаблон представления во вложенных папках, вы можете назвать их для более лёгкого доступа. Например, представьте, что вы включаете представление по адресу resources/views/includes/input.blade.php со следующим содержимым:

<input type="{{ $type ?? 'text' }}">

Вы можете использовать метод include, чтобы представлению includes.input дать имя input. Обычно, это нужно сделать в методе boot вашего AppServiceProvider:

use Illuminate\Support\Facades\Blade;

Blade::include('includes.input', 'input');

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

@input(['type' => 'email'])

Генерация предствлений для контроллера

Вы можете совместить циклы и включения в одной линии с помощью директивы Blade @each:

@each('view.name', $jobs, 'job')

Первый аргумент — это часть представления для визуализации каждого элемента в массиве или коллекции. Второй аргумент — это массив или коллекция вы хотите перебрать. Третий аргумент — это имя переменной, которая будет приписана к текущей итерации внутри представления. Например, если вы проводите итерацию массива jobs, скорее всего вы захотите получить доступ к профессии как переменная job внутри части представления. Ключ текущей итерации будет доступен как переменаня key внутри вашей части представления.

Вы также можете передать 4-ый аргумент в директиву @each. Этот аргумент определит представление, которое будет отображаться, при условии пустого массива.

@each('view.name', $jobs, 'job', 'view.empty')
Представления отображённые через @each не наследуют переменные из родительского представления. Если дочернее представление требует эти переменные, вы должны использовать @foreach или @include.

Стэки

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

@push('scripts')
                    <script src="/example.js"></script>
                @endpush

Вы можете проталкивать стэки так часто, как это необходимо. Чтобы отобразить полное содержание стэка, передайте имя стэка в директиву @stack:

<head>
    <!-- Head Contents -->

    @stack('scripts')
</head>

Если вы хотите вписать контент в начало стэка, вы должны использовать директиву @prepend:

@push('scripts')
                    This will be second...
                @endpush

// Later...

@prepend('scripts')
                    This will be first...
                @endprepend

Сервис инъекция

Директива @inject может быть использована, чтобы извлечь сервис из сервис контейнера. Первый аргумент, передаваемый в @inject, это имя переменной, куда поставится сервис, а второй аргумент это класс или имя интерфейса, который вы желаете получить:

@inject('metrics', 'App\Services\MetricsService')

<div>
    Monthly Revenue: {{ $metrics->monthlyRevenue() }}.
</div>

Расширение Blade

Blade позволяет вам определить ваши пользовательские директивы, используя метод directive. Когда компилятор Blade встречает пользовательскую директиву, будет запущена обратная связь с выражением, которым содержит директива.

Следующий пример создаёт директиву @datetime($var), которая форматирует $var, которая должена быть экземпляром DateTime:

<?php

namespace App\Providers;

use Illuminate\Support\Facades\Blade;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register bindings in the container.
     *
     * @return void
     */
    public function register()
    {
        //
    }

    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        Blade::directive('datetime', function ($expression) {
            return "<?php echo ($expression)->format('m/d/Y H:i'); ?>";
        });
    }
}

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

<?php echo ($var)->format('m/d/Y H:i'); ?>
После обновления догики директивы Blade. вам необходимо удалить все закешированные представления Blade. Это можно сделать используя команду Artisan view:clear.

Пользовательские If-операторы

Программирование пользовательских директив может быть более сложным, чем необходимо, для определения простых выражений состоящих из условий. По этой причине, Blade предоставляет метод Blade::if, который позволяет вам быстро определить пользовательские условные директивы используя замыкание. Например, давайте определим условие, которое проверяет текущее окружение приложения. Мы можем сделать это используя метод boot вашего AppServiceProvider:

use Illuminate\Support\Facades\Blade;

/**
 * Bootstrap any application services.
 *
 * @return void
 */
public function boot()
{
    Blade::if('env', function ($environment) {
        return app()->environment($environment);
    });
}

Пссле определения пользовательского условия, мы можем легко использовать его в наших шаблонах:

@env('local')
    // The application is in the local environment...
@elseenv('testing')
    // The application is in the testing environment...
@else
                    // The application is not in the local or testing environment...
                    @endenv