天天看點

關于laravel中如何在where中使用in這回事原因 總結

原因 

事情是這樣的,新的項目中使用laravel作為開發架構,在使用查詢構造器的時候就出現了這個問題。我在查詢的時候需要使用 ,結果發現 下面這種使用方式是錯誤的,是以就花時間研究了一下。

$where[] = ['id','in',[1,2]];
           

官方給出的答案是使用:

whereIn()
           

我很不解,十分不解,是以就在網上搜了搜資料,有人指出如果想再where中使用in 可以使用這種方法:

$whereOr[] = [Db::Raw("id in (" . join(',',[1,2,3]) . ')'),1];
           

确實,這種方法是可以的,但是通過列印日志可以發現 這種方式最後出來的sql是有有問題的,它的sql是:

id in (1,2,3) = 1 
           

這種方式的sql 和傳統的相比 效果是一緻的。問題是這種sql是不走索引的,因為sql的where條件中是不允許出現表達式的。最後在網上實在找不到解決的辦法,是以去檢視了源碼,where條件的源碼如下:

public function where($column, $operator = null, $value = null, $boolean = 'and')
    {
        //很明顯當我們使用in 的時候是走了這裡 我們進去addArrayOfWheres這個地方看一看
        if (is_array($column)) {
            return $this->addArrayOfWheres($column, $boolean);
        }

       
        [$value, $operator] = $this->prepareValueAndOperator(
            $value, $operator, func_num_args() === 2
        );

      
        if ($column instanceof Closure) {
            return $this->whereNested($column, $boolean);
        }

       
        if ($this->invalidOperator($operator)) {
            [$value, $operator] = [$operator, '='];
        }

        if ($value instanceof Closure) {
            return $this->whereSub($column, $operator, $value, $boolean);
        }

       
        if (is_null($value)) {
            return $this->whereNull($column, $boolean, $operator !== '=');
        }

        $type = 'Basic';

      
        if (Str::contains($column, '->') && is_bool($value)) {
            $value = new Expression($value ? 'true' : 'false');

            if (is_string($column)) {
                $type = 'JsonBoolean';
            }
        }

       

        if (! $value instanceof Expression) {
            $this->addBinding($value, 'where');
        }

        return $this;
    }
           

當我們的where 條件是下面這種方式的時候where 函數中會調用addArrayOfWheres 這個函數。

[
    ['id','=',1],
    ['status','in',[1,2,3]]
]
           

addArrayOfWheres 這個函數就是吧二維數組中每個數組的中的三個元素分别調用where函數。這樣思路就很清晰了。當我們的['status','in',[1,2,3]] 作為元素執行到這裡的時候會變成where('status','=',[1,2,3]) 這種方式調用where函數。但是where函數不可以這樣使用,在laravel中in查新要使用whereIn 函數。是以我們們應該吧where函數改一改。

protected function addArrayOfWheres($column, $boolean, $method = 'where')
    {
        return $this->whereNested(function ($query) use ($column, $method, $boolean) {
            foreach ($column as $key => $value) {
                if (is_numeric($key) && is_array($value)) {
                    $query->{$method}(...array_values($value));
                } else {
                    $query->$method($key, '=', $value, $boolean);
                }
            }
        }, $boolean);
    }
           

至于如何修改where,我是這樣改的:當$operator == ‘in’ 的時候調用whereIn ,經測試是可以的(注意 加入代碼的位置,是放在if(is_array($column)))的後面。

public function where($column, $operator = null, $value = null, $boolean = 'and')
    {
        // If the column is an array, we will assume it is an array of key-value pairs
        // and can add them each as a where clause. We will maintain the boolean we
        // received when the method was called and pass it into the nested where.
        if (is_array($column)) {
            return $this->addArrayOfWheres($column, $boolean);
        }

        if($operator === 'in'){
            return $this->whereIn($column,$value,$boolean);
        }
           

總結

想在where中使用in 查詢 有兩種辦法:

1 缺點是無法使用索引

$whereOr[] = [Db::Raw("id in (" . join(',',[1,2,3]) . ')'),1];
           

2 需要修改源碼,目前不知道會不會影響其他地方,如果有知道的大佬請留言,在此先感謝了!!

 修改位置在vendor/laravel/framework/src/Illuminate/Database/Query/Builder.php  不同版本的laravel 可能位置不同,作者的版本是5.8的在604行加入

if($operator === 'in'){
            return $this->whereIn($column,$value,$boolean);
        }