😄

超小ネタ ポリモーフィック関連はモデルインスタンスを使って検索できる

2024/06/27に公開

Railsのポリモーフィック関連ではモデルインスタンスをwhereメソッドの検索条件として設定できることを知ったので超小ネタとして残しておきます。

テーブル・モデル定義

例として、comments テーブルを commentable ポリモーフィック関連で postsarticles モデルに関連付ける場合を示します。

まず、ridgepoleのSchemafileでテーブルを作成する手順です。
※ridgepole使ってない方ごめんなさい...

# Schemafile

create_table :posts do |t|
  t.string :title
  t.text :content
  t.timestamps
end

create_table :articles do |t|
  t.string :title
  t.text :body
  t.timestamps
end

create_table :comments do |t|
  t.text :content
  t.references :commentable, polymorphic: true, index: true
  t.timestamps
end
db/schema.rbはコチラ
# db/schema.rb
ActiveRecord::Schema[7.1].define(version: 0) do

  create_table "posts", charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t|
    t.string "title"
    t.text "content"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

  create_table "articles", charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t|
    t.string "title"
    t.text "body"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

  create_table "comments", charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t|
    t.text "content"
    t.bigint "commentable_id"
    t.string "commentable_type"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["commentable_type", "commentable_id"], name: "index_comments_on_commentable_type_and_commentable_id"
  end

end

次に、モデルの設定を行います:

# app/models/post.rb

class Post < ApplicationRecord
  has_many :comments, as: :commentable
end

# app/models/article.rb

class Article < ApplicationRecord
  has_many :comments, as: :commentable
end

# app/models/comment.rb

class Comment < ApplicationRecord
  belongs_to :commentable, polymorphic: true
end

これで、Post モデルと Article モデルはそれぞれ Comment モデルとポリモーフィックに関連付けられ、1つの comments テーブルで管理されます。

動作確認

irb(main):001> Post.new().save
  TRANSACTION (0.2ms)  BEGIN
  Post Create (0.9ms)  INSERT INTO `posts` (`title`, `content`, `created_at`, `updated_at`) VALUES (NULL, NULL, '2024-06-27 09:15:12.076130', '2024-06-27 09:15:12.076130')
  TRANSACTION (1.4ms)  COMMIT
=> true

Postを作成。

irb(main):003> Comment.new(commentable: post).save
  TRANSACTION (0.3ms)  BEGIN
  Comment Create (1.0ms)  INSERT INTO `comments` (`content`, `commentable_id`, `commentable_type`, `created_at`, `updated_at`) VALUES (NULL, 1, 'Post', '2024-06-27 09:15:42.899920', '2024-06-27 09:15:42.899920')
  TRANSACTION (5.3ms)  COMMIT
=> true

CommentにPostを紐づけて作成。

irb(main):005> post = Post.first
  Post Load (0.9ms)  SELECT `posts`.* FROM `posts` ORDER BY `posts`.`id` ASC LIMIT 1
=> 
#<Post:0x0000ffff62298e90
...

irb(main):006> Comment.where(commentable: post)
  Comment Load (2.7ms)  SELECT `comments`.* FROM `comments` WHERE `comments`.`commentable_type` = 'Post' AND `comments`.`commentable_id` = 1 /* loading for pp */ LIMIT 11
=> 
[#<Comment:0x0000ffff6229dfd0
  id: 1,
  content: nil,
  commentable_id: 1,
  commentable_type: "Post",
  created_at: Thu, 27 Jun 2024 09:15:42.899920000 UTC +00:00,
  updated_at: Thu, 27 Jun 2024 09:15:42.899920000 UTC +00:00>]

Commentを検索するときに、commentableにPostインスタンスを設定してあげると
WHERE comments.commentable_type = 'Post'
という検索条件が入っているのが分かる。

SMARTCAMP Engineer Blog

Discussion