Railsアプリケーションでpaginationといえば、
will_paginate等のプラグインやGemを使うのが一般的だと思います。
しかし、named_scopeでjoinsを使った場合にうまく
paginationができなかったので、
named_scopeだけを使ってpaginationする方法を考えてみました。
まずは以下のようなnamed_scopeを作ります。
以下の例はPostクラスで宣言される事を想定しています。
1 named_scope :paginate, proc{|page, per_page|
2 {:offset => per_page*((page || 1).to_i - 1),
3 :limit => per_page}} do
4 def count
5 proxy_scope.count(:group => 'posts.id').size
6 end
7 def num_pages
8 (count.to_f/proxy_options[:limit]).ceil
9 end
10 def page
11 proxy_options[:offset]/proxy_options[:limit] + 1
12 end
13 def pages(window = 5, left = 2, right = 2)
14 (1..num_pages).inject([]) do |result, i|
15 i <= left || (num_pages - i) < right ||
16 (i-page).abs < window ? result << i :
17 (result.last.nil? ? result : result << nil)
18 end
19 end
20 end
count
を再定義しているのは、:joins
を含む別なnamed_scopeをチェーンした時に、正しいcountを求めるためです。
コントローラでは、以下のようにScopeを取得します。
1 @posts = Post.paginate(params[:page], 5)
Viewでは以下のように記述します。
1 <% if @posts.page > 1 %>
2 <%= link_to '« Newer',
3 url_for(:page => @posts.page - 1) %>
4 <% else %>
5 « Newer
6 <% end %>
7 <% @posts.pages.each do |i| %>
8 <% if i.nil? %>
9 ...
10 <% elsif i == @posts.page %>
11 <%= i %>
12 <% else %>
13 <%= link_to i, url_for(:page => i) %>
14 <% end %>
15 <% end %>
16 <% if @posts.page < @posts.num_pages %>
17 <%= link_to 'Older »',
18 url_for(:page => @posts.page + 1) %>
19 <% else %>
20 Older »
21 <% end %>