概要
LaravelのクエリビルダーでIDのあいまい検索(1を含むIDを検索するなど)を実装する際に詰まったので,問題のあったコードとその解決策を残しておきます.
環境
Laravel 5.5
Postgresql 9.6
問題点
SQLインジェクションを避けるために,クエリビルダーのwhereRawメソッドでバインディングを行おうとしましたが,エラーが発生して実行できませんでした.
問題のあるコード
以下はすべて動作しません.
$query->whereRaw("CAST(books.id AS TEXT) LIKE %?%",[1]); //1
$query->whereRaw("CAST(books.id AS TEXT) LIKE % ? %",[1]); //2
$query->whereRaw("CAST(books.id AS TEXT) LIKE '%?%'",[1]); //3
$query->whereRaw("CAST(books.id AS TEXT) LIKE '% ? %'",[1]); //4
$query->whereRaw("CAST(books.id AS TEXT) LIKE '%' ? '%'",[1]); //5
1,2の場合は,
WHERE CAST(books.id AS TEXT) LIKE %1%
というSQLが実行されます.バインディングはされますが,%1%の部分を' 'で囲めていないためにエラーが生じます.
3,4では,?を' 'で囲んでしまうと,
WHERE CAST(books.id AS TEXT) LIKE '%?%'
というSQLが発行されますが,?が文字列として扱われるためバインディングができず,エラーが生じます.
5の場合は
WHERE CAST(books.id AS TEXT) LIKE '%' 1 '%'
というSQLが発行されますが,'%' 1 '%'
の部分がエラーとなってしまいます.
解決策
次のようにすると動作しました.
$query->whereRaw("CAST(books.id AS TEXT) LIKE '%' || ? || '%'",[1]);
この場合だと,
WHERE CAST(books.id AS TEXT) LIKE '%' || 1 || '%'
というSQLが発行され,'%' || 1 || '%'
の部分は結合されて'%1%'
となり,目的のSQLが発行っされます.