2017-03-04 64 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這是否解決了您的問題?如果是這樣,你會把它標記爲正確的。 –