我正在按照使用 SQL 函数来构建我的查询。我倾向于获得第一笔到期分期付款。在将 cakephp 更新到版本 3.3(由作曲家)之前,我的查询正在运行(在 cakephp3.1 上)。
在我的控制器中
$this->loadModel('Orders');
$order = $this->Orders->find()
->where(['order_id' => $orderId])
->contain([
'PaymentInstalments' => function($q) {
return $q->find('firstDue');
},
'Users' => function($q) {
return $q->select(['email']);
}
])
->first();
在我的付款分期付款表上
public function findFirstDue(Query $query, array $options)
{
$alias = $this->alias();
$query
->select([
// "id" => $query->func()->min("$alias.id"), ## This use to work before but now is not working
'id' => $query->func()->min(function($row){
return $row->id;
}),
'order_id', 'amount', 'date_due', 'transaction_id'
])
->contain([
'Transactions' => function($q) {
return $q
->select([
'id', 'transaction_date'
])
->where(function ($exp, $q) {
return $exp->isNull('transaction_date');
});
}
]);
debug($query->__debugInfo()['sql']);
die;
return $query;
}
这是我的查询的打印。
'SELECT PaymentInstalments.id AS `PaymentInstalments__id`, PaymentInstalments.order_id AS `PaymentInstalments__order_id`, PaymentInstalments.instalment_num AS `PaymentInstalments__instalment_num`, PaymentInstalments.amount AS `PaymentInstalments__amount`, PaymentInstalments.date_due AS `PaymentInstalments__date_due`, PaymentInstalments.payment_email_sent AS `PaymentInstalments__payment_email_sent`, PaymentInstalments.transaction_id AS `PaymentInstalments__transaction_id`, {
"id": 41408,
"order_id": "10000",
"instalment_num": 1,
"amount": 100,
"date_due": "2016-08-25T12:15:00+01:00",
"payment_email_sent": false,
"transaction_id": null
} AS `PaymentInstalments`.`id`, Transactions.id AS `Transactions__id`, Transactions.transaction_date AS `Transactions__transaction_date` FROM payment_instalments PaymentInstalments LEFT JOIN transactions Transactions ON ((Transactions.transaction_date) IS NULL AND Transactions.id = (PaymentInstalments.transaction_id)) WHERE PaymentInstalments.order_id in (:c0)'
问题是如果我使用
"id" => $query->func()->min("$alias.id"),
我会收到此错误:
您需要选择“PaymentInstalments.order_id”字段
如果我用这个
'id' => $query->func()->min(function($row){
return $row->id;
}),`
我收到此错误:
错误:SQLSTATE[42000]:语法错误或访问冲突:1064 SQL 语法有错误;检查对应的手册 您的 MySQL 服务器版本,以便在 '"id" 附近使用正确的语法: 41408, "order_id": "10000", "instalment_num": 1, "amount": ' at 2号线
请帮忙
$query->min()
是一个集合方法,分别会执行查询并在结果集上调用该方法,即与SQL函数无关。只需查看您的调试输出,您的 SQL 查询中就有结果集数据。
请参阅 Cookbook > 数据库访问和 ORM > 查询生成器 > 查询是集合对象
$query->func()->min()
是正确的方法,这就是生成 SQL 函数表达式对象的方法。
话虽这么说,您遇到的是外键存在存在检查中存在的错误,并且是通过选择列表中的表达式触发的。您应该会看到更多错误,分别是警告,例如
Cake\Database\Expression\FunctionExpression 类的对象无法转换为字符串
请确保在您的问题中始终包含此类内容!如果您没有看到它,请尝试提高错误报告级别。
该警告的原因与 PHP 错误相结合,是整个问题的根源,选择列表被传递给
array_diff()
,它使用字符串比较,即 (string)$elementA === (string)$elementB
,这将失败,因为表达式对象不能转换为字符串。
现在出现了一个有趣的怪癖,如果没有这个怪癖,您只会看到警告,但查询会运行良好。
在 PHP 7 之前,如果将要搜索的键,即
order_id
直接放在选择列表中的函数表达式之后,那么 array_diff()
将找不到它,并说它丢失了。但是,如果它和函数表达式之间至少有一个附加元素,即类似
'id' => $query->func()->min("$alias.id"),
// instead of here
'amount',
'order_id', // put it here
'date_due',
'transaction_id'
然后
array_diff()
will 找到它,尽管抛出字符串转换错误,查询仍将正常执行。但这还不是全部,不不,如果没有发生真正奇怪的事情,它就不会是 PHP。
整个“以不同方式放置密钥和行为改变”仅在错误处理程序回调内部调用像
asort()
这样的数组函数时才会发生。它们被调用的数据并不重要,它不需要以任何方式连接到错误,它可能只是像 $foo = []; asort($foo);
这样的东西,这已经会导致奇怪的行为。
你一定会喜欢 PHP :)
作为修复 bug 之前的解决方法,您可以将 SQL 片段作为字符串传递,例如
'id' => 'MIN(PaymentInstalments.id)'
请将字符串转换问题报告为错误 在 GitHub。
您可以通过硬编码来暂时使其工作:
$alias = $this->alias();
$query->select(['id' => "MIN($alias.id)"]);