箱のプログラミング日記。

渋谷の自社開発企業でRails書いてます。

create前に確認画面を作りたい【Rails】

f:id:y_hakoiri:20191102134313j:plain

Railsで商品予約システムを開発しています。

巷のアプリとかでもよくある「この内容でよろしいですか?」みたいな確認画面を

newとcreateの間に挟みたいな・・・と思いつきました。

 

主なモデルは以下

ユーザー:user

商品:product

注文:order

 

先に、参考にさせていただいた記事

qiita.com

 

自分的にはconfirmアクションを作って間に噛ませるのが

わかりやすいかなと思い試しましたが、いまいちうまく行かなかったので、

上の記事で紹介されているように、newとcreateのみ使用しつつ

confirmを値として持たせる方法で実装しました。

 

もう、本当に上の記事に書いてある通りなのですが、まとめると

 

通常

new→

バリデーション→

バリデーション引っかかったらnewに戻る・クリアしたらcreate

 

今回

new→

バリデーション(ここで一工夫)→

newに戻ってくる(アクションはnewだけど確認用のviewを表示)→

「確定する」的なボタンが押されたらcreate

 

と言った感じで、バリデーションの機能をうまく使って

newとcreateのみでページ推移を実現させている感じです。

 

 

早速やってみる

model/order.rb

validates_acceptance_of :confirming
after_validation :check_confirming
 
def check_confirming
 errors.delete(:confirming)
 self.confirming = errors.empty? ? '1' : ''
end
 
ここで何をしているかと言うと、
今回「DBには保存しないけど、これがないとcreateさせないよ〜」と言う値(=confirming)を使用します
 
通常、DBのカラムに対してバリデーションをかける場合は
validates :date
みたいな感じで書きますが、
 
今回のようにDBに存在しない値に対してバリデーションをかける場合、
validates_acceptance_of
を使用するとのこと。
 
これにより、例えば以下のような
保存するわけじゃないけど同意のチェックがないと先に進めないよ〜
的なページも実現することができる。
 
(ちょっと古くてごめんなさい)
 
 
話を戻して、次の一行
after_validation :check_confirming
これはそのまま。
「バリデーションが終わったら次のメソッド参照してね」
 
check_confirmingメソッド内の記述は以下
 

 

def check_confirming

 errors.delete(:confirming)

 self.confirming = errors.empty? ? '1' : ''

end

 

ちょっとここの記述がいまいち分からなかったけど、

他にバリデーションに引っかかった値がなければ(正常に登録されていれば)

confirmingの値を「1」に設定してね、と言う意味

 

次にコントローラー

controllers/orders_controller.rb

 

def create
 @order = Order.new(order_params)
 if @order.save
  redirect_to root_path, notice: 'ご注文を送信しました'
 else
  render :new
 end
end
 
def order_params
 params.require(:order).permit(:date,
              :time,
              :receiving_method,
              :receiving_store,
              :delivery_address,
              :payment,
              :voucher,
              :message,
              :confirming)
              .merge(user_id: current_user.id)
end

 

最初にcreateアクションが呼ばれた時点では

confirmingが存在しない=saveできないのでnewにrenderします。

 

しかしrenderでnewに戻ってきた時点ですでに

バリデーションを通過している=confirmingの値が1にセットされている

ので、2回目以降createが呼ばれた時はsaveされるようになっています

 

あとはビューで

最初にnewアクションが呼ばれた場合と、

confirmingが設置された状態でrenderで戻ってきた場合で

分岐すればOK

 

views/orders/new.html.haml

- if @order.confirming.blank?

 //confirmingの値がない=最初にnewが呼ばれた時なので

 普通にフォームをここに記述

- else
 %p.confirm 以下の内容でよろしいですか?
 %p.table-title ご注文商品
 %table
 //confirmingの値がある=renderでnewに戻ってきた時なので

 確認用のテーブルをここに記述

 

 

confirmingの値がない場合 (最初のnewアクション)

https://i.gyazo.com/e2a45d306a9009a78867023152b6796b.png

 

 

最後まで入力して確認画面へ

(一旦createが呼ばれるが、confirmingが無いのでsaveされずnewに戻る)

https://i.gyazo.com/c86634e33ef976f064f9436c468ce673.png

 

 

 newに戻ってくる

(バリデーションを経由しconfirmingが既にセットされている)

https://i.gyazo.com/6a576d0f41c1c90bebf0ecbbc8fa1d68.png

 

最終的にここでcreate

(今度はconfirmingの値を持っているので、適切にsaveされてDBに保存される)

 

https://i.gyazo.com/c5d24e76fdde6624fc274594bf517925.png

 

と言う感じ。

 

新規作成のフォームも確認画面もどちらもnewアクションを使用していますが

confirmingを使用することで、表示するビューを分岐した上で

適切にページ推移が実行できるようになっています。