[未経験エンジニア]のオリジナルアプリ制作の反省6. ランキング機能の実装（簡単）

# 今回書くこと
ランキング機能の実装の超簡単なやり方

## 環境
Ruby 2.6.5
Ruby on Rails 6.0.4.3

## 結論
[こちらの記事](https://qiita.com/mitsumitsu1128/items/18fa5e49a27e727f00b4)を参考にデータの取得方法を工夫することで、結構簡単に実装できる。


##前提
記事にお気に入り機能を実装し、そのお気に入り数を参考してランキング機能を実装します。

テーブルとアソシエーションはこの通りです。

- **favoritesテーブル（誰かどの記事にお気に入りしたかを管理=中間テーブル）**
- **usersテーブル(ユーザーの情報を管理)**
- **articlesテーブル（記事の情報を管理）**

![the_brewers.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/948673/2fe78107-d13c-fc66-56f3-b6cc7224a3a4.png)

※（reviewsテーブルは今回関係ありません）


記事にお気に入りが出来る（ファボできる）実装が完了していることを前提とします。

なお、お気に入り機能の作り方については、
↓こちらの↓記事が参考になります。
[【Rails】お気に入り機能](https://qiita.com/manbolila/items/43a04e8d0d5018cf7f62)


## 方針
1. ランキングを表示させたいアクション（7つのアクション以外でも可）内で、お気に入り数が多い順に5つレコードを取得する。
2. ビューで表示する

それでは早速いきましょう。

## 1. コントローラーでデータ取得
まずは、お気に入り数が多い順にレコードを5つ取得します。

```articles_controller.rb
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に必要な情報を入れることができました。

ここからは、ビューでの表示についてお話しします。

```index.html.erb
<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を引数として指定しました。

そして、この何番目だよ！というデータをeachメソッドの中で利用したいので
- 記事情報のarticle
- 何番目を示すi(indexの頭文字)

を記述して、処理の中で利用しています。

##補足2. コントローラーの処理をモデルに移動する（ちょっと上級者向け）

```articles_controller.rb
 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の原則に則れば、データの取得に関する処理は、モデルが担当するべきです。

そこで、モデルにこの処理を記述します。

```article.rb
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メソッドを定義しました。
こうすることで、

```article_controlle.rb
  def index
    @articles = Article.published.order(created_at: :desc).includes(:user)
    @rank_articles = Article.create_article_ranking
  end
```
モデルで定義したクラスメソッドを呼び出す形で、同様の処理が可能になります。

##最後に
最後まで読んでいただき、ありがとうございます。
ソースコード、記事の書き方について「もっとこうしたほうがいいよ！」というご意見、「そこどうなっているの？」というご質問など、お待ちしております。


## 参考文献
- [Railsでお手軽ランキング機能](https://qiita.com/mitsumitsu1128/items/18fa5e49a27e727f00b4)
- [【Rails】お気に入り機能](https://qiita.com/manbolila/items/43a04e8d0d5018cf7f62)
- [【Rails】groupメソッドの使い方を図解形式で仕組みを徹底解説！](https://pikawaka.com/rails/group)