【发布时间】:2019-12-31 10:49:09
【问题描述】:
假设我们有一个显示 20 部电影的随机列表的网站。但是,登录用户可以选择他们最喜欢的电影,因此将显示这些电影。此电影列表显示在主页和其他一些页面中。
为了遵循 DRY 原则,我们可以将这个逻辑封装在它自己的类中,然后将这个类注入到任何需要显示电影列表的地方。此类还将具有将在整个应用程序中使用的其他方法。例如,还有一种方法可以获取一个随机电影。
该类可能如下所示(请注意这是一个简化示例):
class MovieService
{
/** @var Collection $movies */
protected $movies;
public function __construct()
{
$this->movies = Auth::check() ? Auth::user()->favoriteMovies : $this->randomMovies();
}
public function getRandomMovies(): Collection
{
return $this->movies->random(20);
}
public function getOneRandom(): Movie {
return $this->movies->random();
}
protected function randomMovies() {
return Movie::inRandomOrder()->take(20)->get();
}
}
注意:请注意,这是一个示例,有些地方可以改进。
由于此类可以在同一个请求中多次使用,因此最好将其设置为 IoC 容器中的 singleton,以便实例化时运行的查询不会运行不止一次。
但是,现在我们遇到了一个问题。我们需要在控制器的私有方法中使用此类。我们可以直接调用应用容器,如app() 或App::make(),但我们希望避免具有自定义依赖项的外观和全局助手。
class HomeController extends Controller
{
/** @var MovieService $movieService */
protected $movieService;
public function __construct(MovieService $movieService)
{
$this->movieService = $movieService;
}
public function index()
{
$movies = $this->getMovies();
return view('home', compact('movies'));
}
protected function getMovies()
{
// Let's imagine there's some extra logic here so that we would actually need this method.
return $this->movieService->getRandomMovies();
}
}
我们发现了一个问题。 控制器的构造函数在中间件管道之前运行,这意味着没有会话,因此没有用户标识。现在MovieService 中的Auth::check() 始终返回false,因此将始终显示默认电影。
你会怎么做才能解决这个问题?
【问题讨论】:
-
在实例化
MovieService之前有没有试过在构造函数中调用中间件? -
相关: github.com/laravel/framework/issues/15072 - 当它被改变时,这是一个相当热门的话题。
-
@Script47 你能举个例子吗?
-
在
$this->movieService = [...]之前尝试添加$this->middleware('<middleware-here>')。 -
@Script47 我认为这不会做任何事情。会话中间件始终运行,因此没有必要像
$this->middleware()那样调用它,因为依赖项仍在控制器中,并且仍会在中间件之前运行。
标签: php laravel oop ioc-container