コンビニで気軽に採れる高タンパク質を管理するサイトを作成時
ユーザーが口コミ投稿できる機能を作成、星による評価機能があるといいなと思ったので実装。
似たような記事は他にもあるが、slimで書かれている記事がなかったので執筆。
対象者、環境
・Railsで「Raty」を使って星の機能を作成したい人
・slimを使用している
・Rails 5.2.4
ゴール
・星の入力(0.5単位)
・星の表示
・平均点算出
参考URL
イメージ
前提
・Userモデル
・Foodモデル
・Reviewモデル
があることを前提にすすめます。テーブル相関図
今回は、「rate」以外が全てあると思ってください
FoodやReviewはご自身のモデルとあてはめてくださいm(_ _)m
入力編 formで表示しよう
カラム追加
be rails g migration AddRateToReview rate:float
class AddRateToReview < ActiveRecord::Migration[5.2]
def change
add_column :reviews, :rate, :float, null: false, default: 0
# floatにすることを推奨(小数点に対応できるため)。nullの制約は各自判断してください
end
end
モデル側は下記。1以上~5以下の数字はお好みで。
validates :rate, numericality: {
less_than_or_equal_to: 5,
greater_than_or_equal_to: 1
}, presence: true
画像を外部から保存する
こちらのGithubから画像をassets/images
配下に保存します。
jsのコードをコピーする
こちらのGithubからコードをコピーして、jquery_raty.js
等の適宜名前を振り、
applicaiton.js
から require
してあげる
viewに追加
ストロングパラメータに追加等した後、(i18n化もしてます)
#star.form-group
= f.label :rate
= f.hidden_field :rate, id: :review_star
#star がポイントです(jsを呼ぶ)
同じviewファイル内に、
javascript:
$('#star').raty({
size: 36,
starOff: "#{asset_path('star-off.png')}",
starOn: "#{asset_path('star-on.png')}",
starHalf: "#{asset_path('star-half.png')}",
scoreName: 'review[rate]', # reviewカラムに保存するので忘れないように
half: true, # ★の半分の入力を行う
});
# ダブルクオーテーションで囲みましょう
ここまで入力すれば、下記のように表示がされているはず。実際に保存できているか、DBを確認するのも忘れずに!
もし表示出来ていなければ、デバックして確認しましょう。
表示編 eachで回そう
次は、DBに保存された数字をviewに描写します。
_review.html.slimで回す
eachで回すことを前提に話を進行します。
id="star-rate-#{review.id}"
の様に書くことで、idを動的に出来ます。
表示編 平均点を出そう
最後におまけで、reviewの平均点を出したいと思います。
reviewは、foodモデルに紐付いており、食品がレビューを複数持つという関係性です。(詳しくは最初の参考資料を御覧ください)
紐付いたものをaverage等でいい感じにしてあげると,こんな感じで表示ができるはず!
.col-md-3.col-sm-4.col-xs-12.text-center#food_parts
food[id="#{food.id}"]
= link_to image_tag(food.decorate.image_url, width: 220, height: 220, class: 'food_parts_image'),food_path(food)
.card-body
h5
= link_to food.name, food_path(food)
ul.text-left
li 税込#{food.price}円
li #{food.protein}g
li #{food.brand.pluck(:name).first}
li id="star-rate-#{food.id}" 口コミ#{food.reviews.count}件
//注目ポイント ↑
== render 'likes/likes_basic', food: food #いいね機能です、無視してください
//星評価
javascript:
$('#star-rate-#{food.id}').raty({
size: 36,
starOff: "#{asset_path('star-off.png')}",
starOn: "#{asset_path('star-on.png')}",
starHalf: "#{asset_path('star-half.png')}",
half: true,
readOnly: true,
score: "#{food.reviews.average(:rate).to_f.round(1)}",
//注目ポイント↑ 平均点を算出し、round関数で切り上げ
});
//星評価終わり
こんな感じで表示ができるはず!(ちなみに、おすすめのサラダチキンはファミマです)
(画像の星では、口コミ2件で 「4」と「2」の評価がついています。
汚くなったコードはパーシャルで
現状のviewはjsがべた書きになっていると思います。
shared/_star
などを作って、jsだけパーシャルにしましょう!レッツリファクタ!😉
/ 星評価 review_formからrender(newとeditで使用中)
javascript:
$('#star').raty({
size: 36,
starOff: "#{asset_path('star-off.png')}",
starOn: "#{asset_path('star-on.png')}",
starHalf: "#{asset_path('star-half.png')}",
scoreName: 'review[rate]',
half: true,
});
/ 星平均 _foodからrender
- if food.present? #自分の仕様上必要なif文
javascript:
$('#star-rate-#{food.id}').raty({
size: 36,
starOff: "#{asset_path('star-off.png')}",
starOn: "#{asset_path('star-on.png')}",
starHalf: "#{asset_path('star-half.png')}",
half: true,
readOnly: true,
score: "#{food.reviews.average(:rate).to_f.round(1)}",
// 平均点を算出し、round関数で切り上げ
});
これでだいぶすっきりしたと思います。
まとめ
実装初期、jsファイルに必死に'#{asset_path('star-off.png)}',
のようにasset_pathを書いていましたが、呼び込まれずハマりました。
冷静に考えると、jsとRubyで生成されるタイミングが違うので、jsファイルにasset_pathを書いても動きませんよね(という認識です)。gon
というgemを使えばなんとかなる、、らしいのですが、試してはいません。
gonを使ったRailsとJavascriptの連携について
星機能を実装するには、ratyやrate.yoなどがありますが、分かれば比較的簡単なratyはおすすめです。
星、テンション上がりますね😊