Шаблоны в Laravel
Шаблонизатор Blade
Введение
Blade – это простой, но мощный движок шаблонов, входящий в состав Laravel. В отличие от некоторых шаблонизаторов PHP, Blade не ограничивает вас в использовании обычного “сырого” кода PHP в ваших шаблонах. На самом деле, все шаблоны Blade компилируются в обычный PHP-код и кешируются до тех пор, пока не будут изменены, что означает, что Blade добавляет фактически нулевую нагрузку вашему приложению. Файлы шаблонов Blade используют расширение файла .blade.php
и обычно хранятся в каталоге resources/views
.
Шаблоны Blade могут быть возвращены из маршрутов или контроллера с помощью глобального помощника view
. Данные могут быть переданы в шаблоны Blade, используя второй аргумент помощника view
:
Отображение данных
Вы можете отображать данные, которые передаются в шаблоны Blade, заключив переменную в фигурные скобки. Например, учитывая следующий маршрут:
Вы можете отобразить содержимое переменной name
следующим образом:
Выражения вывода { }
Blade автоматически отправляются через функцию htmlspecialchars
PHP для предотвращения XSS-атак.
Вы не ограничены отображением содержимого переменных, переданных в шаблон. Вы также можете вывести результаты любой функции PHP. Фактически, вы можете поместить любой PHP-код в выражение вывода Blade:
Вывод неэкранированных данных
По умолчанию, выражения вывода { }
Blade автоматически отправляются через функцию htmlspecialchars
PHP для предотвращения XSS-атак. Если вы не хотите, чтобы ваши данные были экранированы, вы можете использовать следующий синтаксис:
Директивы Blade
Описание
Помимо наследования шаблонов и отображения данных, Blade также содержит удобные псевдонимы для общих структур управления PHP, таких, как условные операторы и циклы. Эти директивы обеспечивают очень чистый и лаконичный способ работы со структурами управления PHP, но при этом остаются схожими со своими аналогами PHP.
Операторы If
Вы можете создавать операторы 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
:
В дополнение к уже обсужденным условным директивам, директивы @isset
и @empty
могут использоваться в качестве удобных ярлыков для соответствующих функций PHP:
Директивы аутентификации
Директивы @auth
и @guest
могут использоваться для быстрого определения, является ли текущий пользователь аутентифицированным или считается гостем:
@auth
// Пользователь аутентифицирован ...
@endauth
@guest
// Пользователь не аутентифицирован ...
@endguest
При необходимости вы можете указать охранника аутентификации для проверки при использовании директив @auth
и @guest
:
Директивы окружения
Вы можете проверить, запущено ли приложение в эксплуатационном окружении, с помощью директивы @production
:
Или вы можете определить, работает ли приложение в конкретной среде, с помощью директивы @env
:
Директивы секций
Вы можете определить, есть ли в секции наследуемого шаблона содержимое, используя директиву @hasSection
:
@hasSection('navigation')
<div class="pull-right">
@yield('navigation')
</div>
<div class="clearfix"></div>
@endif
Вы можете использовать директиву sectionMissing
, чтобы определить, что в секции нет содержимого:
Директивы сессии
Директива @session
может использоваться для определения существования значения сессии. Если значение сессии существует, содержимое шаблона внутри директив @session
и @endsession
будет оценено. Внутри содержимого директивы @session
вы можете использовать переменную $value
для вывода значения сессии:
Операторы Switch
Операторы Switch могут быть созданы с использованием директив @switch
, @case
, @break
, @default
и @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
При использовании циклов вы также можете пропустить текущую итерацию или завершить цикл, используя директивы @continue
и @break
:
@foreach ($users as $user)
@if ($user->type == 1)
@continue
@endif
<li>{{ $user->name }}</li>
@if ($user->number == 5)
@break
@endif
@endforeach
Вы также можете включить в объявление директивы условие продолжения или прерывания:
Переменная Loop
Во время повторения цикла 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 |
Переменная родительского цикла во вложенном цикле. |
Css-классы и стили по условию
Директива @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-элементу:
Дополнительные атрибуты
Для удобства, вы можете использовать директиву @checked
, чтобы легко указать, должен ли данный флажок HTML быть “отмеченным”. Эта директива будет выводить checked
, если условие выполнится:
Аналогично, директива @selected
может использоваться, чтобы указать, должен ли заданный вариант выбора быть “выбранным”:
<select name="version">
@foreach ($product->versions as $version)
<option value="{{ $version }}" @selected(old('version') == $version)>
{{ $version }}
</option>
@endforeach
</select>
Кроме того, директива @disabled
может использоваться, чтобы указать, должен ли данный элемент быть “отключенным”:
Более того, директива @readonly
может быть использована, чтобы указать, должен ли данный элемент быть “только для чтения”:
Кроме того, директива @required
может быть использована, чтобы указать, должен ли данный элемент быть “обязательным”:
Подключение дочерних шаблонов
Директива @include
Blade позволяет вам включать шаблоны из другого шаблона. Все переменные, доступные для родительского шаблона, будут доступны для включенного шаблона:
Включенный шаблон унаследует все данные, доступные в родительском шаблоне, но вы также можете передать массив дополнительных данных, которые должны быть доступны для включенного шаблона:
Если вы попытаетесь включить несуществующий шаблон, Laravel выдаст ошибку. Если вы хотите включить шаблон, который может присутствовать или отсутствовать, вам следует использовать директиву @includeIf
:
Если вы хотите включить шаблон в зависимости от результата логического выражения, возвращающего либо true
, либо false
, то используйте директивы @includeWhen
и @includeUnless
:
@includeWhen($boolean, 'view.name', ['status' => 'complete'])
@includeUnless($boolean, 'view.name', ['status' => 'complete'])
Чтобы включить первый существующий шаблон из переданного массива шаблонов, вы можете использовать директиву includeFirst
:
Отрисовка шаблонов с коллекциями
Вы можете скомбинировать циклы и подключение шаблона в одну строку с помощью директивы Blade @each
:
Первый аргумент директивы @each
– это шаблон, отображаемый для каждого элемента в массиве или коллекции. Второй аргумент – это массив или коллекция, которую вы хотите перебрать. Третий аргумент – это имя переменной, которая будет присвоена текущей итерации в шаблоне. Так, например, если вы выполняете итерацию по массиву jobs
, обычно вам нужно обращаться к каждому элементу как к переменной job
в шаблоне. Ключ массива для текущей итерации будет доступен как переменная key
в шаблоне.
Вы можете передать четвертый аргумент директиве @each
. Этот аргумент определяет шаблон, который будет отображаться, если переданный массив пуст.
@each('view.name', $jobs, 'job', 'view.empty')
Директива @once
Директива @once
позволяет вам определить часть шаблона, которая будет проанализирована только один раз за цикл визуализации. Это может быть полезно для вставки переданного фрагмента JavaScript в подвал страницы с помощью стеков. Например, если вы отображаете переданный компонент в цикле, то бывает необходимо разместить JavaScript в подвале при визуализации компонента только единожды:
Поскольку директива @once
часто используется в сочетании с директивами @push
или @prepend
, для удобства доступны директивы @pushOnce
и @prependOnce
:
Необработанный PHP
В крайних ситуациях можно встроить PHP-код в ваши шаблоны. Вы можете использовать директиву @php
Blade для размещения блока простого PHP в вашем шаблоне:
Или, если вам нужно использовать только PHP для импорта класса, вы можете использовать директиву @use
:
Второй аргумент может быть использован в директиве @use
для указания псевдонима импортируемого класса:
Комментарии
Blade также позволяет вам определять комментарии в ваших шаблонах. Однако, в отличие от комментариев HTML, комментарии Blade не будут включены в результирующий HTML, возвращаемый вашим приложением:
Компоненты
Описание
Компоненты и слоты предоставляют те же преимущества, что и секции, макеты и включение шаблона из другого шаблона; однако, некоторым может быть легче понять мысленную модель компонентов и слотов. Есть два подхода к написанию компонентов: компоненты на основе классов и анонимные компоненты.
Чтобы создать компонент на основе класса, вы можете использовать команду make:component
Artisan. Чтобы проиллюстрировать, как использовать компоненты, мы создадим простой компонент Alert
. Команда make:component
поместит компонент в каталог app/View/Components
:
Команда make: component
также создаст шаблон для компонента. Шаблон будет помещен в каталог resources/views/components
. При написании компонентов для вашего собственного приложения компоненты автоматически обнаруживаются в каталогах app/View/Components
и resources/views/components
, поэтому дополнительная регистрация компонентов обычно не требуется.
Вы также можете создавать компоненты в подкаталогах:
Приведенная выше команда создаст компонент Input
в каталоге app/View/Components/Forms
, а шаблон будет помещен в каталог resources/views/components/forms
.
Если вы хотите создать анонимный компонент (компонент только с шаблоном в Blade без класса), вы можете использовать флаг --view
при вызове команды make:component
:
Вышеприведенная команда создаст файл Blade по пути resources/views/components/forms/input.blade.php
, который может быть отображен как компонент с помощью <x-forms.input />
.
Отрисовка компонентов
Для отображения компонента вы можете использовать тег компонента Blade в одном из ваших шаблонов Blade. Теги компонентов Blade начинаются со строки x-
, за которой следует имя в «шашлычном регистре» класса компонента:
Если класс компонента имеет вложенность в каталоге app/View/Components
, то вы можете использовать символ .
для обозначения вложенности каталогов. Например, если мы предполагаем, что компонент находится в app/View/Components/Inputs/Button.php
, то мы можем отобразить его так:
Если вы хотите выборочно отображать ваш компонент, вы можете указать метод shouldRender
в классе вашего компонента. Если результат метода shouldRender
равен false
, то компонент не будет отображаться:
Передача данных компонентам
Вы можете передавать данные в компоненты Blade, используя атрибуты HTML. Жестко запрограммированные примитивные значения могут быть переданы компоненту с помощью простых строк атрибутов HTML. Выражения и переменные PHP следует передавать компоненту через атрибуты, которые используют символ :
в качестве префикса:
Вы должны определить необходимые данные компонента в его конструкторе класса. Все общедоступные свойства компонента будут автоматически доступны в шаблоне компонента. Нет необходимости передавать данные в шаблон из метода 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');
}
}
Когда ваш компонент визуализируется, вы можете отображать содержимое общедоступных переменных вашего компонента, выводя переменные по имени:
Именование
Аргументы конструктора компонентов следует указывать с помощью camelCase
, а при обращении к именам аргументов в ваших атрибутах HTML следует использовать kebab-case
. Например, учитывая следующий конструктор компонента:
Аргумент $alertType
может быть передан компоненту следующим образом:
Сокращенный синтаксис атрибутов
При передаче атрибутов компонентам вы также можете использовать “сокращенный синтаксис атрибутов”. Это удобно, поскольку имена атрибутов часто совпадают с именами переменных, к которым они относятся:
Методы компонента
В дополнение к общедоступным переменным, доступным для вашего шаблона компонента, могут быть вызваны любые общедоступные методы компонента. Например, представьте компонент, у которого есть метод isSelected
:
/**
* Определить, является ли переданная опция выбранной.
*/
public function isSelected(string $option): bool
{
return $option === $this->selected;
}
Вы можете выполнить этот метод из своего шаблона компонента, вызвав переменную, соответствующую имени метода:
Атрибуты компонента
Мы уже рассмотрели, как передавать атрибуты данных в компонент; иногда требуется указать дополнительные атрибуты HTML, такие, как class
, которые не являются частью данных, необходимых для функционирования компонента. Как правило, вы хотите передать эти дополнительные атрибуты корневому элементу шаблона компонента. Например, представьте, что мы хотим отобразить компонент alert
следующим образом:
Все атрибуты, которые не являются частью конструктора компонента, будут автоматически добавлены в «коллекцию атрибутов» компонента. Эта коллекция атрибутов автоматически становится доступной для компонента через переменную $attributes
. Все атрибуты могут отображаться в компоненте путем вывода этой переменной:
Слоты
Описание
Вам часто потребуется передавать дополнительный контент вашему компоненту через «слоты». Слоты компонентов отображаются путем вывода переменной $slot
. Чтобы изучить эту концепцию, представим, что компонент alert
имеет следующую разметку:
<!-- /resources/views/components/alert.blade.php -->
<div class="alert alert-danger">
{{ $slot }}
</div>
Мы можем передавать контент в slot
, вставив контент в компонент:
Иногда компоненту может потребоваться отрисовать несколько разных слотов в разных местах внутри компонента. Давайте модифицируем наш компонент оповещения, чтобы учесть вставку слота 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-комментарием:
Атрибуты слотов
Как и в компонентах 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
переменной слота.
Создание макетов
Макеты с использованием компонентов
Большинство веб-приложений поддерживают одинаковый общий макет на разных страницах. Было бы невероятно громоздко и сложно поддерживать наше приложение, если бы нам приходилось повторять весь HTML-макет в каждом создаваемом экране. К счастью, этот макет удобно определить как один компонент Blade, а затем использовать его во всем приложении.
Определение компонента макета
Например, представьте, что мы создаем приложение со списком задач. Мы могли бы определить компонент layout
, который выглядит следующим образом:
Использование компонента макета
Как только компонент 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
из маршрута: