ポリモーフィック関連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>