Шаблонизатор Blade
Содержание:
Введение
Blade — это простой но мощный шаблонизатор, который постовляется вместе с Ларавел.
В отличие от других популряных шаблонизаторов, Блейд не запрещает вам использовать
стандартный PHP код в ваших представлениях. По факту, все представления Blade собираются
в код PHP, кэшируются и хранятся до момента модификации. Это означает, что фактически Blade
не создаёт дополнительный слой нагрузки на ваше приложение. Файлы Blade используют расширение
.blade.php
и обычно хранятся в папке resources/views
.
Наследование шаблонов
Определение шаблона
Есть два главных преимущества использования Blade: наследование шаблонов и секции.
Чтобы начать, давайте взглянем на простой пример. Во-первых, мы проверим шаблон
страницы "master". В силу того, что большое количество приложений имеют один главный
шаблон для большого числа страниц, это удобно определить этот шаблон как
одиночно представление Blade:
<!-- Stored in 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
:
<!-- Stored in 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
,
чтобы дополнить содержание в sidebar шаблон. Директива @parent
будет
земенена содержимым шаблона при загрузке представления.
В противоположность к предыдущему примере, секция sidebar
завершается
директивой @endsection
вместо @show
.
@endsection
только определит секцию, в то время как
@show
определит и мгновенно дополнит секцию.
Директива @yield
также принимает базовое значение как второй параметр.
Значение будет отображаться, если секция для заполнения не определена:
@yield('content', View::make('view.name'))
Представления Blade могут быть возвращены из маршрутов используя глобальный
помощник view
:
Route::get('blade', function () {
return view('child');
});
Компоненты и слоты
Компоненты и слоты дают похожие преимущества для секций и шаблонов;
Однако, некоторые находят компоненты и слоты боле понятными.
Для начала, представим многократно используемый компонет тревоги для
всего приложения:
<!-- /resources/views/alert.blade.php -->
<div class="alert alert-danger">
{{ $slot }}
</div>
Переменная {{ $slot }}
будет включать
содержимое, которое мы бы хотели внедрить в компонент. Теперь, чтобы
построить компонент, мы можем использовать директиву Blade
@component
:
@component('alert')
<strong>Whoops!</strong> Something went wrong!
@endcomponent
Чтобы научить Laravel загружать первое представление, которое существует из
возможных представлений, вы можете использовать директиву componentFirst
:
@componentFirst(['custom.alert', 'alert'])
<strong>Whoops!</strong> Something went wrong!
@endcomponent
Иногда бывает полезно определить несоклько слотов для компонента. Давайте модифицируем
наш компонент тревоги чтобы позволить внедрять заголовок. Именованные слоты могут
отображаться путём "вывода" переменной, которая соответствует их имени:
<!-- /resources/views/alert.blade.php -->
<div class="alert alert-danger">
<div class="alert-title">{{ $title }}</div>
{{ $slot }}
</div>
Теперь мы можем внедрять контент в именованные слоты используя
директиву @slot
. Любой контент не входящий в директиву @slot
будет передан в компонент в переменную $slot
:
@component('alert')
@slot('title')
Forbidden
@endslot
You are not allowed to access this resource!
@endcomponent
Передача дополнительных данных в комопоненты
Иногда может потребоваться передать дополнитлеьный данные в компонент.
По этой причине, вы можете передать массив данных как второй аргумент в
директиву @component
. Все данные будут доступны в шаблоне
компонента как переменные:
@component('alert', ['foo' => 'bar'])
...
@endcomponent
Присвоение псевдонимов компонентам
Если ваши компоненты Blade хранятся во внутренних папках, вы можете присвоить псевдоним
для более легкого доступа. Например, представьте компонет Blade, который располагается
в resources/views/components/alert.blade.php
. Вы можете использовать метод
component
, чтобы обращаться к компоненту не components.alert
,
а просто alert
. Обычно, это можно будет сделать в методе boot
вашего AppServiceProvider
:
use Illuminate\Support\Facades\Blade;
Blade::component('components.alert', 'alert');
После присвоения псевдонима компоненту, к нему можно обрращаться напрямую:
@alert(['type' => 'danger'])
You are not allowed to access this resource!
@endalert
Вы можете опустить параметры компонента, если у компонента нет дополнительных слотов:
@alert
You are not allowed to access this resource!
@endalert
Отображение данных
Вы можете отображать данные, которые вы передали в представление Blade, путём заключения в скобки.
Например, для данного маршрута:
Route::get('greeting', function () {
return view('welcome', ['name' => 'Samantha']);
});
Вы можете отобразить содержание переменной name
таким образом:
Hello, {{ $name }}.
Выражения Blade {{ }}
автоматически
пропускаются через функцию PHP htmlspecialchars
, чтобы
предотвратить XSS атаки.
Вы можете показывать любой контент, а не только те переменные, что были переданы
в представление. Вы можете также вывести результаты любой функции PHP. По факту,
вы можете положить любой код PHP, который вы хотите, во внутрь выражения Blade:
The current UNIX timestamp is {{ time() }}.
Отображение нэкранированных данных
По умолчанию, синтаксис Blade {{ }}
автоматически
проводит через функцию PHP htmlspecialchars
, чтобы предотвратить XSS атаку.
Если вы не хотите, чтобы часть данных могли пропасть, вы можете использовать следующий
синтаксис:
Hello, {!! $name !!}}.
Будьте осторожны при выводе контента пользователями вашего приложения.
Двойные скобки надёжно защищают от XSS атак.
Отображение JSON
Иногда вы можете передавать массив в ваше представление с намерением отразить
его как JSON для инициализации переменной JavaScript. Например:
<script>
var app = <?php echo json_encode($array); ?>;
</script>
Вы можете использовать директиву Blade @json
вместо
функции PHP json_encode
. Директива @json
принимает такие же аргументы, как и функция PHP json_encode
:
<script>
var app = @json($array);
var app = @json($array, JSON_PRETTY_PRINT);
</script>
Вы должны использовать директиву @json
для отображения
существующих перевенных как JSON. Шаблонизатор Blade основывается на регулярных
выражениях и попытка передать сложное выражение в директиву может привести к неудаче.
Директива @json
также полезна для наполнения компонентов
Javascript фреймворка Vue или атрибутов data-*
:
<example-component :some-prop='@json($array)'></example-component>
Использование @json
в атрибутах элемента предполагает,
чтобы он был окружен в одинарных кавычках.
Кодировка элементов HTML
По умолчанию, Blade проводит двойную кодировку элементов HTML. Еслиы вы хотите отменить
двойное кодирование, вызовите метод Blade::withoutDoubleEncoding
из метода
boot
вашего AppServiceProvider
:
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
Blade::withoutDoubleEncoding();
}
}
Blade и JavaScript фреймворки
В силу того, что фрейворки JavaScript также используют двойные скобки в своих целях,
мы можем использовать символ @
, чтобы проинформировать движок Blade,
что данных элемент необходимо оставить нетронутым. Например:
<h1>Laravel</h1>
Hello, @{{ name }}.
В этом примере, символ @
будет убран Blade. Однако, выражение
{{ name }}
не будет тронутов Blade, что позволяет
выражению быть обработанным фреймворком JavaScript.
Директива @verbatim
Если вы показываете переменные JavaScript в значительной части вашего шаблона,
вы можете обернуть HTML в директиву @verbatim
, поэтому вам
не придётся прописывать символ @
для каждого вывода:
@verbatim
<div class="container">
Hello, {{ name }}.
</div>
@endverbatim
Структуры контроля
В дополнение к функциям отображения данных, Blade также даёт удобные инструменты
для типовых структур котнтроля 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
:
@unless (Auth::check())
You are not signed in.
@endunless
В дополнение к директивам условий, которые мы только что обсудили, есть директивы
@isset
и @empty
, которые выполняют те же функции,
что одноимённые аналоги PHP функций:
@isset($records)
// $records is defined and is not null...
@endisset
@empty($records)
// $records is "empty"...
@endempty
Директивы аутентификации
Директивы @auth
и @guest
могут быть использованы
для быстрого определения, что текущий пользователь аутенцифицирован или является
гостем:
@auth
// The user is authenticated...
@endauth
@guest
// The user is not authenticated...
@endguest
Если необходимо, вы можете указать защитника аутентификации
который должен быть выбран при использовании директив @auth
и @guest
:
@auth('admin')
// The user is authenticated...
@endauth
@guest('admin')
// The user is not authenticated...
@endguest
Директивы секции
Вы можете проверить, что у секции есть контент, используя директиву
@hasSection
:
@hasSection('navigation')
<div class="pull-right">
@yield('navigation')
</div>
<div class="clearfix"></div>
@endif
Оператор Switch
Выражения Switch могут быть сконструированы используя директивы
@switch
, @case
, @break
,
@default
и @endswitch
:
@switch($i)
@case(1)
First case...
@break
@case(2)
Second case...
@break
@default
Default case...
@endswitch
Циклы
В дополнение к выражениям условий, Blade предоставляет простые директивы
для работы с циклами. Опять же, для каждой директивы Blade есть аналог
функции 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 ($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
Переменные цикла
При использовании цикла, переменная $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 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 |
Для вложенных циклов, переменная родительского цикла. |
Blade также позволяет делать комментарии для ваших представлений. Но в отличие от
HTML комментариев, комментарии Blade не включаются в отдаваемый приложением HTML:
{{-- This comment will not be present in the rendered HTML --}}
Чистый PHP
В некоторых ситуациях, бывает полезно внедрить чистый PHP код в ваше представление.
Вы можете использовать директиву Blade @php
, чтобы выполнить блок
кода PHP внутри шаблона:
@php
//
@endphp
Blade предоставляет возможность использовать PHP код напрямую. Но слишком частое
использование может свидетельствовать о том, что вы слишком много логики
передаёте в шаблон.
Вставка других представлений
Директива Blade @include
позволяет вам включать в представление другое представления.
Все переменные, доступные для родительского представления,
будут доступны и для включаемого представления:
<div>
@include('shared.errors')
<form>
<!-- Form Contents -->
</form>
</div>
Кроме данных родителя, доступных дочернему представлению, вы также можете передать
дополнительные данные во включаемое представление:
@include('view.name', ['some' => 'data'])
Если вы пытаетесь включить представление, которое не существует, Ларавел выкинет ошибку.
Если вы хотите включить представление, которого может не быть, вам следует использовать
директиву @includeIf
:
@includeIf('view.name', ['some' => 'data'])
Если вы хотите включить представление в зависимости от данных логических условий,
вы можете использовать директиву @includeWhen
:
@includeWhen($boolean, 'view.name', ['some' => 'data'])
Чтобы включить первое представление, которое существует из представленного массива,
вы можете использовать директиву includeFirst
:
@includeFirst(['custom.admin', 'admin'], ['some' => 'data'])
Вам стоит избегать использования __DIR__
и __FILE__
в вашем
представлении Blade, в силу того, что они будет ссылаться на закешированное,
собранное представление.
Присвоение псевдонимов включениям
Если вы включаете в шаблон представления во вложенных папках, вы можете назвать их для
более лёгкого доступа. Например, представьте, что вы включаете представление по адресу
resources/views/includes/input.blade.php
со следующим содержимым:
<input type="{{ $type ?? 'text' }}">
Вы можете использовать метод include
, чтобы представлению includes.input
дать имя input
. Обычно, это нужно сделать в методе boot
вашего
AppServiceProvider
:
use Illuminate\Support\Facades\Blade;
Blade::include('includes.input', 'input');
После присвоения имени включения, вы можете отобразить его используя имя директивы:
@input(['type' => 'email'])
Генерация предствлений для контроллера
Вы можете совместить циклы и включения в одной линии с помощью директивы Blade
@each
:
@each('view.name', $jobs, 'job')
Первый аргумент — это часть представления для визуализации каждого элемента в массиве
или коллекции. Второй аргумент — это массив или коллекция вы хотите перебрать.
Третий аргумент — это имя переменной, которая будет приписана к текущей итерации
внутри представления. Например, если вы проводите итерацию массива jobs
,
скорее всего вы захотите получить доступ к профессии как переменная job
внутри части представления. Ключ текущей итерации будет доступен как переменаня
key
внутри вашей части представления.
Вы также можете передать 4-ый аргумент в директиву @each
.
Этот аргумент определит представление, которое будет отображаться, при условии
пустого массива.
@each('view.name', $jobs, 'job', 'view.empty')
Представления отображённые через @each
не наследуют переменные
из родительского представления. Если дочернее представление требует эти
переменные, вы должны использовать @foreach
или
@include
.
Стэки
Blade позволяет вам проталкивать именованные стэки, которые могут быть отображены
в другом представлении или шаблоне. Это может быть частично полезне для указания
любой JavaScript библиотеки необходимой представлению:
@push('scripts')
<script src="/example.js"></script>
@endpush
Вы можете проталкивать стэки так часто, как это необходимо. Чтобы отобразить
полное содержание стэка, передайте имя стэка в директиву @stack
:
<head>
<!-- Head Contents -->
@stack('scripts')
</head>
Если вы хотите вписать контент в начало стэка, вы должны использовать
директиву @prepend
:
@push('scripts')
This will be second...
@endpush
// Later...
@prepend('scripts')
This will be first...
@endprepend
Сервис инъекция
Директива @inject
может быть использована, чтобы извлечь сервис из
сервис контейнера. Первый аргумент, передаваемый в @inject
, это имя
переменной, куда поставится сервис, а второй аргумент это класс или имя интерфейса,
который вы желаете получить:
@inject('metrics', 'App\Services\MetricsService')
<div>
Monthly Revenue: {{ $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
{
/**
* Register bindings in the container.
*
* @return void
*/
public function register()
{
//
}
/**
* Bootstrap any application services.
*
* @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'); ?>
После обновления догики директивы Blade. вам необходимо удалить все
закешированные представления Blade. Это можно сделать используя команду
Artisan view:clear
.
Пользовательские If-операторы
Программирование пользовательских директив может быть более сложным, чем необходимо,
для определения простых выражений состоящих из условий. По этой причине, Blade предоставляет
метод Blade::if
, который позволяет вам быстро определить пользовательские
условные директивы используя замыкание. Например, давайте определим условие, которое проверяет
текущее окружение приложения. Мы можем сделать это используя метод boot
вашего AppServiceProvider
:
use Illuminate\Support\Facades\Blade;
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
Blade::if('env', function ($environment) {
return app()->environment($environment);
});
}
Пссле определения пользовательского условия,
мы можем легко использовать его в наших шаблонах:
@env('local')
// The application is in the local environment...
@elseenv('testing')
// The application is in the testing environment...
@else
// The application is not in the local or testing environment...
@endenv