[Rails]お気に入り機能

# 実装したいこと
- クチコミ投稿者以外がお気に入り登録をできる
- Ajax通信でお気に入り機能を実装
- マイページにお気に入り一覧を表示

# 目次
1. お気に入りテーブルの作成
2. アソシエーション
3. ルーティング
4. コントローラー
5. ビュー

# 1. お気に入りテーブルの作成
どの投稿に、誰がお気に入りをしたのかを保存するために、お気に入りのテーブルを作成します。

##### ターミナル
```
rails g model like
```

```ruby:db/migrate/20XXXXXXXXXXXX_create_likes.rb
class CreateLikes < ActiveRecord::Migration[6.0]
  def change
    create_table :likes do |t|
      t.references :user, null: false, foreign_key: true
      t.references :post, null: false, foreign_key: true
      t.timestamps
      t.index [:user_id, :post_id], unique: true   # 同じユーザーが同じ投稿をお気に入りできないようにしている
    end
  end
end
```

# 2. アソシエーション
**dependent: :destroyオプション**を用いて、投稿が削除されたらお気に入りも削除されるようにします。親モデルに記述します。

```ruby:app/models/likei.rb
class Like < ApplicationRecord
  belongs_to :user
  belongs_to :post
end
```

```ruby:app/models/post.rb
class Post < ApplicationRecord
  belongs_to :user
  has_many :likes, dependent: :destroy
  has_one_attached :image
  ~略~
end
```

```ruby:app/models/user.rb
class User < ApplicationRecord
 ~略~
  has_many :posts
  has_many :likes
  has_one_attached :image
end
```

# 3. ルーティング
どの投稿に対してのお気に入りかを示す必要があるので、ルーティングネストを用います。
お気に入りの登録と削除ができたら良いので、「createアクション」と「destroyアクション」を設定します。

```ruby:config.routes.rb
Rails.application.routes.draw do
  devise_for :users
  root 'posts#top'
  resources :posts do
    collection do
      get 'top'
    end
    resources :likes, only: [:create, :destroy]
  end
  resources :users, only: [:show]
end
```

# 4. コントローラー
コントローラーに「createアクション」と「destroyアクション」を定義します。
お気に入りの登録は投稿者以外が登録可能にしています。

```ruby:app/controllers/likes_controller.rb
class LikesController < ApplicationController
  def create
    @post = Post.find(params[:post_id])
    if @post.user_id != current_user.id   # 自分の投稿以外にお気に入り登録が可能
      @like = Like.create(user_id: current_user.id, post_id: @post.id)
    end
  end
  def destroy
    @post = Post.find(params[:post_id])
    @like = Like.find_by(user_id: current_user.id, post_id: @post.id)
    @like.destroy
  end
end
```

マイページにユーザーがお気に入り登録をした投稿を表示します。
Likesテーブルからログイン中のユーザーのお気に入りを検索し、降順に並べ、post_idを配列の形で取り出します。
それをfindメソッドで@likesにデータを格納しています。

```ruby:app/controllers/users_controller.rb
class UsersController < ApplicationController
  def show
    @user = User.find(params[:id])
    birthday = @user.birthday
    date_format = "%Y%m%d"
    @age = (Date.today.strftime(date_format).to_i - birthday.strftime(date_format).to_i) / 10000
    @posts = @user.posts.order(created_at: :desc)

    likes = Like.where(user_id: current_user.id).order(created_at: :desc).pluck(:post_id)
    @likes = Post.find(likes)
  end
end
```

# 5. ビュー
お気に入りボタンを表示する部分を部分テンプレートを作成して表示します。
if文でお気に入り登録している時としていない時で分岐しています。

```ruby:app/views/posts/show.html.erb
~略~
<div class='star-btn' id="like_<%= @post.id %>">
  <%= render "likes/like", post: @post %>
</div>
~略~
```

```ruby:app/views/likes/_like.html.erb
<% if !Like.exists?(user_id: current_user.id, post_id: post.id) %>
  <%= link_to post_likes_path(post.id), method: :post, remote: true, class:"like-btn" do %>
    <i class="fas fa-star star-icon1"></i>
  <% end %>
  <span class="star-count1"><%= post.likes.count %></span>
<% else %>
  <%= link_to post_like_path(post_id: post.id, id: post.likes[0].id), method: :delete, remote: true, class:"like-btn" do %>
    <i class="fas fa-star star-icon2"></i>
  <% end %>
  <span class="star-count2"><%= post.likes.count %></span>
<% end %>
```

お気に入りボタンがクリックされたときに「createアクション」または「destroyアクション」が実行され、部分テンプレートがAjax通信で更新されお気に入りボタンが表示されます。
※ jqueryで記述しているためインストールが必要です。

```ruby:app/views/likes/create.js.erb
$('#like_<%= @post.id %>').html("<%= j(render partial: 'likes/like', locals: { post: @post }) %>");
```

```ruby:app/views/likes/destroy.js.erb
$('#like_<%= @post.id %>').html("<%= j(render partial: 'likes/like', locals: { post: @post }) %>");
```

マイページのお気に入り一覧はeach文を用いて表示しています。

```ruby:app/views/users/show.html.erb
<% if @likes.present? %>
    <ul class="relative-post-lists">
    <% @likes.each do |like| %>
      <li class='post-list'>
        <%= link_to post_path(like.id) do %>
        <div class='post-img-content'>
          <%= image_tag like.image, class: "post-img" %>
        </div>
          <div class='relative-post-name'>
            商品名：<%= like.name %>
          </div>
          <div class='relative-item-price'>
            <span><%= like.price %>円(税込み)</span>
          </div>
          <div class="relative-post-evaluation">
            <span>評価：</span>
              <span id="star-rate-<%= like.id %>"></span>
              <script>
                $('#star-rate-<%= like.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: <%= like.evaluation %>,
                });
              </script>
              <%= like.evaluation %>
          </div>
          <div class='user-img-content'>
            <%= image_tag like.user.image ,class:"mini-img" if like.user.image.attached? %>
            <%= link_to like.user.nickname, user_path(like.user.id) %>
          </div>
          <div class="relative-post-date">
            更新日時：<%= like.created_at.strftime("%Y.%m.%d") %>
          </div>
        <% end %>
      </li>
    <% end %>
    </ul>
  <% end %>
```

# 参考リンク
https://qiita.com/manbolila/items/43a04e8d0d5018cf7f62
https://qiita.com/naberina/items/c6b5c8d7756cb882fb20
https://qiita.com/naota7118/items/e009eff939b5a764672d