ブログのような何か

Rails関連の備忘録だったり、個人的なメモだったり・・

Railsのアソシエーションと複数登録 ~モデルの準備~

Facebook等からユーザー情報を持ってくる場合は、個人的には別テーブルに詳細情報をもたせたい。
詳細情報が各項目必ず一つであれば詳細テーブル内に入れてしまえば良いが、
例えば経歴だったり資格だったり、複数持つ情報の場合は詳細テーブルだけでは事足りない。

そんな時のアソシエーションの備忘録 (・ω・)

はじめに

OAuth認証でログインしてきた人に追加で必要な情報を入力させる画面を想定してみる。
今回はそこに持っているゲーム機とゲーム経験を登録させる。
このあたりのデータはなんでも良いが、特定のリストから選択と自由入力項目を複数同時に登録出来るような入力画面を作成する。

要件定義

ざっくりと大体↓こんな感じ。

  • Userは一つのProfileを持つ
  • Profileは複数のゲーム機を持つ
  • ゲーム機名称は別テーブルで持たせて、選択させる
  • Profileは複数のゲーム経験を持つ こちらは自由入力

モデルを作成する

まずは必要なモデルを作る。

class User < ApplicationRecord
 # OAuth認証を想定して最低限の情報で
end
class Profile < ApplicationRecord
 # 入力してほしい情報はすべてここに集約
end
class GameCareer < ApplicationRecord
 # ゲーム経験
end
class GameConsole < ApplicationRecord
 # 所持しているゲーム機 
end
class PossessGame < ApplicationRecord
# 所持してるゲーム機とプロフィールをつなげる中間テーブル
# 今回は繋げるだけなのでIDしか用意し無いが、例えば購入日等あればここに乗せても良い。
end

アソシエーションを組む

ユーザーとプロフィール
class User < ApplicationRecord
  has_one :profile, dependent: :destroy
end
class Profile < ApplicationRecord
  belongs_to :user
end

ユーザーは必ず1つ(2以上はありえない)のプロフィールを持つのでhas_oneで繋げる。
belogns_to側にIDを入れる必要があるので、Userをhas_one,Profileをbelongs_toとする。

プロフィールとゲーム経験
class Profile < ApplicationRecord
  has_many :game_careers, dependent: :destroy
end
class GameCareer < ApplicationRecord
  belongs_to :profile
end

プロフィールは複数のゲーム経験を持つので、has_manyで繋げる。

プロフィールと持っているゲーム機
class Profile < ApplicationRecord
  has_many :possess_games, dependent: :destroy
end
class PossessGame < ApplicationRecord
  belongs_to :profile
  belongs_to :game_console
end
GameConsole < ApplicationRecord
 has_many :possess_games
end

中間テーブルが出て来るが基本的に同じ。やってることは名称のマスタ化。(いわゆる第二正規化)
これでProfile - PossessGame と PossessGame - GameConsoleは繋がったが、
このままだとプロフィールから直接持っているゲーム機を取得するのが面倒なので
ProfileとGameConsoleを繋げる。

class Profile < ApplicationRecord
  -省略-
  has_many :games, through: :possess_games, source: :game_consoles
end
GameConsole < ApplicationRecord
  -省略-
  has_many :profiles, through: :possess_games
end

through: :possess_gamesとすると、中間テーブルを介してデータを取得出来る。
また、has_manyの後にaliasを定義し、sourceでモデル(テーブル名)を指定すると@profile.○○と別名で呼べるので、モデル名が無駄に長いときやわかりにくい時に便利。

一応直接繋げない場合でも↓のように呼び出すことが可能。

@profile.possess_games.map{|game|game.game_console}


ここまでで、アソシエーションが完了。
seedで適当なデータを登録してConsoleを叩くと、データが取得できる。
アソシエーションの詳細はreflect_on_all_associationsメソッドで確認できるので、モデルから何のデータが呼べるのか確認する場合は、nameを取得すると一発。

pry(main)> Profile.reflect_on_all_associations.map(&:name)
=> [:user, :game_careers, :possess_games, :games]

>>> Profileから以下の4つが呼び出せる。
@profile.user 
@profile.game_careers
@profile.possess_games
@profile.games

次は登録画面を作成していく。