項目内容の情報を保存する 〜Active_Hash〜

情報の登録フォーマットを実装する際に避けて通れないのが、選択した項目(collectionなど)をDBに保存すること。
フリマなどのアプリでは都道府県、商品の状態、配送日数などの登録が必要です。アンケートを想定するなら性別、生年月日など様々な場面でみられますね。
項目内容を予めseed.rbなどで用意してテーブルを作るということを想定する方も多くいると思いますが、これは無駄なテーブルを作っていると言っても過言ではありません。

<h2>無駄なテーブル？</h2>
DB設計などでもよく扱われる内容が「正規化」です。ざっくり言ってしまうとデータの構造を効率的でシンプルにすることです。しかしこの正規化を徹底的に行ってしまうと、小さなテーブルが増えてしまいこれも処理の負荷の要因につながります。そのため項目専用テーブルを作ることは極力避けなければなりません。

そんな際に役立つのがActive_hashです

<h2>Active_hashとは？</h2>
専用モデルを作り、その中に予め項目内容の値を用意することでDBを介さずとも項目を用意することができます。言うなれば「モデル内に項目テーブル」を作ることになります。こうすることで小さなテーブルをDB側に用意する必要がなくなるため負荷が軽くなるのです。

<h2>導入手順 </h2>
今回はcollection_selectを使った商品アンケートを想定して実装していこうと思います。
保存・管理したい内容は以下の4つです。
・年代
・性別
・商品を知った経緯
・商品を使ってみての感想

DB負荷などを考えてテーブルは1つにしたいです。
設計としては
1.年代は10代〜90代
2.性別は男性・女性
3.商品を知った経緯は広告、知人、家族、インターネット
4.商品を使ってみての感想は手入力
としていきます。

この中で項目(collection_select)として使用したいのは1~3番の内容です。
ではこれを想定してアンケートを実装していきましょう。
今回の解説はactive_hashの実装のみに留めますのでそれ以外の行程は割愛しますのでご了承ください。

<h3> Step:1　Gemfileにactive_hashを追加しインストールを実行
</h3>

````ruby
gem 'active_hash'
````

ターミナル

````terminal
% bundle install
````

<h3> Step:2 モデル＆マイグレーションファイルの用意 </h3>

gコマンドでモデルを生成  ※モデル名はお好みで

````terminal
% rails g model model question
````

・マイグレーションファイルの準備

active_hashを使う場合項目内容はintegerカラムで管理します。このintegerカラムがidとして動いてくれるのです。そのためカラム名は必ず"_id"をつけることを忘れないようにしましょう。
手入力欄についてはいつものtextカラムで実装します。


00000000000000_create_questions.rb

````ruby
class CreateQuestions < ActiveRecord::Migration[6.0]
  def change
    create_table :questions do |t|
      t.integer :age_id,         null: false
      t.integer :sexuality_id,   null: false
      t.integer :find_item_id,   null: false
      t.text    :comment,        null: false
      t.timestamps
    end
  end
end

````

その後いつものようにテーブルを作成して、マイグレーションファイルを実行します。

````terminal
% rails db:create
% rails db:migrate
````

・項目ごとのモデルファイルを作成する
active_hashを実装するモデルを項目の3つ分を用意します。
マイグレーションファイルは必要ないのでオプションをつけてスキップさせます。

````terminal
% rails g model age --skip-migration
% rails g model sexuality --skip-migration
% rails g model find_item --skip-migration
````

では各項目用に準備したモデルの中身を書いていきましょう

生成したモデルの内部はおそらく初期はこのような形になっていると思います

````ruby
class Age < ApplicationRecord
end
````

これをactive_hash専用モデルにするため、以下のように編集を加えます。

````ruby
class Age < ActiveHash::Base
end
````

これにより、このモデル内で擬似的な項目テーブルを作れるようになりました。
項目をこの中に入れる際はクラスメソッドとハッシュを使って実装していきます。
可読性をあげるポイントは1つめのキーをidに、「もう1つのキーには項目として扱いたい名前をつけること。
例

````ruby
class クラス名 < ActiveHash::Base
  self.data = [
  { id: 0, hoge: fuga }]
end
````

これに沿って以下のようになります。

````ruby
class Age < ActiveHash::Base
  self.data = [
      { id: 0, name: '---' },
      { id: 1, name: '10代' },
      { id: 2, name: '20代' },
      { id: 3, name: '30代' },
      { id: 4, name: '40代' },
      { id: 5, name: '50代' },
      { id: 6, name: '60代' },
      { id: 7, name: '70代' },
      { id: 8, name: '80代' },
      { id: 9, name: '90代' }
  ]
````

````ruby
class FindItem < ActiveHash::Base
  self.data = [
    { id: 0, example: '---' },
    { id: 1, example: '広告で知った' },
    { id: 2, example: '知人の紹介で知った' },
    { id: 3, example: '家族の紹介で知った' },
    { id: 4, example: 'インターネットで知った' }
  ]
end
````

````ruby
class Sexuality < ActiveHash::Base
  self.data = [
    { id: 0, name: '---' },
    { id: 1, name: '男性' },
    { id: 2, name: '女性' }
  ]
end
````

これでactive_hashを使って擬似テーブルの作成は完了。
あとは大元となる一番最初に作ったquestionモデルに連携させる記述を施します。

````ruby
class Question < ApplicationRecord
  extend ActiveHash::Associations::ActiveRecordExtensions
  belongs_to_active_hash :age
  belongs_to_active_hash :sexuality
  belongs_to_active_hash :find_item
end
````



<h3> Step:3 ビューファイルの記述</h3>

````ruby
<h1>当社の商品についてアンケートにご協力ください</h1>

<div class="wrapper">
  <div class="form">
    <%= form_with model: @question, local: true do |f| %>
      <div class="form-box">
        <h3>年代をお選びください</h3>
        <%= f.collection_select(:age_id, Age.all, :id, :name) %>
      </div>
      <div class="form-box">
        <h3>性別をお選びください</h3>
        <%= f.collection_select(:sexuality_id, Sexuality.all, :id, :name)%>
      </div>
      <div class="form-box">
        <h3>当社の商品をどこでお知りになりましたか？</h3>
        <%= f.collection_select(:find_item_id, FindItem.all, :id, :example)%>
      </div>
      <div class="form-text">
        <h3>当社の商品をお使頂いてみての感想を簡単にで構わないのでご記入ください</h3>
        <%= f.text_area :comment %>
      </div>
      <%= f.submit "送信する" %>
    <% end %>
  </div>
</div>
````

ポイントはcollection_selectの引数です。
第一引数はparamsに渡す値(カラム名)、 第二引数には表示させたい項目(モデル名)。

<h3> Step:4 コントローラの記述</h3>

```ruby
class QuestionsController < ApplicationController
  def index
    @question = Question.new
  end

  def create
    # binding.pry
    @question = Question.new(question_params)
    @question.save
    redirect_to root_path
  end

  private
  def question_params
    params.require(:question).permit(
      :age_id, 
      :sexuality_id, 
      :find_item_id, 
      :comment)
  end
end

```
ここについては特に言及箇所はありません。


手短に実装を行ってみました

皆さんもぜひactive_hashに挑戦してみてください