Railsでいいね機能

個人開発しているサービスにいいねボタンをつけるためのメモ。

イメージ

  • 親テーブル
    • posts
    • comments
  • 子テーブル
    • likes

投稿にもコメントにも「いいね」できるようにしたい。

課題

親テーブルが複数ある時にテーブル設計をどうするか。

参考: 複数のテーブルに対して多対一で紐づくテーブルの設計アプローチ|日本橋のシステム開発会社|スパイスファクトリー株式会社

アプローチ方法

  • ポリモーフィック関連
  • 複数の関連テーブル(中間テーブル)
  • 親テーブルの作成
  • テーブルの分割

どれを選択するか。悩んだのでSQLアンチパターンを買って読みました。

SQLアンチパターン

SQLアンチパターン

合わせてmastodonも参考にしました。 mastodonでは、ポリモーフィック関連を採用しているようでした。

  create_table "notifications", id: :serial, force: :cascade do |t|
    t.integer "account_id"
    t.bigint "activity_id"
    t.string "activity_type"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.integer "from_account_id"
    t.index ["account_id", "activity_id", "activity_type"], name: "account_activity", unique: true
    t.index ["activity_id", "activity_type"], name: "index_notifications_on_activity_id_and_activity_type"
  end

なるべくRailsに沿ってつくりたかったので、今回はmastodonを参考にPolymorphicで実装することにしました。

実装

命名については、ableを語基の後ろに付けるものと抽象名詞を使うものとがあるようでした。

参考:Rails ポリモーフィック関連の関連名の命名 - 130単位

このあと通知機能の実装も検討していて、そこでもpolymorphicを使用する可能性があったので、汎用性が高い抽象名詞を利用することにしました。

$ rails g model like activity:references{polymorphic}:index user:references
class CreateLikes < ActiveRecord::Migration[5.0]
  def change
    create_table :likes do |t|
      t.references :activity, polymorphic: true
      t.references :user, foreign_key: true

      t.timestamps
    end
  end
end
$ rake db:migrate
class Like < ActiveRecord::Base
  belongs_to :activity, polymorphic: true
  belongs_to :user

  validates :user_id, uniqueness: { scope: [:activity_type, :activity_id] }
end

class Post < ActiveRecord::Base
  has_many :likes, as: :activity
end

class Comment < ActiveRecord::Base
  has_many :likes, as: :activity
end

参考