フォームから送信された値を、Paginationで引き継ぐ方法

環境はcakePHP1.2RC2です。

検索画面などで、検索条件を入力して検索結果一覧を表示するようなものって結構ありますよね。そこで面倒なページング処理なんですが、CakePHP1.2から利用できるPaginationの機能を使えば簡単にページング実装できるよって話。ただ、普通にPaginationだけ使うと、例えば次へのリンクの中に検索条件のパラメータを入れてくれないので、ページングのリンクをクリックすると検索条件がすべて消えてしまいますorz

そこで、検索画面の一例として下記のようにしてみました。もっと良さげな方法があれば教えてください。

今回は、検索条件を入れると、検索画面が出てくる簡単な実装です。
DBは下記のような感じでデータが入ってます。

+----+-----------+---------------------+--------+
| id | shopid    | orderdate           | amount |
+----+-----------+---------------------+--------+
| 46 | testuser4 | 2008-08-01 00:00:00 |    100 | 
| 47 | testuser4 | 2008-08-01 00:00:00 |    200 | 
| 48 | testuser5 | 2008-08-01 00:00:00 |    300 | 
| 49 | testuser5 | 2008-08-01 00:00:00 |    400 | 
| 50 | testuser5 | 2008-08-01 00:00:00 |    500 | 
+----+-----------+---------------------+--------+

モデルはシンプルに下記のような感じ。
app/models/invoice.php

<?php
class Invoice extends AppModel {
	var $name = 'Invoice';
	var $useTable='invoices';

}

?>


検索画面のViewはこんな感じ。
app/views/invoices/search.ctp

<h2>
検索画面
</h2>

<?php echo $form->create(array("action" => "searchresult", "type" => "post"));?>

ショップID
<?php echo $form->text("Invoice.shopid", $options=array("size" => "40", "maxlength" => "40")); ?>

<br />

合計額
<?php echo $form->text("Invoice.amount", $options=array("size" => "10", "maxlength" => "10")); ?>
円以上

<?php echo $form->end('検索');?>


コントローラでは下記のように実装
app/controllers/invoices_controller.php

<?php
class InvoicesController extends AppController {

	var $name = 'Invoices';
	var $helpers = array('Html', 'Form', 'paginator');

	var $paginate = array("order" => array("Invoice.orderdate" => "desc"),
						  "limit" => 10
	);

	function search(){

	}

	function searchresult() {

		$this->Invoice->recursive = 0;

		if( !empty($this->data) ){
			$shopid = $this->data['Invoice']['shopid'];
			$amount = $this->data['Invoice']['amount'];
		}else{
			$shopid = $this->passedArgs['shopid'];
			$amount = $this->passedArgs['amount'];
		}

		$condition = array();
		$condition = array( "shopid like" => '%' . $shopid  .'%',
				    "amount >=" => $amount
		);

                /* 2008/12/25 下記にurlencodeを追加 */
		$searchword = array();
		$searchword = array( "shopid" => urlencode( $shopid ),
				     "amount" => urlencode( $amount )
		);

		$this->set('searchword', $searchword);
		$this->set('invoices', $this->paginate( $condition ));

	}

?>

/invoice/search/にアクセスするとsearch.ctpのviewを表示し、検索ボタンを押すと/invoice/searchresultに検索条件がPOSTされます。
コントローラのsearchresultでは、検索画面からPOSTされた時点では、$this->dataからPOSTデータを取得、検索結果画面でページングされる時は、POSTではなく、URLの中にパラメータが /invoice/searchresult/shopid:xxx/amount:100/page:2みたいに入ってくるので、URLの中のデータを取得する$this->passedArgs['shopid'];でデータを取得。
検索条件は、$conditionの配列で管理し、ページングのURLに入れる検索条件の値は、$searchwordの配列で管理します。
$searchwordの配列は、Viewに渡します。
2008/12/25追記
$searchwordのデータは、事前にurlencodeをかけておきます。そうしないとViewの中で展開されたurlに任意の文字列が注入できてしまうためです

Viewでは下記のようにoptions(array('url' => $searchword )); ?>という一文で検索条件の値をURLにセットします。そうすると、$paginator->sortや$paginator->nextのURLに自動的に検索条件のパラメータが/invoice/searchresult/shopid:xxx/amount:100/という感じでセットされます。

app/views/invoices/searchresult.ctp

<div class="invoices searchresult">
<h2>
検索結果
</h2>
<p>


<?php
echo $paginator->counter(array(
'format' => __('Page %page% of %pages%, showing %current% records out of %count% total, starting on record %start%, ending on %end%', true)
));
?></p>

<?php /* 2008/12/25修正 この例の$searchwordはArrayデータなのでStringを引数にとるurlencodeだとうまく動かないため、コントローラ側でurlencodeするように修正しました。 */ ?>
<?php $paginator->options(array('url' => $searchword  )); ?>


<table cellpadding="0" cellspacing="0">
<tr>
	<th><?php echo $paginator->sort('id');?></th>
	<th><?php echo $paginator->sort('売り上げ年月','orderdate');?></th>

	<th><?php echo $paginator->sort('ショップID','shopid');?></th>

	<th><?php echo $paginator->sort('合計金額','amount');?></th>
</tr>
<?php

foreach ($invoices as $invoice):

?>
	<tr>
		<td>
			<?php echo h( $invoice['Invoice']['id'] ); ?>
		</td>
		<td>
			<?php echo h( $invoice['Invoice']['orderdate']) ); ?>

		</td>

		<td>
			<?php echo h( $invoice['Invoice']['shopid'] ); ?>

		</td>

		<td>
			<?php echo h( $invoice['Invoice']['amount'] ); ?>
		</td>

	</tr>
<?php endforeach; ?>
</table>
</div>

<div class="paging">
	<?php echo $paginator->prev('<< '.__('previous', true), array(), null, array('class'=>'disabled'));?>
 | 	<?php echo $paginator->numbers();?>
	<?php echo $paginator->next(__('next', true).' >>', array(), null, array('class'=>'disabled'));?>
</div>

これで検索画面とその結果のページング処理が簡単にできるね!



追記
上記の例はフォームからのPOSTでしたが、id:j_okiさんがフォームからのGETリクエストの場合をまとめてくれてます。GJ!
http://d.hatena.ne.jp/j_oki/20080907/1220801326