Paginating_findでページネーション

Rails 2.0になってpaginateが使えなくなったので慌てて、じゃーどーすんのってところが一応落ち着いたので、まとめ(一部再掲)
そもそも今までできたpaginationができなくなるってのはどうなのよ(!)と思うのだが、何はともあれ、Rails2.0ではpaginateができなくなってプラグインでやれという判断が下ったらしい。まあ、確かにViewに依存したコードがあまりActiverecordの側にあるのも気持ちが悪い気もするが...。

paginating_findとwill_paginate

メジャー(?)なpaginationプラグインにはpaginating_findとwill_paginateがあるらしい。なかでもwill_paginateちょっと非効率なSQLがあるとか。(大きな問題ならいずれ直ると思うが)paginating_findのほうが自分的には気に入ったので使います。

paginating_findインストール

script/plugin install http://svn.cardboardrocket.com/paginating_find

その後、サーバ再起動

使い方(controller)

User.find(:all, :page=>{:size=>10, :current=>params[:page]})

のように普通のActiverecordと同じようなfindが使える。:conditionsや:limit、:orderなどのパラメータも渡せる。:pageパラメータがキモ。

使い方(view)

viewでは

<%= link_to '«前', { :page => @users.previous_page } if @stores.previous_page? %>
<%= paginating_links(@users) %>
<%= link_to '次»', { :page => @users.next_page } if @users.next_page? %>

のように使うと

<a href="/users/show/1?page=1">&#171;前</a>
<a href="/users/show/1?page=1">1</a>
2
<a href="/users/show/1?page=3">3</a>
<a href="/users/show/1?page=3">次&#187;</a>

のようなHTMLができます。

少しだけカスタマイズ

このままでは、いくつか問題ありみたい。

  • 1ページだけのリストでも「1」が表示される。
  • 装飾がやりにくい。

そこで正しい方法かどうかよくわかんないけど、ちょっとだけプラグインをいじります。
まず、/vender/plugins/paginating_find/lib/paging_helper.rbを
/lib/paging_helper.rbなどにコピーします。
その後、
paginating_links_eachを以下のように変更

def paginating_links_each(paginator, options = {})
  options = DEFAULT_OPTIONS.merge(options)

  window = ((paginator.page - options[:window_size] + 1)..(paginator.page + options[:window_size] - 1)).select {|w| w >= paginator.first_page && w <= paginator.last_page }

  html = ''

  unless paginator.last_page==paginator.first_page # この条件を追加(A)

    if options[:always_show_anchors] && !window.include?(paginator.first_page)
      html << '<span class="page_link">' + yield(paginator.first_page) + '</span>'  # (B)
      html << ' ... ' unless window.first - 1 == paginator.first_page
      html << ' '
    end

    window.each do |p|
      if paginator.page == p && !options[:link_to_current_page]
        html << '<span class="page_link">' + p.to_s + '</span>'  # (B)
      else
        html << '<span class="page_link">' + yield(p) + '</span>'  # (B)
      end
      html << ' '
    end

    if options[:always_show_anchors] && !window.include?(paginator.last_page)
      html << ' ... ' unless window.last + 1 == paginator.last_page
      html << '<span class="page_link">' + yield(paginator.last_page) + '</span>'  # (B)
    end
  end  #(A)

  html
end

(A)の部分は1ページしかないページネーションに「1」を表示しないようにする部分。
(B)の部分は装飾用の以下のタグを追加する部分。

<span class="page_link">

さらに、好みの問題だが、
ページネーション先のページが
/user/show/1?page=3
のようになっているのが嫌で、
/user/show/1/3/
のようにして(どれぐらい効果あるか知らないが)クロールされやすくするために、routes.rbに以下のような名前付きルートを付けることもできる。

  map.user ':controller/:action/:id/:page/',
  :controller => 'user',
  :action => 'show',
  :id => 0,
  :page => 0


で、この結果できたHTMLが以下のとおり。あとはCSSにpage_link用のCSSを加えるといいのかな。

<a href="/users/show/1/1">&#171;前</a>
<span class="page_link"><a href="/users/show/1/1">1</a></span>
<span class="page_link">2</span>
<span class="page_link"><a href="/users/show/1/3">3</a></span>
<a href="/users/show/1/3">次&#187;</a>


CSSに以下の記述を加えると

.page_link, .page_link a {
  color: #000;
}


出来上がり。


こんなもんで以下がでしょう。お役に立ちますでしょうか?

paginating_findで「Unknown key(s): page」が出る

最初にpaginating_findをインストールして開発を始めると、paginating_findで「Unknown key(s): page」が出ることがあるんですが、これではまっちまいました。これはプラグインインストール後にサーバ再起動で解決だそうです。その発想はなかったわー。
サンクスどっかの国のナイスガイ。

http://www.bobdogz.com/blog/show/22-paginating_find-plug-in

paginating_findでreverse/reverse!

通常のActiveRecordでfind(:all...)したオブジェクトは、Arrayだと思うんだけど、paginating_findつかってfindした結果はArrayではなくて、PagingEnumeratorという物体になっているみたいで、Arrayクラスのreverse、reverse!のような一部の便利メソッドが使えません。代わりに、PagingEnumeratorクラスには、to_aメソッドがあります。to_aしてからreverse/reverse!すれば今までどおり配列の順番を変えたりといった操作ができます。