Blade 模板
Blade 模板
简介
Blade 是 Laravel 内置的简洁且功能强大的模板引擎。 与部分 PHP 模板引擎不同,Blade 不限制你在模板中使用原生 PHP 代码。实际上,所有 Blade 模板都会被编译为原生 PHP 代码并缓存,直至模板被修改;这意味着 Blade 几乎不会给应用程序增加额外开销。 Blade 模板文件使用 .blade.php 扩展名,通常存储在 resources/views 目录中。
可以使用全局的 view 助手从路由或控制器中返回 Blade 视图。当然,正如在 视图 的文档中所提到的那样,可以使用 view 助手函数的第二个参数将数据传递给 Blade 视图:
Route::get('/', function () {
return view('greeting', ['name' => 'Finn']);
});
通过 Livewire 增强 Blade
想要让你的 Blade 模板更上一层楼,轻松构建动态界面吗? 看看 Laravel Livewire 吧。 Livewire 运行你编写带有动态功能的 Blade 组件,而这些动态功能通常只有通过像 React 或 Vue 这样的前端框架才有可能实现。它提供了一种很棒的构建现代响应式前端的方法,无需面对许多 JavaScript 框架那样的复杂性、客户端渲染或构建步骤。
数据显示
你可以通过将变量包装在大括号中来显示传递给 Blade 视图的数据。例如,给定以下路由:
Route::get('/', function () {
return view('welcome', ['name' => 'Samantha']);
});
你可以显示 name 变量的内容,如下所示:
Hello, {{ $name }}.
[注意]
Blade 的{{ }}echo 语句是通过 PHP 的htmlspecialchars函数自动发送的,以防止 XSS 攻击。
你并非只能显示传递给视图的变量的内容。你也可以输出任何 PHP 函数的结果。实际上,你可以将任何你想要的 PHP 代码放在 Blade 输出语句当中:
The current UNIX timestamp is {{ time() }}.
HTML 实体编码
默认情况下,Blade(和 Laravel 的 e 方法)会双重编码 HTML 实体。如果你要禁用双重编码,可以在AppServiceProvider 的 boot 方法中调用 Blade::withoutDoubleEncoding 方法:
<?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 {{ }} 声明会自动调用 PHP 的 htmlspecialchars 方法来阻止 XSS 攻击。如果你不想你的数据被转义,你可以使用下面的语法:
Hello, {!! $name !!}.
[!警告]
显示用户提供的数据时要非常小心。你通常应该使用转义,双花括号在显示用户数据时可以阻止 XSS 攻击。
Blade 和 JavaScript 框架
由于很多 JavaScript 框架也使用“花括号”在浏览器中显示表达式,你可以使用 @ 符号来告知 Blade 渲染引擎将当前表达式保持原样。例如:
<h1>Laravel</h1>
Hello, @{{ name }}.
示例中, @ 符号会被 Blade 移除;但是 {{ name }} 表达式会保持原样,让 Javascript 框架来进行渲染。
@ 符号也被用来转义 Blade 指令:
{{-- Blade template --}}
@@if()
<!-- HTML output -->
@if()
渲染 JSON
有时你会向视图传递一个数组来达到初始化一个 JavaScript 变量的目的。例如:
<script>
var app = <?php echo json_encode($array); ?>;
</script>
或许, 你无需手动调用 json_encode, 可以使用 Illuminate\Support\Js::from 方法指令。from 方法接收与 PHP 原生函数 json_encode 相同的参数; 然而, 它确保正确转义数据能安全嵌入到 HTML 引号内使用。from 方法会返回一段 JSON.parse 格式的 JavaScript 语句,自动将传入的 PHP 数组或对象转换为合法的 JavaScript 对象:
<script>
var app = {{ Illuminate\Support\Js::from($array) }};
</script>
最新版本的 Laravel 应用骨架已内置 Js 门面, 你可以在 Blade 模板中直接通过门面便捷调用该功能:
<script>
var app = {{ Js::from($array) }};
</script>
注意
你应该仅使用Js::from方法来将现有的变量渲染为 JSON 格式。由于 Blade 模板基于正则表达式解析,若尝试向该指令传递复杂表达式(如运算逻辑、多维嵌套表达式),可能导致不可预期的解析失败。
@verbatim 指令
若你需要在模板的大片区域中直接渲染 JavaScript 变量,可以用 @verbatim 指令包裹 HTML 内容。这样无需在每个 Blade 输出语句前添加 @ 符号(避免 Blade 解析冲突):
@verbatim
<div class="container">
Hello, {{ name }}.
</div>
@endverbatim
Blade 指令
除了模板继承和数据渲染,Blade 还为 PHP 常用控制结构(如条件语句、循环)提供了简洁的语法快捷方式。这些快捷指令既保持了 PHP 原生控制结构的逻辑一致性,又提供了更简洁、直观的写法。
If 语句
您可以使用 @if 、@elseif、@else 和 @endif 指令构建 if 语句。 这些指令的功能与其 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
环境指令
您可以使用 @production 指令检查应用程序是否在生产环境中运行:
@production
// Production specific content...
@endproduction
或者,您可以使用 @env 指令确定应用程序是否在特定环境中运行:
@env('staging')
// The application is running in "staging"...
@endenv
@env(['staging', 'production'])
// The application is running in "staging" or "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
会话指令
@session 指令可用于确定是否存在 会话 值。如果会话值存在, @session and @endsession 指令内的模板内容将被执行。在 @session 指令的内容中,你可以回显 $value 变量的会话值:
@session('status')
<div class="p-4 bg-green-100">
{{ $value }}
</div>
@endsession
Switch 语句
你可以使用 @switch, @case, @break, @default 和 @endswitch 语句来构建 Switch 语句:
@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
[技巧]
在进行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
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
如果你处于一个嵌套循环中,你可以通过 parent 属性访问父循环的 $loop 变量:
@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 |
在嵌套循环中,父循环的 loop 变量。 |
条件类与样式(Conditional Classes & Styles)
@class 指令会有条件地编译一个 CSS class 字符串。该指令接受一个类名数组,其中数组键包含你希望添加的类,而值是一个布尔表达式。如果数组元素具有数字键,它将始终被包含在渲染后的类列表中:
@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 指令可用于有条件地向 HTML 元素添加内联 CSS 样式:
@php
$isActive = true;
@endphp
<span @style([
'background-color: red',
'font-weight: bold' => $isActive,
])></span>
<span style="background-color: red; font-weight: bold;"></span>
额外的属性(Additional Attributes)
为了方便,你可以使用 @checked 指令轻松指示某个 HTML 复选框输入是否被 “checked”。如果提供的条件计算结果为 true,此指令将输出 checked:
<input
type="checkbox"
name="active"
value="active"
@checked(old('active', $user->active))
/>
同样,@selected 指令可用于指示某个 select 选项是否应为 “selected”:
<select name="version">
@foreach ($product->versions as $version)
<option value="{{ $version }}" @selected(old('version') == $version)>
{{ $version }}
</option>
@endforeach
</select>
此外,@disabled 指令可用于指示某个元素是否应为 “disabled”:
<button type="submit" @disabled($errors->isNotEmpty())>Submit</button>
而且,@readonly 指令可用于指示某个元素是否应为 “readonly”:
<input
type="email"
name="email"
value="email@laravel.com"
@readonly($user->isNotAdmin())
/>
此外,@required 指令可用于指示某个元素是否应为 “required”:
<input
type="text"
name="title"
value="title"
@required($user->isAdmin())
/>
包含子视图(Including Subviews)
[!注意]
虽然你可以自由使用@include指令,但 Blade 组件 提供了类似的功能,并且相较于@include指令提供了多项优势,例如数据和属性绑定。
Blade 的 @include 指令允许你在一个视图内部包含另一个 Blade 视图。所有对父视图可用的变量都会对被包含的视图可用:
<div>
@include('shared.errors')
<form>
<!-- Form Contents -->
</form>
</div>
尽管被包含的视图会继承父视图中可用的全部数据,你也可以传递一个额外数据的数组,使其可用于被包含的视图:
@include('view.name', ['status' => 'complete'])
如果你尝试 @include 一个不存在的视图,Laravel 将抛出错误。如果你希望包含一个可能存在或不存在的视图,你应该使用 @includeIf 指令:
@includeIf('view.name', ['status' => 'complete'])
如果你希望在给定布尔表达式计算结果为 true 或 false 时包含某个视图,你可以使用 @includeWhen 和 @includeUnless 指令:
@includeWhen($boolean, 'view.name', ['status' => 'complete'])
@includeUnless($boolean, 'view.name', ['status' => 'complete'])
要从给定的视图数组中包含第一个存在的视图,你可以使用 includeFirst 指令:
@includeFirst(['custom.admin', 'admin'], ['status' => 'complete'])
[!警告]
你应该避免在 Blade 视图中使用__DIR__和__FILE__常量,因为它们将引用缓存的、已编译视图所在的位置。
为集合渲染视图(Rendering Views for Collections)
你可以使用 Blade 的 @each 指令,将循环与 include 合并为一行:
@each('view.name', $jobs, 'job')
@each 指令的第一个参数是要为数组或集合中每个元素渲染的视图。第二个参数是你希望迭代的数组或集合,而第三个参数是在该视图中表示当前迭代项的变量名。例如,如果你正在迭代 jobs 数组,你通常希望在视图中以 job 变量访问每个 job。当前迭代项的数组键将在视图中以 key 变量提供。
你也可以为 @each 指令传递第四个参数。这个参数用于指定当给定数组为空时将要渲染的视图:
@each('view.name', $jobs, 'job', 'view.empty')
[!警告]
通过@each渲染的视图不会继承父视图中的变量。如果子视图需要这些变量,你应该改用@foreach和@include指令。
@once 指令(The @once Directive)
@once 指令允许你定义模板中某个部分,使其在每次渲染周期中只会被执行一次。这对于在页面的 header 中通过 stacks 推送某段 JavaScript 时尤其有用。例如,如果你在循环中渲染某个 组件,但希望只在首次渲染该组件时将 JavaScript 推送到 header,那么你可以这样使用:
@once
@push('scripts')
<script>
// Your custom JavaScript...
</script>
@endpush
@endonce
由于 @once 指令经常与 @push 或 @prepend 指令一起使用,因此为了方便,你可以使用 @pushOnce 和 @prependOnce 指令:
@pushOnce('scripts')
<script>
// Your custom JavaScript...
</script>
@endPushOnce
原生 PHP(Raw PHP)
在某些情况下,将 PHP 代码嵌入视图中是很有用的。你可以使用 Blade 的 @php 指令在模板中执行一段普通的 PHP:
@php
$counter = 1;
@endphp
或者,如果你只需要使用 PHP 来导入一个类,你可以使用 @use 指令:
@use('App\Models\Flight')
可以向 @use 指令提供第二个参数,用于为导入的类设置别名:
@use('App\Models\Flight', 'FlightModel')
如果你在同一个命名空间下有多个类,你可以将这些类的导入分组:
@use('App\Models\{Flight, Airport}')
@use 指令也支持导入 PHP 函数和常量,只需在导入路径前添加 function 或 const 修饰符:
@use(function App\Helpers\format_currency)
@use(const App\Constants\MAX_ATTEMPTS)
与类导入一样,函数和常量的别名也是支持的:
@use(function App\Helpers\format_currency, 'formatMoney')
@use(const App\Constants\MAX_ATTEMPTS, 'MAX_TRIES')
带有 function 和 const 修饰符的分组导入也被支持,允许你在一个指令中从同一命名空间导入多个符号:
@use(function App\Helpers\{format_currency, format_date})
@use(const App\Constants\{MAX_ATTEMPTS, DEFAULT_TIMEOUT})
注释(Comments)
Blade 也允许你在视图中定义注释。然而,与 HTML 注释不同,Blade 注释不会被包含在应用程序返回的 HTML 中:
{{-- This comment will not be present in the rendered HTML --}}
组件(Components)
组件和槽位提供了与 sections、layouts 和 includes 类似的好处;然而,有些人可能会发现组件和槽位的思维模型更容易理解。编写组件有两种方式:基于类的组件和匿名组件。
要创建一个基于类的组件,你可以使用 make:component Artisan 命令。为了说明如何使用组件,我们将创建一个简单的 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
上面的命令会在 app/View/Components/Forms 目录中创建一个 Input 组件,并且其视图会被放置在 resources/views/components/forms 目录中。
如果你想创建一个匿名组件(只有 Blade 模板而没有类的组件),你可以在执行 make:component 命令时使用 --view 标志:
php artisan make:component forms.input --view
上述命令会在 resources/views/components/forms/input.blade.php 创建一个 Blade 文件,该文件可以通过 <x-forms.input /> 作为组件进行渲染。
手动注册包组件(Manually Registering Package Components)
当为你自己的应用程序编写组件时,组件会在 app/View/Components 目录和 resources/views/components 目录中自动被发现。
然而,如果你正在构建一个使用 Blade 组件的包,则需要手动注册你的组件类及其 HTML 标签别名。通常你应该在包的服务提供者的 boot 方法中注册你的组件:
use Illuminate\Support\Facades\Blade;
/**
* 在启动过程中进行初始化配置
*/
public function boot(): void
{
Blade::component('package-alert', Alert::class);
}
一旦你的组件被注册,就可以使用它的标签别名进行渲染:
<x-package-alert/>
或者,你可以使用 componentNamespace 方法根据约定自动加载组件类。例如,一个 Nightshade 包可能有 Calendar 和 ColorPicker 组件,它们位于 Package\Views\Components 命名空间中:
use Illuminate\Support\Facades\Blade;
/**
* 在启动过程中进行初始化配置
*/
public function boot(): void
{
Blade::componentNamespace('Nightshade\\Views\\Components', 'nightshade');
}
这将允许使用包的供应商(vendor)命名空间,并通过 package-name:: 语法来使用包组件:
<x-nightshade::calendar />
<x-nightshade::color-picker />
Blade 将自动检测与该组件关联的类,方法是将组件名称转换为 PascalCase。子目录也可通过 “点” 语法得到支持。
渲染组件(Rendering Components)
要显示组件,你可以在某个 Blade 模板中使用 Blade 组件标签。Blade 组件标签以 x- 字符串开头,后跟组件类名的 kebab case 形式:
<x-alert/>
<x-user-profile/>
如果组件类在 app/View/Components 目录中嵌套得更深,你可以使用 . 字符来表示目录嵌套。例如,如果我们假设某个组件位于 app/View/Components/Inputs/Button.php,我们可以这样渲染它:
<x-inputs.button/>
如果你希望有条件地渲染你的组件,你可以在组件类上定义一个 shouldRender 方法。如果 shouldRender 方法返回 false,组件将不会被渲染:
use Illuminate\Support\Str;
/**
* 该组件是否应该被渲染
*/
public function shouldRender(): bool
{
return Str::length($this->message) > 0;
}
索引组件(Index Components)
有时组件属于某个组件组,你可能希望将相关组件分组放在一个目录中。例如,设想一个 “card” 组件具有以下类结构:
App\Views\Components\Card\Card
App\Views\Components\Card\Header
App\Views\Components\Card\Body
由于根 Card 组件嵌套在 Card 目录中,你可能会认为需要通过 <x-card.card> 来渲染该组件。然而,当组件的文件名与组件目录名相匹配时,Laravel 会自动假设该组件是“根”组件,并允许你在渲染时不必重复目录名称:
<x-card>
<x-card.header>...</x-card.header>
<x-card.body>...</x-card.body>
</x-card>
传递数据给组件(Passing Data to Components)
你可以通过 HTML 属性向 Blade 组件传递数据。硬编码的原始值可以通过简单的 HTML 属性字符串传递给组件。PHP 表达式和变量应通过使用 : 字符作为前缀的属性传递给组件:
<x-alert type="error" :message="$message"/>
你应该在组件的类构造函数中定义该组件的所有数据属性。组件上的所有公共属性都会自动提供给组件的视图。在组件的 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');
}
}
当你的组件被渲染时,你可以通过按名称输出变量来显示组件公共变量的内容:
<div class="alert alert-{{ $type }}">
{{ $message }}
</div>
大小写
组件构造函数的参数应使用 camelCase(小驼峰命名),而在 HTML 属性中引用参数名称时应该使用 kebab-case(短横线命名)。例如,给定以下组件构造函数:
/**
* 创建组件实例。
*/
public function __construct(
public string $alertType,
) {}
$alertType 参数可以这样提供给组件:
<x-alert alert-type="danger" />
在将属性传递给组件时,你也可以使用“短属性”语法。这通常很方便,因为属性名经常与其对应的变量名相匹配:
{{-- 短属性语法... --}}
<x-profile :$userId :$name />
{{-- 等同于... --}}
<x-profile :user-id="$userId" :name="$name" />
属性渲染转义
由于一些 JavaScript 框架(例如 Alpine.js)也使用冒号前缀的属性,你可以使用双冒号(::)前缀来告知 Blade 该属性不是一个 PHP 表达式。例如,给定以下组件:
<x-button ::class="{ danger: isDeleting }">
Submit
</x-button>
以下 HTML 将由 Blade 渲染:
<button :class="{ danger: isDeleting }">
Submit
</button>
组件方法
除了公共变量可在你的组件模板中使用之外,组件上的任何公共方法也可以被调用。例如,假设一个组件拥有一个 isSelected 方法:
/**
* 判断给定的选项是否为当前选中的选项。
*/
public function isSelected(string $option): bool
{
return $option === $this->selected;
}
你可以在组件模板中通过调用与方法名匹配的变量来执行该方法:
<option {{ $isSelected($value) ? 'selected' : '' }} value="{{ $value }}">
{{ $label }}
</option>
在组件类中访问属性和插槽
Blade 组件还允许你在类的 render 方法中访问组件名称、属性和插槽。然而,为了访问这些数据,你应该从组件的 render 方法返回一个闭包:
use Closure;
/**
* 获取代表该组件的视图 / 内容。
*/
public function render(): Closure
{
return function () {
return '<div {{ $attributes }}>Components content</div>';
};
}
你的组件 render 方法返回的闭包也可以接收一个 $data 数组作为其唯一参数。该数组将包含多个元素,为你提供有关组件的信息:
return function (array $data) {
// $data['componentName'];
// $data['attributes'];
// $data['slot'];
return '<div {{ $attributes }}>Components content</div>';
}
[!警告]
绝不应将$data数组中的元素直接嵌入到你的render方法返回的 Blade 字符串中,因为这样做可能会通过恶意属性内容导致远程代码执行。
componentName 等于在 HTML 标签中 x- 前缀之后所使用的名称。因此,<x-alert /> 的 componentName 将会是 alert。attributes 元素将包含 HTML 标签上存在的所有属性。slot 元素是一个 Illuminate\Support\HtmlString 实例,包含组件 slot 的内容。
该闭包应返回一个字符串。如果返回的字符串对应一个已存在的视图,则该视图会被渲染;否则,返回的字符串会被作为内联 Blade 视图进行解析。
额外的依赖项
如果你的组件需要从 Laravel 的服务容器解析的依赖,你可以在任何组件数据属性之前列出这些依赖,它们会被容器自动注入:
use App\Services\AlertCreator;
/**
* 创建组件实例
*/
public function __construct(
public AlertCreator $creator,
public string $type,
public string $message,
) {}
隐藏属性 / 方法
如果你想阻止某些公共方法或属性在组件模板中暴露,你可以将它们加入组件上的 $except 数组属性:
<?php
namespace App\View\Components;
use Illuminate\View\Component;
class Alert extends Component
{
/**
* 不应该暴露给组件模板的属性 / 方法。
*
* @var array
*/
protected $except = ['type'];
/**
* 创建组件实例。
*/
public function __construct(
public string $type,
) {}
}
组件属性
我们已经研究了如何向组件传递数据属性;然而,有时你可能需要指定额外的 HTML 属性,例如 class,而这些属性并不是组件运行所必需的数据。通常情况下,你希望将这些额外属性传递给组件模板的根元素。例如,想象我们要像下面这样渲染一个 alert 组件:
<x-alert type="error" :message="$message" class="mt-4"/>
所有不属于组件构造函数参数的属性都会被自动加入组件的“属性包”(attribute bag)中。这个属性包会通过 $attributes 变量自动提供给组件。所有这些属性都可以通过输出此变量在组件中渲染:
<div {{ $attributes }}>
<!-- Component content -->
</div>
[!警告]
当前不支持在组件标签中使用诸如@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">
<!-- Contents of the $message variable -->
</div>
条件合并类(conditionally merge classes)
有时你可能希望在某个条件为 true 时合并 CSS 类。你可以通过 class 方法实现,它接受一个类数组,其中数组键包含你想添加的类或类列表,而数组值是一个布尔表达式。如果数组元素的键是数字,它将始终包含在渲染后的 class 列表中:
<div {{ $attributes->class(['p-4', 'bg-red' => $hasError]) }}>
{{ $message }}
</div>
如果你需要向组件合并其他属性,你可以在 class 方法之后链式调用 merge 方法:
<button {{ $attributes->class(['p-4'])->merge(['type' => 'button']) }}>
{{ $slot }}
</button>
[!注意]
如果你需要在不应该接收合并属性的其他 HTML 元素上有条件地编译类,你可以使用 @class directive。
非 Class 属性合并
在合并不是 class 的属性时,传递给 merge 方法的值会被视为该属性的“默认”值。然而,不同于 class 属性,这些属性不会与注入的属性值合并,而是会被覆盖。例如,一个 button 组件的实现可能如下所示:
<button {{ $attributes->merge(['type' => 'button']) }}>
{{ $slot }}
</button>
若要渲染一个带有自定义 type 的按钮组件,可以在使用组件时指定它。如果未指定 type,则会使用默认的 button 类型:
<x-button type="submit">
Submit
</x-button>
此示例中渲染出的 button 组件的 HTML 将是:
<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 (string $value, string $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>Class attribute is present</div>
@endif
如果向 has 方法传递一个数组,该方法将判断这些属性是否全部存在于组件上:
@if ($attributes->has(['name', 'class']))
<div>All of the attributes are present</div>
@endif
hasAny 方法可用于判断给定属性中是否有任意一个存在于组件上:
@if ($attributes->hasAny(['href', ':href', 'v-bind:href']))
<div>One of the attributes is present</div>
@endif
你可以使用 get 方法来获取特定属性的值:
{{ $attributes->get('class') }}
only 方法可用于只获取具有给定键的属性:
{{ $attributes->only(['class']) }}
except 方法可用于获取除了给定键之外的所有属性:
{{ $attributes->except(['class']) }}
保留关键字
默认情况下,一些关键字被保留供 Blade 内部使用,以便渲染组件。以下关键字不能在你的组件中被定义为公共属性或方法名称:
datarenderresolveViewshouldRenderviewwithAttributeswithName
Slots(插槽)
你经常需要通过“slots”(插槽)向组件传递额外的内容。组件插槽通过输出 $slot 变量来渲染。为了探讨这一概念,我们假设一个 alert 组件具有以下标记:
<!-- /resources/views/components/alert.blade.php -->
<div class="alert alert-danger">
{{ $slot }}
</div>
我们可以通过向组件中注入内容的方式传递 slot 的内容:
<x-alert>
<strong>Whoops!</strong> Something went wrong!
</x-alert>
有时,一个组件可能需要在组件的不同位置渲染多个不同的插槽。让我们修改 alert 组件,使其支持注入一个 “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 注释的“实际”内容:
@if ($slot->hasActualContent())
The scope has non-comment content.
@endif
作用域插槽(Scoped Slots)
如果你使用过诸如 Vue 之类的 JavaScript 框架,你可能熟悉“作用域插槽”,它允许你在插槽内访问该组件的数据或方法。你可以在 Laravel 中通过在组件上定义公共方法或属性,并在插槽内通过 $component 变量访问该组件,从而实现类似的行为。在此示例中,我们假设 x-alert 组件的组件类中定义了一个公共的 formatAlert 方法:
<x-alert>
<x-slot:title>
{{ $component->formatAlert('Server Error') }}
</x-slot>
<strong>Whoops!</strong> Something went wrong!
</x-alert>
插槽属性(Slot Attributes)
与 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 属性。有关如何与属性交互的更多信息,请参考组件属性文档:
@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>
内联组件视图(Inline Component Views)
对于非常小的组件,同时管理组件类与组件的视图模板可能会显得繁琐。出于这个原因,你可以直接从 render 方法返回组件的标记(markup):
/**
* 获取用于呈现该组件的视图或内容
*/
public function render(): string
{
return <<<'blade'
<div class="alert alert-danger">
{{ $slot }}
</div>
blade;
}
生成内联视图组件
要创建一个渲染内联视图的组件,你可以在执行 make:component 命令时使用 inline 选项:
php artisan make:component Alert --inline
动态组件
有时你可能需要渲染一个组件,但在运行时之前无法知道应该渲染哪个组件。在这种情况下,你可以使用 Laravel 内置的 dynamic-component 组件,根据运行时的值或变量来渲染组件:
// $componentName = "secondary-button";
<x-dynamic-component :component="$componentName" class="mt-4" />
手动注册组件
[!警告]
以下关于手动注册组件的文档主要适用于那些编写包含视图组件的 Laravel 包的人。如果你没有在编写包,那么组件文档的这一部分可能与你无关。
当为你自己的应用程序编写组件时,组件会在 app/View/Components 目录和 resources/views/components 目录中自动被发现。
然而,如果你正在构建一个使用 Blade 组件的包,或者将组件放在非常规的目录中,那么你将需要手动注册你的组件类及其 HTML 标签别名,以便 Laravel 知道在哪里找到该组件。你通常应在包的服务提供者的 boot 方法中注册你的组件:
use Illuminate\Support\Facades\Blade;
use VendorPackage\View\Components\AlertComponent;
/**
* 启动(初始化)你的包的服务。
*/
public function boot(): void
{
Blade::component('package-alert', AlertComponent::class);
}
注册组件后,就可以通过它的标签别名进行渲染:
<x-package-alert/>
自动加载包组件
或者,你可以使用 componentNamespace 方法按约定来自动加载组件类。例如,一个 Nightshade 包可能有 Calendar 和 ColorPicker 组件,它们位于 Package\Views\Components 命名空间中:
use Illuminate\Support\Facades\Blade;
/**
* 启动(初始化)你的包的服务。
*/
public function boot(): void
{
Blade::componentNamespace('Nightshade\\Views\\Components', 'nightshade');
}
这将允许使用 package-name:: 语法,通过它们的供应商命名空间来使用包组件:
<x-nightshade::calendar />
<x-nightshade::color-picker />
Blade 将通过将组件名称转换为帕斯卡命名(PascalCase)来自动检测与此组件关联的类。子目录也可以通过使用 “dot”(点)符号来支持。
匿名组件
类似于内联组件,匿名组件提供了一种通过单个文件管理组件的机制。然而,匿名组件使用单个视图文件,并且没有关联的类。要定义一个匿名组件,你只需要将一个 Blade 模板放置在 resources/views/components 目录下。例如,假设你在 resources/views/components/alert.blade.php 中定义了一个组件,你可以像这样简单地渲染它:
<x-alert/>
你可以使用 . 字符来表示组件位于 components 目录更深层的嵌套中。例如,假设组件定义在 resources/views/components/inputs/button.blade.php,你可以像这样渲染它:
<x-inputs.button/>
匿名索引组件
有时,当一个组件由多个 Blade 模板组成时,你可能希望将该组件的模板分组放在一个单独的目录中。例如,想象一个具有以下目录结构的 “accordion” 组件:
/resources/views/components/accordion.blade.php
/resources/views/components/accordion/item.blade.php
这个目录结构允许你像这样渲染 accordion 组件及其 item:
<x-accordion>
<x-accordion.item>
...
</x-accordion.item>
</x-accordion>
然而,为了通过 x-accordion 来渲染 accordion 组件,我们被迫将 “index” accordion 组件模板放在 resources/views/components 目录下,而不是和其他 accordion 相关模板一起嵌套在 accordion 目录中。
值得庆幸的是,Blade 允许你在组件目录内放置一个与该组件目录同名的文件。当这个模板存在时,即使它嵌套在目录中,它仍然可以作为组件的 “root”(根)元素进行渲染。因此,我们可以继续使用上面示例中的相同 Blade 语法;不过,我们将像下面这样调整我们的目录结构:
/resources/views/components/accordion/accordion.blade.php
/resources/views/components/accordion/item.blade.php
数据属性 / 属性
由于匿名组件没有任何关联类,你可能会想知道:如何区分哪些数据应作为变量传递给组件,哪些属性应放入组件的属性包中?
你可以在组件的 Blade 模板顶部使用 @props 指令来指定哪些属性应被视为数据变量。组件上的所有其他属性都可以通过组件的属性包获取。如果你希望为某个数据变量提供默认值,你可以使用变量名作为数组键,默认值作为数组值:
<!-- /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"/>
访问父级数据
有时你可能希望在子组件内部访问父组件的数据。在这些情况下,你可以使用 @aware 指令。例如,假设我们正在构建一个由父级 <x-menu> 和子级 <x-menu.item> 组成的复杂菜单组件:
<x-menu color="purple">
<x-menu.item>...</x-menu.item>
<x-menu.item>...</x-menu.item>
</x-menu>
<x-menu> 组件可能具有如下实现:
<!-- /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> 内可用:
<!-- /resources/views/components/menu/item.blade.php -->
@aware(['color' => 'gray'])
<li {{ $attributes->merge(['class' => 'text-'.$color.'-800']) }}>
{{ $slot }}
</li>
[!警告]
@aware指令无法访问未通过 HTML 属性显式传递给父组件的数据。默认的@props值如果没有显式传递给父组件,将无法通过@aware指令访问。
匿名组件路径
如前所述,匿名组件通常通过将 Blade 模板放置在 resources/views/components 目录内来定义。然而,你可能偶尔希望在默认路径之外,向 Laravel 注册其他匿名组件路径。
anonymousComponentPath 方法的第一个参数接受匿名组件所在位置的“路径”,第二个参数则是可选的“命名空间”,用于指定组件应归属到哪个命名空间下。通常,这个方法应该在应用程序某个服务提供者的 boot 方法中调用:
/**
* 启动应用程序的所有服务。
*/
public function boot(): void
{
Blade::anonymousComponentPath(__DIR__.'/../components');
}
当组件路径在注册时未指定前缀(如上例所示),它们在你的 Blade 组件中也可以在没有对应前缀的情况下被渲染。例如,如果在上面注册的路径中存在一个 panel.blade.php 组件,它可以像这样渲染:
<x-panel />
可以将前缀“命名空间”作为第二个参数传递给 anonymousComponentPath 方法:
Blade::anonymousComponentPath(__DIR__.'/../components', 'dashboard');
当提供了前缀时,该“命名空间”中的组件在渲染时需要将命名空间前缀添加到组件名称前:
<x-dashboard::panel />
构建布局
使用组件构建布局
大多数 Web 应用程序在各个页面之间保持相同的通用布局。如果我们不得不在每个创建的视图中重复整个布局 HTML,那将会非常繁琐并且难以维护。幸运的是,我们可以将这个布局定义为一个单独的 Blade 组件,然后在整个应用程序中使用它。
定义布局组件
例如,假设我们正在构建一个 “todo” 列表应用程序。我们可能会定义一个如下所示的 layout 组件:
<!-- resources/views/components/layout.blade.php -->
<html>
<head>
<title>{{ $title ?? 'Todo Manager' }}</title>
</head>
<body>
<h1>Todos</h1>
<hr/>
{{ $slot }}
</body>
</html>
应用 Layout 组件
一旦 layout 组件被定义,我们就可以创建一个使用该组件的 Blade 视图。在这个示例中,我们将定义一个简单的视图,用于显示我们的任务列表:
<!-- resources/views/tasks.blade.php -->
<x-layout>
@foreach ($tasks as $task)
<div>{{ $task }}</div>
@endforeach
</x-layout>
请记住,注入到组件中的内容将被提供给我们 layout 组件内的默认 $slot 变量。正如你可能已经注意到的,我们的 layout 也会在提供 $title 插槽时使用它;否则,它会显示一个默认标题。我们可以在任务列表视图中使用在组件文档中讨论过的标准插槽语法来注入一个自定义标题:
<!-- 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 视图:
use App\Models\Task;
Route::get('/tasks', function () {
return view('tasks', ['tasks' => Task::all()]);
});
使用模板继承的布局
定义一个布局
布局也可以通过“模板继承”来创建。这是在 components 引入之前构建应用程序的主要方式。
要开始,先来看一个简单的示例。首先,我们将查看一个页面布局。由于大多数 Web 应用程序在多个页面之间保持相同的整体布局,因此将这个布局定义为单个 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 指令用于显示某个指定 section 的内容。
现在我们已经为应用程序定义了一个布局,让我们定义一个继承该布局的子页面。
扩展布局
定义子视图时,使用 @extends Blade 指令来指定子视图应当“继承”哪个布局。扩展 Blade 布局的视图可以通过 @section 指令向布局的各个 section 注入内容。记住,如上例所示,这些 section 的内容将通过 @yield 在布局中显示:
<!-- 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 section 使用了 @parent 指令来追加(而不是覆盖)布局的侧边栏内容。在视图渲染时,@parent 指令将被布局中的内容替换。
[!注意]
与前一个示例相反,这个sidebarsection 使用@endsection结束,而不是@show。@endsection指令只会定义一个 section,而@show会定义并立即输出该 section。
@yield 指令也可以接受一个默认值作为它的第二个参数。如果被输出的 section 未定义,则会渲染这个默认值:
@yield('content', 'Default content')
表单(Forms)
CSRF 字段
在应用程序中定义 HTML 表单时,你应该在表单中包含一个隐藏的 CSRF 令牌字段,以便 CSRF 保护 中间件可以验证请求。你可以使用 @csrf Blade 指令来生成这个令牌字段:
<form method="POST" action="/profile">
@csrf
...
</form>
方法字段(Method Field)
由于 HTML 表单无法发出 PUT、PATCH 或 DELETE 请求,你将需要添加一个隐藏的 _method 字段来伪造这些 HTTP 动词。@method Blade 指令可以为你创建这个字段:
<form action="/foo/bar" method="POST">
@method('PUT')
...
</form>
验证错误(Validation Errors)
@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">Email address</label>
<input
id="email"
type="email"
class="@error('email') is-invalid @else is-valid @enderror"
/>
你可以将特定错误包(error bag)的名称作为第二个参数传递给 @error 指令,以便在包含多个表单的页面上获取验证错误消息:
<!-- /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
堆栈(Stacks)
Blade 允许你将内容推送到命名堆栈中,这些堆栈可以在其他视图或布局中的某个位置被渲染。这在指定子视图所需的任意 JavaScript 库时特别有用:
@push('scripts')
<script src="/example.js"></script>
@endpush
如果你希望仅在某个布尔表达式的结果为 true 时才执行 @push,你可以使用 @pushIf 指令:
@pushIf($shouldPush, 'scripts')
<script src="/example.js"></script>
@endPushIf
你可以根据需要多次向堆栈推送内容。要渲染整个堆栈的内容,将堆栈的名称传递给 @stack 指令:
<head>
<!-- Head Contents -->
@stack('scripts')
</head>
如果你想将内容添加到堆栈的开头,你应该使用 @prepend 指令:
@push('scripts')
This will be second...
@endpush
// Later...
@prepend('scripts')
This will be first...
@endprepend
服务注入(Service Injection)
@inject 指令可用于从 Laravel 服务容器中检索服务。传递给 @inject 的第一个参数是该服务将被放入的变量名,而第二个参数是你希望解析的服务的类名或接口名:
@inject('metrics', 'App\Services\MetricsService')
<div>
Monthly Revenue: {{ $metrics->monthlyRevenue() }}.
</div>
渲染内嵌 Blade 模板(Rendering Inline Blade Templates)
有时你可能需要将原始 Blade 模板字符串转换为有效的 HTML。你可以使用 Blade facade 提供的 render 方法来完成此操作。render 方法接受 Blade 模板字符串,以及一个可选的数据数组用于提供给模板:
use Illuminate\Support\Facades\Blade;
return Blade::render('Hello, {{ $name }}', ['name' => 'Julian Bashir']);
Laravel 通过将内联 Blade 模板写入 storage/framework/views 目录来渲染它们。如果你希望 Laravel 在渲染 Blade 模板之后删除这些临时文件,你可以向该方法提供 deleteCachedView 参数:
return Blade::render(
'Hello, {{ $name }}',
['name' => 'Julian Bashir'],
deleteCachedView: true
);
渲染 Blade 片段(Rendering Blade Fragments)
在使用诸如 Turbo 和 htmx 等前端框架时,有时你可能只需要在 HTTP 响应中返回 Blade 模板的一部分。Blade “片段”(fragments)允许你做到这一点。要开始,请将 Blade 模板的一部分放在 @fragment 和 @endfragment 指令之间:
@fragment('user-list')
<ul>
@foreach ($users as $user)
<li>{{ $user->name }}</li>
@endforeach
</ul>
@endfragment
然后,在渲染使用此模板的视图时,你可以调用 fragment 方法来指定只有所给片段应该包含在发出的 HTTP 响应中:
return view('dashboard', ['users' => $users])->fragment('user-list');
fragmentIf 方法允许你根据给定条件有条件地返回视图的一个片段。否则,将返回整个视图:
return view('dashboard', ['users' => $users])
->fragmentIf($request->hasHeader('HX-Request'), 'user-list');
fragments 和 fragmentsIf 方法允许你在响应中返回多个视图片段。这些片段将被连接在一起:
view('dashboard', ['users' => $users])
->fragments(['user-list', 'comment-list']);
view('dashboard', ['users' => $users])
->fragmentsIf(
$request->hasHeader('HX-Request'),
['user-list', 'comment-list']
);
扩展 Blade(Extending 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
{
/**
* 在应用启动时执行必要的服务设置。
*/
public function register(): void
{
// ...
}
/**
* 在应用启动时执行必要的服务设置。
*/
public function boot(): void
{
Blade::directive('datetime', function (string $expression) {
return "<?php echo ($expression)->format('m/d/Y H:i'); ?>";
});
}
}
正如你所看到的,我们会将 format 方法链接到传递给指令的任何表达式上。所以,在这个示例中,该指令生成的最终 PHP 将是:
<?php echo ($var)->format('m/d/Y H:i'); ?>
[!警告]
更新 Blade 指令的逻辑之后,你需要删除所有已缓存的 Blade 视图。这些缓存的 Blade 视图可以通过view:clearArtisan 命令移除。
自定义 Echo 处理器(Custom Echo Handlers)
如果你尝试在 Blade 中“echo”一个对象,该对象的 __toString 方法将会被调用。__toString 方法是 PHP 内置的“魔术方法”之一。然而,有时你可能无法控制某个类的 __toString 方法,例如当你正在处理的类属于一个第三方库时。
在这些情况下,Blade 允许你为该特定类型的对象注册自定义回显处理程序。为此,你应该调用 Blade 的 stringable 方法。 stringable 方法接受一个闭包。这个闭包类型应该提示它负责呈现的对象的类型。通常,stringable 方法应该在应用程序的 AppServiceProvider 类的 boot 方法中调用:
use Illuminate\Support\Facades\Blade;
use Money\Money;
/**
* 引导任何应用程序服务。
*/
public function boot(): void
{
Blade::stringable(function (Money $money) {
return $money->formatTo('en_GB');
});
}
定义自定义回显处理程序后,您可以简单地回显 Blade 模板中的对象:
Cost: {{ $money }}
自定义 if 声明
编程自定义指令有时比定义简单的自定义条件语句更复杂。因此,Blade 提供了一个 Blade::if 方法,它允许你使用闭包快速定义自定义条件指令。例如,让我们定义一个自定义条件,检查应用程序配置的默认「磁盘」。我们可以在 AppServiceProvider 的 boot 方法中这样做:
use Illuminate\Support\Facades\Blade;
/**
* 引导任何应用程序服务。
*/
public function boot(): void
{
Blade::if('disk', function (string $value) {
return config('filesystems.default') === $value;
});
}
一旦定义了自定义条件,就可以在模板中使用它:
@disk('local')
<!-- 应用程序正在使用本地磁盘... -->
@elsedisk('s3')
<!-- 应用程序正在使用 s3 磁盘... -->
@else
<!-- 应用程序正在使用其他磁盘... -->
@enddisk
@unlessdisk('local')
<!-- 应用程序未使用本地磁盘... -->
@enddisk
本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
Laravel 12 中文文档
关于 LearnKu
推荐文章: