Laravel-扩展Eloquent where子句取决于动态参数


Laravel - extending Eloquent where clauses depending on dynamic parameters

我想构建一系列雄辩的WHERE子句,这些子句依赖于我从json对象收集的搜索参数。

像这样的东西(别管对象的语法,,这只是一种解释,只是为了演示):

$searchmap = "
{
    "color": "red",
    "height": "1",
    "width": "2",
    "weight": "",
    "size": "",
}";

然后我拿着这个物体进行解码,得到一个搜索数组。。。

$search = json_decode($searchmap, true);

如果我的权重和大小被设置为null或是一个"空字符串",我会得到像这样的有说服力的代码。。

$gadgets = Gadget::where('color',   '=', $search['color'])
                 ->where('height',  '=', $search['height'])
                 ->where('width',   '=', $search['width'])
                 ->paginate(9);

如果它们有一个值,那么雄辩的代码会是这样的。。

$gadgets = Gadget::where('color',   '=', $search['color'])
                 ->where('height',  '=', $search['height'])
                 ->where('width',   '=', $search['width'])
                 ->where('weight',  '=', $search['weight'])
                 ->where('size',    '=', $search['size'])
                 ->paginate(9);

有没有一种方法可以动态地实现这一点。

我想问题应该是,有没有一种方法可以根据给定的参数动态地链接雄辩的where子句?

在伪上下文中,我希望做一些类似的事情

$gadgets = Gadget::
    foreach ($search as $key => $parameter) {
        if ( $parameter <> '' ) {
            ->where($key, '=', $parameter)
        }
    }
->paginate(9);

where子句的链接可以用类似的方式创建吗?

谢谢你花时间看这个!


更新:

我也想出了这样的方法,看起来效果很好,但如果改进是个好主意,我愿意接受建议。

$gadgets = New Gadget();
    foreach ($search as $key => $parameter) {
        if($parameter != ''){
            $gadgets = $gadgets->where($key, '=', $parameter);
        }
    }
$gadgets = $gadgets->paginate(9);

最终

感谢下面的@lukasgeiter,我想我会选择这个

$gadgets = Gadget::whereNested(function($query) use ($search) {
    foreach ($search as $key => $value)
        {
            if($value != ''){
                $query->where($key, '=', $value);
            }
        }
}, 'and');
$gadgets = $gadgets->paginate(9);

这很容易。Laravel的where函数允许您传入一个键值对数组。

$searchmap = array(
    'color' => 'red',
    'height' => '1'
    // etc
);
$gadgets = Gadget::where($searchmap)->paginate(9);

如果你很好奇,那就是源('Illuminate'Database'Query'Builder)的相关部分

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->whereNested(function($query) use ($column)
        {
            foreach ($column as $key => $value)
            {
                $query->where($key, '=', $value);
            }
        }, $boolean);
    }
    // many more lines of code....
}

编辑

要对其进行更多控制(例如,将"="更改为另一个比较运算符),请尝试使用laravel内部直接使用的代码:

$gadgets = Gadget::whereNested(function($query) use ($searchmap)
        {
            foreach ($searchmap as $key => $value)
            {
                if($value != ''){
                    $query->where($key, '=', $value);
                }
            }
        }, 'and')->paginate(9);

对于任何需要它的人,这是lukasgeiter答案的一个修改版本,它解决了"可变数量的where"问题,同时还允许(1)每个where子句使用不同的运算符,以及(2)当一个"where"必须能够匹配多个值中的一个时,也可以使用whereIn(下面的函数检测何时传递值数组,因此使用whereIn而不是where)。

开头的$paramSets变量赋值(如下)主要描述了如何使用它

$paramSets = [
        "size"=>["op"=>"=","values"=>"large"],
        "production_date"=>["op"=>">=","values"=>"2015-12-31"],
        "color"=>["op"=>"=","values"=>["red","blue"]],
        "name"=>["op"=>"like","values"=>"M%"]
        ];
    $items = db::table('products')
        ->whereNested(function($query) use ($paramSets) {
            foreach ($paramSets as $k => $v)
            {
                if ($v != ''){
                    if (is_array($v["values"])){
                        $query->whereIn($k,$v["values"]);
                    }
                    else {
                        $query->where($k, $v["op"], $v["values"]);
                    }
                }
            }
        }, 'and');
    dd($items->get());