Rails 診断機能を実装する　タグ機能　マッチ度判断

# 実行環境

```
ruby 3.0.2
Rails 7.0.2.3
```

GitHubはこちら

https://github.com/kakeruAoyama/diagnose_games

※Railsのバージョンによって作成されるコードが違う場合があります。適宜ご自身の環境にあわせて読み換えてください。

### DB設計
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324819/4284be8d-06aa-e9d6-8385-70fb2a9f9f1e.png)

GameとTagが多対多のアソシエーションで結びついている

### 診断機能の概要
gameに紐づいたタグと、ユーザーが質問に答えた回答がマッチしている数が一番多いものが表示される。
すなわち、gameに[tag1, tag2, tag3, tag4]が紐付いている場合、ユーザーの回答が、[tag1, tag2, tag3, tag5]であったならば、マッチ度は75％として表示される。
ただし、[tag1, tag2, tag3, tag5]というgameが他に存在した場合は、こちらがマッチ度100％として表示される。

# アプリケーション作成
必要なモデルを作成しマイグレーション
```bash
$ rails new diagnose_games
$ cd diagnose_games
$ rails g scaffold Game name:string discription:text
$ rails g model Tag name:string
$ rails g model TagGamesRerationship game:references tag:references
$ rails db:migrate
```

root_pathを設定

```ruby:route.rb
root "games#index"
```

# タグ機能を作成

アソシエーションの追加

```ruby:tag.rb
class Tag < ApplicationRecord
  has_many :tag_games_rerationships, dependent: :destroy
  has_many :games, through: :tag_games_rerationships, dependent: :destroy
end
```

```ruby:game.rb
class Game < ApplicationRecord
  has_many :tag_games_rerationships, dependent: :destroy
  has_many :tags, through: :tag_games_rerationships, dependent: :destroy
end
```

seedを作成
後述の質問に合わせて作成してください。

```ruby:seed.rb
Tag.create!([
  # 何人で遊びますか？
  { name: '2人' },
  { name: '3人' },
  { name: '4人' },
  { name: '5人以上' },
  
  # 誰と遊びますか？
  { name: '家族' },
  { name: '友達' },
  { name: '恋人' },
  
  # シチュエーションは？
  { name: 'まったり' },
  { name: '飲み会' },
  { name: '白熱' },
  { name: 'おもしろ' },
  
  # 遊ぶ人の世代は？
  { name: '小/中学生' },
  { name: '高校/大学生' },
  { name: '社会人' },
  
  # プレイ時間は？
  { name: '30分以下' },
  { name: '30〜1時間' },
  { name: '1時間以上' },
  
  # ゲームの種類は?
  { name: 'ブラフ'},
  { name: '正体隠匿'},
  { name: 'タイル配置'},
  { name: '拡大再生産'},
  { name: 'ドラフト'},
  { name: 'ワーカープレイスメント'},
  { name: '陣取り'},
  { name: 'デッキ構築'},
  { name: '競り'},
  { name: '協力'},
  { name: '表現'},
  { name: 'アクション'},
])
```
```
$ rails db:seed
```

タグの入力を追加

```erb:_form.html.erb
  <%= form_with(model: game) do |form| %>
  <% if game.errors.any? %>
    <div style="color: red">
      <h2><%= pluralize(game.errors.count, "error") %> prohibited this game from being saved:</h2>

      <ul>
        <% game.errors.each do |error| %>
          <li><%= error.full_message %></li>
        <% end %>
      </ul>
    </div>
  <% end %>

  <div>
    <%= form.label :name, style: "display: block" %>
    <%= form.text_field :name %>
  </div>

  <div>
    <%= form.label :discription, style: "display: block" %>
    <%= form.text_area :discription %>
  </div>
↓追記
  <div class='form-group'>
    <%= form.collection_check_boxes(:tag_ids, Tag.all, :id, :name) do |tag| %>
      <div class='form-check'>
        <%= tag.label class: 'form-check-label' do %>
          <%= tag.check_box class: 'form-check-input' %>
          <%= tag.text %>
        <% end %>
      </div>
    <% end %>
  </div>
↑ここまで

  <div>
    <%= form.submit %>
  </div>
<% end %>
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324819/b826a1eb-608c-06d8-2882-e934dd73a68b.png)


ストロングパラメーターに追加
```ruby:games_controller.rb
    def game_params
      params.require(:game).permit(:name, :discription, tag_ids: [])
    end
```

タグを表示させる
```erb:_game.html.erb
  <% game.tags.each do |tag| %>
    <span><%= tag.name %></span>
  <% end %>
```

以上で、タグ機能が完成
`rails s`で新規投稿してみてください！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324819/55a62730-74b7-321f-949c-d0acebddf35d.png)



# 診断機能の実装
コントローラーを作成

```
$ rails g controller diagnoses new index
```

選択画面を作成
`questions`はseedの質問数と一致させる
```erb:app/views/diagnoses/new.html.erb
<% tags = Tag.select(&:name) %>
<% questions = [
  # numberはseedで追加したTagの個数と合わせる
  {title: "何人で遊びますか？", number: 4},
  {title: "誰と遊びますか？", number: 3},
  {title: "シチュエーションは？", number: 4},
  {title: "遊ぶ人の世代は？", number: 3},
  {title: "プレイ時間は？", number: 3},
  {title: "ゲームの種類は?", number: 12},
] %>
<% question_id = Tag.first.id %>

<%= form_tag diagnoses_index_path, method: :get do %>
  <% questions.each do |q| %>
    <div class="question-container">
      <%= q[:title] %>
      <% tags.shift(q[:number]).each do |t| %>
        <input type="checkbox" id="checkbox<%= question_id %>" name="answer[]" value="<%= question_id %>" >
        <label for="checkbox<%= question_id %>"><%= t.name %></label>
        <% question_id += 1 %>
      <% end %>
    </div>
  <% end %>
  <%= submit_tag %>
<% end %>
```
`http://localhost:3000/diagnoses/new`へアクセス

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324819/b97ef1b3-fc8a-e2f4-2bb1-1bfa7e7dd9b7.png)

一旦、送信されたパラメータを確認

```erb:app/views/diagnoses/index.html.erb
<%= params[:answer] %>

<%= link_to "診断へ", diagnoses_new_path %>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324819/8251ba0c-f2fc-bbec-25d8-6ed6ad093f80.png)

この送られた情報をもとにGameから最もマッチしたレコードを取り出す処理を書く

```ruby:diagnoses_controller.rb
  def index
    games = Game.all
    selected_tag_ids = params[:answer]
    @mach_tag_count = 0
    tmp_mach_tag_count = 0
    @mached_games = []

    games.each do |game|
      tmp_mach_tag_count = 0
      game.tags.each do |tag|
        if selected_tag_ids&.include?(tag.id.to_s)
          tmp_mach_tag_count = tmp_mach_tag_count + 1 
        end
      end

      if @mach_tag_count < tmp_mach_tag_count
        @mached_games = []
        @mached_games << game
        @mach_tag_count = tmp_mach_tag_count
      elsif @mach_tag_count == tmp_mach_tag_count
        @mached_games << game
      end
    end
  end
```

マッチしたレコードとマッチ度を表示させる

```erb:app/views/diagnoses/index.html.erb
マッチ度：
<%= @mach_tag_count.to_f / params[:answer].length.to_f * 100 %> %

<% @mached_games.each do |game| %>
  <div class="game-container">
    Game名:<%= game.name %><br>
    説明:<%= game.discription %><br>
    タグ:
    <% game.tags.each do |tag| %>
      <%= tag.name %>
    <% end %>
  </div>
<% end %>

<%= link_to "診断へ", diagnoses_new_path %>
<%= link_to "New game", new_game_path %>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324819/e345753e-ce28-06a6-d221-303627675742.png)

以上で完成です。