跳轉至內容

Blade 模板

簡介

Blade 是 Laravel 內建的一個簡單且強大的模板引擎。與某些 PHP 模板引擎不同,Blade 不會限制你在模板中使用原生 PHP 程式碼。事實上,所有 Blade 模板都會被編譯成原生 PHP 程式碼並快取起來,直到被修改,這意味著 Blade 對你的應用程式幾乎沒有任何開銷。Blade 模板檔案使用 .blade.php 副檔名,通常儲存在 resources/views 目錄中。

Blade 檢視可以透過路由或控制器使用全域性 view 輔助函式來返回。當然,如 檢視 文件中所述,可以使用 view 輔助函式的第二個引數將資料傳遞給 Blade 檢視。

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

透過 Livewire 增強 Blade

想讓你的 Blade 模板更上一層樓,並輕鬆構建動態介面嗎?請檢視 Laravel Livewire。Livewire 允許你編寫帶有動態功能的 Blade 元件,這些功能通常只能透過 React、Svelte 或 Vue 等前端框架實現,它提供了一種無需處理複雜性、客戶端渲染或構建步驟即可構建現代響應式前端的絕佳方法。

顯示資料

你可以透過將變數包裹在大括號中來顯示傳遞給 Blade 檢視的資料。例如,假設有以下路由:

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

你可以像這樣顯示 name 變數的內容:

1Hello, {{ $name }}.

Blade 的 {{ }} echo 語句會自動透過 PHP 的 htmlspecialchars 函式進行處理,以防止 XSS 攻擊。

你不僅限於顯示傳遞給檢視的變數內容,還可以 echo 任何 PHP 函式的結果。實際上,你可以在 Blade echo 語句中放置任何你想要的 PHP 程式碼:

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

HTML 實體編碼

預設情況下,Blade(以及 Laravel 的 e 函式)會對 HTML 實體進行雙重編碼。如果你想停用雙重編碼,可以在 AppServiceProviderboot 方法中呼叫 Blade::withoutDoubleEncoding 方法。

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(): void
14 {
15 Blade::withoutDoubleEncoding();
16 }
17}

顯示未轉義的資料

預設情況下,Blade 的 {{ }} 語句會自動透過 PHP 的 htmlspecialchars 函式進行處理,以防止 XSS 攻擊。如果你不希望資料被轉義,可以使用以下語法:

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

在 echo 由應用程式使用者提供的內容時,請務必小心。通常應使用轉義的雙大括號語法,以防止顯示使用者提供的資料時發生 XSS 攻擊。

Blade 與 JavaScript 框架

由於許多 JavaScript 框架也使用“大括號”來指示在瀏覽器中顯示給定的表示式,你可以使用 @ 符號來告知 Blade 渲染引擎某個表示式應保持原樣。例如:

1<h1>Laravel</h1>
2 
3Hello, @{{ name }}.

在此示例中,@ 符號會被 Blade 移除;然而,{{ name }} 表示式將不會被 Blade 引擎觸碰,從而允許你的 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,你可以使用 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@verbatim
2 <div class="container">
3 Hello, {{ name }}.
4 </div>
5@endverbatim

Blade 指令

除了模板繼承和顯示資料外,Blade 還為常見的 PHP 控制結構(如條件語句和迴圈)提供了方便的快捷方式。這些快捷方式提供了一種非常簡潔且易於編寫的方式來處理 PHP 控制結構,同時又保持了與原生 PHP 寫法的一致性。

If 語句

你可以使用 @if@elseif@else@endif 指令來構建 if 語句。這些指令的功能與其對應的 PHP 語句完全相同:

1@if (count($records) === 1)
2 I have one record!
3@elseif (count($records) > 1)
4 I have multiple records!
5@else
6 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@endisset
4 
5@empty($records)
6 // $records is "empty"...
7@endempty

認證指令

@auth@guest 指令可用於快速判斷當前使用者是否已 認證 或是否為訪客:

1@auth
2 // The user is authenticated...
3@endauth
4 
5@guest
6 // The user is not authenticated...
7@endguest

如果需要,在使用 @auth@guest 指令時,可以指定要檢查的認證守衛 (guard):

1@auth('admin')
2 // The user is authenticated...
3@endauth
4 
5@guest('admin')
6 // The user is not authenticated...
7@endguest

環境指令

你可以使用 @production 指令檢查應用程式是否在生產環境中執行:

1@production
2 // Production specific content...
3@endproduction

或者,你可以使用 @env 指令判斷應用程式是否在特定環境中執行:

1@env('staging')
2 // The application is running in "staging"...
3@endenv
4 
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 指令的內容中,你可以 echo $value 變數來顯示會話值:

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

上下文指令

@context 指令可用於判斷 上下文 (context) 值是否存在。如果上下文值存在,則 @context@endcontext 指令內的模板內容將被執行。在 @context 指令的內容中,你可以 echo $value 變數來顯示上下文值:

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

Switch 語句

可以使用 @switch@case@break@default@endswitch 指令構建 Switch 語句:

1@switch($i)
2 @case(1)
3 First case...
4 @break
5 
6 @case(2)
7 Second case...
8 @break
9 
10 @default
11 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@empty
12 <p>No users</p>
13@endforelse
14 
15@while (true)
16 <p>I'm looping forever.</p>
17@endwhile

在遍歷 foreach 迴圈時,你可以使用 迴圈變數 來獲取關於迴圈的有價值資訊,例如你是否處於迴圈的第一次或最後一次迭代中。

使用迴圈時,你還可以使用 @continue@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 @break
10 @endif
11@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

如果你處於巢狀迴圈中,可以透過 parent 屬性訪問父迴圈的 $loop 變數:

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 @endif
6 @endforeach
7@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 複選框輸入是否為“選中”。如果提供的條件計算結果為 true,該指令將 echo checked

1<input
2 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 @endforeach
7</select>

此外,@disabled 指令可用於指示給定的元素是否應為“停用”狀態:

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

而且,@readonly 指令可用於指示給定的元素是否應為“只讀”狀態:

1<input
2 type="email"
3 name="email"
5 @readonly($user->isNotAdmin())
6/>

另外,@required 指令可用於指示給定的元素是否應為“必填”狀態:

1<input
2 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'])

如果你想在給定的布林表示式計算結果為 truefalse@include 一個檢視,可以使用 @includeWhen@includeUnless 指令:

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

要從給定的檢視陣列中包含第一個存在的檢視,可以使用 includeFirst 指令:

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

如果你想包含一個檢視而不從父檢視繼承任何變數,可以使用 @includeIsolated 指令。被包含的檢視將僅能訪問你明確傳遞的變數:

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

你不應該在 Blade 檢視中使用 __DIR____FILE__ 常量,因為它們將指向快取的編譯檢視的位置。

為集合渲染檢視

你可以使用 Blade 的 @each 指令將迴圈和包含合併為一行:

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

@each 指令的第一個引數是為陣列或集合中的每個元素渲染的檢視。第二個引數是你想要遍歷的陣列或集合,而第三個引數是在檢視中分配給當前迭代的變數名。因此,例如,如果你在遍歷一個 jobs 陣列,通常你會在檢視中將每個 job 作為 job 變數進行訪問。當前迭代的陣列鍵在檢視中將作為 key 變數可用。

你還可以向 @each 指令傳遞第四個引數。該引數決定了如果給定的陣列為空時要渲染的檢視。

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

透過 @each 渲染的檢視不會從父檢視繼承變數。如果子檢視需要這些變數,你應該改用 @foreach@include 指令。

@once 指令

@once 指令允許你定義一個在每個渲染週期中只會被執行一次的模板片段。這對於使用 堆疊 (stacks) 將一段特定的 JavaScript 推送到頁面頭部非常有用。例如,如果你在一個迴圈中渲染一個特定的 元件,你可能只希望在元件第一次被渲染時將 JavaScript 推送到頭部:

1@once
2 @push('scripts')
3 <script>
4 // Your custom JavaScript...
5 </script>
6 @endpush
7@endonce

由於 @once 指令經常與 @push@prepend 指令結合使用,為了方便起見,提供了 @pushOnce@prependOnce 指令:

1@pushOnce('scripts')
2 <script>
3 // Your custom JavaScript...
4 </script>
5@endPushOnce

如果你從兩個不同的 Blade 模板推送重複的內容,你應該向 @pushOnce 指令的第二個引數提供一個唯一識別符號,以確保內容只會被渲染一次:

1<!-- pie-chart.blade.php -->
2@pushOnce('scripts', 'chart.js')
3 <script src="/chart.js"></script>
4@endPushOnce
5 
6<!-- line-chart.blade.php -->
7@pushOnce('scripts', 'chart.js')
8 <script src="/chart.js"></script>
9@endPushOnce

原生 PHP

在某些情況下,在檢視中嵌入 PHP 程式碼非常有用。你可以使用 Blade 的 @php 指令在模板中執行一塊原生 PHP 程式碼:

1@php
2 $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 指令還支援透過在匯入路徑前加上 functionconst 修飾符來匯入 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')

函式和常量修飾符也支援分組匯入,允許你在單個指令中從同一個名稱空間匯入多個符號:

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 --}}

元件

元件和插槽提供了與分段 (sections)、佈局 (layouts) 和包含 (includes) 類似的優勢;然而,有些人可能覺得元件和插槽的心智模型更容易理解。編寫元件有兩種方法:基於類的元件和匿名元件。

要建立一個基於類的元件,你可以使用 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

上面的命令將在 app/View/Components/Forms 目錄中建立一個 Input 元件,檢視將被放置在 resources/views/components/forms 目錄中。

手動註冊包元件

在為你自己的應用程式編寫元件時,元件會自動在 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(): void
7{
8 Blade::component('package-alert', Alert::class);
9}

一旦你的元件被註冊,它就可以使用其標籤別名進行渲染:

1<x-package-alert/>

或者,你可以使用 componentNamespace 方法按慣例自動載入元件類。例如,一個 Nightshade 包可能擁有位於 Package\Views\Components 名稱空間內的 CalendarColorPicker 元件:

1use Illuminate\Support\Facades\Blade;
2 
3/**
4 * Bootstrap your package's services.
5 */
6public function boot(): void
7{
8 Blade::componentNamespace('Nightshade\\Views\\Components', 'nightshade');
9}

這將允許使用 package-name:: 語法透過其供應商名稱空間來使用包元件:

1<x-nightshade::calendar />
2<x-nightshade::color-picker />

Blade 將透過對元件名稱進行帕斯卡拼寫法 (Pascal-casing) 來自動檢測連結到此元件的類。子目錄也支援使用“點”符號。

渲染元件

要顯示一個元件,你可以在 Blade 模板中使用 Blade 元件標籤。Blade 元件標籤以字串 x- 開頭,後跟元件類的 kebab-case 名稱:

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 rendered
5 */
6public function shouldRender(): bool
7{
8 return Str::length($this->message) > 0;
9}

索引元件

有時元件屬於一個元件組,你可能希望將相關元件組合在一個目錄中。例如,想象一個具有以下類結構的“card”元件:

1App\Views\Components\Card\Card
2App\Views\Components\Card\Header
3App\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(): View
22 {
23 return view('components.alert');
24 }
25}

當渲染元件時,你可以透過按名稱 echo 變數來顯示元件公共變數的內容:

1<div class="alert alert-{{ $type }}">
2 {{ $message }}
3</div>

大小寫

元件建構函式引數應使用 camelCase 指定,而在 HTML 屬性中引用引數名稱時應使用 kebab-case。例如,給定以下元件建構函式:

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 Submit
3</x-button>

以下 HTML 將由 Blade 渲染:

1<button :class="{ danger: isDeleting }">
2 Submit
3</button>

元件方法

除了公共變數可用於你的元件模板外,還可以呼叫元件上的任何公共方法。例如,想象一個具有 isSelected 方法的元件:

1/**
2 * Determine if the given option is the currently selected option.
3 */
4public function isSelected(string $option): bool
5{
6 return $option === $this->selected;
7}

你可以透過呼叫與方法名稱匹配的變數來從元件模板執行此方法:

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

在元件類中訪問屬性和插槽

Blade 元件還允許你在類的 render 方法內訪問元件名稱、屬性和插槽。但是,為了訪問這些資料,你應該從元件的 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 將為 alertattributes 元素將包含 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 array
13 */
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"/>

所有不屬於元件建構函式的屬性都將自動新增到元件的“屬性包 (attribute bag)”中。此屬性包透過 $attributes 變數自動提供給元件。所有屬性都可以透過 echo 此變數在元件內進行渲染:

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 屬性合併

在合併非 class 屬性時,提供給 merge 方法的值將被視為該屬性的“預設”值。然而,與 class 屬性不同,這些屬性不會與注入的屬性值合併。相反,它們會被覆蓋。例如,一個 button 元件的實現可能如下所示:

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

要渲染帶有自定義 type 的按鈕元件,可以在使用該元件時指定它。如果未指定型別,將使用 button 型別:

1<x-button type="submit">
2 Submit
3</x-button>

此示例中 button 元件渲染的 HTML 將為:

1<button type="submit">
2 Submit
3</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
  • resolve
  • resolveView
  • shouldRender
  • view
  • withAttributes
  • withName

插槽 (Slots)

你通常需要透過“插槽 (slots)”將額外的內容傳遞給元件。元件插槽透過 echo $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>

有時元件可能需要在元件內的不同位置渲染多個不同的插槽。讓我們修改我們的 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 Error
4 </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 @else
7 {{ $slot }}
8 @endif
9</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 Footer
10 </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}

生成內聯檢視元件

要建立一個渲染內聯檢視的元件,你可以在執行 make:component 命令時使用 inline 選項:

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 包可能擁有位於 Package\Views\Components 名稱空間內的 CalendarColorPicker 元件:

1use Illuminate\Support\Facades\Blade;
2 
3/**
4 * Bootstrap your package's services.
5 */
6public function boot(): void
7{
8 Blade::componentNamespace('Nightshade\\Views\\Components', 'nightshade');
9}

這將允許使用 package-name:: 語法透過其供應商名稱空間來使用包元件:

1<x-nightshade::calendar />
2<x-nightshade::color-picker />

Blade 將透過對元件名稱進行帕斯卡拼寫法 (Pascal-casing) 來自動檢測連結到此元件的類。子目錄也支援使用“點”符號。

匿名元件

與內聯元件類似,匿名元件提供了一種透過單個檔案管理元件的機制。但是,匿名元件使用單個檢視檔案,並且沒有關聯的類。要定義匿名元件,你只需要在 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/>

要透過 Artisan 建立匿名元件,可以在呼叫 make:component 命令時使用 --view 標誌:

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

上面的命令將在 resources/views/components/forms/input.blade.php 建立一個 Blade 檔案,該檔案可以作為元件透過 <x-forms.input /> 進行渲染。

匿名索引元件

有時,當一個元件由許多 Blade 模板組成時,你可能希望將給定元件的模板分組在一個目錄中。例如,想象一個具有以下目錄結構的“accordion”元件:

1/resources/views/components/accordion.blade.php
2/resources/views/components/accordion/item.blade.php

此目錄結構允許你渲染 accordion 元件及其子項,如下所示:

1<x-accordion>
2 <x-accordion.item>
3 ...
4 </x-accordion.item>
5</x-accordion>

然而,為了透過 x-accordion 渲染 accordion 元件,我們被迫將“索引”accordion 元件模板放置在 resources/views/components 目錄中,而不是將其與 accordion 相關的其他模板一起巢狀在 accordion 目錄中。

幸運的是,Blade 允許你在元件目錄本身內放置一個與元件目錄名稱匹配的檔案。當該模板存在時,即使它巢狀在目錄中,它也可以被渲染為元件的“根”元素。因此,我們可以繼續使用上述示例中給出的相同 Blade 語法;但是,我們將調整目錄結構如下:

1/resources/views/components/accordion/accordion.blade.php
2/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(): void
5{
6 Blade::anonymousComponentPath(__DIR__.'/../components');
7}

當元件路徑在沒有指定字首的情況下注冊時,如上例所示,它們也可以在你的 Blade 元件中渲染而無需對應的字首。例如,如果 panel.blade.php 元件存在於上述註冊的路徑中,它可以這樣渲染:

1<x-panel />

字首“名稱空間”可以作為第二個引數提供給 anonymousComponentPath 方法:

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

當提供字首時,該“名稱空間”內的元件可以透過在渲染元件時將元件的名稱空間作為元件名稱的字首來渲染:

1<x-dashboard::panel />

構建佈局

使用元件構建佈局

大多數 Web 應用程式在不同頁面上保持相同的總體佈局。如果我們必須在我們建立的每個檢視中重複整個佈局 HTML,那將是非常麻煩且難以維護的。值得慶幸的是,將此佈局定義為單個 Blade 元件 然後在我們的應用程式中全面使用它是非常方便的。

定義佈局元件

例如,想象我們正在構建一個“待辦事項”列表應用程式。我們可能會定義一個如下所示的 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 @endforeach
7</x-layout>

請記住,注入元件的內容將被提供給我們的 layout 元件中的預設 $slot 變數。正如你可能已經注意到的,我們的 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 @endforeach
11</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 @show
11 
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@endsection
12 
13@section('content')
14 <p>This is my body content.</p>
15@endsection

在此示例中,sidebar 分段利用了 @@parent 指令來追加(而不是覆蓋)內容到佈局的側邊欄。當檢視渲染時,@@parent 指令將被佈局的內容所替換。

與之前的示例相反,此 sidebar 分段以 @endsection 而不是 @show 結尾。@endsection 指令只會定義一個分段,而 @show 則會定義並 立即渲染 該分段。

@yield 指令也接受一個預設值作為其第二個引數。如果正在渲染的分段未定義,則將渲染此值。

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

表單

CSRF 欄位

每當你在應用程式中定義 HTML 表單時,你應該在表單中包含一個隱藏的 CSRF 令牌欄位,以便 CSRF 保護 中介軟體可以驗證請求。你可以使用 @csrf Blade 指令生成令牌欄位:

1<form method="POST" action="/profile">
2 @csrf
3 
4 ...
5</form>

Method 欄位

由於 HTML 表單不能發出 PUTPATCHDELETE 請求,因此你需要新增一個隱藏的 _method 欄位來偽造這些 HTTP 動詞。@method Blade 指令可以為你建立此欄位:

1<form action="/foo/bar" method="POST">
2 @method('PUT')
3 
4 ...
5</form>

驗證錯誤

@error 指令可用於快速檢查給定屬性是否存在 驗證錯誤訊息。在 @error 指令內,你可以 echo $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<input
6 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

堆疊 (Stacks)

Blade 允許你推送到命名堆疊,這些堆疊可以在另一個檢視或佈局中的其他地方渲染。這對於指定子檢視所需的任何 JavaScript 庫特別有用:

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

如果你想在給定布林表示式計算結果為 true@push 內容,可以使用 @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@endpush
4 
5// Later...
6 
7@prepend('scripts')
8 This will be first...
9@endprepend

@hasstack 指令可用於判斷堆疊是否為空:

1@hasstack('list')
2 <ul>
3 @stack('list')
4 </ul>
5@endif

服務注入

@inject 指令可用於從 Laravel 服務容器 檢索服務。傳遞給 @inject 的第一個引數是服務將被放入的變數名稱,而第二個引數是你希望解析的服務的類或介面名稱:

1@inject('metrics', 'App\Services\MetricsService')
2 
3<div>
4 Monthly Revenue: {{ $metrics->monthlyRevenue() }}.
5</div>

渲染內聯 Blade 模板

有時你可能需要將原始 Blade 模板字串轉換為有效的 HTML。你可以使用 Blade 門面提供的 render 方法來實現這一點。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: true
5);

渲染 Blade 片段

當使用 Turbohtmx 等前端框架時,你有時可能只需要在 HTTP 響應中返回 Blade 模板的一部分。Blade “片段 (fragments)”允許你做到這一點。要入門,請將 Blade 模板的一部分放置在 @fragment@endfragment 指令中:

1@fragment('user-list')
2 <ul>
3 @foreach ($users as $user)
4 <li>{{ $user->name }}</li>
5 @endforeach
6 </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');

fragmentsfragmentsIf 方法允許你在響應中返回多個檢視片段。片段將被連線在一起。

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(): void
14 {
15 // ...
16 }
17 
18 /**
19 * Bootstrap any application services.
20 */
21 public function boot(): void
22 {
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 “echo”一個物件,將呼叫該物件的 __toString 方法。__toString 方法是 PHP 內建的“魔術方法”之一。然而,有時你可能無法控制給定類的 __toString 方法,例如當你與之互動的類屬於第三方庫時。

在這些情況下,Blade 允許你為該特定型別的物件註冊自定義 echo 處理程式。要實現這一點,你應該呼叫 Blade 的 stringable 方法。stringable 方法接受一個閉包。該閉包應該對它負責渲染的物件型別進行型別提示。通常,stringable 方法應該在你的應用程式的 AppServiceProvider 類的 boot 方法中呼叫:

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}

一旦定義了你的自定義 echo 處理程式,你就可以在 Blade 模板中簡單地 echo 該物件了:

1Cost: {{ $money }}

自定義 If 語句

當定義簡單的自定義條件語句時,編寫自定義指令有時比必要的更復雜。因此,Blade 提供了一個 Blade::if 方法,它允許你使用閉包快速定義自定義條件指令。例如,讓我們定義一個檢查應用程式配置的預設“磁碟”的自定義條件。我們可以在 AppServiceProviderboot 方法中執行此操作:

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