跳到主要內容

Blade 範本

📝 此頁面為 Laravel 官方文檔的繁體中文翻譯。查看原始英文版本

Blade 範本

簡介

Blade 是 Laravel 中包含的一個簡單但功能強大的範本引擎。與一些 PHP 範本引擎不同,Blade 不會限制您在範本中使用純 PHP 程式碼。事實上,所有 Blade 範本都會被編譯成純 PHP 程式碼並快取直到它們被修改,這意味著 Blade 幾乎為您的應用程式增加了零開銷。Blade 範本檔案使用 .blade.php 檔案副檔名,通常儲存在 resources/views 目錄中。

Blade 視圖可以透過全域 view 輔助函數從路由或控制器返回。當然,正如視圖文檔中所述,資料可以透過 view 輔助函數的第二個參數傳遞給 Blade 視圖:

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

使用 Livewire 增強 Blade

想要將您的 Blade 範本提升到更高層級並輕鬆建立動態介面嗎?請查看 Laravel Livewire。Livewire 允許您編寫增強了動態功能的 Blade 元件,這些功能通常只能透過像 React、Svelte 或 Vue 這樣的前端框架實現,提供了一種建立現代、響應式前端的好方法,而無需許多 JavaScript 框架的複雜性、客戶端渲染或建立步驟。

顯示資料

您可以透過將變數包裝在花括號中來顯示傳遞給 Blade 視圖的資料。例如,給定以下路由:

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

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

Hello, {{ $name }}.

[!NOTE] Blade 的 {{ }} echo 語句會自動透過 PHP 的 htmlspecialchars 函數來防止 XSS 攻擊。

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

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

HTML 實體編碼

預設情況下,Blade(以及 Laravel 的 e 函數)會雙重編碼 HTML 實體。如果您想禁用雙重編碼,請從 AppServiceProviderboot 方法中呼叫 Blade::withoutDoubleEncoding 方法:

<?php

namespace App\Providers;

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

class AppServiceProvider extends ServiceProvider
{
    /**
     * 引導任何應用程式服務。
     */
    public function boot(): void
    {
        Blade::withoutDoubleEncoding();
    }
}

顯示未轉義的資料

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

Hello, {!! $name !!}.

[!WARNING] 在 echo 由應用程式使用者提供的內容時要非常小心。顯示使用者提供的資料時,您通常應使用轉義的、雙花括號語法來防止 XSS 攻擊。

Blade 和 JavaScript 框架

由於許多 JavaScript 框架也使用「花括號」來指示應在瀏覽器中顯示給定表達式,您可以使用 @ 符號來通知 Blade 渲染引擎表達式應保持不變。例如:

<h1>Laravel</h1>

Hello, @{{ name }}.

在此範例中,@ 符號將被 Blade 移除;但是,{{ name }} 表達式將保持不變,允許由您的 JavaScript 框架渲染。

@ 符號也可用於轉義 Blade 指令:

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

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

渲染 JSON

有時您可能希望將一個陣列傳遞給您的視圖,以便將其渲染為 JSON 來初始化一個 JavaScript 變數。例如:

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

但是,您可以使用 Illuminate\Support\Js::from 方法代替手動呼叫 json_encodefrom 方法接受與 PHP 的 json_encode 函數相同的參數;但是,它將確保生成的 JSON 已被正確轉義以包含在 HTML 引號中。from 方法將返回一個字串 JSON.parse JavaScript 語句,該語句將把給定的物件或陣列轉換為有效的 JavaScript 物件:

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

Laravel 應用程式骨架的最新版本包含一個 Js Facade,它在您的 Blade 範本中提供了對此功能的便利存取:

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

[!WARNING] 您應該只使用 Js::from 方法將現有變數渲染為 JSON。Blade 範本基於正規表達式,嘗試將複雜表達式傳遞給指令可能會導致意外失敗。

@verbatim 指令

如果您在範本的大部分區域中顯示 JavaScript 變數,您可以將 HTML 包裝在 @verbatim 指令中,這樣您就不必為每個 Blade echo 語句添加 @ 符號前綴:

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

Blade 指令

除了模板繼承和顯示資料外,Blade 還為常見的 PHP 控制結構提供了便利的捷徑,例如條件語句和迴圈。這些捷徑提供了一種非常乾淨、簡潔的方式來處理 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 已定義且不為 null...
@endisset

@empty($records)
    // $records 為「空」...
@endempty

身份驗證指令

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

@auth
    // 使用者已通過身份驗證...
@endauth

@guest
    // 使用者未通過身份驗證...
@endguest

如有需要,您可以在使用 @auth@guest 指令時指定應檢查的身份驗證守衛:

@auth('admin')
    // 使用者已通過身份驗證...
@endauth

@guest('admin')
    // 使用者未通過身份驗證...
@endguest

環境指令

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

@production
    // 生產環境特定內容...
@endproduction

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

@env('staging')
    // 應用程式正在 "staging" 中運行...
@endenv

@env(['staging', 'production'])
    // 應用程式正在 "staging" 或 "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 指令可用於判斷session值是否存在。如果 session 值存在,@session@endsession 指令中的模板內容將被評估。在 @session 指令的內容中,您可以 echo $value 變數來顯示 session 值:

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

上下文指令

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

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

Switch 語句

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

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

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

    @default
        Default case...
@endswitch

迴圈

除了條件語句外,Blade 還為處理 PHP 的迴圈結構提供了簡單的指令。同樣,這些指令中的每一個都與其 PHP 對應物的功能完全相同:

@for ($i = 0; $i < 10; $i++)
    The current value is {{ $i }}
@endfor

@foreach ($users as $user)
    <p>This is user {{ $user->id }}</p>
@endforeach

@forelse ($users as $user)
    <li>{{ $user->name }}</li>
@empty
    <p>No users</p>
@endforelse

@while (true)
    <p>I'm looping forever.</p>
@endwhile

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

使用迴圈時,您還可以使用 @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 在巢狀迴圈中,父迴圈的迴圈變數。

條件類別和樣式

@class 指令有條件地編譯一個 CSS 類別字串。該指令接受一個類別陣列,其中陣列鍵包含您希望添加的類別或類別,而值是一個布林表達式。如果陣列元素具有數字鍵,它將始終包含在渲染的類別列表中:

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

額外屬性

為了方便,您可以使用 @checked 指令輕鬆指示給定的 HTML 核取方塊輸入是否為「已選取」。如果提供的條件評估為 true,此指令將 echo checked

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

同樣地,@selected 指令可用於指示給定的選擇選項是否應為「已選取」:

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

此外,@disabled 指令可用於指示給定的元素是否應為「已禁用」:

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

此外,@readonly 指令可用於指示給定的元素是否應為「唯讀」:

<input
    type="email"
    name="email"
    value="[email protected]"
    @readonly($user->isNotAdmin())
/>

此外,@required 指令可用於指示給定的元素是否應為「必填」:

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

包含子視圖

[!NOTE] 雖然您可以自由使用 @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'])

如果您想在給定的布林表達式評估為 truefalse@include 一個視圖,您可以使用 @includeWhen@includeUnless 指令:

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

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

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

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

如果您想包含一個視圖而不從父視圖繼承任何變數,您可以使用 @includeIsolated 指令。包含的視圖將只能存取您明確傳遞的變數:

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

[!WARNING] 您應該避免在 Blade 視圖中使用 __DIR____FILE__ 常數,因為它們將引用快取的、編譯的視圖的位置。

為集合渲染視圖

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

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

@each 指令的第一個參數是要為陣列或集合中的每個元素渲染的視圖。第二個參數是您要迭代的陣列或集合,而第三個參數是將在視圖中分配給當前迭代的變數名稱。因此,例如,如果您正在迭代 jobs 陣列,通常您會希望在視圖中將每個 job 作為 job 變數存取。當前迭代的陣列金鑰將作為 key 變數在視圖中可用。

您還可以向 @each 指令傳遞第四個參數。此參數確定如果給定陣列為空時將渲染的視圖。

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

[!WARNING] 透過 @each 渲染的視圖不會繼承父視圖中的變數。如果子視圖需要這些變數,您應該改用 @foreach@include 指令。

@once 指令

@once 指令允許您定義模板的一部分,該部分每個渲染週期只會被評估一次。這對於使用堆疊將給定的 JavaScript 推送到頁面的標頭中非常有用。例如,如果您在迴圈中渲染給定的元件,您可能希望只在元件第一次渲染時將 JavaScript 推送到標頭:

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

由於 @once 指令通常與 @push@prepend 指令結合使用,因此 @pushOnce@prependOnce 指令可為您提供便利:

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

如果您正在從兩個單獨的 Blade 範本中推送重複的內容,您應該提供一個唯一標識符作為 @pushOnce 指令的第二個參數,以確保內容只渲染一次:

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

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

原始 PHP

在某些情況下,將 PHP 程式碼嵌入到您的視圖中很有用。您可以使用 Blade 的 @php 指令在模板中運行一段純 PHP:

@php
    $counter = 1;
@endphp

或者,如果您只需要使用 PHP 來匯入一個類別,您可以使用 @use 指令:

@use('App\Models\Flight')

可以向 @use 指令提供第二個參數來為匯入的類別設置別名:

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

如果您在相同命名空間中有多個類別,您可以將這些類別的匯入分組:

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

@use 指令還支援透過在匯入路徑前添加 functionconst 修飾符來匯入 PHP 函數和常數:

@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')

函數和 const 修飾符也支援分組匯入,允許您在單個指令中從相同命名空間匯入多個符號:

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

字體

使用 Laravel 的 Vite 字體優化時,您可以使用 @fonts 指令在應用程式的佈局中渲染您配置的字體預載入連結和內聯字體 CSS:

<!doctype html>
<head>
    {{-- ... --}}

    @fonts
    @vite('resources/js/app.js')
</head>

@fonts 指令渲染在 vite.config.js 檔案中配置的所有字體系列。該指令通常應放在應用程式根佈局的 <head> 中,在使用這些字體的任何內容之前。

如果頁面只需要您配置的字體中的一部分,您可以向指令傳遞一個或多個字體別名:

{{-- 載入單個字體別名... --}}
@fonts('sans')

{{-- 載入多個字體別名... --}}
@fonts(['sans', 'mono'])

字體別名是在 Vite 配置中定義字體時使用 alias 選項配置的。@fonts 指令呼叫 Vite Facade 提供的 fonts 方法,該方法也可以直接調用:

{{ Vite::fonts(['sans', 'mono']) }}

註解

Blade 還允許您在視圖中定義註解。但是,與 HTML 註解不同,Blade 註解不包含在應用程式返回的 HTML 中:

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

元件

元件和插槽提供了與區段、佈局和包含類似的好處;但是,有些人可能發現元件和插槽的心智模型更容易理解。編寫元件有兩種方法:基於類別的元件和匿名元件。

要建立基於類別的元件,您可以使用 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 目錄中。

手動註冊套件元件

為您自己的應用程式編寫元件時,元件會在 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 套件可能有一個位於 Package\Views\Components 命名空間中的 CalendarColorPicker 元件:

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 來自動偵測與該元件關聯的類別。也支援使用「點」語法的子目錄。

匿名元件

與內聯元件類似,匿名元件提供了一種透過單個檔案管理元件的機制。但是,匿名元件使用一個視圖檔案且沒有關聯的類別。要定義匿名元件,您只需要將 Blade 模板放在 resources/views/components 目錄中。例如,假設您在 resources/views/components/alert.blade.php 定義了一個元件,您可以像這樣渲染它:

<x-alert/>

您可以使用 . 字元來指示元件是否巢狀在 components 目錄的更深處。例如,假設元件定義在 resources/views/components/inputs/button.blade.php,您可以像這樣渲染它:

<x-inputs.button/>

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

php artisan make:component forms.input --view

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

匿名索引元件

有時,當一個元件由多個 Blade 模板組成時,您可能希望將給定元件的模板分組在單個目錄中。例如,想像一個具有以下目錄結構的「手風琴」元件:

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

此目錄結構允許您像這樣渲染手風琴元件及其項目:

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

但是,為了透過 x-accordion 渲染手風琴元件,我們被迫將「索引」手風琴元件模板放在 resources/views/components 目錄中,而不是與其他手風琴相關模板一起巢狀在 accordion 目錄中。

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

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

資料屬性 / 屬性

由於匿名元件沒有任何關聯的類別,您可能想知道如何區分哪些資料應作為變數傳遞給元件,以及哪些屬性應放在元件的屬性包中。

您可以使用 @props 指令在元件的 Blade 模板頂部指定哪些屬性應被視為資料變數。元件上的所有其他屬性將透過元件的屬性包可用。如果您想給資料變數一個預設值,您可以將變數名稱指定為陣列鍵,將預設值指定為陣列值:

<!-- /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>

[!WARNING] @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 元件然後在整個應用程式中使用它是很方便的。

定義佈局元件

例如,想像我們正在建立一個「待辦事項」清單應用程式。我們可能會定義一個如下所示的 layout 元件:

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

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

應用佈局元件

定義 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()]);
});

使用模板繼承建立佈局

定義佈局

佈局也可以透過「模板繼承」來建立。這是元件引入之前建立應用程式的主要方式。

首先,讓我們看一個簡單的範例。首先,我們將檢查一個頁面佈局。由於大多數 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 指令用於顯示給定區段的內容。

現在我們已經為應用程式定義了一個佈局,讓我們定義一個繼承該佈局的子頁面。

擴展佈局

定義子視圖時,使用 @extends Blade 指令指定子視圖應「繼承」哪個佈局。擴展 Blade 佈局的視圖可以使用 @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 區段使用 @@parent 指令將內容附加(而不是覆蓋)到佈局的側邊欄。@@parent 指令將在視圖渲染時被佈局的內容替換。

[!NOTE] 與上面的範例相反,此 sidebar 區段以 @endsection 結尾,而不是 @show@endsection 指令將只定義一個區段,而 @show 將定義並立即 yield 該區段。

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

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

表單

CSRF 欄位

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

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

    ...
</form>

方法欄位

由於 HTML 表單不能發出 PUTPATCHDELETE 請求,您將需要添加一個隱藏的 _method 欄位來欺騙這些 HTTP 動詞。@method Blade 指令可以為您建立此欄位:

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

    ...
</form>

驗證錯誤

@error 指令可用於快速檢查給定屬性是否存在驗證錯誤消息。在 @error 指令中,您可以 echo $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 指令,以在包含多個表單的頁面上檢索驗證錯誤消息:

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

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

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

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

堆疊

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

@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

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

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

服務注入

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

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

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

渲染內聯 Blade 範本

有時您可能需要將原始 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 片段

使用像 Turbohtmx 這樣的前端框架時,您可能偶爾需要僅在 HTTP 響應中返回 Blade 模板的一部分。Blade「片段」允許您做到這一點。首先,將 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');

fragmentsfragmentsIf 方法允許您在響應中返回多個視圖片段。片段將被連接在一起:

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

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

擴展 Blade

Blade 允許您使用 directive 方法定義自己的自訂指令。當 Blade 編譯器遇到自訂指令時,它將使用指令包含的表達式呼叫提供的回呼。

以下範例建立一個 @datetime($var) 指令,該指令格式化給定的 $var,它應該是 DateTime 的實例:

<?php

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'); ?>

[!WARNING] 更新 Blade 指令的邏輯後,您需要刪除所有快取的 Blade 視圖。可以使用 view:clear Artisan 命令移除快取的 Blade 視圖。

自訂 Echo 處理器

如果您嘗試使用 Blade「echo」一個物件,將會呼叫物件的 __toString 方法。__toString 方法是 PHP 的內建「魔法方法」之一。但是,有時您可能無法控制給定類別的 __toString 方法,例如當與您互動的類別屬於第三方函式庫時。

在這些情況下,Blade 允許您為該特定類型的物件註冊一個自訂 echo 處理器。為此,您應該呼叫 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');
    });
}

定義自訂 echo 處理器後,您可以直接在 Blade 範本中 echo 該物件:

Cost: {{ $money }}

自訂 If 語句

編程自訂指令有時比定義簡單的自訂條件語句更複雜。出於這個原因,Blade 提供了一個 Blade::if 方法,該方法允許您使用閉包快速定義自訂條件指令。例如,讓我們定義一個檢查應用程式配置的預設「磁碟」的自訂條件。我們可以在 AppServiceProviderboot 方法中完成此操作:

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