让我们讨论一些很多人无意中发现的事情:您不能直接在控制器的构造函数中使用会话数据。
此更改是在 Laravel 5.3 中引入的,当时重新连接了中间件管道以使全局范围与会话数据一起工作。
在这篇文章中,我们将更详细地研究这个问题,并研究解决它的各种方法
问题
人们想要在控制器的构造函数中获取会话数据的原因有很多。让我们看两个例子:
-
将当前用户共享给所有视图:
与其
auth()->user()
在您的视图中使用,不如在所有视图中使用一个全局变量有时更好$user
。实现这一目标的一种快速方法是向基本控制器添加一个构造函数并从那里共享它:public function __construct() { view()->share('user', auth()->user()); }
-
在需要会话的控制器上设置属性:
如果您在所有方法中都需要相同的信息,您可以选择将其设置为控制器上的属性:
public function __construct() { $this->account = Account::find(session('account_id')); }
在这两种情况下,构造函数中的代码都假定会话已启动并准备就绪。但是如果我们尝试运行代码,它就会失败。此时,请求还没有被路由并且没有可用的会话数据。
解决方案#1:Authenticated
事件
对于每个请求,Laravel 都会Authenticated
在用户通过身份验证时触发事件。您可以在服务提供商中收听此事件,并$user
从那里分享:
use Illuminate\Auth\Events\Authenticated;
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
$this->app['events']->listen(Authenticated::class, function ($e) {
view()->share('user', $e->user);
});
}
}
解决方案 #2:内联视图编辑器
如果您更愿意从基本控制器共享用户,则可以使用内联视图编辑器:
public function __construct()
{
view()->composer('*', function ($view) {
$view->with('user', auth()->user());
});
}
由于回调只会在视图组合时执行,因此您可以访问会话。
解决方案 #3:将属性转换为方法
让我们回到问题部分的第二个例子。与其直接在构造函数中查找当前帐户,不如将其逻辑放入其自己的方法中,仅在实际需要时调用它:
public function index()
{
$account = $this->account();
// use it as before...
}
protected function account()
{
return Account::find(session('account_id'));
}
这对于新项目非常有用,但如果您已经有一个依赖于所有控制器方法都可用的属性的大项目,则可能会有点乏味。对于这种情况,您可以使用内联中间件。
解决方案#4:内联中间件
内联中间件比其他解决方案复杂一些,但可能是最灵活的。
您之前可能已经看到$this->middleware('auth')
,它直接从控制器构造函数中注册现有的中间件。您可能不知道的是,您实际上可以使用闭包动态定义中间件。
使用内联中间件,我们可以挂钩请求的路由,以便我们可以完全访问会话:
public function __construct()
{
$this->middleware(function ($request, $next) {
$this->account = Account::find(session('account_id'));
return $next($request);
});
}
我们还可以使用内联中间件来共享$user
所有视图:
public function __construct()
{
$this->middleware(function ($request, $next) {
view()->share('user', $request->user());
return $next($request);
});
}
如您所见,这比其他解决方案稍微复杂一些,但它为我们提供了最大的灵活性