Шаблоны Blade
11.x
.
Почему это важно?
- Введение
- Наследование шаблонов
- Определение макета
- Наследование макета
- Компоненты и слоты
- Отображение данных
- Фреймворки Blade и JavaScript
- Управляющие конструкции
- Оператор If
- Циклы
- Переменная Loop
- Комментарии
- PHP
- Включение подшаблонов
- Отрисовка шаблонов для коллекций
- Стеки
- Внедрение сервисов
- Наследование Blade
Введение
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
.