2017-03-04 62 views
0

想象一下实体GenreBook如何组织REST关系的控制器? [Laravel routes]

每个都有API资源端点/genre/book。在Laravel路线可能是:

$app->resource('/genre', GenreController::class); 

我想要一个关系的端点。 GET /genre/1/book,根据Genre#1获取书籍。

这里最好的做法是什么?将处理程序放在GenreController,BookController或者可能是一个全新的控制器?

在旁注中,我使用的是dingo-api包,但我不认为这有什么区别。

+0

您genre.books的输出是否与'books'相同,但是它的作用范围是'genre',或者您将包含其他信息。另外,你会想要从'genre.books'创建一个完整的资源吗?还是只是'index'和'show'方法? –

+0

另外,您使用的是什么版本的Laravel? –

+0

@RossWilson我正在使用Lumen/Laravel 5.4。我希望我想返回相同的结果,并且我喜欢使'store'和'update'方法一致的想法,但我不想结束太多不必要的复杂性。 – Zoon

回答

0

所以@Chris建议了一个专门的关系控制器,而@RossWilson有一种天才的方式来重新使用控制器来处理关系(至少对于加载Book的行为)。

不幸流明的RouteProvider返回一个简单的数组,因此不具有$request->route($param)方便,更重要的是,$request->route()->forgetParameter($param)

===新的解决方案===

我落得这样做基本上为@RossWilson建议完全一样,只是在被流明支持的方式。我没有在控制器的__construct中获得路由参数,而是创建了一个中间件,将路由参数移动到请求的输入和查询数组上。

中间件看起来是这样的:

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); 
} 

我在执行我只能为GETDELETE请求的查询参数,并输入参数POSTPUT

然后,您可以重新使用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额外参数)。

我也想出了一个简单的方式为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输入变量。

当我看到它,你有两个选择:

  1. 使用中间件路线参数移动到请求输入。
  2. 把它放在你的FormRequestauthorize方法中(我没有测试过,因为我不喜欢把这种逻辑放在这里)。

Laravel:如果你不使用流明,我建议考虑看看@ RossWilson的答案,这在我看来是一个小清洁。

1

虽然这里没有100%的具体答案 - 它几乎总是更容易为每个资源设置一个控制器,如果您想要直接点击该关系,那么它的关系就更容易了,它的声音是您想要的去做。

如果你可以(通常)坚持主要行动(索引,创建,存储,显示,编辑,更新,删除),它会使它更容易。它将使事情保持有组织,未来的项目开发人员将能够轻松地遵循这一结构。

大阅读: DHH为大本营的方法:http://jeromedalbert.com/how-dhh-organizes-his-rails-controllers/

白宫API指南:https://github.com/WhiteHouse/api-standards#white-house-web-api-standards

2

一种选择是只使用当前BooksController。添加以下到您BooksController

public function __construct(Request $request) 
{ 
    if ($genreId = $request->route('genre')) { 

     $request->route()->forgetParameter('genre'); 

     Book::addGlobalScope('genreScope', function ($query) use ($genreId) { 
      $query->whereGenreId($genreId); 
     }); 
    } 
} 

这将使你的书是由Genre范围,并删除它作为路由PARAM。

然后,你的路线也只是:

$api->resource('genre.book'); 

请注意,使用这种方法,你仍然会使用你的storeupdate方法以同样的方式,即通过genre_id的请求。

希望这会有所帮助!

+0

谢谢,我喜欢可重用性! '$ request-> route($ param = null)'对我来说是新的,你能指出我正确的方向,至于为什么route('genre')'是流派ID? – Zoon

+0

@Zoon当你做'Route :: resource()'(或者'$ app')时,它基本上会为你生成一个资源的所有路由。以'show',资源将创建基本上是'get('genre/{genre}/book/{book}')'的路线。 '{genre}'基本上是说uri段是可变的,并且可以通过索引“流派”访问,因此'$ request-> route('流派')' –

+0

@ zoon这是否解决了您的问题?如果是这样,你会把它标记为正确的。 –