LoginSignup
5
2

More than 1 year has passed since last update.

【Rails6】5段階評価の星レビュー機能を実装する手順(raty.js)

Last updated at Posted at 2022-12-17

概要

  • ユーザーが任意のitemに対して5段階評価機能の星評価(非同期通信)ができるように実装したので、その手順について記す。
    • Userは、itemの詳細ページ(/show)にいくと、5段階評価の☆評価(0.5単位)とコメントを行い、レビューを送信する(/form)。
    • トップ画面(/index)にitemが一覧化されており、itemごとに☆評価の内の総合評価の平均点(関数で切り上げ)が表示される。
    • itemの詳細ページ(/show)にも、総合評価の平均点(関数で切り上げ)が表示される。

前提

データベース設計

image.png

星評価ができるraty.jsについて

jQueryとRatyの導入

  • yarn add jquery
    • Rails5以前の導入方法ではjquery-railsというGemをインストールするのが基本のよう
    • 今回はWebpackerで管理するのでyarnコマンドを使用してインストール。
    • Rails6では、webpackで複数のjsコンパイルして出力する。JavaScriptの標準バンドラーは Webpackerになっている。
  • webpackerの設定ファイルで、jQueryを管理下に追加するための追記を行うため、以下を追記
config/webpack/environment.js
const webpack = require('webpack')
environment.plugins.prepend('Provide',
  new webpack.ProvidePlugin({
    $: 'jquery/src/jquery',
    jQuery: 'jquery/src/jquery'
  })
)
  • なお、jQueryでは、$: ‘jquery’jQuery: ‘jquery’どちらの書き方もできるため、どちらも書いている。 WordPress では後者らしい。

  • application.jsに以下を追記。

    • このあと作成する外部ファイルraty.jsについても記載しておく
app/javascript/packs/application.js
require("jquery")  
###
window.$ = window.jQuery = require('jquery');
require('packs/raty')
  • turbolinksについて

    • // require("turbolinks").start()でturbolinksは無効化しておいた方がいいかもしれない。turbolinksとはRails4から正式導入された画面遷移を高速化させるgemライブラリ。js, cssの読み込みを初回時に行い、次回以降の読み込み処理を省略することで高速化する役割があるが、Javascriptのイベント発生のタイミングが違ってくることがあるため、無効化しておくのが良いかと。ただ、メリットとデメリットがあるし、部分的な無効化もできるらしく完全な悪者ではないので、各自で調べてみてください。要は、ページが読み込まれた時に発火するloadイベント(JavaScript)と、turbolinksの画面遷移時に高速化する機能がぶつかってしまうことがある、ということですね。
  • app/javascript/packs にjsファイルraty.jsを新規作成。

  • 上記記載のraty.jsのGithubソースからraty.jsを探し、中身にコピペする。(定期的に更新?されているみたいです)

Ratyの星評価画像の準備

  • 上記記載のraty.jsGithubソースにあるimagesフォルダの画像をapp/assets/imagesに保存。今回の場合は、以下の画像を保存。
    • app/assets/images/cancel-off.png
    • app/assets/images/cancel-on.png
    • app/assets/images/star-half.png
    • app/assets/images/star-off.png
    • app/assets/images/star-on.png

モデル側を設定する

  • migrateでratingカラムたちを追加。floatにしているのは、トップビューと詳細ページで平均点を出す際に、小数点にする必要があるため。
# frozen_string_literal: true

class CreateReviews < ActiveRecord::Migration[6.0]
  def change
    create_table :reviews do |t|
      t.text        :comment, null: false
      t.float       :all_rating, null: false, default: 0
      t.float       :rating1, null: false, default: 0
      t.float       :rating2, null: false, default: 0
      t.float       :rating3, null: false, default: 0
      t.float       :rating4, null: false, default: 0
      t.references  :user, null: false, foreign_key: true
      t.references  :item, null: false, foreign_key: true
      t.timestamps
    end
  end
end
  • reviewモデルにvalidation追記。ここで星評価の1~5を設定できる。
app/models/review.rb
  validates :all_rating, numericality: {
    less_than_or_equal_to: 5,
    greater_than_or_equal_to: 1}, presence: true

  • reviewsコントローラーのストロングパラメーターへratingカラムたちを追加する。
app/controllers/reviews_controller.rb
  def review_params
    params.require(:review).permit(:comment, :all_rating, :rating1, :rating2, :rating3, :rating4).merge(
      user_id: current_user.id, item_id: params[:item_id]
    )
  end

送信フォーム(/form)と詳細ページ(/show)のViewを編集する

  • 5つの評価項目と数が多いので、部分テンプレートpartial(form_rateとshow_rate)をつかって管理する。リファクタリング!

  • _form_rate.html.erbにjquery発火イベントを追記する。reviewカラムに保存するので、review[all_rating]とする。

    • 0.5単位なのでhalf: trueとしている
app/views/items/_form_rate.html.erb
<%# jquery発火イベントを記載%>
  <script>
    $('#star').raty({
      size     : 36,
      starOff:  '<%= asset_path('star-off.png') %>',
      starOn : '<%= asset_path('star-on.png') %>',
      starHalf: '<%= asset_path('star-half.png') %>',
      scoreName: 'review[all_rating]',
      half: true,
    });
  </script>
<%# 同様に、rating1なども記載する%>
  • _show_rate.html.erbにもjquery発火イベントを追記する。
app/views/items/_show_rate.html.erb
<div class="starrate">
<%# 平均点を算出し、round関数で切り上げ %>
<%=@all_rating %><%= @reviews.average(:all_rating).to_f.round(1) %>
<%# 評価数を星に置き換える %>
<div class="average-review-rating" data-score=<%= @reviews.average(:all_rating) %>></div>
<%# jquery発火イベントを記載%>
<script>
  $('.average-review-rating').raty({
    readOnly: true,
    starOn: "<%= asset_path('star-on.png') %>",
    starOff: "<%= asset_path('star-off.png') %>",
    starHalf: "<%= asset_path('star-half.png') %>",
    score: function() {
      return $(this).attr('data-score')
    }
  });
</script>
<br>
<%# 同様に、rating1なども記載する%>
</div>
  • _form.html.erbに以下を追加する。これで、上でつくった部分テンプレートのスクリプトを呼び出せる
app/views/items/_form.html.erb
<%# 平均点を算出し、round関数で切り上げ %>
<%=@all_rating %><%= @reviews.average(:all_rating).to_f.round(1) %>
<%# 評価数を星に置き換える %>
 <div class="average-review-rating" data-score=<%= @reviews.average(:all_rating) %>></div> 

<th class="table__col1">総合評価</th>
 <td class="table__col1">  <div class="field" id="star"><% f.label :all_rating %></div></td>
</tr>
<%# 同様に、rating1なども記載する%>

<%= render partial: "items/form_rate", locals: { item: @item } %>
  • show.html.erbに以下を追加する。これで、上でつくった部分テンプレートのスクリプトを呼び出せる
app/views/items/show.html.erb
<%= render partial: "items/show_rate", locals: { item: @item } %>
  • メモ:エラーActionView::Template::Error (Missing partial...について
    • show.html.erbにて、"show_rate"から"items/show_rate"にしたことで解決。reviewsコントローラーのcreate発動によってreviewsフォルダの中にありませんよーという意味になっていたのか?詳細は不明。

トップビュー(/index)に平均点を表示する

  • items_controller.rbに以下を追加する。
app/controllers/items_controller.rb
def index
    @reviews = Review.all
    @all_rating = '総合評価'
    @rating1 = '評価1'
    @rating2 = '評価2'
    @rating3 = '評価3'
    @rating4 = '評価4'
end
  • _item.html.erb(部分テンプレートにしていない場合は、items/index.html.erb)に以下を追加する。
  • 平均点はround関数で切り上げる
  • トップビューに表示するのは、all_rating(総合評価)のみ
app/views/items/_item.html.erb
<div class="average-review-rating" data-score=<%= item.reviews.average(:all_rating) %>></div>
 <%=@all_rating %><%= item.reviews.average(:all_rating).to_f.round(1) %>
</div>
  • 平均点の表示について、ちょっと躓いたので、一応メモ。
     ⭕️ <%= item.reviews.average(:all_rating).to_f.round(1) %> → itemに紐づくreviewsを一括表示させる
     ❌ <%= @reviews.average(:all_rating) %> → reviewsの全平均がどのitemにも同じように表示されてしまうのでNG
     ❌ <%= @item.reviews.count %> → reviewの個数が表示されてしまうのでNG
5
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2