所以@Chris 建议为关系使用专用控制器,而@RossWilson 有一种天才的方法可以为关系重用控制器(至少对于加载 Book 的操作)。
不幸的是 Lumen 的RouteProvider 返回一个简单的数组,因此没有$request->route($param) 更重要的是$request->route()->forgetParameter($param) 的便利性。
=== 新解决方案 ===
我最终做了与@RossWilson 建议的基本完全相同的事情,只是以 Lumen 支持的方式。我没有在 Controller 的 __construct 中获取 Route 参数,而是制作了一个将 Route 参数 移动到 Request 的输入和查询数组的中间件。
中间件看起来像这样:
public function handle($request, $next)
{
if ($genre_id = Arr::get($request->route()[2], 'genre')) {
// Add 'genre_id' to the input array (not replacing it if it already exists).
$request->merge(['genre_id' => $request->input('genre_id', $genre_id)]);
// Add 'genre_id' to the query array.
$request->query->add('genre_id', $genre_id);
// Forget the route parameter
// Has to be done manually, because Lumen...
$route = $request->route();
$request->setRouteProvider(function() use ($route) {
Arr::forget($route[2], 'genre');
return $route;
});
}
// Pass the updated $request to $next.
return $next($request);
}
在我的实现中,我只为GET 和DELETE 请求设置查询参数,并为POST 和PUT 设置输入参数。
然后您可以将BookController 重新用于genre.book 资源,从$request->query('genre_id') 过滤并从$request->input('genre_id') 关联关系。
=== 原始解决方案 ===
相反,我最终得到了一个专用的关系控制器GenreBookController,它继承自非关系控制器BookController。由于需要匹配的方法声明(请参阅下面的 $book_id = null 解决此问题的方法),它并没有想象中的那么优雅,但它非常苗条和枯燥。
GenreBookController extends BookController:
protected function addGlobalScope($genre_id)
{
// Thanks Ross Wilson for the global scope suggestion.
Book::addGlobalScope('genreScope', function ($query) use ($genre_id) {
$query->where('genre_id', $genre_id);
});
}
public function show(Request $request, $genre_id, $book_id = null)
{
$this->addGlobalScope($genre_id);
return parent::show($request, $book_id);
}
对于 BookController,show 方法照常进行(它不需要知道 show 上的额外参数)。
我还想出了一个简单的方法让GenreBookController 将流派参数传递给store 方法:
public function store(Request $request, $genre_id)
{
// This way lets an input genre_id override the Route parameter.
$request->merge(['genre_id' => $request->input('genre_id', $genre_id)]);
// This way forces the Route parameter to be used over input parameters.
$request->merge(['genre_id' => $genre_id]);
return parent::store($request);
}
再一次,对于BookController,一切照旧,它当然可以对通过$request->input('genre_id') 的流派进行任何验证/授权。这样就没有重复的验证和授权逻辑。
关于 FormRequests 的说明
如果您使用 FormRequests 来验证genre_id,则验证发生在之前GenreBookController 可以从路由参数中设置genre_id 输入变量。
在我看来,你有两个选择:
- 使用中间件将路由参数移动到请求输入。
- 把它放在你的
FormRequest 的authorize 方法上(我没有测试过这个,因为我不喜欢把这样的逻辑放在这里)。
Laravel:如果你不使用 Lumen,我建议看一下@RossWilson 的回答,我认为它更简洁一些。