* * * * *
[TOC]
## 簡介
Blade 是 Laravel 提供的一個既簡單又強大的模板引擎。和其他流行的 PHP 模板引擎不一樣,Blade 并不限制你在視圖中使用原生 PHP 代碼。所有 Blade 視圖文件都將被編譯成原生的 PHP 代碼并緩存起來,除非它被修改,否則不會重新編譯,這就意味著 Blade 基本上不會給你的應用增加任何額外負擔。Blade 視圖文件使用?`.blade.php`?擴展名,一般被存放在?`resources/views`?目錄。
## 模板繼承
### 定義頁面布局
Blade 的兩個主要優點是?*模板繼承*?和?*區塊*?。
為方便開始,讓我們先通過一個簡單的例子來上手。首先,我們需要確認一個 "master" 的頁面布局。因為大多數 web 應用是在不同的頁面中使用相同的布局方式,我們可以很方便的定義這個 Blade 布局視圖:
~~~
<!-- 文件保存于 resources/views/layouts/app.blade.php -->
<html>
<head>
<title>應用程序名稱 - @yield('title')</title>
</head>
<body>
@section('sidebar')
這是 master 的側邊欄。
@show
<div class="container">
@yield('content')
</div>
</body>
</html>
~~~
如你所見,該文件包含了典型的 HTML 語法。不過,請注意?`@section`?和?`@yield`?命令。?`@section`?命令正如其名字所暗示的一樣是用來定義一個視圖區塊的,而?`@yield`?指令是用來顯示指定區塊的內容的。
現在,我們已經定義好了這個應用程序的布局,讓我們接著來定義一個繼承此布局的子頁面。
### 繼承頁面布局
當定義子頁面時,你可以使用 Blade 提供的?`@extends`?命令來為子頁面指定其所 「繼承」 的頁面布局。 當子頁面繼承布局之后,即可使用?`@section`?命令將內容注入于布局的?`@section`?區塊中。切記,在上面的例子里,布局中使用?`@yield`?的地方將會顯示這些區塊中的內容:
~~~
<!-- Stored in 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`?命令追加布局中的 sidebar 區塊中的內容,如果不使用則會覆蓋掉布局中的這部分內容。?`@parent`?命令會在視圖被渲染時替換為布局中的內容。
當然,可以通過在路由中使用全局輔助函數?`view`?來返回 Blade 視圖:
~~~
Route::get('blade', function () {
return view('child');
});
~~~
## 組件 & Slots
組件和 slots 能提供類似于區塊和布局的好處;不過,一些人可能發現組件和 slots 更容易理解。首先,讓我們假設一個會在我們應用中重復使用的「警告」組件:
~~~
<!-- /resources/views/alert.blade.php -->
<div class="alert alert-danger">
{{ $slot }}
</div>
~~~
`{{ $slot }}`?變量將包含我們希望注入到組件的內容。現在,我們可以使用?`@component`?指令來構造這個組件:
~~~
@component('alert')
<strong>哇!</strong> 出現了一些問題!
@endcomponent
~~~
有些時候它對于定義組件的多個 slots 是非常有幫助的。讓我們修改我們的警告組件,讓它支持注入一個「標題」。 已命名的 slots 將顯示「相對應」名稱的變量的值:
~~~
<!-- /resources/views/alert.blade.php -->
<div class="alert alert-danger">
<div class="alert-title">{{ $title }}</div>
{{ $slot }}
</div>
~~~
現在,我們可以使用?`@slot`?指令注入內容到已命名的 slot 中,任何沒有被?`@slot`?指令包裹住的內容將傳遞給組件中的?`$slot`?變量:
~~~
@component('alert')
@slot('title')
拒絕
@endslot
你沒有權限訪問這個資源!
@endcomponent
~~~
#### 傳遞額外的數據給組件
有時候你可能需要傳遞額外的數據給組件。為了解決這個問題,你可以傳遞一個數組作為第二個參數傳遞給?`@component`?指令。所有的數據都將以變量的形式傳遞給組件模版:
~~~
@component('alert', ['foo' => 'bar'])
...
@endcomponent
~~~
## 顯示數據
你可以使用 「中括號」 包住變量以顯示傳遞至 Blade 視圖的數據。如下面的路由設置:
~~~
Route::get('greeting', function () {
return view('welcome', ['name' => 'Samantha']);
});
~~~
你可以像這樣顯示?`name`?變量的內容:
~~~
Hello, {{ $name }}.
~~~
當然也不是說一定只能顯示傳遞至視圖的變量內容。你也可以顯示 PHP 函數的結果。事實上,你可以在 Blade 中顯示任意的 PHP 代碼:
~~~
The current UNIX timestamp is {{ time() }}.
~~~
> {note} Blade?`{{ }}`?語法會自動調用 PHP?`htmlspecialchars`?函數來避免 XSS 攻擊。
#### 當數據存在時輸出
有時候你可能想要輸出一個變量,但是你并不確定這個變量是否已經被定義,我們可以用像這樣的冗長 PHP 代碼表達:
~~~
{{ isset($name) ? $name : 'Default' }}
~~~
事實上,Blade 提供了更便捷的方式來代替這種三元運算符表達式:
~~~
{{ $name or 'Default' }}
~~~
在這個例子中,如果?`$name`?變量存在,它的值將被顯示出來。但是,如果它不存在,則會顯示?`Default`?。
#### 顯示未轉義過的數據
在默認情況下,Blade 模板中的?`{{ }}`?表達式將會自動調用 PHP?`htmlspecialchars`?函數來轉義數據以避免 XSS 的攻擊。如果你不想你的數據被轉義,你可以使用下面的語法:
~~~
Hello, {!! $name !!}.
~~~
> {note} 要非常小心處理用戶輸入的數據時,你應該總是使用?`{{ }}`?語法來轉義內容中的任何的 HTML 元素,以避免 XSS 攻擊。
### Blade & JavaScript 框架
由于很多 JavaScript 框架都使用花括號來表明所提供的表達式,所以你可以使用?`@`?符號來告知 Blade 渲染引擎你需要保留這個表達式原始形態,例如:
~~~
<h1>Laravel</h1>
Hello, @{{ name }}.
~~~
在這個例子里,`@`?符號最終會被 Blade 引擎剔除,并且?`{{ name }}`?表達式會被原樣的保留下來,這樣就允許你的 JavaScript 框架來使用它了。
#### `@verbatim`?指令
如果你需要在頁面中大片區塊中展示 JavaScript 變量,你可以使用?`@verbatim`?指令來包裹 HTML 內容,這樣你就不需要為每個需要解析的變量增加?`@`?符號前綴了:
~~~
@verbatim
<div class="container">
Hello, {{ name }}.
</div>
@endverbatim
~~~
## 控制結構
除了模板繼承與數據顯示的功能以外,Blade 也給一般的 PHP 結構控制語句提供了方便的縮寫,比如條件表達式和循環語句。這些縮寫提供了更為清晰簡明的方式來使用 PHP 的控制結構,而且還保持與 PHP 語句的相似性。
### If 語句
你可以通過?`@if`,?`@elseif`,?`@else`?及?`@endif`?指令構建?`if`?表達式。這些命令的功能等同于在 PHP 中的語法:
~~~
@if (count($records) === 1)
我有一條記錄!
@elseif (count($records) > 1)
我有多條記錄!
@else
我沒有任何記錄!
@endif
~~~
為了方便,Blade 也提供了一個?`@unless`?命令:
~~~
@unless (Auth::check())
你尚未登錄。
@endunless
~~~
### 循環
除了條件表達式外,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
~~~
> {tip} 當循環時,你可以使用?[循環變量](Blade模板.md)?來獲取循環中有價值的信息,比如循環中的首次或最后的迭代。
當使用循環時,你可能也需要一些結束循環或者跳出當前循環的命令:
~~~
@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 ($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
~~~
如果你是在一個嵌套的循環中,你可以通過使用?`$loop`?變量的?`parent`?屬性來獲取父循環中的?`$loop`?變量:
~~~
@foreach ($users as $user)
@foreach ($user->posts as $post)
@if ($loop->parent->first)
This is 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->depth` | 當前循環的嵌套深度。 |
| `$loop->parent` | 當在嵌套的循環內時,可以訪問到父循環中的 $loop 變量。 |
### 注釋
Blade 也允許在頁面中定義注釋,然而,跟 HTML 的注釋不同的是,Blade 注釋不會被包含在應用程序返回的 HTML 內:
~~~
{{-- 此注釋將不會出現在渲染后的 HTML --}}
~~~
### PHP
在某些情況下,它對于你在視圖文件中嵌入 php 代碼是非常有幫助的。你可以在你的模版中使用 Blade 提供的?`@php`?指令來執行一段純 PHP 代碼:
~~~
@php
//
@endphp
~~~
> {tip} 雖然 Blade 提供了這個功能,但頻繁地使用也同時意味著你在你的模版中嵌入了太多的邏輯了。
## 引入子視圖
你可以使用 Blade 的?`@include`?命令來引入一個已存在的視圖,所有在父視圖的可用變量在被引入的視圖中都是可用的。
~~~
<div>
@include('shared.errors')
<form>
<!-- Form Contents -->
</form>
</div>
~~~
盡管被引入的視圖會繼承父視圖中的所有數據,你也可以通過傳遞額外的數組數據至被引入的頁面:
~~~
@include('view.name', ['some' => 'data'])
~~~
當然,如果你嘗試使用?`@include`?去引用一個不存在的視圖,Laravel 會拋出錯誤。如果你想引入一個視圖,而你又無法確認這個視圖存在與否,你可以使用?`@includeIf`?指令:
~~~
@includeIf('view.name', ['some' => 'data'])
~~~
> {note} 請避免在 Blade 視圖中使用?`__DIR__`?及?`__FILE__`?常量,因為他們會引用視圖被緩存的位置。
### 為集合渲染視圖
你可以使用 Blade 的?`@each`?命令將循環及引入結合成一行代碼:
~~~
@each('view.name', $jobs, 'job')
~~~
第一個參數為每個元素要渲染的子視圖,第二個參數是你要迭代的數組或集合,而第三個參數為迭代時被分配至子視圖中的變量名稱。舉個例子,如果你需要迭代一個?`jobs`?數組,通常子視圖會使用?`job`?作為變量來訪問 job 信息。子視圖使用?`key`?變量作為當前迭代的鍵名。
你也可以傳遞第四個參數到?`@each`?命令。當需要迭代的數組為空時,將會使用這個參數提供的視圖來渲染。
~~~
@each('view.name', $jobs, 'job', 'view.empty')
~~~
## 堆棧
Blade 也允許你在其它視圖或布局中為已經命名的堆棧中壓入數據,這在子視圖中引入必備的 JavaScript 類庫時尤其有用:
~~~
@push('scripts')
<script src="/example.js"></script>
@endpush
~~~
你可以根據需要多次壓入堆棧,通過?`@stack`?命令中鍵入堆棧的名字來渲染整個堆棧:
~~~
<head>
<!-- Head Contents -->
@stack('scripts')
</head>
~~~
## 服務注入
你可以使用?`@inject`?命令來從 Larvel?[service container](控制器.md)?中取出服務。傳遞給?`@inject`?的第一個參數為置放該服務的變量名稱,而第二個參數為你想要解析的服務的類或是接口的名稱:
~~~
@inject('metrics', 'App\Services\MetricsService')
<div>
Monthly Revenue: {{ $metrics->monthlyRevenue() }}.
</div>
~~~
## 拓展 Blade
Blade 甚至允許你使用?`directive`?方法來注冊自己的命令。當 Blade 編譯器遇到該命令時,它將會帶參數調用提供的回調函數。
以下例子會創建一個把指定的?`$var`?格式化的?`@datetime($var)`?命令:
~~~
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* 運行服務注冊后的啟動進程。
*
* @return void
*/
public function boot()
{
Blade::directive('datetime', function ($expression) {
return "<?php echo ($expression)->format('m/d/Y H:i'); ?>";
});
}
/**
* 在容器注冊綁定。
*
* @return void
*/
public function register()
{
//
}
}
~~~
如你所見,我們可以使用鏈式調用?`format`?方法的表述方式傳遞到指令。所以,在這個例子里,最終該指令生成了的 PHP 代碼如下:
~~~
<?php echo $var->format('m/d/Y H:i'); ?>
~~~
> {note} 在更新 Blade 指令的邏輯后,你將需要刪除所有已緩存的 Blade 視圖,使用?`view:clear`?Artisan 命令來清除被緩存的視圖。
- 前言
- 翻譯說明
- 發行說明
- 升級說明
- 貢獻導引
- 入門指南
- 安裝
- 配置信息
- 文件夾結構
- 請求周期
- 開發環境部署
- Homestead
- Valet
- 核心概念
- 服務容器
- 服務提供者
- Facades
- Contracts
- HTTP層
- 路由
- 中間件
- CSRF 保護
- 控制器
- 請求
- 響應
- 視圖
- Session
- 表單驗證
- 前端
- Blade 模板
- 本地化
- 前端指南
- 編輯資源 Mix
- 安全
- 用戶認證
- Passport OAuth 認證
- 用戶授權
- 加密解密
- 哈希
- 重置密碼
- 綜合話題
- Artisan 命令行
- 廣播系統
- 緩存系統
- 集合
- 錯誤與日志
- 事件系統
- 文件存儲
- 輔助函數
- 郵件發送
- 消息通知
- 擴展包開發
- 隊列
- 任務調度
- 數據庫
- 快速入門
- 查詢構造器
- 分頁
- 數據庫遷移
- 數據填充
- Redis
- Eloquent ORM
- 快速入門
- 模型關聯
- Eloquent 集合
- 修改器
- 序列化
- 測試
- 快速入門
- HTTP 測試
- 瀏覽器測試 Dusk
- 數據庫測試
- 測試模擬器
- 官方擴展包
- Cashier 交易工具包
- Envoy 部署工具
- Scout 全文搜索
- Socialite 社會化登錄