並び替え(⭐️評価順も)
さぁ切り替えて記事書いていきますよー
(と、自分用に細くメモった自分のうっすい記事。笑)
で、前回はsayaさんの記事で実装したんですが、、、、
ただ今回私のPFでは
『投稿に直接評価する』のではなく
『投稿に対して行うコメントと共に評価⭐️を送る』
という形にしてるんですですね。えぇ。
ってことでだいぶ記載違って死にかけたんで備忘録を。。
記載を加える場所
1.post model
2.posts controller
3.views/post/index
4.views/post/search
1.post model
#並び替え
scope :latest, -> {order(created_at: :desc)}
scope :old, -> {order(created_at: :asc)}
# 平均評価順で並び替えるスコープ
scope :highest_rated, -> {
left_joins(:comments)
.group(:id)
.order(Arel.sql('COALESCE(AVG(comments.star), 0) DESC'))
}
んー分からんすぎぃ笑
解説
まずscopeの書き方は
scope :スコープ名(任意), -> {条件式}
評価順に関しては長いから数行になってるだけ。
つまりhighest_rated はこのスコープの名前で、-> { ... } はこのスコープの定義です。
scope :highest_rated(名前), -> {highest_ratedの条件式}
left_joins(:comments)とは???
left_joinsメソッドとは左テーブルのレコードすべてと、結合条件にマッチする右テーブルのレコードを返します。(google参照)
つまりは左(post)テーブルと指定した右テーブル(comment)をくっつけてくれるメソッドと覚える
⭐️これはアソシエーションしてないと実装不可!!!
⭐️引数には、アソシエーションで定義した関連名を渡します。
今回はモデルにてhas_many :commentsって書いてるので( )の中は(comments)って事!!!
.group(:id)は何のために書く?
簡単に言うと集計関数(今回はAVG)の使用に使うから!
AVG(平均値)などの集計関数を使用する場合、その関数がどの範囲で計算されるべきかを明確にするためにグループ化が必要です。
今回は各投稿(post_id)ごとに平均求めるからPostテーブルの中でもidカラムでグループ化!
これは、同じ post_id に関連するコメントの評価をまとめて、その平均を計算するためのもの。
なっるほっど💆♀️
.order(Arel.sql('COALESCE(AVG(comments.star), 0) DESC'))
COALESCEは、与えられた引数の中で最初にNULLではない値を返す関数。
つまり、AVG(comments.star)がNULLの場合に、代わりに0を返すようにします。これにより、ソート中にNULLが問題になることを防ぎます。
AVG(able名.カラム名)で平均値を計算します。
DESC は降順を意味し、星評価の平均値が高い投稿から順に並び替えます。
Arel.sql('SQL文')がよくわかんない
Active RecordやArelのメソッドチェーンだけでは表現しきれない複雑なSQLを実行したい場合に、Arel.sqlを使って純粋なSQL文を挿入します。
出そうで、、んーまぁ柔軟に任意の式に対応できるって事?かな???(sql)
2.posts controller
def index
if params[:latest]
@posts = Post.latest.includes(:comments)
elsif params[:old]
@posts = Post.old.includes(:comments)
elsif params[:star_count]
@posts = Post.highest_rated.includes(:comments)
else
@posts = Post.includes(:comments)
end
end
本当は.page(params[:page]).per(12)が全コードの後ろにくっついてたんですけど省略
params[: ]は[: ]が入力された場合は〜〜〜(1個下のコード)ってこと。
星評価が[:star_count]なのはviewでその名前にしてるからってだけ。(任意)
書き方は全部『model名.scope名.includes(:comments)』
.includes(:comments)は今回みたいに評価がコメントに入ってる場合に記載。投稿自体に評価ついてたらいらない。
3.views/post/index
<%= link_to '新しい順', books_path(latest: "true") %>
|<%= link_to '古い順', books_path(old: "true") %>
|<%= link_to '評価の高い順', books_path(star_count: "true") %>
これで実装できた!!!
レイアウトでドロップダウンメニューに変更🕺
<div class="dropdown">
<button class="btn btn-outline-dark dropdown-toggle" type="button" id="sortDropdown" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
</button>
<div class="dropdown-menu" aria-labelledby="sortDropdown">
<%= link_to '新しい順', posts_path(latest: true) , class: 'dropdown-item' %>
<%= link_to '古い順', posts_path(old: true), class: 'dropdown-item' %>
<%= link_to '評価の高い順', posts_path(star_count: true), class: 'dropdown-item' %>
</div>
</div>
ここはこういうものって覚えよう。って事でAIに教えてもらいました。
Discussion