Skip to content
全部文档

Blade 模板

简介

Blade 是 Laravel 中包含的简单但功能强大的模板引擎。与某些 PHP 模板引擎不同,Blade 并不限制你在模板中使用纯 PHP 代码。事实上,所有 Blade 模板都被编译为纯 PHP 代码并缓存,直到它们被修改,这意味着 Blade 基本上为你的应用程序增加了零开销。Blade 模板文件使用 .blade.php 文件扩展名,通常存储在 resources/views 目录中。

Blade 视图可以使用全局 view 辅助函数从路由或控制器返回。当然,正如 视图 的文档中提到的,可以使用 view 辅助函数的第二个参数将数据传递到 Blade 视图:

php
Route::get('/', function () {
    return view('greeting', ['name' => 'Finn']);
});

用 Livewire 增强 Blade

想要将你的 Blade 模板提升到一个新的水平并轻松构建动态界面吗?查看 Laravel Livewire。 Livewire 允许你编写通过动态功能增强的 Blade 组件,这些功能通常只能通过 React、Svelte 或 Vue 等前端框架实现,从而提供了一种构建现代反应式前端的好方法,而无需复杂性、客户端渲染或许多 JavaScript 框架的构建步骤。

显示数据

你可以通过将变量括在花括号中来显示传递到 Blade 视图的数据。例如,给定以下路由:

php
Route::get('/', function () {
    return view('welcome', ['name' => 'Samantha']);
});

你可以像这样显示 name 变量的内容:

blade
Hello, {{ $name }}.

INFO

Blade 的 {{ }} echo 语句会通过 PHP 的 htmlspecialchars 函数自动发送,以防止 XSS 攻击。

你不限于显示传递给视图的变量的内容。你还可以回显任何 PHP 函数的结果。事实上,你可以将任何你想要的 PHP 代码放入 Blade echo 语句中:

blade
The current UNIX timestamp is {{ time() }}.

HTML 实体编码

默认情况下,Blade(和 Laravel e 函数)将对 HTML 实体进行双重编码。如果你想禁用双重编码,请从 AppServiceProviderboot 方法中调用 Blade::withoutDoubleEncoding 方法:

php
<?php

namespace App\Providers;

use Illuminate\Support\Facades\Blade;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     */
    public function boot(): void
    {
        Blade::withoutDoubleEncoding();
    }
}

显示未转义数据

默认情况下,Blade &#123;&#123; &#125;&#125; 语句会通过 PHP 的 htmlspecialchars 函数自动发送,以防止 XSS 攻击。如果你不希望数据被转义,可以使用以下语法:

blade
Hello, {!! $name !!}.

WARNING

回显应用程序用户提供的内容时要非常小心。在显示用户提供的数据时,你通常应该使用转义的双大括号语法来防止 XSS 攻击。

Blade 与 JavaScript 框架

由于许多 JavaScript 框架还使用「大括号」来指示应在浏览器中显示给定的表达式,因此你可以使用 @ 符号来通知 Blade 渲染引擎表达式应保持不变。例如:

blade
<h1>Laravel</h1>

Hello, @{{ name }}.

在本例中,@符号将被Blade移除;但是,Blade 引擎不会影响 &#123;&#123; name &#125;&#125; 表达式,从而允许你的 JavaScript 框架渲染它。

@ 符号也可用于转义 Blade 指令:

blade
{{-- Blade template --}}
@@if()

<!-- HTML output -->
@if()

渲染 JSON

有时,你可能会将数组传递到视图,以便将其呈现为 JSON,以便初始化 JavaScript 变量。例如:

php
<script>
    var app = <?php echo json_encode($array); ?>;
</script>

但是,你可以使用 Illuminate\Support\Js::from 方法,而不是手动调用 json_encodefrom 方法接受与 PHP 的 json_encode 函数相同的参数;但是,它将确保生成的 JSON 已正确转义以包含在 HTML 引号中。 from 方法将返回一个字符串 JSON.parse JavaScript 语句,该语句将给定的对象或数组转换为有效的 JavaScript 对象:

blade
<script>
    var app = {{ Illuminate\Support\Js::from($array) }};
</script>

Laravel 应用程序框架的最新版本包括一个 Js 外观,它可以在 Blade 模板中方便地访问此功能:

blade
<script>
    var app = {{ Js::from($array) }};
</script>

WARNING

你应该仅使用 Js::from 方法将现有变量呈现为 JSON。 Blade 模板基于正则表达式,尝试将复杂表达式传递给指令可能会导致意外失败。

@verbatim 指令

如果你在模板的大部分内容中显示 JavaScript 变量,则可以将 HTML 包装在 @verbatim 指令中,这样你就不必在每个 Blade echo 语句前添加 @ 符号:

blade
@verbatim
    <div class="container">
        Hello, {{ name }}.
    </div>
@endverbatim

Blade 指令

除了模板继承和显示数据之外,Blade还为常见的PHP控制结构(例如条件语句和循环)提供了方便的快捷方式。这些快捷方式提供了一种非常干净、简洁的 PHP 控制结构使用方式,同时也让 PHP 同行保持熟悉。

If 语句

你可以使用 @if@elseif@else@endif 指令构造 if 语句。这些指令的功能与其 PHP 对应指令相同:

blade
@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 指令:

blade
@unless (Auth::check())
    You are not signed in.
@endunless

除了已经讨论过的条件指令之外,@isset@empty指令还可以用作各自 PHP 函数的便捷快捷方式:

blade
@isset($records)
    // $records is defined and is not null...
@endisset

@empty($records)
    // $records is "empty"...
@endempty

认证指令

@auth@guest指令可用于快速确定当前用户是已验证还是访客:

blade
@auth
    // The user is authenticated...
@endauth

@guest
    // The user is not authenticated...
@endguest

如果需要,你可以指定使用 @auth@guest 指令时应检查的身份验证防护:

blade
@auth('admin')
    // The user is authenticated...
@endauth

@guest('admin')
    // The user is not authenticated...
@endguest

环境指令

你可以使用 @production 指令检查应用程序是否在生产环境中运行:

blade
@production
    // Production specific content...
@endproduction

或者,你可以使用 @env 指令确定应用程序是否在特定环境中运行:

blade
@env('staging')
    // The application is running in "staging"...
@endenv

@env(['staging', 'production'])
    // The application is running in "staging" or "production"...
@endenv

区块指令

你可以使用 @hasSection 指令确定模板继承部分是否包含内容:

blade
@hasSection('navigation')
    <div class="pull-right">
        @yield('navigation')
    </div>

    <div class="clearfix"></div>
@endif

你可以使用 sectionMissing 指令来确定某个部分是否没有内容:

blade
@sectionMissing('navigation')
    <div class="pull-right">
        @include('default-navigation')
    </div>
@endif

会话指令

@session指令可用于确定会话值是否存在。如果会话值存在,则将评估 @session@endsession 指令中的模板内容。在 @session 指令的内容中,你可以回显 $value 变量来显示会话值:

blade
@session('status')
    <div class="p-4 bg-green-100">
        {{ $value }}
    </div>
@endsession

上下文指令

@context指令可用于确定上下文值是否存在。如果上下文值存在,则将评估 @context@endcontext 指令中的模板内容。在 @context 指令的内容中,你可以回显 $value 变量以显示上下文值:

blade
@context('canonical')
    <link href="{{ $value }}" rel="canonical">
@endcontext

Switch 语句

Switch 语句可以使用 @switch@case@break@default@endswitch 指令构造:

blade
@switch($i)
    @case(1)
        First case...
        @break

    @case(2)
        Second case...
        @break

    @default
        Default case...
@endswitch

循环

除了条件语句之外,Blade 还提供了用于处理 PHP 循环结构的简单指令。同样,这些指令的功能与它们的 PHP 对应指令相同:

blade
@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

INFO

在迭代 foreach 循环时,你可以使用 loop 变量 来获取有关循环的有价值的信息,例如你是否处于循环的第一次或最后一次迭代。

使用循环时,你还可以使用 @continue@break 指令跳过当前迭代或结束循环:

blade
@foreach ($users as $user)
    @if ($user->type == 1)
        @continue
    @endif

    <li>{{ $user->name }}</li>

    @if ($user->number == 5)
        @break
    @endif
@endforeach

你还可以在指令声明中包含继续或中断条件:

blade
@foreach ($users as $user)
    @continue($user->type == 1)

    <li>{{ $user->name }}</li>

    @break($user->number == 5)
@endforeach

循环变量

当迭代 foreach 循环时,$loop 变量将在循环内可用。该变量提供对一些有用信息的访问,例如当前循环索引以及这是循环的第一次还是最后一次迭代:

blade
@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

如果处于嵌套循环中,则可以通过 parent 属性访问父循环的 $loop 变量:

blade
@foreach ($users as $user)
    @foreach ($user->posts as $post)
        @if ($loop->parent->first)
            This is the first iteration of the parent loop.
        @endif
    @endforeach
@endforeach

$loop变量还包含各种其他有用的属性:

财产描述
$loop->index当前循环迭代的索引(从 0 开始)。
$loop->iteration当前循环迭代(从 1 开始)。
$loop->remaining循环中剩余的迭代。
$loop->count正在迭代的数组中的项目总数。
$loop->first这是否是循环的第一次迭代。
$loop->last这是否是循环的最后一次迭代。
$loop->even这是否是循环的偶数迭代。
$loop->odd这是否是循环中的奇数迭代。
$loop->depth当前循环的嵌套级别。
$loop->parent当处于嵌套循环中时,父循环变量。

条件类名与样式

@class指令有条件地编译CSS类字符串。该指令接受一个类数组,其中数组键包含你要添加的一个或多个类,而值是一个布尔表达式。如果数组元素有数字键,它将始终包含在渲染的类列表中:

blade
@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元素:

blade
@php
    $isActive = true;
@endphp

<span @style([
    'background-color: red',
    'font-weight: bold' => $isActive,
])></span>

<span style="background-color: red; font-weight: bold;"></span>

附加属性

为了方便起见,你可以使用 @checked 指令轻松指示给定的 HTML 复选框输入是否已「选中」。如果提供的条件计算结果为 true,则该指令将回显 checked

blade
<input
    type="checkbox"
    name="active"
    value="active"
    @checked(old('active', $user->active))
/>

同样,@selected指令可用于指示是否应「选择」给定的选择选项:

blade
<select name="version">
    @foreach ($product->versions as $version)
        <option value="{{ $version }}" @selected(old('version') == $version)>
            {{ $version }}
        </option>
    @endforeach
</select>

此外,@disabled指令可用于指示是否应「禁用」给定元素:

blade
<button type="submit" @disabled($errors->isNotEmpty())>Submit</button>

此外,@readonly指令可用于指示给定元素是否应该是「只读」:

blade
<input
    type="email"
    name="email"
    value="email@laravel.com"
    @readonly($user->isNotAdmin())
/>

此外,@required指令可用于指示给定元素是否应该是「必需的」:

blade
<input
    type="text"
    name="title"
    value="title"
    @required($user->isAdmin())
/>

包含子视图

INFO

虽然你可以自由使用 @include 指令,但 Blade 组件 提供类似的功能,并且比 @include 指令具有多种优势,例如数据和属性绑定。

Blade 的 @include 指令允许你从另一个视图中包含 Blade 视图。父视图可用的所有变量都将可供包含的视图使用:

blade
<div>
    @include('shared.errors')

    <form>
        <!-- Form Contents -->
    </form>
</div>

即使包含的视图将继承父视图中可用的所有数据,你也可以传递应可供包含的视图使用的附加数据数组:

blade
@include('view.name', ['status' => 'complete'])

如果你尝试 @include 一个不存在的视图,Laravel 将抛出错误。如果你想包含可能存在或不存在的视图,则应使用 @includeIf 指令:

blade
@includeIf('view.name', ['status' => 'complete'])

如果你想在给定布尔表达式计算结果为 truefalse 时查看 @include 视图,你可以使用 @includeWhen@includeUnless 指令:

blade
@includeWhen($boolean, 'view.name', ['status' => 'complete'])

@includeUnless($boolean, 'view.name', ['status' => 'complete'])

要包含给定视图数组中存在的第一个视图,你可以使用 includeFirst 指令:

blade
@includeFirst(['custom.admin', 'admin'], ['status' => 'complete'])

如果你想包含一个视图而不从父视图继承任何变量,你可以使用 @includeIsolated 指令。包含的视图只能访问你显式传递的变量:

blade
@includeIsolated('view.name', ['user' => $user])

WARNING

你应该避免在 Blade 视图中使用 __DIR____FILE__ 常量,因为它们将引用缓存的编译视图的位置。

为集合渲染视图

你可以使用 Blade 的 @each 指令将循环和包含合并到一行中:

blade
@each('view.name', $jobs, 'job')

@each指令的第一个参数是要为数组或集合中的每个元素呈现的视图。第二个参数是你希望迭代的数组或集合,而第三个参数是将分配给视图中当前迭代的变量名称。因此,例如,如果你要迭代 jobs 数组,通常你会希望将每个作业作为视图中的 job 变量进行访问。当前迭代的数组键将作为视图中的 key 变量提供。

你还可以将第四个参数传递给 @each 指令。该参数确定给定数组为空时将呈现的视图。

blade
@each('view.name', $jobs, 'job', 'view.empty')

WARNING

通过 @each 渲染的视图不会继承父视图的变量。如果子视图需要这些变量,你应该使用 @foreach@include 指令。

@once 指令

@once指令允许你定义模板的一部分,每个渲染周期仅评估一次。这对于使用 堆栈 将给定的 JavaScript 片段推送到页面标题中可能很有用。例如,如果你在循环内渲染给定的 组件,你可能希望仅在第一次渲染组件时将 JavaScript 推送到标头:

blade
@once
    @push('scripts')
        <script>
            // Your custom JavaScript...
        </script>
    @endpush
@endonce

由于 @once 指令通常与 @push@prepend 指令结合使用,因此可以使用 @pushOnce@prependOnce 指令以方便你使用:

blade
@pushOnce('scripts')
    <script>
        // Your custom JavaScript...
    </script>
@endPushOnce

如果你从两个单独的 Blade 模板推送重复内容,则应提供一个唯一标识符作为 @pushOnce 指令的第二个参数,以确保内容仅呈现一次:

blade
<!-- pie-chart.blade.php -->
@pushOnce('scripts', 'chart.js')
    <script src="/chart.js"></script>
@endPushOnce

<!-- line-chart.blade.php -->
@pushOnce('scripts', 'chart.js')
    <script src="/chart.js"></script>
@endPushOnce

原生 PHP

在某些情况下,将 PHP 代码嵌入到视图中很有用。你可以使用 Blade @php 指令在模板中执行纯 PHP 块:

blade
@php
    $counter = 1;
@endphp

或者,如果你只需要使用 PHP 导入一个类,你可以使用 @use 指令:

blade
@use('App\Models\Flight')

可以向 @use 指令提供第二个参数来为导入的类别名:

blade
@use('App\Models\Flight', 'FlightModel')

如果同一命名空间中有多个类,则可以对这些类的导入进行分组:

blade
@use('App\Models\{Flight, Airport}')

@use指令还支持通过在导入路径前添加functionconst修饰符来导入PHP函数和常量:

blade
@use(function App\Helpers\format_currency)
@use(const App\Constants\MAX_ATTEMPTS)

就像类导入一样,函数和常量也支持别名:

blade
@use(function App\Helpers\format_currency, 'formatMoney')
@use(const App\Constants\MAX_ATTEMPTS, 'MAX_TRIES')

function 和 const 修饰符也支持分组导入,允许你在单个指令中从同一命名空间导入多个符号:

blade
@use(function App\Helpers\{format_currency, format_date})
@use(const App\Constants\{MAX_ATTEMPTS, DEFAULT_TIMEOUT})

注释

Blade 还允许你在视图中定义注释。但是,与 HTML 注释不同,Blade 注释不包含在应用程序返回的 HTML 中:

blade
{{-- This comment will not be present in the rendered HTML --}}

组件

组件和插槽提供与部分、布局和包含类似的好处;然而,有些人可能会发现组件和插槽的心理模型更容易理解。编写组件有两种方法:基于类的组件和匿名组件。

要创建基于类的组件,你可以使用 make:component Artisan 命令。为了说明如何使用组件,我们将创建一个简单的 Alert 组件。 make:component命令会将组件放置在app/View/Components目录中:

shell
php artisan make:component Alert

make:component命令还将为组件创建一个视图模板。该视图将被放置在resources/views/components目录中。为你自己的应用程序编写组件时,会在 app/View/Components 目录和 resources/views/components 目录中自动发现组件,因此通常不需要进一步注册组件。

你还可以在子目录中创建组件:

shell
php artisan make:component Forms/Input

上面的命令将在app/View/Components/Forms目录中创建一个Input组件,并且视图将放置在resources/views/components/forms目录中。

手动注册包组件

为你自己的应用程序编写组件时,会自动在app/View/Components目录和resources/views/components目录中发现组件。

但是,如果你正在构建使用 Blade 组件的包,则需要手动注册组件类及其 HTML 标记别名。你通常应该在包服务提供者的 boot 方法中注册你的组件:

php
use Illuminate\Support\Facades\Blade;

/**
 * Bootstrap your package's services.
 */
public function boot(): void
{
    Blade::component('package-alert', Alert::class);
}

一旦你的组件被注册,它就可以使用它的标签别名来呈现:

blade
<x-package-alert/>

或者,你可以使用 componentNamespace 方法按照约定自动加载组件类。例如,Nightshade包可能具有驻留在Package\Views\Components命名空间中的CalendarColorPicker组件:

php
use Illuminate\Support\Facades\Blade;

/**
 * Bootstrap your package's services.
 */
public function boot(): void
{
    Blade::componentNamespace('Nightshade\\Views\\Components', 'nightshade');
}

这将允许供应商名称空间使用 package-name:: 语法使用包组件:

blade
<x-nightshade::calendar />
<x-nightshade::color-picker />

Blade 将通过对组件名称进行 pascal 大小写来自动检测链接到该组件的类。还支持使用「点」表示法的子目录。

渲染组件

要显示组件,你可以在 Blade 模板之一中使用 Blade 组件标签。 Blade 组件标签以字符串 x- 开头,后跟组件类的 kebab case 名称:

blade
<x-alert/>

<x-user-profile/>

如果组件类在app/View/Components目录中嵌套得更深,则可以使用.字符来指示目录嵌套。例如,如果我们假设一个组件位于app/View/Components/Inputs/Button.php,我们可以像这样渲染它:

blade
<x-inputs.button/>

如果你想有条件地渲染你的组件,你可以在你的组件类上定义一个 shouldRender 方法。如果 shouldRender 方法返回 false 该组件将不会被渲染:

php
use Illuminate\Support\Str;

/**
 * Whether the component should be rendered
 */
public function shouldRender(): bool
{
    return Str::length($this->message) > 0;
}

索引组件

有时组件是组件组的一部分,你可能希望将相关组件分组在一个目录中。例如,想象一个具有以下类结构的「卡片」组件:

text
App\Views\Components\Card\Card
App\Views\Components\Card\Header
App\Views\Components\Card\Body

由于根 Card 组件嵌套在 Card 目录中,因此你可能认为需要通过 <x-card.card> 渲染该组件。但是,当组件的文件名与组件目录的名称匹配时,Laravel 会自动假定该组件是「根」组件,并允许你在不重复目录名称的情况下渲染组件:

blade
<x-card>
    <x-card.header>...</x-card.header>
    <x-card.body>...</x-card.body>
</x-card>

向组件传递数据

你可以使用 HTML 属性将数据传递到 Blade 组件。可以使用简单的 HTML 属性字符串将硬编码的原始值传递给组件。 PHP 表达式和变量应通过使用 : 字符作为前缀的属性传递给组件:

blade
<x-alert type="error" :message="$message"/>

你应该在其类构造函数中定义组件的所有数据属性。组件上的所有公共属性将自动可供组件的视图使用。没有必要从组件的 render 方法将数据传递到视图:

php
<?php

namespace App\View\Components;

use Illuminate\View\Component;
use Illuminate\View\View;

class Alert extends Component
{
    /**
     * Create the component instance.
     */
    public function __construct(
        public string $type,
        public string $message,
    ) {}

    /**
     * Get the view / contents that represent the component.
     */
    public function render(): View
    {
        return view('components.alert');
    }
}

渲染组件时,你可以通过按名称回显变量来显示组件公共变量的内容:

blade
<div class="alert alert-{{ $type }}">
    {{ $message }}
</div>

大小写

组件构造函数参数应使用 camelCase 指定,而在 HTML 属性中引用参数名称时应使用 kebab-case。例如,给定以下组件构造函数:

php
/**
 * Create the component instance.
 */
public function __construct(
    public string $alertType,
) {}

$alertType 参数可以像这样提供给组件:

blade
<x-alert alert-type="danger" />

短属性语法

将属性传递给组件时,你还可以使用「短属性」语法。这通常很方便,因为属性名称经常与其对应的变量名称匹配:

blade
{{-- Short attribute syntax... --}}
<x-profile :$userId :$name />

{{-- Is equivalent to... --}}
<x-profile :user-id="$userId" :name="$name" />

转义属性渲染

由于某些 JavaScript 框架(例如 Alpine.js)也使用冒号前缀的属性,因此你可以使用双冒号(::)前缀来通知 Blade 该属性不是 PHP 表达式。例如,给定以下组件:

blade
<x-button ::class="{ danger: isDeleting }">
    Submit
</x-button>

Blade 将呈现以下 HTML:

blade
<button :class="{ danger: isDeleting }">
    Submit
</button>

组件方法

除了组件模板可用的公共变量之外,还可以调用组件上的任何公共方法。例如,想象一个具有 isSelected 方法的组件:

php
/**
 * Determine if the given option is the currently selected option.
 */
public function isSelected(string $option): bool
{
    return $option === $this->selected;
}

你可以通过调用与方法名称匹配的变量来从组件模板执行此方法:

blade
<option {{ $isSelected($value) ? 'selected' : '' }} value="{{ $value }}">
    {{ $label }}
</option>

在组件类中访问属性与插槽

Blade 组件还允许你访问类的 render 方法内的组件名称、属性和插槽。但是,为了访问此数据,你应该从组件的 render 方法返回一个闭包:

php
use Closure;

/**
 * Get the view / contents that represent the component.
 */
public function render(): Closure
{
    return function () {
        return '<div {{ $attributes }}>Components content</div>';
    };
}

组件的 render 方法返回的闭包也可能接收 $data 数组作为其唯一参数。该数组将包含几个提供有关组件信息的元素:

php
return function (array $data) {
    // $data['componentName'];
    // $data['attributes'];
    // $data['slot'];

    return '<div {{ $attributes }}>Components content</div>';
}

WARNING

$data 数组中的元素绝对不应该直接嵌入到 render 方法返回的 Blade 字符串中,因为这样做可能允许通过恶意属性内容远程执行代码。

componentName 等于在 x- 前缀之后的 HTML 标记中使用的名称。所以<x-alert />componentName将是alertattributes 元素将包含 HTML 标记上存在的所有属性。 slot 元素是一个 Illuminate\Support\HtmlString 实例,其中包含组件槽的内容。

闭包应该返回一个字符串。如果返回的字符串对应于现有视图,则将渲染该视图;否则,返回的字符串将被评估为内联Blade 视图。

额外依赖

如果你的组件需要来自 Laravel 的 service 容器 的依赖项,你可以将它们列在组件的任何数据属性之前,它们将由容器自动注入:

php
use App\Services\AlertCreator;

/**
 * Create the component instance.
 */
public function __construct(
    public AlertCreator $creator,
    public string $type,
    public string $message,
) {}

隐藏属性 / 方法

如果你想防止某些公共方法或属性作为变量公开给组件模板,你可以将它们添加到组件上的 $except 数组属性中:

php
<?php

namespace App\View\Components;

use Illuminate\View\Component;

class Alert extends Component
{
    /**
     * The properties / methods that should not be exposed to the component template.
     *
     * @var array
     */
    protected $except = ['type'];

    /**
     * Create the component instance.
     */
    public function __construct(
        public string $type,
    ) {}
}

组件属性

我们已经研究了如何将数据属性传递给组件;但是,有时你可能需要指定其他 HTML 属性,例如 class,这些属性不是组件运行所需数据的一部分。通常,你希望将这些附加属性向下传递到组件模板的根元素。例如,假设我们想要渲染一个 alert 组件,如下所示:

blade
<x-alert type="error" :message="$message" class="mt-4"/>

所有不属于组件构造函数的属性都会自动添加到组件的「属性包」中。该属性包通过 $attributes 变量自动可供组件使用。所有属性都可以通过回显此变量在组件内呈现:

blade
<div {{ $attributes }}>
    <!-- Component content -->
</div>

WARNING

目前不支持在组件标签内使用诸如@env之类的指令。例如,<x-alert :live="@env('production')"/>将不会被编译。

默认 / 合并属性

有时你可能需要指定属性的默认值或将附加值合并到组件的某些属性中。为此,你可以使用属性包的 merge 方法。此方法对于定义一组应始终应用于组件的默认 CSS 类特别有用:

blade
<div {{ $attributes->merge(['class' => 'alert alert-'.$type]) }}>
    {{ $message }}
</div>

如果我们假设该组件的使用方式如下:

blade
<x-alert type="error" :message="$message" class="mb-4"/>

组件的最终渲染 HTML 将如下所示:

blade
<div class="alert alert-error mb-4">
    <!-- Contents of the $message variable -->
</div>

有条件地合并类名

有时,如果给定条件是true,你可能希望合并类。你可以通过 class 方法来完成此操作,该方法接受一个类数组,其中数组键包含你要添加的一个或多个类,而值是一个布尔表达式。如果数组元素有数字键,它将始终包含在渲染的类列表中:

blade
<div {{ $attributes->class(['p-4', 'bg-red' => $hasError]) }}>
    {{ $message }}
</div>

如果需要将其他属性合并到组件上,可以将 merge 方法链接到 class 方法上:

blade
<button {{ $attributes->class(['p-4'])->merge(['type' => 'button']) }}>
    {{ $slot }}
</button>

INFO

如果你需要有条件地编译不应接收合并属性的其他 HTML 元素上的类,则可以使用 @class 指令

非 class 属性合并

合并不是 class 属性的属性时,提供给 merge 方法的值将被视为该属性的「默认」值。但是,与 class 属性不同,这些属性不会与注入的属性值合并。相反,它们将被覆盖。例如,button组件的实现可能如下所示:

blade
<button {{ $attributes->merge(['type' => 'button']) }}>
    {{ $slot }}
</button>

要使用自定义 type 渲染按钮组件,可以在使用组件时指定它。如果未指定类型,则将使用 button 类型:

blade
<x-button type="submit">
    Submit
</x-button>

本例中 button 组件的渲染 HTML 为:

blade
<button type="submit">
    Submit
</button>

如果你希望class以外的属性将其默认值和注入值连接在一起,你可以使用prepends方法。在此示例中,data-controller属性将始终以profile-controller开头,任何其他注入的data-controller值将放置在此默认值之后:

blade
<div {{ $attributes->merge(['data-controller' => $attributes->prepends('profile-controller')]) }}>
    {{ $slot }}
</div>

检索与过滤属性

你可以使用 filter 方法过滤属性。此方法接受一个闭包,如果你希望保留属性包中的属性,则该闭包应返回 true

blade
{{ $attributes->filter(fn (string $value, string $key) => $key == 'foo') }}

为了方便起见,你可以使用 whereStartsWith 方法来检索键以给定字符串开头的所有属性:

blade
{{ $attributes->whereStartsWith('wire:model') }}

相反,whereDoesntStartWith方法可用于排除键以给定字符串开头的所有属性:

blade
{{ $attributes->whereDoesntStartWith('wire:model') }}

使用 first 方法,你可以渲染给定属性包中的第一个属性:

blade
{{ $attributes->whereStartsWith('wire:model')->first() }}

如果你想检查组件上是否存在某个属性,可以使用 has 方法。此方法接受属性名称作为其唯一参数,并返回一个布尔值,指示该属性是否存在:

blade
@if ($attributes->has('class'))
    <div>Class attribute is present</div>
@endif

如果将数组传递给 has 方法,该方法将确定组件上是否存在所有给定属性:

blade
@if ($attributes->has(['name', 'class']))
    <div>All of the attributes are present</div>
@endif

hasAny 方法可用于确定组件上是否存在任何给定属性:

blade
@if ($attributes->hasAny(['href', ':href', 'v-bind:href']))
    <div>One of the attributes is present</div>
@endif

你可以使用 get 方法检索特定属性的值:

blade
{{ $attributes->get('class') }}

only 方法可用于仅检索具有给定键的属性:

blade
{{ $attributes->only(['class']) }}

except 方法可用于检索除具有给定键的属性之外的所有属性:

blade
{{ $attributes->except(['class']) }}

保留关键字

默认情况下,一些关键字保留供 Blade 内部使用,以便渲染组件。以下关键字不能定义为组件内的公共属性或方法名称:

  • data
  • render
  • resolve
  • resolveView
  • shouldRender
  • view
  • withAttributes
  • withName

插槽

你经常需要通过「槽」将附加内容传递给你的组件。组件槽通过回显 $slot 变量来呈现。为了探索这个概念,我们假设 alert 组件具有以下标记:

blade
<!-- /resources/views/components/alert.blade.php -->

<div class="alert alert-danger">
    {{ $slot }}
</div>

我们可以通过将内容注入到组件中来将内容传递给slot

blade
<x-alert>
    <strong>Whoops!</strong> Something went wrong!
</x-alert>

有时,组件可能需要在组件内的不同位置呈现多个不同的槽。让我们修改警报组件以允许注入「标题」槽:

blade
<!-- /resources/views/components/alert.blade.php -->

<span class="alert-title">{{ $title }}</span>

<div class="alert alert-danger">
    {{ $slot }}
</div>

你可以使用 x-slot 标签定义命名槽的内容。任何不在显式 x-slot 标签内的内容都将传递到 $slot 变量中的组件:

xml
<x-alert>
    <x-slot:title>
        Server Error
    </x-slot>

    <strong>Whoops!</strong> Something went wrong!
</x-alert>

你可以调用插槽的 isEmpty 方法来确定插槽是否包含内容:

blade
<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
@if ($slot->hasActualContent())
    The scope has non-comment content.
@endif

作用域插槽

如果你使用过 Vue 等 JavaScript 框架,你可能会熟悉「作用域插槽」,它允许你从插槽内的组件访问数据或方法。你可以通过在组件上定义公共方法或属性并通过 $component 变量访问插槽中的组件来在 Laravel 中实现类似的行为。在这个例子中,我们假设 x-alert 组件在其组件类上定义了一个公共 formatAlert 方法:

blade
<x-alert>
    <x-slot:title>
        {{ $component->formatAlert('Server Error') }}
    </x-slot>

    <strong>Whoops!</strong> Something went wrong!
</x-alert>

插槽属性

与 Blade 组件一样,你可以将额外的 attributes 分配给插槽,例如 CSS 类名称:

xml
<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 属性。有关如何与属性交互的更多信息,请参阅组件属性的文档:

blade
@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 方法返回组件的标记:

php
/**
 * Get the view / contents that represent the component.
 */
public function render(): string
{
    return <<<'blade'
        <div class="alert alert-danger">
            {{ $slot }}
        </div>
    blade;
}

生成内联视图组件

要创建渲染内联视图的组件,你可以在执行 make:component 命令时使用 inline 选项:

shell
php artisan make:component Alert --inline

动态组件

有时你可能需要渲染一个组件,但直到运行时才知道应该渲染哪个组件。在这种情况下,你可以使用 Laravel 内置的 dynamic-component 组件来根据运行时值或变量渲染组件:

blade
// $componentName = "secondary-button";

<x-dynamic-component :component="$componentName" class="mt-4" />

手动注册组件

WARNING

以下有关手动注册组件的文档主要适用于那些编写包含视图组件的 Laravel 包的人。如果你不编写包,则组件文档的这一部分可能与你无关。

为你自己的应用程序编写组件时,会自动在app/View/Components目录和resources/views/components目录中发现组件。

但是,如果你正在构建一个使用 Blade 组件的包或将组件放置在非常规目录中,则需要手动注册组件类及其 HTML 标签别名,以便 Laravel 知道在哪里可以找到该组件。你通常应该在包服务提供者的 boot 方法中注册你的组件:

php
use Illuminate\Support\Facades\Blade;
use VendorPackage\View\Components\AlertComponent;

/**
 * Bootstrap your package's services.
 */
public function boot(): void
{
    Blade::component('package-alert', AlertComponent::class);
}

一旦你的组件被注册,它就可以使用它的标签别名来呈现:

blade
<x-package-alert/>

自动加载包组件

或者,你可以使用 componentNamespace 方法按照约定自动加载组件类。例如,Nightshade包可能具有驻留在Package\Views\Components命名空间中的CalendarColorPicker组件:

php
use Illuminate\Support\Facades\Blade;

/**
 * Bootstrap your package's services.
 */
public function boot(): void
{
    Blade::componentNamespace('Nightshade\\Views\\Components', 'nightshade');
}

这将允许供应商名称空间使用 package-name:: 语法使用包组件:

blade
<x-nightshade::calendar />
<x-nightshade::color-picker />

Blade 将通过对组件名称进行 pascal 大小写来自动检测链接到该组件的类。还支持使用「点」表示法的子目录。

匿名组件

与内联组件类似,匿名组件提供了一种通过单个文件管理组件的机制。然而,匿名组件使用单个视图文件并且没有关联的类。要定义匿名组件,你只需将 Blade 模板放置在 resources/views/components 目录中即可。例如,假设你在 resources/views/components/alert.blade.php 定义了一个组件,你可以简单地像这样渲染它:

blade
<x-alert/>

你可以使用 . 字符来指示组件是否嵌套在 components 目录中更深处。例如,假设组件定义在 resources/views/components/inputs/button.blade.php,你可以像这样渲染它:

blade
<x-inputs.button/>

要通过 Artisan 创建匿名组件,你可以在调用 make:component 命令时使用 --view 标志:

shell
php artisan make:component forms.input --view

上面的命令将在resources/views/components/forms/input.blade.php创建一个Blade文件,它可以通过<x-forms.input />渲染为组件。

匿名索引组件

有时,当一个组件由许多 Blade 模板组成时,你可能希望将给定组件的模板分组到一个目录中。例如,想象一个具有以下目录结构的「accordion」组件:

text
/resources/views/components/accordion.blade.php
/resources/views/components/accordion/item.blade.php

此目录结构允许你渲染折叠组件及其项目,如下所示:

blade
<x-accordion>
    <x-accordion.item>
        ...
    </x-accordion.item>
</x-accordion>

但是,为了通过 x-accordion 渲染折叠组件,我们被迫将「index」折叠组件模板放置在 resources/views/components 目录中,而不是将其与其他折叠组件相关模板一起嵌套在 accordion 目录中。

值得庆幸的是,Blade 允许你在组件目录本身中放置与组件目录名称匹配的文件。当此模板存在时,即使它嵌套在目录中,也可以将其呈现为组件的「根」元素。因此,我们可以继续使用上例中给出的相同 Blade 语法;但是,我们将像这样调整目录结构:

text
/resources/views/components/accordion/accordion.blade.php
/resources/views/components/accordion/item.blade.php

数据属性 / 特性

由于匿名组件没有任何关联的类,你可能想知道如何区分哪些数据应作为变量传递给组件以及哪些属性应放置在组件的属性包中。

你可以使用组件 Blade 模板顶部的 @props 指令指定哪些属性应被视为数据变量。组件上的所有其他属性都可以通过组件的属性包获得。如果你想给数据变量一个默认值,你可以指定变量的名称作为数组键,默认值作为数组值:

blade
<!-- /resources/views/components/alert.blade.php -->

@props(['type' => 'info', 'message'])

<div {{ $attributes->merge(['class' => 'alert alert-'.$type]) }}>
    {{ $message }}
</div>

给定上面的组件定义,我们可以像这样渲染组件:

blade
<x-alert type="error" :message="$message" class="mb-4"/>

访问父级数据

有时你可能希望从子组件内的父组件访问数据。在这些情况下,你可以使用 @aware 指令。例如,假设我们正在构建一个由父级 <x-menu> 和子级 <x-menu.item> 组成的复杂菜单组件:

blade
<x-menu color="purple">
    <x-menu.item>...</x-menu.item>
    <x-menu.item>...</x-menu.item>
</x-menu>

<x-menu>组件可能有如下的实现:

blade
<!-- /resources/views/components/menu/index.blade.php -->

@props(['color' => 'gray'])

<ul {{ $attributes->merge(['class' => 'bg-'.$color.'-200']) }}>
    {{ $slot }}
</ul>

因为 color 属性仅传递给父级 (<x-menu>),所以它在 <x-menu.item> 中不可用。但是,如果我们使用 @aware 指令,我们也可以在 <x-menu.item> 中使用它:

blade
<!-- /resources/views/components/menu/item.blade.php -->

@aware(['color' => 'gray'])

<li {{ $attributes->merge(['class' => 'text-'.$color.'-800']) }}>
    {{ $slot }}
</li>

WARNING

@aware指令无法访问未通过 HTML 属性显式传递给父组件的父数据。未显式传递给父组件的默认 @props 值无法通过 @aware 指令访问。

匿名组件路径

如前所述,匿名组件通常是通过将 Blade 模板放置在 resources/views/components 目录中来定义的。但是,除了默认路径之外,你有时可能还想向 Laravel 注册其他匿名组件路径。

anonymousComponentPath 方法接受匿名组件位置的「路径」作为其第一个参数,并接受组件应放置在其下的可选「命名空间」作为其第二个参数。通常,应从应用程序之一的 服务提供者boot 方法调用此方法:

php
/**
 * Bootstrap any application services.
 */
public function boot(): void
{
    Blade::anonymousComponentPath(__DIR__.'/../components');
}

当组件路径注册时没有指定前缀(如上例所示)时,它们可能会在你的 Blade 组件中呈现而没有相应的前缀。例如,如果上面注册的路径中存在一个 panel.blade.php 组件,它可能会像这样渲染:

blade
<x-panel />

前缀「命名空间」可以作为 anonymousComponentPath 方法的第二个参数提供:

php
Blade::anonymousComponentPath(__DIR__.'/../components', 'dashboard');

当提供前缀时,在渲染组件时,可以通过将组件的名称空间作为组件名称的前缀来渲染该「名称空间」内的组件:

blade
<x-dashboard::panel />

构建布局

使用组件构建布局

大多数 Web 应用程序在各个页面上都保持相同的总体布局。如果我们必须在创建的每个视图中重复整个布局 HTML,那么维护我们的应用程序将非常麻烦且困难。值得庆幸的是,将此布局定义为单个 Blade 组件 然后在整个应用程序中使用它很方便。

定义布局组件

例如,假设我们正在构建一个「待办事项」列表应用程序。我们可以定义一个如下所示的 layout 组件:

blade
<!-- resources/views/components/layout.blade.php -->

<html>
    <head>
        <title>{{ $title ?? 'Todo Manager' }}</title>
    </head>
    <body>
        <h1>Todos</h1>
        <hr/>
        {{ $slot }}
    </body>
</html>

应用布局组件

一旦定义了 layout 组件,我们就可以创建一个使用该组件的 Blade 视图。在此示例中,我们将定义一个显示任务列表的简单视图:

blade
<!-- resources/views/tasks.blade.php -->

<x-layout>
    @foreach ($tasks as $task)
        <div>{{ $task }}</div>
    @endforeach
</x-layout>

请记住,注入组件的内容将提供给 layout 组件中的默认 $slot 变量。你可能已经注意到,我们的 layout 也尊重 $title 插槽(如果提供);否则,将显示默认标题。我们可以使用组件文档中讨论的标准槽语法从任务列表视图中注入自定义标题:

blade
<!-- resources/views/tasks.blade.php -->

<x-layout>
    <x-slot:title>
        Custom Title
    </x-slot>

    @foreach ($tasks as $task)
        <div>{{ $task }}</div>
    @endforeach
</x-layout>

现在我们已经定义了布局和任务列表视图,我们只需要从路线返回 task 视图:

php
use App\Models\Task;

Route::get('/tasks', function () {
    return view('tasks', ['tasks' => Task::all()]);
});

使用模板继承构建布局

定义布局

布局也可以通过「模板继承」创建。这是在引入组件之前构建应用程序的主要方式。

首先,让我们看一个简单的示例。首先,我们将检查页面布局。由于大多数 Web 应用程序在各个页面上都保持相同的总体布局,因此可以方便地将此布局定义为单个 Blade 视图:

blade
<!-- resources/views/layouts/app.blade.php -->

<html>
    <head>
        <title>App Name - @yield('title')</title>
    </head>
    <body>
        @section('sidebar')
            This is the master sidebar.
        @show

        <div class="container">
            @yield('content')
        </div>
    </body>
</html>

正如你所看到的,该文件包含典型的 HTML 标记。但是,请注意 @section@yield 指令。顾名思义,@section指令定义了一段内容,而@yield指令用于显示给定部分的内容。

现在我们已经为应用程序定义了布局,让我们定义一个继承该布局的子页面。

扩展布局

定义子视图时,使用 @extends Blade 指令指定子视图应「继承」哪个布局。扩展 Blade 布局的视图可以使用 @section 指令将内容注入到布局的部分中。请记住,如上例所示,这些部分的内容将使用 @yield 显示在布局中:

blade
<!-- resources/views/child.blade.php -->

@extends('layouts.app')

@section('title', 'Page Title')

@section('sidebar')
    @@parent

    <p>This is appended to the master sidebar.</p>
@endsection

@section('content')
    <p>This is my body content.</p>
@endsection

在此示例中,sidebar部分利用@@parent指令将内容附加(而不是覆盖)到布局的侧边栏。当视图渲染时,@@parent指令将被布局的内容替换。

INFO

与前面的示例相反,此 sidebar 部分以 @endsection 而不是 @show 结尾。 @endsection指令只会定义一个节,而@show将定义并立即产生该节。

@yield指令还接受默认值作为其第二个参数。如果生成的部分未定义,则将呈现该值:

blade
@yield('content', 'Default content')

表单

CSRF 字段

每当你在应用程序中定义 HTML 表单时,都应该在表单中包含一个隐藏的 CSRF 令牌字段,以便 CSRF 保护 中间件可以验证请求。你可以使用 @csrf Blade 指令来生成令牌字段:

blade
<form method="POST" action="/profile">
    @csrf

    ...
</form>

Method 字段

由于 HTML 表单无法发出 PUTPATCHDELETE 请求,因此你需要添加隐藏的 _method 字段来欺骗这些 HTTP 动词。 @method Blade 指令可以为你创建此字段:

blade
<form action="/foo/bar" method="POST">
    @method('PUT')

    ...
</form>

验证错误

@error指令可用于快速检查给定属性是否存在验证错误消息。在 @error 指令中,你可以回显 $message 变量以显示错误消息:

blade
<!-- /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 指令来呈现内容:

blade
<!-- /resources/views/auth.blade.php -->

<label for="email">Email address</label>

<input
    id="email"
    type="email"
    class="@error('email') is-invalid @else is-valid @enderror"
/>

你可以将特定错误包的名称作为第二个参数传递给@error指令,以检索包含多个表单的页面上的验证错误消息:

blade
<!-- /resources/views/auth.blade.php -->

<label for="email">Email address</label>

<input
    id="email"
    type="email"
    class="@error('email', 'login') is-invalid @enderror"
/>

@error('email', 'login')
    <div class="alert alert-danger">{{ $message }}</div>
@enderror

堆栈

Blade 允许你推送到命名堆栈,这些堆栈可以在另一个视图或布局中的其他位置渲染。这对于指定子视图所需的任何 JavaScript 库特别有用:

blade
@push('scripts')
    <script src="/example.js"></script>
@endpush

如果你想在给定布尔表达式计算结果为 true 时获取 @push 内容,则可以使用 @pushIf 指令:

blade
@pushIf($shouldPush, 'scripts')
    <script src="/example.js"></script>
@endPushIf

你可以根据需要多次压入堆栈。要渲染完整的堆栈内容,请将堆栈的名称传递给 @stack 指令:

blade
<head>
    <!-- Head Contents -->

    @stack('scripts')
</head>

如果你想将内容添加到堆栈的开头,你应该使用 @prepend 指令:

blade
@push('scripts')
    This will be second...
@endpush

// Later...

@prepend('scripts')
    This will be first...
@endprepend

@hasstack指令可用于确定堆栈是否为空:

blade
@hasstack('list')
    <ul>
        @stack('list')
    </ul>
@endif

服务注入

@inject指令可用于从 Laravel 服务容器检索服务。传递给 @inject 的第一个参数是将服务放入的变量的名称,而第二个参数是你要解析的服务的类或接口名称:

blade
@inject('metrics', 'App\Services\MetricsService')

<div>
    Monthly Revenue: {{ $metrics->monthlyRevenue() }}.
</div>

渲染内联 Blade 模板

有时你可能需要将原始 Blade 模板字符串转换为有效的 HTML。你可以使用 Blade 外观提供的 render 方法来完成此操作。 render 方法接受 Blade 模板字符串和一个可选的数据数组以提供给模板:

php
use Illuminate\Support\Facades\Blade;

return Blade::render('Hello, {{ $name }}', ['name' => 'Julian Bashir']);

Laravel 通过将内联 Blade 模板写入 storage/framework/views 目录来渲染它们。如果你希望 Laravel 在渲染 Blade 模板后删除这些临时文件,你可以向该方法提供 deleteCachedView 参数:

php
return Blade::render(
    'Hello, {{ $name }}',
    ['name' => 'Julian Bashir'],
    deleteCachedView: true
);

渲染 Blade 片段

当使用 Turbohtmx 等前端框架时,你有时可能只需要在 HTTP 响应中返回 Blade 模板的一部分。Blade「碎片」可以让你做到这一点。首先,将 Blade 模板的一部分放入 @fragment@endfragment 指令中:

blade
@fragment('user-list')
    <ul>
        @foreach ($users as $user)
            <li>{{ $user->name }}</li>
        @endforeach
    </ul>
@endfragment

然后,当渲染使用此模板的视图时,你可以调用 fragment 方法来指定仅指定的片段应包含在传出的 HTTP 响应中:

php
return view('dashboard', ['users' => $users])->fragment('user-list');

fragmentIf 方法允许你根据给定条件有条件地返回视图的片段。否则,将返回整个视图:

php
return view('dashboard', ['users' => $users])
    ->fragmentIf($request->hasHeader('HX-Request'), 'user-list');

fragmentsfragmentsIf 方法允许你在响应中返回多个视图片段。这些片段将连接在一起:

php
view('dashboard', ['users' => $users])
    ->fragments(['user-list', 'comment-list']);

view('dashboard', ['users' => $users])
    ->fragmentsIf(
        $request->hasHeader('HX-Request'),
        ['user-list', 'comment-list']
    );

扩展 Blade

Blade 允许你使用 directive 方法定义自己的自定义指令。当 Blade 编译器遇到自定义指令时,它将使用指令包含的表达式调用提供的回调。

以下示例创建一个 @datetime($var) 指令,用于格式化给定的 $var,它应该是 DateTime 的实例:

php
<?php

namespace App\Providers;

use Illuminate\Support\Facades\Blade;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     */
    public function register(): void
    {
        // ...
    }

    /**
     * Bootstrap any application services.
     */
    public function boot(): void
    {
        Blade::directive('datetime', function (string $expression) {
            return "<?php echo ($expression)->format('m/d/Y H:i'); ?>";
        });
    }
}

正如你所看到的,我们将把 format 方法链接到传递到指令中的任何表达式上。因此,在本例中,该指令生成的最终 PHP 将是:

php
<?php echo ($var)->format('m/d/Y H:i'); ?>

WARNING

更新 Blade 指令的逻辑后,你将需要删除所有缓存的 Blade 视图。可以使用 view:clear Artisan 命令删除缓存的 Blade 视图。

自定义 Echo 处理程序

如果你尝试使用 Blade「回显」对象,则将调用该对象的 __toString 方法。 __toString方法是PHP内置的「魔法方法」之一。但是,有时你可能无法控制给定类的 __toString 方法,例如当你正在交互的类属于第三方库时。

在这些情况下,Blade 允许你为该特定类型的对象注册自定义回显处理程序。为了实现这一点,你应该调用 Blade 的 stringable 方法。 stringable 方法接受闭包。这个闭包应该类型提示它负责渲染的对象的类型。通常,应该在应用程序的 AppServiceProvider 类的 boot 方法中调用 stringable 方法:

php
use Illuminate\Support\Facades\Blade;
use Money\Money;

/**
 * Bootstrap any application services.
 */
public function boot(): void
{
    Blade::stringable(function (Money $money) {
        return $money->formatTo('en_GB');
    });
}

定义自定义回显处理程序后,你可以简单地回显 Blade 模板中的对象:

blade
Cost: {{ $money }}

自定义 If 语句

在定义简单的自定义条件语句时,对自定义指令进行编程有时比所需的更为复杂。因此,Blade 提供了 Blade::if 方法,允许你使用闭包快速定义自定义条件指令。例如,让我们定义一个自定义条件来检查应用程序配置的默认「磁盘」。我们可以在 AppServiceProviderboot 方法中执行此操作:

php
use Illuminate\Support\Facades\Blade;

/**
 * Bootstrap any application services.
 */
public function boot(): void
{
    Blade::if('disk', function (string $value) {
        return config('filesystems.default') === $value;
    });
}

定义自定义条件后,你可以在模板中使用它:

blade
@disk('local')
    <!-- The application is using the local disk... -->
@elsedisk('s3')
    <!-- The application is using the s3 disk... -->
@else
    <!-- The application is using some other disk... -->
@enddisk

@unlessdisk('local')
    <!-- The application is not using the local disk... -->
@enddisk