【Laravel】ルーティングがすっきり!リソースコントローラの活用

learn laravel resource controller Webアプリ開発

今回はリソースコントローラのテンプレートに合わせてリファクタリングした過程をご紹介します。
Laravelで簡単なブログアプリを作った後、Laravel Breezeを適用しようとして、routes/web.phpが上書きされてしまいました。
改めて、URIとHTTPメソッドの組み合わせごとに、コントローラのメソッドを割り当て、ルート名を付ける作業が面倒に思いました。
そこで、モデル作成のコマンドでリソースコントローラのオプションがあるくらいだから、ルーティングもテンプレート的なものを利用してもっと効率的に実装できるのでは?と考え、調べてみるとありました。
調べて実践した内容をご紹介します。

リソースコントローラへのリファクタリング

リソースコントローラとは

参考:Laravel 10.x コントローラ
上記サイトを参考に自分なりの言葉でまとめると、下記特徴を持つコントローラです。

  • CRUDと呼ばれるリソースの作成、読み取り、更新、削除のいずれか、またはすべてを行う
  • Artisanコマンドのmake:controller、make:modelで–resourceオプションを指定して手軽に作成できる
  • URIやメソッド名が機械的に決まるため命名に悩まなくて済む、かつ、ブレない

修正するコード

今回修正するコードは下記記事で作成したコードになります。

過去記事関係なくリソースコントローラに変更する時の要所だけ見たい人もbefore/afterのコードを記載するので、参考になるかと思います。

修正箇所と内容

リソースコントローラを使うにあたり、下記コードを修正します。

No.修正ファイル修正内容
1routes/web.php各ルートの定義をRoute::resourceにまとめる
2app/Http/Controllers/ArticleController.phpリソースコントローラに合った引数、処理に修正
3resources/view/index.blade.phpルートの変更に伴い、リンクの修正
ブログアプリの修正箇所

ルーティングの修正(routes/web.php)

下記before/afterのようにコードを修正します。

~略~
Route::controller(ArticleController::class)->group(function (){
    Route::get('/articles', 'index')->name('articles.index');
    Route::get('/articles/new', 'create')->name('articles.create');
    Route::post('/articles', 'store')->name('articles.store');
    Route::get('/articles/{id}', 'show')->name('articles.show');
    Route::get('/articles/{id}/edit', 'edit')->name('articles.edit');
    Route::put('/articles/{id}', 'update')->name('articles.update');
    Route::delete('/articles/{id}', 'destroy')->name('articles.destroy');
});
Route::redirect('/','/articles');
Route::resource('articles', ArticleController::class);
Route::redirect('/','/articles');

9行かかっていたコードが1行で済むようになりました。
どのコントローラでどのテーブルを扱っているか、わかりやすくもなりますね!

ただし、この修正だけでブログサイトにアクセスすると下記のようなエラーが発生します。

Missing required parameter for [Route: articles.show] [URI: articles/{article}] [Missing parameter: article].

これは、beforeで{id}とした箇所がリソースコントローラの制約に違反しているためです。
リソースコントローラでは、下記表のようにルート名やコントローラのメソッド名が決まるため、それに合わせる必要があります。
そのため、コントローラとビューも修正します。

No.HTTPメソッドURIメソッド名ルート名
1GET/articlesindexarticles.index
2GET/articles/createcreatearticles.create
3POST/articlesstorearticles.store
4GET/articles/{article}showarticles.show
5GET/articles/{article}/editeditarticles.edit
6PUT/PATCH/articles/{articles}updatearticles.update
7DELETE/articles/{articles}destroyarticles.destroy
リソースコントローラでの登録

コントローラの修正(app/Http/Controllers/ArticleController.php)

下記before/afterのようにコードを修正します。

~略~
    /**
     * Display the specified resource.
     * Article $articleを$idに変更
     */
    public function show($id)
    {
        $article = Article::find($id);
        return view('show',['article' => $article]);
    }
~略~
    public function edit($id)
    {
        $article = Article::find($id);
        $categories = Category::all();
        return view('edit',['article' => $article, 'categories' => $categories]);
    }
~略~
    public function update(Request $request, $id)
    {
        Article::find($id)->update([
            'title' => request('title'),
            'content' => request('content'),
            'category_id' => request('category_id'),
        ]);
        return redirect('articles/'.$id);
    }
~略~
    public function destroy($id)
    {
        $article = Article::find($id);
        $article->delete();
        return redirect('/articles');
    }
~略~
    /**
     * Display the specified resource.
     * $idをArticle $articleに戻す
     */
    public function show(Article $article)
    {
        return view('show',['article' => $article]);
    }
~略~
    public function edit(Article $article)
    {
        $categories = Category::all();
        return view('edit',['article' => $article, 'categories' => $categories]);
    }
~略~
    public function update(Request $request, Article $article)
    {
        $article->update([
            'title' => request('title'),
            'content' => request('content'),
            'category_id' => request('category_id'),
        ]);
        return redirect('articles/'.$article->id);
    }

Article::find($id)としていた処理が不要になり、引数のarticleをそのまま使うだけになりました。
これはLaravelが、パスパラメータ(URIで{}で囲まれた部分)とコントローラメソッドの型名が一致する場合、自動でパスパラメータに紐づく値をデータベースから取得してくれるためです。

resoures/views/index.blade.php

ビューでルート名を使用して、リンクを作成していたため、このファイルも修正が必要です。

<!--修正前-->
<td><a href={{ route('articles.show', ['id' => $article->id]) }}>{{ $article->title }}</a></td>

<!--修正後-->
<td><a href={{ route('articles.show', ['article' => $article]) }}>{{ $article->title }}</a></td>

また、URIをハードコードしている他の個所も修正しておくと良いかもしれません。

感想

ブログアプリ作成時に各URIを1つずつ実装することもLaravelを理解する上で良かったのですが、リソースコントローラを使うとかなりすっきりしたコードになることがわかりました。
これからはリソースコントローラを使い、それで足りない場合は本当に1つのコントローラで実装する必要があるのか、2つに分けた方が良いのではないか、を考えて開発していこうと思いました。

コメント

タイトルとURLをコピーしました