# Blade 模板
- [簡介](#introduction)
- [模板繼承](#template-inheritance)
- [定義頁面布局](#defining-a-layout)
- [繼承頁面布局](#extending-a-layout)
- [顯示數據](#displaying-data)
- [Blade & JavaScript 框架](#blade-and-javascript-frameworks)
- [控制結構](#control-structures)
- [If 語句](#if-statements)
- [循環](#loops)
- [循環變量](#the-loop-variable)
- [注釋](#comments)
- [引入子視圖](#including-sub-views)
- [為集合渲染視圖](#rendering-views-for-collections)
- [堆棧](#stacks)
- [服務注入](#service-injection)
- [擴充 Blade](#extending-blade)
<a name="introduction"></a>
## 簡介
Blade 是 Laravel 提供的一個既簡單又強大的模板引擎。和其他流行的 PHP 模板引擎不一樣,Blade 并不限制你在視圖中使用原生 PHP 代碼。所有 Blade 視圖文件都將被編譯成原生的 PHP 代碼并緩存起來,除非它被修改,否則不會重新編譯,這就意味著 Blade 基本上不會給你的應用增加任何額外負擔。Blade 視圖文件使用 `.blade.php` 擴展名,一般被存放在 `resources/views` 目錄。
<a name="template-inheritance"></a>
## 模板繼承
<a name="defining-a-layout"></a>
### 定義頁面布局
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` 指令是用來顯示指定區塊的內容的。
現在,我們已經定義好了這個應用程序的布局,讓我們接著來定義一個繼承此布局的子頁面。
<a name="extending-a-layout"></a>
### 繼承頁面布局
當定義子頁面時,你可以使用 Blade 提供的 `@extends` 命令來為子頁面指定其所 「繼承」 的頁面布局。 當視圖 `@section` Blade 的布局之后,即可使用 `@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');
});
<a name="displaying-data"></a>
## 顯示數據
你可以使用 「中括號」 包住變量以顯示傳遞至 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 `htmlentities` 函數來避免 XSS 攻擊。
#### 當數據存在時輸出
有時候你可能想要輸出一個變量,但是你并不確定這個變量是否已經被定義,我們可以用像這樣的冗長 PHP 代碼表達:
{{ isset($name) ? $name : 'Default' }}
事實上,Blade 提供了更便捷的方式來代替這種三元運算符表達式:
{{ $name or 'Default' }}
在這個例子中,如果 `$name` 變量存在,它的值將被顯示出來。但是,如果它不存在,則會顯示 `Default` 。
#### 顯示未轉義過的數據
在默認情況下,Blade 模板中的 `{{ }}` 表達式將會自動調用 PHP `htmlentities` 函數來轉義數據以避免 XSS 的攻擊。如果你不想你的數據被轉義,你可以使用下面的語法:
Hello, {!! $name !!}.
> {note} 要非常小心處理用戶輸入的數據時,你應該總是使用 `{{ }}` 語法來轉義內容中的任何的 HTML 元素,以避免 XSS 攻擊。
<a name="blade-and-javascript-frameworks"></a>
### Blade & JavaScript 框架
由于很多 JavaScript 框架都使用花括號來表明所提供的表達式,所以你可以使用 `@` 符號來告知 Blade 渲染引擎你需要保留這個表達式原始形態,例如:
<h1>Laravel</h1>
Hello, @{{ name }}.
在這個例子里,`@` 符號最終會被 Blade 引擎剔除,并且 `{{ name }}` 表達式會被原樣的保留下來,這樣就允許你的 JavaScript 框架來使用它了。
#### `@verbatim` 指令
如果你需要在頁面中大片區塊中展示 JavaScript 變量,你可以使用 `@verbatim` 指令來包裹 HTML 內容,這樣你就不需要為每個需要解析的變量增加 `@` 符號前綴了:
@verbatim
<div class="container">
Hello, {{ name }}.
</div>
@endverbatim
<a name="control-structures"></a>
## 控制結構
除了模板繼承與數據顯示的功能以外,Blade 也給一般的 PHP 結構控制語句提供了方便的縮寫,比如條件表達式和循環語句。這些縮寫提供了更為清晰簡明的方式來使用 PHP 的控制結構,而且還保持與 PHP 語句的相似性。
<a name="if-statements"></a>
### If 語句
你可以通過 `@if`, `@elseif`, `@else` 及 `@endif` 指令構建 `if` 表達式。這些命令的功能等同于在 PHP 中的語法:
@if (count($records) === 1)
我有一條記錄!
@elseif (count($records) > 1)
我有多條記錄!
@else
我沒有任何記錄!
@endif
為了方便,Blade 也提供了一個 `@unless` 命令:
@unless (Auth::check())
你尚未登錄。
@endunless
<a name="loops"></a>
### 循環
除了條件表達式外,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} 當循環時,你可以使用 [循環變量](#the-loop-variable) 來獲取循環中有價值的信息,比如循環中的首次或最后的迭代。
當使用循環時,你可能也需要一些結束循環或者跳出當前循環的命令:
@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
<a name="the-loop-variable"></a>
### 循環變量
當循環時,你可以在循環內訪問 `$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 變量。
<a name="comments"></a>
### 注釋
Blade 也允許在頁面中定義注釋,然而,跟 HTML 的注釋不同的是,Blade 注釋不會被包含在應用程序返回的 HTML 內:
{{-- 此注釋將不會出現在渲染后的 HTML --}}
<a name="including-sub-views"></a>
## 引入子視圖
你可以使用 Blade 的 `@include` 命令來引入一個已存在的視圖,所有在父視圖的可用變量在被引入的視圖中都是可用的。
<div>
@include('shared.errors')
<form>
<!-- Form Contents -->
</form>
</div>
盡管被引入的視圖會繼承父視圖中的所有數據,你也可以通過傳遞額外的數組數據至被引入的頁面:
@include('view.name', ['some' => 'data'])
> {note} 請避免在 Blade 視圖中使用 `__DIR__` 及 `__FILE__` 常量,因為他們會引用視圖被緩存的位置。
<a name="rendering-views-for-collections"></a>
### 為集合渲染視圖
你可以使用 Blade 的 `@each` 命令將循環及引入結合成一行代碼:
@each('view.name', $jobs, 'job')
第一個參數為每個元素要渲染的局部視圖,第二個參數你要迭代的數組或集合,而第三個參數為迭代時被分配至視圖中的變量名稱。舉個例子,如果你需要迭代一個 `job` 數組。通常你會想要在局部渲染視圖中使用 `job` 作為變量來訪問 job 信息。在你的試圖部分中 `key` 變量將是當前迭代的關鍵。
你也可以傳遞第四個參數到 `@each` 命令。此參數為當指定的數組為空時,將會被渲染的視圖。
@each('view.name', $jobs, 'job', 'view.empty')
<a name="stacks"></a>
## 堆棧
Blade 也允許你在其它視圖或布局中為已經命名的堆棧中壓入數據,這在子視圖中引入必備的 JavaScript 類庫時尤其有用:
@push('scripts')
<script src="/example.js"></script>
@endpush
你可以根據需要多次壓入堆棧,通過 `@stack` 命令中鍵入堆棧的名字來渲染整個堆棧:
<head>
<!-- Head Contents -->
@stack('scripts')
</head>
<a name="service-injection"></a>
## 服務注入
你可以使用 `@inject` 命令來從 Larvel [service container](/docs/{{version}}/container) 中取出服務。傳遞給 `@inject` 的第一個參數為置放該服務的變量名稱,而第二個參數為你想要解析的服務的類或是接口的名稱:
@inject('metrics', 'App\Services\MetricsService')
<div>
Monthly Revenue: {{ $metrics->monthlyRevenue() }}.
</div>
<a name="extending-blade"></a>
## 擴充 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 命令來清除被緩存的視圖。
## 譯者署名
| 用戶名 | 頭像 | 職能 | 簽名 |
|---|---|---|---|
| [@江邊望海](http://blog.jiangbianwanghai.com) | <img class="avatar-66 rm-style" src="https://dn-phphub.qbox.me/uploads/avatars/5306_1470714129.jpeg?imageView2/1/w/100/h/100"> | 翻譯 | 鄭州悉知資深技術經理、講師,10多年軟件產品研發、測試、咨詢及管理工作經驗。Follow me [@jiangbianwanghai](https://github.com/jiangbianwanghai/) at Github |
| [@summerblue](https://github.com/summerblue) | <img class="avatar-66 rm-style" src="https://avatars2.githubusercontent.com/u/324764?v=3&s=100"> | Review | A man seeking for Wisdom. |
- 說明
- 翻譯說明
- 發行說明
- 升級說明
- 貢獻導引
- 入門指南
- 安裝
- 配置信息
- 文件夾結構
- 錯誤與日志
- 開發環境
- HomeStead
- Valet
- 核心概念
- 服務容器
- 服務提供者
- 門面(facades)
- contracts
- HTTP層
- 路由
- 中間件
- CSRF保護
- 控制器
- 請求
- 響應
- Session
- 表單驗證
- 視圖與模板
- 視圖
- Blade模板
- 本地化
- Javascript與CSS
- 入門指南
- laravel-elixir
- 安全
- 用戶認證
- 用戶授權
- 重置密碼
- API授權
- 加密解密
- 哈希
- 綜合話題
- 廣播系統
- 緩存系統
- 事件系統
- 文件存儲
- 郵件發送
- 消息通知
- 隊列
- 數據庫
- 快速入門
- 查詢構造器
- 分頁
- 數據庫遷移
- 數據填充
- redis
- Eloquent ORM
- 快速入門
- 模型關聯
- Eloquent集合
- 修改器
- 序列化
- Artisan控制臺
- Artisan 命令行
- 任務調度
- 測試
- 快速入門
- 應用程序測試
- 數據庫測試
- 模擬器
- 官方擴展包
- Cashier交易包
- Envoy 部署工具
- Passport OAuth 認證
- Scout 全文搜索
- Socialite 社交化登錄
- 附錄
- 集合
- 輔助函數
- 擴展包開發
- 交流說明