概要
ツイート投稿機能とツイートへのいいね機能を持つアプリケーションを開発中、
いいねした順番にツイートを取得する方法について調べたのでメモ。
環境
ruby 3.0.2
rails 6.1.4
mysql 8.0.26
やりたいこと
ツイートの作成順がデフォルトの時に、
ツイートの作成順ではなくユーザーがいいねした順にツイートを取得したい。
先に結論
reorderを使用し、中間テーブルのカラムを直接指定する
# ユーザー詳細画面で表示したい場合
def show
@user = User.find(params[:id])
@favorites = @user.favorite_tweets.reorder('favorites.created_at DESC')
end
以下、詳細
関連するモデル
class User < ApplicationRecord
has_many :tweets, dependent: :destroy
has_many :favorites, dependent: :destroy
has_many :favorite_tweets, through: :favorites, source: :tweet
end
class Tweet < ApplicationRecord
belongs_to :user
has_many :favorites, dependent: :destroy
has_many :favorite_tweets, through: :favorites, source: :tweet
# ツイートのデフォルトとしては投稿順に並べたいためここで指定
default_scope -> { order(created_at: :desc) }
end
class Favorite < ApplicationRecord
belongs_to :user
belongs_to :tweet
end
ER図
結論に至るまで
コンソールで確認しつつ試行錯誤。
# userに一人目のユーザーを取得
user = User.find(1)
# favoritesに、userがいいねしたツイートを取得
favorites = user.favorite_tweets
これだと、tweetのデフォルトである、
tweetsテーブルのcreated_atカラムの降順
での取得となってしまう。
(このデフォルトはtweet.rbのdefault_scopeで指定しているもの)
そもそもどんなクエリが発行されているのか?
ActiveRecordで発行されるSQLを確認するのに、to_sqlという便利なメソッドがあるとのこと。
先ほどの中身を確認。
# userに一人目のユーザーを取得
user = User.find(1)
# userがいいねしたツイートを取得するSQLを確認
user.favorite_tweets.to_sql
# 見やすいよう改行
=>
"SELECT `tweets`.*
FROM `tweets`
INNER JOIN `favorites`
ON `tweets`.`id` = `favorites`.`tweet_id`
WHERE `favorites`.`user_id` = 1
ORDER BY `tweets`.`created_at` DESC"
ORDER BY tweets.created_at DESC"
とあるように、tweetsテーブルのcreated_atカラムをDESCで並べている。
指定したいのは
ユーザーがツイートをいいねした順番
つまり、
中間テーブルのcreated_at
なので、
上記ORDER BYの箇所を
ORDER BY favorites.created_at DESC
にできればいいのでは?
defaultを上書きしたい
# userに一人目のユーザーを取得
user = User.find(1)
# userがいいねしたツイートを取得するSQLを確認
>> user.favorite_tweets.order('favorites.created_at DESC').to_sql
# 見やすいよう改行
=>
"SELECT `tweets`.*
FROM `tweets`
INNER JOIN `favorites`
ON `tweets`.`id` = `favorites`.`tweet_id`
WHERE `favorites`.`user_id` = 1
ORDER BY `tweets`.`created_at` DESC, favorites.created_at DESC"
ORDER BY tweets.created_at DESC, favorites.created_at DESC"
.orderでは、デフォルトの後の指定として追加できるだけのよう。
ここで結論
reorderで初期化の上、順番を指定
# userに一人目のユーザーを取得
user = User.find(1)
# userがいいねしたツイートを取得するSQLを確認
>> user.favorite_tweets.reorder('favorites.created_at DESC').to_sql
# 見やすいよう改行
=>
"SELECT `tweets`.*
FROM `tweets`
INNER JOIN `favorites`
ON `tweets`.`id` = `favorites`.`tweet_id`
WHERE `favorites`.`user_id` = 1
ORDER BY tweet_bookmarks.created_at DESC"
無事、意図通りのいいね順で取得することができた。
参考にした記事
- reorder含む、default_scopeの初期化
- 【Railsドキュメント】reorderについて
最後に
より良い方法や間違い等ありましたらご指摘いただけますと幸いです!