今回書くこと
ランキング機能の実装の超簡単なやり方
環境
Ruby 2.6.5
Ruby on Rails 6.0.4.3
結論
こちらの記事を参考にデータの取得方法を工夫することで、結構簡単に実装できる。
##前提
記事にお気に入り機能を実装し、そのお気に入り数を参考してランキング機能を実装します。
テーブルとアソシエーションはこの通りです。
- favoritesテーブル(誰かどの記事にお気に入りしたかを管理=中間テーブル)
- usersテーブル(ユーザーの情報を管理)
- articlesテーブル(記事の情報を管理)
※(reviewsテーブルは今回関係ありません)
記事にお気に入りが出来る(ファボできる)実装が完了していることを前提とします。
なお、お気に入り機能の作り方については、
↓こちらの↓記事が参考になります。
【Rails】お気に入り機能
方針
- ランキングを表示させたいアクション(7つのアクション以外でも可)内で、お気に入り数が多い順に5つレコードを取得する。
- ビューで表示する
それでは早速いきましょう。
1. コントローラーでデータ取得
まずは、お気に入り数が多い順にレコードを5つ取得します。
class ArticlesController < ApplicationController
def index
@articles = Article.published.order(created_at: :desc).includes(:user)
@rank_articles = Article.find(Favorite.group(:article_id).order('count(article_id) desc').limit(5).pluck(:article_id))
end
(以下略)
end
@rank_articlesに、取得したレコードを代入しています。
少し長いので、どのようにデータを取得したのか説明を加えます。
まず、Favotitesテーブルにあるarticle_idカラムのデータの中で、重複しているものを一まとめにしています。一まとめにすると言っても、データの総数は変わりません。
Favorite.group(:article_id)
次に、article_idの数をカウントし、その数が多い順に並べ替えます。
order('count(article_id) desc')
そして、取得するレコード数は最大で5つとします。
limit(5)
最後に、ビューで配列としてデータを扱うために、pluckメソッドで配列の形で上位5つのレコードidを取得します。
pluck(:article_id)
このデータを、ビューで利用します。
2. ビューで表示する。
ここまでで、@rank_articlesに必要な情報を入れることができました。
ここからは、ビューでの表示についてお話しします。
<div class="rank">
<h2>真似したいランキング</h2>
<div class="rank_box">
<% @rank_articles.each.with_index(1) do |article, i|%>
<div class="rank_title">
<%= i %>位 <%= link_to article.title, article_path(article), class: "title_name"%>
</div>
<% end %>
</div>
</div>
4行目で、@rank_articlesに入っているレコードを一つずつ表示できるように処理します。
(今は、ブロック内のiは無視しても大丈夫です。)
<% @rank_articles.each.with_index(1) do |article, i|%>
あとは、思い思いの形で表示してもらえればと思います。
補足1. each.with_index()
では、補足として、すぐ上で取り上げた表現について説明しようと思います。
ざっくりいうと、「何番目だよ!」という情報と共に、eachメソッドの処理が行われるメソッドです。()内の数は、最初の値を示しています。今回は、1位を最小の値にしたかったので、1を引数として指定しました。
そして、この何番目だよ!というデータをeachメソッドの中で利用したいので
- 記事情報のarticle
- 何番目を示すi(indexの頭文字)
を記述して、処理の中で利用しています。
##補足2. コントローラーの処理をモデルに移動する(ちょっと上級者向け)
def index
@articles = Article.published.order(created_at: :desc).includes(:user)
@rank_articles = Article.find(Favorite.group(:article_id).order('count(article_id) desc').limit(5).pluck(:article_id))
end
今回は、データの取得の処理を、コントローラーに記述しました。
しかし、CoCの原則に則れば、データの取得に関する処理は、モデルが担当するべきです。
そこで、モデルにこの処理を記述します。
def self.create_article_ranking
Article.find(Favorite.group(:article_id).order('count(article_id) desc').limit(5).pluck(:article_id))
end
このように、クラスメソッドとしてcreate_article_rankingメソッドを定義しました。
こうすることで、
def index
@articles = Article.published.order(created_at: :desc).includes(:user)
@rank_articles = Article.create_article_ranking
end
モデルで定義したクラスメソッドを呼び出す形で、同様の処理が可能になります。
##最後に
最後まで読んでいただき、ありがとうございます。
ソースコード、記事の書き方について「もっとこうしたほうがいいよ!」というご意見、「そこどうなっているの?」というご質問など、お待ちしております。