3.8. Шаблоны Blade
- 1. Введение
- 2. Отображение данных
- 3. Директивы Blade
- 4. Компоненты
- 5. Анонимные компоненты
- 6. Создание макетов
- Forms
- Stacks
- Service Injection
- Rendering Inline Blade Templates
- Rendering Blade Fragments
- Extending 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 }}.
Выражения 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 !!}.
Будьте очень осторожны при выводе содержимого, предоставленного пользователями вашего приложения. Обычно следует использовать экранированный синтаксис с двойными фигурными скобками, чтобы предотвратить 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-шаблонах:
Вы должны использовать метод 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
Во время итерации через цикл 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. Включение дочерних представлений
Хотя вы можете использовать директиву @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'])
Вам следует избегать использования констант __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')
Представления, рендерящиеся через @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\CardApp\Views\Components\Card\HeaderApp\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-компонент и использовать его во всём приложении.