0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Rails いいね機能の作り方

Last updated at Posted at 2025-06-02

はじめに

この記事はプログラミング初学者が他の記事を参考にしたり、実際に実装してみたりして、アウトプットの一環としてまとめたものです。内容に不備などあればご指摘いただますと幸いです。

今回Xクローン作成中にいいね機能を実装しました。
そのいいね機能について備忘録も兼ねて記事を作成していきます。

また、リツイート機能も同じ手順で実装することができます。

実現したいこと

投稿されたツイートに対して、いいねをつけたり、外したりできる機能を実装すること。

いいね機能について

いいねする時のイメージ

like_1.png
  • ①のユーザーに注目すると、1人のユーザーから複数の線が伸びています。
    → つまりユーザーは「たくさんのツイートをいいね」することができます。
  • ②のツイートに注目すると、1つのツイートから複数の線が伸びています。
    →つまりツイートは「たくさんのユーザーにいいね」されます。

このように 「ユーザーもツイートもたくさん持っている」関係を多対多(M:N)の関係 といいます。
そして、多対多(M:N)の関係には中間テーブルが必要!

先ほどの図に中間テーブルを加えてみると

like_2.png

このようになります。

  • 中間テーブルでは、いいねするユーザーといいねされるツイートの関係を保存しています。
  • また、いいねするユーザーと中間テーブルが1対多。
    いいねされるツイートと中間テーブルが1対多。
    つまり、中間テーブルを介することで多対多を1対多で表現することができます。

中間テーブルを用いてER図を表すと

like_er.png

このようになります。

  • 中間テーブルはお互いのidを保存するテーブル。
  • 中間テーブルに、user_idとtweet_idを保存することで、いいねするユーザーといいねされるツイートの関係を実現しています。
    こうすることで、ツイートに対して、誰にいいねされたかやいいね数を判断することができます。

Likeモデルとテーブルの作成

それでは、実装していきましょう!
以下のコマンドでモデルを作成します。

$ rails g model like user_id:integer tweet_id:integer

生成されたマイグレーションファイルを以下のように編集します。

db/migrate/migrate/○○○_create_likes.rb
class CreateLikes < ActiveRecord::Migration[7.0]
  def change
    create_table :likes do |t|
      t.integer :user_id
      t.integer :tweet_id

      t.timestamps
    end

    add_index :likes, %i[user_id tweet_id], unique: true # 追加
  end
end

1人のユーザーは1回のいいねまでとしたいので、add_indexを使って一意制約を記述します。
これで、user_idとtweet_idの組み合わせをユニーク(1通り)に設定します。

以下のコマンドでマイグレーションを実行しましょう。

$ rails db:migrate

アソシエーションの設定

それぞれのファイルに以下を追加します。
こちらはDB側と同じようにアプリケーション側にも一意制約を記述しておきましょう。

app/models/like.rb
  belongs_to :user
  belongs_to :tweet
  
  # 1人のユーザーは1回のいいねまでとしたいので、一意制約を記述します。
  # これで、user_idとtweet_idの組み合わせをユニーク(1通り)に設定します。
  validates :user_id, uniqueness: { scope: :tweet_id }
app/models/user.rb
has_many :likes, dependent: :destroy
app/models/tweet.rb
has_many :likes, dependent: :destroy

dependent: :destroyは、has_manyで使えるオプションです。
1:Nの関係において、「1」のデータが削除された場合、関連する「N」のデータも削除される設定。
ここでは、ツイートが削除された場合に関連するいいねも削除されるように設定してます。

ルーティングの設定

いいねは、ツイートに対して、いいねするのでネストしたルーティングを作成します。

config/routes.rb
resources :tweets do
  resource :likes, only: [:create, :destroy]
end

また、likeの詳細ページは作らない、つまりlikeのidは要らず省略したいため、resourceと、単数形のメソッドを利用しています。

ルーティングを見てみると
スクリーンショット 2025-05-30 18.50.07.png
こんな感じで作成されます。
また、ネストしたルーティングを作成したことでtweet_idparams[:tweet_id]で取得できます。

Controllerの作成

以下のコマンドでコントローラを作成します。

$ rails g controller likes

生成されたコントローラのファイルを以下のように編集します。

app/controllers/likes_controller.rb
class LikesController < ApplicationController
  def create
    # current_userに関連したLikeクラスの新しいインスタンスを作成。
    # つまり、like.user_id = current_user.idが済んだ状態で生成されている。
    # buildはnewと同じ意味で、アソシエーションしながらインスタンスをnewする時に形式的に使われる。
    @like = current_user.likes.build(tweet_id: params[:tweet_id])
    @like.save!
    # redirect_backを使うと、直前のページにリダイレクトをしてくれる。
    # fallback_locationには直前のページに戻れなかった際のパスを記載する。
    redirect_back(fallback_location: root_path)
  end

  def destroy
    @like = current_user.likes.find_by(tweet_id: params[:tweet_id])
    @like.destroy!
    redirect_back(fallback_location: root_path)
  end
end

ユーザーがツイートをいいねしたかどうかを判定するメソッド

ユーザーがツイートをいいねしてあるかどうかを判定したいので以下のように記載します。

app/models/tweet.rb
def liked_by?(user)
  likes.exists?(user_id: user.id)
end

メソッド内のコードは、tweetに関連したいいね(like)の中で、今いいねしようとしているuser_idが存在するかどうかを判定しています。
このメソッドがtrueであればいいねを外す、falseであればいいねをするという流れでveiwを作っていきます。

Viewの作成

先ほどのliked_by?メソッドを使って作成していきます。

app/views/tweets/index.html.slim
= @tweets.each do |tweet|
  div.d-flex.align-items-center
    - if tweet.liked_by?(current_user) # ユーザーがいいねしたかどうかで分岐 
      # いいねしている場合以下を表示
      = link_to tweet_likes_path(tweet.id), data: { turbo_method: :delete } do
        i.bi.bi-heart-fill
    - else
      # いいねしていない場合以下を表示
      = link_to tweet_likes_path(tweet.id), data: { turbo_method: :post } do
        i.bi.bi-heart
    span class="#{'liked' if tweet.liked_by?(current_user)}"
      = tweet.likes.count # countメソッドを使っていいねの数を表示

bootstrapのアイコンをlink_toで囲むことでいいねボタンを実装しています。

cssと実際の動作画面はこちらになります。
以下がcssです。

app/assets/stylesheets/application.bootstrap.scss
bi-heart-fill,
.liked {
  color: red;
}

こうすることでいいねしている場合にアイコンといいね数を赤色に表示しています。

動作画面はこのような感じになります。
like.gif

おわりに

最後まで読んでいただきありがとうございました。
少しでも皆さんの参考になれば幸いです。

参考にしたサイト

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?