「Railsのコントローラーの仕事は何か? - スモールスタート」という記事がRailsのコントローラーを設計する際のとても良い指針となっているので、ちょくちょく参考にさせて頂いております。ここからさらに考えたことをまとめてみます。
Railsのコントローラーの責務を意識することが大事です。あくまでもよいコントローラーとなっているかは、URLで表されるリソースに対して、コントローラーのアクションの責務が明確であるか です。
scaffoldで考える
scaffoldを作ってみましょう。
rails g scaffold post title content:text
以下の様なコントローラーが出来上がります。
class PostsController < ApplicationController before_action :set_post, only: [:show, :edit, :update, :destroy] respond_to :html def index @posts = Post.all respond_with(@posts) end def show respond_with(@post) end def new @post = Post.new respond_with(@post) end def edit end def create @post = Post.new(post_params) @post.save respond_with(@post) end def update @post.update(post_params) respond_with(@post) end def destroy @post.destroy respond_with(@post) end private def set_post @post = Post.find(params[:id]) end def post_params params.require(:post).permit(:title, :content) end end
ここで注目したいのは、show
, edit
, update
, destroy
でset_post
がbefore_action
で実行される理由です。この理由をどう捉えるか考えましょう。私は2つの理由が思い浮かびました。
- 同じ処理が複数回あるのでまとめた
- 各アクションのメインの責務ではないのでフィルターにまとめた
同じ処理が複数回あるのでまとめた
見ての通りDRYのため、と考えることが出来ますね。初めてこのscaffoldの処理を見た時こう思いました。これの考えは正解だと思っています。
各アクションのメインの責務ではないのでフィルターにまとめた
ただ、私はこちらを推します。
- showは、URLで指定されたリソースを 表示する
- editは、URLで指定されたリソースを更新するためのフォームを表示する
- updateは、URLで指定されたリソースを更新する
- destroyは、URLで指定されたリソースを削除する
大事なのは、強調した部分です。コントローラの責務は、URLで表されるリソースに、どんな処理をするかです。
共通化している部分は、URLで表されるリソースを取得する部分なので、コントローラーで大事な処理というよりも、注目すべき対象となるリソース です。それはメインではないのでフィルターに任せます。メインというよりサブぐらいですかね。
なので、インスタンス変数 として目立たせるのです。「Railsのコントローラーの仕事は何か? - スモールスタート」でも言及されているようにviewにレンダリングするために渡したいというよりも、そのアクションで 注目している対象はなにか? という点が大事です。
そしてアクションの中には大事な処理だけを書くというスタンスです。
この考えで行くと@posts = Post.all
や@post = Post.new
もフィルターで良いと考えます。改修した、scaffoldが以下です。
class PostsController < ApplicationController # URLからリソースを取得しインスタンス変数にセットするフィルターを定義 before_action :set_new_post, only: [:new, :create] before_action :set_posts, only: [:index] before_action :set_post, only: [:show, :edit, :update, :destroy] respond_to :html def index respond_with(@posts) end def show respond_with(@post) end def new respond_with(@post) end def edit end def create @post.save respond_with(@post) end def update @post.update(post_params) respond_with(@post) end def destroy @post.destroy respond_with(@post) end private def set_post @post = Post.find(params[:id]) end def set_posts @posts = Post.all end def set_new_post @post = Post.new(post_params) end def post_params params.fetch(:post).permit(:title, :content) end end
このように考えると、複数呼ばれるからset_postを切り出すという方針ではないので、どのコントローラーでも(showだけなどset_postが1度しか呼ばれない場合も)
- URLから対象となるリソースを決定する処理をフィルターで定義
- リソースに対して行う処理を各アクションに書く
という流れに統一出来ると思います。良いRailsのコントローラーとは処理が同じようなものとなると考えます。
複雑な処理となった場合は、パーフェクトRailsにもあるように、Service層の導入によりコントローラーをシンプルに保つことができます。また、インスタンス変数を整理するためには、apotonick/cells、drapergem/draperなどでviewを整理することを検討しましょう。
- 作者: すがわらまさのり,前島真一,近藤宇智朗,橋立友宏
- 出版社/メーカー: 技術評論社
- 発売日: 2014/10/31
- メディア: Kindle版
- この商品を含むブログ (1件) を見る