【Rails】kaminari&ransackでページネーションと検索機能を実装

【ページネーション】
1ページあたり20件に設定。
スタイルはBootstrap4で実装します。

【検索機能】
投稿一覧から検索とブックマーク済み投稿一覧から検索の2パターン実装していきます。
検索条件はタイトルか本文の部分一致とします。

【事前準備】
・ブックマーク機能を実装しておいてください。
参考資料：[【Rails】ブックマーク(お気に入り)機能](https://qiita.com/hiro266/items/8a0374155cd18adbd7cf)
・bootstrap4を使用できるように設定しておいてください。
参考資料：[BootstrapをRailsに導入してみよう！徹底解説！](https://tech-boost.jp/magazine/Programming-languages/ruby/1073142)

# 環境
`Rails 5.2.3`
`mysql 5.7.28`
`gem kaminari`
`gem ransack`


# 実装
それでは実装していきます。

## gemの追加
`gem kaminari`
`gem ransack`

Gemfileに追加し、`bundle install`

```ruby:Gemfile
gem 'kaminari'
gem 'ransack'
```

```:ターミナル
$ bundle install
```

## ページネーション実装
まずはページネーションから実装していきます。

ターミナルで`kaminari`の設定ファイルを自動生成するコマンドを入力します。

```:ターミナル
$ rails g kaminari:config
```
これで`kaminari`の動作を変えるために必要な設定ファイルを生成できます。

次にページネーション用のビューファイルをを自動生成するコマンドを入力します。

```:ターミナル
$ rails g kaminari:views bootstrap4
```
これでbootstrap4のページネーションのデザインに合わせたビューファイルが自動で生成されます。

次に1ページあたりのデータ取得数を書き換えます。
デフォルトでは25件のデータを取得する設定になっています。

```ruby:config/initializers/kaminari_config.rb
# frozen_string_literal: true
Kaminari.configure do |config|
  config.default_per_page = 20 # 25から20に書き換え
  # config.max_per_page = nil
  # config.window = 4
  # config.outer_window = 0
  # config.left = 0
  # config.right = 0
  # config.page_method_name = :page
  # config.param_name = :page
  # config.params_on_first_page = false
end
```

次はコントローラーにpageメソッドを追加していきます。

```ruby:board_controller.rb
def index
  @boards = Board.includes(:user).page(params[:page])
end

def bookmarks
  @boards = current_user.bookmark_boards.includes(:user).page(params[:page])                  
end
```

`kaminari`をインストールしたことでモデルクラスに対して`pageメソッド`が使用できるようになります。
先ほど設定した1ページあたりのデータ取得件数を引数の`params[:page]`に格納し、ビューで表示するようにします。

この時点で1ページ目には20件のデータが表示されているはずです。
2ページ目以降も見れるようにリンクを貼ります。
リンクを貼りたい箇所に`<%= paginate @boards %>`を記述すればOKです。
[![Image from Gyazo](https://i.gyazo.com/f85499ac60d86ad3676e89f391621b9e.png)](https://gyazo.com/f85499ac60d86ad3676e89f391621b9e)
サーバーを再起動して、このようなリンクが表示されていれば実装完了です。

### 日本語化

実装は完了ですが英語の部分を日本語化しておきます。

デフォルトのロケール(言語)を日本語にします。

```ruby:config/application.rb
module アプリ名
  class Application < Rails::Application
  # 追記↓
    config.i18n.default_locale = :ja
```

次に日本語訳を設定するファイルを作成・記述していきます。
`config/locales/ja.yml`を作成し任意の日本語を記述していきます。

```ruby:config/locales/ja.yml
ja:
  views:
    pagination:
      first: 最初
      last: 最後
      previous: 前
      next: 次
      truncate: ...
```
これで設定が完了したのでサーバーを再起動したら反映されているはずです。

## 検索機能実装

### controllerを編集

```ruby:boards_controller.rb
def index
    @q = Board.ransack(params[:q]) # 検索オブジェクト作成
    @boards = @q.result.includes(:user).page(params[:page]) # 検索結果(検索しなければ全件取得)
end

def bookmarks
    @q = current_user.bookmark_boards.ransack(params[:q]) # 検索オブジェクト作成
    @boards = @q.result.includes(:user).page(params[:page]) # 検索結果(検索しなければ全件取得
end
```

### viewを編集(検索フォーム)

```ruby:boards/index.html.erb
<!-- 検索フォーム --> 
<%= render 'search_form', url: boards_path, q: @q %>
```

```ruby:boards/bookmarks.html.erb
<!-- 検索フォーム -->
<%= render 'search_form', url: bookmarks_boards_path, q: @q %>
```

```ruby:_search_form.html.erb
<%= search_form_for q, url: url do |f| %>
  <div class="input-group mb-3">
    <%= f.search_field :title_or_body_cont, class: "form-control", placeholder: '検索ワード' %>
    <div class="input-group-append">
      <%= f.submit "検索", class: "btn btn-primary" %>
    </div>
  </div>
<% end %>
```

`<%= search_form_for q, url: url do |f| %>`のurlを一覧とブックマーク一覧のurlで指定することでその範囲内から検索をすることができます。

`<%= f.search_field :title_or_body_cont,...`でtitleカラムとbodyカラムのどちらからでも検索が部分的に一致すれば表示されます。
条件はこちらのサイトを参考にしました。
[Ransackで簡単に検索フォームを作る73のレシピ](http://nekorails.hatenablog.com/entry/2017/05/31/173925)

### viewを編集(検索結果一覧)

```ruby:index.html.erb,bookmarks.html.erb共通
<!-- 掲示板一覧 -->
<% if @boards.any? %>
  <div class="row d-flex">
    <%= render @boards %>
  </div>
<% else %>
  <h1><%= '検索結果がありません。' %></h1>
<% end %>
<%= paginate @boards %>
```

これでインスタンスにデータが存在すれば一覧表示され、なければ検索結果がありませんと表示されます。

# まとめ

わりとすんなり実装できたかなと思います。
詰まったとことしては検索範囲を全boardとbookmark_boardで範囲分けする部分でしたが、urlを検索範囲に指定することで解決しました。