Skip to content

3.8. Шаблоны Blade

1. Введение

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

Шаблоны Blade могут быть возвращены из маршрутов или контроллеров с использованием глобального помощника view. Разумеется, как указано в документации по представлениям, данные могут быть переданы в шаблон Blade через второй аргумент помощника view:

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

1.1. Ускорение работы Blade с помощью Livewire

Хотите вывести свои шаблоны Blade на новый уровень и легко создавать динамические интерфейсы? Ознакомьтесь с Laravel Livewire. Livewire позволяет писать компоненты Blade, дополненные динамическими возможностями, которые обычно возможны только с использованием фронтенд-фреймворков, таких как React или Vue. Это отличный подход для создания современных реактивных интерфейсов без сложностей, рендеринга на стороне клиента и этапов сборки, присущих многим JavaScript-фреймворкам.

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

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

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

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

Hello, {{ $name }}.
lightbulb

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

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

Текущая метка времени UNIX: {{ time() }}.

2.1. Кодирование HTML-сущностей

По умолчанию Blade (и функция Laravel e) будет выполнять двойное кодирование HTML-сущностей. Если вы хотите отключить двойное кодирование, вызовите метод Blade::withoutDoubleEncoding из метода boot вашего AppServiceProvider:

<?php
 
namespace App\Providers;
 
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\ServiceProvider;
 
class AppServiceProvider extends ServiceProvider
{
/**
* Инициализация любых сервисов приложения.
*/
public function boot(): void
{
Blade::withoutDoubleEncoding();
}
}

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

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

Hello, {!! $name !!}.
exclamation

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

2.2. Blade и JavaScript-фреймворки

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

<h1>Laravel</h1>
 
Hello, @{{ name }}.

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

Символ @ также может использоваться для экранирования директив Blade:

{{-- Blade template --}}
@@if()
 
<!-- HTML output -->
@if()

2.2.1. Рендеринг JSON

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

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

Однако вместо ручного вызова json_encode вы можете использовать метод-директиву Illuminate\Support\Js::from. Метод from принимает те же аргументы, что и функция PHP json_encode, но гарантирует, что результирующий JSON будет корректно экранирован для включения в HTML-кавычки. Метод from вернёт строку с JavaScript-выражением JSON.parse, которое преобразует переданный объект или массив в корректный JavaScript-объект:

<script>
var app = {{ Illuminate\Support\Js::from($array) }};
</script>

Последние версии скелета приложения Laravel включают фасад Js, который обеспечивает удобный доступ к этой функциональности в ваших Blade-шаблонах:

exclamation

Вы должны использовать метод Js::from только для рендеринга существующих переменных в формате JSON. Шаблонизатор Blade основан на регулярных выражениях, и попытки передать сложные выражения в директиву могут привести к неожиданным ошибкам.

2.2.2. Директива @verbatim

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

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

3. Директивы Blade

Помимо наследования шаблонов и отображения данных, Blade также предоставляет удобные сокращения для распространённых управляющих структур PHP, таких как условные операторы и циклы. Эти сокращения обеспечивают очень чистый и лаконичный способ работы с управляющими структурами PHP, оставаясь при этом знакомыми их PHP-эквивалентам.

3.1. Условные операторы

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

@if (count($records) === 1)
У меня одна запись!
@elseif (count($records) > 1)
У меня несколько записей!
@else
У меня нет записей!
@endif

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

@unless (Auth::check())
Вы не авторизованы.
@endunless

Помимо уже рассмотренных условных директив, директивы @isset и @empty могут использоваться как удобные сокращения для соответствующих PHP-функций:

@isset($records)
// $records определена и не равна null...
@endisset
 
@empty($records)
// $records является "пустой"...
@endempty

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

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

@auth
// Пользователь аутентифицирован...
@endauth
 
@guest
// Пользователь не аутентифицирован...
@endguest

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

@auth('admin')
// Пользователь аутентифицирован...
@endauth
 
@guest('admin')
// Пользователь не аутентифицирован...
@endguest

3.1.2. Директивы окружения

Вы можете проверить, работает ли приложение в рабочем окружении, с помощью директивы @production:

@production
// Контент для рабочего окружения...
@endproduction

Или вы можете определить, работает ли приложение в конкретном окружении, с помощью директивы @env:

@env('staging')
// Приложение работает в "предпродакшене"...
@endenv
 
@env(['staging', 'production'])
// Приложение работает в "предпродакшене" или в "продакшене"...
@endenv

3.1.3. Директивы секций

Вы можете определить, имеет ли секция шаблонного наследования содержимое, с помощью директивы @hasSection:

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

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

@
@sectionMissing('navigation')
<div class="pull-right">
@include('default-navigation')
</div>
@endif

3.1.4. Директивы сессий

Директива @session позволяет проверить, существует ли указанное значение в сессии. Если значение есть, содержимое шаблона между директивами @session и @endsession будет выполнено. Внутри директивы @session вы можете использовать переменную $value, чтобы вывести значение сессии:

@session('status')
<div class="p-4 bg-green-100">
{{ $value }}
</div>
@endsession

3.2. Операторы Switch

Операторы Switch могут быть построены с помощью директив @switch, @case, @break, @default и @endswitch:

@switch($i)
@case(1)
Первый вариант...
@break
 
@case(2)
Второй вариант...
@break
 
@default
Вариант по умолчанию...
@endswitch

3.3. Циклы

Помимо условных операторов, 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
lightbulb

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

При работе с циклами вы также можете пропустить текущую итерацию или завершить цикл, используя директивы @continue и @break:

@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

3.4. Переменная loop

Во время выполнения цикла foreach внутри цикла будет доступна переменная $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->even Указывает, является ли текущая итерация четной.
$loop->odd Указывает, является ли текущая итерация нечетной.
$loop->depth Уровень вложенности текущего цикла.
$loop->parent Переменная цикла родительского уровня во вложенном цикле.

3.5 Условные классы и стили

Директива @class условно компилирует строку с CSS-классами. Директива принимает массив классов, где ключ массива содержит класс или классы, которые вы хотите добавить, а значение — это булево выражение. Если элемент массива имеет числовой ключ, он всегда будет включен в итоговый список классов:

@php
$isActive = false;
$hasError = true;
@endphp
 
<span @class([
'p-4',
'font-bold' => $isActive,
'text-gray-500' => ! $isActive,
'bg-red' => $hasError,
])></span>
 
<span class="p-4 text-gray-500 bg-red"></span>

Аналогично, директива @style может использоваться для условного добавления встроенных CSS-стилей к HTML-элементу:

@php
$isActive = true;
@endphp
 
<span @style([
'background-color: red',
'font-weight: bold' => $isActive,
])></span>
 
<span style="background-color: red; font-weight: bold;"></span>

3.6. Дополнительные атрибуты

Для удобства вы можете использовать директиву @checked для простого указания, установлен ли данный HTML-checkbox. Эта директива выведет checked, если переданное условие оценивается как true:

<input
type="checkbox"
name="active"
value="active"
@checked(old('active', $user->active))
/>

Аналогично, директива @selected может использоваться для указания, должен ли выбранный вариант в элементе select быть отмечен как "selected":

<select name="version">
@foreach ($product->versions as $version)
<option value="{{ $version }}" @selected(old('version') == $version)>
{{ $version }}
</option>
@endforeach
</select>

Кроме того, директива @disabled может использоваться для указания, должен ли данный элемент быть отключен с помощью атрибута "disabled":

<button type="submit" @disabled($errors->isNotEmpty())>Submit</button>

Более того, директива @readonly может использоваться для указания, должен ли данный элемент быть помечен как "readonly":

<input
type="email"
name="email"
value="email@laravel.com"
@readonly($user->isNotAdmin())
/>

Кроме того, директива @required может использоваться для указания, должен ли данный элемент быть обязательным с атрибутом "required":

<input
type="text"
name="title"
value="title"
@required($user->isAdmin())
/>

3.7. Включение дочерних представлений

lightbulb

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

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

<div>
@include('shared.errors')
 
<form>
<!-- Содержимое формы -->
</form>
</div>

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

@include('view.name', ['status' => 'complete'])

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

@includeIf('view.name', ['status' => 'complete'])

Если вы хотите использовать директиву @include для включения представления, если заданное булево выражение оценивается как true или false, вы можете использовать директивы @includeWhen и @includeUnless:

@includeWhen($boolean, 'view.name', ['status' => 'complete'])
 
@includeUnless($boolean, 'view.name', ['status' => 'complete'])

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

@includeFirst(['custom.admin', 'admin'], ['status' => 'complete'])
exclamation

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

3.7.1. Рендеринг представлений для коллекций

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

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

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

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

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

Представления, рендерящиеся через @each, не унаследуют переменные от родительского представления. Если дочернее представление требует этих переменных, вместо этого следует использовать директивы @foreach и @include.

3.8. Директива @once

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

@once
@push('scripts')
<script>
// Ваш кастомный JavaScript...
</script>
@endpush
@endonce

Поскольку директива @once часто используется в сочетании с директивами @push или @prepend, для вашего удобства доступны директивы @pushOnce и @prependOnce:

@pushOnce('scripts')
<script>
// Ваш кастомный JavaScript...
</script>
@endPushOnce

3.9. Чистый PHP

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

@php
$counter = 1;
@endphp

Или, если вам нужно использовать PHP только для импорта класса, вы можете использовать директиву @use:

@use('App\Models\Flight')

Второй аргумент может быть передан в директиву @use для создания псевдонима для импортированного класса:

@use('App\Models\Flight', 'FlightModel')

3.10. Комментарии

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

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

4. Компоненты

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

Для создания компонента на основе класса вы можете использовать команду Artisan make:component. Чтобы продемонстрировать, как использовать компоненты, мы создадим простой компонент Alert. Команда make:component поместит компонент в директорию app/View/Components:

php artisan make:component Alert

Команда make:component также создаст шаблон представления для компонента. Представление будет размещено в директории resources/views/components. При написании компонентов для вашего приложения компоненты автоматически обнаруживаются в директориях app/View/Components и resources/views/components, поэтому обычно дополнительная регистрация компонента не требуется.

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

php artisan make:component Forms/Input

Команда выше создаст компонент Input в директории app/View/Components/Forms, а представление будет размещено в директории resources/views/components/forms.

Если вы хотите создать анонимный компонент (компонент только с шаблоном Blade и без класса), вы можете использовать флаг --view при вызове команды make:component:

php artisan make:component forms.input --view

Команда выше создаст файл Blade в resources/views/components/forms/input.blade.php, который может быть отображен как компонент через <x-forms.input />.

4.0.1. Ручная регистрация компонентов пакета

При создании компонентов для вашего приложения, компоненты автоматически обнаруживаются в директориях app/View/Components и resources/views/components.

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

use Illuminate\Support\Facades\Blade;
 
/**
* Инициализация сервисов вашего пакета.
*/
public function boot(): void
{
Blade::component('package-alert', Alert::class);
}

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

<x-package-alert/>

Кроме того, вы можете использовать метод componentNamespace для автозагрузки классов компонентов по соглашению. Например, пакет Nightshade может содержать компоненты Calendar и ColorPicker, которые находятся в пространстве имен Package\Views\Components:

use Illuminate\Support\Facades\Blade;
 
/**
* Инициализация сервисов вашего пакета.
*/
public function boot(): void
{
Blade::componentNamespace('Nightshade\\Views\\Components', 'nightshade');
}

Это позволит использовать компоненты пакета через его пространство имен с помощью синтаксиса package-name:::

<x-nightshade::calendar />
<x-nightshade::color-picker />

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

4.1. Рендеринг компонентов

Для отображения компонента вы можете использовать тег компонента Blade в одном из ваших Blade-шаблонов. Теги компонентов Blade начинаются с строки x-, за которой следует имя компонента в формате kebab-case:

<x-alert/>
 
<x-user-profile/>

Если класс компонента находится глубже в директории app/View/Components, вы можете использовать символ . для указания вложенности директорий. Например, если компонент расположен в app/View/Components/Inputs/Button.php, вы можете отобразить его следующим образом:

<x-inputs.button/>

Если вы хотите условно отобразить компонент, вы можете определить метод shouldRender в классе компонента. Если метод shouldRender возвращает false, компонент не будет отображен:

use Illuminate\Support\Str;
 
/**
* Должен ли компонент быть отрендерен
*/
public function shouldRender(): bool
{
return Str::length($this->message) > 0;
}

4.2. Индексация компонентов

Иногда компоненты являются частью группы компонентов, и вам может потребоваться сгруппировать связанные компоненты в одной директории. Например, представьте компонент "card" со следующей структурой классов:

App\Views\Components\Card\Card
App\Views\Components\Card\Header
App\Views\Components\Card\Body

Поскольку корневой компонент Card находится в директории Card, вы могли бы ожидать, что вам нужно будет отобразить компонент через <x-card.card>. Однако, когда имя файла компонента совпадает с именем директории компонента, Laravel автоматически предполагает, что этот компонент является "корневым", и позволяет отобразить компонент без повторения имени директории:

<x-card>
<x-card.header>...</x-card.header>
<x-card.body>...</x-card.body>
</x-card>

4.3. Передача данных в компоненты

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

<x-alert type="error" :message="$message"/>

Все атрибуты данных компонента следует определить в его конструкторе класса. Все публичные свойства компонента автоматически становятся доступными в представлении компонента. Нет необходимости передавать данные в представление через метод render компонента:

<?php
 
namespace App\View\Components;
 
use Illuminate\View\Component;
use Illuminate\View\View;
 
class Alert extends Component
{
/**
* Создание экземпляра компонента.
*/
public function __construct(
public string $type,
public string $message,
) {}
 
/**
* Получение представления или контента для отображения компонента.
*/
public function render(): View
{
return view('components.alert');
}
}

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

4.3.1. Регистр символов

Аргументы конструктора компонента должны быть указаны с использованием camelCase, в то время как kebab-case следует использовать при обращении к именам аргументов в атрибутах HTML. Например, для следующего конструктора компонента:

/**
* Создание экземпляра компонента.
*/
public function __construct(
public string $alertType,
) {}

Аргумент $alertType может быть передан в компонент следующим образом:

<x-alert alert-type="danger" />
















6. Создание макетов

6.1. Макеты с использованием компонентов

Большинство веб-приложений используют один и тот же общий макет на разных страницах. Было бы чрезвычайно неудобно и трудно поддерживать наше приложение, если бы нам пришлось повторять весь HTML макета в каждом создаваемом представлении. К счастью, удобно определить этот макет как единый Blade-компонент и использовать его во всём приложении.

6.1.1. Определение компонента-макета