Шаблоны 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']);});
{tip} Хотите вывести свои шаблоны Blade на новый уровень и с легкостью создавать динамические интерфейсы? Проверьте Laravel Livewire.
Отображение данных
Вы можете отображать данные, которые передаются в Ваши представления Blade, заключив переменную в фигурные скобки. Например, учитывая следующий маршрут:
Route::get('/', function () { return view('welcome', ['name' => 'Вася']);});
Вы можете отобразить содержимое переменной name
следующим образом:
Привет, {{ $name }}.
{tip} Операторы вывода данных Blade
{{ }}
автоматически отправляются через PHP-функциюhtmlspecialchars
для предотвращения атак XSS.
Вы не ограничены отображением содержимого переменных, переданных в представление. Вы также можете повторить результаты любой функции PHP. Фактически, Вы можете поместить любой PHP-код в выражение echo шаблонизатора Blade:
Текущая временная метка UNIX - {{ 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{ /** * Загрузите любые сервисы приложений. * * @return void */ public function boot() { Blade::withoutDoubleEncoding(); }}
Отображение неэкранированных данных
По умолчанию операторы Blade {{ }}
автоматически отправляются через PHP-функцию htmlspecialchars
для предотвращения атак XSS. Если Вы не хотите, чтобы Ваши данные были экранированы, Вы можете использовать следующий синтаксис:
Привет, {!! $name !!}.
{note} Будьте очень осторожны при повторении содержимого, предоставленного пользователями Вашего приложения. Обычно следует использовать экранированный синтаксис двойных фигурных скобок для предотвращения атак XSS при отображении данных, предоставленных пользователем.
Blade и JavaScript Фреймворки
Поскольку во многих фреймворках JavaScript также используются «фигурные» скобки, чтобы указать, что данное выражение должно отображаться в браузере, Вы можете использовать символ @
, чтобы сообщить механизму рендеринга Blade, что выражение должно оставаться нетронутым. Например:
<h1>Laravel</h1> Привет, @{{ name }}.
В этом примере Blade удаляет символ @
; однако выражение {{ name }}
останется нетронутым механизмом Blade, что позволит обработать его Вашей инфраструктурой 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
вернет строку JSON.parse
, которая преобразует данный объект или массив в допустимый объект JavaScript:
<script> var app = {{ Illuminate\Support\Js::from($array) }};</script>
Последние версии скелета приложения Laravel включают фасад Js
, который обеспечивает удобный доступ к этой функциональности в ваших шаблонах Blade:
<script> var app = {{ Js::from($array) }};</script>
{note} Вы должны использовать только метод
Js::from
, чтобы отображать существующие переменные как JSON. Шаблоны Blade основаны на регулярных выражениях, и попытки передать сложное выражение в директиву могут привести к неожиданным сбоям.
Директива @verbatim
Если Вы показываете переменные JavaScript в большой части своего шаблона, Вы можете заключить HTML в директиву @verbatim
, чтобы Вам не приходилось добавлять к каждому оператору Blade echo префикс @
:
@verbatim <div class="container"> Привет, {{ name }}. </div>@endverbatim
Директивы Blade
Помимо наследования шаблонов и отображения данных, Blade также предоставляет удобные ярлыки для общих структур управления PHP, таких как условные операторы и циклы. Эти сочетания клавиш обеспечивают очень чистый и лаконичный способ работы со структурами управления PHP, но при этом остаются знакомыми своим аналогам PHP.
Операторы if
Вы можете создавать операторы if
, используя директивы @if
, @elseif
, @else
и @endif
. Эти директивы работают так же, как и их аналоги в PHP:
@if (count($records) === 1) У меня есть одна запись!@elseif (count($records) > 1) У меня несколько записей!@else У меня нет записей!@endif
For convenience, Blade also provides an @unless
directive:
@unless (Auth::check()) Вы не вошли в аккаунт.@endunless
В дополнение к уже обсужденным условным директивам, директивы @isset
и @empty
могут использоваться как удобные ярлыки для соответствующих функций PHP:
@isset($records) // $records определены и не равны нулю...@endisset @empty($records) // $records равна "пустоте"...@endempty
Директивы аутентификации
Директивы @auth
и @guest
могут использоваться для быстрого определения, является ли текущий пользователь аутентифицированным или гостем:
@auth // Пользователь аутентифицирован...@endauth @guest // Пользователь не аутентифицирован...@endguest
При необходимости Вы можете указать защиту аутентификации, которую следует проверять при использовании директив @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
Операторы switch
Операторы switch могут быть созданы с помощью директив @switch
, @case
, @break
, @default
и @endswitch
:
@switch($i) @case(1) Первый случай... @break @case(2) Второй случай... @break @default Случай по умолчанию...@endswitch
Циклы
В дополнение к условным операторам Blade предоставляет простые директивы для работы со структурами циклов PHP. Опять же, каждая из этих директив функционирует идентично своим аналогам PHP:
@for ($i = 0; $i < 10; $i++) Текущее значение {{ $i }}@endfor @foreach ($users as $user) <p>Это пользователь {{ $user->id }}</p>@endforeach @forelse ($users as $user) <li>{{ $user->name }}</li>@empty <p>Нет пользователей</p>@endforelse @while (true) <p>Я зацикливаюсь навсегда.</p>@endwhile
{tip} При повторении цикла
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) Это первая итерация. @endif @if ($loop->last) Это последняя итерация. @endif <p>Это пользователь {{ $user->id }}</p>@endforeach
Если Вы находитесь во вложенном цикле, Вы можете получить доступ к переменной родительского цикла $loop
через свойство parent
:
@foreach ($users as $user) @foreach ($user->posts as $post) @if ($loop->parent->first) Это первая итерация родительского цикла. @endif @endforeach@endforeach
Переменная $loop
также содержит множество других полезных свойств:
Свойство | Описание |
---|---|
$loop->index |
Индекс текущей итерации цикла (начинается с 0). |
$loop->iteration |
Текущая итерация цикла (начинается с 1). |
$loop->remaining |
Итерации, оставшиеся в цикле. |
$loop->count |
Общее количество элементов в повторяемом массиве. |
$loop->first |
Является ли это первой итерацией цикла. |
$loop->last |
Является ли это последней итерацией цикла. |
$loop->even |
Является ли это четным повторением цикла. |
$loop->odd |
Является ли это нечетным повторением цикла. |
$loop->depth |
Уровень вложенности текущего цикла. |
$loop->parent |
Находясь во вложенном цикле, переменная родительского цикла. |
Условные классы
Директива @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>
Включение подпредставлений
{tip} Хотя Вы можете использовать директиву
@include
, Blade компоненты предоставляют аналогичные функции и предлагают несколько преимуществ по сравнению с директивой@include
, например привязку данных и атрибутов.
Директива Blade @include
позволяет Вам включать представление Blade из другого представления. Все переменные, доступные для родительского представления, будут доступны для включенного представления:
<div> @include('shared.errors') <form> <!-- Содержание формы --> </form></div>
Несмотря на то, что включенное представление унаследует все данные, доступные в родительском представлении, Вы также можете передать массив дополнительных данных, которые должны быть доступны для включенного представления:
@include('view.name', ['status' => 'complete'])
Если Вы попытаетесь добавить несуществующее представление @include
, Laravel выдаст ошибку. Если Вы хотите включить представление, которое может присутствовать или отсутствовать, Вам следует использовать директиву @includeIf
:
@includeIf('view.name', ['status' => 'complete'])
Если Вы хотите добавить представление @include
, если данное логическое выражение оценивается как true
или false
, Вы можете использовать директивы @includeWhen
и @includeUnless
:
@includeWhen($boolean, 'view.name', ['status' => 'complete']) @includeUnless($boolean, 'view.name', ['status' => 'complete'])
Чтобы включить первое представление, которое существует из данного массива представлений, Вы можете использовать директиву includeFirst
:
@includeFirst(['custom.admin', 'admin'], ['status' => 'complete'])
{note} Вам следует избегать использования констант
__DIR__
и__FILE__
в Ваших представлениях Blade, поскольку они будут ссылаться на расположение кэшированного, скомпилированного представления.
Визуализация представлений для коллекций
Вы можете комбинировать циклы и включать в одну строку с помощью директивы Blade @each
:
@each('view.name', $jobs, 'job')
Первый аргумент директивы @each
- это представление, отображаемое для каждого элемента в массиве или коллекции. Второй аргумент - это массив или коллекция, которые Вы хотите перебрать, а третий аргумент - это имя переменной, которая будет присвоена текущей итерации в представлении. Так, например, если Вы выполняете итерацию по массиву заданий jobs
, обычно Вам нужно обращаться к каждому заданию как к переменной задания job
в представлении. Ключ массива для текущей итерации будет доступен как переменная key
в представлении.
Вы также можете передать четвертый аргумент директиве @each
. Этот аргумент определяет представление, которое будет отображаться, если данный массив пуст.
@each('view.name', $jobs, 'job', 'view.empty')
{note} Представления, отображаемые с помощью
@each
, не наследуют переменные родительского представления. Если дочернему представлению требуются эти переменные, Вам следует использовать вместо них директивы@foreach
и@include
.
Директива @once
Директива @once
позволяет Вам определить часть шаблона, которая будет оцениваться только один раз за цикл рендеринга. Это может быть полезно для вставки данного фрагмента JavaScript в заголовок страницы с помощью stacks. Например, если Вы визуализируете данный компонент в цикле, Вы можете захотеть вставить JavaScript в заголовок только при первой визуализации компонента:
@once @push('scripts') <script> // Ваш собственный JavaScript... </script> @endpush@endonce
Необработанный PHP
В некоторых ситуациях полезно встраивать PHP-код в Ваши представления. Вы можете использовать директиву Blade @php
для выполнения блока простого PHP в Вашем шаблоне:
@php $counter = 1;@endphp
Комментарии
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(){ Blade::component('package-alert', Alert::class);}
После того, как ваш компонент зарегистрирован, он может быть отображен с использованием псевдонима тега:
<x-package-alert/>
В качестве альтернативы вы можете использовать метод componentNamespace
для автоматической загрузки классов компонентов по соглашению. Например, пакет Nightshade
может иметь компоненты Calendar
b ColorPicker
, которые находятся в пространстве имен Package\Views\Components
:
use Illuminate\Support\Facades\Blade; /** * Загрузка сервисов вашего пакета. * * @return void */public function boot(){ Blade::componentNamespace('Nightshade\\Views\\Components', 'nightshade');}
Это позволит использовать компоненты пакета в пространстве имен их провайдеров, используя синтаксис package-name::
:
<x-nightshade::calendar /><x-nightshade::color-picker />
Blade автоматически обнаружит класс, связанный с этим компонентом, заключив имя компонента в паскаль. Подкаталоги также поддерживаются с использованием "точечной" нотации.
Рендеринг компонентов
Для отображения компонента Вы можете использовать тег компонента Blade в одном из Ваших шаблонов Blade. Теги компонентов Blade начинаются со строки x-
, за которой следует кебаб кейс имя класса компонента:
<x-alert/> <x-user-profile/>
Если класс компонента вложен глубже в каталог App\View\Components
, Вы можете использовать символ .
для обозначения вложенности каталогов. Например, если мы предполагаем, что компонент находится в App\View\Components\Inputs\Button.php
, мы можем отобразить его так:
<x-inputs.button/>
Передача данных в компоненты
Вы можете передавать данные компонентам Blade, используя атрибуты HTML. Жестко запрограммированные примитивные значения могут быть переданы компоненту с помощью простых строк атрибутов HTML. Выражения и переменные PHP должны передаваться компоненту через атрибуты, которые используют символ :
в качестве префикса:
<x-alert type="error" :message="$message"/>
Вы должны определить необходимые данные компонента в его конструкторе класса. Все общедоступные свойства компонента будут автоматически доступны в представлении компонента. Нет необходимости передавать данные в представление из метода компонента render
:
<?php namespace App\View\Components; use Illuminate\View\Component; class Alert extends Component{ /** * The alert type. * * @var string */ public $type; /** * The alert message. * * @var string */ public $message; /** * Создайте экземпляр компонента. * * @param string $type * @param string $message * @return void */ public function __construct($type, $message) { $this->type = $type; $this->message = $message; } /** * Get the view / contents that represent the component. * * @return \Illuminate\View\View|\Closure|string */ public function render() { return view('components.alert'); }}
Когда Ваш компонент визуализируется, Вы можете отображать содержимое общедоступных переменных Вашего компонента, повторяя переменные по имени:
<div class="alert alert-{{ $type }}"> {{ $message }}</div>
Преобразование регистра
Аргументы конструктора компонентов следует указывать с помощью camelCase
, а при обращении к именам аргументов в Ваших атрибутах HTML следует использовать kebab-case
. Например, учитывая следующий конструктор компонента:
/** * Создайте экземпляр компонента. * * @param string $alertType * @return void */public function __construct($alertType){ $this->alertType = $alertType;}
Аргумент $alertType
может быть предоставлен компоненту следующим образом:
<x-alert alert-type="danger" />
Экранирование рендеринга атрибутов
Поскольку некоторые фреймворки JavaScript, такие как Alpine.js, также используют атрибуты с префиксом двоеточия, вы можете использовать префикс с двойным двоеточием (::
), чтобы сообщить Blade, что атрибут не является выражением PHP. Например, учитывая следующий компонент:
<x-button ::class="{ danger: isDeleting }"> Submit</x-button>
Blade отобразит следующий HTML-код:
<button :class="{ danger: isDeleting }"> Submit</button>
Методы компонента
В дополнение к общедоступным переменным, доступным для вашего шаблона компонента, могут быть вызваны любые общедоступные методы компонента. Например, представьте компонент, у которого есть метод isSelected
:
/** * Определите, является ли данная опция выбранной в данный момент. * * @param string $option * @return bool */public function isSelected($option){ return $option === $this->selected;}
Вы можете выполнить этот метод из своего шаблона компонента, вызвав переменную, соответствующую имени метода:
<option {{ $isSelected($value) ? 'selected="selected"' : '' }} value="{{ $value }}"> {{ $label }}</option>
Доступ к атрибутам и слотам в классах компонентов
Компоненты Blade также позволяют получить доступ к имени компонента, атрибутам и слоту внутри метода рендеринга класса. Однако, чтобы получить доступ к этим данным, Вы должны вернуть замыкание из метода render
Вашего компонента. Замыкание получит массив $data
в качестве единственного аргумента. Этот массив будет содержать несколько элементов, предоставляющих информацию о компоненте:
/** * Получить представление / содержимое, представляющее компонент. * * @return \Illuminate\View\View|\Closure|string */public function render(){ return function (array $data) { // $data['componentName']; // $data['attributes']; // $data['slot']; return '<div>Components content</div>'; };}
componentName
равнозначно имени, используемому в теге HTML после префикса x-
. Таким образом, элемент <x-alert />
- componentName
будет alert
. Элемент attributes
будет содержать все атрибуты, которые присутствовали в теге HTML. Элемент slot
- это экземпляр Illuminate\Support\HtmlString
с содержимым слота компонента.
Замыкание должно возвращать строку. Если возвращенная строка соответствует существующему представлению, это представление будет визуализировано; в противном случае возвращенная строка будет оцениваться как встроенное представление Blade.
Дополнительные зависимости
Если Вашему компоненту требуются зависимости от сервисного контейнера Laravel, Вы можете указать их перед любыми атрибутами данных компонента, и они будут автоматически внедрены контейнером:
use App\Services\AlertCreator /** * Создайте экземпляр компонента. * * @param \App\Services\AlertCreator $creator * @param string $type * @param string $message * @return void */public function __construct(AlertCreator $creator, $type, $message){ $this->creator = $creator; $this->type = $type; $this->message = $message;}
Скрытие атрибутов / методов
Если вы хотите, чтобы некоторые общедоступные методы или свойства не отображались как переменные в шаблоне компонента, вы можете добавить их в свойство массива $except
в вашем компоненте:
<?php namespace App\View\Components; use Illuminate\View\Component; class Alert extends Component{ /** * The alert type. * * @var string */ public $type; /** * The properties / methods that should not be exposed to the component template. * * @var array */ protected $except = ['type'];}
Атрибуты компонента
Мы уже рассмотрели, как передавать атрибуты данных в компонент; однако иногда Вам может потребоваться указать дополнительные атрибуты HTML, такие как class
, которые не являются частью данных, необходимых для функционирования компонента. Как правило, Вы хотите передать эти дополнительные атрибуты корневому элементу шаблона компонента. Например, представьте, что мы хотим отобразить компонент alert
следующим образом:
<x-alert type="error" :message="$message" class="mt-4"/>
Все атрибуты, которые не являются частью конструктора компонента, будут автоматически добавлены в «мешок атрибутов» компонента. Этот набор атрибутов автоматически становится доступным для компонента через переменную $attributes
. Все атрибуты могут отображаться в компоненте путем повторения этой переменной:
<div {{ $attributes }}> <!-- Контент компонента --></div>
{note} Использование директив, таких как
@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>
{tip} Если вам нужно условно скомпилировать классы для других элементов HTML, которые не должны получать объединенные атрибуты, вы можете использовать директиву
@class
directive.
Слияние неклассовых атрибутов
При объединении атрибутов, которые не являются атрибутами 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 ($value, $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>Атрибут класса присутствует</div>@endif
Вы можете получить значение определенного атрибута, используя метод get
:
{{ $attributes->get('class') }}
Зарезервированные ключевые слова
По умолчанию некоторые ключевые слова зарезервированы для внутреннего использования Blade для визуализации компонентов. Следующие ключевые слова не могут быть определены как общедоступные свойства или имена методов в ваших компонентах:
-
data
-
render
-
resolveView
-
shouldRender
-
view
-
withAttributes
-
withName
Слоты
Вам часто нужно будет передавать дополнительный контент Вашему компоненту через «слоты». Слоты компонентов отображаются путем повторения переменной $slot
. Чтобы изучить эту концепцию, представим, что компонент alert
имеет следующую разметку:
<!-- /resources/views/components/alert.blade.php --> <div class="alert alert-danger"> {{ $slot }}</div>
Мы можем передавать контент в slot
, вставляя контент в компонент:
<x-alert> <strong>Упс!</strong> Что-то пошло не так!</x-alert>
Иногда компоненту может потребоваться отрисовать несколько разных слотов в разных местах внутри компонента. Давайте модифицируем наш компонент оповещения, чтобы учесть вставку слота «заголовок»:
<!-- /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 name="title"> Ошибка сервера </x-slot> <strong>Упс!</strong> Что-то пошло не так!</x-alert>
Слоты с ограниченным доступом
Если Вы использовали фреймворк JavaScript, такой как Vue, Вы, возможно, знакомы со «слотами с заданной областью», которые позволяют Вам получать доступ к данным или методам из компонента в Вашем слоте. Вы можете добиться аналогичного поведения в Laravel, определив общедоступные методы или свойства в Вашем компоненте и получив доступ к компоненту в Вашем слоте через переменную $component
. В этом примере мы предположим, что компонент x-alert
имеет общедоступный метод formatAlert
, определенный в его классе компонента:
<x-alert> <x-slot name="title"> {{ $component->formatAlert('Ошибка сервера') }} </x-slot> <strong>Упс!</strong> Что-то пошло не так!</x-alert>
Slot Attributes
Like Blade components, you may assign additional attributes to slots such as CSS class names:
<x-card class="shadow-sm"> <x-slot name="heading" class="font-bold"> Heading </x-slot> Content <x-slot name="footer" class="text-sm"> Footer </x-slot></x-card>
To interact with slot attributes, you may access the attributes
property of the slot's variable. For more information on how to interact with attributes, please consult the documentation on component 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
:
/** * Получите представление / содержимое, представляющее компонент. * * @return \Illuminate\View\View|\Closure|string */public function render(){ return <<<'blade' <div class="alert alert-danger"> {{ $slot }} </div> blade;}
Создание встроенных компонентов представления
Чтобы создать компонент, который отображает встроенное представление, Вы можете использовать параметр inline
при выполнении команды make:component
:
php artisan make:component Alert --inline
Анонимные компоненты
Подобно встроенным компонентам, анонимные компоненты предоставляют механизм для управления компонентом через один файл. Однако анонимные компоненты используют один файл представления и не имеют связанного класса. Чтобы определить анонимный компонент, Вам нужно только поместить шаблон Blade в Ваш каталог resources/views/components
. Например, если Вы определили компонент в resources/views/components/alert.blade.php
, Вы можете просто отрендерить его так:
<x-alert/>
Вы можете использовать символ .
, чтобы указать, вложен ли компонент глубже в каталог components
. Например, если компонент определен в resources/views/components/inputs/button.blade.php
, Вы можете отобразить его так:
<x-inputs.button/>
Anonymous Index Components
Sometimes, when a component is made up of many Blade templates, you may wish to group the given component's templates within a single directory. For example, imagine an "accordion" component with the following directory structure:
/resources/views/components/accordion.blade.php/resources/views/components/accordion/item.blade.php
This directory structure allows you to render the accordion component and its item like so:
<x-accordion> <x-accordion.item> ... </x-accordion.item></x-accordion>
However, in order to render the accordion component via x-accordion
, we were forced to place the "index" accordion component template in the resources/views/components
directory instead of nesting it within the accordion
directory with the other accordion related templates.
Thankfully, Blade allows you to place an index.blade.php
file within a component's template directory. When an index.blade.php
template exists for the component, it will be rendered as the "root" node of the component. So, we can continue to use the same Blade syntax given in the example above; however, we will adjust our directory structure like so:
/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"/>
Accessing Parent Data
Sometimes you may want to access data from a parent component inside a child component. In these cases, you may use the @aware
directive. For example, imagine we are building a complex menu component consisting of a parent <x-menu>
and child <x-menu.item>
:
<x-menu color="purple"> <x-menu.item>...</x-menu.item> <x-menu.item>...</x-menu.item></x-menu>
The <x-menu>
component may have an implementation like the following:
<!-- /resources/views/components/menu/index.blade.php --> @props(['color' => 'gray']) <ul {{ $attributes->merge(['class' => 'bg-'.$color.'-200']) }}> {{ $slot }}</ul>
Because the color
prop was only passed into the parent (<x-menu>
), it won't be available inside <x-menu.item>
. However, if we use the @aware
directive, we can make it available inside <x-menu.item>
as well:
<!-- /resources/views/components/menu/item.blade.php --> @aware(['color' => 'gray']) <li {{ $attributes->merge(['class' => 'text-'.$color.'-800']) }}> {{ $slot }}</li>
Динамические компоненты
Иногда Вам может потребоваться визуализировать компонент, но Вы не знаете, какой компонент следует визуализировать, до времени выполнения. В этой ситуации Вы можете использовать встроенный в Laravel компонент dynamic-component
для рендеринга компонента на основе значения времени выполнения или переменной:
<x-dynamic-component :component="$componentName" class="mt-4" />
Регистрация компонентов вручную
{note} Следующая документация по ручной регистрации компонентов в первую очередь применима к тем, кто пишет пакеты Laravel, которые включают компоненты представления. Если Вы не пишете пакет, эта часть документации компонента может быть вам не нужна.
При написании компонентов для Вашего собственного приложения компоненты автоматически обнаруживаются в каталогах app/View/Components
и resources/views/components
.
Однако, если Вы создаете пакет, который использует компоненты Blade или размещаете компоненты в нетрадиционных каталогах, Вам нужно будет вручную зарегистрировать класс компонента и его псевдоним HTML-тега, чтобы Laravel знал, где найти компонент. Обычно Вы должны зарегистрировать свои компоненты в методе boot
сервис провайдера Вашего пакета:
use Illuminate\Support\Facades\Blade;use VendorPackage\View\Components\AlertComponent; /** * Загрузка Вашего сервисного пакета. * * @return void */public function boot(){ Blade::component('package-alert', AlertComponent::class);}
После того, как Ваш компонент был зарегистрирован, он может быть отображен с использованием псевдонима тега:
<x-package-alert/>
Автозагрузка компонентов пакета
Как вариант, вы можете использовать метод componentNamespace
для автоматической загрузки классов компонентов по соглашению. Например, пакет Nightshade
может иметь компоненты Calendar
и ColorPicker
, которые находятся в пространстве имен Package\Views\Components
:
use Illuminate\Support\Facades\Blade; /** * Загрузка Вашего сервисного пакета. * * @return void */public function boot(){ Blade::componentNamespace('Nightshade\\Views\\Components', 'nightshade');}
Это позволит использовать компоненты пакета по пространству имен их провайдеров, используя синтаксис package-name::
:
<x-nightshade::calendar /><x-nightshade::color-picker />
Blade автоматически обнаружит класс, связанный с этим компонентом, заключив имя компонента в паскаль. Подкаталоги также поддерживаются с использованием "точечной" нотации.
Создание макетов
Макеты с использованием компонентов
Большинство веб-приложений поддерживают одинаковый общий макет на разных страницах. Было бы невероятно громоздко и сложно поддерживать наше приложение, если бы нам приходилось повторять весь 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 name="title"> Custom Title </x-slot> @foreach ($tasks as $task) {{ $task }} @endforeach</x-layout>
Теперь, когда мы определили наш макет и представления списка задач, нам просто нужно вернуть представление task
из маршрута:
use App\Models\Task; Route::get('/tasks', function () { return view('tasks', ['tasks' => Task::all()]);});
Макеты с использованием наследования шаблонов
Определение макета
Макеты также могут быть созданы с помощью «наследования шаблонов». Это был основной способ создания приложений до появления компонентов.
Для начала рассмотрим простой пример. Сначала мы рассмотрим макет страницы. Поскольку большинство веб-приложений поддерживают один и тот же общий макет на разных страницах, удобно определить этот макет как единое представление Blade:
<!-- resources/views/layouts/app.blade.php --> <html> <head> <title>Название Приложения - @yield('title')</title> </head> <body> @section('sidebar') Это главная боковая панель. @show <div class="container"> @yield('content') </div> </body></html>
Как видите, этот файл содержит типичную разметку HTML. Однако обратите внимание на директивы @section
и @yield
. Директива @section
, как следует из названия, определяет раздел содержимого, тогда как директива @yield
используется для отображения содержимого данного раздела.
Теперь, когда мы определили макет для нашего приложения, давайте определим дочернюю страницу, которая наследует макет.
Расширение макета
При определении дочернего представления используйте директиву Blade @extends
, чтобы указать, какой макет дочернее представление должно «наследовать». Представления, которые расширяют макет Blade, могут вставлять контент в разделы макета с помощью директив @section
. Помните, как показано в приведенном выше примере, содержимое этих разделов будет отображаться в макете с помощью @yield
:
<!-- resources/views/child.blade.php --> @extends('layouts.app') @section('title', 'Page Title') @section('sidebar') @parent <p>Это добавлено на главную боковую панель.</p>@endsection @section('content') <p>Это содержимое тела.</p>@endsection
В этом примере секция sidebar
использует директиву @parent
для добавления (а не перезаписи) содержимого на боковую панель макета. Директива @parent
будет заменена содержимым макета при визуализации представления.
{tip} В отличие от предыдущего примера, этот раздел
sidebar
заканчивается@endsection
вместо@show
. Директива@endsection
будет определять только раздел, в то время как@show
будет определять и немедленно передать раздел.
Директива @yield
также принимает значение по умолчанию в качестве второго параметра. Это значение будет отображаться, если получаемый раздел не определен:
@yield('content', 'Контент по умолчанию')
Формы
Поле CSRF
Каждый раз, когда вы определяете HTML-форму в своем приложении, вы должны включать в форму скрытое поле токена CSRF, чтобы мидлвар 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">Адрес электронной почты</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
Вы можете помещать в стек сколько угодно раз. Чтобы отобразить полное содержимое стека, передайте имя стека в директиву @stack
:
<head> <!-- Head Contents --> @stack('scripts')</head>
Если Вы хотите добавить контент в начало стека, Вы должны использовать директиву @prepend
:
@push('scripts') Это будет второй...@endpush // Later... @prepend('scripts') Это будет первый...@endprepend
Сервисная инъекция
Директива @inject
может использоваться для извлечения службы из Laravel сервис контейнер. Первый аргумент, переданный в @inject
, - это имя переменной, в которую будет помещена служба, а второй аргумент - это имя класса или интерфейса службы, которую Вы хотите разрешить:
@inject('metrics', 'App\Services\MetricsService') <div> Ежемесячный доход: {{ $metrics->monthlyRevenue() }}.</div>
Расширение Blade
Blade позволяет Вам определять Ваши собственные пользовательские директивы с помощью метода directive
. Когда компилятор Blade встречает настраиваемую директиву, он вызывает предоставленный обратный вызов с выражением, содержащимся в директиве.
В следующем примере создается директива @datetime($var)
, которая форматирует заданный $var
, который должен быть экземпляром DateTime
:
<?php namespace App\Providers; use Illuminate\Support\Facades\Blade;use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider{ /** * Зарегистрируйте любые сервисы приложения. * * @return void */ public function register() { // } /** * Загрузите любые сервисы приложений. * * @return void */ public function boot() { Blade::directive('datetime', function ($expression) { return "<?php echo ($expression)->format('m/d/Y H:i'); ?>"; }); }}
Как видите, мы привяжем метод format
к любому выражению, переданному в директиву. Итак, в этом примере последний PHP, сгенерированный этой директивой, будет:
<?php echo ($var)->format('m/d/Y H:i'); ?>
{note} После обновления логики директивы Blade Вам нужно будет удалить все кэшированные представления 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; /** * Bootstrap any application services. * * @return void */public function boot(){ Blade::stringable(function (Money $money) { return $money->formatTo('en_GB'); });}
После того, как ваш собственный обработчик эха был определен, вы можете просто повторить объект в своем шаблоне Blade:
Cost: {{ $money }}
Пользовательские операторы if
Программирование настраиваемой директивы иногда бывает более сложным, чем необходимо при определении простых настраиваемых условных операторов. По этой причине Blade предоставляет метод Blade::if
, который позволяет Вам быстро определять пользовательские условные директивы с помощью замыканий. Например, давайте определим настраиваемое условие, которое проверяет настроенный по умолчанию «диск» для приложения. Мы можем сделать это в методе boot
нашего AppServiceProvider
:
use Illuminate\Support\Facades\Blade; /** * Загрузите любые сервисы приложений. * * @return void */public function boot(){ Blade::if('disk', function ($value) { return config('filesystems.default') === $value; });}
После того, как пользовательское условие было определено, Вы можете использовать его в своих шаблонах:
@disk('local') <!-- Приложение использует локальный диск... -->@elsedisk('s3') <!-- Приложение использует диск s3... -->@else <!-- Приложение использует другой диск... -->@enddisk @unlessdisk('local') <!-- Приложение не использует локальный диск... -->@enddisk