企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
[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 ```