[CakePHP]LIKE文のワイルドカードをエスケープする

CakePHPでは等号(=)以外のSQL演算子を特殊な方法で指定します。たとえば、比較演算子であれば以下のような感じ。

[phpcode]
$params[‘conditions’][‘User.created <'] = date('Y-m-d'); [/phpcode] 同様にLIKE文もこのように指定できます。 [phpcode] $params['conditions']['User.name LIKE'] = $this->data[‘User’][‘name’];
[/phpcode]

ここで困ったのがワイルドカードを使用する場合。検索文字列を%%でくくるなんていうことはよくあることですが、フォームに「%」を入れられてしまった場合、エスケープ処理をしないと全レコードが一致してしまい、検索結果が大変なことになります。

[phpcode]
// エスケープ処理をしていないワイルドカード検索
$params[‘conditions’][‘User.name LIKE’] = ‘%’.$this->data[‘User’][‘name’].’%’;
[/phpcode]

LIKE文に指定する文字列は独自のエスケープ関数をAppModelに作成して呼び出すことにしましょう。なお、下記ソースコードはUTF-8での使用を想定しているので、そのほかの文字コードで記述する場合はstr_replace()の代わりにmb_ereg_replace()を使用することを検討してください。

[phpcode]
class AppModel extends Model
{
/**
* SQLのLIKE分のワイルドカードをエスケープする
*
* @params string $str LIKE文に指定する検索文字列
* @params boolean $before 検索文字列の前に % を付与するか
* @params boolean $after 検索文字列の後に % を付与するか
* @return string ワイルドカードがエスケープされたLIKE文
*/
function escapeLikeSentence($str, $before = false, $after = false)
{
$result = str_replace(‘\\’, ‘\\\\’, $str); // \ -> \\
$result = str_replace(‘%’, ‘\\%’, $result); // % -> \%
$result = str_replace(‘_’, ‘\\_’, $result); // _ -> \_
return (($before) ? ‘%’ : ”).$result.(($after) ? ‘%’ : ”);
}
}
[/phpcode]

モデル内での使用方法
[phpcode]
// エスケープ処理をしたワイルドカード検索
$like = $this->escapeLikeSentence($this->data[‘User’][‘name’], true, true);
$params[‘conditions’][‘User.name LIKE’] = $like;
[/phpcode]

こういうエスケープ処理ってフレームワーク側でやってくれるものかと思ってたんですが、ワイルドカードで検索される可能性もあるから、勝手にエスケープはしないんですかね。

About: uechoco