Laravel在ViewServiceProvider获取路由前缀时出现null错误
我完全懂你遇到的坑——在ViewServiceProvider的registerViewFinder里想拿路由前缀时,直接报了Call to a member function getPrefix() on null的错,核心原因是服务提供者的register方法执行时机太早了,这时候请求刚进来,路由还没完成解析匹配,$request->route()自然拿不到东西。
下面给你两个靠谱的解决方案,都是避开“时机太早”这个问题的:
方案一:给后台路由组加专属中间件
既然所有后台路由都在cp前缀的分组里,咱们直接给这个分组加个中间件,专门处理后台模板的加载逻辑,干净又高效。
生成后台模板中间件
先跑命令创建中间件:php artisan make:middleware SetCpTemplate打开
app/Http/Middleware/SetCpTemplate.php,写逻辑:<?php namespace App\Http\Middleware; use Closure; use Illuminate\Support\Facades\View; class SetCpTemplate { public function handle($request, Closure $next) { // 强制把后台视图的加载目录指向cp文件夹 View::addLocation(resource_path('views/cp')); // 如果你的后台视图用了命名空间,也可以这么写: // View::addNamespace('cp', resource_path('views/cp')); return $next($request); } }注册中间件
去app/Http/Kernel.php里,找到$routeMiddleware数组,加一行:'cp.template' => \App\Http\Middleware\SetCpTemplate::class,更新路由组配置
把刚注册的中间件加到后台路由组里:Route::group([ 'namespace' => 'Cp', 'prefix' => 'cp', 'middleware'=>['admin', 'auth', 'cp.template'] ], function() { Route::get('/' , 'IndexController@index' )->name('index_cp'); });调整ViewServiceProvider的动态模板逻辑
原来的数据库动态切换逻辑可以保留,移到boot方法里,同时用请求路径判断是否是后台路由:public function boot() { View::composer('*', function ($view) { $request = app(\Illuminate\Http\Request::class); // 排除后台路由,剩下的走数据库动态模板逻辑 if (!$request->is('cp/*') && !$request->is('cp')) { $templateFolder = // 你的数据库查询代码,比如从配置表取模板名 View::addLocation(resource_path("views/{$templateFolder}")); } }); }
方案二:监听路由匹配事件
Laravel有个RouteMatched事件,会在路由成功匹配后触发,这时候拿路由前缀就绝对安全了。
生成事件监听器
跑命令创建监听器:php artisan make:listener SetTemplateBasedOnRoute打开
app/Listeners/SetTemplateBasedOnRoute.php写逻辑:<?php namespace App\Listeners; use Illuminate\Routing\Events\RouteMatched; use Illuminate\Support\Facades\View; class SetTemplateBasedOnRoute { public function handle(RouteMatched $event) { $route = $event->route; if ($route->getPrefix() === 'cp') { // 后台路由强制用cp模板 View::addLocation(resource_path('views/cp')); } else { // 非后台路由从数据库取模板 $templateFolder = // 你的数据库查询逻辑 View::addLocation(resource_path("views/{$templateFolder}")); } } }注册事件监听器
去app/Providers/EventServiceProvider.php的$listen数组里加一行:protected $listen = [ \Illuminate\Routing\Events\RouteMatched::class => [ \App\Listeners\SetTemplateBasedOnRoute::class, ], ];
为啥原来的代码会报错?
再给你补个知识点:Laravel服务提供者的register方法是在容器绑定阶段执行的,这时候应用还没开始处理请求的路由匹配,$request->route()自然是null。哪怕是boot方法,直接拿路由也可能在某些场景(比如请求静态资源)下出问题,所以用中间件或者路由匹配事件是更稳妥的选择。
内容的提问来源于stack exchange,提问作者hretic

