jqGridとPHPとZend_Dbでテーブルを検索する(JSONによるAjax)


建設予定地

とりあえずの目次

  • てっとり早く済ませたい人は
  • はじめるまえに
  • jqGridの検索は3種類ある
  • Filter
  • Single
  • Multi
  • 3種類対応

2010/12/07 追記

ちょっと手が回っていなくて、このまま未完のまま放置させてもらいます。


てっとり早く済ませたい人は

jqGridの開発元が販売しているjqGrid for PHPを買うがよろし。
そんなに高くないのにZend_Db依存から抜け出せるはPHPだけで完結できるはで仕事で使うにはよさそうです。


え?
残念ながらぼくは買っていません。
予算申請するのが怖いのです。はい。
次の案件がうまくいったら予算申請するんだっ



はじめるまえに

この記事は以下の道具を使用しています。
※ライブラリでもフレームワークでもないし。なんて書けばいいのかわからないから道具。
PHP, Zend_Db
MySQL
jQuery, jQuery UI, jqGrid


jqGridの検索は3種類ある

wiki:jqgriddocsのSearchingにToolbar,Custome,Single,Advance(Multiple)って4種類あるんだけれど、ToolbarとCustomeは多分同じ動きをする。(はい、試していませんw)


この記事ではjqGridの検索を以下の3種類に分けて考えてみる。

  1. Filter (filterToolbar, filterGrid)
  2. Single (searchGrid)
  3. Multie (searchGrid with multipleSearch=true)

3種類対応

してません。週末にちゃんとしたコードを書きます。
載せられない部分を削ったら素敵な感じになりました。

<?php

/* common/common.php で$db = Zend_Db_Abstractを作っています。 */
require_once('common/common.php');

// ここらへんにユーザー認証系のコードを書いています。


// GETとPOSTと区別するかを設定
// jqGrid.mtype = (get | post)
$req = $_POST; // $_GET, $_POST, $_REQUEST

// 一度に表示する件数と現在のページを設定
$page  = isset($req['page']) ? $req['page'] :  1;
$limit = isset($req['rows']) ? $req['rows'] : 25;

// ソートに関する設定
$sidx  = isset($req['sidx']) ? $req['sidx'] : '';    // order by $sidx $sord
$sord  = isset($req['sord']) ? $req['sord'] : 'asc'; // asc, desc

// 検索用のデータが設定されているかどうか
// 検索(true | false)
$s      = isset($req['_search']) ? $req['_search']: false;



$table = 'TABLE_NAME';
$select = $db->select();

// ここでアクセス権限関係のWHERE句を追加しています。
// Zend_Db_Selectで複雑な検索条件の設定方法については
// Idea of Zend_Db_Select nested where clause
// という記事を書いているので読んでね。

/* ORDER BY
 * $sidxは field,field,field かもしれない、その場合は分割する必要がある。
 * field asc, field asc, field asc となる
 *
 * まぁ、僕には関係ありませんけれどね。
 */
if ($sidx) {
    $select->order(trim($sidx . ' ' . $sord));
}


/*
 * あ。。
 * 使わないからmultipleSearch以外のコード消しちゃってる。
 * しかも、中途半端に削っているからエラー処理がめちゃくちゃ。
 * 週末ぐらいに掲載できるコードを探してきます。
 */


// multipleSearch:true のとき
/*
 * ["_search"]      => "true"
 * ["filters"]      => '{"groupOp":"AND","rules":[{"field":"name","op":"eq","data":""}, {...}, ...]}'
 *
 * filtersはJSONです。PHPでいうところにStringです。
 * 
 * json_decodeを使用するためには、文字コードがUTF-8になってなくてはいけない。
 * jqGridからPOSTされるデータは、基本的にはUTF-8です。
 * ただし、レンサバの設定で問答無用にEUC-JPへ変換されていなければ。
 */
$filters = isset($req['filters']) ? $req['filters'] : '';

if ($filters) {
    $filters = json_decode($filters);
}

/*
 * SingleとMultiの場合には条件が設定できる
 * filtersのなかのrulesのop項目です。
 */
$operator = array(
    eq => array('='   , '', ''),    // 等しい
    ne => array('<>'  , '', ''),    // 等しくない
    lt => array('<'   , '', ''),    // 〜より少ない
    le => array('<='  , '', ''),    // 〜以下
    gt => array('>'   , '', ''),    // 〜より大きい
    ge => array('>='  , '', ''),    // 〜以上
    bw => array('LIKE', '' , '%'),  // 〜で始まる( val%)
    bn => array('NOT LIKE', '', '%'), // 〜で始まらない
    in => array('IN', '(', ')'),    // 〜が含まれる
    ni => array('NOT IN', '(', ')'), 
    ew => array('LIKE', '%', '' ),  // 〜で終わる(%val )
    en => array('NOT LIKE', '%', ''),
    cn => array('LIKE', '%', '%'),   // 〜を含む  (%val%)
    nc => array('NOT LIKE', '%', '%')
);


$groupOp = $filters->groupOp; // (AND | OR)

/* 
 * $rule->op はちゃんとチェックしたほうがいいかもね。
 *
 *
 * 検索条件的にワイルドカートが入るとまずい場合は
 * ちゃんと各自エスケープしてね
 */
foreach ( $filters->rules as $rule )
{
    $nest2[] = $db->quoteInto($rule->field . ' ' . $operator[$rule->op][0] . ' ?',
                          $operator[$rule->op][1] . $rule->data . $operator[$rule->op][2]);
}

if ($groupOp == 'AND')
{
    $nest2 = implode(' AND ', $nest2);
    $select->where($nest2);
} else if ($groupOp == 'OR') {
    $nest2 = implode(' OR ', $nest2);
    $select->where($nest2);
}

/**
 * 条件に合致する総数を取得
 **/
$select_count = $select;
$select_count->from($table, 'count(*)');
$count = $db->fetchOne($select_count);

$select->reset( Zend_Db_Select::COLUMNS);
$select->reset( Zend_Db_Select::FROM   );

/**
 * 総ページ数を取得
 **/
if ( $count > 0) {
    $total_pages = ceil($count/$limit);
} else {
    $total_pages = 0;
}

/**
 * データ取得用SQL作成
 **/

// SQL文用パラメータ生成(limit用)
if ($page > $total_pages) {$page=$total_pages;}
$start = $limit*$page - $limit;

$select->from($table, array('ID', 'ここにfiled名を書いていく、式でもいいけれど'))
       ->limitPage($page, $limit);

// データ取得
$rows = $db->fetchAll($select);

/**
 * 戻り値生成
 **/
$ret->page = $page;           // $req['page']
$ret->total = $total_pages;
$ret->records = (int)$count;

/*
 * id はユニーク値がいいとおもうよ
 */
$i = 0;
foreach ($rows as $key => $value) {
    $ret->rows[$i]['id'] = $value['ID'];
    $ret->rows[$i]['cell'] = array_values($value );
    $i++;
}

$json = json_encode($ret);

header('Content-Type: application/json; charset=UTF-8');
echo $json;