Laravel如何向继承Blade公共模板的多视图统一传递数据
Blade模板继承机制不会自动传递父模板依赖的变量,所有被加载的视图(包括你继承的公共侧边栏asidenav)所需的变量,都必须在当前请求的渲染链路中完成注入。你之前仅在home控制器方法中传递了$categories变量,category方法未传递该变量,侧边栏模板拿不到数据就会抛出未定义变量错误。
小提示:你当前
asidenav模板内的<span>标签未闭合,建议补全为<span>{{ $category->name }}</span></a>,避免前端渲染结构异常。
不需要在每个控制器方法重复查询传参,以下三种方案都可以实现一次定义、所有加载asidenav的页面自动获取分类数据,按需选择即可:
方案1:视图合成器(推荐,按需注入无冗余)
这是Laravel官方专门针对公共模板数据注入场景设计的功能,可以精准给指定模板注入数据,不会产生多余的全局开销。
- 打开
app/Providers/ViewServiceProvider.php(如果不存在该文件可以直接新建),在boot方法中注册视图合成逻辑:
<?php namespace App\Providers; use App\Models\Category; use Illuminate\Support\Facades\View; use Illuminate\Support\ServiceProvider; class ViewServiceProvider extends ServiceProvider { public function boot() { // 仅给asidenav侧边栏模板注入分类数据 View::composer('asidenav', function ($view) { $view->with('categories', Category::select('id', 'name')->get()); }); } }
- 打开
config/app.php,在providers配置数组中添加该服务提供者的注册项:
'providers' => [ // 其他框架自带、已安装扩展的服务提供者 App\Providers\ViewServiceProvider::class, ],
配置完成后直接删除所有控制器方法中重复查询、传递$categories的代码即可,所有加载/继承asidenav模板的页面都会自动拿到分类数据。
方案2:全局视图共享(适合全站通用数据)
如果分类导航是全站所有页面都要展示的公共模块,可以直接在AppServiceProvider的boot方法中全局共享变量:
<?php namespace App\Providers; use App\Models\Category; use Illuminate\Support\Facades\View; class AppServiceProvider extends ServiceProvider { public function boot() { // 所有视图都可以直接访问$categories变量 View::share('categories', Category::select('id', 'name')->get()); } }
注意:该方案会让所有视图(包括不需要分类数据的接口、页面)都携带该变量,会产生不必要的查询开销,非全站通用数据不推荐使用。
方案3:模板内直接查询(适合小项目快速实现)
如果项目体量小,不想新增服务提供者配置,可以直接在asidenav.blade.php模板中查询分类数据,完全脱离控制器传参依赖:
<ul> @foreach(\App\Models\Category::select('id','name')->get() as $category) <li> <a href="{{ route('category',$category->id) }}"> <span>{{ $category->name }}</span> </a> </li> @endforeach </ul>
注意:该方案将数据库查询逻辑写在视图层,不符合MVC分层规范,仅适合小型项目快速迭代使用,中大型项目不推荐。
无论选择哪种方案,都可以给分类查询加缓存,避免每次请求都触发数据库查询:
$categories = \Cache::remember('nav_categories', 3600, function () { return Category::select('id', 'name')->get(); });
后续分类数据有更新时,清除nav_categories缓存即可同步最新数据。
内容的提问来源于stack exchange,提问作者Ray Paras

