Laravel. Шаблонизатор Blade. Часть 2 из 2
06.11.2020
Теги: Laravel • PHP • Web-разработка • Компонент • Теория • Фреймворк • Шаблонизатор • ШаблонСайта
Использование стеков
Стек — это заглушка где-то в родительском шаблоне. А дочерние шаблоны могут вставлять свое содержимое на место этой заглушки. Причем старое содержимое при этом не теряется, а новое содержимое вставляется перед старым или после него. Это очень похоже на php-функции array_push()
и array_unshift()
, которые добавляют элемент в конец или начало массива.
Создадим родительский шаблон (общий шаблон сайта):
<!-- resources/views/layouts/site.blade.php --> <html> <head> <title>@yield('title', 'Заголовок по умолчанию')</title> <link rel="stylesheet" href="/css/common.css"> <!-- заглушка, куда будет помещен контент из стека --> @stack('styles') </head> <body> <div id="content"> @yield('content') </div> </body> </html>
Создадим дочерний шаблон (каталог товаров):
<!-- resources/views/catalog/index.blade.php --> @extends('layouts.site') @section('title', 'Каталог товаров') @push('styles') <!-- добавляем что-нибудь в конец стека --> <link rel="stylesheet" href="/css/append.css"> @endpush @section('content') <p>Это контент каталога товаров.</p> @endsection
Создадим дочерний-дочерний шаблон (товар каталога):
<!-- resources/views/catalog/product.blade.php --> @extends('catalog.index') @section('title', 'Товар каталога') @prepend('styles') <!-- добавляем что-нибудь в начало стека --> <link rel="stylesheet" href="/css/prepend.css"> @endprepend @section('content') <p>Это контент товара каталога.</p> @endsection
В результате страница товара каталога будет иметь вид:
<html> <head> <title>Товар каталога</title> <link rel="stylesheet" href="/css/common.css"> <!-- заглушка, куда будет помещен контент из стека --> <link rel="stylesheet" href="/css/prepend.css"> <link rel="stylesheet" href="/css/append.css"> </head> <body> <div id="content"> <p>Это контент товара каталога.</p> </div> </body> </html>
Компоненты и слоты
Допустим, нам надо показывать пользователю сообщение об ошибке. Обертку для сообщения мы выносим в отдельный шаблон:
<!-- resources/views/parts/alert.blade.php --> <div class="alert alert-danger alert-dismissible" role="alert"> <button type="button" class="close" data-dismiss="alert" aria-label="Закрыть"> <span aria-hidden="true">×</span> </button> {{ $message }} </div>
Теперь можем где-то в другом шаблоне показать сообщение об ошибке:
<!-- какой-то другой шаблон, где надо показать сообщение об ошибке --> @include('parts.alert', ['message' => '<p>Lorem ipsum ... voluptatibus.</p><p>Blanditiis consequatur ... repellendus.</p>'])
Сообщение об ошибке может быть большим, оно может быть отформатировано с помощью тегов <p>
, <ul>
, <strong>
. И все это приходится передавать в шаблон parts.alert
одной строкой. Давайте посмотрим, как можно сделать то же самое с помощью компонента и слота:
<!-- resources/views/parts/alert.blade.php --> <div class="alert alert-danger alert-dismissible" role="alert"> <button type="button" class="close" data-dismiss="alert" aria-label="Закрыть"> <span aria-hidden="true">×</span> </button> {{ $slot }} </div>
Теперь можем где-то в другом шаблоне показать сообщение об ошибке:
<!-- какой-то другой шаблон, где надо показать сообщение об ошибке --> @component('parts.alert') <p> Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis consequatur cumque delectus dolorem itaque obcaecati provident, qui quibusdam repellendus saepe. <p> <p> Lorem ipsum dolor sit amet, consectetur adipisicing elit. Adipisci aut dolore ea illum impedit molestias nostrum omnis quasi repellat veritatis. </p> @endcomponent
Все, что находится между @component
и @endcomponent
, передается в шаблон parts.alert
и доступно в переменной $slot
.
Составные слоты
Можно передать в шаблон parts.alert
не одну переменную, а две — $heading
и $slot
:
<!-- resources/views/parts/alert.blade.php --> <div class="alert alert-danger alert-dismissible" role="alert"> <button type="button" class="close" data-dismiss="alert" aria-label="Закрыть"> <span aria-hidden="true">×</span> </button> <h4 class="alert-heading">{{ $heading }}</h4> {{ $slot }} </div>
<!-- какой-то другой шаблон, где надо показать сообщение об ошибке --> @component('parts.alert') @slot('heading') Alert! Something wrong! @endslot <p> Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis consequatur cumque delectus dolorem itaque obcaecati provident, qui quibusdam repellendus saepe. <p> <p> Lorem ipsum dolor sit amet, consectetur adipisicing elit. Adipisci aut dolore ea illum impedit molestias nostrum omnis quasi repellat veritatis. </p> @endcomponent
Или даже три — $heading
, $slot
и $class
:
<!-- resources/views/parts/alert.blade.php --> <div class="alert alert-{{ $class }} alert-dismissible" role="alert"> <button type="button" class="close" data-dismiss="alert" aria-label="Закрыть"> <span aria-hidden="true">×</span> </button> <h4 class="alert-heading">{{ $heading }}</h4> {{ $slot }} </div>
<!-- какой-то другой шаблон, где надо показать сообщение об ошибке --> @component('parts.alert', ['class' => 'danger']) @slot('heading') Alert! Something wrong! @endslot <p> Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis consequatur cumque delectus dolorem itaque obcaecati provident, qui quibusdam repellendus saepe. <p> <p> Lorem ipsum dolor sit amet, consectetur adipisicing elit. Adipisci aut dolore ea illum impedit molestias nostrum omnis quasi repellat veritatis. </p> @endcomponent
Создание своих директив
Какова цель создания собственных директив? Во-первых — чтобы не загружать шаблон логическими конструкциями, во-вторых — чтобы избежать дублирования кода. Например, в личном кабинете у администратора сайта должны быть кнопки редактирования и удаления, а у обычного пользователя сайта таких кнопок быть не должно. Сперва надо проверить, что пользователь аутентифицирован, а потом — что он является администратором.
Директивы создаются в методе boot()
класса AppServiceProvider
и определяются следующим образом:
namespace App\Providers; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider { /** * Register any application services. * * @return void */ public function register() { // ... } /** * Bootstrap any application services. * * @return void */ public function boot() { Blade::directive('some_name', function($expression) { // какой-то код return $something; }); } }
1. Директива «Иконка»
Если на сайте используются иконки Font Awesome, то в шаблонах много кода типа:
<i class="fas fa-shopping-basket"></i> <!-- корзина покупателя --> <i class="fas fa-arrow-circle-right"></i> <!-- стрелка вправо --> <i class="fas fa-download"></i> <!-- скачать файл -->
Давайте создадим директиву @icon()
, чтобы вставлять иконки по имени:
class AppServiceProvider extends ServiceProvider { public function boot() { Blade::directive('icon', function($expression) { $name = str_replace("'", '', $expression); return '<i class="fas fa-' . $name . '"></i>'; }); } }
Теперь можем вставлять иконки с использованием новой директивы:
<p>Цена: {{ number_format($product->price, 2, '.', '') }}</p> <!-- Форма для добавления товара в корзину --> <form action="{{ route('basket.add', ['id' => $product->id]) }}" method="post"> @csrf <button type="submit" class="btn btn-success"> @icon('shopping-basket') Добавить в корзину </button> </form>
2. Директива «Цена»
В примере выше используется функция number_format()
для форматирования цены. Давайте создадим директиву @price()
:
class AppServiceProvider extends ServiceProvider { public function boot() { Blade::directive('icon', function($expression) { $name = str_replace("'", '', $expression); return '<i class="fas fa-' . $name . '"></i>'; }); Blade::directive('price', function($expression) { return "<?php echo number_format($expression, 2, '.', ''); ?>"; }); } }
Теперь можно форматировать цену с использованием новой директивы:
<p>Цена: @price($product->price)</p> <!-- Форма для добавления товара в корзину --> <form action="{{ route('basket.add', ['id' => $product->id]) }}" method="post"> @csrf <button type="submit" class="btn btn-success"> @icon('shopping-basket') Добавить в корзину </button> </form>
3. Директива «Админ»
Директива @admin
, которая проверяет, что пользователь аутентифицирован и является администратором:
class AppServiceProvider extends ServiceProvider { public function boot() { Blade::directive('icon', function($expression) { $name = str_replace("'", '', $expression); return '<i class="fas fa-' . $name . '"></i>'; }); Blade::directive('price', function($expression) { return "<?php echo number_format($expression, 2, '.', ''); ?>"; }); Blade::if('admin', function() { return auth()->check() && auth()->user()->admin; }); } }
@admin <p>Это администратор сайта</p> @else <p>Это обычный пользователь</p> @endadmin
Внедрение сервисов
Директива @inject()
служит для извлечения сервиса из сервис-контейнера. Первый аргумент — это имя переменной, в которую будет помещён сервис. А второй — имя класса или интерфейса сервиса, который нужно извлечь.
@inject('metrics', 'App\Services\MetricsService') <p>Месячный доход: {{ $metrics->monthlyRevenue() }}</p>
- Laravel. Шаблонизатор Blade. Часть 1 из 2
- Laravel. Использование View Composers
- Мини-блог на Laravel, часть 3. Постраничная навигация, layout-шаблон и поиск по блогу
- Блог на Laravel 7, часть 12. Доп.страницы сайта в панели управления и в публичной части
- Блог на Laravel 7, часть 11. Панель управления — назначение ролей и прав для пользователей
- Блог на Laravel 7, часть 10. Личный кабинет — CRUD-операции над постами и комментариями
- Блог на Laravel 7, часть 9. Панель управления — создание, публикация, удаление комментариев
Поиск: Laravel • PHP • Web-разработка • Теория • Фреймворк • Шаблонизатор • Шаблон сайта • Blade • Стек • Слот • Компонент