Шаблони Blade · Laravel
Blade - простий і водночас потужний шаблонізатор, що постачається разом із Laravel. На відміну від деяких PHP-шаблонізаторів, Blade не забороняє використовувати звичайний PHP-код у шаблонах. Насправді всі шаблони Blade компілюються у звичайний PHP-код і кешуються, доки їх не змінять, тож Blade практично не додає накладних витрат вашому застосунку. Файли шаблонів Blade мають розширення .blade.php і зазвичай зберігаються в теці resources/views.
Представлення (view) Blade можна повертати з маршрутів або контролерів за допомогою глобального хелпера view. Як і згадано в документації про представлення, дані передаються в представлення Blade другим аргументом хелпера view:
Route::get('/', function () {
return view('greeting', ['name' => 'Finn']);
});
Розширення Blade за допомогою Livewire
Хочете вивести шаблони Blade на новий рівень і легко будувати динамічні інтерфейси? Погляньте на Laravel Livewire. Livewire дозволяє писати компоненти Blade із динамічною функціональністю, яка зазвичай можлива лише через фронтенд-фреймворки на кшталт React, Svelte чи Vue. Це хороший підхід до сучасних реактивних інтерфейсів без складнощів, клієнтського рендерингу й кроків збірки, властивих багатьом JavaScript-фреймворкам.
Виведення даних
Дані, передані у представлення Blade, виводяться обгортанням змінної у фігурні дужки. Наприклад, для такого маршруту:
Route::get('/', function () {
return view('welcome', ['name' => 'Samantha']);
});
Вміст змінної name можна вивести так:
Hello, {{ $name }}.
Примітка. Вирази виведення
{{ }}у Blade автоматично проходять через PHP-функціюhtmlspecialchars, щоб запобігти XSS-атакам.
Ви не обмежені виведенням змінних, переданих у представлення. Так само можна вивести результат будь-якої PHP-функції. Фактично всередині виразу виведення Blade можна розмістити будь-який PHP-код:
The current UNIX timestamp is {{ time() }}.
Кодування HTML-сутностей
За замовчуванням Blade (і функція e в Laravel) робить подвійне кодування 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 output -->
@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 і водночас лишаються схожими на свої 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
// Вміст лише для продакшену...
@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
Директиви контексту
Директива @context визначає, чи існує значення контексту. Якщо значення контексту існує, вміст шаблону між @context і @endcontext буде обчислено. Усередині директиви @context можна вивести змінну $value, щоб показати значення контексту:
@context('canonical')
<link href="{{ $value }}" rel="canonical">
@endcontext
Оператори 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-чекбокс має бути "checked". Директива виведе 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())
/>
Включення підпредставлень
Примітка. Ви вільні використовувати директиву
@include, проте компоненти Blade дають схожу функціональність і кілька переваг над@include, зокрема привʼязку даних і атрибутів.
Директива @include дозволяє включити одне представлення Blade всередині іншого. Усі змінні, доступні батьківському представленню, будуть доступні і включеному:
<div>
@include('shared.errors')
<form>
<!-- Form Contents -->
</form>
</div>
Хоча включене представлення успадкує всі дані батьківського, ви можете передати ще й масив додаткових даних:
@include('view.name', ['status' => 'complete'])
Якщо спробувати підключити через @include представлення, якого не існує, Laravel викине помилку. Щоб включити представлення, яке може існувати, а може й ні, використовуйте директиву @includeIf:
@includeIf('view.name', ['status' => 'complete'])
Щоб підключити представлення, коли булевий вираз обчислюється в true або false, скористайтеся директивами @includeWhen і @includeUnless:
@includeWhen($boolean, 'view.name', ['status' => 'complete'])
@includeUnless($boolean, 'view.name', ['status' => 'complete'])
Щоб підключити перше представлення, яке існує, із заданого масиву представлень, використовуйте директиву includeFirst:
@includeFirst(['custom.admin', 'admin'], ['status' => 'complete'])
Щоб підключити представлення, не успадковуючи жодних змінних батьківського, використовуйте директиву @includeIsolated. Включене представлення матиме доступ лише до змінних, які ви передали явно:
@includeIsolated('view.name', ['user' => $user])
Попередження. Уникайте констант
__DIR__і__FILE__у представленнях Blade, бо вони вказуватимуть на розташування кешованого скомпільованого представлення.
Рендеринг представлень для колекцій
Цикли та включення можна обʼєднати в один рядок директивою @each:
@each('view.name', $jobs, 'job')
Перший аргумент директиви @each - представлення, яке рендериться для кожного елемента масиву чи колекції. Другий аргумент - масив або колекція, яку ви хочете перебрати, а третій - імʼя змінної, що буде призначена поточній ітерації всередині представлення. Наприклад, якщо ви перебираєте масив jobs, зазвичай ви захочете звертатися до кожного завдання (job) як до змінної job у представленні. Ключ масиву для поточної ітерації буде доступний у представленні як змінна key.
Директиві @each можна передати й четвертий аргумент. Він визначає представлення, яке буде відрендерено, якщо переданий масив порожній.
@each('view.name', $jobs, 'job', 'view.empty')
Попередження. Представлення, відрендерені через
@each, не успадковують змінних батьківського представлення. Якщо дочірньому представленню потрібні ці змінні, використовуйте натомість директиви@foreachта@include.
Директива @once
Директива @once дозволяє визначити частину шаблону, яка буде обчислена лише один раз за цикл рендерингу. Це буває корисно, щоб додати певний фрагмент JavaScript у header сторінки через стеки. Наприклад, якщо ви рендерите певний компонент у циклі, ви можете захотіти додати JavaScript у header лише під час першого рендерингу компонента:
@once
@push('scripts')
<script>
// Ваш власний JavaScript...
</script>
@endpush
@endonce
Оскільки директива @once часто використовується разом із @push або @prepend, для зручності доступні директиви @pushOnce і @prependOnce:
@pushOnce('scripts')
<script>
// Ваш власний JavaScript...
</script>
@endPushOnce
Якщо ви додаєте однаковий вміст із двох різних шаблонів Blade, передайте унікальний ідентифікатор другим аргументом директиви @pushOnce, щоб вміст відрендерився лише один раз:
<!-- pie-chart.blade.php -->
@pushOnce('scripts', 'chart.js')
<script src="/chart.js"></script>
@endPushOnce
<!-- line-chart.blade.php -->
@pushOnce('scripts', 'chart.js')
<script src="/chart.js"></script>
@endPushOnce
Чистий PHP
Іноді корисно вбудувати PHP-код у представлення. Директива Blade @php виконує блок звичайного PHP усередині шаблону:
@php
$counter = 1;
@endphp
Якщо ж PHP потрібен лише для імпорту класу, використовуйте директиву @use:
@use('App\Models\Flight')
Другим аргументом директиви @use можна задати псевдонім імпортованого класу:
@use('App\Models\Flight', 'FlightModel')
Якщо кілька класів належать до одного простору імен, їхні імпорти можна згрупувати:
@use('App\Models\{Flight, Airport}')
Директива @use підтримує також імпорт PHP-функцій і констант: для цього шлях імпорту префіксується модифікатором function або const:
@use(function App\Helpers\format_currency)
@use(const App\Constants\MAX_ATTEMPTS)
Як і для класів, для функцій і констант підтримуються псевдоніми:
@use(function App\Helpers\format_currency, 'formatMoney')
@use(const App\Constants\MAX_ATTEMPTS, 'MAX_TRIES')
Групові імпорти теж працюють із модифікаторами function і const, тож однією директивою можна імпортувати кілька символів з одного простору імен:
@use(function App\Helpers\{format_currency, format_date})
@use(const App\Constants\{MAX_ATTEMPTS, DEFAULT_TIMEOUT})
Шрифти
Якщо ви користуєтеся оптимізацією шрифтів через Vite у Laravel, директива @fonts відрендерить у макеті застосунку налаштовані посилання preload для шрифтів та інлайновий CSS шрифтів:
<!doctype html>
<head>
{{-- ... --}}
@fonts
@vite('resources/js/app.js')
</head>
Директива @fonts рендерить усі родини шрифтів, налаштовані у файлі vite.config.js. Зазвичай її розміщують у <head> кореневого макета застосунку перед будь-яким вмістом, що використовує ці шрифти.
Якщо сторінці потрібна лише частина налаштованих шрифтів, передайте директиві один або кілька псевдонімів шрифтів:
{{-- Load a single font alias... --}}
@fonts('sans')
{{-- Load multiple font aliases... --}}
@fonts(['sans', 'mono'])
Псевдоніми шрифтів налаштовуються опцією alias під час визначення шрифтів у конфігурації Vite. Директива @fonts викликає метод fonts фасаду Vite, який можна викликати й напряму:
{{ Vite::fonts(['sans', 'mono']) }}
Коментарі
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.
Ручна реєстрація компонентів пакета
Коли ви пишете компоненти для власного застосунку, вони автоматично виявляються в теках 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 автоматично визначить клас, привʼязаний до цього компонента, перевівши імʼя компонента в pascal case. Підтеки теж підтримуються через "крапкову" нотацію.
Рендеринг компонентів
Щоб показати компонент, використайте тег компонента 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;
}
Індексні компоненти
Іноді компоненти є частиною групи компонентів, і ви можете захотіти згрупувати повʼязані компоненти в одній теці. Уявіть, наприклад, компонент "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>
Передавання даних у компоненти
Дані передаються в компоненти 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');
}
}
Коли компонент рендериться, вміст його публічних змінних виводиться за іменем:
<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>
Blade відрендерить такий HTML:
<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 компонента має повертати замикання:
use Closure;
/**
* Отримати представлення / вміст, що репрезентує компонент.
*/
public function render(): Closure
{
return function () {
return '<div {{ $attributes }}>Components content</div>';
};
}
Замикання, повернуте методом render, може приймати єдиним аргументом масив $data. Цей масив містить кілька елементів з інформацією про компонент:
return function (array $data) {
// $data['componentName'];
// $data['attributes'];
// $data['slot'];
return '<div {{ $attributes }}>Components content</div>';
}
Попередження. Елементи масиву
$dataніколи не слід напряму вбудовувати в рядок Blade, який повертає методrender: це може відкрити віддалене виконання коду через шкідливий вміст атрибутів.
componentName дорівнює імені, використаному в HTML-тезі після префікса x-. Тож для <x-alert /> componentName буде alert. Елемент attributes міститиме всі атрибути, присутні в HTML-тезі. Елемент slot - це екземпляр Illuminate\Support\HtmlString із вмістом слота компонента.
Замикання має повертати рядок. Якщо повернутий рядок відповідає наявному представленню, буде відрендерено це представлення; інакше рядок буде обчислено як інлайнове представлення Blade.
Додаткові залежності
Якщо компоненту потрібні залежності з контейнера сервісів 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"/>
Усі атрибути, яких немає в конструкторі компонента, автоматично потраплять у "мішок атрибутів" (attribute bag) компонента. Цей мішок автоматично доступний у компоненті через змінну $attributes. Усі атрибути рендеряться в компоненті виведенням цієї змінної:
<div {{ $attributes }}>
<!-- Component content -->
</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">
<!-- Contents of the $message variable -->
</div>
Умовне обʼєднання класів
Іноді класи потрібно обʼєднувати, лише якщо певна умова істинна. Для цього є метод 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
Під час обʼєднання атрибутів, які не є class, значення, передані в метод merge, вважаються "типовими" значеннями атрибута. Проте, на відміну від атрибута class, такі атрибути не обʼєднуються з переданими значеннями атрибутів, а перезаписуються ними. Наприклад, реалізація компонента button може мати такий вигляд:
<button {{ $attributes->merge(['type' => 'button']) }}>
{{ $slot }}
</button>
Щоб відрендерити компонент 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') }}
Метод only повертає лише атрибути із заданими ключами:
{{ $attributes->only(['class']) }}
Метод except повертає всі атрибути, крім тих, що мають задані ключі:
{{ $attributes->except(['class']) }}
Зарезервовані ключові слова
За замовчуванням деякі ключові слова зарезервовані для внутрішніх потреб Blade під час рендерингу компонентів. Такі ключові слова не можна визначати як публічні властивості або імена методів у ваших компонентах:
datarenderresolveresolveViewshouldRenderviewwithAttributeswithName
Слоти
Часто виникає потреба передати в компонент додатковий вміст через "слоти". Слоти компонента рендеряться виведенням змінної $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
Слоти з областю видимості
Якщо ви працювали з 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>
Вбудовані представлення компонентів
Для зовсім невеликих компонентів тримати окремо і клас компонента, і його шаблон представлення (view) буває незручно. Тому розмітку компонента можна повернути прямо з методу 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" />
Ручна реєстрація компонентів
[!WARNING] Наведена далі документація про ручну реєстрацію компонентів стосується переважно тих, хто пише пакети 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/>
Щоб створити анонімний компонент через Artisan, використайте прапорець --view під час виклику команди make:component:
php artisan make:component forms.input --view
Наведена команда створить файл Blade за шляхом resources/views/components/forms/input.blade.php, який можна рендерити як компонент через <x-forms.input />.
Анонімні index-компоненти
Іноді компонент складається з багатьох шаблонів 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 дозволяє покласти всередину теки компонента файл, імʼя якого збігається з назвою цієї теки. Коли такий шаблон існує, він рендериться як «кореневий» елемент компонента, навіть будучи вкладеним у теку. Тож ми можемо й далі користуватися тим самим синтаксисом Blade, що й у прикладі вище, але структуру тек змінимо так:
/resources/views/components/accordion/accordion.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 ми можемо зробити його доступним і там:
<!-- /resources/views/components/menu/item.blade.php -->
@aware(['color' => 'gray'])
<li {{ $attributes->merge(['class' => 'text-'.$color.'-800']) }}>
{{ $slot }}
</li>
[!WARNING] Директива
@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)
<div>{{ $task }}</div>
@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)
<div>{{ $task }}</div>
@endforeach
</x-layout>
Тепер, коли макет і представлення списку завдань оголошені, лишається повернути представлення task з маршруту:
use App\Models\Task;
Route::get('/tasks', function () {
return view('tasks', ['tasks' => Task::all()]);
});
Макети на успадкуванні шаблонів
Оголошення макета
Макети можна створювати й через «успадкування шаблонів» (template inheritance). До появи компонентів це був основний спосіб будувати застосунки.
Почнімо з простого прикладу. Спершу розглянемо макет сторінки. Оскільки більшість вебзастосунків має однаковий загальний макет на різних сторінках, його зручно оголосити одним представленням Blade:
<!-- resources/views/layouts/app.blade.php -->
<html>
<head>
<title>App Name - @yield('title')</title>
</head>
<body>
@section('sidebar')
This is the master sidebar.
@show
<div class="container">
@yield('content')
</div>
</body>
</html>
Як бачите, цей файл містить звичайну HTML-розмітку. Зверніть увагу на директиви @section і @yield. Директива @section, як випливає з назви, оголошує секцію вмісту, а @yield виводить вміст заданої секції.
Тепер, коли макет для застосунку оголошено, оголосімо дочірню сторінку, яка його успадковує.
Розширення макета
Оголошуючи дочірнє представлення, використовуйте директиву Blade @extends, щоб указати, який макет воно має «успадкувати». Представлення, що розширюють макет Blade, можуть вставляти вміст у секції макета через директиви @section. Памʼятайте: як показано в прикладі вище, вміст цих секцій виводиться в макеті через @yield:
<!-- resources/views/child.blade.php -->
@extends('layouts.app')
@section('title', 'Page Title')
@section('sidebar')
@@parent
<p>This is appended to the master sidebar.</p>
@endsection
@section('content')
<p>This is my body content.</p>
@endsection
У цьому прикладі секція sidebar використовує директиву @@parent, щоб додати вміст до бічної панелі макета, а не перезаписати її. Під час рендерингу представлення директива @@parent буде замінена вмістом із макета.
[!NOTE] На відміну від попереднього прикладу, ця секція
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">Post 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">Email address</label>
<input
id="email"
type="email"
class="@error('email') is-invalid @else is-valid @enderror"
/>
Другим параметром директиві @error можна передати імʼя конкретної сумки помилок, щоб отримати повідомлення про помилки валідації на сторінках із кількома формами:
<!-- /resources/views/auth.blade.php -->
<label for="email">Email address</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 Contents -->
@stack('scripts')
</head>
Щоб додати вміст на початок стека, використовуйте директиву @prepend:
@push('scripts')
This will be second...
@endpush
// Later...
@prepend('scripts')
This will be first...
@endprepend
Директива @hasstack перевіряє, чи стек порожній:
@hasstack('list')
<ul>
@stack('list')
</ul>
@endif
Впровадження сервісів
Директива @inject дозволяє отримати сервіс із контейнера сервісів 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 видаляв ці тимчасові файли після рендерингу шаблону Blade, передайте методу аргумент deleteCachedView:
return Blade::render(
'Hello, {{ $name }}',
['name' => 'Julian Bashir'],
deleteCachedView: true
);
Рендеринг фрагментів Blade
Із фронтенд-фреймворками на кшталт Turbo та htmx інколи потрібно повернути в HTTP-відповіді лише частину шаблону Blade. Саме це дозволяють «фрагменти» 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 натрапляє на власну директиву, він викликає переданий колбек із виразом, що міститься в директиві.
Наступний приклад створює директиву @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'); ?>
[!WARNING] Після зміни логіки директиви Blade потрібно видалити всі закешовані представлення Blade. Зробити це можна Artisan-командою
view:clear.
Власні обробники виводу
Якщо спробувати вивести («echo») обʼєкт через Blade, буде викликано метод __toString цього обʼєкта. Метод __toString - одна з вбудованих у PHP «магічних методів». Проте інколи ви не контролюєте __toString певного класу, наприклад коли клас належить сторонній бібліотеці.
У таких випадках Blade дозволяє зареєструвати власний обробник виводу для конкретного типу обʼєктів. Для цього викличте метод Blade stringable. Метод 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')
<!-- The application is using the local disk... -->
@elsedisk('s3')
<!-- The application is using the s3 disk... -->
@else
<!-- The application is using some other disk... -->
@enddisk
@unlessdisk('local')
<!-- The application is not using the local disk... -->
@enddisk
Перекладаємо з офіційної документації, розділ за розділом, і не ховаємо недоперекладене. Помітили неточність у терміні чи реченні: напишіть, виправимо.