WeBlog

Webに関する情報を中心に発信してるブログ

kaminariでページネーションを作成する

ページネーションとは?

ページネーションは、商品や掲示板などが一覧で表示されているページで、次のページや前のページに行くリンクのことです。

下記の画像のようなものがページネーションです。

f:id:weblog_tec:20210503092317p:plain

どうしてページネーションが必要かと言うよ、1つのページに何百件とか表示してしまうと、ユーザーも自分が求める商品を探すのも大変です。

また、1ページに表示するデータが多くなり、ブラウザに表示されるまでに時間がかかります。

ページネーションを使えば、1ページする表示する数を制御できるので、素早く画面に表示して、ユーザーの使い勝手も良くなります。

ページネーションの仕組み

f:id:weblog_tec:20210503092408j:plain

上のようなテーブルが存在したとします。

掲示板の情報と、その掲示板を誰が作成したのかの情報をもったテーブルです。

例えば1つのページに最大で3つの掲示板を表示するとします。

ページングは1つのページに掲示板をいくつか表示します。

次のページにいった場合は次の掲示板をいくつか表示します。

上のテーブルで言うと、1ページ目はidカラムが「1、2、3」になっている3つの掲示板をテーブルから取得して画面に表示します。

2ページではidカラムが「4、5、6」になっている3つの掲示板をテーブルから取得して画面に表示します。

3ページ目ではidカラムが「7、8、9」になっている3つの掲示板をテーブルから取得して画面に表示します。

4ページ目は1つしかないので掲示板を1つ取得して画面に表示します。

こんな感じでテーブルからレコードを取得する必要があります。

最大でページングを5つ表示するとした場合、上の表では4ページ分しか掲示板がないので4ページ分だけページングを表示します。

1ページ目を取得してみる

これをSQL文で表してみると下記のようになります。

# 全レコードのうち0番目から、3レコード分取得する
SELECT * FROM boards LIMIT 3 OFFSET 0;

「0番目から」をOFFSET 0が表していて、「3レコード」をLIMIT 3が表しています。

レコードの1番最初は「0番目」から始まります。

なので「0~2番目」までの「3レコード」を取得します。

このようにSQLで取得すれば、先ほどの表のidカラムが「1、2、3」のレコードを取得できます。

2ページ目を取得してみる

次に2ページ目を取得してみます。

# 全レコードのうち3番目から、3レコード分取得する
SELECT * FROM boards LIMIT 3 OFFSET 3;

2ページ目を取得するときは「3番目から3レコード」取得する必要があります。

上記のように書けばidカラムが「4、5、6」のレコードを取得できます。

ここで注目しないといけないのは、OFFSET 3が変わるということです。

なので、現在何ページ目にいるのかが重要になります。

現在何ページ目いるのかによってSQLOFFSETを動的に変える必要があります。

どうやってOFFSETを変えるのか?

OFFSET部分を動的に変えるためにGETパラメーターを使います。

下記のような感じでURLの後ろに?key = value付与してあげます。

valueの部分には現在のページを与えます。

このようなURLを各ページネーションのリンクに埋め込めば良いです。

# URLの後ろに?key = valueの形でパラメーターを渡す

http://localhost:3000/boards?page=3

このパラメーターのpage=3Railsではparamsという変数の中に入ってくるので、

params[:page]としてあげることでを取得できます。

ではSQLのOFFSET部分をどうやって動的に変更するかですが、下記のようになります。

# 送られてきたパラメーターを変数に代入
page = params[:page]

# 変数から1を引いた値に3かける
SELECT * FROM boards LIMIT 3 OFFSET (page - 1)*3

現在ページが3ページ目(?page = 3)だった場合です。

このGETパラメーターをサーバー側のコントローラーで受け取ってあげて、現在のページ数を元にSQL文を作ります。

SQL文のLIMIT 3は変わらないですが、OFFSETの部分は動的に変更する必要があります。

現在ページから−1してあげて、掛けるしてあげれば良いです。

そうすれば1ページ目の時は(1−1)*3になるので、下記のようなSQLになります。

LIMIT 3 OFFSET 0になるので0番目から3レコード取得できます。

# URL
http://localhost:3000/boards?page=1

# pageに1が入る
page = params[:page]

# pageに当てはめる
SELECT * FROM boards LIMIT 3 OFFSET (page - 1)*3

# pageが1になるので、(1−1)*3になる
SELECT * FROM boards LIMIT 3 OFFSET (1 - 1)*3

# こんなSQLになる
SELECT * FROM boards LIMIT 3 OFFSET 0

2ページ目の時は書きのようになります。

LIMIT 3 OFFSET 3になるので、3番目から3レコード取得できます。

# URL
**http://localhost:3000/boards?page=2

# pageに1が入る
page = params[:page]

# pageに当てはめる
SELECT * FROM boards LIMIT 3 OFFSET (page - 1)*3

# pageが1になるので、(1−1)*3になる
SELECT * FROM boards LIMIT 3 OFFSET (2 - 1)*3

# こんなSQLになる
SELECT * FROM boards LIMIT 3 OFFSET 3

このようにすれば、先ほどの表の通りにレコードを取得できます。

ページネーションのリンクを動的にする

先ほど、SQLOFFSET部分を動的に変更するためにパラメーターから値を取得して動的に変えることができました。

では、ページネーションのリンクはどうすれば動的にできるでしょうか?

f:id:weblog_tec:20210503092529j:plain

例えば、現在のページが3ページ目だった場合を考えてみます。

次のページや前のページへ飛ぶ際のリンクを動的に生成しするにはどうすればいいでしょうか。

4ページ目へのリンクは、現在のページにプラス「1」してあげるといいです。

それを押せば4ページ目という形でサーバーにGETパラメーターで情報が送信されます。

2ページ目のリンクは現在のページから「−1」してあげればいいです。

それを押せば2ページ目という形でサーバーにGETパラメーターで情報が送信されます。

これをコードに落とし込んでみます。

@currentPage = params[:page] # 現在ページ

@minPage = @currentPage - 2 # 最小のページ
@maxPage = @currentPage + 2 # 最大のページ

<% (@minPage..@maxPage).each do |i| %>
  <ul>    
    <li>  
      <a href="**http://localhost:3000/boards?page=**"<%= i %>> 
 <%= i %> </a> 
    </li>
  </ul>
<% end %>

each文を使ってループで回して、aタグ生成しています。

each文を使うには最初と最後を決めないといけません。

なので、現在のページが3ページ目だった場合は

$currentPage =が現在のページでになります。

$minPage が最小のページで計算するとになります。

$maxPageが最大のページで計算するとになります。

上記のように「1〜5」までループでして、それをURLのパラメーターに埋め込むことで動的に作ることができます。

これで1〜5までにページングのリンクを作成できます。

しかし、これでは不十分です。

f:id:weblog_tec:20210503092604j:plain

@currentPage = params[:page] # 現在ページ

@minPage = @currentPage - 2 # 最小のページ
@maxPage = @currentPage + 2 # 最大のページ

<% (@minPage..@maxPage).each do |i| %>
  <ul>    
    <li>  
      <a href="**http://localhost:3000/boards?page=**"<%= i %>> <%= i %> </a> 
    </li>
  </ul>
<% end %>

何が不十分なのかですが、上記のコードでは、現在のページが「3ページ目」だった場合は大丈夫です。

しかし、今回は「総ページ数が最大で5ページ」までのページングを表示すると仮定します。

現在のページが4ページ目だった場合に、先ほどのeach文に当てはめると@minPageが2で、@maxPageが6になります。

なので、「2〜6」のページングが表示されてしまいます。

図にすると上記のようになります。

しかし、本当は「1〜5」で表示して「4」が現在ページとして赤くなって欲しいです。

これを解決するには、現在のページと総ページ(最終ページ)から「−1」した数値を比べて同じだった場合は、現在のページ「+1」項目のページングを出すようにします。

要は現在ページが「4ページ目」だった場合は、「4」と「総ページ - 1」が比べて同じ場合は、

現在ページである「4」に「1」足した「5」まで表示するということです。

そのためには、「総ページ数」をどうやって出すかが大事になります。

総ページ数を計算する

f:id:weblog_tec:20210503092642j:plain

総ページ数は「全レコード数 ➗ ページごとの表示レコード数」で割る事で求めることができます。

なのでテーブルに保存されている全レコードが「100件」で、1ページごとに表示するレコード数が「20件」だった場合は、

総ページ数は「100➗ 20 = 5 」になります。

もしテーブルに保存されている全レコード数が「70件」で、1ページごとに表示するレコード数が「20件」だった場合は、「3.5」になります。

その時は繰り上げて、総ページ数を「4」にします。

現在ページが最終ページより1ページ手前の場合

話を戻して、「総ページ数が5ページ、現在4ページ目」の時は、最初のコードでは2〜6ページが表示される不具合がありました。

その時の対策は総ページ数が5ページで4ページ目にきた時に、6ページ目を表示しないようにして「1〜5」ページを表示するようにします。

この処理をコードにすると下記のようになります。

# if文を新たに追加
<% if @currentPage == @totalPage - 1 %>
     @minPage = @currentPage - 3 # 最小ページは「4 - 3 = 1」
     @maxPage = @currentPage + 1 # 最大ページは「4 + 1 = 5」
<% else %>
    @minPage = @currentPage - 2 # 最小のページ
    @maxPage = @currentPage + 2 # 最大のページ
<% end %>

# HTML
<% (@minPage..@maxPage).each do |i| %>
  <ul>    
    <li>  
      <a href="**http://localhost:3000/boards?page=**"<%= i %>> <%= i %> </a> 
    </li>
  </ul>
<% end %>

If~else文に変更しました。

if文の条件が新しく加わっています。

else文の中身は前回と同じです。

総ページ数が5で、現在のページが4ページ目だった時は、if文の条件に当てはまります。

@currentPageは現在のページ。今回の不具合で言うと「4」になります。

@totalPageは「全レコード数 ➗ ページ毎の表示項目数」で計算した値です。

この例では「5」です。

これでもまだ不足している部分があります。

総ページ数が「5ページ」で、現在のページが「5ページ目」だった場合

f:id:weblog_tec:20210503092758j:plain

# if文を新たに追加
<% @currentPage == @totalPage - 1 %>
     @minPage = @currentPage - 3 # 最小ページは「4 - 3 = 1」
     @maxPage = @currentPage + 1 # 最大ページは「4 + 1 = 5」
<% else %>
    @minPage = @currentPage - 2 # 最小のページ
    @maxPage = @currentPage + 2 # 最大のページ
<% end %>

# HTML
<% (@minPage..@maxPage).each do |i| %>
  <ul>    
    <li>  
      <a href="**http://localhost:3000/boards?page=**" <%= i %>> <%= i %> </a> 
    </li>
  </ul>
<% end %>

何が不十分かというと、総ページ数が5ページで、現在のページが「5ページ目」だった場合、前回のコードだとif〜else文elseの処理に入ってしまいます。

elseの処理に入ると、「3〜7」のページングが表示されてしまいます。

本当は、現在5ページ目だった場合は、「1~5」までページングを表示したいです。

# if文を新たに追加
<%  @currentPage == @totalPage %>
     @minPage = @currentPage - 4 # 最小ページは「5 - 4 = 1」
     @maxPage = @currentPage     # 最大ページは「5」
<% elsif @currentPage == @totalPage - 1 %>
     @minPage = @currentPage - 3 # 最小ページは「4 - 3 = 1」
     @maxPage = @currentPage + 1 # 最大ページは「4 + 1 = 5」
<% else %>
    @minPage = @currentPage - 2 # 最小のページ
    @maxPage = @currentPage + 2 # 最大のページ
<% end %>

# HTML
<% (@minPage..@maxPage).each do |i| %>
  <ul>    
    <li>  
      <a href="**http://localhost:3000/boards?page=#{i}**"> <%= i %> </a> 
    </li>
  </ul>
<% end %>

この不具合の対策として新たにif文を追加して条件を書きます。

このif文を追加すると、「現在のページが5ページ目」で「総ページが5ページ」だった場合に、

if文の条件に入って、要件通りeach文を使って「1〜5」のページングが表示されます。

els if文else文の処理は前回と同じです。

これでもまだ不十分です。

総ページ数が5ページで、現在のページが2ページ目だった場合

f:id:weblog_tec:20210503092827j:plain

# if文を新たに追加
<% @currentPage == @totalPage %>
     @minPage = @currentPage - 4 # 最小ページは「5 - 4 = 1」
     @maxPage = @currentPage     # 最大ページは「5」
<% elsif @currentPage == @totalPage - 1 %>
     @minPage = @currentPage - 3 # 最小ページは「4 - 3 = 1」
     @maxPage = @currentPage + 1 # 最大ページは「4 + 1 = 5」
<% else %>
    @minPage = @currentPage - 2 # 最小のページ
    @maxPage = @currentPage + 2 # 最大のページ
<% end %>

# HTML
<% (@minPage..@maxPage).each do |i| %>
  <ul>    
    <li>  
      <a href="**http://localhost:3000/boards?page=#{i}**"> <%= i %> </a> 
    </li>
  </ul>
<% end %>

総ページ数が5で、現在のページが2ページ目だった場合、先ほどのelse文の処理に入ってしまい、

「0〜4」のページングが表示されてしまいます。

でも本当は「1~5」のページングを表示したいです。

# if文を新たに追加
<% if @currentPage == @totalPage %>
     @minPage = @currentPage - 4 # 最小ページは「5 - 4 = 1」
     @maxPage = @currentPage     # 最大ページは「5」
<% elsif @currentPage == @totalPage - 1 %>
     @minPage = @currentPage - 3 # 最小ページは「4 - 3 = 1」
     @maxPage = @currentPage + 1 # 最大ページは「4 + 1 = 5」
<% elsif @currentPage == 2 %>
     @minPage = @currentPage - 1 # 最小ページは「2 - 1 = 1」
     @maxPage = @currentPage + 3 # 最大ページは「2 + 3 = 5」
<% else %>
     @minPage = @currentPage - 2 # 最小のページ
     @maxPage = @currentPage + 2 # 最大のページ
<% end %>

# HTML
<% (@minPage..@maxPage).each do |i| %>
  <ul>    
    <li>  
      <a href="**http://localhost:3000/boards?page=#{i}**"> <%= i %> </a> 
    </li>
  </ul>
<% end %>

解決策として、elsif文を新しく追加している。

追加したのはelsif(@currentPage == 2)の条件です。

他は全く同じです。

これで現在のページが2ページ目だった場合は、each文を使って「1〜5」のページングが表示されます。

しかし、これでもまだ不足しています。

総ページ数が5ページで、現在のページが1ページ目だった場合

f:id:weblog_tec:20210503092855j:plain

# if文を新たに追加
<%  if @currentPage == @totalPage %>
     @minPage = @currentPage - 4 # 最小ページは「5 - 4 = 1」
     @maxPage = @currentPage     # 最大ページは「5」
<% }elsif @currentPage == @totalPage - 1 %>
     @minPage = @currentPage - 3 # 最小ページは「4 - 3 = 1」
     @maxPage = @currentPage + 1 # 最大ページは「4 + 1 = 5」
<% elsif @currentPage == 2 %>
     @minPage = @currentPage - 1 # 最小ページは「2 - 1 = 1」
     @maxPage = @currentPage + 3 # 最大ページは「2 + 3 = 5」
<% else %>
     @minPage = @currentPage - 2 # 最小のページ
     @maxPage = @currentPage + 2 # 最大のページ
<% end %>

# HTML
<% (@minPage..@maxPage).each do |i| %>
  <ul>    
    <li>  
      <a href="**http://localhost:3000/boards?page=#{i}**"> <%= i %> </a> 
    </li>
  </ul>
<% end %>

総ページ数が5ページで、現在のページが1ページ目だった場合、前回までのコードではelse文に入ってしまい、「−1〜3」まで表示されてしまいます。

でも本当は「1~5」まで表示したいです。

# if文を新たに追加
<% @currentPage == @totalPage %>
     @minPage = @currentPage - 4 # 最小ページは「5 - 4 = 1」
     @maxPage = @currentPage     # 最大ページは「5」
<% elsif @currentPage == @totalPage - 1 %>
     @minPage = @currentPage - 3 # 最小ページは「4 - 3 = 1」
     @maxPage = @currentPage + 1 # 最大ページは「4 + 1 = 5」
<% elsif @currentPage == 2 %>
     @minPage = @currentPage - 1 # 最小ページは「2 - 1 = 1」
     @maxPage = @currentPage + 3 # 最大ページは「2 + 3 = 5」
<% elsif @currentPage == 1 %>
     @minPage = @currentPage     # 最小ページは「1」
     @maxPage = @currentPage + 4 # 最大ページは「1 + 4 = 5」
<% else %>
     @minPage = @currentPage - 2 # 最小のページ
     @maxPage = @currentPage + 2 # 最大のページ
<% end %>

# HTML
<% (@minPage..@maxPage).each do |i| %>
  <ul>    
    <li>  
      <a href="**http://localhost:3000/boards?page=#{i}**"> <%= i %> </a> 
    </li>
  </ul>
<% end %>

この対策として、elsif文を新しく追加しています。

追加したのはelsif(@currentPage == 1)の条件分岐です。

他は全く同じです。

上記のようにすれば、現在のページが1ページ目の場合は、追加したelsif文に入って「1〜5」のページングが表示されます。

さらに問題があります。

表示する最大のページネーションの数より総ページ数が少ないパターンの場合

f:id:weblog_tec:20210503092928j:plain

# if文を新たに追加
<% if @currentPage == @totalPage %>
     @minPage = @currentPage - 4 # 最小ページは「5 - 4 = 1」
     @maxPage = @currentPage     # 最大ページは「5」
<% elsif @currentPage == @totalPage - 1 %>
     @minPage = @currentPage - 3 # 最小ページは「4 - 3 = 1」
     @maxPage = @currentPage + 1 # 最大ページは「4 + 1 = 5」
<% elsif @currentPage == 2 %>
     @minPage = @currentPage - 1 # 最小ページは「2 - 1 = 1」
     @maxPage = @currentPage + 3 # 最大ページは「2 + 3 = 5」
<% elsif @currentPage == 1 %>
     @minPage = @currentPage     # 最小ページは「1」
     @maxPage = @currentPage + 4 # 最大ページは「1 + 4 = 5」
<% else %>
     @minPage = @currentPage - 2 # 最小のページ
     @maxPage = @currentPage + 2 # 最大のページ
<% end %>

# HTML
<% (@minPage..@maxPage).each do |i| %>
  <ul>    
    <li>  
      <a href="**http://localhost:3000/boards?page=#{i}**"> <%= i %> </a> 
    </li>
  </ul>
<% end %>

例えば、登録してある掲示板が少なくて、計算した結果、総ページ数が2ページで、現在のページが2ページ目だった場合です。

最大で表示項目数(表示する最大のページネーションの数)を5ページと設定していますが、テーブルに登録されているデータが少なくて計算したら総ページ数が「2ページ、3ページ、4ページ」だった場合です。

表示項目数(表示する最大のページネーションの数)よりも、総ページ数が少ないと言うことです。

前回までのコードだと、if文の条件に入ってしまい、「−2〜2」まで表示されてしまいます。

本当は、「1~2」までのページングを表示したいです。

# if文を新たに追加
<% @currentPage == @totalPage %>
     @minPage = @currentPage - 4 # 最小ページは「5 - 4 = 1」
     @maxPage = @currentPage     # 最大ページは「5」
<% elsif(@currentPage == @totalPage - 1 %>
     @minPage = @currentPage - 3 # 最小ページは「4 - 3 = 1」
     @maxPage = @currentPage + 1 # 最大ページは「4 + 1 = 5」
<% elsif @currentPage == 2 %>
     @minPage = @currentPage - 1 # 最小ページは「2 - 1 = 1」
     @maxPage = @currentPage + 3 # 最大ページは「2 + 3 = 5」
<% elsif @currentPage == 1 %>
     @minPage = @currentPage     # 最小ページは「1」
     @maxPage = @currentPage + 4 # 最大ページは「1 + 4 = 5」
<% elsif @totalPage < @pageColumn %>
     @minPage = 1          # 最小ページは「1」
     @maxPage = @totalPage # 最大ページはtotalページ
<% else %>
     @minPage = @currentPage - 2 # 最小のページ
     @maxPage = @currentPage + 2 # 最大のページ
<% end %>

# HTML
<% (@minPage..@maxPage).each do |i| %>
  <ul>    
    <li>  
      <a href="**http://localhost:3000/boards?page=#{i}**"> <%= i %> </a> 
    </li>
  </ul>
<% end %>

その場合の対策として、「1〜総ページ数」まで表示するようにします。

@totalPageは、総ページ数を表しています。

図で言うと「2」です。

@pageColumnが、表示項目数(表示する最大のページネーションの数) を表しています。

今回は@pageColumn = 5と設定しています。

@pageColumnは自分が表示項目数を最大で何ページにしたいかによります。

これで現在ページが2ページで総ページが2だった場合でも、新しく追加したelsif文に入って問題なく動作します。

ページネーションのイメージはこんな感じです。

実際このコードでは足りないことが多いですし、きちんと動きません。

ページネーションのイメージとしてこんな感じです。

ページネーションで大切なこと

・現在のページ数

→ 自分が今何ページ目にいるのか?

・総ページ数

→最大で5つのページングを表示できるが、実際はいくつのページングが表示されるのか??

最大で5つのページングを表示するけど、データ数が少ないから3ページ分のページングしか表示できない場合は、総ページ数は3になります。

総ページ数が「6、7、8・・・」になる場合もあります。

・ページングの表示項目数

→ 最大で何ページ分のページングを表示するか?(ex. 最大5つ)

・一覧表示コンテンツ数

→1つのページにいくつのコンテンツを表示するか??

これを使って、総ページ数を計算しているから必要になる。(ex. 20件)

Rails】kaminariを使ってページネーション

Railsにはkaminariというページネーションを簡単に作成できるgemがあります。

このgemを使っていきます。

https://github.com/kaminari/kaminari#configuring-kaminari

kaminariをインストールする

まずはkaminariと言うgemをインストールする必要があります。

Gemfileにkaminariを書いてbundle installします。

# Gemfile
gem 'kaminari'

これでgemを使える状態になります。

kaminariの使い方

まずは、コントローラー側で下記のように書きます。

# コントローラーのアクション
class ProductsController < ApplicationController
  def index
    @products = Product.all.page(params[:page]).per(20)
  end
end

.page()メソッドがkaminariをインストールすると、モデルクラスのメソッド(クラスメソッド)として使えるようになります。

このメソッドは、ページネーションにおける「何番目か?」を指定します。

なのでSQLでいうOFFSET部分を動的に生成してくれます。

先ほどのページネーションのSQL文が下記になります。

# 送られてきたパラメーターを変数に代入
page = params[:page]

# 変数から1を引いた値に3かける
SELECT * FROM boards LIMIT 3 OFFSET (page - 1)*3

このSQLをみると、OFFSET部分に現在のページ(page)が渡っていると思います。

なので、.page()メソッドの引数に現在何ページ目なのかの情報を渡してあげる必要があります。

params[:page]はビューのリクエストリンクに?page=1というような現在ページのパラメーターが設定されるので、その値をparams[:page]で取得して、.page()メソッドに渡してあげています。

そうすることで、SQLを作ってくれています。

perメソッドもpageメソッドと同様、kaminariというgemをインストールすることで利用できるメソッドです。

perメソッドの引数には1ページあたりに表示する件数を指定します。

なので、.per(20)と言うのが、1つの画面に20個の商品を表示する設定です。

perメソッドがLIMIT 33に当たる部分や、OFFSET (page - 1)*3*3に該当します。

ビュー側の書き方

# ビューファイル
<div class="row">
    <div class="col-12">
      <!-- ページネーション -->
      <%= paginate @products %>
    </div>
  </div>

ビュー側では、<%= paginate @products %>と書くだけです。

これはpaginateと言うメソッドです。

このメソッドもkaminariをインストールすると使うことができます。

paginateメソッドの引数にはコントローラー側で作ったインスタンス変数を引数として渡します。

なので先ほどのコードで言うと

・ProductsController#indexで書いた@products

・ビューファイルで書いた<%= paginate @products %>@products

この2つは同じ名前にしないといけません。

f:id:weblog_tec:20210503093014p:plain

# HTMLの表示
<nav class="pagination" role="navigation" aria-label="pager">
    
    <span class="page current">
    1
  </span>

  <span class="page">
    <a rel="next" href="/boards?page=2">2</a>
  </span>

  <span class="page">
    <a href="/boards?page=3">3</a>
  </span>

</nav>

画像の要はページネーションが作成されて、HTMLは上記のように表示されます。

これだけでページネーションを実現できます。

<div class='dropdown-menu dropdown-menu-right'>
     <%= link_to '商品一覧', products_path, class: 'dropdown-item' %>
</div>

ちなみにですが、ヘッダーに「商品一覧画面」に行くためのリンクとかがあると思います。

そのURLをhttp://localhost:3000/products?page=1のように?page=1を付与する必要はありません。

上記ではパスがproducts_pathとなっています。

なので、http://localhost:3000/productsみたいなURLが生成されます。

でも本当はhttp://localhost:3000/products?page=1みたいに?page=1を後ろにつけてあげたいです。

そのためにproducts_path(page: 1)みたいに書いてあげると、現在のページのパラメーターを持たせることができます。

ただ持たせる必要はありません。

持たせない場合をbinding.pryで止めてみます。

       6: def index
    7:   @boards = Board.all.includes(:user).order(created_at: :desc).page(params[:page])
    8:   binding.pry
 => 9: end

[1] pry(#<BoardsController>)> params[:page]
=> nil
[2] pry(#<BoardsController>)> Board.all.page(params[:page]).per(20)
  Board Load (0.3ms)  SELECT  "boards".* FROM "boards" LIMIT ? OFFSET ?  [["LIMIT", 20], ["OFFSET", 0]]

止めてみると、params[:page]nilになっています。

当然http://localhost:3000/productsなURLなので、params[:page]はないです。

でも、Board.all.page(params[:page]).per(20)とすると、OFFSETは「0」でSQLが走っています。

要はnilの場合は、page()メソッドが中で勝手に1ページ目と判定してくれるので、(1 - 1)*20で0になります。

なので?page=1のパラメーターを設定する必要はありません。

設定ファイルを作成しカスタマイズする

kaminariでは設定ファイルを作成することができます。

先ほど、コントローラー側で.per(20)と直書きで20を書いていました。

しかし、いろんなページでページネーションを作成するときに.per(20)をいろんなとこで書くと、

後で.per(10)にしたくなった場合、per()メソッドが書いたあるとこを全部書き直さないといけません。

大変です。

なので設定ファイルを作成して、そこで共通の設定を書きます。

下記コマンドで設定ファイルを作成します。

% rails g kaminari:config

Running via Spring preloader in process 23707
      create  config/initializers/kaminari_config.rb

そうすると、config/initializers/配下にkaminari_config.rbと言うファイルができます。

中身は下記のようになっています。

# config/initializers/kaminari_config.rb
**Kaminari.configure do |config|
  config.default_per_page = 20 # 1つのページに表示する数
  # config.max_per_page = nil  # 1ページに表示する最大数
  # config.window = 4          # 現在のページから、左右何ページ分のリンクを表示させるか
  # config.outer_window = 0    # 最初と最後のリンクにいるときに、左右何ページ分のリンクを表示させるか
  # config.left = 0            
  # config.right = 0
  # config.page_method_name = :page
  # config.param_name = :page
  # config.max_pages = nil
  # config.params_on_first_page = false
end**

一番上のconfig.default_per_page 20と設定すると、

per(20)と言うメソッドを省略できます。

なので下記のようにコントローラーのアクションで書けます。

per(20)は消していいです。

# コントローラーのアクション
class ProductsController < ApplicationController
  def index
    @products = Product.all.page(params[:page])
end

https://qiita.com/you8/items/df68aaee3010e282d1ae

https://github.com/kaminari/kaminari#general-configuration-options

http://nekorails.hatenablog.com/entry/2018/10/15/005146

自分でページネーションの見た目を変える

kaminariで表示させるページネーションはビューの中でcssを当てる事は出来ません。

要は<%= paginate @products %>のコードを書いてもHTMLがどう表示されるのかわからないですし、どのページにいるのかによってもHTMLが変わります。

なので、見た目を変えたい場合はターミナルで下記のコマンドを実行します。

ターミナル

% rails g kaminari:views default

https://github.com/kaminari/kaminari#customizing-the-pagination-helper

するとapp/viewsフォルダにkaminariディレクトリが追加されます。

そのフォルダの中に〇〇.html.erbと言うページネーションの部分だけのHTMLが記述されています。

なので、このファイルのHTML部分にclass属性やid属性を付与して、CSSファイルでそのクラス属性やid属性にCSSを当てる記述を書けば、デザインを自分でカスタマイズできます。

要は、自分でデザインをカスタマイズしたいときは上記のコードをターミナルで実行します。

https://pikawaka.com/rails/kaminari

CSSフレームワークを使ってデザインを当てる

さっきは自分でデザインをカスタマイズするときの話でした。

しかし、bootstrapなどのcssフレームワークを使っている場合は別のコマンドを打ちます。

その時は下記のようなコマンドを打ちます。

% rails g kaminari:views [CSSフレームワーク名]

CSSフレームワーク名」はそれぞれのフレームワークの名前に指定します。

bootstrap4を使っている場合は下記のコマンドになります。

% rails g kaminari:views bootstrap4

そうすると、app/viewsフォルダにkaminariディレクトリが追加されます。

その中に〇〇.html.erbと言うページネーションのHTMLが記述されています。

例えば中身は下記のような感じです。

<li class="page-item">
  <%= link_to_unless current_page.first?, raw(t 'views.pagination.first'), url, remote: remote, class: 'page-link' %>
</li>

class属性が元から付与されています。

このclass属性にBootstrapのデザインが反映されます。

なので、このclass属性はBootstrapのデザインを当てるためにclass属性です。

他にもいろんなCSSフレームワークがあるので、確認してください。

https://github.com/kaminari/kaminari#themes

https://github.com/amatsuda/kaminari_themes

kaminariを日本語化設定する

kaminariのページネーションはデフォルトで英語です。

なので、次のページにいくリンクなどは英語で表示されます。

日本語にするには、config/localesフォルダ配下にkaminari_ja.ymlというファイルを作成し、下記のようなコピペして貼り付ければ良いです。

ja:
  views:
    pagination:
      first: "&laquo; 最初"
      last: "最後 &raquo;"
      previous: "&lsaquo; 前"
      next: "次 &rsaquo;"
      truncate: "..."
  helpers:
    page_entries_info:
      one_page:
        display_entries:
          zero: ""
          one: "<strong>1-1</strong>/1件中"
          other: "<strong>1-%{count}</strong>/%{count}件中"
      more_pages:
        display_entries: "<strong>%{first}-%{last}</strong>/%{total}件中"