Blade 模板
- Introduction
- Displaying Data
- Blade Directives
- Components
- Anonymous Components
- Building Layouts
- Forms
- Stacks
- Service Injection
- Rendering Inline Blade Templates
- Rendering Blade Fragments
- Extending Blade
介绍
Blade 是 Laravel 内置的一款简洁而强大的模板引擎。与其他 PHP 模板引擎不同,Blade 并不限制您在模板中使用纯 PHP 代码。事实上,所有 Blade 模板都会被编译成纯 PHP 代码并缓存,直到被修改为止,这意味着 Blade 几乎不会给您的应用程序带来任何开销。Blade 模板文件使用.blade.php
文件扩展名,通常存储在resources/views
目录中。
Blade 视图可以通过全局view
Helpers从路由或控制器返回。当然,正如视图文档中提到的,可以使用Helpers的第二个参数将数据传递给 Blade 视图view
:
1Route::get('/', function () {2 return view('greeting', ['name' => 'Finn']);3});
配备 Livewire 的增压刀片
想要将 Blade 模板提升到一个新的高度并轻松构建动态界面吗?不妨看看Laravel Livewire。Livewire允许你编写 Blade 组件,并添加动态功能,而这些功能通常只能通过 React 或 Vue 等前端框架才能实现。Livewire 提供了一种构建现代响应式前端的绝佳方法,无需像许多 JavaScript 框架那样处理复杂的客户端渲染或构建步骤。
显示数据
你可以通过将变量括在花括号中来显示传递给 Blade 视图的数据。例如,给定以下路由:
1Route::get('/', function () {2 return view('welcome', ['name' => 'Samantha']);3});
name
您可以像这样显示变量的内容:
1Hello, {{ $name }}.
Blade 的{{ }}
echo 语句会自动通过 PHP 的htmlspecialchars
函数发送,以防止 XSS 攻击。
您不仅可以显示传递给视图的变量内容,还可以回显任何 PHP 函数的结果。实际上,您可以将任何 PHP 代码放入 Blade 的回显语句中:
1The current UNIX timestamp is {{ time() }}.
HTML实体编码
默认情况下,Blade(以及 Laravele
函数)将对 HTML 实体进行双重编码。如果您想禁用双重编码,请Blade::withoutDoubleEncoding
在boot
方法中调用该方法AppServiceProvider
:
1<?php 2 3namespace App\Providers; 4 5use Illuminate\Support\Facades\Blade; 6use Illuminate\Support\ServiceProvider; 7 8class AppServiceProvider extends ServiceProvider 9{10 /**11 * Bootstrap any application services.12 */13 public function boot(): void14 {15 Blade::withoutDoubleEncoding();16 }17}
显示未转义的数据
默认情况下,Blade{{ }}
语句会自动通过 PHPhtmlspecialchars
函数发送,以防止 XSS 攻击。如果您不希望数据被转义,可以使用以下语法:
1Hello, {!! $name !!}.
回显应用程序用户提供的内容时务必小心。通常,应使用转义的双花括号语法来防止显示用户提供的数据时遭受 XSS 攻击。
Blade 和 JavaScript 框架
由于许多 JavaScript 框架也使用“花括号”来指示给定的表达式应显示在浏览器中,因此您可以使用该@
符号来通知 Blade 渲染引擎该表达式应保持不变。例如:
1<h1>Laravel</h1>2 3Hello, @{{ name }}.
在这个例子中,@
符号将被 Blade 删除;但是,{{ name }}
表达式将保持不变,从而允许它由您的 JavaScript 框架呈现。
该@
符号也可用于转义 Blade 指令:
1{{-- Blade template --}}2@@if()3 4<!-- HTML output -->5@if()
渲染 JSON
有时,你可能会将一个数组传递给视图,以便将其渲染为 JSON,从而初始化 JavaScript 变量。例如:
1<script>2 var app = <?php echo json_encode($array); ?>;3</script>
json_encode
但是,您可以使用 method 指令,而不是手动调用Illuminate\Support\Js::from
。该from
方法接受与 PHP 函数相同的参数json_encode
;但是,它会确保生成的 JSON 经过正确转义,以便包含在 HTML 引号中。该from
方法将返回一个JSON.parse
JavaScript 语句字符串,该语句会将给定的对象或数组转换为有效的 JavaScript 对象:
1<script>2 var app = {{ Illuminate\Support\Js::from($array) }};3</script>
Laravel 应用程序骨架的最新版本包含一个Js
外观,可在 Blade 模板中方便地访问此功能:
1<script>2 var app = {{ Js::from($array) }};3</script>
您应该仅使用该Js::from
方法将现有变量渲染为 JSON。Blade 模板基于正则表达式,尝试将复杂的表达式传递给指令可能会导致意外失败。
指令@verbatim
如果要在模板的大部分内容中显示 JavaScript 变量,则可以将 HTML 包装在@verbatim
指令中,这样就不必在每个 Blade echo 语句前加上@
符号:
1@verbatim2 <div class="container">3 Hello, {{ name }}.4 </div>5@endverbatim
Blade 指令
除了模板继承和数据显示之外,Blade 还提供了常见 PHP 控制结构(例如条件语句和循环)的便捷快捷方式。这些快捷方式提供了一种非常简洁、干净的方式来处理 PHP 控制结构,同时又保留了 PHP 对应结构的熟悉感。
If 语句
您可以if
使用@if
、@elseif
、@else
和@endif
指令构建语句。这些指令的功能与 PHP 中的指令相同:
1@if (count($records) === 1)2 I have one record!3@elseif (count($records) > 1)4 I have multiple records!5@else6 I don't have any records!7@endif
为了方便起见,Blade 还提供了一个@unless
指令:
1@unless (Auth::check())2 You are not signed in.3@endunless
除了已经讨论过的条件指令之外,@isset
和@empty
指令还可以用作各自 PHP 函数的便捷快捷方式:
1@isset($records)2 // $records is defined and is not null...3@endisset4 5@empty($records)6 // $records is "empty"...7@endempty
身份验证指令
和@auth
指令@guest
可用于快速确定当前用户是否已通过身份验证或者是访客:
1@auth2 // The user is authenticated...3@endauth4 5@guest6 // The user is not authenticated...7@endguest
@auth
如果需要,您可以指定在使用和指令时应检查的身份验证保护@guest
:
1@auth('admin')2 // The user is authenticated...3@endauth4 5@guest('admin')6 // The user is not authenticated...7@endguest
环境指令
您可以使用该指令检查应用程序是否在生产环境中运行@production
:
1@production2 // Production specific content...3@endproduction
或者,您可以使用指令确定应用程序是否在特定环境中运行@env
:
1@env('staging')2 // The application is running in "staging"...3@endenv4 5@env(['staging', 'production'])6 // The application is running in "staging" or "production"...7@endenv
章节指令
您可以使用指令确定模板继承部分是否具有内容@hasSection
:
1@hasSection('navigation')2 <div class="pull-right">3 @yield('navigation')4 </div>5 6 <div class="clearfix"></div>7@endif
您可以使用sectionMissing
指令来确定某个部分是否没有内容:
1@sectionMissing('navigation')2 <div class="pull-right">3 @include('default-navigation')4 </div>5@endif
会话指令
该@session
指令可用于判断会话值是否存在。如果会话值存在,则会执行@session
和指令中的模板内容。在指令内容中,您可以回显变量以显示会话值:@endsession
@session
$value
1@session('status')2 <div class="p-4 bg-green-100">3 {{ $value }}4 </div>5@endsession
Switch 语句
可以使用@switch
、@case
、@break
和指令构造 Switch 语句@default
:@endswitch
1@switch($i) 2 @case(1) 3 First case... 4 @break 5 6 @case(2) 7 Second case... 8 @break 9 10 @default11 Default case...12@endswitch
循环
除了条件语句之外,Blade 还提供了用于 PHP 循环结构的简单指令。同样,这些指令的功能与 PHP 中的指令完全相同:
1@for ($i = 0; $i < 10; $i++) 2 The current value is {{ $i }} 3@endfor 4 5@foreach ($users as $user) 6 <p>This is user {{ $user->id }}</p> 7@endforeach 8 9@forelse ($users as $user)10 <li>{{ $user->name }}</li>11@empty12 <p>No users</p>13@endforelse14 15@while (true)16 <p>I'm looping forever.</p>17@endwhile
在循环迭代时foreach
,您可以使用循环变量来获取有关循环的有价值的信息,例如您是处于循环的第一次迭代还是最后一次迭代。
@continue
使用循环时,您还可以跳过当前迭代或使用and指令结束循环@break
:
1@foreach ($users as $user) 2 @if ($user->type == 1) 3 @continue 4 @endif 5 6 <li>{{ $user->name }}</li> 7 8 @if ($user->number == 5) 9 @break10 @endif11@endforeach
您还可以在指令声明中包含继续或中断条件:
1@foreach ($users as $user)2 @continue($user->type == 1)3 4 <li>{{ $user->name }}</li>5 6 @break($user->number == 5)7@endforeach
循环变量
在循环迭代过程中foreach
,$loop
循环内部会有一个变量可用。该变量提供一些有用的信息,例如当前循环索引以及这是循环的第一次迭代还是最后一次迭代:
1@foreach ($users as $user) 2 @if ($loop->first) 3 This is the first iteration. 4 @endif 5 6 @if ($loop->last) 7 This is the last iteration. 8 @endif 9 10 <p>This is user {{ $user->id }}</p>11@endforeach
如果您处于嵌套循环中,则可以$loop
通过以下parent
属性访问父循环的变量:
1@foreach ($users as $user)2 @foreach ($user->posts as $post)3 @if ($loop->parent->first)4 This is the first iteration of the parent loop.5 @endif6 @endforeach7@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 类字符串。该指令接受一个类数组作为参数,数组键包含要添加的类,值则为布尔表达式。如果数组元素的键为数字,则该数字将始终包含在渲染的类列表中:
1@php 2 $isActive = false; 3 $hasError = true; 4@endphp 5 6<span @class([ 7 'p-4', 8 'font-bold' => $isActive, 9 'text-gray-500' => ! $isActive,10 'bg-red' => $hasError,11])></span>12 13<span class="p-4 text-gray-500 bg-red"></span>
同样,该@style
指令可用于有条件地向 HTML 元素添加内联 CSS 样式:
1@php 2 $isActive = true; 3@endphp 4 5<span @style([ 6 'background-color: red', 7 'font-weight: bold' => $isActive, 8])></span> 9 10<span style="background-color: red; font-weight: bold;"></span>
附加属性
为了方便起见,您可以使用该@checked
指令轻松指示给定 HTML 复选框输入是否“选中”。checked
如果提供的条件满足以下条件,则该指令将回显true
:
1<input2 type="checkbox"3 name="active"4 value="active"5 @checked(old('active', $user->active))6/>
同样,该@selected
指令可用于指示是否应“选择”给定的选择选项:
1<select name="version">2 @foreach ($product->versions as $version)3 <option value="{{ $version }}" @selected(old('version') == $version)>4 {{ $version }}5 </option>6 @endforeach7</select>
此外,该@disabled
指令还可用于指示是否应“禁用”给定元素:
1<button type="submit" @disabled($errors->isNotEmpty())>Submit</button>
此外,该@readonly
指令可用于指示给定元素是否应为“只读”:
1<input2 type="email"3 name="email"4 value="email@laravel.com"5 @readonly($user->isNotAdmin())6/>
此外,该@required
指令还可用于指示给定元素是否应为“必需”:
1<input2 type="text"3 name="title"4 value="title"5 @required($user->isAdmin())6/>
包含子视图
虽然您可以自由使用该@include
指令,但 Blade组件提供了类似的功能,并且比该@include
指令具有多种优势,例如数据和属性绑定。
Blade@include
指令允许你在另一个视图中引入一个 Blade 视图。所有父视图可用的变量,被引入的视图同样可以使用:
1<div>2 @include('shared.errors')3 4 <form>5 <!-- Form Contents -->6 </form>7</div>
尽管包含的视图将继承父视图中可用的所有数据,但您也可以传递一个应该提供给包含视图的附加数据数组:
1@include('view.name', ['status' => 'complete'])
如果你尝试访问@include
一个不存在的视图,Laravel 将会抛出错误。如果你想要包含一个可能存在也可能不存在的视图,你应该使用以下@includeIf
指令:
1@includeIf('view.name', ['status' => 'complete'])
如果您想要@include
查看给定的布尔表达式的计算结果是否为true
或false
,则可以使用@includeWhen
and@includeUnless
指令:
1@includeWhen($boolean, 'view.name', ['status' => 'complete'])2 3@includeUnless($boolean, 'view.name', ['status' => 'complete'])
要包含给定视图数组中存在的第一个视图,您可以使用该includeFirst
指令:
1@includeFirst(['custom.admin', 'admin'], ['status' => 'complete'])
您应该避免在 Blade 视图中使用__DIR__
和__FILE__
常量,因为它们将引用缓存的、已编译视图的位置。
渲染集合的视图
您可以使用 Blade@each
指令将循环和包含组合成一行:
1@each('view.name', $jobs, 'job')
该@each
指令的第一个参数是要为数组或集合中的每个元素渲染的视图。第二个参数是要迭代的数组或集合,第三个参数是将在视图中分配给当前迭代的变量名。例如,如果您要迭代一个数组jobs
,通常您需要将每个作业作为job
视图中的变量来访问。当前迭代的数组键将作为key
视图中的变量使用。
您还可以向该指令传递第四个参数@each
。此参数决定了当给定数组为空时将渲染的视图。
1@each('view.name', $jobs, 'job', 'view.empty')
通过 渲染的视图@each
不会从父视图继承变量。如果子视图需要这些变量,则应使用@foreach
和@include
指令。
指令@once
该@once
指令允许您定义模板中每个渲染周期仅执行一次的部分。这对于使用堆栈将给定的 JavaScript 代码推送到页面标题中非常有用。例如,如果您在循环中渲染给定组件,您可能希望仅在组件首次渲染时将 JavaScript 代码推送到标题中:
1@once2 @push('scripts')3 <script>4 // Your custom JavaScript...5 </script>6 @endpush7@endonce
由于指令通常与或指令@once
一起使用,因此可以使用和指令来方便您使用:@push
@prepend
@pushOnce
@prependOnce
1@pushOnce('scripts')2 <script>3 // Your custom JavaScript...4 </script>5@endPushOnce
原始 PHP
在某些情况下,将 PHP 代码嵌入到视图中很有用。您可以使用 Blade@php
指令在模板中执行一段纯 PHP 代码块:
1@php2 $counter = 1;3@endphp
或者,如果您只需要使用 PHP 导入一个类,您可以使用该@use
指令:
1@use('App\Models\Flight')
可以向该@use
指令提供第二个参数来为导入的类指定别名:
1@use('App\Models\Flight', 'FlightModel')
如果同一个命名空间内有多个类,则可以对这些类的导入进行分组:
1@use('App\Models\{Flight, Airport}')
该@use
指令还支持通过在导入路径前添加function
或const
修饰符来导入 PHP 函数和常量:
1@use(function App\Helpers\format_currency)2@use(const App\Constants\MAX_ATTEMPTS)
就像类导入一样,函数和常量也支持别名:
1@use(function App\Helpers\format_currency, 'formatMoney')2@use(const App\Constants\MAX_ATTEMPTS, 'MAX_TRIES')
函数和 const 修饰符也支持分组导入,从而允许您在单个指令中从同一命名空间导入多个符号:
1@use(function App\Helpers\{format_currency, format_date})2@use(const App\Constants\{MAX_ATTEMPTS, DEFAULT_TIMEOUT})
评论
Blade 还允许你在视图中定义注释。但是,与 HTML 注释不同,Blade 注释不会包含在应用程序返回的 HTML 中:
1{{-- This comment will not be present in the rendered HTML --}}
成分
组件和插槽与部分、布局和包含项提供类似的好处;然而,有些人可能觉得组件和插槽的思维模型更容易理解。编写组件有两种方法:基于类的组件和匿名组件。
要创建基于类的组件,可以使用make:component
Artisan 命令。为了说明如何使用组件,我们将创建一个简单的Alert
组件。该make:component
命令会将组件放置在以下app/View/Components
目录中:
1php artisan make:component Alert
该make:component
命令还将为组件创建一个视图模板。该视图模板将被放置在resources/views/components
目录中。在为自己的应用程序编写组件时,组件会在app/View/Components
目录和resources/views/components
目录中自动发现,因此通常无需进一步注册组件。
您还可以在子目录中创建组件:
1php artisan make:component Forms/Input
上面的命令将Input
在目录中创建一个组件app/View/Components/Forms
,并且视图将放置在该resources/views/components/forms
目录中。
如果您想创建一个匿名组件(仅具有 Blade 模板而没有类的组件),您可以--view
在调用make:component
命令时使用该标志:
1php artisan make:component forms.input --view
上述命令将创建一个 Blade 文件,resources/views/components/forms/input.blade.php
该文件可以通过 呈现为组件<x-forms.input />
。
手动注册包组件
当为自己的应用程序编写组件时,会在app/View/Components
目录和resources/views/components
目录内自动发现组件。
但是,如果你正在构建一个使用 Blade 组件的包,则需要手动注册组件类及其 HTML 标签别名。通常,你应该在boot
包的服务提供者方法中注册组件:
1use Illuminate\Support\Facades\Blade;2 3/**4 * Bootstrap your package's services.5 */6public function boot(): void7{8 Blade::component('package-alert', Alert::class);9}
一旦你的组件被注册,它就可以使用其标签别名进行渲染:
1<x-package-alert/>
或者,你也可以按照约定使用该componentNamespace
方法自动加载组件类。例如,一个Nightshade
包可能包含Calendar
位于ColorPicker
命名空间内的组件Package\Views\Components
:
1use Illuminate\Support\Facades\Blade;2 3/**4 * Bootstrap your package's services.5 */6public function boot(): void7{8 Blade::componentNamespace('Nightshade\\Views\\Components', 'nightshade');9}
这将允许使用以下package-name::
语法通过供应商命名空间使用包组件:
1<x-nightshade::calendar />2<x-nightshade::color-picker />
Blade 会自动检测链接到此组件的类,方法是使用 Pascal 大小写格式对组件名称进行排序。子目录也支持使用“点”符号。
渲染组件
要显示组件,你可以在 Blade 模板中使用 Blade 组件标签。Blade 组件标签以字符串开头,x-
后跟组件类的短横线命名名称:
1<x-alert/>2 3<x-user-profile/>
如果组件类在app/View/Components
目录中嵌套得更深,可以使用.
字符来指示目录嵌套。例如,假设某个组件位于app/View/Components/Inputs/Button.php
,则可以像这样渲染它:
1<x-inputs.button/>
如果你想有条件地渲染组件,你可以shouldRender
在组件类上定义一个方法。如果该shouldRender
方法返回false
:
1use Illuminate\Support\Str;2 3/**4 * Whether the component should be rendered5 */6public function shouldRender(): bool7{8 return Str::length($this->message) > 0;9}
指数成分
有时组件是组件组的一部分,您可能希望将相关组件分组到单个目录中。例如,假设有一个“卡片”组件,其类结构如下:
1App\Views\Components\Card\Card2App\Views\Components\Card\Header3App\Views\Components\Card\Body
由于根Card
组件嵌套在Card
目录中,您可能希望通过 来渲染组件<x-card.card>
。但是,当组件的文件名与组件目录的名称匹配时,Laravel 会自动假定该组件是“根”组件,并允许您在渲染组件时无需重复目录名称:
1<x-card>2 <x-card.header>...</x-card.header>3 <x-card.body>...</x-card.body>4</x-card>
向组件传递数据
您可以使用 HTML 属性将数据传递给 Blade 组件。硬编码的原始值可以使用简单的 HTML 属性字符串传递给组件。PHP 表达式和变量应通过使用 字符:
作为前缀的属性传递给组件:
1<x-alert type="error" :message="$message"/>
您应该在组件的类构造函数中定义组件的所有数据属性。组件上的所有公共属性将自动提供给组件的视图。无需从组件的render
方法将数据传递给视图:
1<?php 2 3namespace App\View\Components; 4 5use Illuminate\View\Component; 6use Illuminate\View\View; 7 8class Alert extends Component 9{10 /**11 * Create the component instance.12 */13 public function __construct(14 public string $type,15 public string $message,16 ) {}17 18 /**19 * Get the view / contents that represent the component.20 */21 public function render(): View22 {23 return view('components.alert');24 }25}
当渲染组件时,您可以通过按名称回显变量来显示组件公共变量的内容:
1<div class="alert alert-{{ $type }}">2 {{ $message }}3</div>
套管
组件构造函数参数应使用 指定camelCase
,而kebab-case
在 HTML 属性中引用参数名称时应使用 。例如,给定以下组件构造函数:
1/**2 * Create the component instance.3 */4public function __construct(5 public string $alertType,6) {}
$alertType
可以像这样向组件提供参数:
1<x-alert alert-type="danger" />
简短属性语法
向组件传递属性时,也可以使用“短属性”语法。这通常很方便,因为属性名通常与它们对应的变量名匹配:
1{{-- Short attribute syntax... --}}2<x-profile :$userId :$name />3 4{{-- Is equivalent to... --}}5<x-profile :user-id="$userId" :name="$name" />
转义属性渲染
由于某些 JavaScript 框架(例如 Alpine.js)也使用冒号前缀的属性,因此您可以使用双冒号 ( ::
) 前缀来告知 Blade 该属性不是 PHP 表达式。例如,给定以下组件:
1<x-button ::class="{ danger: isDeleting }">2 Submit3</x-button>
Blade 将呈现以下 HTML:
1<button :class="{ danger: isDeleting }">2 Submit3</button>
组件方法
除了组件模板可以使用的公共变量之外,组件上的任何公共方法都可以被调用。例如,假设一个组件有这样一个isSelected
方法:
1/**2 * Determine if the given option is the currently selected option.3 */4public function isSelected(string $option): bool5{6 return $option === $this->selected;7}
您可以通过调用与方法名称匹配的变量从组件模板执行此方法:
1<option {{ $isSelected($value) ? 'selected' : '' }} value="{{ $value }}">2 {{ $label }}3</option>
访问组件类中的属性和插槽
Blade 组件还允许你在类的 render 方法中访问组件名称、属性和 slot。但是,为了访问这些数据,你应该在组件的render
方法中返回一个闭包:
1use Closure; 2 3/** 4 * Get the view / contents that represent the component. 5 */ 6public function render(): Closure 7{ 8 return function () { 9 return '<div {{ $attributes }}>Components content</div>';10 };11}
组件render
方法返回的闭包也可能接收一个$data
数组作为其唯一参数。该数组将包含几个提供有关组件信息的元素:
1return function (array $data) {2 // $data['componentName'];3 // $data['attributes'];4 // $data['slot'];5 6 return '<div {{ $attributes }}>Components content</div>';7}
数组中的元素$data
永远不应直接嵌入到render
方法返回的 Blade 字符串中,因为这样做可能允许通过恶意属性内容执行远程代码。
componentName
等于 HTML 标签中前缀后使用的名称。x-
因此<x-alert />
的componentName
将是alert
。该attributes
元素将包含 HTML 标签上的所有属性。该slot
元素是一个Illuminate\Support\HtmlString
包含组件插槽内容的实例。
闭包应该返回一个字符串。如果返回的字符串对应一个现有视图,则渲染该视图;否则,返回的字符串将被评估为内联 Blade 视图。
附加依赖项
如果您的组件需要 Laravel服务容器的依赖项,您可以在任何组件的数据属性之前列出它们,它们将自动由容器注入:
1use App\Services\AlertCreator; 2 3/** 4 * Create the component instance. 5 */ 6public function __construct( 7 public AlertCreator $creator, 8 public string $type, 9 public string $message,10) {}
隐藏属性/方法
如果您希望防止某些公共方法或属性作为变量暴露给组件模板,则可以将它们添加到$except
组件上的数组属性中:
1<?php 2 3namespace App\View\Components; 4 5use Illuminate\View\Component; 6 7class Alert extends Component 8{ 9 /**10 * The properties / methods that should not be exposed to the component template.11 *12 * @var array13 */14 protected $except = ['type'];15 16 /**17 * Create the component instance.18 */19 public function __construct(20 public string $type,21 ) {}22}
组件属性
我们已经研究了如何将数据属性传递给组件;但是,有时你可能需要指定额外的 HTML 属性,例如class
,这些属性不属于组件运行所需的数据。通常,你需要将这些额外的属性向下传递给组件模板的根元素。例如,假设我们要渲染一个alert
如下所示的组件:
1<x-alert type="error" :message="$message" class="mt-4"/>
所有不属于组件构造函数的属性都会自动添加到组件的“属性包”中。该属性包会通过$attributes
变量自动提供给组件使用。所有属性都可以通过回显此变量在组件内渲染:
1<div {{ $attributes }}>2 <!-- Component content -->3</div>
@env
目前不支持在组件标签中
使用诸如 之类的指令。例如,<x-alert :live="@env('production')"/>
将不会被编译。
默认/合并属性
有时您可能需要为属性指定默认值,或将附加值合并到组件的某些属性中。为此,您可以使用属性包的merge
方法。此方法对于定义一组始终应用于组件的默认 CSS 类特别有用:
1<div {{ $attributes->merge(['class' => 'alert alert-'.$type]) }}>2 {{ $message }}3</div>
如果我们假设这个组件是这样使用的:
1<x-alert type="error" :message="$message" class="mb-4"/>
该组件最终呈现的 HTML 将如下所示:
1<div class="alert alert-error mb-4">2 <!-- Contents of the $message variable -->3</div>
有条件地合并类
有时,如果给定条件为 ,您可能希望合并类true
。您可以通过class
方法来做到这一点,该方法接受一个类数组,其中数组键包含要添加的一个或多个类,而值是一个布尔表达式。如果数组元素具有数字键,它将始终包含在渲染的类列表中:
1<div {{ $attributes->class(['p-4', 'bg-red' => $hasError]) }}>2 {{ $message }}3</div>
如果需要将其他属性合并到组件上,可以将该merge
方法链接到该class
方法上:
1<button {{ $attributes->class(['p-4'])->merge(['type' => 'button']) }}>2 {{ $slot }}3</button>
如果需要有条件地编译不应接收合并属性的其他 HTML 元素上的类,则可以使用@class 指令。
非类属性合并
当合并非class
属性的属性时,提供给方法的值merge
将被视为该属性的“默认”值。然而,与class
属性不同的是,这些属性不会与注入的属性值合并,而是会被覆盖。例如,一个button
组件的实现可能如下所示:
1<button {{ $attributes->merge(['type' => 'button']) }}>2 {{ $slot }}3</button>
要使用自定义 来渲染按钮组件type
,可以在使用组件时指定。如果未指定类型,button
则将使用以下类型:
1<x-button type="submit">2 Submit3</x-button>
本例中组件的渲染 HTMLbutton
为:
1<button type="submit">2 Submit3</button>
如果您希望某个属性除了class
默认值和注入值之外,还能包含其他值,可以使用该prepends
方法。在本例中,该data-controller
属性始终以默认值开头profile-controller
,任何其他注入data-controller
值都将位于该默认值之后:
1<div {{ $attributes->merge(['data-controller' => $attributes->prepends('profile-controller')]) }}>2 {{ $slot }}3</div>
检索和过滤属性
你可以使用 方法来过滤属性filter
。该方法接受一个闭包,true
如果你希望将属性保留在属性包中,则该闭包应该返回:
1{{ $attributes->filter(fn (string $value, string $key) => $key == 'foo') }}
为了方便起见,您可以使用该whereStartsWith
方法检索所有以给定字符串开头的键的属性:
1{{ $attributes->whereStartsWith('wire:model') }}
相反,该whereDoesntStartWith
方法可用于排除所有以给定字符串开头的键的属性:
1{{ $attributes->whereDoesntStartWith('wire:model') }}
使用该first
方法,您可以渲染给定属性包中的第一个属性:
1{{ $attributes->whereStartsWith('wire:model')->first() }}
如果你想检查组件上是否存在某个属性,可以使用该has
方法。该方法接受属性名称作为其唯一参数,并返回一个布尔值,指示该属性是否存在:
1@if ($attributes->has('class'))2 <div>Class attribute is present</div>3@endif
如果将数组传递给has
方法,该方法将确定组件上是否存在所有给定的属性:
1@if ($attributes->has(['name', 'class']))2 <div>All of the attributes are present</div>3@endif
该hasAny
方法可用于确定组件上是否存在任何给定的属性:
1@if ($attributes->hasAny(['href', ':href', 'v-bind:href']))2 <div>One of the attributes is present</div>3@endif
您可以使用下列方法检索特定属性的值get
:
1{{ $attributes->get('class') }}
该only
方法可用于仅检索具有给定键的属性:
1{{ $attributes->only(['class']) }}
该except
方法可用于检索除具有给定键的属性之外的所有属性:
1{{ $attributes->except(['class']) }}
保留关键字
默认情况下,某些关键字保留供 Blade 内部使用,用于渲染组件。以下关键字不能在组件中定义为公共属性或方法名:
data
render
resolveView
shouldRender
view
withAttributes
withName
插槽
你经常需要通过“插槽”向组件传递额外的内容。组件插槽通过回显$slot
变量来渲染。为了探索这个概念,假设一个alert
组件有以下标记:
1<!-- /resources/views/components/alert.blade.php -->2 3<div class="alert alert-danger">4 {{ $slot }}5</div>
slot
我们可以通过向组件注入内容来传递内容:
1<x-alert>2 <strong>Whoops!</strong> Something went wrong!3</x-alert>
有时,组件可能需要在组件内的不同位置渲染多个不同的插槽。让我们修改一下警报组件,使其能够注入“title”插槽:
1<!-- /resources/views/components/alert.blade.php -->2 3<span class="alert-title">{{ $title }}</span>4 5<div class="alert alert-danger">6 {{ $slot }}7</div>
您可以使用标签定义指定插槽的内容x-slot
。任何未在显式标签内的内容都x-slot
将通过变量传递给组件$slot
:
1<x-alert>2 <x-slot:title>3 Server Error4 </x-slot>5 6 <strong>Whoops!</strong> Something went wrong!7</x-alert>
您可以调用插槽的isEmpty
方法来确定插槽是否包含内容:
1<span class="alert-title">{{ $title }}</span>2 3<div class="alert alert-danger">4 @if ($slot->isEmpty())5 This is default content if the slot is empty.6 @else7 {{ $slot }}8 @endif9</div>
此外,该hasActualContent
方法还可用于确定插槽是否包含任何非 HTML 注释的“实际”内容:
1@if ($slot->hasActualContent())2 The scope has non-comment content.3@endif
作用域插槽
如果您使用过 Vue 等 JavaScript 框架,您可能对“作用域插槽”很熟悉,它允许您在插槽内访问组件的数据或方法。在 Laravel 中,您可以通过在组件上定义公共方法或属性,并通过$component
变量在插槽内访问组件来实现类似的行为。在本例中,我们假设x-alert
组件在其组件类上定义了一个公共formatAlert
方法:
1<x-alert>2 <x-slot:title>3 {{ $component->formatAlert('Server Error') }}4 </x-slot>5 6 <strong>Whoops!</strong> Something went wrong!7</x-alert>
插槽属性
与 Blade 组件一样,您可以为插槽分配其他属性,例如 CSS 类名:
1<x-card class="shadow-sm"> 2 <x-slot:heading class="font-bold"> 3 Heading 4 </x-slot> 5 6 Content 7 8 <x-slot:footer class="text-sm"> 9 Footer10 </x-slot>11</x-card>
要与插槽属性交互,您可以访问attributes
插槽变量的属性。有关如何与属性交互的更多信息,请参阅组件属性文档:
1@props([ 2 'heading', 3 'footer', 4]) 5 6<div {{ $attributes->class(['border']) }}> 7 <h1 {{ $heading->attributes->class(['text-lg']) }}> 8 {{ $heading }} 9 </h1>10 11 {{ $slot }}12 13 <footer {{ $footer->attributes->class(['text-gray-700']) }}>14 {{ $footer }}15 </footer>16</div>
内联组件视图
对于非常小的组件,同时管理组件类和组件的视图模板可能会显得很麻烦。因此,你可以直接从render
方法中返回组件的标记:
1/** 2 * Get the view / contents that represent the component. 3 */ 4public function render(): string 5{ 6 return <<<'blade' 7 <div class="alert alert-danger"> 8 {{ $slot }} 9 </div>10 blade;11}
生成内联视图组件
要创建呈现内联视图的组件,您可以inline
在执行make:component
命令时使用该选项:
1php artisan make:component Alert --inline
动态组件
有时你可能需要渲染一个组件,但直到运行时才知道应该渲染哪个组件。在这种情况下,你可以使用 Laravel 的内置dynamic-component
组件,根据运行时值或变量来渲染组件:
1// $componentName = "secondary-button";2 3<x-dynamic-component :component="$componentName" class="mt-4" />
手动注册组件
以下关于手动注册组件的文档主要适用于编写包含视图组件的 Laravel 扩展包的用户。如果您没有编写扩展包,则这部分组件文档可能与您无关。
当为自己的应用程序编写组件时,会在app/View/Components
目录和resources/views/components
目录内自动发现组件。
但是,如果你正在构建一个使用 Blade 组件的包,或者将组件放置在非常规目录中,则需要手动注册组件类及其 HTML 标签别名,以便 Laravel 知道在哪里找到该组件。通常,你应该在boot
包的服务提供商的方法中注册组件:
1use Illuminate\Support\Facades\Blade; 2use VendorPackage\View\Components\AlertComponent; 3 4/** 5 * Bootstrap your package's services. 6 */ 7public function boot(): void 8{ 9 Blade::component('package-alert', AlertComponent::class);10}
一旦你的组件被注册,它就可以使用其标签别名进行渲染:
1<x-package-alert/>
自动加载包组件
或者,你也可以按照约定使用该componentNamespace
方法自动加载组件类。例如,一个Nightshade
包可能包含Calendar
位于ColorPicker
命名空间内的组件Package\Views\Components
:
1use Illuminate\Support\Facades\Blade;2 3/**4 * Bootstrap your package's services.5 */6public function boot(): void7{8 Blade::componentNamespace('Nightshade\\Views\\Components', 'nightshade');9}
这将允许使用以下package-name::
语法通过供应商命名空间使用包组件:
1<x-nightshade::calendar />2<x-nightshade::color-picker />
Blade 会自动检测链接到此组件的类,方法是使用 Pascal 大小写格式对组件名称进行排序。子目录也支持使用“点”符号。
匿名组件
与内联组件类似,匿名组件提供了一种通过单个文件管理组件的机制。然而,匿名组件使用单个视图文件,并且没有关联的类。要定义匿名组件,你只需要在resources/views/components
目录中放置一个 Blade 模板。例如,假设你在 中定义了一个组件resources/views/components/alert.blade.php
,你可以像这样简单地渲染它:
1<x-alert/>
你可以使用.
字符来指示组件是否嵌套在components
目录中更深的位置。例如,假设组件定义在resources/views/components/inputs/button.blade.php
,你可以像这样渲染它:
1<x-inputs.button/>
匿名指数成分
有时,当一个组件由多个 Blade 模板组成时,你可能希望将给定组件的模板分组到单个目录中。例如,设想一个具有以下目录结构的“手风琴”组件:
1/resources/views/components/accordion.blade.php2/resources/views/components/accordion/item.blade.php
此目录结构允许您像这样呈现手风琴组件及其项目:
1<x-accordion>2 <x-accordion.item>3 ...4 </x-accordion.item>5</x-accordion>
但是,为了通过呈现手风琴组件x-accordion
,我们被迫将“索引”手风琴组件模板放在resources/views/components
目录中,而不是将其accordion
与其他手风琴相关模板一起嵌套在目录中。
值得庆幸的是,Blade 允许你将与组件目录名匹配的文件放置在组件目录中。当此模板存在时,即使它嵌套在目录中,也可以将其渲染为组件的“根”元素。因此,我们可以继续使用上面示例中提供的 Blade 语法;但是,我们将调整目录结构,如下所示:
1/resources/views/components/accordion/accordion.blade.php2/resources/views/components/accordion/item.blade.php
数据属性/特性
由于匿名组件没有任何关联的类,您可能想知道如何区分哪些数据应该作为变量传递给组件,哪些属性应该放在组件的属性包中。
您可以使用组件 Blade 模板顶部的指令指定哪些属性应被视为数据变量@props
。组件上的所有其他属性将通过组件的属性包获取。如果您希望为数据变量指定默认值,可以将变量名称指定为数组键,并将默认值指定为数组值:
1<!-- /resources/views/components/alert.blade.php -->2 3@props(['type' => 'info', 'message'])4 5<div {{ $attributes->merge(['class' => 'alert alert-'.$type]) }}>6 {{ $message }}7</div>
鉴于上述组件定义,我们可以像这样渲染组件:
1<x-alert type="error" :message="$message" class="mb-4"/>
访问父级数据
有时你可能想在子组件中访问父组件的数据。在这种情况下,你可以使用@aware
指令。例如,假设我们正在构建一个由 父级<x-menu>
和 子级组成的复杂菜单组件<x-menu.item>
:
1<x-menu color="purple">2 <x-menu.item>...</x-menu.item>3 <x-menu.item>...</x-menu.item>4</x-menu>
该<x-menu>
组件可能有如下实现:
1<!-- /resources/views/components/menu/index.blade.php -->2 3@props(['color' => 'gray'])4 5<ul {{ $attributes->merge(['class' => 'bg-'.$color.'-200']) }}>6 {{ $slot }}7</ul>
因为该color
prop 仅传递给了父级 ( <x-menu>
),所以它在 内部不可用<x-menu.item>
。但是,如果我们使用指令,我们也@aware
可以使它在 内部可用:<x-menu.item>
1<!-- /resources/views/components/menu/item.blade.php -->2 3@aware(['color' => 'gray'])4 5<li {{ $attributes->merge(['class' => 'text-'.$color.'-800']) }}>6 {{ $slot }}7</li>
该@aware
指令无法访问未通过 HTML 属性显式传递给父组件的父数据。@props
该指令也无法访问未显式传递给父组件的默认值@aware
。
匿名组件路径
如前所述,匿名组件通常通过在resources/views/components
目录中放置 Blade 模板来定义。但是,除了默认路径之外,有时您可能还想在 Laravel 中注册其他匿名组件路径。
该anonymousComponentPath
方法接受匿名组件所在位置的“路径”作为第一个参数,以及组件所在位置的可选“命名空间”作为第二个参数。通常,此方法应从boot
应用程序的某个服务提供商的方法中调用:
1/**2 * Bootstrap any application services.3 */4public function boot(): void5{6 Blade::anonymousComponentPath(__DIR__.'/../components');7}
如果组件路径注册时没有指定前缀(如上例所示),则它们在 Blade 组件中渲染时也可能没有相应的前缀。例如,如果某个panel.blade.php
组件存在于上面注册的路径中,则可能会像这样渲染:
1<x-panel />
前缀“namespaces”可以作为该方法的第二个参数提供anonymousComponentPath
:
1Blade::anonymousComponentPath(__DIR__.'/../components', 'dashboard');
当提供前缀时,可以在渲染组件时通过将组件的命名空间添加到组件名称的前缀来渲染该“命名空间”内的组件:
1<x-dashboard::panel />
建筑布局
使用组件的布局
大多数 Web 应用程序在各个页面上都使用相同的通用布局。如果我们必须在创建的每个视图中重复整个布局 HTML,那么应用程序的维护将会变得极其繁琐和困难。幸运的是,我们可以方便地将此布局定义为单个Blade 组件,然后在整个应用程序中使用它。
定义布局组件
例如,假设我们正在构建一个“todo”列表应用程序。我们可能会定义一个layout
如下所示的组件:
1<!-- resources/views/components/layout.blade.php --> 2 3<html> 4 <head> 5 <title>{{ $title ?? 'Todo Manager' }}</title> 6 </head> 7 <body> 8 <h1>Todos</h1> 9 <hr/>10 {{ $slot }}11 </body>12</html>
应用布局组件
layout
定义好组件后,我们就可以创建一个使用该组件的 Blade 视图了。在本例中,我们将定义一个显示任务列表的简单视图:
1<!-- resources/views/tasks.blade.php -->2 3<x-layout>4 @foreach ($tasks as $task)5 <div>{{ $task }}</div>6 @endforeach7</x-layout>
请记住,注入到组件中的内容将被提供给组件$slot
内的默认变量layout
。您可能已经注意到,如果提供了插槽,我们的组件layout
也会遵循该插槽;否则,将显示默认标题。我们可以使用组件文档$title
中讨论的标准插槽语法,从任务列表视图中注入自定义标题:
1<!-- resources/views/tasks.blade.php --> 2 3<x-layout> 4 <x-slot:title> 5 Custom Title 6 </x-slot> 7 8 @foreach ($tasks as $task) 9 <div>{{ $task }}</div>10 @endforeach11</x-layout>
现在我们已经定义了布局和任务列表视图,我们只需要从task
路由返回视图:
1use App\Models\Task;2 3Route::get('/tasks', function () {4 return view('tasks', ['tasks' => Task::all()]);5});
使用模板继承的布局
定义布局
布局也可以通过“模板继承”来创建。这是在引入组件之前构建应用程序的主要方式。
首先,我们来看一个简单的示例。首先,我们来看一下页面布局。由于大多数 Web 应用程序在各个页面上都使用相同的通用布局,因此可以方便地将此布局定义为单个 Blade 视图:
1<!-- resources/views/layouts/app.blade.php --> 2 3<html> 4 <head> 5 <title>App Name - @yield('title')</title> 6 </head> 7 <body> 8 @section('sidebar') 9 This is the master sidebar.10 @show11 12 <div class="container">13 @yield('content')14 </div>15 </body>16</html>
如您所见,此文件包含典型的 HTML 标记。但是,请注意@section
和@yield
指令。@section
指令,顾名思义,定义一段内容,而@yield
指令用于显示给定部分的内容。
现在我们已经为我们的应用程序定义了布局,让我们定义一个继承该布局的子页面。
扩展布局
定义子视图时,使用@extends
Blade 指令指定子视图应“继承”哪个布局。扩展 Blade 布局的视图可以使用@section
指令将内容注入布局的各个部分。请记住,如上例所示,这些部分的内容将使用 显示在布局中@yield
:
1<!-- resources/views/child.blade.php --> 2 3@extends('layouts.app') 4 5@section('title', 'Page Title') 6 7@section('sidebar') 8 @@parent 9 10 <p>This is appended to the master sidebar.</p>11@endsection12 13@section('content')14 <p>This is my body content.</p>15@endsection
在此示例中,该sidebar
部分利用@@parent
指令将内容附加(而不是覆盖)到布局的侧边栏。@@parent
当视图渲染时,该指令将被布局的内容替换。
与前面的示例相反,本sidebar
节以 而@endsection
不是结尾@show
。该@endsection
指令将仅定义一个部分,而@show
将定义并立即产生该部分。
该@yield
指令还接受一个默认值作为其第二个参数。如果被 yield 的部分未定义,则会渲染此值:
1@yield('content', 'Default content')
表格
CSRF 字段
每当你在应用程序中定义 HTML 表单时,都应该在表单中包含一个隐藏的 CSRF 令牌字段,以便CSRF 保护中间件能够验证请求。你可以使用@csrf
Blade 指令来生成令牌字段:
1<form method="POST" action="/profile">2 @csrf3 4 ...5</form>
方法字段
由于 HTML 表单无法发出PUT
、PATCH
或DELETE
请求,因此您需要添加一个隐藏_method
字段来伪装这些 HTTP 动词。Blade@method
指令可以为您创建此字段:
1<form action="/foo/bar" method="POST">2 @method('PUT')3 4 ...5</form>
验证错误
该@error
指令可用于快速检查给定属性是否存在验证错误消息@error
。在指令中,你可以回显$message
变量来显示错误消息:
1<!-- /resources/views/post/create.blade.php --> 2 3<label for="title">Post Title</label> 4 5<input 6 id="title" 7 type="text" 8 class="@error('title') is-invalid @enderror" 9/>10 11@error('title')12 <div class="alert alert-danger">{{ $message }}</div>13@enderror
由于该@error
指令编译为“if”语句,因此@else
当属性没有错误时,您可以使用该指令来呈现内容:
1<!-- /resources/views/auth.blade.php -->2 3<label for="email">Email address</label>4 5<input6 id="email"7 type="email"8 class="@error('email') is-invalid @else is-valid @enderror"9/>
您可以将特定错误包的名称作为第二个参数传递给@error
指令,以检索包含多个表单的页面上的验证错误消息:
1<!-- /resources/views/auth.blade.php --> 2 3<label for="email">Email address</label> 4 5<input 6 id="email" 7 type="email" 8 class="@error('email', 'login') is-invalid @enderror" 9/>10 11@error('email', 'login')12 <div class="alert alert-danger">{{ $message }}</div>13@enderror
堆栈
Blade 允许你将数据推送到命名堆栈,这些堆栈可以在另一个视图或布局的其他位置渲染。这对于指定子视图所需的 JavaScript 库尤其有用:
1@push('scripts')2 <script src="/example.js"></script>3@endpush
如果您想知道@push
给定布尔表达式的计算结果是否为true
,则可以使用该@pushIf
指令:
1@pushIf($shouldPush, 'scripts')2 <script src="/example.js"></script>3@endPushIf
你可以根据需要多次推送到堆栈。要渲染完整的堆栈内容,请将堆栈名称传递给@stack
指令:
1<head>2 <!-- Head Contents -->3 4 @stack('scripts')5</head>
如果您想要将内容添加到堆栈的开头,则应使用以下@prepend
指令:
1@push('scripts')2 This will be second...3@endpush4 5// Later...6 7@prepend('scripts')8 This will be first...9@endprepend
服务注入
该@inject
指令可用于从 Laravel服务容器中检索服务。传递给的第一个参数@inject
是将放置服务的变量的名称,而第二个参数是要解析的服务的类或接口名称:
1@inject('metrics', 'App\Services\MetricsService')2 3<div>4 Monthly Revenue: {{ $metrics->monthlyRevenue() }}.5</div>
渲染内联 Blade 模板
有时你可能需要将原始 Blade 模板字符串转换为有效的 HTML。你可以使用Facaderender
提供的方法来实现Blade
。该render
方法接受 Blade 模板字符串和一个可选的数据数组,用于提供给模板:
1use Illuminate\Support\Facades\Blade;2 3return Blade::render('Hello, {{ $name }}', ['name' => 'Julian Bashir']);
Laravel 通过将内联 Blade 模板写入storage/framework/views
目录来渲染它们。如果您希望 Laravel 在渲染 Blade 模板后删除这些临时文件,您可以deleteCachedView
向该方法传入以下参数:
1return Blade::render(2 'Hello, {{ $name }}',3 ['name' => 'Julian Bashir'],4 deleteCachedView: true5);
渲染刀片碎片
使用Turbo和htmx等前端框架时,有时可能需要在 HTTP 响应中仅返回 Blade 模板的一部分。Blade 的“片段”功能正好可以满足您的需求。首先,将 Blade 模板的一部分放在@fragment
和@endfragment
指令中:
1@fragment('user-list')2 <ul>3 @foreach ($users as $user)4 <li>{{ $user->name }}</li>5 @endforeach6 </ul>7@endfragment
然后,在渲染使用此模板的视图时,您可以调用该fragment
方法来指定仅应在传出的 HTTP 响应中包含指定的片段:
1return view('dashboard', ['users' => $users])->fragment('user-list');
该fragmentIf
方法允许你根据给定条件有条件地返回视图的片段。否则,将返回整个视图:
1return view('dashboard', ['users' => $users])2 ->fragmentIf($request->hasHeader('HX-Request'), 'user-list');
和方法允许您在响应中返回多个视图片段。这些片段将被连接在一起fragments
:fragmentsIf
1view('dashboard', ['users' => $users])2 ->fragments(['user-list', 'comment-list']);3 4view('dashboard', ['users' => $users])5 ->fragmentsIf(6 $request->hasHeader('HX-Request'),7 ['user-list', 'comment-list']8 );
扩展 Blade
Blade 允许你使用 方法来定义自定义指令directive
。当 Blade 编译器遇到自定义指令时,它将使用该指令包含的表达式调用提供的回调函数。
下面的示例创建了一个@datetime($var)
指令,用于格式化给定的$var
,它应该是 的一个实例DateTime
:
1<?php 2 3namespace App\Providers; 4 5use Illuminate\Support\Facades\Blade; 6use Illuminate\Support\ServiceProvider; 7 8class AppServiceProvider extends ServiceProvider 9{10 /**11 * Register any application services.12 */13 public function register(): void14 {15 // ...16 }17 18 /**19 * Bootstrap any application services.20 */21 public function boot(): void22 {23 Blade::directive('datetime', function (string $expression) {24 return "<?php echo ($expression)->format('m/d/Y H:i'); ?>";25 });26 }27}
如你所见,我们将该format
方法链接到传递给指令的任何表达式上。因此,在此示例中,该指令最终生成的 PHP 代码如下:
1<?php echo ($var)->format('m/d/Y H:i'); ?>
更新 Blade 指令的逻辑后,你需要删除所有已缓存的 Blade 视图。可以使用view:clear
Artisan 命令删除已缓存的 Blade 视图。
自定义 Echo 处理器
如果您尝试使用 Blade 来“回显”一个对象,该对象的__toString
方法将被调用。__ toString方法是 PHP 内置的“魔法方法”之一。然而,有时您可能无法控制给__toString
定类的方法,例如,当您与之交互的类属于第三方库时。
在这些情况下,Blade 允许你为特定类型的对象注册自定义的 echo 处理程序。为此,你应该调用 Blade 的stringable
方法。该stringable
方法接受一个闭包作为参数。此闭包应该类型Prompts它负责渲染的对象类型。通常,该方法应该在应用程序类的方法stringable
中调用:boot
AppServiceProvider
1use Illuminate\Support\Facades\Blade; 2use Money\Money; 3 4/** 5 * Bootstrap any application services. 6 */ 7public function boot(): void 8{ 9 Blade::stringable(function (Money $money) {10 return $money->formatTo('en_GB');11 });12}
一旦定义了自定义回显处理程序,您就可以简单地在 Blade 模板中回显对象:
1Cost: {{ $money }}
自定义 If 语句
在定义简单的自定义条件语句时,编写自定义指令有时会比必要的更复杂。因此,Blade 提供了一种Blade::if
方法,允许您使用闭包快速定义自定义条件指令。例如,让我们定义一个自定义条件,用于检查应用程序配置的默认“磁盘”。我们可以在boot
以下方法中执行此操作AppServiceProvider
:
1use Illuminate\Support\Facades\Blade; 2 3/** 4 * Bootstrap any application services. 5 */ 6public function boot(): void 7{ 8 Blade::if('disk', function (string $value) { 9 return config('filesystems.default') === $value;10 });11}
一旦定义了自定义条件,您就可以在模板中使用它:
1@disk('local') 2 <!-- The application is using the local disk... --> 3@elsedisk('s3') 4 <!-- The application is using the s3 disk... --> 5@else 6 <!-- The application is using some other disk... --> 7@enddisk 8 9@unlessdisk('local')10 <!-- The application is not using the local disk... -->11@enddisk