Любите загадки? Событие еще доступно на сайте.
Ищете работу? Мы поможем!
Ищете работу? Мы поможем!

Шаблоны Blade

Вы просматриваете документ для прошлой версии.
Рассмотрите возможность обновления вашего проекта до актуальной версии 10.x. Почему это важно?

Введение

Blade — простой, но мощный шаблонизатор, поставляемый с Laravel. В отличие от других популярных шаблонизаторов для PHP Blade не ограничивает вас в использовании чистого PHP-кода в ваших шаблонах. На самом деле все шаблоны Blade скомпилированы в чистый PHP-код и кешированы, пока в них нет изменений, а значит, Blade практически не нагружает ваше приложение. Файлы шаблонов Blade используют расширение .blade.php и обычно хранятся в директории resources/views.

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

Определение макета

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

<!-- Хранится в 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:

<!-- Хранится в resources/views/child.blade.php -->

@extends('layouts.app')

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

@section('sidebar')
    @parent

    <p>Это дополнение к основной боковой панели.</p>
@endsection

@section('content')
    <p>Это содержимое тела страницы.</p>
@endsection

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

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

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>Ой!</strong> Что-то пошло не так!
@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-шаблоны, обернув переменную в фигурные скобки. Например, для такого роута:

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

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

Hello, {{ $name }}.

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

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

Blade-оператор {{ }} автоматически отправляется через PHP-функцию htmlspecialchars для предотвращения XSS-атак.

Вывод неэкранированных данных

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

Hello, {!! $name !!}.

Будьте очень осторожны и экранируйте переменные, которые содержат ввод от пользователя. Всегда используйте экранирование синтаксисом с двойными скобками, чтобы предотвратить XSS-атаки при отображении предоставленных пользователем данных.

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

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

<h1>Laravel</h1>

Hello, @{{ name }}.

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

Директива @verbatim

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

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

Управляющие конструкции

В дополнение к наследованию шаблонов и отображению данных Blade предоставляет удобные сокращения для распространенных управляющих конструкций PHP, таких как условные операторы и циклы. Эти сокращения обеспечивают очень чистый и краткий способ работы с управляющими конструкциями 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

Циклы

В дополнение к условным операторам Blade предоставляет простые директивы для работы с конструкциями циклов PHP. Данные директивы тоже идентичны их PHP-прообразам:

@for ($i = 0; $i < 10; $i++)
    Текущее значение: {{ $i }}
@endfor

@foreach ($users as $user)
    <p>Это пользователь {{ $user->id }}</p>
@endforeach

@forelse ($users as $user)
    <li>{{ $user->name }}</li>
@empty
    <p>Нет пользователей</p>
@endforelse

@while (true)
    <p>Этот цикл будет длиться вечно.</p>
@endwhile

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

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

@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

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

@foreach ($users as $user)
    @if ($loop->first)
        Это первая итерация.
    @endif

    @if ($loop->last)
        Это последняя итерация.
    @endif

    <p>Это пользователь {{ $user->id }}</p>
@endforeach

Если вы во вложенном цикле, вы можете обратиться к переменной $loop родительского цикла через свойство parent:

@foreach ($users as $user)
    @foreach ($user->posts as $post)
        @if ($loop->parent->first)
            Это первая итерация родительского цикла.
        @endif
    @endforeach
@endforeach

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

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

Комментарии

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

{{-- Этого комментария не будет в итоговом HTML --}}

PHP

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

@php
    //
@endphp

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

Включение подшаблонов

Включение подшаблонов является повторение функционала компонентов, только в “старом” стиле.

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

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

    <form>
        <!-- Содержимое формы -->
    </form>
</div>

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

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

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

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

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

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

Вам следует избегать использования констант __DIR__ и __FILE__ в ваших Blade-шаблонах, поскольку они будут ссылаться на расположение кешированных, скомпилированных шаблонов.

Отрисовка шаблонов для коллекций

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

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

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

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

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

Стеки

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

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

“Пушить” в стек можно сколько угодно раз. Для отрисовки всего содержимого стека передайте имя стека в директиву @stack:

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

    @stack('scripts')
</head>

Внедрение сервисов

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

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

<div>
    Месячный доход: {{ $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
{
    /**
     * Выполнение послерегистрационной загрузки сервисов.
     *
     * @return void
     */
    public function boot()
    {
        Blade::directive('datetime', function ($expression) {
            return "<?php echo ($expression)->format('m/d/Y H:i'); ?>";
        });
    }

    /**
     * Регистрация привязок в контейнере.
     *
     * @return void
     */
    public function register()
    {
        //
    }
}

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

<?php echo ($var)->format('m/d/Y H:i'); ?>

После изменения логики директивы Blade вам надо удалить все кешированные шаблоны Blade. Это можно сделать Artisan-командой view:clear.