LoginSignup
7
4

More than 3 years have passed since last update.

kaminariのlast_page?の動作に気をつけて!

Last updated at Posted at 2019-11-26

主旨と結論

連番で管理するのではなく、「前へ」「次へ」ボタンを設置しての運用です。
記事のポイントは、データが入っていない状態でページネーション機能を使う際に気をつけること
常に何らかのデータが存在する場合は気にする必要はありません。

下記を実現したい場合に参考になります。

  • 最初のページは「前へ」ボタンを消す
  • 最後のページは「次へ」ボタンを消す
  • 最初で最後のページは両方とも消す←ここで次へ問題が発生します。

なので

当初問題のlast_page?の条件分岐は、

<% unless pagenates.last_page? %>
 <a href="<%= path_to_next_page(pagenates) %>">次のページ</a>
<% end %>

のようにしていました。
これだと、データがない状態では、最初で最後のページに次へボタンが表示されてしまうのです。

最終的には下記のようにすればOK

<% unless pagenates.last_page? || paenates.out_of_range %>
 <a href="<%= path_to_next_page(pagenates) %>">次のページ</a>
<% end %>

まとめ

  • last_page?は、現在ページと総ページ数が同じ時にtrueを返し、違う時にfalseを返す。
  • データが存在しない際は、表示されるページ数(current_page)は、1となるが、total_pagesは、0となる。
  • そのため、データが存在しない場合、表示されたページが最初で最後のページとなるものの条件分岐にはout_of_rangeを併用する必要がある。

最初で最後のページの戻り値は下記の通り

メソッド データあり データなし
current_page 1 1
total_pages 1 0
last_page? true false
first_page? true true
out_of_range false true

解説

結論に書きましたが、last_page?は、最後のページの場合trueを返す、ではありません。
last_page?は、現在ページと総ページ数が同じ時にtrueを返し、違う時にfalseを返すです。

kaminariのソースを確認します。

def last_page?
  current_page == total_page
end

これだけではよくわからないので、

  • current_pageメソッド
  • total_pageメソッド

を確認します。

current_pageメソッド

    def current_page
      offset_without_padding = offset_value
      offset_without_padding -= @_padding if defined?(@_padding) && @_padding
      offset_without_padding = 0 if offset_without_padding < 0

      (offset_without_padding / limit_value) + 1
    rescue ZeroDivisionError
      raise ZeroPerPageOperation, "Current page was incalculable. Perhaps you called .per(0)?"
    end

これは、offset_without_paddingの値が0になるのでしょうか。
データがないのでpaddingなど出ないはずです。
最後には、

(offset_without_padding / limit_value) + 1

として、0をlimit_valueで割って+1しているので、基本的に戻り値は1以上であることが想像できます。

そして、下記の実行結果を確認すると、1とでていましたのでよしとしましょう。
お分かりの方コメント頂けると幸いです、、、、!!!


<%= @users.current_page %>

total_pagesメソッド

    def total_pages
      count_without_padding = total_count
      count_without_padding -= @_padding if defined?(@_padding) && @_padding
      count_without_padding = 0 if count_without_padding < 0

      total_pages_count = (count_without_padding.to_f / limit_value).ceil
      max_pages && (max_pages < total_pages_count) ? max_pages : total_pages_count
    rescue FloatDomainError
      raise ZeroPerPageOperation, "The number of total pages was incalculable. Perhaps you called .per(0)?"
    end

これも
count_without_paddingが0になるのはわかります。
あとは単純な計算とif文ですね。
戻り値がtotal_pages_countになるのはわかりますので、答えは0。(お分かりの方コメンry...)

そして、下記の結果は0でした。

<%= @users.total_pages %>

まとめ、last_page?メソッドとout_of_rangeメソッド

def last_page?
  current_page == total_page
end

データがない状態では、

  • current_pageは1
  • total_pageは0

となりますので、最初で最後のページで、last_page?を使って条件分岐してしまうと、falseが返ってきます。

そこで登場するのが

  • out_of_rangeメソッド

これは

    def out_of_range?
      current_page > total_pages
    end

なので、データがない状態ではtrueが返ってきます。
現在ページの方が合計ページを上回ることって普通ないと思うのですが、今回のようなケースを想定していたのでしょうか。

git_hub、紹介したメソッドが定義されているページ

Git_Hub/kaminari
https://github.com/kaminari/kaminari/blob/c5186f5d9b7f23299d115408e62047447fd3189d/kaminari-core/lib/kaminari/models/page_scope_methods.rb

7
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
4