【Rails】星★の評価を実装する（入力、保存、表示）

#はじめに

開発しているメモアプリにランク付けをしていきたいと思います。
今回は三段階で評価していこうと思います。
それに伴い、星★による評価の入力・保存・表示について学習していきます。

## 参考

[jQuery Raty](http://itref.fc2web.com/javascript/jquery/raty.html)

[Railsで星の評価機能をつける](https://qiita.com/kitaokeita/items/1e40724c3384507cec13)

[jQuery Ratyの使い方](https://github.com/wbotelhos/raty)

# マイグレーションファイルにカラムを追加

notesテーブル(メモテーブル)のrateカラム（型:float）に評価値を保存する
すでに、テーブルとカラムは作成されていることを前提に進めます。

カラムは、半分の星による評価を行う場合、「0.5」や「1.5」という値を保存することになるため、float型にしておく必要があります。

もし、integerやstringの型として作成してしまっていた場合は、データベースによって変更方法は異なりますが、型の変更が必要になります。下記の記事でこの型の変更の沼にハマってしまった時の解決策を書いています。

```
class CreateNotes < ActiveRecord::Migration[5.2]
  def change
    create_table :notes do |t|
      t.text :title
      t.integer :user_id
      t.integer :category_id
      t.text :explanation
      t.float :rate

      t.timestamps
    end
  end
end
```

```
rails db:migrate:reset
```

## javascriptの読み込み、★画像の読み込み

上記参考リンクを確認しながら、javascriptの読み込みと星の画像の読み込みを行います。

やり方（２種類）

①jQuery Ratyを使用するには、https://github.com/wbotelhos/raty からjquery.raty.jsをダウンロードする。

  ダウンロードしたスクリプトをウェブサイトの任意の場所に配置する。

  app/javascriptsのフォルダの中にダウンロードしたjquery.raty.jsファイルを配置する

②jQuery Ratyを使用するHTMLの中でJavaScriptを読み込む。RatyはjQueryのプラグインなので、jQueryのスクリプトも必要である。

```
    <script src="/js/jquery.min.js"></script>
    <script src="/js/jquery.raty.js"></script>
```

今回は①のやり方で実装していきます。

＊ここでJavaScriptも定義していきます。
`javascripts/application.js`ファイルに追加する

```
//= require rails-ujs
//= require activestorage
//= require turbolinks
//= require jquery       #追加
//= require jquery_ujs　 #追加
//= require_tree .
```

Gemファイル追加

```
gem 'jquery-rails'
```

```
budle install
```

# モデルの記述(紐付け)

```note.rb
class Note < ApplicationRecord
  # ユーザーとの紐付け
  belongs_to :user,optional: true

　# バリデーション
  validates :title, presence: true
  validates :explanation, presence: true
  
  # カテゴリーと紐付け
  belongs_to :category

  validates :rate, presence: true
  validates :rate, numericality: {
    # only_integer: true,
    less_than_or_equal_to: 3,
    greater_than_or_equal_to: 1,
  }

 ＃　numericality＝空を許可する
    numericalityは、デフォルトでは小数も許容してしまいます。rateカラムでは整数のみ許可したいので、 only_integerを。
 例
   validates :param３, :numericality => { :less_than_or_equal_to => ３}
   # 数字が３以下であるか
   validates :param3, :numericality => { :greater_than_or_equal_to => １ }
   # 数字が１以上であるか

end
```

#コントローラーの記述
```notes_controller.rb
class NotesController < ApplicationController
  before_action :authenticate_user!
  def new
    @note = Note.new
  end

  def create
    # @note = Note.new(note_params)
    @note = current_user.notes.build(note_params)
    @note.save
    redirect_to notes_path
  end

  def index
    # is_validがマッチするレコードを全て取得
    @categorys = Category.where(is_valid: true)
    @q = Note.all.ransack(params[:q])
    @notes = @q.result(distinct: true)
  end

  def show
    @note = Note.find(params[:id])
  end

  def edit
    @note = Note.find(params[:id])
  end

  def update
    @note = Note.find(params[:id])
    @note.update(note_params)
    redirect_to note_path
  end

  def destroy
    @note = Note.find(params[:id])
    @note.destroy
    redirect_to notes_path
  end

  def search
    @categorys = Category.where(is_valid: true)
    @category = Category.find(params[:id])
    @q = @category.notes.all.ransack(params[:q])
    @notes = @q.result(distinct: true).page(params[:page])
    @title = @category.name
    render 'notes/index'
  end

  private

  def note_params
    params.require(:note).permit(:title, :category_id, :explanation,:user_id,:rate)
  end
end
```

ここでは`note_params`メソッドに`rate`カラムを追加します。

# メモ登録フォームを作る
```_form.html.erb
<%= form_with model:note, local: true do |f| %>
  <div class='form-group'>
    <%= f.label :タイトル %>
    <%= f.text_field :title, class: 'form-control', id: 'note_title' %>
  </div>
  <div class='form-group'>
    <%= f.label :カテゴリー %>
    <%= f.collection_select :category_id, Category.all, :id, :name %>
  </div>
  <!-- 評価 -->
  <div class="form-group row" id="star">
    <%= f.label :rate,'重要度 ', class:'col-md-1 col-form-label' %>
    <%= f.hidden_field :rate, id: :review_star %>
  </div>
  <div class='form-group'>
    <div id='editor'>
      <%= f.label :内容 %>
      <%= f.text_area :explanation, rows: 10, class: 'form-control', id: 'note_explanation', "v-model" => "input", name: "note[explanation]" %>
    <h2><i class="fas fa-eye"></i> Preview</h2>
    <div id="preview-field" v-html='input | marked'>
    </div>
    <div ></div>
  </div>
  <%= f.submit '登録', class: 'btn btn-success' %>
  </div>
  <% end %>

  <!-- リアルタイムプレビュー -->
  <script type="text/javascript">
    window.onload = function() {
      new Vue({
      el: '#editor',
      data: {
        input: '<%== j @note.explanation %>',
      },
      filters: {
      marked: marked,
      },
      });
    };

   <!-- 評価 -->
    $('#star').raty({
      size     : 36,
      starOff:  '<%= asset_path('star-off.png') %>',
      starOn : '<%= asset_path('star-on.png') %>',
      starHalf: '<%= asset_path('star-half.png') %>',
      scoreName: 'note[rate]',
      half: true,
    });
  </script>
```

## ポイントは下記

    # ★の半分の入力ができるようにするため
      half: true,

## 星による評価の表示

メモ一覧にて、★を表示したい。
上記の「★の入力、保存」と異なる点は、1.入力はせず表示する 、 2.繰り返し処理をするということになります

```_notes_index.html.erb
<div class='row'>
  <table class='table'>
    <thead>
      <tr>
        <th>タイトル</th>
        <th>カテゴリー</th>
        <th>重要度</th>
      </tr>
    </thead>
    <tbody>
      <% @notes.each do |note| %>
      <% if user_signed_in? && current_user.id == note.user_id %>
      <tr>
        <td>
          <%= link_to note_path(note) do %>
            <%= note.title %>
          <% end %>
        </td>
        <td><%= note.category.name %></td>
        <!--　評価　-->
　　　　　<td>
         <div id="star-rate-<%= note.id %>"></div>
          <script>
            // 繰り返し処理でもidをidを一意にできるようにnote_idを入れる
          $('#star-rate-<%= note.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: <%= note.rate %>,
          });
          </script>
        </td>
      </tr>
      <% end %>
      <% end %>
    </tbody>
  </table>
</div>
```

## ポイントは下記

    # 繰り返し処理を実行してもidを一意に保てるようにreview.idを埋め込む
    <div id="note-rate-<%= note.id %>"></div>

    # 読み取り専用（入力できない）
    readOnly: true,

    # 星の入力値を読み込む
    score: <%= note.rate %>,


これで完成！

#最後に
説明が分かりづらかったりすると思いますがご了承ください。
また、間違っているところがあればご教授いただけると幸いです。