2014/12/20
テキストフィールドを CSV として処理する CakePHP ビヘイビア・その2
-
-
今回もテキストフィールドを CSV として処理するビヘイビアの作成を進める。
前回の取得用コールバックに引き続き、今回は保存時のコールバックを作成する。
データ保存時の変換
データ保存時には、beforeSave() コールバックで保存データをチェックし、該当するフィールドのデータをカンマ区切り文字列に変換する。渡されたデーがが空配列や空文字列の場合は '' (空文字列) に変換する。
01 | class SimpleCsvFieldBehavior extends ModelBehavior |
04 | public function beforeSave(Model $model , array $options = array ()) |
07 | foreach ( $this ->actual_settings[ 'fields' ] as $csv_field ) |
09 | if ( isset( $this ->data[ $this ->alias][ $csv_field ]) ) |
11 | if ( is_array ( $this ->data[ $this ->alias][ $csv_field ]) ) |
13 | $this ->data[ $this ->alias][ $csv_field ] = implode( $this ->_csv_separator, $this ->data[ $this ->alias][ $csv_field ]); |
15 | else if ( strlen ( $this ->data[ $this ->alias][ $csv_field ]) == 0 ) |
17 | $this ->data[ $this ->alias][ $csv_field ] = '' ; |
絞り込み条件の変換
最後に、find() 時の検索式の変換処理を beforeFind() コールバックで行なう。条件の変換は再帰処理になるので、プライベートメソッド __condition_replace() として独立させておく。
__condition_replace() の内部処理では、簡単のため条件配列のキーは {モデル名}.{フィールド名} のみ、値はスカラー値のみ (整数のみ) としており、!= や大小比較、IN 演算、あるいは MySQL 関数呼び出しには対応していない。
__condition_replace() では、CSV フィールドを検出すると、その {モデル名}.{フィールド名} => {値} の組を次の「OR」形式に変換する。
'OR' => array(
"{モデル名}.{フィールド名} LIKE" => "{値}", // 値が単独値の場合
"{モデル名}.{フィールド名} LIKE" => "{値}{区切り文字}", // CSV形式の先頭にマッチ
"{モデル名}.{フィールド名} LIKE" => "{区切り文字}{値}{区切り文字}", // CSV形式の途中にマッチ
"{モデル名}.{フィールド名} LIKE" => "{区切り文字}{値}", // CSV形式の末尾にマッチ
)
これにより、CSV フィールドに値が含まれるかどうかを文字列演算で行なうことができる。
この書き換えメソッドを beforeFind() コールバックで使用して条件配列を書き換える。実際のコードは下記のようになる。
01 | class SimpleCsvFieldBehavior extends ModelBehavior |
04 | private function __condition_replace( $array , $replace_key_patterns ) |
06 | $separator = $this ->actual_settings[ 'separator' ]; |
10 | foreach ( $array as $key => $value ) |
12 | if ( in_array( $key , $replace_key_patterns ) ) |
14 | $ret [] = array ( 'OR' => array ( |
15 | array ( "{$key} LIKE" => "{$value}" ), |
16 | array ( "{$key} LIKE" => "{$value}{$separator}%" ), |
17 | array ( "{$key} LIKE" => "%{$separator}{$value}{$separator}%" ), |
18 | array ( "{$key} LIKE" => "%{$separator}{$value}" ) |
21 | else if ( is_array ( $value ) ) |
23 | $ret [ $key ] = $this ->__condition_replace( $value , $replace_key_patterns ); |
36 | public function beforeFind(Model $model , array $query ) |
38 | $model_name = $model ->alias; |
39 | $fields = $this ->actual_settings[ 'fields' ]; |
41 | $query [ 'conditions' ] = $this ->__condition_replace( $query [ 'conditions' ], array_map ( function ( $f ) use ( $model_name ) { return "{$model_name}.{$f}" ; }, $fields )); |
以上で CSV フィールドへの対応が簡易的ではあるが可能となる。