Railsでお気に入り機能をつけてみた

## お気に入り機能を追加
使用者が施設をお気に入りに登録できるようにしました

### 中間テーブルを作成
```ruby:ターミナル
rails g model Favorite user:references institution:references
```
```ruby:XXX_crate_favorites.rb
class CreateFavorites < ActiveRecord::Migration[6.1]
  def change
    create_table :favorites do |t|
      t.references :user, null: false, foreign_key: true
      t.references :institution, null: false, foreign_key: true

      t.timestamps
      # userとinstitutionの組み合わせが重複しないように設定
      t.index [:user_id, :institution_id], unique: true
    end
  end
end
```

###　関連付け
```ruby:app/models/institution.rb
class Institution < ApplicationRecord
  # dependent: :destroy　でinstitutionが削除されたら、関連するfavoritesも削除する
  has_many :favorites, dependent: :destroy
end
```
``dependent:`` -> institutionがdestroyしたときに、関連づけたfavoritesの操作を設定

```ruby:app/models/user.rb
class User < ApplicationRecord
  has_many :favorites, dependent: :destroy
end
```

### Routing
```ruby:config/routes.rb
Rails.application.routes.draw do
  resources :institutions do
    resource :favorites, only: [:create, :destroy]
  end
end
```
``favorite``の登録と削除をするために、``crate``と``destroy``を設定

### Controller
```ruby:app/controllers/favorites_controller.rb
class FavoritesController < ApplicationController
  # お気に入り追加
  def create
    # institution_idに一致するinstitutionを取得
    @institution = Institution.find(params[:institution_id])
    # current_userとinstitutionに一致するfavoriteを作成
    @favorite  = Favorite.create(user_id: current_user.id, institution_id: @institution.id)
  end

  # お気に入り削除
  def destroy
    # institution_idに一致するinstitutionを取得
    @institution = Institution.find(params[:institution_id])
    # # current_userとinstitutionに一致するfavoriteを取得
    @favorite = Favorite.find_by(user_id: current_user.id, institution_id: @institution.id)
    # 取得したfavoriteを削除
    @favorite.destroy
  end
end
```

### View
```erb:app/views/institutions/index.html.erb
<!-- お気に入りを追加する場所にパーシャルファイルを追加 -->
<%= render "favorites/favorite", institution: institution %>
```
```erb:app/veiws/favorites/_favorite.html.erb
<!-- idセレクタを個々に設定するため、institution.idを入れる -->
<div class='star-btn' id="favorite_<%= institution.id %>">
<!-- 条件付きで存在を確認 (!が先頭についているため、Favoriteが存在しない時) -->
  <% if !Favorite.exists?(user_id: current_user.id, institution_id: institution.id) %>
<!-- favorite#createへのリンクを表示 -->
    <%= link_to institution_favorites_path(institution.id), method: :post, remote: true, class:"like-btn" do %>
      <i class="fa fa-star-o">お気に入り</i>
    <% end %>
<!-- favoritesの数を表示 -->
    <span class="star-count1"><%= institution.favorites.count %></span>
<!-- Favoriteが存在するとき -->
  <% else %>
<!-- favorite#deleteへのリンクを表示 -->
    <%= link_to institution_favorites_path(institution_id: institution.id, id: institution.favorites[0].id), method: :delete, remote: true, class:"like-btn" do %>
      <i class="fa fa-star">お気に入り解除</i>
    <% end %>
<!-- favoritesの数を表示 -->
    <span class="star-count2"><%= institution.favorites.count %></span>
  <% end %>
</div>
```

### Ajax設定
favorites/_favorite.html.erb(Ajax設定するファイル)と同じディレクトリに作成
``$('発火ID').html("<%= j(render)　%>")``

```erb:app/views/favorites/create.js.erb
<!-- favorite#createの動き -->
$('#favorite_<%= @institution.id %>').html("<%= j(render partial: 'favorites/favorite', locals: { institution: @institution }) %>");
```
```erb:app/views/favorites/detele.js.erb
<!-- favorite#deleteの動き -->
$('#favorite_<%= @institution.id %>').html("<%= j(render partial: 'favorites/favorite', locals: { institution: @institution }) %>");
```

### お気に入り一覧を表示
```ruby:app/controllers/users_controller.rb
class UsersController < ApplicationController
  def index
    @user = User.find(current_user.id)
    favorites = Favorite.where(user_id: current_user.id).pluck(:institution_id)
    @favorites_list = Institution.find(favorites)
  end
end
```

```ruby:ruby
# modelにhas_many throughをつけると、@user.institutionで呼び出せる
class User < ApplicationRecord
  has_many :favorites, dependent: :destroy
  has_many :institutions, through: :favorites
end

class Institution < ApplicationRecord
  has_many :favorites, dependent: :destroy
  has_many :users, through: :favorites
end

class UsersController < ApplicationController
  def index
    @user = User.find(current_user.id)
    # userが属するfavoriteを取得し、favoriteからinstitution_idを取得
    favorite_institution = @user.favorite.pluck(:institution_id)
    # Institutionモデルから取得したinstitution_idに当てはまるものを取得
    @favorites_list = Institution.find(favorite_institution)
  end
end
```

```erb:app/views/users/index.html.erb
<div class="main">
  <h2>お気に入り一覧</h2>
    <% @favorites_list.each do |favorite| %>
      <div class="name"><%= favorite.name %></div>
      <div class="postcode"><%= favorite.postcode %></div>
      <div class="address"><%= institution.address %></div>
      <div class="introduction"><%= institution.introduction%></div>
      <%= render "favorites/favorite", institution: institution %>
    <% end %>
</div>
```

### **おまけ** font-awesome-railsを導入
[公式HP](https://fontawesome.com/v4.7/icons/)から使用できるアイコンを確認できる

```ruby:Gemfile
gem 'font-awesome-rails'
```
```ruby:ターミナル
bundle install
```
```css:app/assets/stylesheets/application.css
*= require font-awesome
```


## 参考資料
[[Rails]お気に入り機能](https://qiita.com/iwkmsy9618/items/37debd2aa6a453f729b6)
[Railsで多対多のお気に入り機能を作成する](https://tkmrtech.hateblo.jp/entry/2019/10/06/173020)
[中間テーブルを使ったお気に入り機能の実装！](https://miiina01220.hatenablog.com/entry/2020/11/20/172739)
[rails お気に入り機能の実装をしてみた](https://zenn.dev/j_09/articles/e0188500f43d3a)
[Active Record の関連付け](https://railsguides.jp/association_basics.html)
[【Rails】データが1件でもあるかどうかチェックするにはModel.exists?](https://opiyotan.hatenablog.com/entry/tips-model-exists)
[【Ruby on Rails】部分テンプレート（js.erb）を用いた非同期通信について（基礎/開発）](https://qiita.com/yoshi-2015/items/6d5eb88abb2a4ef09f92)
[【Rails】 remote: trueでフォーム送信をAjax実装する方法とは？](https://pikawaka.com/rails/remote-true)
[font-awesome-railsの使い方を徹底解説！](https://pikawaka.com/rails/font_awesome_rails)