【Rails】お気に入り機能の実装手順を公開!!【簡単にできます】

あおい（https://twitter.com/aoi_engineer ）です。
Ruby on Railsでお気に入り機能を実装したので手順を公開します。

# 実装したい機能

- お気に入り登録・解除できること
- 未ログイン時にはお気に入りできない
- 未ログイン時にはお気に入り一覧ページにアクセスできない
- お気に入りボタンをクリックするとAjax通信でデータ保存ができる


## 1. ブランチ作成
```
git checkout -b favorites
``` 

## 2. Favoriteモデルの作成

```
% rails g model Favorite user_id:integer post_id:integer
```

## 3. migrationファイルの編集

```ruby
def change
    create_table :favorites do |t|
      t.integer :user_id
      t.integer :post_id
      t.timestamps
    end
    add_index :favorites, [:user_id, :post_id], unique: true  # 追記
  end
```
データベースに反映させます。

```
% rails db:migrate
```

## 4. 関連付け
```favorite.rb
class Favorite < ApplicationRecord
  belongs_to :user
  belongs_to :post
end
```

```user.rb
has_many :favorites, dependent: :destroy
```

```post.rb
has_many :favorites, dependent: :destroy
```

## 5. バリデーションの追加

```favorite.rb
belongs_to :user
belongs_to :post
validates :user_id, presence: true  # 追記
validates :post_id, presence: true  # 追記
```

## 6. コントローラー作成

```
% rails generate controller Favorites
```

## 7. コントローラー編集

```favorites_controller.rb
class FavoritesController < ApplicationController
 before_action :authenticate_user!

 def create
  @post = Post.find(params[:post_id])
    @user = @post.user
    current_user.favorite(@post)
    respond_to do |format|
      format.html { redirect_to request.referrer || root_url }
      format.js
    end
 end

 def destroy
  @post = Post.find(params[:post_id])
    current_user.favorites.find_by(post_id: @post.id).destroy
    respond_to do |format|
      format.html { redirect_to request.referrer || root_url }
      format.js
    end
 end

end
```

## 8. ルート編集
```route.rb
 post   "favorites/:post_id/create"  => "favorites#create"
 delete "favorites/:post_id/destroy" => "favorites#destroy"
end
```

## 9. お気に入り登録のメソッドを作成

```user.rb
  def favorite(post)
    Favorite.create!(user_id: id, post_id: post.id)
  end

  # 投稿のお気に入り解除する
  def unfavorite(post)
    Favorite.find_by(user_id: id, post_id: post.id).destroy
  end

  # 現在のユーザーがお気に入り登録してたらtrueを返す
  def favorite?(post)
    !Favorite.find_by(user_id: id, post_id: psot.id).nil?
  end

```

## 10. Ajaxの実装

- **app/views/favorites/create.js.erb （新規）**
- **app/views/favorites/destroy.js.erb （新規）**

```create.js.erb
$("#favorite-<%= @post.id %>").html("<%= escape_javascript(render('users/unfavorite')) %>");
```

```destroy.js.erb
$("#favorite-<%= @post.id %>").html("<%= escape_javascript(render('users/favorite')) %>");
```

## 11. お気に入りフォームの作成

#### app/views/users/_favorite_form.html.erb （新規）

```app/views/users/_favorite_form.html.erb
<div id="favorite-<%= @post.id %>">
  <% if !current_user.nil? && current_user.favorite?(@post) %>
    <%= render 'users/unfavorite' %>
  <% else %>
    <%= render 'users/favorite' %>
  <% end %>
</div>
```

## 12. それぞれのボタンを作成
#### app/views/users/_favorite.html.erb （新規）

```app/views/users/_favorite.html.erb
<%= link_to "/favorites/#{@post.id}/create", method: :post, class: 'like', remote: true do %>
  <div class="favorite">お気に入りに登録</div>
<% end %>
```
#### app/views/users/_unfavorite.html.erb （新規）
```app/views/users/_unfavorite.html.erb
<%= link_to "/favorites/#{@post.id}/destroy", method: :post, class: 'like', remote: true do %>
  <div class="unfavorite">お気に入り登録済み</div>
<% end %>
```

## 13. ボタンによって色を変更

```app/assets/stylesheets/~.scss
like {
 color: gray;
}

unlike {
 color: red;
}
```

## 14. 表示させたいページに追加

```~.erb
<%= render 'users/favorite_form' %>
```

以上です！
参考になった方はLGTMお願いします🙇‍♂️