1.继承BaseController预设公共变量
我们的视图有很多公共部分,比如导航菜单、底部信息、用户信息等,通常我们会以单独的视图组件来处理这些元素区块,但是如何从后端传递这些组件需要的数据变量是个问题,因为这些组件在多个页面中共用,从后端角度来看,会涉及到多个路由/控制器方法,难道我们要每次都重复获取并传递这些数据吗?有没有一种方式可以支持一处定义,多处复用?
那还用问,肯定有的叁,是不是脑海中闪现出了BaseController,嗯,是的,在没有接触laravel之前,通常我们处理上述问题的方法都是继承一个基控制器,把公共变量放在基类中实现,这样就能达到复用的目的,就像下面这样:
<?php
namespace App\Http\Controllers;
use App\Models\Config;
class BaseController extends Controller
{
private $cfg;
public function __construct()
{
$this->__init();
}
protected function __init()
{
$this->cfg = (object)Config::getAll();
}
}
然后在需要继承BaseController的控制器继承基类
<?php
namespace App\Http\Controllers;
use App\Models\Article;
class HomeController extends BaseController
{
public function __construct()
{
parent::__construct();
}
public function index()
{
$list = Article::getRecent();
return view('home.index', ['list'=> $list,'cfg'=>$cfg]);
}
}
当只有部分页面需要$cfg参数时或者其中部分页面需要别的公共变量时就需要继承另一个基类或者将全部的公共变量都放在BaseController里面,这么做能实现需求,但是有点浪费,不建议这么做。
2.使用laravel的view()->share方法
通过视图对象提供的 share 方法,我们可以在某个服务提供者如 AppServiceProvider 的 boot 方法中定义共享的视图变量:
view()->share('siteName', 'Laravel学院');
view()->share('cfg', cfg::all());
如果不指定视图组件的话,上述代码的含义是在所有视图中共享 cfg 变量,这当然是有点浪费了,不推荐这么做,我们通常会以闭包方式通过 View Composer 指定视图作用域来预设共享「变量」:
3.使用laravel的view()->composer方法
我们在 app/Http/ViewComposers 目录下创建一个自定义 View Composer 类 CategoryComposer.php:
<?php
/**
* Created by shmilyelva
* Date: 2019/3/27
* Time: 下午3:00
*/
namespace App\Http\ViewComposers;
use App\Models\Category;
use Illuminate\Contracts\View\View;
class CategoryComposer
{
private $category;
public function __construct(Category $category) {
$this->category = $category;
}
public function compose(View $view) {
$view->with('navs', $this->category->getNavs());
}
}
我们在 CategoryComposer 类的构造函数中注入了一个 Category 模型类,该模型类会在实例化的时候自动注入,然后我们将变量预设逻辑定义在 compose 方法中。这样,当我们在 View Composer 中调用 CategoryComposer 类的时候,compose 方法会被自动调用从而完成变量预设,在AppServiceProvider的boot()方法中增加如下内容
<?php
namespace App\Providers;
use App\Http\ViewComposers\CategoryComposer;
use App\Models\Config;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Schema;
use App\Observers\CommentObserver;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
//自定义类方式
view()->composer('layouts.main',CategoryComposer::class);//复杂的预设变量通过自定义类实现,导航栏视图共享
//闭包方式
view()->composer(['layouts.main','login.create','users.create'],function ($view) {
$view->with('cfg',(object)Config::getAll());
});//多个页面配置信息视图共享,如果是是全部页面可以使用*代替
}
//others
}
我们可以通过这段代码取代之前的闭包函数定义的 View Composer,但是除非预设逻辑很复杂,否则推荐使用闭包函数方式来实现,既简洁又减少了不必要的类初始化和方法调用对性能的损耗。
然后我们就可以愉快的在视图上使用这些预设变量了
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>@yield('title', '首页') - {{ $cfg->title }}</title>
<meta name="keywords" content="{{ $cfg->keyword }}"/>
<meta name="description" content="{{ $cfg->description }}" />
... //others