Fluent Query Builder以错误的顺序获取绑定变量


Fluent Query Builder Getting Bind Variables in Wrong Order?

我一直在为一些查询没有返回所有应该返回的行而头疼,我认为原因是绑定变量没有正确排序。这是一个流畅的bug,还是我做错了什么?

下面是一个简单的例子,来展示正在发生的事情。查询q1从具有简单where条件的表中进行选择。查询q2联接到一个具有ON语句中条件的表(有点)。主查询q联接到一个具有任意条件的表。

$q1 = DB::table('c')->where('d', '=', 'second');
$q2 = DB::table('e')->join('f', function($join){$join->where('f.id', '=', 'third');});
$q = DB::table('x')->join('y', function($join){$join->where('y.id', '=', 'first');})
    ->unionAll($q1) // binds to 'second'
    ->unionAll($q2); // binds to 'third'
var_dump($q->toSql());
var_dump($q->getBindings());

当它运行时,这就是Fluent(在Laravel 4.2中)生成的查询和绑定数组:

(select * from `x` inner join `y` on `y`.`id` = ?)
union all
(select * from `c` where `d` = ?)
union all
(select * from `e` inner join `f` on `f`.`id` = ?)
array(3) {
  [0]=>
  string(2) "first"
  [1]=>
  string(2) "third"
  [2]=>
  string(2) "second"
}

假设绑定变量从开始到结束按顺序匹配,则第二个和第三个查询的绑定变量是错误的,例如(select * from c where d = 'f1'),它应该是(select * from c where d = 'd1')

似乎q1的where子句被放在绑定变量数组的末尾,而不管随后向并集添加了多少额外的查询。也许这就是这个简单的例子看起来的样子。也许绑定变量不应该按照我认为的顺序排列?

Laravel将上述查询和绑定数组直接传递给PDO,无需进一步处理。

我的问题的答案是"是"。Fluent在UNION查询之间混合绑定变量,因此最终的绑定变量数组的顺序错误。

Laravel查询生成器为数组中查询的每个部分保留绑定变量:

// Illuminate'Database'Query'Builder
protected $bindings = array(
    'select' => [],
    'join'   => [],
    'where'  => [],
    'having' => [],
    'order'  => [],
);

这样,您就可以按照自己喜欢的任何顺序构建查询——先SELECT,先WHERE,或者稍微混合一下。这对于单个查询来说非常好。

然后,在构建联合时,Laravel所做的是将联合中每个查询的这些数组合并在一起。这就是绑定变量混淆的地方。相反,它应该将每个查询的绑定变量数组解析为单个(线性)数组,然后按照连接联合查询的顺序连接这些数组。

我把它当作一个bug来养。我看不到一个简单的解决方案,所以我使用了一个变通方法:分别运行每个查询,然后在PHP中将结果合并在一起,而不是期望数据库在联合查询中合并结果。其他解决方案,如手动将查询SQL和绑定连接在一起,并将它们直接传递给PDO也可以,但我尽量不在Laravel中重写逻辑。

https://github.com/laravel/framework/issues/5833

我的建议是,至少不要在Laravel 4.2中使用工会。如果您的查询都没有任何绑定变量,那么您可以使用它。但是,请注意,查询生成器将对您传递到查询任何部分的任何字符串、数字、日期、数字或数组使用绑定变量。