LoginSignup
0
0

More than 1 year has passed since last update.

1回のフォーム送信で複数のモデルを保存する方法

Posted at

 今回は一つのリクエストで、2つのモデルに対して保存処理を行う方法についてまとめます。例えば購入記録を保持するテーブルと、それに紐づく住所を保持するテーブルがあるとします。この時、どうすれば一回のリクエスで二つのテーブルに対しえて保存処理を行えるのでしょうか。その解決方法としてFormフォームオブジェクトパターンを新しく学んだので、備忘録としてまとめます。

Formフォームオブジェクトパターン

 Formフォームオブジェクトパターンとは、一つのフォーム送信で複数のモデルを操作したい時に使います。一つのフォーム送信で複数のモデルを操作する時には例えば以下のような問題が発生します。
①一度で複数のモデルのバリデーションを通過させないといけない
②バリデーションで弾かれた場合、複数のモデルのエラーメッセージを表示させないといけない。

これらを実装しようとすると複雑なコードを書かなくてはなりません。
そういった問題を解決するためにFormフォームオブジェクトパターンを使います。
使い方としては

①modelsディレクトリ直下に自作ファイルを作成し、複数のモデルに関する処理をまとめて記述する
②コントローラーに自作したクラスのインスタンス変数を渡す

順番に解説していきます。

『今回はどこかにお金を送金するような仕組みを考えます。
モデルとしては次の二つです。
①moneysテーブル(:money、:user_id)
②addressテーブル(:city, :house_number,:money_id)
この2つのモデルを1回のリクエストで保存するという処理です』

新たにmodelsディレクトリ直下にファイルを作成し、クラスを定義する

まずはmdoels直下に自作ファイルを作ります。moneysテーブルとaddressテーブルをまとめて、「money_address.rb」とします。
そして次に、この自作したクラスの中に、2つのテーブルのカラムをまとめて記述します。

money_address.rb
class OrderShipping
  include ActiveModel::Model
  attr_accessor :user_id, :money, :city, :house_number
end

ここでの注意点は、以下の二つです
①attr_accessor→ゲーターとセッターを定義してくれるメソッド。
②include ActiveModel::Model→ActiveModel::Modelをincludeすることで、form-withの引数にできたり、バリデーションの設定ができたりします。

自作クラスにバリデーションを設定する

 次に自作したmoney_address.rbにmoneyテーブルとaddressテーブルのバリデーションを全て設定します。

money_address.rb
 class MoneyAddress
  include ActiveModel::Model
  attr_accessor :user_id, :money, :city, :house_number
   with_options presence: true do
    validates :user_id
    validates :money
    validates :city
    validates :house_number
   end

自作したクラスにバリデーションが設定できるのは、ActiveModel::Modelをincludeしているからです。

自作クラスに保存する処理を設定する

 次に自作したorder_shippng.rbにmoneyテーブルとaddressテーブル、両方を保存する処理を加えます。

money_address.rb
class MoneyAddress
  include ActiveModel::Model
  attr_accessor :user_id, :money, :city, :house_number
   with_options presence: true do
    validates :user_id
    validates :money
    validates :city
    validates :house_number

  def save
    money = Money.create(user_id: user_id, money: money)

    Address.create(city: city, house_number: house_number, money_id: money_id)
  end
end

以上で自作クラスの設定は完了です。

コントローラーで自作クラスを呼び出す

コントローラーで、以下のように自作クラスを呼び出します。

money_controller.rb
class MoneyController < ApplicationController

  def index
  end

  def new
    @money_address = MoneyAddress.new
  end

  def create
    @money_address = MoneyAddress.new(money_params)
    if @money_address.valid?
      @money_address.save
      redirect_to root_path
    else
      render :new
    end
  end

  private

  def money_params
    params.require(:money_address).permit(city: city, house_number: house_number, money: money).merge(user_id: current_user.id)
  end

end

@money_address.valid?で自作クラスで定義したバリデーションを全て実行し、
@money_address.saveで自作クラスで定義した二つのモデルを保存しています。

Formフォームオブジェクトパターンの実装が完です。
これによりviewのform-withに@money_addressを渡すことができるので、

①一度で複数のモデルのバリデーションを通過させないといけない
②バリデーションで弾かれた場合、複数のモデルのエラーメッセージを表示させないといけない。
という問題を解決できます。

まとめ

 今回は一つのフォーム送信で複数のモデルを操作する時に使う、Formフォームオブジェクトパターンについてまとめました。初学者にとっては迷いやすい部分だと思うので、今後も学んでいきたいと思います。

0
0
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
0
0