【Rails】raty.jsを用いた星5レビュー機能の実装

## はじめに
プログラミング学習を始めて２ヶ月目になります。
学習したことを自分なりに解釈理解し、アウトプットできればと思いQiitaを始めてみました。


## 今回実装していく流れ

- 事前準備（jQuery、raty.jsの導入）
- 投稿フォームに星レビュー機能の追加
- 投稿の詳細画面での星レビュー表示
- 投稿の一覧画面での星レビュー表示

## 開発環境
- ruby  2.6.3p62
- Rails 6.1.4

## 前提モデル
![Bookers2のER図before.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2581621/8ee9b573-c28b-8982-34a1-2e1d35adea87.png)


# 〜事前準備〜

## 1. jQueryを導入
rails6では、yarnでjqueryをインストールしてください。
```:ターミナル
yarn add jquery
```
次に、webpackerの設定ファイルで、jQueryを管理下に追加するための記述を追加します。

```ruby:config/webpack/environment.js
const { environment } = require('@rails/webpacker')
#追記
const webpack = require('webpack')

environment.plugins.prepend('Provide',
  new webpack.ProvidePlugin({
    $: 'jquery/src/jquery',
    jQuery: 'jquery/src/jquery',
    jquery: 'jquery/src/jquery',
  })
)
#ここまで
module.exports = environment
```
最後に、jQueryを呼び出します。

```ruby:app/javascript/packs/application.js
省略

#追記
window.$ = window.jQuery = require('jquery');

```

## 2.　raty.jsの導入

##### 星レビューするときの、星画像をこちらのgithubでダウンロードします。
　https://github.com/wbotelhos/raty/tree/master/lib/images

今回使う画像は
`star-on.png`
`star-off.png`
になります。
ダウンロードした画像は、
assets/imagesフォルダの中へ格納します。

##### raty.jsファイルの作成
`app/javascript/packs` に　`raty.js`を作成。

次に下記URLのjsコードを先ほど作成した`raty.js`に貼り付けます。
https://github.com/wbotelhos/raty/blob/master/lib/jquery.raty.js

![スクリーンショット 2022-03-09 16.43.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2581621/4fbb1ed0-2df4-fc87-b91e-8da29529e95c.png)

以上で　raty.jsの導入は完了です。


# 投稿フォームに星レビュー機能の追加
## 1.　"star"カラムの追加
ターミナルにて、カラム追加のコマンドを実行。
```
rails g migration AddStarToBooks star:string
```
これでbooksテーブルにstring型のstarカラムの追加用マイグレーションファイルが作成されます。

```
rails db:migrate
```
次に作成したマイグレーションファイルをマイグレートしていきます。
</br>
カラムを追加したER図
![Bookers2のER図after.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2581621/139cb6bf-6324-36cb-bbff-c42344e8aa51.png)

## 2."star"カラムを保存できるようにデータ操作を許可する。


```diff_ruby:controllers/books_controller.rb
.
.
.
def create
    @book = Book.new(book_params)
    @book.user_id = current_user.id
    if @book.save
      redirect_to book_path(@book), notice: "You have created book successfully."
    else
      @books = Book.all
      render 'index'
    end
  end
.
.
.

private

  def book_params
+  params.require(:book).permit(:title, :body, :star)
  end
.
.
.
end

```
booksコントローラーのストロングパラメーターへstarカラムを追記します。
permitメソッドへ追記することで'star'が保存できるようになりました。
　次は、保存はできるようになっていますが、星レビューの投稿ができないので
投稿フォームviewへ星レビューする場所を作っていきます。


## 3.投稿フォームのviewへ星レビュー機能のjavascriptを埋め込み
```diff_html:views/books/_form.html.erb
<%= form_with model:book,local:true do |f| %>
  <div class="form-group">
    <%= f.label :title %>
    <%= f.text_field :title, class: 'form-control book_title' %>
  </div>
  <div class="form-group">
    <%= f.label :opinion %>
    <%= f.text_area :body, class: 'form-control book_body' %>
  </div>
  <div class="form-group">
    <%= f.submit class: 'btn btn-success' %>
  </div>

ーーーーーーーーーーーーーーここからが星レビュー機能ーーーーーーーーーーーーーーーー
  <div class="form-group row" id="star">
    <%= f.label :star,'評価 ', class:'col-md-3 col-form-label' %>
    <%= f.hidden_field :star, id: :review_star %>
　　　　</div>
  <script>
　　　　$('#star').empty();
  $('#star').raty({
    size     : 36,
    starOff:  '<%= asset_path('star-off.png') %>',
    starOn : '<%= asset_path('star-on.png') %>',
 　 　　　scoreName: 'book[star]',　
    half: false,
  });
  </script>
ーーーーーーーーーーーーーーここまでが星レビュー機能ーーーーーーーーーーーーーーーー
<% end %>
```
```point
$('#star').empty();　　　　　　　　　　　　　#これがないとブラウザバックで星が増え続ける不具合が発生します。詳しくは参考にした記事を参照ください。　
scoreName: 'book[star]',　 #これがbookモデルのstarカラムへ値を保存という意味になる。
half: false, 　　　　　　　　　　　　　　　　　　　　　　　　　#今回は５段階評価で⭐️半分評価は入力できないように設定します。
```

id="star"で　<strong>"star"</strong>　というid名をつけて
$('#star').raty( 　　　　　で　<strong>"star"</strong>　に"raty.js"を以下の内容で適用させ、
scoreName: 'book[star]'　で　bookモデルのstarカラムへ保存します。
ってことだと思います。

これで星レビューが投稿できるようになっていると思います。
次は投稿したレビューを表示できるようにしていきます。
#
#### 参考記事
- ブラウザバックで星が増える不具合の参考記事

https://qiita.com/hana_s_e_0925/items/1a72927b03c01229de07

#

# 投稿の詳細画面での星レビュー表示

```diff_html: views/books/show.html.erb
<div class='container'>
  <div class='row'>
    <div class='col-md-3'>
      <h2>User info</h2>
      <%= render 'users/info', user: @book.user %>
      <h2 class="mt-3">New book</h2>
      <%= render 'form', book: Book.new %>
    </div>
    <div class='col-md-8 offset-md-1'>
  		<h2>Book detail</h2>
  		<table class='table'>
  		  <tr>
          <td><%= link_to(@book.user) do %>
            <%= image_tag @book.user.get_profile_image, size:"100x100" %><br>
            <%= @book.user.name %>
            <% end %>
          </td>
          <td><%= link_to @book.title, @book %></td>
          <td><%= @book.body %></td>
          <td class=<%= "favorite-" + @book.id.to_s %> >
            <%= render "favorites/btn", book: @book %>
          </td>
          <td class="book-comments-counter">
            <%= render "book_comments/counter", book: @book %>
          </td>
ーーーーーーーーーーーーーーーーーーーーーここから星レビュー機能の追加ーーーーーーーーーーーーーーーーーーーーーーーーーー
          <td>
            <div id="star-rate<%= @book.id%>"></td>
             <script>
              $('#star-rate<%= @book.id %>').empty();
              $('#star-rate<%= @book.id%>').raty({
                 size      : 36,
                 starOff   : '<%= asset_path('star-off.png') %>',
                 starOn    : '<%= asset_path('star-on.png') %>',
                 half      : false,
                 readOnly: true,
                 score: <%= @book.star %>,
               });
             </script>
          </td>
ーーーーーーーーーーーーーーーーーーーーここまで星レビュー機能の追加ーーーーーーーーーーーーーーーーーーーーーーーーーーー
          <% if @book.user == current_user %>
            <td><%= link_to 'Edit', edit_book_path(@book), class: "btn btn-sm btn-success edit_book_#{@book.id}" %></td>
            <td><%= link_to 'Destroy', @book, method: :delete, data: { confirm: '本当に消しますか？' }, class: "btn btn-sm btn-danger destroy_book_#{@book.id}" %></td>
          <% end %>
        </tr>
      </table>

      <div class="book-comments-index">
        <%= render "book_comments/index", book: @book %>
      </div>

      <%= render "book_comments/form", book: @book, book_comment: @book_comment %>

    </div>
  </div>
</div>
```

```:point

$('#star-rate<%= @book.id %>').empty();　　　　　#これがないとブラウザバックで星が増え続ける不具合が発生します。詳しくは参考にした記事を参照ください。　
readOnly: true,         　 　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　#読み取り専用、編集できないようにする。
score: <%= @book.star %>,　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　#@book= Book.find(params[:id])のstarカラムを取り出す。

```


これで投稿の詳細画面での表示はできたと思います。

次は、投稿一覧ページへ星レビュー表示させていきます。


# 投稿の一覧画面での星レビュー表示

```diff_html: views/books/_index.html.erb
<table class='table table-hover table-inverse'>
  <thead>
    <tr>
      <th></th>
      <th>Title</th>
      <th>Opinion</th>
      <th colspan="3"></th>
    </tr>
  </thead>
  <tbody>
    <% books.each do |book| %>
      <tr>
        <td><%= link_to(book.user) do %>
          <%= image_tag book.user.get_profile_image, size:'50x50' %>
          <% end %>
        </td>
        <td><%= link_to book.title,book, class: "book_#{book.id}" %></td>
        <td><%= book.body %></td>
        <td class=<%= "favorite-" + book.id.to_s %> >
            <%= render "favorites/btn", book: book %>
        </td>
ーーーーーーーーーーーーーーーーーーーーーここから星レビュー機能の追加ーーーーーーーーーーーーーーーーーーーーーーーーーー
        <td>
          <div id="star-rate<%= book.id%>"> </td>
          <script>
            $('#star-rate<%= book.id %>').empty();
            $('#star-rate<%= book.id%>').raty({
              size      : 36,
              starOff   : '<%= asset_path('star-off.png') %>',
              starOn    : '<%= asset_path('star-on.png') %>',
              half      : false,
              readOnly: true,
              score: <%= book.star %>,
            });
          </script>
          </div>    
ーーーーーーーーーーーーーーーーーーーーーここまで星レビュー機能の追加ーーーーーーーーーーーーーーーーーーーーーーーーーー
        <td><%= render "book_comments/counter", book: book %></td>
      </tr>
    <% end %>
  </tbody>
</table>
```

追加する記述は、詳細ページとほとんど同じ内容になりまね。
以上で完成です。

次の記事、ソート機能の実装をやっています。

https://qiita.com/kcl215/items/c0222befe4a6b270986a

## 完成イメージ

![bookers.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2581621/4aa1671e-bee3-95a6-55db-0673cd5b6a9c.gif)


## 参考した記事

https://qiita.com/nozomi_nozomi/items/eb6848c6025ca63af1c4