発生した問題
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)」。ここまでの段階で、上書きファイルの内容は下のようになる。
module PaginationHelper
class SearchPagination < WillPaginate::ActionView::LinkRenderer
def url(page)#書き換えるメソッドと同じ名前に
#処理
end
end
end
あとは、処理の中で好きに書けば良い。今回はrequest.full_pathを使って現在のurlを取得し、お尻にparams[:page]をつける処理にした。
あとは、viewのwill_paginateのrendererオプションにオーバーライドしたいやつを指定。
<%= will_paginate @mylovers, renderer: PaginationHelper::SearchPagination
これで完了。will_paginateはrendererオプションでオーバーライドして色々カスタマイズができる。