情報の登録フォーマットを実装する際に避けて通れないのが、選択した項目(collectionなど)をDBに保存すること。
フリマなどのアプリでは都道府県、商品の状態、配送日数などの登録が必要です。アンケートを想定するなら性別、生年月日など様々な場面でみられますね。
項目内容を予めseed.rbなどで用意してテーブルを作るということを想定する方も多くいると思いますが、これは無駄なテーブルを作っていると言っても過言ではありません。
無駄なテーブル?
DB設計などでもよく扱われる内容が「正規化」です。ざっくり言ってしまうとデータの構造を効率的でシンプルにすることです。しかしこの正規化を徹底的に行ってしまうと、小さなテーブルが増えてしまいこれも処理の負荷の要因につながります。そのため項目専用テーブルを作ることは極力避けなければなりません。そんな際に役立つのがActive_hashです
Active_hashとは?
専用モデルを作り、その中に予め項目内容の値を用意することでDBを介さずとも項目を用意することができます。言うなれば「モデル内に項目テーブル」を作ることになります。こうすることで小さなテーブルをDB側に用意する必要がなくなるため負荷が軽くなるのです。導入手順
今回はcollection_selectを使った商品アンケートを想定して実装していこうと思います。 保存・管理したい内容は以下の4つです。 ・年代 ・性別 ・商品を知った経緯 ・商品を使ってみての感想DB負荷などを考えてテーブルは1つにしたいです。
設計としては
1.年代は10代〜90代
2.性別は男性・女性
3.商品を知った経緯は広告、知人、家族、インターネット
4.商品を使ってみての感想は手入力
としていきます。
この中で項目(collection_select)として使用したいのは1~3番の内容です。
ではこれを想定してアンケートを実装していきましょう。
今回の解説はactive_hashの実装のみに留めますのでそれ以外の行程は割愛しますのでご了承ください。
Step:1 Gemfileにactive_hashを追加しインストールを実行
gem 'active_hash'
ターミナル
% bundle install
Step:2 モデル&マイグレーションファイルの用意
gコマンドでモデルを生成 ※モデル名はお好みで
% rails g model model question
・マイグレーションファイルの準備
active_hashを使う場合項目内容はintegerカラムで管理します。このintegerカラムがidとして動いてくれるのです。そのためカラム名は必ず"_id"をつけることを忘れないようにしましょう。
手入力欄についてはいつものtextカラムで実装します。
00000000000000_create_questions.rb
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
その後いつものようにテーブルを作成して、マイグレーションファイルを実行します。
% rails db:create
% rails db:migrate
・項目ごとのモデルファイルを作成する
active_hashを実装するモデルを項目の3つ分を用意します。
マイグレーションファイルは必要ないのでオプションをつけてスキップさせます。
% rails g model age --skip-migration
% rails g model sexuality --skip-migration
% rails g model find_item --skip-migration
では各項目用に準備したモデルの中身を書いていきましょう
生成したモデルの内部はおそらく初期はこのような形になっていると思います
class Age < ApplicationRecord
end
これをactive_hash専用モデルにするため、以下のように編集を加えます。
class Age < ActiveHash::Base
end
これにより、このモデル内で擬似的な項目テーブルを作れるようになりました。
項目をこの中に入れる際はクラスメソッドとハッシュを使って実装していきます。
可読性をあげるポイントは1つめのキーをidに、「もう1つのキーには項目として扱いたい名前をつけること。
例
class クラス名 < ActiveHash::Base
self.data = [
{ id: 0, hoge: fuga }]
end
これに沿って以下のようになります。
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代' }
]
class FindItem < ActiveHash::Base
self.data = [
{ id: 0, example: '---' },
{ id: 1, example: '広告で知った' },
{ id: 2, example: '知人の紹介で知った' },
{ id: 3, example: '家族の紹介で知った' },
{ id: 4, example: 'インターネットで知った' }
]
end
class Sexuality < ActiveHash::Base
self.data = [
{ id: 0, name: '---' },
{ id: 1, name: '男性' },
{ id: 2, name: '女性' }
]
end
これでactive_hashを使って擬似テーブルの作成は完了。
あとは大元となる一番最初に作ったquestionモデルに連携させる記述を施します。
class Question < ApplicationRecord
extend ActiveHash::Associations::ActiveRecordExtensions
belongs_to_active_hash :age
belongs_to_active_hash :sexuality
belongs_to_active_hash :find_item
end
Step:3 ビューファイルの記述
<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に渡す値(カラム名)、 第二引数には表示させたい項目(モデル名)。
Step:4 コントローラの記述
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に挑戦してみてください