简介
Blade 是 Laravel 提供的模板引擎,它简单强大。不像其他的 PHP 模板引擎,Blade 允许在视图中使用原生 PHP 代码。实际上,所有的 Blade 视图最终都会被编译成原生 PHP 代码,缓存在 storage/framework/views
文件夹中。Laravel 使用的是这些编译后的缓存文件,而不是视图本身,所以,Blade 对于应用程序来说是零开销的。当你修改了视图文件,那么它会重新编译并缓存,以便使用。Blade 视图以 blade.php
为后缀名,一般存放于 resources/views
文件夹中。
模板继承
定义布局文件
Blade 模板引擎的主要两个优点是 “模板继承” 和 “区块”。举一个简单的例子,一个项目里,几乎所有的页面都是一样的布局,这时候就可以把这个布局提炼出来,作为母版页,继承了这个母版页的的页面都有一样的布局效果,成为母版页的子页。母版页还叫布局文件,布局文件就是一个 Blade 视图:
<!-- Stored in 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>
布局文件里除了基础的 HTNL 标签,还使用了两个指令:@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')
<p> This is appended to the master sideebar</p>
@endsection
@section('content')
<p>This ismy body content. </p>
@endsection
可以看到,在布局文件中使用 @yield
指令的地方,在子页中仍然使用 @section
注入内容;在布局文件中使用 @section
指令定义的一个好处是:在子页中使用 @section
注入时,可以使用 @parent
指令附加(而非重写)在布局文件中的内容,而在布局文件中使用 @yield
指令定义的地方是做不到的。@parent
指令会在视图渲染的时替换成布局文件里的内容。
注意,与在文件布局里定义的 sidebar
不同的是,子页里使用 @endsection
结束,而非 @show
。因为 @endsection
仅用来定义区块,而 @show
是用来定义、立马产出区块的。
从路由中直接返回视图文件,要用到全局辅助函数 helper
:
Route::get('blade', function(){
return view('child');
})
组件&插槽
组件和插槽提供了类似布局和区块的优点。而组件和插槽的心智模型更符合直觉。设想一下,在我们的项目中有一个可重复的 “弹框” 组件:
<!-- Stored resource/views/alter.blade.php -->
<div class="alter alter-danger">
{{ $slot }}
</div>
{{ $slot }} 表示插入组建的内容。构建此组件,是使用 Blade 的@component
指令:
@component('alter')
<strong>Whoops!</strong> Something went wrong!
@endcomponent
在这个场景里,{{ $slot }} 变量的内容是:
<strong>Whoops! </strong> Something went wrong!
有时一个组件需要多个插槽。这时,只需要稍改组件代码,定义一个 “标题” 插槽,这个插槽称命名插槽。命名插槽是通过简单的 “打印” 匹配其名称的变量来显示内容的:
<!-- Stored resources/views/alter.blade.php-->
<div class="alter alter-danger">
<div class="alter-title">{{ $title }}</div>
{{ $slot }}
</div>
为名名插槽注入内容,使用 @slot
指令。所有不在 @slot
指令里的内容都会传递给组件里的 $slot
变量。
@compontent ('alter')
@slot('title')
Forbidden
@endslot
You are not allowed to access this resource!
@edcompontent~
为组建传递额外数据:
有时需要为组建件递额外数据。为此,可以为 @conponent
指令传递第二个数组参数。指定要传递的额外。据所有过去的额外数据作为变量,在组件模板里都可以取的:
@component
<compontent('alter', ['foo' => 'bar');
.....
@endCompontent
显示数据
向 Blade 视图传递数据,是通过将变量包裹在 [ ]
里实现的:
Route::get('greeting', function(){
return view('welcome', [ 'name' => ''Samantha']);
})
下面就可以使用 name
变量显示内容了:
Hello! {{ $name }}
{{ }}
是 Blade 视图的打印语句,当然,打印语句里不限制只能打印变量内容,也可以使用 PHP 函数。实际上,打印语句这里可以使用任何 PHP 代码:
The current UNIX timestamp is {{ time() }}
显示非转移数据
默认,所有传递给 Blade {{ }}
语句的内容都会使用 htmlspecialchar
函数处理、将内容转义,避免 XSS 攻击。如果无需转义输出的内容,可以使用下面语法:
Hello! {{!! $name !!}}.
不过千万要小心,应该优先选择使用转义的 {{ }}
语法避免 XXS 攻击。因为,有时你很难避免用户有意的、无意的数据输入。
Blade & JavaScript 框架
由于一些 JavaScript 框架也使用花括号 {{ }}
语法解析内容,为了区分开 Blade 和这些用到的 JavaScript 框架,你可以使用 @
符号来告诉 Blade 模板引擎说,这个地方不要动,保持原样就可以了:
<h1>Laravel</h1>
Hello! @{{ $name }}
上面例子里, @
符号会从 Blade 中删除,而 {{ $name }}
会保持原样,用来给你的 JavaScript 框架渲染使用。
@verbatim
指令
如果使用 JavaScript 框架渲染的模块区域很大,这时就可以使用 @verbatim
指令包裹这些模板区域,这样就避免了在每个Blade 打印语句前都跟上 @
符号的麻烦:
@verbatim
<div class="container">
Hello! {{ $name }}
</div>
@endverbatim
控制结构
除了模板继承和显示数据,Blade 还未常见的 PHP 结构提供了快捷方式,比如条件谈判和循环。这些快捷方式提供了一种非常干净、简洁的控制结构,并且保持了原生 PHP 的形式。
if 语句
构造 if
语句使用 @if
、@elseif
、@else 和 endif
指令。这些指令和原生 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.
@endless
除了讨论过的条件判断指令,Blade 还提供了 @isset
和 @empty
指令,都与在原生 PHP 里的对应功能相同:
@isset($recodes)
// $recodes is defined and is not null ...
@endisset
@empty($recodes)
// $recodes is "empty" ...
@endempty
认证
@auth
和 @guest
指令用来判断当前用户是认证用户还是游客:
@auth
// The user is authenticated...
@endauth
@guest
// The useris not authenticated...
@endguest
Switch 语句
switch
语句使用 @switch
、@case
、@break
、@default
和 @endswitch
指令构建:
@switch($i)
@case(1)
First case...
@break;
@case(2)
Second case...
@break;
@default
Default case...
@endswitch
循环
Blade 还提供了循环方面的指令。再一次声明:这里的每一个指令都与原生 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 user </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>
@break($user->number == 5)
@endforeach
$loop
变量
在循环时,内部有一个可用变量 $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
变量提供的有用属性列举如下:
注释
Blade 的注释语法是 {{-- --}}
。不像 HTML 注释,Blade 模板注释不会出现在最终渲染的 HTML 代码里:
{{-- This is comment will not be present in the rendered HTML --}}
PHP
Blade 允许在一个视图里通过 @include
指令引入一个视图。使用 @include
指令的视图称为父级视图,引入的视图称为子视图。父级视图的所有变量在子视图里都是可以获得的:
<div>
@include('shared.error')
<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'])
注意:不要使用 __DIR__
和 __FILE__
常量引入 Blade 视图,因为程序实际使用的是编译后的、缓存在 storage/framework/views
文件夹中的文件。
为集合渲染视图
@each
指令整合了循环数据和引入视图的功能:
@each(''view.name', $job, 'job')
第一个参数是为数组或者集合中每个元素渲染数据时指定的视图,第二个参数是要便利的数组或集合,第三个参数是当前迭代的元素数据在子视图中的变量名。在上面的例子里,我们遍历的数组是 jobs
, 在 view.name
视图里渲染 job
变量,当前迭代的 key 使用 key
变量获取。
也可以为 @each
指令传递第四个参数,这是在给定数组元素为空时渲染的视图:
@each($bollean, 'view.name', [ 'some' => 'data'], 'view.empty')
注意:使用 @each
渲染的视图不从父级模板里继承变量。如果子视图还需要这些变量,你应该使用 foreach
和 @include
指令组合。
堆栈
Blade 允许你使用 @push
指令向命名堆栈里推入内容,命令堆栈以 @stack
指令定义,可以定义在普通视图或者布局文件里,推入的内容会在视图或者布局文件里渲染出来。这在为子视图添加额外的 JavaScript 库的场景下特别有用。
<!-- 在视图或者布局文件里定义堆栈 -->
<head>
<!-- Head Contents -->
@stack('scripts')
</head>
<!-- 在子视图中推入堆栈内容 -->
@push('scripts')
<script src="/example.js"></script>
@endpush
你可以尽你自己所需的多次向堆栈推入数据。
服务注入
@inject
指令可以从 Laravel 的服务提供者中获得服务。传递给 @inject
指令的第一个参数是一个变量名,获得的服务就是存放在这个变量里,第二个参数就是你要解析的服务的类名或接口名。
@inject('metrics', 'App\Services\MetricesService')
<div>
Monthly Revenue: {{ $metrics->monthlyRevenue() }}.
</div>
扩展 Blade
Blade 允许你使用 directive
方法创建自定义指令。当 Blade 方法遇到自定义指令时,会将接收到的表达式 (expression)放在自定义指令的回调闭包里处理。
在下面的例子里,我们创建了一个 @datatime($var)
指令,$var
应该是一个DateTime
实例:
namespace App\Providers;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Perform post-registration booting of services.
*
* @return void
*/
public function boot()
{
Blade::directive('datetime', function($expression){
return "<?php echo ($expression->format('m/d/Y H:i'); ?>)";
});
}
/**
* Register bindings in the container.
*
* @return void
*/
public function register()
{
//
}
}
可以看到,我们在传过来的表达式上使用了 format
方法。在这个例子里,@datetime($var)
指令最终生成的 PHP 代码如下:
<?php echo ($var)->format('m/d/Y H:i'); ?>
注意:在更新自定义指令后,需要删除所有的视图文件,可以用 view:cache
Artisan 命令实现。
自定义 if 语句
当自定义涉及简单的条件判断时,使用 Blade::directive
的方式可能会变得稍复杂些。为此, Blade 引入了 Blade::if
方法使用闭包来快速自定义条件判断指令。例如,我们定义一个指令,判断当前的项目环境,可以选择在 AppServiceProvider
的 boot
里做这件事情:
use Illuminate\Support\Facades\Blade;
/**
* Perform post-registration booting of services.
*
* @return void
*/
public function boot()
{
Blade::if('env', function($expression){
return app()->environment($environment);
})
}
定义好后,咱使用它:
@env('local')
// The application is in the local environment...
@else
// The application is not in the local environment...
@endenv
是不是很简单呢 ??