1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Rails】Formオブジェクトについて

Posted at

記事概要

Ruby on RailsにFormオブジェクトを実装する方法について、まとめる

前提

  • Ruby on Railsでアプリケーションを作成している

サンプルアプリ(GitHub)

Formオブジェクトパターンとは

1つのフォームから送信した情報を複数のテーブルに保存する、Railsを利用する開発における実装パターン

処理イメージ

Image from Gyazo
Image from Gyazo

ActiveModel::Model

クラスにActiveModel::Modelをincludeすると、そのクラスのインスタンスはActiveRecordを継承したクラスのインスタンスと同様にform_withrenderなどのヘルパーメソッドの引数として扱え、バリデーションの機能を使用できるようになる

Formオブジェクトパターンを実装するためにActiveModel::Modelをincludeしたクラスのことを、「Formオブジェクト」と呼ぶこともある

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

  1. app/modelsディレクトリ配下に、手動でdonation_address.rbを作成する
  2. 以下のように記述し、クラスを定義する
    donation_address.rb
    class DonationAddress
    end
    

手順2.作成したクラスにform_withメソッドに対応する機能とバリデーションを行う機能を持たせる

  1. ActiveModel::Modelをincludeする
    donation_address.rb
    class DonationAddress
      # ActiveModel::Modelをinclude
      include ActiveModel::Model
    end
    

手順3.保存したい複数のテーブルのカラム名全てを属性値として扱えるようにする

  1. attr_accessorを使用し、donationsテーブルとaddressesテーブルに保存したいカラム名を、すべて指定する
    donation_address.rb
    class DonationAddress
      # ActiveModel::Modelをinclude
      include ActiveModel::Model
      # donationsテーブルとaddressesテーブルに保存したいカラム名
      attr_accessor :postal_code, :prefecture, :city, :house_number, :building_name, :price, :user_id
    end
    

手順4.バリデーションの処理を書く

  1. donationモデルのバリデーションを削除する
    donation.rb
    class Donation < ApplicationRecord
      belongs_to :user
      has_one :address
    
      # 1以上、1000000以下の整数を許可する
      # validates :price, presence: true, numericality: {only_integer: true, greater_than_or_equal_to: 1, less_than_or_equal_to: 1000000, message: "is invalid"}
    end
    
  2. addressモデルのバリデーションを削除する
    address.rb
    class Address < ApplicationRecord
      belongs_to :donation
    
      # 数字3桁、ハイフン、数字4桁の並びのみ許可する
      # validates :postal_code, presence: true, format: {with: /\A[0-9]{3}-[0-9]{4}\z/, message: "is invalid. Include hyphen(-)"}
      # 0以外の整数を許可する
      # validates :prefecture, numericality: {other_than: 0, message: "can't be blank"}
    end
    
  3. donationモデルとaddressモデルにあったバリデーションの記述を、Formオブジェクトへ記述する
    donation_address.rb
    class DonationAddress
      # ActiveModel::Modelをinclude
      include ActiveModel::Model
      # donationsテーブルとaddressesテーブルに保存したいカラム名
      attr_accessor :postal_code, :prefecture, :city, :house_number, :building_name, :price, :user_id
    
      # donationsテーブルとaddressesテーブルのバリデーション
      with_options presence: true do
        validates :price, numericality: {only_integer: true, greater_than_or_equal_to: 1, less_than_or_equal_to: 1000000, message: 'is invalid'}
        validates :user_id # 「belongs_to :user」のアソシエーションによって設定されているバリデーションを新たに追加
        validates :postal_code, format: {with: /\A[0-9]{3}-[0-9]{4}\z/, message: "is invalid. Include hyphen(-)"}
      end
      validates :prefecture, numericality: {other_than: 0, message: "can't be blank"}
    end
    

手順5.データをテーブルに保存する処理を書く

  1. フォームから送られてきた情報をテーブルに保存する処理を記述する
    donation_address.rb
    # 中略
    
    # フォームから送られてきた情報をテーブルに保存する処理
    def save
      # 寄付情報を保存し、変数donationに代入する
      donation = Donation.create(price: price, user_id: user_id)
      # 住所を保存する
      # donation_idには、変数donationのidと指定する
      Address.create(postal_code: postal_code, prefecture: prefecture, city: city, house_number: house_number, building_name: building_name, donation_id: donation.id)
    end
    
    # 中略
    

手順6.コントローラーでFormオブジェクトのインスタンスを生成する

  1. Formオブジェクトのインスタンスを生成する
    donations_controller.rb
    class DonationsController < ApplicationController
      before_action :authenticate_user!, except: :index
      def index
      end
    
      def new
        # newアクションで生成したインスタンスは、form_withのmodelオプションに指定できる
        @donation_address = DonationAddress.new
      end
    
      def create
        # エラーハンドリングしていた場合、ストロングパラメーターによって値を取得したインスタンスが、renderで表示されたビューのmodelオプションに指定
        @donation_address = DonationAddress.new(donation_address_params)
    
        if @donation_address.valid?
          @donation_address.save
          redirect_to root_path
        else
          render :new, status: :unprocessable_entity
        end
      end
    
      private
      # ストロングパラメーターを設定
      def donation_address_params
        # フォームがFormオブジェクトのインスタンスに紐付くことにより、送信されるパラメーターは、donation_addressハッシュを含む二重構造になる
        params.require(:donation_address).permit(:postal_code, :prefecture, :city, :house_number, :building_name, :price).merge(user_id: current_user.id)
      end
    
      # ストロングパラメーターを削除
      # def donation_params
      #   params.permit(:price).merge(user_id: current_user.id)
      # end
    
      # def address_params
      #   params.permit(:postal_code, :prefecture, :city, :house_number, :building_name).merge(donation_id: @donation.id)
      # end
    end
    

手順7.フォームのmodelオプションを設定する

  1. 生成したインスタンスをapp/views/donations/new.html.erbで利用する
    <%= form_with model: @donation_address, url: donations_path, local: true do |f| %>
    <%# 削除 %> <%#= form_with url: donations_path, local: true do |f| %>
    
    <%# 省略 %>
    

手順8.エラーメッセージを表示する

  1. メッセージを表示させるための部分テンプレート_error_messages.html.erbを、app/views/donationsディレクトリに手動作成する
  2. 部分テンプレートに下記を記述する
    <% if model.errors.any? %>
      <div class="error-alert">
        <ul>
          <% model.errors.full_messages.each do |message| %>
            <li class="error-message">
              <%= message %>
            </li>
          <% end %>
        </ul>
      </div>
    <% end %>
    
  3. 部分テンプレートを読み込めるように、app/views/donations/new.html.erbに下記を記述する
    <%= form_with model: @donation_address, url: donations_path, local: true do |f| %>
    <%# 削除 %> <%#= form_with url: donations_path, local: true do |f| %>
      <%= render 'error_messages', model: @donation_address %> <%# エラーメッセージの表示 %>
    
    <%# 省略 %>
    

Ruby on Railsまとめ

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?