0
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】診断機能を実装する(1ページ1問+タイプ別結果+履歴表示付き)

Posted at

1. 概要

追加・編集するファイル

  • 追記:config/routes.rb
  • 新規:app/models/diagnosis.rb
  • 新規:db/migrate/*_create_diagnoses.rb
  • 新規:app/controllers/diagnoses_controller.rb
  • 新規:app/views/diagnoses/index.html.erb
  • 新規:app/views/diagnoses/new.html.erb
  • 新規:app/views/diagnoses/show.html.erb

2. controller/model 作成

コマンドプロンプト
rails g controller Diagnoses index new show
コマンドプロンプト
rails g model Diagnosis result_type:string result_detail:text answers:json session_token:string
コマンドプロンプト
rails db:migrate

3. ルーティング編集

config/routes.rb
resources :diagnoses, only: [:index, :new, :create, :show, :destroy]

4. モデル編集

app/models/deagnosis.rb
class Diagnosis < ApplicationRecord
  validates :result_type, :result_detail, presence: true
end

5. コントローラー

app/controllers/diagnoses_controller.rb
class DiagnosesController < ApplicationController
  # =====▼ここを編集すれば問題数・選択肢数を自由化できます(3×3が雛形)▼=====
  QUESTIONS = [
    {
      id: 1,
      title: '学習を始めるとき、まずは?',
      choices: [
        { id: '1a', label: '手を動かして試す', value: 1 },
        { id: '1b', label: 'ドキュメントを読む', value: 2 },
        { id: '1c', label: '動画で概要を掴む', value: 3 }
      ]
    },
    {
      id: 2,
      title: '復習のやり方は?',
      choices: [
        { id: '2a', label: 'ノートに整理', value: 1 },
        { id: '2b', label: 'ミニアプリ作り直し', value: 2 },
        { id: '2c', label: '要点だけ見返す', value: 3 }
      ]
    },
    {
      id: 3,
      title: '困ったときは?',
      choices: [
        { id: '3a', label: '論理的に切り分け', value: 1 },
        { id: '3b', label: 'まずは検索', value: 2 },
        { id: '3c', label: '実験して挙動を確認', value: 3 }
      ]
    }
  ].freeze

  # 解答は下から追加/編集してね!
  TYPE_DEFS = {
    A: {
      title:  'タイプA:設計重視派',
      detail: '事前準備や情報整理を大切にし、安定した学習ペースで力を積み上げるタイプ。'
    },
    B: {
      title:  'タイプB:効率・ショートカット志向',
      detail: '要点を素早く掴み、最短経路を探索するタイプ。試行回数を増やすと伸びやすい。'
    },
    C: {
      title:  'タイプC:実践ドリブン型',
      detail: 'まず手を動かして学び、体験から理解を深めるタイプ。小さく作って改善が得意。'
    }
  }.freeze

  # 回答コード(例 "1-2-2")→ タイプを決めるルール
  # 複数パターンを同じ結果にしたい場合は `patterns: ['1-2-2','1-1-2']` のように配列で指定
   # * はすべての値
  PATTERN_RULES = [
    { patterns: ['1-2-2', '1-1-2'], result_type: :A },
    { patterns: ['3-1-2'],          result_type: :B },
    { patterns: ['1-*-3'],          result_type: :C },
    { patterns: ['*-3-*'],          result_type: :B }
    # ここに追記していけばOK(上に書いたものほど優先)
  ].freeze
  # =====▲編集ポイントここまで▲=====

  DEFAULT_RESULT_TYPE = :C

  before_action :init_session!

  def index
    @diagnoses = Diagnosis.order(created_at: :desc).limit(50)
  end

  def new
    @step  = (params[:step] || 1).to_i
    @total = QUESTIONS.size
    return redirect_to new_diagnosis_path(step: 1) if @step <= 0 || @step > @total
    @question = QUESTIONS[@step - 1]
  end

  def create
    step = (params[:step] || 1).to_i
    question = QUESTIONS[step - 1] or return head :not_found

    chosen_id = params[:choice_id].to_s.presence
    unless chosen_id
      redirect_to new_diagnosis_path(step: step), alert: '1つ選んでください' and return
    end

    choice = question[:choices].find { |c| c[:id].to_s == chosen_id }
    session[:answers][question[:id].to_s] = { q: question[:title], a: choice[:label], value: choice[:value] }

    next_step = step + 1
    if next_step <= QUESTIONS.size
      redirect_to new_diagnosis_path(step: next_step) and return
    end

    code      = QUESTIONS.map { |q| session[:answers][q[:id].to_s][:value] }.join('-')
    type_sym  = find_type_by_rules(code) || DEFAULT_RESULT_TYPE
    type_def  = TYPE_DEFS[type_sym]

    qa_array = QUESTIONS.map { |q| session[:answers][q[:id].to_s] }
    diag = Diagnosis.create!(
      result_type:   type_def[:title],
      result_detail: type_def[:detail],
      answers:       qa_array,
      session_token: session.id.to_s
    )

    session[:answers] = {}

    redirect_to diagnosis_path(diag)
  end

  def show
    @diagnosis = Diagnosis.find(params[:id])
  end

  def destroy
    Diagnosis.find(params[:id]).destroy!
    redirect_to diagnoses_path, notice: '削除しました'
  end

  private

  def init_session!
    session[:answers] ||= {}
  end

  def wildcard_match?(pattern, code)
    p = pattern.split('-'); c = code.split('-')
    return false unless p.size == c.size
    p.zip(c).all? { |x, y| x == '*' || x == y }
  end

  def find_type_by_rules(code)
    PATTERN_RULES.each do |rule|
      Array(rule[:patterns]).each do |pat|
        return rule[:result_type] if wildcard_match?(pat, code)
      end
    end
    nil
  end
end

6. ビュー

1. 結果一覧ページ

app/views/diagnoses/index.html.erb
<h1>診断履歴</h1>

<p><a href="<%= new_diagnosis_path(step: 1) %>">診断を始める</a></p>

<% if @diagnoses.any? %>
  <table>
    <thead>
      <tr>
        <th>日時</th>
        <th>結果</th>
        <th>リンク</th>
        <th>削除</th>
      </tr>
    </thead>
    <tbody>
      <% @diagnoses.each do |d| %>
        <tr>
          <td><%= d.created_at.strftime("%Y-%m-%d %H:%M") %></td>
          <td><%= d.result_type %></td>
          <td><%= link_to '表示', diagnosis_path(d) %></td>
          <td><%= button_to '削除', diagnosis_path(d), method: :delete, data: { confirm: '削除しますか?' } %></td>
        </tr>
      <% end %>
    </tbody>
  </table>
<% else %>
  <p>まだ診断履歴はありません。</p>
<% end %>

2. 診断ページ

app/views/diagnoses/new.html.erb
<h1>診断</h1>

<% if flash[:alert] %><p><%= flash[:alert] %></p><% end %>

<h2>Q<%= @step %>/<%= @total %></h2>
<p><%= @question[:title] %></p>

<%= form_with url: diagnoses_path, method: :post do %>
  <input type="hidden" name="step" value="<%= @step %>">
  <ul>
    <% @question[:choices].each do |c| %>
      <li>
        <label>
          <%= radio_button_tag "choice_id", c[:id], false %>
          <%= c[:label] %>
        </label>
      </li>
    <% end %>
  </ul>

  <p>
    <%= submit_tag(@step == @total ? '結果を見る' : '次へ') %>
    <a href="<%= diagnoses_path %>">履歴へ戻る</a>
  </p>
<% end %>

3. 結果ページ

app/views/diagnoses/show.html.erb
<h1>診断結果</h1>

<p><strong><%= @diagnosis.result_type %></strong></p>
<p><%= @diagnosis.result_detail %></p>

<h2>あなたの回答</h2>
<% Array(@diagnosis.answers).each_with_index do |qa, i| %>
  <p>
    <strong>Q<%= i+1 %>:</strong> <%= qa['q'] || qa[:q] %><br>
    <strong>A:</strong> <%= qa['a'] || qa[:a] %>
  </p>
<% end %>

<p>
  <a href="<%= new_diagnosis_path(step: 1) %>">もう一度診断する</a> |
  <a href="<%= diagnoses_path %>">履歴を見る</a>
</p>
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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?