Rails アソシエーションを活用して、コントローラー処理を短く記載する方法
前提
・UsersモデルとBoardsモデルをアソシエーションしていること(1対多) (has_manyとbelong to)
#app/models/user.rb class User < ApplicationRecord authenticates_with_sorcery! has_many :boards, dependent: :destroy
#app/models/board.rb class Board < ApplicationRecord belongs_to :user
実装
実装前にアソシエーションを入れない場合を記入
#app/controllers/boards_controller.rb # 初期化した後に値を代入 def create @board = Board.new(board_params)※ @board.user_id = current_user.id if @board.save redirect_to boards_path, success: t('.success') else flash.now[:danger] = t('.fail') render :new end end
次にアソシエーションを活用した場合
#アソシエーションを活用 def create @board = current_user.boards.build(board_params) if @board.save redirect_to boards_path, success: t('.success') else flash.now[:danger] = t('.fail') render :new end end
※引数に(board_params)となっているのは、boards_controller.rbにストロングパラメーターを設定している為。
private def board_params params.require(:board).permit(:title, :body) end
説明
モデルにhas_many :boardsを設定することで、userオブジェクトでboardsというメソッドが使えるようになる。 current_user.boards.newと実行することで、user_idを登録したboardオブジェクトを初期化できる。 また、newの引数にパラメータを渡すことで入力フォームから渡ってきた情報のオブジェクトが作成できる。 このように、アソシエーションで関連したオブジェクトを初期化する際は、通常の初期化と区別してnewではなくbuildと記載することがある。 ちなみに、buildはnewのエイリアス(別名)のため、どちらで記載しても挙動は変わらない。
以上の記法は開発現場では当たり前のように使われているため、開発者としてはこの記載に慣れておく必要がある。 この記載を知らなければオブジェクトを初期化してから必要なデータを代入したり、引数のパラメータ情報のmergeなどが必要になる。
また、「掲示板のオブジェクトを初期化する」「初期化したオブジェクトにログインユーザーのIDを登録する」と2行に分かれた情報を理解させるより、 「ログインユーザーに関連した掲示板のオブジェクトを作成する」と記載した方が読み手に取ってのコードの理解が早くなる。 このように1行で記載することで、全体のコード量が削減されるので、他のより重要な処理の記載に集中できるようになるメリットがある。
パラメータにmergeすることで1行でも記載できるが、アソシエーションで関連しているオブジェクトであることを強調した方が良い。 また、モデルにアソシエーションの関連性がない情報を登録する場合は、mergeを使って短く記載することが可能。
#例 Board.new(board_params.merge(user_id: current_user.id))
追記1 コントローラー側で値を保持しない処理の場合、ローカル変数にする
CommentsControllerのcreateアクションでリダイレクトさせる場合、不要なインスタンス変数を使っていないこと
# 例 def create comment = current_user.comments.build(comment_params) if comment.save redirect_to board_path(comment.board), success: 省略 else redirect_to board_path(comment.board), danger: 省略 end end
成功時、失敗時どちらもredirectするので、インスタンス変数にしてもそれをViewで参照することがない。 今までは登録失敗時にrenderで表示することが多かったため、@commentの保存失敗時にredirect_toを使うことに疑問を持った方もいるかもしれません。
今回は、今までのような入力フォームの再表示とは違い、コメントの作成・失敗後は紐づいている掲示板の詳細画面に遷移して、そこで関連するコメントの一覧を読み込んで表示する仕様になっています。
仮にrenderで表示する場合は、Board#showのアクションと同様に、@boardのインスタンス変数を取得している必要があります。
入力するコメントは1カラムのみで、エラーメッセージも1種類でフォームの入力内容によって出し分けたりしないことから、解答例ではredirect_toを使って実装しました。 この場合にはインスタンス変数を使う必要はないので、ローカル変数を使っています。