お気に入り機能と一覧の実装

備忘録です！！

##Bookmarkモデルの作成

```ruby:ターミナル
rails g model Bookmark user:references board:references
```

```ruby:bookmark.rb
belongs_to :user
belongs_to :board
validates :user_id, uniqueness: { scope: :board_id }
```
お気に入りを外す処理では、ユーザーと掲示板のお気に入りの関係性がなくなるように該当のレコードを削除します。
もしuserとboardの組み合わせのレコードが重複すると、お気に入りを外しても関係性のレコードが残り、お気に入りが解除できなくなってしまいます。
そのため、ユーザーと掲示板の組み合わせのレコードを一意にするためのunique制約が必要となります。

##userモデルとboardモデルのアソシエーション

```ruby:user.rb
has_many :bookmarks, dependent: :destroy
has_many :bookmark_boards, through: :bookmarks, source: :board
```

```ruby:board.rb
has_many :bookmarks, dependent: :destroy
has_many :bookmark_boards, through: :bookmarks, source: :board

def bookmark_by?(user)
  bookmarks.where(user_id: user.id).exists?
end
```

ここで、

```
has_many :bookmark_boards, through: :bookmarks, source: :board
```

に注目してください。

さらに、

```
def bookmark_by?(user)
  bookmarks.where(user_id: user.id).exists?
end
```

では、bookmarkにuser_idがあるかないかのメソッドを定義しています。
後に、引数にcurrent_userを入れて、お気に入りボタンの出し入れをします。

has many throughオプションでユーザーがお気に入りした投稿を直接アソシエーションで取得できます。
sourceでは参照するモデルを指定しています。

##ルーティングの追加

```ruby:routes.rb
resources :boards, only: %i[index new create show edit destroy update], shallow: true do
  get :bookmarks, on: :collection
  resource :bookmarks, only: %i[create destroy]
end
```

bookmarksをネストし、さらにcollectionでbookmarksという新しいアクションを作成します。

```
bookmarks_boards GET    /boards/bookmarks(.:format)               boards#bookmarks
board_bookmarks  DELETE /boards/:board_id/bookmarks(.:format)     bookmarks#destroy
                 POST   /boards/:board_id/bookmarks(.:format)     bookmarks#create
```

bookmarksというアクションを呼び出したら、お気に入りした掲示板に一覧が出るようにします。

##bookmarksコントローラーの作成

```ruby:bookmarks_controller
def create
  @bookmark = current_user.bookmarks.build(board_id: params[:id])
  @bookmark.save!
  redirect_back fallback_location: root_path, success: t('defaults.message.bookmark')
end

def destroy
  current_user.bookmarks.find_by(board_id: params[:id].destroy!
  redirect_back fallback_location: root_path, success: t('defaults.message.not_bookmark')
end
```

お気に入りを作成、削除を実装しています。

##boardsコントローラーの追加

```ruby:boards.controller
def bookmarks
  @board = current_user.bookmark_boards.includes(:user).order(created_at: :desc)
```

ここで、先ほど作成したbookmarksアクションを使います。
book_boardsは、has_namy throughオプションにより、直接アソシエーションでお気に入りした投稿を取得しています。

##Viewの追加

今回は、お気にりした時、お気に入りされていない時、お気に入りされているのかを出し入れの３つに分けてパーシャルを作成します。

```ruby:_bookmark.html.erb
<%= link_to board_bookmarks_path(board.id), id: "js-bookmark-button-for-board-#{board.id}", method: :post do %>
  <%= icon 'far', 'star' %>
<% end %>
```

```ruby:_unbookmark.html.erb
<%= link_to board_bookmarks_path(board.id), id: "js-bookmark-button-for-board-#{board.id}", method: :delete do %>
  <%= icon 'fas', 'star' %>
<% end %>
```

```ruby:_bookmark_area.thml.erb
<% if board.bookmark_by?(current_user) %>
  <%= render 'unbookmark', board: board %>
<% else %>
  <%= render 'bookmark', board: board %>
<% end %>
```

ブックマークボタンのIDはjs-bookmark-button-for-board-掲示板IDというuniqueな要素に設定しています。

お気に入りの出し入れでは、先ほど作成したboard.bookmark_by?を用い、お気に入りボタンを出し入れしています。

```ruby:_board.html.erb
<% if current_user == board.user %>
  <div class='mr10 float-right'>
    <%= link_to edit_board_path(board.id), id: "button-edit-#{board.id}" do %>
      <%= icon 'fa', 'pen' %>
    <% end %>
    <%= link_to board_path(board.id), id: "button-delete-#{board.id}", method: :delete, data: {confirm: '本当に削除してもよろしいです'} do %>
      <%= icon 'fas', 'trash' %>
    <% end %>
  </div>
<% else %>
  <div class='mr10 float-right'>
    <%= render 'boards/bookmark_area', board: board %>
  </div> 
<% end %>
```

自分の投稿には、お気に入りボタンを出ないようにします。

```ruby:bookmarks.html.erb
<% content_for(:title, 'ブックマーク一覧') %>
<div class="container pt-3">
  <div class="row">
    <div class="col-lg-10 offset-lg-1">
      <!-- 検索フォーム -->
      <form>
        <div class="input-group mb-3"><input class="form-control" placeholder="検索ワード" type="search"/>
          <div class="input-group-append"><input type="submit" value="検索" class="btn btn-primary"/></div>
        </div>
      </form>
    </div>
  </div>
  <!-- 掲示板一覧 -->
  <div class="row">
    <div class="col-12">
      <div class="row">
        <% if @board.present? %>
          <%= render @board  %>
        <% else %>
          <P><%= t('.no_title') %></p>
        <% end %>  
      </div>
      </div>
    </div>
  </div>
</div>
```
お気に入り一覧を表示していきます。