【Laravel】クエリビルダのバインディング〜DB::raw()メソッドでバインドについて〜

Laravel

はじめに

公式ドキュメントにも書かれているとおり、Laravelのクエリビルダ はSQLインジェクション対策としてPDOパラメーターによるバインディングを使用しています。

LaravelクエリビルダはアプリケーションをSQLインジェクション攻撃から守るために、PDOパラメーターによるバインディングを使用します。バインドする文字列をクリーンにしてから渡す必要はありません。

https://readouble.com/laravel/7.x/ja/queries.html#introduction

ただクエリでSQLを直接使用したい場合はrawメソッドを使用するのですが、使用方法を間違えるとSQLインジェクションの脆弱性を生むことになります。

rawメソッドを使用する場合もしっかりバインドをするように心がけましょう

第2引数にバインド値を指定する方法

今から紹介するrawメソッドには第2引数のオプションにバインド値を配列で渡すことができます。ドキュメントにも書かれていますので、間違ってもsprintfなどで生成したSQLを渡さないようにしましょう。

ダメなパターン

$orders = DB::table('orders')
                ->selectRaw(sprintf("price * %d as price_with_tax", 1.0825))
                ->get();

selectRawメソッド

$orders = DB::table('orders')
                ->selectRaw('price * ? as price_with_tax', [1.0825])
                ->get();

whereRaw / orWhereRawメソッド

$orders = DB::table('orders')
                ->whereRaw('price > IF(state = "TX", ?, 100)', [200])
                ->get();

havingRaw / orHavingRawメソッド

$orders = DB::table('orders')
                ->select('department', DB::raw('SUM(price) as total_sales'))
                ->groupBy('department')
                ->havingRaw('SUM(price) > ?', [2500])
                ->get();

setBindingsでバインド値を指定する方法

DB::rawメソッドには第2引数のオプションが無いためバインド値を渡すことことができません。ドキュメントにも特に記載はなかったのですが、こちらのサイトで詳しく紹介されていました。

LaravelクエリビルダーのDB::rawで変数の値をバインドしようしたら詰まった
CASE式で変数の値を使おうとしたら詰まりました。Laravelクエリビルダーむずい。例えば下記のよ

先にも紹介しましたがLaravelのクエリビルダ はPDOパラメータを利用していますので、疑問符パラメータを活用してクエリを生成します。

DB::rawメソッド

    $orders = DB::table('orders')
        ->select([
            DB::raw(' CASE WHEN detleted_at IS NOT NULL THEN ? ELSE ? END as "deleted_at" ')
            ->setBindings([
                '削除',
                '未削除'
            ])
        ])
        ->get();

mergeBindingsでバインド値を指定する方法

【Laravel】クエリビルダ:サブクエリのJOIN〜条件付きサブクエリにはmergeBindingsを使おう〜」でも紹介しましたが、mergeBindingsでバインド値を合わせることができます。

        $subquery1 = DB::table('table1')
            ->where('status', '=', 'Success');
        $subquery2 = DB::table('table2')
            ->where('status', '=', 'New');
        $query = DB::table(DB::raw("({$subquery1->toSql()}) as s1"))
            ->leftjoin(DB::raw("({$subquery2->toSql()}) as s2"), function($join){
                $join->on('s1.id', '=','s2.relid');
            })
            ->mergeBindings($subquery1)
            ->mergeBindings($subquery2)
        ->where('t.id', '=', 1);
        $data = $query->get();

疑問符パラメータを使用していますので生成されるSQLを意識しながら順番にバインドされるように設定をしていきます。

生成されるSQL

$query->toSql();
>>
select * from (select * from `table1` where `status` = ?) as s1 left join (select * from `table2` where `status` = ?) as s2 on `s1`.`id` = `s2`.`relid` where `t`.`id` = ?

バインド値の配列

$query->getBindings();
>>
array (size=3)
  0 => string 'Success' (length=7)
  1 => string 'New' (length=3)
  2 => int 1

コメント

タイトルとURLをコピーしました