WeBlog

Webに関する情報を中心に発信してるブログ

アソシエーションを使って、user_idを外部キーとしてもつオブジェクトを作成する

掲示板などでユーザーがフォームから送信したデータと紐づくように、どのユーザーが投稿したかを外部キーとして持つオブジェクトを作成したい時があります。

例えば、Twitterなどでツイートした情報がどのユーザーが投稿した情報なのか識別できないといけないです。

その際に、外部キーとしてuser_idを持ったオブジェクトを作成しないといけません。

その外部キーカラムに値を設定する方法を書いていきます。

モデルファイル

今回はアソシエーションを使って外部キーに値を設定したいので、アソシエーションをはります。

Userモデルファイルでは、has_manyを使ってアソシエーションを定義しています。

1人のユーザーは複数の投稿をするので、has_many :boardsと書いています。

なので、複数形にしないといけません。

class User < ApplicationRecord
  authenticates_with_sorcery!
  has_many :boards, dependent: :destroy
end

掲示板側から見ると、1つの掲示板は1人のユーザーと紐づいています。

なので、belongs_to :userと書いています。

なので、単数系にしないといけません。

class Board < ApplicationRecord
  belongs_to :user
end

アソシエーションを使って投稿にuser_idを持たせる

ユーザーがフォームからタイトルと本文を送信する場合に、その送信した情報がどのユーザーが投稿したのか情報を持っておく必要があります。

その際に、外部キーとしてuser_idが必要ですが、その外部キーをどのように取得するかです。

思いつく方法としては下記のように書くことができます。

ストロングパラメータを使ってモデルのオブジェクトを作り、後からログインしているユーザーのidを代入する方法です。

@board = Board.new(board_params)
@board.user_id = current_user.id

しかし、アソシエーションをはっているともっと効率的に書くことができます。

下記のように書くことで1行で済みます。

Userモデルにhas_many :boardsというアソシエーションを貼ることで、Userモデルのオブジェクトでboardsというメソッドを呼び出すことができます。

buildメソッドはnewメソッドと役割は同じですが、アソシエーションを使って、オブジェクトを作成していることを明示するためにbuildメソッドを使います。

@board = current_user.boards.build(board_params)

current_user.boardsを実行してみた結果

current_user.boardsを実行するとどうなるのかをrails consoleで試してみます。

現在ログインしているユーザーのidが2番だった場合は、外部キーのuser_idが2番になっているデータを全て取得してきています。

current_user.boardsは現在ログインしているユーザーが投稿したデータを全て取得することができることがわかります。

> current_user.boards

=> [#<Board:0x00007fe702a5c148
  id: 2,
  title: "タイトル2",
  body: "本文2",
  user_id: 2,
  created_at: Thu, 11 Mar 2021 08:10:46 JST +09:00,
  updated_at: Thu, 11 Mar 2021 08:10:46 JST +09:00>,
 #<Board:0x00007fe703857ea0
  id: 3,
  title: "タイトル3",
  body: "本文3",
  user_id: 2,
  created_at: Thu, 11 Mar 2021 08:10:46 JST +09:00,
  updated_at: Thu, 11 Mar 2021 08:10:46 JST +09:00>,
 #<Board:0x00007fe703857c70
  id: 4,
  title: "タイトル4",
  body: "本文4",
  user_id: 2,
  created_at: Thu, 11 Mar 2021 08:10:46 JST +09:00,
  updated_at: Thu, 11 Mar 2021 08:10:46 JST +09:00>,

以下続く

current_user.boards.buildを実行してみた結果

current_user.boards.buildを実行すると下記のような結果になります。

現在ログインしているユーザーidが2番だっった場合は、current_user.boards.buildでBoardモデルのオブジェクトが作成されて、外部キーであるuser_idに2という値が自動で設定されています。

@board.user_id = current_user.idと書かなくても、自動で設定してくれるのでとても便利です。

> current_user.boards.build

=> #<Board:0x00007fe6f8f9a6c8 id: nil, title: nil, body: nil, user_id: 2, created_at: nil, updated_at: nil>

current_user.boards.build(title: 'タイトル', body: '本文')を実行して結果

current_user.boards.buildの引数にユーザーがフォームから送信した情報を設定してみます。

そうするとBoardモデルのオブジェクトが作成されて、きちんと値が設定されています。

> current_user.boards.build(title: 'タイトル', body: '本文')
=> #<Board:0x00007fe6fe667848 id: nil, title: "タイトル", body: "本文", user_id: 2, created_at: nil, updated_at: nil>

なので、先ほど書いた下記のような書き方で、アソシエーションを使ってモデルのオブジェクトが作成できます。

@board = current_user.boards.build(board_params)

mergeメソッドを使って外部キーに値を設定する

rubyにはmergeメソッドがあります。

https://docs.ruby-lang.org/ja/latest/method/Hash/i/merge.html

このメソッドは連想配列(ハッシュ)に対して、別のハッシュをマージした結果を返します。

なので、これを使うことで外部キーに値を設定することができます。

# ストロングパラメーターを使って、ユーザーがフォームから送信した値を取得
> board_params
=> <ActionController::Parameters {"title"=>"ssss", "body"=>"ssssss"} permitted: true>

# ストロングパラメーターを使って、ユーザーがフォームから送信した値を取得し、mergeメソッドでログインしているユーザーのidをマージする
> board_params.merge(user_id: current_user.id)
=> <ActionController::Parameters {"title"=>"ssss", "body"=>"ssssss", "user_id"=>2} permitted: true>

# mergeした値を使ってモデルのオブジェクトを作る
> board = Board.new(board_params.merge(user_id: current_user.id))
=> #<Board:0x00007fe702714878 id: nil, title: "ssss", body: "ssssss", user_id: 2, created_at: nil, updated_at: nil>