ランキング機能
私の勝手な見解ですが、ランキング機能は、人々の目をサービスに惹かせるにはとてもいい機能だと思います。
自分の投稿したものが今何位なのかというのは、商品を作成する側からすると非常に気になるものです。
今ではありふれている機能ですが、しっかり戦略があるんだろうなあ。。という薄っぺらい意見から、機能実装入りますw
今回は、特定の食べ物(=food)が、いいねの数によってランキングされるという機能です。
ランキング機能実装
MVCから入ります。
model
has_many: likes
#like.rb
belongs_to: food
view
#ランキング結果を全て出したい時
<%=@food_ranking.all%>
#1位だけを出したい時
<%=@food_ranking.first%>
ここちょっと悩みどころなんですけど、開発途中だとランキング1位がnilの場合が多いから、強制的にエラーになります。
すでにランキングが埋められている状態で開発するか、nilの場合とそうでない場合をif文で切り分けるか好きなようにすれば良いかと。。
ちなみに自分は、if文で切り分けています。正式にリリースとかするのであれば切り分けた方がいいかもです。。。
この例では入れてないですが。。
controller
いよいよネックのコントローラーです
food_like_count = Food.joins(:likes).group(:food_id).count
food_liked_ids = Hash[food_like_count.sort_by{ |_, v| -v }].keys
@food_ranking= Food.where(id: food_liked_ids)
正直ここは初心者からしたら、かなりの難題でした。。。。
一個一個見ていきます
1行目 food_like_count = Food.joins(:likes).group(:food_id).count
・joins
内部結合を行うメソッドです。内部結合って何やねんって話なんですが、図を見るとわかりやすいです。
今、foodテーブルとlikeテーブルの状態が上記のようになっているとします。
右のlikeテーブルは、ある特定のユーザーが、特定のfoodをいいねしている状態を表しています。すなわちいいね機能です。一番上のレコードは3というuserが1であるvegetableをいいねしていることになります。
※わかりづらいという方は、ここについて詳しく書いている記事がありますので、お読みください。=>いいね機能の実装と仕組みhttps://qiita.com/tomoharutsutsumi/items/4b045fa12f29502f4b4b
ではこれを内部結合すると。。。
このようになります。ちょっと特徴的なのはlikeテーブルで存在していないfood_id(この場合は3のfishです)は削除されています。
・group
これは読んで字のごとく、グループ化します。さらに詳しくいうと特定のカラムを基準としグループ化します。
今回は.group(:food_id)となっています。
上の内部結合の例で言えば、food_idが同じものをまとめるという動作になります。food_idが1であるものが2つあるので、これらはまとめられます。
・count
これは有名な数えるメソッドです。上のgroupでまとめたものの中身が何個あるかを数えます。
上の表であれば、food_id1は2、2は1になります。
ちなみにこの1行目を動作させると、答えはハッシュになります。
※ハッシュとはなんぞやの記事=>ハッシュと配列の違いhttps://qiita.com/tomoharutsutsumi/items/2dd5dd4b6edb17244cd6
{1=>2, 2=>1}という感じで返ってきます。
2行目 food_liked_ids = Hash[food_like_count.sort_by{ |_, v| -v }].keys
・Hash
ざっくりいうと、これは配列になっているものをハッシュに変換できるものです。
・sort_by{ |_, v| -v }
まずはsort_byですが{}内にある条件で、要素の順番を並べ替えなさいというメソッドです。今回でいう要素とはfood_rankの中にある値のことです。(ex.{1=>2, 2=>1})
では条件とは何かなんですが。。。これは「value(1=>2で言えば2の部分)が大きい順で」という意味です。
そうすれば、いいねをしている人数が増えることになるので、結果ランキング上位の並び替えとなります。
・keys
最後にこの部分ですが、これはハッシュのキーの部分(1=>2で言えば1の部分)を取り出します。
これで、いいねしているユーザーが多い順にfoodのidが取れます。
3行目 Food.where(id: food_liked_ids)
ランキングにされたidと同じidを持っているfoodを見つけます。
これで、ランキングの完成です。
補足
ちなみに以下の記事と合わせて読んでいただければ、ユーザーがいいねできるようになり、かつそのいいね数によってランキングができるようになる、というそれっぽいことが実現できます。ぜひ!
いいね機能の実装と仕組みhttps://qiita.com/tomoharutsutsumi/items/4b045fa12f29502f4b4b