[TOC]
# Blade 模板
## 簡介
Blade 視圖文件使用`.blade.php`作為文件擴展名,被存放在`resources/views`目錄。可以直接使用原生PHP代碼。所有 Blade 視圖文件都將被編譯成原生的 PHP 代碼并緩存,修改后才會重新編譯。
## 模板繼承
### 定義布局
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>
// 主視圖沒有內容
@yield('content')
// 主視圖有內容
@section('sidebar')
This is the master sidebar.
@show
```
### 擴展布局
```
<!-- 保存在 resources/views/child.blade.php 中 -->
// 繼承主視圖,目錄使用點符號間隔
@extends('layouts.app')
// 純文本顯示
@section('title', 'Page Title')
// 子視圖有內容,@parent
顯示主視圖內容,沒有 @parent
則覆蓋主視圖
@section('sidebar')
@parent
<p>This is appended to the master sidebar.</p>
@endsection
@section('content')
<p>This is my body content.</p>
@endsection
```
>[info] 提示:和上一個示例相反,這里的`sidebar`片段使用`@endsection`代替`@show`來結尾。`@endsection`指令僅定義了一個片段,`@show`則在定義的同時**立即 yield**這個片段。
## 組件 & 插槽
這是一個可復用的「alert」組件:
```
<!-- /resources/views/alert.blade.php -->
<div class="alert alert-danger">
{{ $slot }}
</div>
```
`{{ $slot }}`變量將包含我們想要注入到組件的內容。現在,我們使用 Blade 的`@component`指令構建這個組件:
```
@component('alert')
<strong>Whoops!</strong> Something went wrong!
@endcomponent
```
修改 alert 組件以允許其注入 「title」。命名插槽可以通過與其匹配的 「回顯」 變量顯示:
```
<!-- /resources/views/alert.blade.php -->
<div class="alert alert-danger">
<div class="alert-title">{{ $title }}</div>
{{ $slot }}
</div>
```
現在,我們能夠使用`@slot`指令向命名插槽注入內容。不在`@slot`指令內的內容都將傳遞給組件中的`$slot`變量:
```
@component('alert')
@slot('title')
Forbidden
@endslot
You are not allowed to access this resource!
@endcomponent
```
#### 向組件傳遞額外的數據
```
@component('alert', ['foo' => 'bar'])
...
@endcomponent
```
#### 給組件起別名
通常情況,別名在`AppServiceProvider`的`boot`方法中設置:
```
use Illuminate\Support\Facades\Blade;
Blade::component('components.alert', 'alert');
```
使用別名渲染
```
@alert(['type' => 'danger'])
You are not allowed to access this resource!
@endalert
```
如果沒有額外的插槽,可以省略組件參數:
```
@alert
You are not allowed to access this resource!
@endalert
```
## 顯示數據
通過包裹在雙花括號內的變量顯示傳遞給 Blade 視圖的數據。
```
Route::get('greeting', function () {
return view('welcome', ['name' => 'Samantha']);
});
// Blade視圖渲染
Hello, {{ $name }}.
// 可以在 Blade 的回顯語句中放置你想要的任意 PHP 代碼:
The current UNIX timestamp is {{ time() }}.
```
>[success] 提示:Blade`{{ }}`語句是自動經過 PHP 的`htmlspecialchars`函數傳遞來防范 XSS 攻擊的。
#### 顯示非轉義字符
默認情況下, Blade 中`{{ }}`語句自動經由 PHP 的`htmlspecialchars`函數傳遞以防范 XSS 攻擊。如果不希望數據被轉義,可以使用下面的語法:
```
Hello, {!! $name !!}.
```
>[success] 注意:在回顯應用的用戶提供的內容時需要謹慎小心。在顯示用戶提供的數據時,有必要一直使用雙花括號語法轉義來防范 XSS 攻擊。
#### 渲染 JSON
```
// 手動調用
<script>
var app = <?php echo json_encode($array); ?>;
</script>
// 使用`@json`Blade 指令代替手動調用`json_encode`函數
<script>
var app = @json($array);
</script>
```
#### HTML 實體編碼
默認情況下,Blade (以及 Laravel 的`e`助手)將對 HTML 實體雙重編碼。如果要禁用雙重編碼,可以在`AppServiceProvider`的`boot`中調用`Blade::withoutDoubleEncoding`方法:
```
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* 引導任意應用服務。
*
* @return void
*/
public function boot()
{
Blade::withoutDoubleEncoding();
}
}
```
### Blade & JavaScript 框架
由于很多 JavaScript 框架也使用花括號表明給定的表達式將要在瀏覽器中顯示, 可以使用`@`符號通知 Blade 渲染引擎某個表達式應保持不變。`@`符號將被 Blade 刪除;在 Blade 引擎中`{{ name }}`表達式將保持不變,取而代之的是 JavaScript 引擎將渲染該表達式。
```
<h1>Laravel</h1>
Hello, @{{ name }}.
```
**`@verbatim`指令**
如果要在大段的模板中顯示 JavaScript 變量,可以將 HTML 包裹在`@verbatim`指令中,這樣就不需要為每個 Blade 回顯語句添加`@`符號:
```
@verbatim
<div class="container">
Hello, {{ name }}.
</div>
@endverbatim
```
## 控制結構
### If 語句
```
@if (count($records) === 1)
I have one record!
@elseif (count($records) > 1)
I have multiple records!
@else
I don't have any records!
@endif
```
unless 除非(!)
```
@unless (Auth::check())
You are not signed in.
@endunless
```
isset 、empty
```
@isset($records)
// $records 被定義且不是 null...
@endisset
@empty($records)
// $records 為空...
@endempty
```
#### 身份驗證指令
`@auth`和`@guest`指令能夠用于快速確定當前用戶是經過身份驗證的,還是一個訪客:
```
@auth
// 此用戶身份已驗證...
@endauth
@guest
// 此用戶身份未驗證...
@endguest
```
校驗指定角色
```
@auth('admin')
// 此用戶身份已驗證...
@endauth
@guest('admin')
// 此用戶身份未驗證...
@endguest
```
#### 片段指令
可以使用`@hasSection`指令檢查片斷是否存在內容:
```
// 主視圖
@hasSection('navigation')
<div class="pull-right">
@yield('navigation')
</div>
<div class="clearfix"></div>
@endif
// 子視圖
@section('navigation')
<p>navigation</p>
@endsection
// 渲染結果
<div class="pull-right">
<p>navigation</p>
</div>
<div class="clearfix"></div>
```
### Switch 指令
```
@switch($i)
@case(1)
First case...
@break
@case(2)
Second case...
@break
@default
Default case...
@endswitch
```
### 循環
```
@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
```
在循環中終結循環或路過本次迭代:
```
@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
```
在嵌套循環中,可以借助`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`變量還包含其它幾種有用的屬性:**
### 注釋
Blade 注釋不會包含在返回給應用的 HTML 中:
```
{{-- This comment will not be present in the rendered HTML --}}
```
### PHP
可以在模板中使用`@php`指令執行原生的 PHP 代碼塊:
```
@php
//
@endphp
```
## 表單
### CSRF / Method
HTML 表單不能發出`PUT`、`PATCH`及`DELETE`請求,需要加入隱藏的`_method`域來模仿這些 HTTP 動詞。Blade 的`@method`指令能夠幫你創建這個域:
```
// 使用`@csrf`Blade 指令來生成令牌字段
<form method="POST" action="/profile">
@method('PUT')
@csrf
</form>
<form method="POST" action="/profile">
<input type="hidden" name="_method" value="PUT">
<input type="hidden" name="_token" value="{{ csrf_token() }}">
</form>
```
## 引入子視圖
Blade 的`@include`指令允許你從其它視圖中引入 Blade 視圖。父視圖中所有可用的變量都將在被引入的視圖中可用:
```
<div>
@include('shared.errors')
<form>
<!-- Form Contents -->
</form>
</div>
```
被包含的視圖不僅會繼承父視圖的所有可用數據,還能夠以數組形式向被包含的視圖傳遞額外數據:
```
@include('view.name', ['some' => 'data'])
```
如果傳遞給`@include`一個不存在的視圖,Laravel 會拋出錯誤。想要包含一個不能確定存在與否的視圖,需要使用`@includeIf`指令:
```
@includeIf('view.name', ['some' => 'data'])
```
想要包含一個依賴于給定布爾條件的視圖,可以使用`@includeWhen`指令:
```
@includeWhen($boolean, 'view.name', ['some' => 'data'])
```
要包含給定視圖數組中第一個存在的視圖,可以使用`includeFirst`指令:
```
@includeFirst(['custom.admin', 'admin'], ['some' => 'data'])
```
>[danger] 注意:應當盡量避免在 Blade 視圖中使用`__DIR__`和`__FILE__`魔術常量,因為它們將指向緩存中經過編譯的視圖的位置。
### 給被包含的視圖起別名
一個帶有如下內容的 Blade 視圖內容被存儲在`resources/views/includes/input.blade.php`文件中:
```
<input type="{{ $type ?? 'text' }}">
```
可以使用`include`方法為`includes.input`起一個叫做`input`的別名。通常,這會在`AppServiceProvider`的`boot`方法中完成:
```
use Illuminate\Support\Facades\Blade;
Blade::include('includes.input', 'input');
```
使用別名渲染
```
@input(['type' => 'email'])
```
### 為集合渲染視圖
```
@each('view.name', $jobs, 'job')
```
第一個參數是渲染數組或集合的每個元素的視圖片段。第二個參數是希望被迭代的數組或集合,第三個參數則是將被分配給視圖中當前迭代的變量名。例如,想要迭代`jobs`數組,通常會在視圖片段中使用`job`變量訪問每個任務。當前迭代的 key 將作為視圖片段中的`key`變量。
也可以向`@each`指令傳遞第四個參數。這個參數是當給定數組為空時要渲染的視圖片段。
```
@each('view.name', $jobs, 'job', 'view.empty')
```
>[danger] 注意:借助`@each`渲染視圖,無法從父視圖中繼承變量。如果子視圖需要這些變量,就必須使用`@foreach`和`@include`代替它。
## 堆棧
Blade 允許你將視圖壓入堆棧,這些視圖能夠在其它視圖或布局中被渲染。這在子視圖中指定需要的 JavaScript 庫時非常有用:
```
@push('scripts')
<script src="/example.js"></script>
@endpush
```
如果需要,可以多次壓入堆棧。通過向`@stack`指令傳遞堆棧名稱來完成堆棧內容的渲染:
```
<head>
<!-- 頭部內容 -->
@stack('scripts')
</head>
```
如果想要將內容預置在棧頂,需要使用`@prepend`指令:
```
@push('scripts')
This will be second...
@endpush
// 然后...
@prepend('scripts')
This will be first...
@endprepend
```
## Service 注入
`@inject`指令可以用于自 Laravel 的 [服務容器] 中獲取服務。傳遞給`@inject`的第一個參數是將要置入的服務變量名,第二個參數是希望被解析的類或接口名:
```
@inject('metrics', 'App\Services\MetricsService')
<div>
Monthly Revenue: {{ $metrics->monthlyRevenue() }}.
</div>
```
## 擴展 Blade
Blade 允許你使用`directive`方法自定義指令。當 Blade 編譯器遇到自定義指令時,這會調用該指令包含的表達式提供的回調。
下面的例子創建了`@datetime($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'); ?>
// 模板使用,顯示:22/22/2020 22:22
@datetime(new \DateTime())
```
>[danger] 注意:在更新 Blade 指令的邏輯之后,需要使用`view:clear`Artisan 命令刪除 Blade 視圖緩存。
### 自定義 If 語句
Blade 提供了`Blade::if`方法,它允許你使用閉包快速度定義條件指令。例如,定義一個校驗當前應用環境的自定義指令,可以在`AppServiceProvider`的`boot`方法中這樣做:
```
use Illuminate\Support\Facades\Blade;
/**
* 執行注冊后引導服務
*
* @return void
*/
public function boot()
{
Blade::if('env', function ($environment) {
return app()->environment($environment);
});
}
```
一旦定義了自定義條件指令,就可以在模板中輕松的使用:
```
@env('local')
// 應用在本地環境中運行...
@elseenv('testing')
// 應用在測試環境中運行...
@else
// 應用沒有在本地和測試環境中運行...
@endenv
```
- 入門指南
- 安裝
- 部署
- 基礎功能
- 路由
- 中間件
- CSRF 保護
- 控制器
- 請求
- 響應
- 視圖
- URL
- Session
- 表單驗證
- 錯誤
- 日志
- 前端開發
- Blade 模板
- 本地化
- 腳手架
- 編譯資源 Mix
- 安全相關
- 用戶認證
- API 認證
- 綜合話題
- 命令行
- 廣播
- 緩存
- 集合
- 事件
- 文件存儲
- 輔助函數
- 郵件發送
- 消息通知
- 擴展包開發
- 隊列
- 任務調度
- 數據庫
- 快速入門
- 查詢構造器
- 分頁
- 數據庫遷移
- 數據填充
- Redis
- Eloquent ORM
- 快速入門
- 速查表
- Artisan
- Auth
- Blade
- Cache
- Collection
- Composer
- Config
- Container
- Cookie
- DB
- Environment
- Event
- File
- Helper
- Input
- Lang
- Log
- Model
- Pagination
- Queue
- Redirect
- Request
- Response
- Route
- SSH
- Schema
- Security
- Session
- Storage
- String
- URL
- UnitTest
- Validation
- View