ポリモーフィック関連2 (コールバックで関連テーブルへのレコード保存の設定とその時吐かれるSQL)

profyでは、ActiveRecordコールバックを利用して、questions、answersテーブルのレコードが作られたら、ポリモーフィック関連先のテーブルであるfeed_contentsテーブルにもレコードが保存されるようにしています。
ここの仕組みが難しいと感じたので調べて記事にしてみました。

 ActiveRecordコールバックとは?

ActiveRecordコールバックとは、createやdestroy、updateなど、特定のイベント発生時に呼び出されるメソッドのことで、これらのイベント発生時に常に実行されるコードを書くことができます。

profyでは、after_createコールバックを利用して、QuestionクラスのインスタンスとAnswerクラスのインスタンスが作成された後、FeedContentクラスのインスタンスも常に作成されるよう設定しています。

question.rb
class Question < ApplicationRecord
  after_create :create_feed_content  
#ここでコールバック指定。questionクラスのインスタンスが作成されたら必ずcreate_feed_contentメソッドが呼ばれるよう指定しています。

  belongs_to :user
  belongs_to :group
  has_many :answers
  has_one :feed_content, as: :content, dependent: :destroy

  validates_presence_of :user_id, :text, :group_id

  def user_answer(user_id)
    Answer.find_by(user_id: user_id, question_id: id)
  end

  private
  def create_feed_content
    self.feed_content = FeedContent.create(group_id: group_id, updated_at: updated_at)
#feed_contentsテーブルのgroup_idカラムとupdated_atカラムに、selfのgroup_id、updated_atの値を保存しています。
  end
end
answer.rb
class Answer < ApplicationRecord
  after_create :create_feed_content
#Answerクラスのインスタンスが作成されたらcreate_feed_contentメソッドが呼ばれるようコールバックを指定しています。

  belongs_to :question
  belongs_to :user
  has_one :feed_content, as: :content, dependent: :destroy

  private
  def create_feed_content
    self.feed_content = FeedContent.create(group_id: question.group_id, updated_at: updated_at)
#feed_contentsテーブルのgroup_idカラムとupdated_atカラムに、selfと関連するquesionのgroup_idの値とselfのupdated_atの値を保存しています。
  end
end

 吐かれるSQL

上記のようにafter_createコールバックを設定し、Questionクラスのインスタンスを作成してみると、以下のように、questionsテーブルにレコードが保存された後、feed_contentsテーブルにもレコードが保存されているのが確認できます。

[1] pry(main)> Question.create(user_id: 4, group_id: 1, text: "test")
   (42.0ms)  SET NAMES utf8,  @@SESSION.sql_mode = CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'),  @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 2147483
   (0.2ms)  BEGIN
  User Load (0.4ms)  SELECT  `users`.* FROM `users` WHERE `users`.`id` = 4 LIMIT 1
  Group Load (0.3ms)  SELECT  `groups`.* FROM `groups` WHERE `groups`.`id` = 1 LIMIT 1
  Question Create (1.2ms)  INSERT INTO `questions` (`user_id`, `text`, `group_id`, `created_at`, `updated_at`) VALUES (4, 'test', 1, '2018-11-22 05:32:25', '2018-11-22 05:32:25')
  FeedContent Load (0.5ms)  SELECT  `feed_contents`.* FROM `feed_contents` WHERE `feed_contents`.`content_id` = 4 AND `feed_contents`.`content_type` = 'Question' LIMIT 1
  FeedContent Create (37.6ms)  INSERT INTO `feed_contents` (`content_id`, `content_type`, `group_id`, `created_at`, `updated_at`) VALUES (4, 'Question', 1, '2018-11-22 05:32:25', '2018-11-22 05:32:25')
   (0.4ms)  COMMIT
=> #<Question:0x00007fcff2e8e0f8
 id: 4,
 user_id: 4,
 text: "test",
 group_id: 1,
 created_at: Thu, 22 Nov 2018 05:32:25 UTC +00:00,
 updated_at: Thu, 22 Nov 2018 05:32:25 UTC +00:00>

Answersクラスのインスタンスを作成した時も同じように、answersテーブルにレコードが保存された後、feed_contentsテーブルにもレコードが保存されているのが確認できます。

[2] pry(main)> Answer.create(question_id: 4, user_id: 3, text: "test_answer")
   (0.2ms)  BEGIN
  Question Load (45.3ms)  SELECT  `questions`.* FROM `questions` WHERE `questions`.`id` = 4 LIMIT 1
  User Load (0.4ms)  SELECT  `users`.* FROM `users` WHERE `users`.`id` = 3 LIMIT 1
  Answer Create (24.4ms)  INSERT INTO `answers` (`question_id`, `user_id`, `text`, `created_at`, `updated_at`) VALUES (4, 3, 'test_answer', '2018-11-22 06:07:02', '2018-11-22 06:07:02')
  FeedContent Load (0.4ms)  SELECT  `feed_contents`.* FROM `feed_contents` WHERE `feed_contents`.`content_id` = 3 AND `feed_contents`.`content_type` = 'Answer' LIMIT 1
  FeedContent Create (0.2ms)  INSERT INTO `feed_contents` (`content_id`, `content_type`, `group_id`, `created_at`, `updated_at`) VALUES (3, 'Answer', 1, '2018-11-22 06:07:02', '2018-11-22 06:07:02')
   (33.2ms)  COMMIT
=> #<Answer:0x00007fcff2a6deb0
 id: 3,
 question_id: 4,
 user_id: 3,
 text: "test_answer",
 created_at: Thu, 22 Nov 2018 06:07:02 UTC +00:00,
 updated_at: Thu, 22 Nov 2018 06:07:02 UTC +00:00>

profy3_—_ruby_bin_rails_c_—_191×53_と_Slack_-_TECH__CAMP.png