初めに
ポートフォリオテーマとして業者のレビューサイトを作成しています。
以下の画像のようなレビューサイトを作っていこうと思います。
※自分用でまとめてますので、分かり辛かったらすいません。
また理解不足・誤りがあったらコメント頂けますと幸いです。m(__)m<ヨロシクオネガイシマス
前提条件
①今回の記事ではランキング機能のみ(※同率順位を踏まえた順位)についてまとめます
②レビュー機能や並び替え機能については、以下URLの記事に記載されている内容を参考にしてください
[rails初心者]複数権限でのログインを使ったレビューサイトの作成(基本)
[rails初心者]複数権限でのログインを使ったレビューサイトの作成(応用)
今回作成するランキング機能について
・同率順位がある場合、同じ順位として表示すること
・すべての業者に対してランキング機能を実装する(ページネーションの移動後でもその順位は継承されていること)
主なやること
・コントローラの編集(配列の付け足し)
・モデルの編集(平均値算出処理の編集)
・ビューページの編集(配列が付け足されたことによる修正作業)
この3点です
開発環境
・rails 6.1.7・ruby 3.1.2
・raty 3.1.1
・jquery 3.6.1
・bootstrap 4.5
コントローラの記述
配列の付け足し
今回はmapメソッドを使って次のようなメソッドを作っていきます
def アクション名
same_rate = 0
rank = 1
traders = Trader.find(Review.group(:trader_id).order('avg(star) desc').pluck(:trader_id)).map.with_index(1) do |trader, i|
if i == 1 #ランキング処理の一番最初だけ以下の処理を行う
same_rate = trader.review_average #一番最初だけ「same_rate」に値が入っていないので、先に値を入れる
end
if trader.review_average != same_rate #同率順位がない場合以下の処理
rank = i #同率が無いので、順位を更新
same_rate = trader.review_average #「same_rate」に現在の値を代入 → 次の値と比べる用として運用するイメージ
{trader: trader, index: rank } #[trader = trader]という配列と[index = rank]という配列それぞれをセットにしている
else #同率順位がある場合以下の処理
same_rate = trader.review_average
{trader: trader, index: rank }
end
end
@traders = Kaminari.paginate_array(traders).page(params[:page]).per(5)
#Kaminari.paginate_array → pageメソッドを配列に対して用いる場合に必要な記述
解説1
まずはsame_rate = 0
, rank = 1
の部分についてです。
使う意味合いとしては次の通りです。
ポイント①同率順位を考慮する=same_rate
を使って同率かどうかを調べる
ポイント②順位の表示=rank
を使って順位を表示させる
そしてこの行でやっていることは次の通りです。
same_rate = 0
, rank = 1
の部分では、これらの変数の初期値を最初に決めている感じです。
解説2
次にtraders =Trader.find( 省略 ).map.with_index(1) do |trader, i|
の部分です。
使う意味合いとしては次の通りです。
ポイント①もともとある配列(Trader.find( 省略 )
)に別の配列を付け足す(.map
)
ポイント②新しい方の配列は1からスタートさせたい(.with_index(1)
)
ポイント③変数は2種類(|trader, i|
)
補足説明
実はmap
メソッドは今回が初めてでした (´▽`)テヘペロ
ということで簡単にまとめると
map
メソッド == each
+処理を実行 だと思いました( ..)φメモメモ
※詳しくは参考サイトのリンクを参照してくださいm(__)mオネガイシヤス
配列変数.map {|変数名| 具体的な処理 }
今回のmap
メソッドさんのシナリオ(やって欲しい挙動)は次の通りです。
もともとある配列(Trader.find( 省略 )
)に別の配列(.with_index(1)
)を付け足す(.map
)。
その際使う変数は2種類(|trader, i|
)とする。
また新しく作る配列(.with_index(1)
)には書き方が二種類あるらしいです。
今回は「1」からスタートしたかったので、.with_index(1)
を選びました。
ここまでの情報をまとめてみるとこんなイメージです。字が汚いのはすいません(;^ω^)
以下が参考記事のリンクです。
【Rails入門】mapメソッドを完全攻略!配列操作の基礎を学ぼう
【Ruby】インデックス有りのループ処理を行う場合、each_with_indexよりもwith_indexの方が便利だった
解説3
続いては if の条件部分trader.review_average != same_rate
です。
使う意味合いとしては次の通りです。
ポイント①同率順位がいるかどうかを調べている
ポイント②trader.review_average
には今回のチェック対象の平均値が入っている
ポイント③same_rate
には前回チェック時に対象が持っていた平均値が入っている
つまりこんな感じです。
「前回の平均値 == 今回の平均値」
または
「前回の平均値 != 今回の平均値」
どっちでもOK(処理の中身が逆になるだけなので)
解説4
続いてsame_rate = trader.review_average
と{trader: trader, index: rank }
です。
これらの意味合いとしては次の通りです。
ポイント①same_rate
の更新
ポイント②|trader, i|
に入る値を決めている
ポイント①について・・・
先程の解説3の時に同率順位かどうかをチェックしたと思います。
このチェックでは「一つ前の順位と同じか?」という見えない条件があり、
それをクリアするためには随時更新作業が必要になります。
なのでsame_rate = trader.review_average
処理が記述されているわけです。
また{trader: trader, index: rank }
については、次の画像のように「配列1と配列2」それぞれにどの値を代入するかを決めているだけです。
モデルの編集(平均値算出処理の編集)
平均値算出処理の編集
まず前回紹介した平均値算出用に作ったメソッドがこちらです
##評価点の平均値
def review_average(trader)
if trader.reviews.count >= 1 #顧客(client)に評価されたことがあるか否かを確認
reviews=Review.where(trader_id: trader.id).pluck(:star)#starカラムに保存されている評価点数を取得
#reviews.map!(&:to_i)#取得した変数(reviews)が文字列の場合、数列型へ変更しましょう
reviews.sum.fdiv(reviews.length).round(1)#取得した変数をもとに平均値を算出
#round(1)とすることで小数点第2位に対して四捨五入をします。
else
0.0 #顧客(client)に評価されたことが無い場合、平均値を0.0に設定
end
end
これを次のように書き替えます。
##評価点の平均値
def review_average
if reviews.count >= 1
reviews=Review.where(trader_id: id).pluck(:star)#starカラムに保存されている評価点数(文字列型)を取得
reviews.map!(&:to_i)#取得した変数(reviews)を数列型へ変更する
reviews.sum.fdiv(reviews.length).round(1)#取得した変数をもとに平均値を算出
#round(1)とすることで小数点第2位に対して四捨五入をします。
else
0.0
end
end
主な変更点
・メソッド名の横にある引数の(trader)
を排除
・引数が無くなったので、レシーバ( . の左の文字)はself
になる
・ただself
はレシーバである場合、省略可能
やることは以上になります。
ビューページの編集
配列が付け足されたことによる修正作業
結論からいうと、次のような記述になります。
<% @traders.each do |trader| %>
<div>第<br><%= trader[:index] %><br>位</div>
<% if trader[:trader].profile_image.attached? %>
<%= image_tag(trader[:trader].get_profile_image(100,100)) %>
<% else %>
<%= image_tag 'leader_sagyouin_woman.jpg', size: "100x100" %>
<% end %>
<%= trader[:trader].name %>
<%= trader[:trader].email %>
<%= trader[:trader].telephone_number %>
<%= trader[:trader].genre.name %>
<div>評価平均:<%= trader[:trader].review_average %></div>
<% end %>
<%= paginate @traders %>
主な変更点
・変数(trader
)の隣に配列([:index]
or [:trader]
)を入れる
なぜ配列を記述しないといけないのか?
まずeach文で使っている @tradersには画像のtraders(ローカル変数)
と同じポジションになります。
※コントローラ内で代入しているため
それを踏まえて次の画像を見てみましょう。
tradersという 大枠の中に配列①と配列②の 中枠が二つあります。
この中枠が2種類存在することで、どちらの枠(配列)なのかを宣言しないといけないからです
以上で、やることは終わりました。
bootstrapの書き方などを整えると次の様なサイトが出来上がりました。
ページネーションで飛んだとしても順位は引き継がれているので、ランキング機能の実装が完了しました。
さいごに
今回PF作成のテーマとしてレビューサイト(+ランキング機能)を作ってみました。
まだ学習中の身なので理解不足・誤りがあったらコメント頂けますと幸いです。m(__)m<ヨロシクオネガイシマス