はじめに
本記事ではRailsで簡単に診断機能を作る方法を紹介します。
Viewページを質問の数だけ作成し、if文を使用して前の質問の回答結果に応じて質問を分岐させる簡単な設計です。結果はdb/seeds.rb
を使って診断結果を登録し、データベースから取得するようになっています。
注意
この実装方法では診断結果をDBに保存することはできません。
保存したい場合の実装方法も記事後半に載せているので、保存したい場合はそちらも合わせてお読みください
診断の流れ
- 質問ページ (question1, question2, question3)
- URLパラメータを使って回答を次のページへ送る
- 最後に診断結果をデータベースから取得して表示
実装してみよう
Diagnosis
モデルの作成
既存のモデルに備え付けるでもいいですし、新しいモデルを作るでもどちらでも構いません。今回は新たにDiagnosis
モデルを作成し、実装を進めていくこととします。
rails generate model Diagnosis result_key:string result_detail:text
rails db:migrate
カラムの役割について説明します。まず、result_key
カラムは最終質問の結果を格納するカラムです。最後の質問の回答結果に「あ」〜「く」の記号(result_key)を割り振っています。もし、結果が「あ」だったらresult_keyに対応する診断結果をDBから取り出します。
続いてresult_detail
カラムは診断結果のテキストを格納するカラムです。例えば「診断の結果、あなたには〇〇がおすすめです!」のようなものです。
diagnoses_controller.rb
の作成
今度はコントローラーを作成します。作成時に一緒にquestion1.html.erb
、question2.html.erb
、question3.html.erb
、result.html.erb
も作成しています。
ややこしいですが、diagnosisの複数形はdiagnoses
なのでdiagnoses
で実装しています
rails generate controller Diagnoses question1 question2 question3 result
ルーティングの設定 config/routes.rb
設計上質問に行く前に一度1ページ挟みたいとかあれば適宜追記をお願いします!
Rails.application.routes.draw do
get "diagnoses/question1" #追加
get "diagnoses/question2" #追加
get "diagnoses/question3" #追加
get "diagnoses/result" #追加
end
診断データの登録db/seeds.rb
Diagnosis.create([
{ result_key: "あ", result_detail: "あなたにオススメなのは ① です!" },
{ result_key: "い", result_detail: "あなたにオススメなのは ② です!" },
{ result_key: "う", result_detail: "あなたにオススメなのは ③ です!" },
{ result_key: "え", result_detail: "あなたにオススメなのは ④ です!" },
{ result_key: "お", result_detail: "あなたにオススメなのは ⑤ です!" },
{ result_key: "か", result_detail: "あなたにオススメなのは ⑥ です!" },
{ result_key: "き", result_detail: "あなたにオススメなのは ⑦ です!" },
{ result_key: "く", result_detail: "あなたにオススメなのは ⑧ です!" }
])
seedファイルに書き込めたら以下のコマンドを実行します。
rails db:seed
質問ページquestion.html.erb
の作成
以下はあくまで参考までに!自分のアプリの質問項目に変更して使ってね!
<h1>診断スタート</h1>
<h2>選ぶなら?</h2>
<a href="/diagnoses/question2?answer=A">Aを選ぶ</a>
<a href="/diagnoses/question2?answer=B">Bを選ぶ</a>
<% if params[:answer] == "A" %>
<h1>Aを選んだあなた</h1>
<h2>質問その2</h2>
<a href="/diagnoses/question3?answer=C&prev_answer=<%= params[:answer] %>">Cを選ぶ</a>
<a href="/diagnoses/question3?answer=D&prev_answer=<%= params[:answer] %>">Dを選ぶ</a>
<% elsif params[:answer] == "B" %>
<h1>Bを選んだあなた</h1>
<h2>質問その2</h2>
<a href="/diagnoses/question3?answer=E&prev_answer=<%= params[:answer] %>">Eを選ぶ</a>
<a href="/diagnoses/question3?answer=F&prev_answer=<%= params[:answer] %>">Fを選ぶ</a>
<% end %>
<% if params[:answer] == "C" %>
<h1>Cを選んだあなた</h1>
<h2>最終質問</h2>
<a href="/diagnoses/result?answer=あ">オススメ1結果を見る</a>
<a href="/diagnoses/result?answer=い">オススメ2結果を見る</a>
<% elsif params[:answer] == "D" %>
<h1>Dを選んだあなた</h1>
<h2>最終質問</h2>
<a href="/diagnoses/result?answer=う">オススメ3結果を見る</a>
<a href="/diagnoses/result?answer=え">オススメ4結果を見る</a>
<% elsif params[:answer] == "E" %>
<h1>Eを選んだあなた</h1>
<h2>最終質問</h2>
<a href="/diagnoses/result?answer=お">オススメ5結果を見る</a>
<a href="/diagnoses/result?answer=か">オススメ6結果を見る</a>
<% elsif params[:answer] == "F" %>
<h1>Fを選んだあなた</h1>
<h2>最終質問</h2>
<a href="/diagnoses/result?answer=き">オススメ7結果を見る</a>
<a href="/diagnoses/result?answer=く">オススメ8結果を見る</a>
<% end %>
診断結果を取得するresultアクションapp/controllers/diagnoses_controller.rb
の作成
診断結果を取得するためのロジックをここに記述します。
def result
@diagnosis = Diagnosis.find_by(result_key: params[:answer])
unless @diagnosis
flash[:alert] = "診断結果が見つかりませんでした"
redirect_to root_path
end
end
診断結果ページresult.html.erb
の作成
ここに診断結果を表示します。
<div id="diagnosis">
<% if @diagnosis.present? %>
<h2>診断結果</h2>
<p><%= @diagnosis.result_detail %></p>
<% else %>
<p>診断結果が見つかりません。</p>
<% end %>
</div>
以上です!お疲れ様でした!
追記(診断結果をDBに保存したい人向け)
上記のやり方では、View上でparams[:answer]
を渡しているため、診断結果はデータベースに保存されません。
単純に一時的に表示させたい場合は問題ありませんが、もし診断結果をDBに保存する必要がある場合は、result
のパラメータをController
に渡し、適切にデータを保存する必要があります。以下にそのやり方を示します。
変更点:①routes.rb
の修正
診断結果をPOST
で送信し、Controllerで処理するようにするため、POST ルートを追加します。
Rails.application.routes.draw do
get "diagnoses/question1"
get "diagnoses/question2"
get "diagnoses/question3"
# 追加(診断結果を GET でも表示できるようにする)
get "diagnoses/result", to: "diagnoses#result"
# 追加(診断結果を POST で送信し、DB に保存する)
post "diagnoses/result", to: "diagnoses#result"
end
変更点②:diagnoses_controller.rb
の修正
診断結果をPOST
で送信し、診断履歴をDBに保存するように修正。
question3
アクションとresult
アクションを大きく変更しています
def question3
@question = "最終質問"
@options = []
session[:previous_answers] ||= []
session[:previous_answers] << params[:answer] if params[:answer].present?
case params[:answer]
when "C"
@message = "Cを選んだあなた"
@options = [
{ text: "オススメ1結果を見る", value: "あ" },
{ text: "オススメ2結果を見る", value: "い" }
]
when "D"
@message = "Dを選んだあなた"
@options = [
{ text: "オススメ3結果を見る", value: "う" },
{ text: "オススメ4結果を見る", value: "え" }
]
when "E"
@message = "Eを選んだあなた"
@options = [
{ text: "オススメ5結果を見る", value: "お" },
{ text: "オススメ6結果を見る", value: "か" }
]
when "F"
@message = "Fを選んだあなた"
@options = [
{ text: "オススメ7結果を見る", value: "き" },
{ text: "オススメ8結果を見る", value: "く" }
]
else
flash[:alert] = "無効な選択です"
redirect_to diagnoses_question1_path
end
end
def result
if request.post?
@diagnosis = Diagnosis.find_by(result_key: params[:answer])
if @diagnosis.nil?
flash[:alert] = "診断結果が見つかりませんでした"
redirect_to root_path and return
end
# 診断履歴をDBに保存(過去の回答履歴も記録)
if user_signed_in?
DiagnosisHistory.create(
user: current_user,
diagnosis: @diagnosis,
previous_answers: (session[:previous_answers] || []).join(", ")
)
end
# 診断後はセッションをリセット
session[:previous_answers] = []
redirect_to diagnoses_result_path(answer: params[:answer])
else
@diagnosis = Diagnosis.find_by(result_key: params[:answer])
if @diagnosis.nil?
flash[:alert] = "診断結果が見つかりませんでした"
redirect_to root_path and return
end
render :result
end
end
-
POST
リクエストを受け付けて診断結果を処理できるようにした -
DiagnosisHistory.create
を追加し、診断結果をデータベースに保存できるようにした -
redirect_to diagnoses_result_path(answer: params[:answer])
を追加し、診断後にGET
で結果ページを表示するようにした
変更点③:question3.html.erb
の修正
診断結果の選択をGET
ではなくPOST
で送信するように修正。
<h1><%= @message %></h1>
<h2><%= @question %></h2>
<% @options.each do |option| %>
<%= form_with url: diagnoses_result_path, method: :post, local: true do %>
<%= hidden_field_tag :answer, option[:value] %>
<%= submit_tag option[:text] %>
<% end %>
<% end %>
変更点④:DiagnosisHistory
モデルの追加
DiagnosisHistory をデータベースで管理するために、モデルを作成し、マイグレーションを適用する必要がありました...
以下のコマンドを実行して、DiagnosisHistory モデルを作成します。
rails generate model DiagnosisHistory user:references diagnosis:references previous_answers:text
rails db:migrate
Userモデルなどある場合はリレーションも忘れず行ってください!
まとめ
ルーティングではpost "diagnoses/result" を追加、ControllerはPOSTで診断結果を受け取り、DB に保存。質問ページ (question3.html.erb) はPOSTでフォーム送信し、診断結果ページ (result.html.erb)で@diagnosisを使って表示するという流れで実装しました。