まずその落とし穴ですが、前回までのスクリプトでなにがしかの検索をしてみます、まあページングされないと困るので、わざと比較的多くのデータが引っかかるような語句、例えば「市」などで検索してみます。
いちいち画像は貼りませんが、一見大丈夫そうに見えます。てゆーかこの時点(1ページ目表示)ではなんの問題もありません。きちんと「市」でフィルタリングされた結果が返っています。
総件数も、ページングリンクのLASTの部分にポインタを合わせてみるとわかりますが、フィルタリングなしの時は590だったオフセット値が430に減っているので、きちんと数えられているとおもいます。
そこで次へのリンクをクリックします。表示が2ページ目に移動します。一見大丈夫そうです。が、よく見ると「日高町」とか「枝幸町」「安平町」など「市」でフィルタリングした場合は引っかからないはずの新市町村の名前が見えます。
あれれ?とおもってデバッグ表示を見てみると、POSTデータはありませんなどと書いてあるし、SQLもWHERE nc like '%%'になっています。なんでだ?とおもってもう一度リロードしたり何度もページングリンクをクリックしたり…。
でも結局検索フォームをサブミットした直後のページはよいのですが、ページングリンクを一度でもクリックしてしまうとダメです。要するに検索語句がリセットされちゃっているんですね。
まあここまで読んで「そんなの当たり前だろ!」と言える人はすでに相当プログラミングに慣れている方か、プロの方でしょう。私のように趣味でやってるような素人プログラマはこれって結構ハマルポイントなんじゃないでしょうか?(オレダケ?)
というのも…(ここから先は純粋に愚痴です)、そもそもページングする時ってどういう時なんでしょうか?静的なHTMLであれば長くなりそうだったら当然そこでファイルを分けるなりなんなりするでしょう。つまりページングが必要な場合というのはなんらかの動的なデータ、しかも条件によって長さ(というか数)が変化するようなデータを表示する時なのではないでしょうか?
そしてそうした状況は、もうほとんど検索結果表示画面なんじゃないでしょうか?それ以外だと例えば情報系?(ITMediaとかAllAboutとかCodeZineとか…)のサイトによくある一つの記事を数ページにわけて表示したりとか、Blogなどでも長い記事を数ページに分けて表示したりしているところもあるようですが、結局これらもデータベースには元々の長い記事があって、それを表示時にページングしているという点では、ある種の検索結果だとおもうわけです。
んでそういう検索結果をページングする際に、まあいろいろな方法があるとおもいますが、共通していることは検索に用いた語句なりIDなり、いわゆる検索条件といったものを保持していなければならないということです。
例えばこのBlogでエサにしている自作の汚い手続き型スクリプトでは、ページング部分で生成しているURLリンクにgetクエリ文字列として渡しているわけです。
http://localhost/gappei.php?nc=市&page=3のような感じです。(※もちろん日本語はURLエンコードしたりしてますが…)
フレームワークと言ってよいのかどうか知りませんがPHPにはPEARというライブラリ?があります。その中のPagerなどは上記のようにformデータをgetで受け取ってる場合は特に何もしなくても上のようにクエリ文字列を付加したリンクを生成してくれます。
もちろんPEARのPagerでもformデータがpostになっている場合は、上の状況と同じようになります(--;こうした場合(formがpost)通常のHTMLだけではAリンクからformをsubmitすることができないので、<a href="javascript:document.form1.submit();">のようにjavascriptを使ったりします。
いずれにしろ検索条件を次に移動するページに伝えてあげないとならないわけですが、ここからがハマリ所でして…。
CodeIgniterで上記の方法を再現しようとすると、まずgetでformデータを送ることはできません。これはユーザガイドのセキュリティに明記されています。もちろんここにあるようにクエリ文字列オプションを有効にすれば使えますが、オフにした場合その他の影響が大きすぎて普通はしないでしょう。
そうなると次はjavascriptとなるわけですが、paginationのconfig配列のbase_urlのところにjavascript:document.form1.submit();などと書いても無意味ですし、そもそも仮に↑のように書けたとしても今度はページングに使用するページ数なりオフセット値なりを渡すことができません。
通常そうした場合はhiddenフィールドなどを使うとおもうのですが、ページネーションの生成するリンクはどう見てもpostデータをサブミットするような構造にはなっていない、というかmod_rewriteを通すことを前提としたようなURI(/class/method/parameterのような)構造を生成してしまうわけです。
ではいったい検索条件を次ページに伝えるにはどうすればよいのでしょうか?一般的にページを跨いで何かを保持するにはセッションを使います。ショッピングカートの作成例なんかでよく説明されています。
ですがここからが今回の本題なんですが「検索条件ごときにいちいちセッションなんか使う」ものなんでしょうか?というのもセッションなどという大袈裟なモノを持ち出さなくても、上記のようにgetでクエリ文字列のリンクを生成したり、javascriptでsubmitしたり…というようにやり方はいろいろ考えられるわけです。
しかもCodeIgniterはデフォルト状態ではgetが使えないわけです。んでページネーションクラスを使用するとpostを自動的にサブミットしてくれるわけでもなく、単にoffsetパラメータ付きのURIを生成するだけです。ページネーションを必要とする場面がほとんどなんらかの検索結果の表示にも関わらずです。
結論から言うとCodeIgniterで検索条件を維持しつつページングする場合は、セッション(またはクッキー)を使うしかありません(※たぶん、ってか他の方法があったら是非教えて欲しいです)。これは別にCodeIgniterに限った話じゃないかもしれません。例えばCakePHPでもデフォルトのページャーは同じようなものですし、Pearも上述のとおりです。
ですがCakeの場合はURIが/class/method/parameterのような形になっていても?hoge=hage&foo=barみたいなクエリ文字列を付け足せますし、Pearの場合はgetの時は自動的にクエリ文字列付きのURIを生成してくれます。
でもCodeIgniterのページネーションは何もやってくれません。そしてクエリ文字列付きのURIはデフォルトでは許可されていません。残るはセッションなんですが、果たしてページングのために検索条件ごときをセッションデータとして保持するという実装は正しいものなんでしょうか?
もしこれが標準的なごく当たり前のプログラム作法?ならば、一言どこかに書いておいて欲しかったなぁとおもうわけです。プロの方はいざしらず素人はそういうことがわからないわけです。つまりこの問題に限らず、なんか実装できるし動いてるかもしれないけどこれってこんなんでいいの?的なことってなかなか情報がないんですよね。
せめてページネーションクラスのところに一言
検索結果等をページングする場合はセッションを使用し検索条件等を保持するようにします
とだけ書いておいて欲しかった。それだけで素人は安心するんです。それだけでセッション機構というなにやら複雑そうな&大袈裟そうなモノにも手を出してみようか?と考えられるんです。
以上長い愚痴でした(でもハマッたんです)。
ラベル:愚痴 codeigniter