簡體   English   中英

如何組織用於REST關系的控制器? [Laravel路線]

[英]How to organise controllers for REST relationships? [Laravel routes]

想象一下實體GenreBook

每個都有API資源端點/genre/book 在Laravel中的路線可能是:

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

我想要關系的終結點。 GET /genre/1/book ,以獲取Genre 1下的書籍。

什么是最佳做法? 將處理程序放在GenreControllerBookController還是全新的控制器中?

在旁注中,我使用的是dingo-api程序包,但我認為這沒有任何區別。

一種選擇是僅使用當前的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並且也將其作為路線參數刪除。

然后,您的路線將是:

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

請注意,使用此方法,您仍將以相同的方式使用storeupdate方法,即在請求中傳遞genre_id

希望這可以幫助!

盡管這里沒有100%的具體答案-每個資源都有一個控制器幾乎總是比較容易的,然后如果您想直接點擊它,那么就很容易做到這一點。

如果您可以(通常)堅持執行主要操作(索引,創建,存儲,顯示,編輯,更新,刪除),那么它將變得更加容易。 它可以使事情井井有條,將來從事您的項目的開發人員將可以輕松地遵循結構。

精彩閱讀:DHH用於大本營的方法: http ://jeromedalbert.com/how-dhh-organizes-his-rails-controllers/

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

因此,@ 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);
}

在我的實現中,我只為GETDELETE請求設置查詢參數,為POSTPUT輸入參數。

然后,您可以為genre.book資源重新使用BookController ,從$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將genre參數傳遞給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可以從route參數設置genre_id輸入變量之前進行。

在我看來,您有兩種選擇:

  1. 使用中間件將route參數移動到Request輸入上。
  2. 將其放在FormRequestauthorize方法上(我還沒有測試過,因為我不想在這里放這樣的邏輯)。

Laravel:如果您不使用Lumen,我建議您看一下@RossWilson的答案,我認為它稍微清潔了一些。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM