Blade 允许你继承、修改和包含其他的视图
用 @yield 和 @section .. @show 设置布局
- 定义一个布局视图,也就是模板
<!-- 父模板 resources/views/layouts/master.blade.php --><!doctype html><html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>@yield('title','主页')</title></head><body><div class="container">@yield('content')</div>@section('footerScripts')<script src="app.js"></script>@show</body></html>
@yield 和 @section 基本完全一样,都用在父模板中,子视图会覆盖其中的内容,都可以设置默认值; 唯一区别在于,@section 可以在子模版中到用父模板的内容, 通过 @parent。
- 定义一个子视图,继承刚才的模板
<!-- 继承父模版 resources/views/dashboard.blade.php -->
@extends('layouts.master')
@section('title','dashboard')
@section('content')
Welcome to your application dashboard!
@endsection
@section('footerScripts')
@parent
<script src="dashboard.js"></script>
@stop
tip:
@show VS @endsection VS @stop
`@show` 用于父模板中;
`@endsection` 用于子模版中
`@stop` 是 `@endsection` 的 别名
@extends
表示一个视图继承另一个视图,即: 这个视图不是单独存在的,它的作用更像是定义了各个部分的内容,而不是一个完整的 html 页面。
@extends('layouts.master') 表示它继承自 resources/views/layouts/master.blade.php 这个文件
每个视图只能继承一个文件,且继承应该放在第一行
@section and @endsection
简单内容支持 @section('title','dashboard') 这种写法,@stop 是 @endsection 的别名, 两者完全一样
@parent
父模板中,使用 @section('footerScripts') 含默认值的形式定义了 footerScript 部分,意味着,在子模版中,我们有两种选择,1.完全重写父模板的内容,2.在父模板的基础上追加内容。
@parent 即表示获取父模板的内容,故例子中意味着追加
包含视图
@include
<!-- resources/views/home.blade.php -->
<div class="content" data-page-name="{{ $pageName }}">
<p>Here's why you should sign up for our app: <strong>It's Great.</strong></p>
@include('sign-up-button', ['text' => 'See just how great it is'])
</div>
<!-- resources/views/sign-up-button.blade.php -->
<a class="button button--callout" data-page-name="{{ $pageName }}">
<i class="exclamation-icon"></i> {{ $text }}
</a>
传递数据,你可以通过第二个参数,显式的传递(如 $text),也可以引用包含文件中的可以用的变量(如 $pageName)。
more ↓
{{-- Include a view if it exists --}}
@includeIf('sidebars.admin', ['some' => 'data'])
{{-- Include a view if a passed variable is truth-y --}}
@includeWhen($user->isAdmin(), 'sidebars.admin', ['some' => 'data'])
{{-- Include the first view that exists from a given array of views --}}
@includeFirst(['customs.header', 'header'], ['some' => 'data'])
@each
当你需要循环 @include 一些视图时,考虑 @each
渲染文章和文章评论
{{ 原始状态 }}
@if (count($article->comments)>0)
@foreach( $article->comments as $comment )
@include('comments.item',['comment'=>$comment])
@endforeach
@else
@include('comments.no-item')
@endif
{{ 优化1 }}
@forelse($article->comments as $comment)
@include('comments.item')
@empty
@include('comments.no-item')
@endforelse
{{ 优化2 }}
@each('comments.item', $article->comments, 'comment', 'comments.no-item')
参数说明
- 循环要渲染的视图
- 需要循环的数据,通常是 array 或 collection
- 赋予当前渲染视图的变量名
- 当循环数据(参数2)为空时,渲染的视图 【可选】
使用 Stacks
当多次继承时,如果每个页面都要向父模板添加内容,基本的 include 就不好处理了。
特别是某些页面或 sections 有一些特定的 css 或 js 需要加载时,include 是不合适的,因为依赖顺序的问题。 Stacks 被用来解决这些问题。
在父模板定义一个 stack ,它仅仅是一个占位符,在子模版,你可以 @push/@endpush 推一些内容到 堆栈(stack) 的尾部,或者,@prepend/@endprepend 插入一些内容到 堆栈(stack) 的首部 。
<!-- resources/views/layouts/app.blade.php -->
<html>
<head><!-- the head --></head>
<body>
<!-- the rest of the page -->
<script src="/css/global.css"></script>
<!-- the placeholder where stack content will be placed -->
@stack('scripts')
</body>
</html>
<!-- resources/views/jobs.blade.php -->
@extends('layouts.app')
@push('scripts')
<!-- push something to the bottom of the stack -->
<script src="/css/jobs.css"></script>
@endpush
<!-- resources/views/jobs/apply.blade.php -->
@extends('jobs')
@prepend('scripts')
<!-- push something to the top of the stack -->
<script src="/css/jobs--apply.css"></script>
@endprepend
将生成
<html>
<head><!-- the head --></head>
<body>
<!-- the rest of the page -->
<script src="/css/global.css"></script>
<!-- the placeholder where stack content will be placed -->
<script src="/css/jobs--apply.css"></script>
<script src="/css/jobs.css"></script>
</body>
</html>
使用 Components 和 Slots
当你包含的模板要引入 大块 内容做变量时,考虑使用 compontens 和 slot
<!-- resources/views/partials/modal.blade.php -->
<div class="modal">
<div>{{ $content }}</div>
<div class="close button etc">...</div>
</div>
<!-- in another template -->
@include('partials.modal', [
'body' => '<p>The password you have provided is not valid. Here are the rules for valid passwords: [...]</p><p><a href="#">...</a></p>'
])
{{ 优化 }}
<!-- resources/views/partials/modal.blade.php -->
<div class="modal">
<div>{{ $slot }}</div>
<div class="close button etc">...</div>
</div>
<!-- in another template -->
@component('partials.modal')
<p>The password you have provided is not valid.
Here are the rules for valid passwords: [...]</p>
<p><a href="#">...</a></p>
@endcomponent
多个 slot
<!-- resources/views/partials/modal.blade.php -->
<div class="modal">
<div class="modal-header">{{ $title }}</div>
<div>{{ $slot }}</div>
<div class="close button etc">...</div>
</div>
@component('partials.modal')
@slot('title')
Password validation failure
@endslot
<p>The password you have provided is not valid.Here are the rules for valid passwords: [...]</p>
<p><a href="#">...</a></p>
@endcomponent
为你的组件起一个别名
// AppServiceProvider@boot
Blade::component('partials.modal', 'modal');
<!-- in a template -->
@modal
Modal content here
@endmodal
视图合成器 View Composers 和 服务注入
你可以在路由中,直接传递参数给视图, 第三章 👇
Route::get('passing-data-to-views', function () {
return view('dashboard')
->with('key', 'value');
});
但是,有时,你发现你需要一遍遍的传递相同的数据给视图,或者一些 header 之类的,你愿意每次都使用路由传递吗?
使用 View Composers 绑定数据
谢天谢地,View Composers 是一种简单的方式,它允许你指定一些视图加载时,传递给他们某些数据。
假想: 我们有一个侧边栏目 partials.sidebar (resources/views/partials/sidebar.blade.php) , 显示最新的文章,多个页面都要用到这个侧边栏。 我们应该怎么传递数据呢?
- 路由
Route::get('home', function () {
return view('home')
->with('posts', Post::recent());
});
Route::get('about', function () {
return view('about')
->with('posts', Post::recent());
});
// 不断重复,烦人的方式
- 使用 share
// Some service provider
public function boot()
{
// ...
view()->share('recentPosts', Post::recent());
}
// view()->share 分享变量到每一个视图,过火了,哥们
- 基于
View Composers使用闭包,实现视图范围控制
// Some service provider@boot
view()->composer('partials.sidebar', function ($view) {
$view->with('recentPosts', Post::recent());
});
多个视图时
view()->composer(['partials.header', 'partials.footer'], function($view){
$view->with('key','data');
})
//or 使用通配符
View::composer('partials.*', function () {
$view->with('recentPosts', Post::recent());
});
- 基于
View Composers使用类,实现视图范围控制
最灵活 但 也最复杂- 创建
RecentPostsComposer.php官方文档建议放在App\Http\ViewComposers
- 创建
<?php
namespace App\Http\ViewComposers;
use App\Post;
use Illuminate\Contracts\View\View;
class RecentPostsComposer
{
public function compose(View $view)
{
$view->with('recentPosts', Post::recent());
}
}
这个 Composer 被调用时 compose() 就会执行 ,这里绑定了数据。
2. 将这个类放到某个地方, 一般创建 `ViewComposerServiceProvider`, 暂时先在 `App\Providers\AppServiceProvider` 中注册
// App\Providers\AppServiceProvider
public function boot()
{
view()->composer('partials.sidebar', \App\Http\ViewComposers\RecentPostsComposer::class);
}
Blade service injection
有三种类似的数据我们需要传到视图:
- 需要迭代的数据集合
- 需要在视图显示的对象
- 产生数据或视图的服务
注入服务最简单的方式如下:
通过路由注入服务
// 参数中指定类型 为服务
Route::get('backend/sales', function (AnalyticsService $analytics) {
return view('backend.sales-graphs')
->with('analytics', $analytics);
});
// 视图中使用服务
<div class="finances-display">
{{ $analytics->getBalance() }} / {{ $analytics->getBudget() }}
</div>
Blade service injection 可以让事情变得更简单
直接注入服务到视图
@inject('analytics', 'App\Services\Analytics')
<div class="finances-display">
{{ $analytics->getBalance() }} / {{ $analytics->getBudget() }}
</div>
自定义 Blade 指令
你可以自定义你的 blade 指令,你可能会想,这是一种简化代码的好方式,例如: @button('buttonName') 替换一堆按钮的 html ,但是,这是糟糕的,更好的方式可能是包含一个视图模板。
自定义模板指令,倾向于简化最有用的重复逻辑。例如,@if(auth()->guest()) (检验用户是否登录),我们可以定义一个 @ifGuest 的指令替代它。
一般创建一个服务注册自定义指令, 暂时先在 App\Providers\AppServiceProvider 中注册
public function boot()
{
Blade::directive('ifGuest', function () {
return "<?php if (auth()->guest()): ?>";
});
}
我们已经注册了这个指令,当我们写 @ifGuest 时,它将会被替换成 <?php if (auth()->guest()): ?>。
tip: 你可能会写出如下代码。
Blade::directive('ifGuest', function () {
// 这是反模式的 ! 不要复制.
$ifGuest = auth()->guest();
return "<?php if ({$ifGuest}): ?>";
});
以上代码,假设自定义从指令每次页面加载时都重新生成,但实际情况是,blade 缓存会缓存指令,所以以上代码将达不到你的预期。
带参数的自定义指令
// Binding
Blade::directive('newlinesToBr', function ($expression) {
return "<?php echo nl2br({$expression}); ?>";
});
// In use
<p>@newlinesToBr($message->body)</p>
例子: 多用户站点使用自定义指令
假设我们有个多用户应用,有 www.myapp.com, client1.myapp.com, client2.myapp.com 等网站。
我们已经封装了一个类 (Context), 用来捕获当前访问者相关的一些信息和逻辑,例如,当前的认证用户是谁,他正在访问公共站点还是子站点。
我们可能经常做出如下操作:
@if (app('context')->isPublic())
© Copyright MyApp LLC
@else
© Copyright {{ app('context')->client->name }}
@endif
我们可以简化 @if (app('context')->isPublic()) 为 @ifPublic,之后代码将如下
// Binding
Blade::directive('ifPublic', function () {
return "<?php if (app('context')->isPublic()): ?>";
});
// In use
@ifPublic
© Copyright MyApp LLC
@else
© Copyright {{ app('context')->client->name }}
@endif
进一步简化,使用 if
// Binding
Blade::if('ifPublic', function () {
return (app('context'))->isPublic();
});
