active_hashとは
gemの一種でハッシュのデータを、ActiveRecordと同じように使えるようにしてくれる。
都道府県など基本的に変更されないデータはデータベースに保存する必要性がなく、ビューファイルにデータを直接書いてしまうと、可読性に欠けてしまう。このような場面でactive_hashを使う。
目標
簡単なActiveHashを使用したスポーツ記事の投稿アプリの作成(今回CSSは割愛)
ActiveHashを導入
gem 'active_hash'
モデルを作成する
次にモデルを作成する。
今回モデルはsportsモデルとgenreモデルの2つ作成する。
まずはrails g modelでsportsモデルを作成。
rails g model sports
次にgenreモデルを作成します。
rails g model genre --skip-migration
今回の--skip-migrationとは、モデルファイルを作成するときに、マイグレーションファイルの生成を行わないためのオプションです。
次にgenre.rbを編集していきます。
この時にActiveHash::Baseクラスを継承するようにします。ActiveHash::Baseを継承することで、ActiveRecordと同じようなメソッドを使用できるようになります。結果以下のようなオブジェクトを記述したとします。
class Genre < ActiveHash::Base
self.data = [
{ id: 1, name: '--' },
{ id: 2, name: '野球' },
{ id: 3, name: 'サッカー' },
{ id: 4, name: 'テニス' },
{ id: 5, name: '卓球' },
{ id: 6, name: 'バスケットボール' },
{ id: 7, name: '陸上' },
{ id: 8, name: 'スポーツ' }
]
end
ジャンルのデータは、配列にハッシュ形式で格納しています。
またsportsモデルのマイグレーションファイルにgenre_idというカラムを追加することによってsportsテーブルのなかでGenreモデル(ActiveHash)のidを外部キーとして管理することで、その記事に紐付いたジャンルの取得ができるようになります。
class CreateSports < ActiveRecord::Migration[6.0]
def change
create_table :sports do |t|
t.string :title , null: false
t.text :text , null: false
t.integer :genre_id , null: false t.timestamps
end
end
end
$ rails db:create
$ rails db:migrate
アソシエーションの設定
active_hashにはbelongs_to_active_hashメソッドが用意されているので、
sports.rbに このメソッドを記述し、アソシエーションを定義する。
class Sports < ApplicationRecord
extend ActiveHash::Associations::ActiveRecordExtensions
belongs_to :genre
end
バリデーションを設定
先ほどのsports.rbにバリデーションを追記する。
class Sports < ApplicationRecord
extend ActiveHash::Associations::ActiveRecordExtensions
belongs_to :genre
#空の投稿を保存できないようにする
validates :title, :text, presence: true
#ジャンルの選択が「--」の時は保存できないようにする
validates :genre_id, numericality: { other_than: 1 }
end
ルーティングの設定
Rails.application.routes.draw do
root to: 'sportses#index'
resources :sportses
end
コントローラーとビューを作成
rails g controller sportses index new
コントローラーの編集
class ArticlesController < ApplicationController
def index
@sportses = Sports
end
def new
end
def create
@sports = Sports.new(sports_params)
if @sports.save
redirect_to root_path
else
render :new
end
end
private
def sports_params
params.require(:sports).permit(:title,:text,:genre_id)
end
end
ビューの編集
<h1>記事一覧</h1>
<%= link_to "投稿する", new_sports_path, class:"post" %>
<% @sportses.each do |sports| %>
<div class="sports">
<div class="sports-genre">
<%= sports.genre.name %>
</div>
<div class="sports-title">
<%= sports.title %>
</div>
<div class="sports-text">
<%= sports.text %>
</div>
</div>
<% end %>
プルダウンバーの実装
記事投稿画面の実装です。
<%= form_with model: @sports, url:sportses_path, local: true do |f| %>
<div class="sports-box">
記事を投稿する
<%= f.text_field :title, class:"title", placeholder:"タイトル" %>
<%= f.text_area :text, class:"text", placeholder:"テキスト" %>
<%= f.collection_select(:genre_id, Genre.all, :id, :name, {}, {class:"genre-select"}) %>
<%= f.submit "投稿する" ,class:"btn" %>
</div>
<% end %>
collection_selectを使うことで先ほどのアクティブハッシュをプルダウン形式で使用することができる。
collection_selectは第一引数から第五引数まで存在し以下の順に引数を入力していく。
Genre.allによってgenre.rbのデータ(配列)を指定している。ビューでユーザーが実際に選んだスポーツ名はgenre_idとしてsportsモデルに保存されるという流れになる。
<%= form.collection_select(保存されるカラム名, オブジェクトの配列, カラムに保存される項目, 選択肢に表示されるカラム名, オプション, htmlオプション) %>
<%= f.collection_select(:genre_id, Genre.all, :id, :name, {}, {class:"genre-select"}) %>
終わりに
初めてここまで本格的なアウトプットをしたので至らない部分は多かったと思う。編集してわかりやすいようにしていくが今回この記事を書くことによってアクティブハッシュに関する理解が自分の中でグッと整理できた。