Rails
RubyOnRails
will_paginate

will_paginateのurlを変更する

発生した問題

will_paginateをrailsアプリに組み込んでいるが、paramsを用いた検索を併用すると・・・2ページ目以降のurlがおかしい?

例えば、/search/myloversというページでparamsを用いたDB検索を行う。そのとき、発行されるurlは、
 /search/mylovers?age_gte=20&age_lte=25&occupation=model
といった感じになる。しかし、このとき、will_paginateを用いて複数ページに結果を分割していたとして、will_paginateが作成する「次へ」ボタンのurlは、
 /search/mylovers?age_gte=20&age_lte=25&occupation=model&page=2
とはならないときがある。

原因

url_forヘルパーがroute.rbの設定に依存すること

will_paginateはurl_forヘルパーを用いてurlを作成し、それをボタンに埋め込む。しかし、もしrails側のroutes.rbにおいて、
 get search/mylovers/:occupation => search/index
みたいな設定をしていた場合(つまり、params[:occupation]をurl内に組み込んでいた場合)、url_forヘルパーは、
 /search/mylovers?age_gte=20&age_lte=25&occupation=model
のかわりに、
 /search/mylovers/model?age_gte=20&age_lte=25
を返してくる。つまり、routes.rbでの設定を逆に解釈する。routes.rbでは、「もし、/search/myloversの後ろになんかひっついてきたら、それをparams[:occupation]として解釈してね」という指示を与えたつもりが、url_forヘルパーは気を回して「もし、params[:occupation]が渡ってきたら、search/mylovers/の後ろに引っ付けるようにしよう」としてしまう。

 基本的にはそれでも問題ない場合が多いが、例えば、params[:occupation]が配列形式である場合には、このurl_forヘルパーは壊れる。
 /search/mylovers?occupation=model&occupation=nurse
というurlを発行すると、url_forヘルパーは、
 /search/mylovers/modelnurse
を返してくる。もちろんこれは無効urlとなり、ページネーションが動作しないことになる。

解決策

 will_paginate内で作られるurlに問題があるので、gemのオーバーライドをする。オーバーライドとは、既存のメソッドの上書きのようなもの。オーバーライドのやり方は以下のステップ。

  • 上書き用のファイルを用意
  • 上書きするメソッドの位置を特定
  • 上書き内容を書く

 今回は、app/helper内にファイルを用意した。次に、gem内部のどのメソッドを修正したいか(するべきか)を特定する。今回の場合、発行されるurlを変更したいので、urlを作成しているメソッドを探す。見つけたら、書き換えるメソッドのある場所を、module名::class名で指定する。今回はmoduleがネストしていたので、WillPaginate::ActionView::LinkRenderer。書き換えるメソッド名は「url(page)」。ここまでの段階で、上書きファイルの内容は下のようになる。

paginatehelper.rb
module PaginationHelper
 class SearchPagination < WillPaginate::ActionView::LinkRenderer
  def url(page)#書き換えるメソッドと同じ名前に
    #処理
  end
 end
end

あとは、処理の中で好きに書けば良い。今回はrequest.full_pathを使って現在のurlを取得し、お尻にparams[:page]をつける処理にした。

あとは、viewのwill_paginateのrendererオプションにオーバーライドしたいやつを指定。

index.html.erb
<%= will_paginate @mylovers, renderer: PaginationHelper::SearchPagination

これで完了。will_paginateはrendererオプションでオーバーライドして色々カスタマイズができる。