お気に入り登録を実装（非同期通信）

現在RubyとJavaScriptを用いて記事投稿サイトを作成しています。
今回は**非同期通信のお気に入り登録**を実装します。
お気に入り記事一覧ページの実装までを書いていきたいと思います。

■仕様等
・お気に入りアイコンをクリックで登録/解除
・jQueryを使用しない

###■完成後の動作
[![Image from Gyazo](https://i.gyazo.com/3a294759ebd11a6fc8f7a3aaae93581b.gif)](https://gyazo.com/3a294759ebd11a6fc8f7a3aaae93581b)

###■お気に入り機能
####①Userモデルにお気に入り操作に関するメソッドを記述
bookmarkモデル、コントローラーを作成した後、
お気に入り操作に関するメソッドをuserモデルに追記していきます。
※bookmarksテーブルのカラムはuser_idとarticle_idです。

```Ruby:user.rb
         has_many :bookmarks_articles, through: :bookmarks, source: :article
         def bookmark(article)
           bookmarks_articles << article
         end

         def unbookmark(article)
           bookmarks_articles.delete(article)
         end

         def bookmark?(article)
           bookmarks_articles.include?(article)
         end
```
**1行目**
`has_many :bookmarks_articles, through: :bookmarks, source: :article`
ユーザーがお気に入り登録した記事を全て取得します。
**2行目〜4行目**
`bookmark(article)`
引数に渡された記事情報を1行目のbookmarks_articlesに追加します。
**6行目〜8行目**
`unbookmark(article)`
引数に渡された記事情報を1行目のbookmarks_articlesから削除します。
**10行目〜12行目**
`bookmark?(article)`
引数に渡された記事情報が1行目のbookmarks_articlesに存在するかを判定します。

####②ルーティングの設定

```Ruby:routes.rb
Rails.application.routes.draw do
  devise_for :users
  root to: "articles#index"
  resources :bookmarks, only: [:create, :destroy]
end
```
**3行目**
`resources :bookmarks, only: [:create, :destroy]`
お気に入りの登録と解除を実装するので、createとdestroyを指定します。

####③コントローラーの設定
```Ruby:bookmarks_controller.rb
class BookmarksController < ApplicationController
  def create
    @article = Article.find(params[:article_id])
    current_user.bookmark(@article)
  end

  def destroy
    @article = current_user.bookmarks.find_by(id: params[:id]).article
    current_user.unbookmark(@article)
  end
end
```
**4行目/9行目**
`current_user.bookmark(@article)`
`current_user.unbookmark(@article)`
userモデルで設定した各メソッドの引数に、対象の記事情報を渡しています。

####④ビューの記述
```html:index.html
    <% @articles.each do |article| %>
      <div class="article-content" id="article_<%= article.id %>">
        <%= render 'shared/articles', article: article %>
      </div>
    <% end %>
```
```html:_articles.html
<% if user_signed_in? %>
  <% if current_user.bookmark?(article) %>
    <%= render 'bookmarks/unbookmark', article: article %>
  <% else %>
    <%= render 'bookmarks/bookmark', article: article %>
  <% end %>
<% end %>
```

###■非同期処理の設定
```html:_bookmark.html
<%= link_to bookmarks_path(article_id: article.id), class: "bookmark", id: "js-bookmark-button-for-article-#{article.id}", method: :post, remote: true do %>
<% end %> 
```
```html:_unbookmark.html
<%= link_to bookmark_path(current_user.bookmarks.find_by(article_id: article.id)), class: "bookmark", id: "js-bookmark-button-for-article-#{article.id}", method: :delete, remote: true do %>
<% end %> 
```
**各1行目**
`<%= link_to bookmarks_path(article_id: article.id), class: "bookmark", id: "js-bookmark-button-for-article-#{article.id}", method: :post, remote: true do %>`
`<%= link_to bookmark_path(current_user.bookmarks.find_by(article_id: article.id)), class: "bookmark", id: "js-bookmark-button-for-article-#{article.id}", method: :delete, remote: true do %>`
各末尾のオプションに注目してください。
`remote:true`を設定することでJavaScript形式のリクエストが送信されます。
送信後、コントローラーのcreate/destroyアクションが実行されると、
`create.js.erb`/`destroy.js.erb`に遷移します。

```Ruby:create.js.erb
document.getElementById('article_<%= @article.id %>').innerHTML = '<%= escape_javascript( render 'shared/articles', article: @article ) %>'
```
```Ruby:destroy.js.erb
document.getElementById('article_<%= @article.id %>').innerHTML = '<%= escape_javascript( render 'shared/articles', article: @article ) %>'
```
対象の記事を取得して、index.htmlにもあるrenderの部分を挿入しています。
`escape_javascript`は改行のエスケープ処理を行います。

以上でお気に入り登録を実装することができました。

###■お気に入り一覧表示ページの実装
[![Image from Gyazo](https://i.gyazo.com/ca649be681e31207d3e76ebd76c32054.gif)](https://gyazo.com/ca649be681e31207d3e76ebd76c32054)
####①ルーティングの設定
```Ruby:routes.rb
Rails.application.routes.draw do
  devise_for :users
  root to: "articles#index"
  resources :articles do
    collection do
      get :bookmarks
    end
  end
  resources :bookmarks, only: [:create, :destroy]
end
```
**4行目〜8行目**
`:articles`にネストする形で`collection do :bookmarks end`を記述します。

####②コントローラーの設定
```Ruby:articles_controller.rb
  def bookmarks
    @bookmarks_articles =
    current_user.bookmarks_articles.includes(:user).order('created_at DESC')
  end
```
`current_user.bookmarks_articles`
現在のユーザーの全てのお気に入り記事を取得しています。（冒頭のUserモデルにて設定）

####③ビューを記述
```erb:bookmark.html.erb
    <% @bookmarks_articles.each do |article| %>
      <div class="article-content" id="article_<%= article.id %>">
        <%= render 'shared/articles', article: article %>
      </div>
    <% end %>
```
インスタンス変数を受け取って一覧を表示しています。

以上でお気に入り記事一覧表示ページの完成です。

####参考記事
https://miiina01220.hatenablog.com/entry/2020/11/20/172739
https://techtechmedia.com/favorite-function-rails/
https://qiita.com/naberina/items/c6b5c8d7756cb882fb20