【Laravel】条件付きサブクエリにはmergeBindingsを使おう

<!-- wp:paragraph -->
<p>Laravelのクエリビルダーで条件付きサブクエリを<span class="marker">DB::raw()</span>を使用して<span class="marker">join</span>をした時に<span class="marker-red">Invalid parameter number</span>となったので、対処方法をまとめます。</p>
<!-- /wp:paragraph -->

こちらの記事は[ブログ](https://www.unagino-nedoko.com/laravel/subquery-where)でも公開しています。

<!-- wp:heading -->
<h2>Where句のバインド値をマージしないとエラーになる</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>試しに以下のようなSQLをクエリビルダ ーのDB::raw()を使用して実装していきたいと思います。</p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code>select * from (
    -- サブクエリ１
    <span class="red">select * from table1 where status = 'Success'</span>
) as s1
left join (
　　　　　　　　-- サブクエリ２
    <span class="blue">select * from table2 where status = 'New</span><span class="marker-blue"><span class="blue">'</span>
</span>) as s2 on s1.id = s2.relid
where t.id = 1</code></pre>
<!-- /wp:code -->

<!-- wp:paragraph -->
<p>定義したサブクエリを<span class="marker">toSql()</span>でSQL文を生成して、<span class="marker">DB::rwa()</span>を使用して組み立てていきたいと思います。</p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code>        //　サブクエリ１
        <span class="red">$subquery1 = \DB::table('table1')
            -&gt;where('status', '=', 'Success');</span>
        //　サブクエリ2
        <span class="blue">$subquery2 = \DB::table('table2')
            -&gt;where('status', '=', 'New');</span>

        $query = \DB::table(\DB::raw("({<span class="red">$subquery1-&gt;toSql()</span>}) as s1"))
            -&gt;leftjoin(\DB::raw("({<span class="blue">$subquery2-&gt;toSql()</span>}) as s2"), function($join){
                $join-&gt;on('s1.id', '=','s2.relid');
            })
        -&gt;where('t.id', '=', 1);
        $data = $query-&gt;get();</code></pre>
<!-- /wp:code -->

<!-- wp:paragraph -->
<p>実際に実行をする以下のようにparameterのエラーが発生します。これはサブクエリをtoSql()でSQLを生成してもバインドした値は含まれないないのが原因です。</p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code>"SQLSTATE&#91;HY093]: Invalid parameter number (SQL: select * from (select * from `table1` where `status` = 1) as s1 left join (select * from `table2` where `status` = ?) as s2 on `s1`.`id` = `s2`.`relid` where `s1`.`id` = ?) "</code></pre>
<!-- /wp:code -->

<!-- wp:heading -->
<h2>mergeBindingsを使用してバインド値を追加する</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p><span class="marker">mergeBindings()</span>を使ってバインド値をマージしていきます。<br>マージする順番も気をつける必要がありますので注意してください。</p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code>        <span class="red">$subquery1 = \DB::table('table1')
            -&gt;where('status', '=', 'Success');</span>
        <span class="blue">$subquery2 = \DB::table('table2')
            -&gt;where('status', '=', 'New');</span>
        $query = \DB::table(\DB::raw("({<span class="red">$subquery1-&gt;toSql()</span>}) as s1"))
            -&gt;leftjoin(\DB::raw("({<span class="blue">$subquery2-&gt;toSql()</span>}) as s2"), function($join){
                $join-&gt;on('s1.id', '=','s2.relid');
            })
            -&gt;<span class="red">mergeBindings($subquery1)</span>
            -&gt;<span class="blue">mergeBindings($subquery2)</span>
        -&gt;where('t.id', '=', 1);
        $data = $query-&gt;get();</code></pre>
<!-- /wp:code -->