テックキャンプ86日目〜商品出品機能②/ActiveHash

kobasaです(´ω`*)商品出品機能で盛大に詰まってましたw
夕方までにレビュー依頼出すつもりだったんだけどなぁ。ActiveHashにやられました。

ActiveHash

ActiveHashとは

カテゴリーや都道府県など、「基本的に変更されないデータ」はデータベースに保存する必要がない。

ActiveHashを使用すると変更されないデータをモデルファイル内に直接記述することで、データベースに保存せずにデータを取り扱うことができる。
また、モデルに記述したデータに対してActiveRecordのようなメソッドを用いることができる。

Gemの導入とマイグレーションファイルの記述

ActiveHashはGemなのでまずはGemファイルに記述してbundle install

gem 'active_hash'

次にマイグレーションファイルの記述。

class CreateItems < ActiveRecord::Migration[6.0]
  def change
    create_table :items do |t|
      t.string     :name,                     null: false
      t.text       :description,            null: false
      t.integer    :category_id,         null: false
      t.integer    :item_status_id,   null: false
      t.integer    :price,                     null: false
      t.references :user,                   foreign_key: true
      t.timestamps
    end
  end
end

商品(item)を表示するときに「変更されないデータ(categoryやitem_status)」を紐付けて取得するため、末尾に「_id」をつけたカラム名で保存する。
データの項目名をidで管理する。idは数値になるのでinteger型を指定。

モデルファイルの記述

変更されないデータのモデルファイルの記述

次に「変更されないデータ名」でモデルファイルを作成する。
「item_status.rb」など(末尾に_idは不要)。

class ItemStatus < ActiveHash::Base
  self.data = [
    { id: 1, item_status: '---' },
    { id: 2, item_status: '未使用に近い' },
    { id: 3, item_status: 'やや傷や汚れあり' },
    { id: 4, item_status: '全体的に状態が悪い' }
  ]

  include ActiveHash::Associations
  has_many :items
end

①1行目でクラスを定義し、ActiveHash::Baseクラスを継承する。
※ファイル名で単語の区切りに_を使用してもクラス名では大文字で単語を区切ること!

②2〜7行目でデータを配列にハッシュ形式で格納する。
idと項目名(item_statusの部分。任意の名前)でデータを記述していく。
データベースにはidの値、integer型で保存する。
※integer型は0から始まる値は識別できない?ため、未選択を表す ‘—‘のidは1にしておく。

③9〜10行目ではアソシエーションの記述をしている。
ActiveHashを使用する場合は9行目の記述をし、10行目でデータを保存するテーブルとの関係を記述している。
変更されないデータ名は複数の出品(item)に選択されるのでhas_many。

出品側のモデルファイルの記述

class Item < ApplicationRecord
  # バリデーションの設定
  with_options numericality: { other_than: 1, message: "can't be blank" } do
    validates :category_id
    validates :item_status_id
  end

  # アソシエーションの設定
  has_one_attached :image
  belongs_to :user

  extend ActiveHash::Associations::ActiveRecordExtensions
  belongs_to :category
  belongs_to :item_status
end

④numericalityは数値かどうかを検証するバリデーション。数値以外は保存しない。
項目名はidの値で保存されるためnumericalityを使用する。

またid:1は未選択’–‘とみなし、保存したくないのでother_than: 1と記述する。
これでid:1以外なら保存できる。
そのままだとエラーメッセージが伝わりづらいものになるため、
messageオプションで “can’t be blank”を指定している。

データベースのカラム名にバリデーションをかけるため_idが必要

⑤ActiveHashを使用したアソシエーションの記述。
選択項目との関係はbelongs_to。モデルとの関係性のため_idは不要

プルダウンでデータを表示させる

<%= form_with model: @item, url: items_path, local: true do |f| %>

<%= f.collection_select(:item_status_id, ItemStatus.all, :id, :item_status, {}, {class:"select-box", id:"item-sales-status"}) %>

<% end %>

collection_selectメソッドを使用することで、データをプルダウン形式で表示することができる。

  • 第一引数:DB保存先のカラム名。
  • 第二引数:配列データの指定。メソッドで範囲指定も可。
  • 第三引数:カラムに保存される項目。
  • 第四引数:プルダウンに表示される項目名。
  • 第五引数:オプション。ない場合は{}。
  • htmlオプション:クラス名など。

①DBに保存するカラム名なので_idが必要。
②配列は変更されないデータのモデルファイル内にあるためクラス名を記述。
全てのデータを取得するのでallメソッドを使用。
③DBに保存される値の項目名(id:)
④プルダウンに表示される値の項目名(:item_status)。ハッシュ内で任意で指定したやつ。

まとめ

_idの有無や、カラム名に_を使用した場合はクラス名の記述に注意!

コメント

  1. きっかけはアッパーキャメルケース より:

    アソシエーションのときの記述ほんとぐっちゃぐちゃなりますね
    このときはこう!みたいな表(チートシート)作ったほうがええんかな……

    • kobasa より:

      チートシートあると便利そうですね(´ω`*)
      ActiveHash自体は便利なんですけど記述がややこしい。
      コピペでいけるところはコピペしたいです。

タイトルとURLをコピーしました