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">«前</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">次»</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">«前</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">次»</a>
CSSに以下の記述を加えると
.page_link, .page_link a { color: #000; }
こんなもんで以下がでしょう。お役に立ちますでしょうか?
paginating_findで「Unknown key(s): page」が出る
最初にpaginating_findをインストールして開発を始めると、paginating_findで「Unknown key(s): page」が出ることがあるんですが、これではまっちまいました。これはプラグインインストール後にサーバ再起動で解決だそうです。その発想はなかったわー。
サンクスどっかの国のナイスガイ。
paginating_findでreverse/reverse!
通常のActiveRecordでfind(:all...)したオブジェクトは、Arrayだと思うんだけど、paginating_findつかってfindした結果はArrayではなくて、PagingEnumeratorという物体になっているみたいで、Arrayクラスのreverse、reverse!のような一部の便利メソッドが使えません。代わりに、PagingEnumeratorクラスには、to_aメソッドがあります。to_aしてからreverse/reverse!すれば今までどおり配列の順番を変えたりといった操作ができます。