Logicky Blog

Logickyの開発ブログです

cakePHP 2.3 AjaxなPagination

はまりにはまったができた。

コントローラー

class CustomersController extends AppController {
    public $components = array('RequestHandler');
    public $helpers = array('Js','Paginator');
    public $paginate = array(
            'limit' => 5,
            'order' => array(
                    'Customer.id' => 'asc'
            )
    );

/**
 * index method
 *
 * @return void
 */
    public function index() {}

    //お客様データの検索
    public function search(){
        $this->Customer->recursive = 0;
        //検索条件の設定
        $conditions = array();
        if($this->request->data){
            $customer = $this->request->data['Customer'];
            if($customer['si']){
                $conditions['Customer.id LIKE'] = '%' . $customer['si']  . '%';
            }
            if($customer['sn']){
                $conditions['Customer.name LIKE'] = '%' . $customer['sn']  . '%';
            }
            if($customer['sc']){
                $conditions["or"] = array(
                    'Customer.call1 LIKE' => '%' . $customer['sc']  . '%',
                    'Customer.call2 LIKE' => '%' . $customer['sc']  . '%'
                );
            }
            if($customer['sm']){
                $conditions['Customer.mail LIKE'] = '%' . $customer['sm']  . '%';
            }
            if($customer['sa']){
                $conditions['Customer.address LIKE'] = '%' . $customer['sa']  . '%';
            }
        }
        $this->set('customers', $this->paginate($conditions));
        $this->render('/Customers/json/search','ajax');
    }

ビュー(/View/Customers/index.ctp)

<form>
<?php echo $this->Js->submit('検索',array('update'=>'#search_result','class'=>'btn btn-primary search_btn','div'=>false,'url'=>'/customers/search'));?>
</form>
<?php echo $this->Js->writeBuffer(array('inline'=>false));?>
<div id="search_result"></div>

ビュー(/View/Customers/json/search.ctp)

<table>
<tr><th class="id">ID</th><th class="name">名前</th><th class="mail">メール</th><th class="call">電話1</th><th class="call">電話2</th><th class="address">住所</th><th class="button"></th></tr>
<?php foreach($customers as $c):?>
<tr><td><?php echo $c['Customer']['id'];?></td>
<td><?php echo $c['Customer']['name'];?></td>
<td><?php echo $c['Customer']['mail'];?></td>
<td><?php echo $c['Customer']['call1'];?></td>
<td><?php echo $c['Customer']['call2'];?></td>
<td><?php echo $c['Customer']['address'];?></td>
<td><?php echo $this->Form->button('詳細',array('type'=>'button','class'=>'btn btn-primary syosai_btn','div'=>false));?></td></tr>
<?php endforeach;?>
</table>
<?php echo $this->paginator->counter(array('format' => '%page%ページ /  %pages%ページ '));?><br>
<?php echo $this->paginator->prev('', array('update' => '#search_result'), null, array('class' => 'disabled'));?>
<?php echo $this->paginator->next('', array('update' => '#search_result'), null, array('class' => 'disabled'));?>
<?php echo $this->Js->writeBuffer(array('inline'=>true));?>

追記(2013年6月8日):

上記だと、Sortとか次のページとかがAjaxにはなるけど、検索条件が維持されていない!!!!セッションとかで勝手に管理してくれてたりするわけではないらしい。自分でセッションつかうか、Paginatorヘルパーが出力するJavascriptのコードを書き換える必要があるようだ。セッション使うのが一番お手軽っぽいけど、AjaxなPaginatorを複数で使っているので全部修正するのがめんどくさい。検索したらセッションに検索条件を入れて、新たに検索されたらセッションの検索条件を変更しないといけない。Paginatorヘルパーをオーバーライドして、sortとかのオプションで検索条件のフォームをしていできるようにしたら修正もお手軽である。

Paginatorヘルパーは、$this->Paginator->sort('id','ID',array('update'=>'#search_result));みたいな感じである。この第三引数に、'method'=>'post'を追加すると、POSTでアクセスしてくれる。さらに、この第三引数の配列に、'form'=>'#フォームのID'といった感じで検索条件を設定しているフォームを指定することで検索条件をdataとしてリクエストしてくれるようにする。

sort関数などは、標準だと、オプション配列はJqueryEngineHelperのrequest関数で扱われている。これをMyJqueryEngineHelperとしてオーバーライドする。

<?php
App::uses('AppHelper', 'View/Helper');
App::uses('JqueryEngineHelper', 'View/Helper');

class MyJqueryEngineHelper extends JqueryEngineHelper {

    public function request($url, $options = array()) {
        $url = html_entity_decode($this->url($url), ENT_COMPAT, Configure::read('App.encoding'));
        $options = $this->_mapOptions('request', $options);
        if (isset($options['data']) &amp;&amp; is_array($options['data'])) {
            $options['data'] = $this->_toQuerystring($options['data']);
        }
        $options['url'] = $url;
        if (isset($options['update'])) {
            $wrapCallbacks = isset($options['wrapCallbacks']) ? $options['wrapCallbacks'] : true;
            $success = '';
            if (isset($options['success']) &amp;&amp; !empty($options['success'])) {
                $success .= $options['success'];
            }
            $success .= $this->jQueryObject . '("' . $options['update'] . '").html(data);';
            if (!$wrapCallbacks) {
                $success = 'function (data, textStatus) {' . $success . '}';
            }
            $options['dataType'] = 'html';
            $options['success'] = $success;
            unset($options['update']);

            //追記
            if(isset($options['form'])){
                $form =  $this->jQueryObject . '("' . $options['form'] . '").serialize()';
                $options['data'] = $form;
            }
            //追記終了
        }
        $this->log($options);
        $callbacks = array('success', 'error', 'beforeSend', 'complete');
        if (!empty($options['dataExpression'])) {
            $callbacks[] = 'data';
            unset($options['dataExpression']);
        }

        //追加
        if(!empty($options['form'])){
            $callbacks[] = 'data';
            unset($options['form']);
        }
        //追記終了

        $options = $this->_prepareCallbacks('request', $options);
        $options = $this->_parseOptions($options, $callbacks);
        return $this->jQueryObject . '.ajax({' . $options . '});';
    }
}

そして、これをコントローラーで呼び出す。今回はAppControllerで呼び出した。classNameというのは便利だ(参考:【CakePHP2】コンポーネント、ヘルパーのメソッドをオーバーライドする)。

public $helpers = array('JqueryEngine' =>array(
        'className' => 'MyJqueryEngine'
));

そして、ビューで下記のようにオプション設定をする。

<?php $this->Paginator->options(array(
        'method' => 'post',
        'form'=>'#検索条件がしていされているフォーム'
));?>

追記(2013年6月10日)

注意!$this->Paginator->sortというように、paginatorではなく、Paginatorと最初のPを大文字に統一しないと、オプション設定が反映されません。なぜかpaginatorでも動くんだけど。