Blade 模板


Blade 範本

  • ##簡介
  • 模板繼承
    • 定義佈局
    • #繼承佈局
  • ##Components & Slots
  • 循環變數
    • 註解
    • #PHP
  • 表單
    • CSRF 欄位
    • Method 欄位
  • #引入子視圖

為集合渲染視圖

#堆疊服務注入

Blade 擴充功能

#自訂If 語句

##############簡介######Blade 是Laravel 提供的一個簡單又強大的模板引擎。和其他流行的 PHP 模板引擎不同,Blade 並不限制你在視圖中使用原生 PHP 程式碼。所有 Blade 視圖檔案都將被編譯成原生的 PHP 程式碼並快取起來,除非它被修改,否則不會重新編譯,這意味著 Blade 基本上不會為你的應用程式增加任何負擔。 Blade 視圖檔案使用 ###.blade.php### 作為檔案副檔名,被存放在 ###resources/views### 目錄。 #########################模板繼承################################################################

定義佈局

Blade 的兩個主要優點是模板繼承區塊。為方便入門,讓我們先透過一個簡單的例子來上手。首先,我們來研究一個「主」頁面佈局。因為大多數 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 指令是用來顯示指定部分的內容。

現在,我們已經定義了這個應用程式的佈局,接下來,我們定義一個繼承此佈局的子頁面。

#擴充佈局

#在定義子視圖時,使用Blade 的@extends指令指定子視圖要“繼承”的視圖。擴充自 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 指令向佈局的sidebar 追加(而非覆蓋)內容。在渲染視圖時,@parent 指令將會被佈局中的內容取代。

{tip} 和上一個範例相反,這裡的  sidebar 片段使用  @endsection 取代 @show# 來結束。 @endsection 指令只定義了一個片段, @show 則在定義的同時 立即 yield 這個片段。

Blade 視圖可以使用全域全域view 助手自路由中傳回:

Route::get('blade', function () {  
  return view('child');
});

組件& 插槽組件和插槽提供了與片段和佈局類似的好處;不過組件和插槽的思維模型更易於理解。我們先來看一個可重複使用的「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 指令的第二個參數。所有的資料將作為變更提供給元件模板:

@component('alert', ['foo' => 'bar'])  
  ...
@endcomponent

給元件起別名

如果元件儲存在子目錄中,你可能會想要給它們一個別名以方便訪問。舉例來說,如果一個Blade 元件儲存在 resources/views/components/alert.blade.php 中,。就可以使用component 方法將components.alert 的別名命名為alert。 . 通常情況下,這個過程將在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']);
});

就可以這樣利用name 變數顯示其內容:

Hello, {{ $name }}.

{tip} Blade { }} 語句是自動經過 PHP 的htmlspecialchars 函數傳遞來防範 XSS 攻擊的。

不限於顯示傳遞給視圖的變數的內容,你也可以顯示任一 PHP 函數的結果。實際上,你可以在Blade 的回顯語句中放置你想要的任意PHP 代碼:

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

顯示非轉義字元

預設情況下, Blade中{{ }} 語句自動經由PHP 的htmlspecialchars 函數傳遞以防範XSS 攻擊。如果不希望資料被轉義,可以使用下面的語法:

Hello, {!! $name !!}.

{note} 在回顯應用程式的使用者提供的內容時需要謹慎小心。在顯示使用者提供的資料時,有必要一直使用雙花括號語法轉義來防範 XSS 攻擊。

渲染JSON

有時,為了初始化一個JavaScript 變量,你可能會向視圖傳遞一個數據,並將其渲染成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 實體雙重編碼。如果要停用雙重編碼,可以在AppServiceProviderboot 中呼叫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 渲染引擎某個表達式應保持不變。範例如下:

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

在這個範例中, @ 符號將會被Blade 刪除;在Blade 引擎中{{ name }} 表達式將保持不變,取而代之的是JavaScript 引擎將渲染該表達式。

@verbatim 指令

如果要在大段的範本中JavaScript 變量,則可以將HTML 包裹在@verbatim 指令中,這樣就不需要為每個Blade 回顯語句新增@ 符號:

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

控制結構

除了模板繼承和資料顯示, 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

片段指令可以使用

@hasSection

指令檢查片段是否有內容:

@hasSection('navigation')
   <div class="pull-right">
 @yield('navigation')   
    </div>  
    <div class="clearfix">
    </div>
 @endif

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

{tip} 迴圈中可以使用循環變數 取得迴圈的可評估訊息,例如現在是處於迴圈的第一次迭代還是最後一次迭代中:

在循環中,也可以終結循環或路過本次迭代:

@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 變數也包含其它幾種有用的屬性:

#屬性描述
#$loop->index #當前迭代的索引(從0 開始計數)。
$loop->iteration#目前循環迭代 (從 1 開始計算)。
$loop->remaining迴圈中剩餘迭代的數量。
$loop->count#被迭代的陣列元素的總數。
$loop->first是否為迴圈的第一次迭代。
$loop->last#是否為循環的最後一次迭代。
$loop->depth#目前迭代的巢狀深度層級數。
$loop->parent#巢狀迴圈中,父迴圈的迴圈變數

註解

Blade 也允許在視圖中定義註解。不過與HTML 註解不同,Blade 註解不會包含在傳回給應用程式的HTML 中:

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

##PHP

#某些情況下,在視圖中嵌入PHP 程式碼很有用。可以在模板中使用 

@php 指令執行原生的PHP 程式碼區塊:

@php    //@endphp

{tip} 儘管Blade 提供了這個特性,但頻繁使用意味著模板中嵌入了過多的邏輯。

表單

CSRF 域

只要在應用程式中定義了HTML 表單,就一定要在表單中包含隱藏的CSRF 令牌域,這樣一來CSRF 保護中介軟體就能校驗請求。可以使用Blade 的

@csrf 指令產生令牌域:

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

Method 域

#HTML 表單不能發出

PUTPATCHDELETE 請求,需要加入隱藏的_method 域來模仿這些HTTP 動詞。 Blade 的@method 指令能夠幫你建立這個網域:

<form action="/foo/bar" method="POST">
    @method('PUT')    
    ...
</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'])

{note } 應盡量避免在Blade 視圖中使用

__DIR____FILE__ 魔術常數,因為它們將指向快取中經過編譯的視圖的位置。

給被包含的視圖起別名

如果你的 Blade 被包含視圖們儲存在子目錄中,你可能會想要為它們起個易於存取的別名。例如,一個帶有以下內容的Blade 視圖內容被儲存在resources/views/includes/input.blade.php 檔案中:

<input type="{{ $type ?? 'text' }}">

可以使用include方法為 includes.input 取一個叫做input 的別名。通常,這會在AppServiceProviderboot 方法中完成:

use Illuminate\Support\Facades\Blade;
Blade::include('includes.input', 'input');

一旦被包含的視圖擁有了別名,就可以像Blade 指令一樣使用別名渲染它:

@input(['type' => 'email'])

為集合渲染視圖

可以使用Blade 的@each 指令在一行中整合循環和包含:

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

第一個參數是渲染數組或集合的每個元素的視圖片段。第二個參數是希望被迭代的陣列或集合,第三個參數則是將被指派給視圖中目前迭代的變數名稱。例如,想要迭代 jobs 數組,通常會在視圖片段中使用 job 變數來存取每個任務。目前迭代的 key 將作為視圖片段中的 key 變數。

也可以向 @each 指令傳遞第四個參數。這個參數是當給定數組為空時要渲染的視圖片段。

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

{note} 使用 @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#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'); ?>

{note} 在更新 Blade 指令的邏輯之後,需要 Blade 視圖的所有快取。可以使用  view:clear Artisan 指令刪除 Blade 視圖快取。

自訂If 語句

在定義簡單的、自訂條件語句時,寫自定義指令比必須的步驟複雜。在這種情況下,Blade 提供了 Blade::if 方法,它允許你使用閉包快速度定義條件指令。例如,定義一個校驗目前應用環境的自訂指令,可以在 AppServiceProviderboot 方法中這樣做:

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
本文章首發在LearnKu.com 網站上。