- Введение
- Отображение данных
- Директивы Blade
- Компоненты
- Анонимные компоненты
- Построение макетов
- Формы
- Стэки
- Внедрение сервисов
- Отображение встроенных шаблонов Blade
- Отображение фрагментов Blade
- Расширение Blade
#Введение
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']);
});
#Усиление Blade с помощью Livewire
Хотите вывести ваши шаблоны Blade на новый уровень и легко создавать динамические интерфейсы? Ознакомьтесь с Laravel Livewire. Livewire позволяет писать компоненты Blade с динамическим функционалом, который обычно возможен только через фронтенд-фреймворки, такие как React или Vue, предоставляя отличный способ создания современных реактивных интерфейсов без сложностей, клиентского рендеринга или этапов сборки многих JavaScript-фреймворков.
#Отображение данных
Вы можете отображать данные, переданные в ваши шаблоны Blade, обернув переменную в фигурные скобки. Например, для следующего маршрута:
Route::get('/', function () {
return view('welcome', ['name' => 'Samantha']);
});
Вы можете вывести содержимое переменной name следующим образом:
Hello, {{ $name }}.
Выражения вывода Blade {{ }} автоматически проходят через функцию PHP htmlspecialchars для предотвращения XSS-атак.
Вы не ограничены выводом только переданных в представление переменных. Вы также можете вывести результат любой PHP-функции. Фактически, вы можете поместить любой PHP-код внутри выражения вывода Blade:
The current UNIX timestamp is {{ time() }}.
#Кодирование 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();
}
}
#Отображение неэкранированных данных
По умолчанию выражения Blade {{ }} автоматически проходят через функцию PHP htmlspecialchars для предотвращения XSS-атак. Если вы не хотите, чтобы ваши данные экранировались, вы можете использовать следующий синтаксис:
Hello, {!! $name !!}.
Будьте очень осторожны при выводе содержимого, предоставленного пользователями вашего приложения. Обычно следует использовать экранированный синтаксис с двойными фигурными скобками, чтобы предотвратить XSS-атаки при отображении данных, введённых пользователями.
#Blade и JavaScript-фреймворки
Поскольку многие JavaScript-фреймворки также используют фигурные скобки для обозначения выражений, которые должны отображаться в браузере, вы можете использовать символ @, чтобы сообщить движку Blade, что выражение должно остаться без изменений. Например:
<h1>Laravel</h1>
Hello, @{{ name }}.
В этом примере символ @ будет удалён Blade, однако выражение {{ name }} останется без изменений, позволяя вашему JavaScript-фреймворку отобразить его.
Символ @ также можно использовать для экранирования директив Blade:
{{-- Blade template --}}
@@if()
<!-- HTML-вывод -->
@if()
#Отображение 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:
<script>
var app = {{ Js::from($array) }};
</script>
Используйте метод Js::from только для отображения существующих переменных в формате JSON. Шаблонизатор Blade основан на регулярных выражениях, и попытка передать сложное выражение в директиву может привести к непредвиденным ошибкам.
#Директива @verbatim
Если вы отображаете JavaScript-переменные в большой части шаблона, вы можете обернуть HTML в директиву @verbatim, чтобы не нужно было префиксировать каждое выражение Blade символом @:
@verbatim
<div class="container">
Hello, {{ name }}.
</div>
@endverbatim
#Директивы Blade
Помимо наследования шаблонов и отображения данных, Blade предоставляет удобные сокращения для распространённых PHP-конструкций управления, таких как условные операторы и циклы. Эти сокращения обеспечивают чистый и лаконичный синтаксис, оставаясь при этом знакомыми для пользователей PHP.
#Условные операторы
Вы можете создавать условные операторы 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 определён и не равен null...
@endisset
@empty($records)
// $records "пустой"...
@endempty
#Директивы аутентификации
Директивы @auth и @guest можно использовать, чтобы быстро определить, является ли текущий пользователь аутентифицирован или гостем:
@auth
// Пользователь аутентифицирован...
@endauth
@guest
// Пользователь не аутентифицирован...
@endguest
При необходимости вы можете указать guard аутентификации, который должен проверяться при использовании директив @auth и @guest:
@auth('admin')
// Пользователь аутентифицирован...
@endauth
@guest('admin')
// Пользователь не аутентифицирован...
@endguest
#Директивы окружения
Вы можете проверить, запущено ли приложение в production-среде, используя директиву @production:
@production
// Контент, специфичный для production...
@endproduction
Или определить, запущено ли приложение в конкретном окружении, используя директиву @env:
@env('staging')
// Приложение запущено в "staging"...
@endenv
@env(['staging', 'production'])
// Приложение запущено в "staging" или "production"...
@endenv
#Директивы секций
Вы можете проверить, содержит ли секция наследования шаблона контент, используя директиву @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
#Директивы сессии
Директива @session позволяет проверить, существует ли значение в сессии. Если значение существует, содержимое между директивами @session и @endsession будет обработано. Внутри директивы @session вы можете вывести переменную $value для отображения значения сессии:
@session('status')
<div class="p-4 bg-green-100">
{{ $value }}
</div>
@endsession
#Операторы switch
Операторы switch можно создавать с помощью директив @switch, @case, @break, @default и @endswitch:
@switch($i)
@case(1)
First case...
@break
@case(2)
Second case...
@break
@default
Default case...
@endswitch
#Циклы
Помимо условных операторов, Blade предоставляет простые директивы для работы с циклами PHP. Каждая из этих директив работает так же, как соответствующая конструкция 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 вы можете использовать переменную цикла, чтобы получить полезную информацию, например, находитесь ли вы на первой или последней итерации.
При использовании циклов вы также можете пропустить текущую итерацию или прервать цикл с помощью директив @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
#Переменная цикла
При переборе цикла foreach внутри цикла будет доступна переменная $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 the 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 |
Вложенный цикл: переменная цикла родителя. |
#Условные классы и стили
Директива @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>
#Дополнительные атрибуты
Для удобства можно использовать директиву @checked, чтобы легко указать, отмечен ли конкретный HTML-элемент input типа checkbox. Эта директива выведет checked, если переданное условие вычислится в true:
<input type="checkbox"
name="active"
value="active"
@checked(old('active', $user->active)) />
Аналогично, директива @selected может использоваться для указания, должен ли элемент select иметь выбранный option:
<select name="version">
@foreach ($product->versions as $version)
<option value="{{ $version }}" @selected(old('version') == $version)>
{{ $version }}
</option>
@endforeach
</select>
Кроме того, директива @disabled может использоваться для указания, должен ли элемент быть отключён:
<button type="submit" @disabled($errors->isNotEmpty())>Submit</button>
Также директива @readonly может использоваться для указания, должен ли элемент быть доступен только для чтения:
<input type="email"
name="email"
value="email@laravel.com"
@readonly($user->isNotAdmin()) />
Кроме того, директива @required может использоваться для указания, что элемент является обязательным:
<input type="text"
name="title"
value="title"
@required($user->isAdmin()) />
#Включение подшаблонов
Хотя вы можете использовать директиву @include, Blade компоненты обеспечивают аналогичную функциональность и дают несколько преимуществ по сравнению с директивой @include, например связывание данных и атрибутов.
Директива @include Blade позволяет включать один шаблон 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, так как они будут указывать на расположение кэшированного, скомпилированного шаблона.
#Отображение представлений для коллекций
Вы можете объединить циклы и включения в одну строку с помощью директивы @each Blade:
@each('view.name', $jobs, 'job')
Первый аргумент директивы @each — это шаблон, который будет отображаться для каждого элемента массива или коллекции. Второй аргумент — массив или коллекция для итерации, а третий — имя переменной, которая будет присвоена текущему элементу в шаблоне. Например, при переборе массива jobs обычно вы будете обращаться к каждому элементу как к переменной job. Ключ текущей итерации будет доступен как переменная key в шаблоне.
Вы также можете передать четвёртый аргумент директиве @each. Этот аргумент определяет шаблон, который будет отображён, если массив пуст.
@each('view.name', $jobs, 'job', 'view.empty')
Представления, отображаемые через @each, не наследуют переменные из родительского шаблона. Если дочернему шаблону нужны эти переменные, используйте директивы @foreach и @include.
#Директива @once
Директива @once позволяет определить часть шаблона, которая будет обработана только один раз за цикл рендеринга. Это полезно, например, для добавления JavaScript в заголовок страницы с помощью стэков. Например, если вы отображаете компонент в цикле, вы можете захотеть добавить JavaScript в заголовок только при первом рендеринге компонента:
@once
@push('scripts')
<script>
// Ваш пользовательский JavaScript...
</script>
@endpush
@endonce
Поскольку директива @once часто используется вместе с директивами @push или @prepend, доступны удобные директивы @pushOnce и @prependOnce:
@pushOnce('scripts')
<script>
// Ваш пользовательский JavaScript...
</script>
@endPushOnce
#Чистый PHP
В некоторых случаях полезно встроить PHP-код в ваши шаблоны. Вы можете использовать директиву Blade @php для выполнения блока обычного PHP в шаблоне:
@php
$counter = 1;
@endphp
Или, если вам нужно только импортировать класс, используйте директиву @use:
@use('App\Models\Flight')
Второй аргумент может быть передан директиве @use для создания псевдонима импортируемого класса:
@use('App\Models\Flight', 'FlightModel')
#Комментарии
Blade также позволяет определять комментарии в шаблонах. В отличие от HTML-комментариев, комментарии Blade не включаются в HTML, возвращаемый приложением:
{{-- This comment will not be present in the rendered HTML --}}
#Компоненты
Компоненты и слоты предоставляют схожие преимущества с секциями, макетами и включениями, однако некоторым удобнее воспринимать модель компонентов и слотов. Существует два подхода к созданию компонентов: на основе классов и анонимные компоненты.
Для создания компонента на основе класса используйте 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 />.
#Ручная регистрация компонентов пакетов
При создании компонентов для собственного приложения они автоматически обнаруживаются в каталогах 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 автоматически определит класс, связанный с компонентом, преобразуя имя компонента в PascalCase. Подкаталоги также поддерживаются с помощью нотации через точки.
#Отображение компонентов
Для отображения компонента используйте тег компонента 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;
}
#Передача данных в компоненты
Вы можете передавать данные в компоненты Blade через 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');
}
}
При отображении компонента вы можете вывести содержимое публичных переменных компонента, обращаясь к ним по имени:
<div class="alert alert-{{ $type }}">
{{ $message }}
</div>
#Стиль написания имён
Аргументы конструктора компонента должны быть указаны в camelCase, а при ссылке на них в HTML-атрибутах использовать kebab-case. Например, для следующего конструктора компонента:
/**
* Создать экземпляр компонента.
*/
public function __construct(
public string $alertType,
) {}
Аргумент $alertType можно передать компоненту так:
<x-alert alert-type="danger" />
#Краткий синтаксис атрибутов
При передаче атрибутов компонентам можно использовать «краткий синтаксис атрибутов». Это удобно, так как имена атрибутов часто совпадают с именами переменных:
{{-- Short attribute syntax... --}}
<x-profile :$userId :$name />
{{-- Is equivalent to... --}}
<x-profile :user-id="$userId" :name="$name" />
#Экранирование рендеринга атрибутов
Поскольку некоторые JavaScript-фреймворки, например Alpine.js, также используют атрибуты с префиксом двоеточия, вы можете использовать двойной префикс ::, чтобы сообщить Blade, что атрибут не является PHP-выражением. Например, для следующего компонента:
<x-button ::class="{ danger: isDeleting }">
Submit
</x-button>
Следующий HTML будет сгенерирован Blade:
<button :class="{ danger: isDeleting }">
Submit
</button>
#Методы компонентов
Помимо публичных переменных, в шаблоне компонента можно вызывать любые публичные методы. Например, представьте компонент с методом isSelected:
/**
* Определить, является ли опция выбранной.
*/
public function isSelected(string $option): bool
{
return $option === $this->selected;
}
Вы можете вызвать этот метод из шаблона компонента, обращаясь к переменной с именем метода:
<option {{ $isSelected($value) ? 'selected' : '' }} value="{{ $value }}">
{{ $label }}
</option>
#Доступ к атрибутам и слотам внутри классов компонентов
Компоненты Blade также позволяют получить доступ к имени компонента, атрибутам и слоту внутри метода render класса. Однако, чтобы получить эти данные, нужно вернуть замыкание из метода render вашего компонента. Замыкание будет получать массив $data в качестве единственного аргумента. Этот массив будет содержать несколько элементов, которые содержат информацию о компоненте:
use Closure;
/**
* Получить представление / содержимое компонента.
*/
public function render(): Closure
{
return function (array $data) {
// $data['componentName'];
// $data['attributes'];
// $data['slot'];
return '<div>Содержимое компонента</div>';
};
}
componentName соответствует имени, используемому в HTML-теге после префикса x-. Таким образом, у <x-alert /> значение componentName будет alert. Элемент attributes содержит все атрибуты, присутствовавшие в HTML-теге. Элемент slot — это экземпляр Illuminate\Support\HtmlString с содержимым слота компонента.
Замыкание должно возвращать строку. Если возвращённая строка соответствует существующему представлению, будет отрендерено это представление; в противном случае возвращённая строка будет интерпретирована как встроенное Blade-представление.
#Дополнительные зависимости
Если вашему компоненту требуются зависимости из service container Laravel, вы можете перечислить их перед любыми атрибутами данных компонента, и они будут автоматически внедрены контейнером:
use App\Services\AlertCreator;
/**
* Создать экземпляр компонента.
*/
public function __construct(
public AlertCreator $creator,
public string $type,
public string $message,
) {}
#Сокрытие атрибутов / методов
Если вы хотите предотвратить отображение некоторых публичных методов или свойств в виде переменных в шаблоне компонента, вы можете добавить их в массив $except в вашем компоненте:
<?php
namespace App\View\Components;
use Illuminate\View\Component;
class Alert extends Component
{
/**
* Свойства / методы, которые не должны быть доступны в шаблоне компонента.
*
* @var array
*/
protected $except = ['type'];
/**
* Создать экземпляр компонента.
*/
public function __construct(
public string $type,
) {}
}
#Атрибуты компонента
Мы уже рассмотрели, как передавать атрибуты данных в компонент; однако иногда нужно указать дополнительные HTML-атрибуты, например class, которые не являются частью данных, необходимых для работы компонента. Обычно такие дополнительные атрибуты нужно передать корневому элементу шаблона компонента. Например, предположим, что мы хотим отрендерить компонент alert следующим образом:
<x-alert type="error" :message="$message" class="mt-4"/>
Все атрибуты, которые не являются частью конструктора компонента, автоматически добавляются в "мешок атрибутов" компонента. Этот мешок автоматически доступен в компоненте через переменную $attributes. Все атрибуты можно вывести в компоненте, просто выведя эту переменную:
<div {{ $attributes }}>
<!-- Содержимое компонента -->
</div>
Использование директив, таких как @env, внутри тегов компонентов в настоящее время не поддерживается. Например, <x-alert :live="@env('production')"/> не будет скомпилирован.
#Атрибуты по умолчанию / объединённые атрибуты
Иногда нужно указать значения атрибутов по умолчанию или объединить дополнительные значения с некоторыми атрибутами компонента. Для этого можно использовать метод merge мешка атрибутов. Этот метод особенно полезен для определения набора CSS-классов по умолчанию, которые всегда должны применяться к компоненту:
<div {{ $attributes->merge(['class' => 'alert alert-'.$type]) }}>
{{ $message }}
</div>
Если предположить, что этот компонент используется следующим образом:
<x-alert type="error" :message="$message" class="mb-4"/>
Итоговый HTML, сгенерированный компонентом, будет выглядеть так:
<div class="alert alert-error mb-4">
<!-- Содержимое переменной $message -->
</div>
#Условное объединение классов
Иногда может понадобиться объединить классы при условии true. Это можно сделать с помощью метода class, который принимает массив классов, где ключ массива содержит класс или несколько классов, которые вы хотите добавить, а значение — логическое выражение. Если у элемента массива числовой ключ, он всегда будет включён в итоговый список классов:
<div {{ $attributes->class(['p-4', 'bg-red' => $hasError]) }}>
{{ $message }}
</div>
Если нужно объединить другие атрибуты с вашим компонентом, можно цепочкой вызвать метод merge после метода class:
<button {{ $attributes->class(['p-4'])->merge(['type' => 'button']) }}>
{{ $slot }}
</button>
Если нужно условно компилировать классы для других HTML-элементов, которые не должны получать объединённые атрибуты, можно использовать директиву @class.
#Объединение не-классовых атрибутов
При объединении атрибутов, отличных от class, значения, переданные в метод merge, считаются значениями по умолчанию для атрибута. Однако, в отличие от атрибута class, эти атрибуты не объединяются с внедрёнными значениями, а перезаписываются. Например, реализация компонента button может выглядеть так:
<button {{ $attributes->merge(['type' => 'button']) }}>
{{ $slot }}
</button>
Чтобы отрендерить компонент кнопки с пользовательским type, его можно указать при использовании компонента. Если тип не указан, будет использован тип button:
<x-button type="submit">
Submit
</x-button>
Отрендеренный HTML компонента button в этом примере будет таким:
<button type="submit">
Submit
</button>
Если вы хотите, чтобы атрибут, отличный от class, имел объединённое значение по умолчанию и внедрённые значения, можно использовать метод prepends. В этом примере атрибут data-controller всегда будет начинаться с profile-controller, а любые дополнительные внедрённые значения data-controller будут добавлены после этого значения по умолчанию:
<div {{ $attributes->merge(['data-controller' => $attributes->prepends('profile-controller')]) }}>
{{ $slot }}
</div>
#Получение и фильтрация атрибутов
Вы можете фильтровать атрибуты с помощью метода filter. Этот метод принимает замыкание, которое должно возвращать true, если атрибут нужно сохранить в мешке атрибутов:
{{ $attributes->filter(fn (string $value, string $key) => $key == 'foo') }}
Для удобства можно использовать метод whereStartsWith, чтобы получить все атрибуты, ключи которых начинаются с заданной строки:
{{ $attributes->whereStartsWith('wire:model') }}
А метод whereDoesntStartWith позволяет исключить все атрибуты, ключи которых начинаются с заданной строки:
{{ $attributes->whereDoesntStartWith('wire:model') }}
С помощью метода first можно вывести первый атрибут из мешка атрибутов:
{{ $attributes->whereStartsWith('wire:model')->first() }}
Если нужно проверить наличие атрибута в компоненте, используйте метод has. Он принимает имя атрибута и возвращает булево значение, указывающее, присутствует ли атрибут:
@if ($attributes->has('class'))
<div>Class attribute is present</div>
@endif
Если в метод has передать массив, он проверит, присутствуют ли все указанные атрибуты в компоненте:
@if ($attributes->has(['name', 'class']))
<div>All of the attributes are present</div>
@endif
Метод hasAny проверяет, присутствует ли хотя бы один из указанных атрибутов в компоненте:
@if ($attributes->hasAny(['href', ':href', 'v-bind:href']))
<div>One of the attributes is present</div>
@endif
Значение конкретного атрибута можно получить с помощью метода get:
{{ $attributes->get('class') }}
#Зарезервированные ключевые слова
По умолчанию некоторые ключевые слова зарезервированы для внутреннего использования Blade при рендеринге компонентов. Следующие ключевые слова нельзя использовать в качестве публичных свойств или методов в ваших компонентах:
datarenderresolveViewshouldRenderviewwithAttributeswithName
#Слоты
Часто нужно передавать дополнительное содержимое в компонент через "слоты". Слоты компонента выводятся через переменную $slot. Рассмотрим пример: компонент alert имеет следующий шаблон:
<!-- /resources/views/components/alert.blade.php -->
<div class="alert alert-danger">
{{ $slot }}
</div>
Мы можем передать содержимое в slot, вложив его в компонент:
<x-alert>
<strong>Whoops!</strong> Something went wrong!
</x-alert>
Иногда компоненту нужно отрендерить несколько разных слотов в разных местах шаблона. Давайте изменим наш компонент alert, чтобы добавить возможность передачи слота "title":
<!-- /resources/views/components/alert.blade.php -->
<span class="alert-title">{{ $title }}</span>
<div class="alert alert-danger">
{{ $slot }}
</div>
Вы можете определить содержимое именованного слота с помощью тега x-slot. Любое содержимое, не заключённое в явный тег x-slot, будет передано в компонент в переменной $slot:
<x-alert>
<x-slot:title>
Server Error
</x-slot>
<strong>Whoops!</strong> Something went wrong!
</x-alert>
Вы можете вызвать метод isEmpty слота, чтобы проверить, содержит ли слот содержимое:
<span class="alert-title">{{ $title }}</span>
<div class="alert alert-danger">
@if ($slot->isEmpty())
This is default content if the slot is empty.
@else
{{ $slot }}
@endif
</div>
Кроме того, метод hasActualContent позволяет определить, содержит ли слот "реальное" содержимое, не являющееся HTML-комментарием:
@if ($slot->hasActualContent())
The scope has non-comment content.
@endif
#Scoped Slots (Слоты с областью видимости)
Если вы использовали JavaScript-фреймворки, такие как Vue, вам могут быть знакомы "scoped slots", которые позволяют получить доступ к данным или методам компонента внутри слота. В Laravel похожее поведение достигается определением публичных методов или свойств в компоненте и доступом к компоненту внутри слота через переменную $component. В этом примере предположим, что у компонента x-alert есть публичный метод formatAlert:
<x-alert>
<x-slot:title>
{{ $component->formatAlert('Server Error') }}
</x-slot>
<strong>Whoops!</strong> Something went wrong!
</x-alert>
#Атрибуты слотов
Как и у Blade-компонентов, вы можете назначать дополнительные атрибуты слотам, например CSS-классы:
<x-card class="shadow-sm">
<x-slot:heading class="font-bold">
Heading
</x-slot>
Content
<x-slot:footer class="text-sm">
Footer
</x-slot>
</x-card>
Для работы с атрибутами слота можно обращаться к свойству attributes переменной слота. Подробнее о работе с атрибутами смотрите в разделе атрибуты компонента:
@props([
'heading',
'footer',
])
<div {{ $attributes->class(['border']) }}>
<h1 {{ $heading->attributes->class(['text-lg']) }}>
{{ $heading }}
</h1>
{{ $slot }}
<footer {{ $footer->attributes->class(['text-gray-700']) }}>
{{ $footer }}
</footer>
</div>
#Встроенные представления компонентов
Для очень маленьких компонентов может быть неудобно управлять и классом компонента, и его шаблоном. Поэтому можно возвращать разметку компонента напрямую из метода render:
/**
* Получить представление / содержимое, представляющее компонент.
*/
public function render(): string
{
return <<<'blade'
<div class="alert alert-danger">
{{ $slot }}
</div>
blade;
}
#Создание компонентов с встроенным представлением
Чтобы создать компонент с встроенным представлением, можно использовать опцию inline при выполнении команды make:component:
php artisan make:component Alert --inline
#Динамические компоненты
Иногда нужно отрендерить компонент, но заранее неизвестно, какой именно. В таких случаях можно использовать встроенный компонент Laravel dynamic-component, который рендерит компонент на основе значения или переменной во время выполнения:
// $componentName = "secondary-button";
<x-dynamic-component :component="$componentName" class="mt-4" />
#Ручная регистрация компонентов
Следующая документация по ручной регистрации компонентов в основном предназначена для тех, кто разрабатывает пакеты Laravel с компонентами представлений. Если вы не пишете пакет, этот раздел может быть для вас неактуален.
При создании компонентов для собственного приложения компоненты автоматически обнаруживаются в директориях app/View/Components и resources/views/components.
Однако если вы создаёте пакет, использующий Blade-компоненты, или размещаете компоненты в нестандартных директориях, вам нужно вручную зарегистрировать класс компонента и его HTML-псевдоним, чтобы Laravel знал, где искать компонент. Обычно регистрацию компонентов выполняют в методе boot сервис-провайдера пакета:
use Illuminate\Support\Facades\Blade;
use VendorPackage\View\Components\AlertComponent;
/**
* Загрузить сервисы вашего пакета.
*/
public function boot(): void
{
Blade::component('package-alert', AlertComponent::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 автоматически определит класс, связанный с компонентом, преобразуя имя компонента в PascalCase. Поддиректории также поддерживаются с помощью нотации через точку.
#Анонимные компоненты
Подобно встроенным компонентам, анонимные компоненты позволяют управлять компонентом через один файл. Однако анонимные компоненты используют только один файл представления и не имеют связанного класса. Чтобы определить анонимный компонент, достаточно поместить Blade-шаблон в директорию resources/views/components. Например, если у вас есть компонент в resources/views/components/alert.blade.php, его можно отрендерить так:
<x-alert/>
Вы можете использовать символ . для указания вложенности компонента в директории components. Например, если компонент находится в resources/views/components/inputs/button.blade.php, его можно отрендерить так:
<x-inputs.button/>
#Анонимные компоненты с индексом
Иногда, когда компонент состоит из множества Blade-шаблонов, может быть удобно сгруппировать шаблоны компонента в одной директории. Например, представим компонент "accordion" со следующей структурой директорий:
/resources/views/components/accordion.blade.php
/resources/views/components/accordion/item.blade.php
Такая структура позволяет рендерить компонент accordion и его элементы следующим образом:
<x-accordion>
<x-accordion.item>
...
</x-accordion.item>
</x-accordion>
Однако чтобы рендерить компонент accordion через x-accordion, нам пришлось разместить шаблон "index" компонента accordion в директории resources/views/components, а не вложить его в директорию accordion вместе с другими шаблонами accordion.
К счастью, Blade позволяет поместить файл index.blade.php в каталог шаблона компонента. Если для компонента существует шаблон index.blade.php, он будет отрендерен как «корневой» узел компонента. Поэтому мы можем продолжать использовать тот же синтаксис Blade, что и в примере выше; однако изменим структуру каталогов следующим образом:
/resources/views/components/accordion/index.blade.php
/resources/views/components/accordion/item.blade.php
#Свойства данных / атрибуты
Поскольку анонимные компоненты не имеют связанного класса, может возникнуть вопрос, как отличить, какие данные должны передаваться в компонент как переменные, а какие атрибуты должны попадать в мешок атрибутов компонента.
Вы можете указать, какие атрибуты считать переменными данных, используя директиву @props в начале Blade-шаблона компонента. Все остальные атрибуты будут доступны через мешок атрибутов компонента. Если нужно задать значение по умолчанию для переменной данных, укажите имя переменной как ключ массива, а значение по умолчанию — как значение массива:
<!-- /resources/views/components/alert.blade.php -->
@props(['type' => 'info', 'message'])
<div {{ $attributes->merge(['class' => 'alert alert-'.$type]) }}>
{{ $message }}
</div>
Исходя из определения компонента выше, мы можем отрендерить компонент следующим образом:
<x-alert type="error" :message="$message" class="mb-4"/>
#Доступ к данным родителя
Иногда нужно получить доступ к данным родительского компонента внутри дочернего. В таких случаях можно использовать директиву @aware. Например, представим сложный компонент меню, состоящий из родительского <x-menu> и дочернего <x-menu.item>:
<x-menu color="purple">
<x-menu.item>...</x-menu.item>
<x-menu.item>...</x-menu.item>
</x-menu>
Компонент <x-menu> может иметь следующую реализацию:
<!-- /resources/views/components/menu/index.blade.php -->
@props(['color' => 'gray'])
<ul {{ $attributes->merge(['class' => 'bg-'.$color.'-200']) }}>
{{ $slot }}
</ul>
Поскольку проп color передан только в родительский компонент (<x-menu>), он не будет доступен внутри <x-menu.item>. Однако если использовать директиву @aware, мы можем сделать его доступным и внутри <x-menu.item>:
<!-- /resources/views/components/menu/item.blade.php -->
@aware(['color' => 'gray'])
<li {{ $attributes->merge(['class' => 'text-'.$color.'-800']) }}>
{{ $slot }}
</li>
Директива @aware не может получить доступ к данным родителя, которые явно не переданы родительскому компоненту через HTML-атрибуты. Значения по умолчанию @props, не переданные явно родительскому компоненту, недоступны для директивы @aware.
#Пути анонимных компонентов
Как уже обсуждалось, анонимные компоненты обычно определяются размещением Blade-шаблона в директории resources/views/components. Однако иногда может потребоваться зарегистрировать дополнительные пути анонимных компонентов в Laravel помимо пути по умолчанию.
Метод anonymousComponentPath принимает первым аргументом "путь" к расположению анонимных компонентов, а вторым необязательным аргументом — "пространство имён", под которым должны размещаться компоненты. Обычно этот метод вызывается из метода boot одного из сервис-провайдеров вашего приложения:
/**
* Загрузить сервисы приложения.
*/
public function boot(): void
{
Blade::anonymousComponentPath(__DIR__.'/../components');
}
Если пути компонентов регистрируются без указанного префикса, как в примере выше, их можно рендерить в Blade без соответствующего префикса. Например, если в зарегистрированном пути есть компонент panel.blade.php, его можно отрендерить так:
<x-panel />
Префиксы "пространств имён" можно указать вторым аргументом метода anonymousComponentPath:
Blade::anonymousComponentPath(__DIR__.'/../components', 'dashboard');
Если указан префикс, компоненты в этом "пространстве имён" можно рендерить, добавляя префикс к имени компонента при его использовании:
<x-dashboard::panel />
#Создание макетов
#Макеты с использованием компонентов
Большинство веб-приложений используют одинаковый общий макет на разных страницах. Было бы крайне неудобно и сложно поддерживать приложение, если бы нам приходилось повторять весь HTML макета в каждом представлении. К счастью, удобно определить этот макет как один Blade-компонент и использовать его по всему приложению.
#Определение компонента макета
Например, представим, что мы создаём приложение для списка дел. Мы можем определить компонент layout, который выглядит следующим образом:
<!-- resources/views/components/layout.blade.php -->
<html>
<head>
<title>{{ $title ?? 'Todo Manager' }}</title>
</head>
<body>
<h1>Todos</h1>
<hr/>
{{ $slot }}
</body>
</html>
#Использование компонента макета
После определения компонента layout мы можем создать Blade-представление, использующее этот компонент. В этом примере определим простое представление, отображающее список задач:
<!-- resources/views/tasks.blade.php -->
<x-layout>
@foreach ($tasks as $task)
{{ $task }}
@endforeach
</x-layout>
Помните, что содержимое, вложенное в компонент, будет передано в переменную $slot по умолчанию в нашем компоненте layout. Как вы могли заметить, наш layout также учитывает слот $title, если он задан; в противном случае отображается заголовок по умолчанию. Мы можем передать пользовательский заголовок из представления списка задач, используя стандартный синтаксис слотов, описанный в документации по компонентам:
<!-- resources/views/tasks.blade.php -->
<x-layout>
<x-slot:title>
Custom Title
</x-slot>
@foreach ($tasks as $task)
{{ $task }}
@endforeach
</x-layout>
Теперь, когда мы определили макет и представление списка задач, нам осталось вернуть представление tasks из маршрута:
use App\Models\Task;
Route::get('/tasks', function () {
return view('tasks', ['tasks' => Task::all()]);
});
#Макеты с использованием наследования шаблонов
#Определение макета
Макеты также можно создавать с помощью "наследования шаблонов". Это был основной способ построения приложений до появления компонентов.
Для начала рассмотрим простой пример. Сначала посмотрим на макет страницы. Поскольку большинство веб-приложений используют одинаковый общий макет на разных страницах, удобно определить этот макет как одно 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', 'Заголовок страницы')
@section('sidebar')
@@parent
<p>Это добавлено к основной боковой панели.</p>
@endsection
@section('content')
<p>Это содержимое основной части.</p>
@endsection
В этом примере секция sidebar использует директиву @@parent, чтобы добавить содержимое к боковой панели макета, а не перезаписывать её. Директива @@parent будет заменена содержимым макета при рендеринге представления.
В отличие от предыдущего примера, эта секция sidebar завершается директивой @endsection, а не @show. Директива @endsection только определяет секцию, тогда как @show определяет и немедленно выводит секцию.
Директива @yield также принимает значение по умолчанию в качестве второго параметра. Это значение будет выведено, если секция, которую пытаются вывести, не определена:
@yield('content', 'Default content')
#Формы
#Поле CSRF
Всякий раз, когда вы определяете HTML-форму в приложении, следует включать скрытое поле CSRF-токена, чтобы middleware защиты CSRF мог проверить запрос. Для генерации поля токена можно использовать директиву Blade @csrf:
<form method="POST" action="/profile">
@csrf
...
</form>
#Поле метода
Поскольку HTML-формы не поддерживают методы PUT, PATCH или DELETE, необходимо добавить скрытое поле _method, чтобы имитировать эти HTTP-методы. Директива Blade @method создаёт это поле:
<form action="/foo/bar" method="POST">
@method('PUT')
...
</form>
#Ошибки валидации
Директива @error позволяет быстро проверить, существуют ли сообщения об ошибках валидации для заданного атрибута. Внутри директивы @error можно вывести переменную $message для отображения сообщения об ошибке:
<!-- /resources/views/post/create.blade.php -->
<label for="title">Заголовок поста</label>
<input id="title"
type="text"
class="@error('title') is-invalid @enderror">
@error('title')
<div class="alert alert-danger">{{ $message }}</div>
@enderror
Поскольку директива @error компилируется в условие "if", можно использовать директиву @else для вывода содержимого, если ошибки для атрибута нет:
<!-- /resources/views/auth.blade.php -->
<label for="email">Адрес электронной почты</label>
<input id="email"
type="email"
class="@error('email') is-invalid @else is-valid @enderror">
Вы можете передать имя конкретного набора ошибок вторым параметром в директиву @error, чтобы получать сообщения об ошибках валидации на страницах с несколькими формами:
<!-- /resources/views/auth.blade.php -->
<label for="email">Адрес электронной почты</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 позволяет добавлять содержимое в именованные стэки, которые затем можно вывести в другом месте, например, в другом представлении или макете. Это особенно полезно для подключения JavaScript-библиотек, необходимых дочерним представлениям:
@push('scripts')
<script src="/example.js"></script>
@endpush
Если вы хотите @push содержимое, когда заданное булево выражение равно true, можно использовать директиву @pushIf:
@pushIf($shouldPush, 'scripts')
<script src="/example.js"></script>
@endPushIf
Вы можете добавлять содержимое в стэк сколько угодно раз. Чтобы вывести всё содержимое стэка, передайте имя стэка в директиву @stack:
<head>
<!-- Содержимое head -->
@stack('scripts')
</head>
Если нужно добавить содержимое в начало стэка, используйте директиву @prepend:
@push('scripts')
This will be second...
@endpush
// Позже...
@prepend('scripts')
This will be first...
@endprepend
#Внедрение сервисов
Директива @inject может использоваться для получения сервиса из service container Laravel. Первый аргумент, передаваемый в @inject, — это имя переменной, в которую будет помещён сервис, а второй аргумент — имя класса или интерфейса сервиса, который вы хотите разрешить:
@inject('metrics', 'App\Services\MetricsService')
<div>
Monthly Revenue: {{ $metrics->monthlyRevenue() }}.
</div>
#Рендеринг встроенных Blade-шаблонов
Иногда может понадобиться преобразовать строку с сырым Blade-шаблоном в валидный HTML. Это можно сделать с помощью метода render, предоставляемого фасадом Blade. Метод render принимает строку Blade-шаблона и необязательный массив данных для передачи в шаблон:
use Illuminate\Support\Facades\Blade;
return Blade::render('Hello, {{ $name }}', ['name' => 'Julian Bashir']);
Laravel рендерит встроенные Blade-шаблоны, записывая их в директорию storage/framework/views. Если вы хотите, чтобы Laravel удалял эти временные файлы после рендеринга шаблона, вы можете передать аргумент deleteCachedView в метод:
return Blade::render(
'Hello, {{ $name }}',
['name' => 'Julian Bashir'],
deleteCachedView: true
);
#Рендеринг фрагментов Blade
При использовании frontend-фреймворков, таких как Turbo и htmx, иногда нужно возвращать только часть Blade-шаблона в HTTP-ответе. Фрагменты Blade позволяют сделать именно это. Для начала поместите часть вашего Blade-шаблона между директивами @fragment и @endfragment:
@fragment('user-list')
<ul>
@foreach ($users as $user)
<li>{{ $user->name }}</li>
@endforeach
</ul>
@endfragment
Затем, при рендеринге представления, использующего этот шаблон, вы можете вызвать метод fragment, чтобы указать, что в исходящий HTTP-ответ должен быть включён только указанный фрагмент:
return view('dashboard', ['users' => $users])->fragment('user-list');
Метод fragmentIf позволяет условно возвращать фрагмент представления на основе заданного условия. В противном случае будет возвращено всё представление целиком:
return view('dashboard', ['users' => $users])
->fragmentIf($request->hasHeader('HX-Request'), 'user-list');
Методы fragments и fragmentsIf позволяют возвращать несколько фрагментов представления в ответе. Фрагменты будут объединены вместе:
view('dashboard', ['users' => $users])
->fragments(['user-list', 'comment-list']);
view('dashboard', ['users' => $users])
->fragmentsIf(
$request->hasHeader('HX-Request'),
['user-list', 'comment-list']
);
#Расширение Blade
Blade позволяет определять собственные пользовательские директивы с помощью метода directive. Когда компилятор Blade встречает пользовательскую директиву, он вызывает переданный callback с выражением, содержащимся в директиве.
В следующем примере создаётся директива @datetime($var), которая форматирует переданный $var, который должен быть экземпляром DateTime:
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Зарегистрировать сервисы приложения.
*/
public function register(): void
{
// ...
}
/**
* Запустить сервисы приложения.
*/
public function boot(): void
{
Blade::directive('datetime', function (string $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.
#Пользовательские обработчики вывода
Если вы пытаетесь вывести объект с помощью Blade, будет вызван метод __toString объекта. Метод __toString — один из встроенных "магических" методов PHP. Однако иногда у вас нет контроля над методом __toString класса, например, если класс принадлежит сторонней библиотеке.
В таких случаях Blade позволяет зарегистрировать пользовательский обработчик вывода для конкретного типа объекта. Для этого следует вызвать метод stringable фасада Blade. Метод stringable принимает замыкание, которое должно указывать тип объекта, для которого оно отвечает. Обычно метод stringable вызывается в методе boot класса AppServiceProvider вашего приложения:
use Illuminate\Support\Facades\Blade;
use Money\Money;
/**
* Запустить сервисы приложения.
*/
public function boot(): void
{
Blade::stringable(function (Money $money) {
return $money->formatTo('en_GB');
});
}
После определения пользовательского обработчика вывода вы можете просто вывести объект в вашем Blade-шаблоне:
Cost: {{ $money }}
#Пользовательские условные операторы
Иногда программирование пользовательской директивы сложнее, чем нужно, для определения простых пользовательских условных операторов. По этой причине Blade предоставляет метод Blade::if, который позволяет быстро определить пользовательские условные директивы с помощью замыканий. Например, определим условие, проверяющее настроенный диск по умолчанию для приложения. Это можно сделать в методе boot класса AppServiceProvider:
use Illuminate\Support\Facades\Blade;
/**
* Запустить сервисы приложения.
*/
public function boot(): void
{
Blade::if('disk', function (string $value) {
return config('filesystems.default') === $value;
});
}
После определения пользовательского условия вы можете использовать его в шаблонах:
@disk('local')
<!-- Приложение использует локальный диск... -->
@elsedisk('s3')
<!-- Приложение использует диск s3... -->
@else
<!-- Приложение использует другой диск... -->
@enddisk
@unlessdisk('local')
<!-- Приложение не использует локальный диск... -->
@enddisk