【Ruby on Rails】投稿点数ランキング機能（全体表示）

# 目標
- ユーザーの投稿点数ランキング
 - 投稿に対して評価点数を合計/平均して、その投稿ユーザーの投稿点数ランキング
- 投稿の点数ランキング（user部分を投稿に変更したら可能です）
 - 投稿に対して評価点数を合計/平均して、その投稿の点数ランキング


# 開発環境
ruby 2.5.7
Rails 5.2.4.3
OS: macOS Catalina

# 前提
※　▶◯◯　を選択すると、説明等が出てきますので、
　　よくわからない場合の参考にしていただければと思います。

- [deviseでログイン環境構築](https://qiita.com/japwork/items/275110b810965fbdf72f)
- [ログインユーザーのみができる投稿機能](https://qiita.com/japwork/items/8dfb52121036791ac4fb)
- [投稿の編集機能（更新、削除）](https://qiita.com/japwork/items/e258ccbbe4f48ea5a3b5)

※数値がまだないユーザーも表示できるよう、
left_joinsとsort_byを使って、ランキング機能を実装します。
※他のランキング方法もあるため、随時投稿していきます。


# カラム追加
投稿に対するコメントにて、評価をつけるため追加。

```:ターミナル
$ rails g migration AddScoreToComments score:integer
```

```rb:db/migrate/xxxxxxxxxxxxx_add_score_to_comments.rb
class AddScoreToComments < ActiveRecord::Migration[5.2]
  def change
    add_column :comments, :score, :integer, default: 0
  end
end
```
このままでは点数に差が生まれてきてしまうので、
viewで点数を選択式にできるように記述。

```erb:app/views/posts/show.html.erb
評価：<%= f.number_field :score,min:1,max:5 %>
```
```erb:合計値を出す場合
<% @sum = 0 %>
<% @post.comments.each do |comment| %>
  <% @sum += comment.score %>
<% end %>
<%= @sum %>
```
```erb:平均値を出す場合
<% @average = 0 %>
<% @post.comments.each do |comment| %>
  <% @average += (comment.score / @user.comments.count) %>
<% end %>
<%= @average %>
```

準備が出来たので、本題に進みます。

# controller
コントローラーが肝になります。

```rb:合計値のランキング：app/controllers/users_controller.rb
  def rank
    @users = User.
              left_joins(:comments).
              distinct.
              sort_by do |user|
                hoges = user.comments
                if hoges.present?
                  hoges.map(&:score).sum
                else
                  0
                end
              end.
              reverse
  end
```
```rb:平均値のランキング：app/controllers/users_controller.rb
  def rank
    @users = User.
              left_joins(:comments).
              distinct.
              sort_by do |user|
                hoges = user.comments
                if hoges.present?
                  hoges.map(&:score).sum / hoges.size
                else
                  0
                end
              end.
              reverse
  end
```
<details><summary>補足</summary>
・left_joins(:comments)：左外部結合と言って、コメントに紐づくuserを全て取得し、
　コメントがない場合はnullで取得するメソッドです。こちらの解説がわかりやすいです。
　[【Rails】left_joinsメソッドで定義する左外部結合とは？](https://pikawaka.com/rails/left_joins)
・distinct：重複レコードを1つにまとめるためのメソッドであり、
　今回の場合はコメントが2つ以上あると、userも2つ以上取得してしまうため、
　重複を回避するために使用します。
・sort_by do |user|：配列を小さい順に並び替えるメソッドです。
　値としてはif以降の条件で代入していきます。
・map(&:score)：scoreの値だけ、追加していくメソッドです。
　こちらの解説がわかりやすいです。
　[Railsでよく見る arr.map(&:id) の意味](https://qiita.com/s_tatsuki/items/869af9c0c33d9d650f3f)
・reverse：sort_byで小さい順に並び替えたため、逆にしています。
　ただし、同数値の場合、新しい順になるため、注意が必要です。
　ここは解決出来ましたら再度更新します。
・hoges.size:hogesの数を取得しています。その数値を元に平均を出しています。
</details>


# view
下記のようにeach降順順で表示可能
app/views/users/rank.html.erbを作成後、

```erb:app/views/users/rank.html.erb
<% @users.each do |user| %>
  <%= link_to user_path(user) do %>
    <%= user.name %><br>
  <% end %>
<% end %>
```

# routing
```rb:config/routes.rb
get '/rank', to: 'users#rank'
```

# 参考サイト
[Railsでお手軽ランキング機能]
(https://qiita.com/mitsumitsu1128/items/18fa5e49a27e727f00b4)
[【Rails】ランキング機能の実装](https://qiita.com/hitochan/items/258215ec62ead338c2eb)

 
# まとめ
今回は高いスコアを投稿しているユーザーを表示したかったため、
このような記述となりましたが、
あまり実用的ではないかもしれません。
ただ、これらのランキングを使えば、
点数を高く評価している人だけ、または低い人だけに
アプローチを取ることができるため、営業向きのランキングかと思います。

また投稿の点数ランキングは口コミランキングなどを実装したい場合は、
実用的な記述になるかと思います。