1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

[Rails] 1つのフォームから複数のテーブルに情報を保存する

Last updated at Posted at 2021-01-02

はじめに

「ユーザー情報」「本」「ジャンル」、の3つの情報を1つのフォームから送信して、
別々のテーブルに保存する機能をFormオブジェクトを使って実装する。

具体的に言うと、自分で定義したクラスに、form_withメソッドに対応する機能とバリデーションを行う機能を持たせ、そのクラスのインスタンスメソッドで複数のテーブルへの保存の処理などを行う。

Formオブジェクト

Formオブジェクトとは、Railsのデザインパターンの1つです。

このデザインパターンは、「1つのフォーム送信で複数のモデルを操作したい場合」や、
「テーブルに保存しない情報に対するバリデーションを行いたい場合」に使います。
元々modelにバリデーション等様々な処理が書かれているものを、form層に切り出すことで多くのmodelで同一の処理を使えるということです。


#実装
form層に切り出すモデルファイル
にinclude 「ActiveModel::Model」 と 「attr_accessor」 を設定する

user_book.rb
class UserBook
  include ActiveModel::Model
  attr_accessor :nickname, :age, :gender, :book_title, :genre

end

○ActiveModel::Model

あるクラスにActiveModel::Modelをincludeすると、そのクラスのインスタンスはActiveRecordを継承したクラスのインスタンスと同様に form_with や render などのヘルパーメソッドの引数として扱えたり、バリデーションの機能が使えるようになります。
Formオブジェクトパターンを実装するためにActiveModel::Modelをincludeしたクラスのことを「Formオブジェクト」と呼ぶこともあります。

○attr_accessor

記述したクラスに、ゲッターとセッターを定義してくれるメソッドです。与えられた引数をもとに属性を設定し、これを取得するメソッド(ゲッター)と更新するメソッド(セッター)を定義してくれます。

・値の取得ができるメソッドの定義(ゲッター)
・値の更新ができるメソッドの定義(セッター)

今回のフォームで利用する全ての属性(つまり、保存したい各テーブルのカラム名全て)についてゲッターとセッターを定義することで、このFormオブジェクトのインスタンスを生成した際にform_withの引数として利用できるようになります。

○切り出す処理を記述

user_book.rb
class UserBook
  include ActiveModel::Model
  attr_accessor :nickname, :age, :gender, :book_title, :genre

  with_options presence: true do
    validates :nickname
    validates :age
    validates :genre
  end

  def save
    user = User.create(nickname: nickname, age: age, gender: gender)
    Book.create(book_title: book_title, user_id: user.id)
    Genre.create(genre: genre, user_id: user.id)
  end
end

設定したいバリデーション全てと、
def save ~ end 内では
フォームからパラメーターとして送られてきた情報をテーブルに保存する処理を書いています。
※Book・Genreオブジェクトでどのユーザーと結びついているのかを保存したいので、
ユーザー情報をuserに代入して、user_idカラムにuser.idで取得した1人のユーザー情報を保存させています。

○Formオブジェクトのインスタンスを生成

フォームから送られてきた全ての情報をハッシュ形式に整形するbook_paramsを定義し、
createアクションでこれを呼び出してUserBookクラスのインスタンスを作成することに利用しています。

books_controller.rb
class BooksController < ApplicationController
  def index
  end

  def new
    @user_book = UserBook.new
  end

  def create
    @user_book = UserBook.new(book_params)
      if @user_book.valid?
        @user_book.save
        redirect_to action: :index
      else
        render action: :new
      end
  end

  private
  
  def book_params
    params.require(:user_book).permit(:nickname, :age, :gender, :book_title, :genre)
  end
end

○Formオブジェクトのインスタンスを引数として渡す

new.html.erbのform_withメソッドに、作成したFormオブジェクトのインスタンスを引数として渡し、入力した情報がMVCを通って処理されるようにします。

app/views/books/new.html.erb
<%= form_with(model: @user_book, url: donations_path, local: true) do |form| %>
  <%= render 'error_messages', model: @user_book %>

  # フォーム内容

<% end %>

この記事は下記を参考させていただいております。
https://product-development.io/posts/rails-design-pattern-form-objects

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?