質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

Q&A

0回答

445閲覧

フォームオブジェクトで、複数のモデルが絡むバリデーションを行いたい

zendendo

総合スコア43

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

0グッド

0クリップ

投稿2017/12/12 00:56

編集2017/12/13 22:54

###前提・実現したいこと
ruby(ruby on rails)で、単一ポイントの取引機能(振込機能)があり、
支払ユーザーが保有する口座の残高を超える取引額を不可にしたい(支払ユーザーの口座残高未満の取引額ならモデルに保存・更新したい)と考えています。

ただし、残高を記録しているモデルと取引額を記録しているモデルは別々になっています。

いわゆる複数のモデルが絡むバリデーションになると思うのですが、そうゆう場合は、フォームオブジェクトで設定した方がいいと聞き、
フォームオブジェクトでバリデーションを作成してみたのですが、
よくわからず、うまくいかず困っています。

現状では、入力フォームで「支払先の口座番号」と「取引額」を入力すると、
3つのモデルが同時に動くようになっています。
図にするとこんな感じです。
イメージ説明
データのやりとりはこんな感じです。
イメージ説明
例えば、支払ユーザーの口座残高が10000(1万)なら、1万1以上の数値の入力を
AmmountTransaction(取引)モデルのamount(取引額)、
DepositsAndwithdrawal(入出金)モデルのamount(取引額)、
BasicIncomeAccount(口座)モデルのbalance(残高)を更新する際に使う増減額、
で禁止(バリデーション)できるようにしたいのですが、
その場合、formオブジェクトやコントローラ等のファイルには、どんな記述をすれば
いいのでしょうか?

教えて頂ければ幸いです。

###試しに書いたソースコード

フォームオブジェクトのコードになります。
app/models/form/account_transaction_form.rb

ruby

1class AccountTransactionForm 2 3 include ActiveModel::Model 4 5 attr_accessor :erroes, :amount, :deposit_account_id 6 7 def initialize(amount, withdrawal_account_id, deposit_account_id, basic_income_account) 8 @amount = amount 9 @withdrawal_account_id = withdrawal_account_id 10 @deposit_account_id = deposit_account_id 11 @balance = basic_income_acccount.balance 12 @errors = ActiveModel::Errors.new(self) 13 end 14 15 #検証に関する記述 16 def valid? 17 errors.clear 18 if @balance < @amount 19 errors.add(:amount, "残高以上の送金はできません") 20 end 21 errors.empty? 22 end 23end 24

コントローラのコードです。3つのモデルを動かす取引処理の記述が書かれています。
取引処理は複数のモデルが絡む固有の処理(ビジネスロジックというもの?)なので、
この固有の処理もフォームオブジェクトやサービスクラスに
フォームオブジェクトに移動させた方がいいのですが、質問の本題から離れるのでひとまずおいておきます。
コントローラには、フォームオブジェクトを呼び出すコードを記述をすると思うのですが、
仮に複数のモデルが絡むバリデーションだけを行うフォームオブジェクトの場合、
どこにどんな記述をすればいいのでしょうか

ruby

1class AccountTransactionsController < ApplicationController 2 3 #取引を作成する 4 def new 5 @account_transaction = AccountTransaction.new 6 end 7 8 #取引処理をする為に3つのモデルを同時に保存・更新している 9 def create 10 #取引モデルに1件のレコードを新規作成 11 AccountTransaction.transaction do 12 @account_transaction = AccountTransaction.new( 13 withdrawal_account_id: current_user.basic_income_account.id, 14 deposit_account_id: BasicIncomeAccount.find_by(account_number: params[:account_transaction][:deposit_account_id]).id, 15 amount: params[:account_transaction][:amount] 16 ) 17 @account_transaction.save! 18 #入出金モデルに2件のレコードを新規作成 19 DepositsAndWithdrawal.transaction do 20 #出金側レコードを作成保存 21 @account_transaction.deposits_and_withdrawals.build( 22 transaction_type: "出金", 23 basic_income_account_id: current_user.basic_income_account.id, 24 amount: -1 * params[:account_transaction][:amount].to_f 25 ) 26 @account_transaction.save! 27 #出金側レコードを作成保存 28 @account_transaction.deposits_and_withdrawals.build( 29 transaction_type: "入金", 30 basic_income_account_id: BasicIncomeAccount.find_by(account_number: params[:account_transaction][:deposit_account_id]).id, 31 amount: params[:account_transaction][:amount].to_f 32 ) 33 @account_transaction.save! 34 #口座モデルを更新(残高の増減) 35 BasicIncomeAccount.transaction do 36 @withdrawal_basic_income_account = current_user.basic_income_account 37 @deposit_basic_income_account = BasicIncomeAccount.find_by(account_number: params[:account_transaction][:deposit_account_id]) 38 39 @withdrawal_amount = params[:account_transaction][:amount].to_f 40 @deposit_amount = params[:account_transaction][:amount].to_f 41 42 @withdrawal_basic_income_account.update(balance: @withdrawal_basic_income_account.balance - @withdrawal_amount) 43 @deposit_basic_income_account.update(balance: @deposit_basic_income_account.balance + @deposit_amount) 44 end 45 end 46 end 47 redirect_to root_path 48 rescue => e 49 render plain: e.message 50 51 end 52 53 54 private 55 def account_transaction_params 56 params.require(:account_transaction).permit(:deposit_account_id, :amount) 57 end 58end

入力フォーム画面のコードです。

<%= form_for @account_transaction do |f|%> <% if @account_transaction.errors.any? %> <h3>入力内容にエラーが<%= @account_transaction.errors.count %>件あります</h3> <ul> <% @account_transaction.errors.full_messages.each do |message| %> <li><%= message %></li> <% end %> </ul> <% end %> <div class="field"> <%= f.label :送金相手の口座番号 %> <%= f.text_field :deposit_account_id %> <%= f.label :送金額 %> <%= f.number_field :amount,min:1,max:999999999999999 %> <%= f.submit"送金確認" %> </div> <% end %>

3つのモデルの状態です。

ruby

1 create_table "account_transactions", force: :cascade do |t| 2 t.decimal "amount" 3 t.datetime "created_at", null: false 4 t.datetime "updated_at", null: false 5 t.integer "withdrawal_account_id" 6 t.integer "deposit_account_id" 7 t.index ["deposit_account_id"], name: "index_account_transactions_on_deposit_account_id" 8 t.index ["withdrawal_account_id"], name: "index_account_transactions_on_withdrawal_account_id" 9 end 10 11 create_table "basic_income_accounts", force: :cascade do |t| 12 t.integer "user_id" 13 t.string "account_number" 14 t.decimal "balance" 15 t.datetime "created_at", null: false 16 t.datetime "updated_at", null: false 17 t.index ["user_id"], name: "index_basic_income_accounts_on_user_id" 18 end 19 20 create_table "deposits_and_withdrawals", force: :cascade do |t| 21 t.integer "account_transaction_id" 22 t.string "transaction_type" 23 t.decimal "amount" 24 t.datetime "created_at", null: false 25 t.datetime "updated_at", null: false 26 t.integer "basic_income_account_id" 27 t.index ["account_transaction_id"], name: "index_deposits_and_withdrawals_on_account_transaction_id" 28 t.index ["basic_income_account_id"], name: "index_deposits_and_withdrawals_on_basic_income_account_id" 29 end

###補足情報(言語/FW/ツール等のバージョンなど)
Rails 5.1.3
ruby 2.4.1
devise (4.3.0)

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだ回答がついていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問