在cakephp中使用SQL函数返回错误

在cakephp中使用SQL函数返回错误,cakephp,cakephp-3.x,cakephp-3.3,Cakephp,Cakephp 3.x,Cakephp 3.3,我将按照以下步骤构建我的查询。我倾向于拿到第一笔到期分期付款。我的查询(在cakephp3.1上)在更新cakephp到版本3.3(由composer)之前工作 在我的控制器中 $this->loadModel('Orders'); $order = $this->Orders->find() ->where(['order_id' => $orderId]) ->contain([ 'PaymentIn

我将按照以下步骤构建我的查询。我倾向于拿到第一笔到期分期付款。我的查询(在cakephp3.1上)在更新cakephp到版本3.3(由composer)之前工作

在我的控制器中

$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)),
我会得到以下错误:

您需要选择“付款分期付款.订单id”字段

如果我用这个

'id' => $query->func()->min(function($row){
    return $row->id;
}),`
我得到这个错误:

错误:SQLSTATE[42000]:语法错误或访问冲突:1064您 SQL语法有错误;检查相应的手册 您的MySQL服务器版本需要使用“id”附近的正确语法: 41408,“订单编号”为“10000”,“分期付款数量”为1,“金额”为: 第2行

请提供任何帮助

您不能使用收集方法
$query->min()
是一个集合方法,分别执行查询并调用结果集上的方法,即它与SQL函数无关。只要看看调试输出,SQL查询中就有结果集数据

请参见

$query->func()->min()
就是要走的路,这就是生成SQL函数表达式对象的方法

关于函数表达式,内核中有一个bug 也就是说,您遇到的是中存在的错误,并且是由选择列表中的表达式触发的。您应该会看到更多错误,分别是警告,如

无法将类Cake\Database\Expression\FunctionExpression的对象转换为字符串

请确保在您的问题中始终包含类似的内容!如果您没有看到,请尝试提高错误报告级别

该警告的原因,再加上一个PHP错误,是整个问题的根源,选择列表ist被传递到
array_diff()
,它使用字符串比较,即
(string)$elementA===(string)$elementB
,这将失败,因为表达式对象无法转换为字符串

PHP很奇怪 现在出现了一个有趣的怪癖,如果没有它,您只会看到一个警告,但是查询会运行良好

在PHP7之前,如果将正在搜索的键(即
order\u id
)直接放在选择列表中的函数表达式之后,则
array\u diff()
将找不到它,并说它丢失了。但是,如果在它和函数表达式之间至少有一个附加元素,例如

'id' => $query->func()->min("$alias.id"),
// instead of here
'amount',
'order_id', // put it here
'date_due',
'transaction_id'
然后
array_diff()
将找到它,尽管抛出了字符串转换错误,查询仍将正常执行。但这还不是全部,不,不,如果没有真正奇怪的事情发生,就不会是PHP

整个“以不同的方式放置键和行为更改”仅在错误处理程序回调内部调用数组函数(如
asort()
)时才会发生。不管调用的是什么数据,它都不需要以任何方式连接到错误,它可能只是类似于
$foo=[];asort($foo),这会导致这种奇怪的行为

你一定喜欢PHP:)

使用硬编码的SQL片段作为解决方法 在bug修复之前,作为一种变通方法,您可以将SQL片段作为字符串传递,如

'id' => 'MIN(PaymentInstalments.id)'
最后不能不提
请将字符串转换问题报告为错误。

您可以通过硬编码暂时使其正常工作:

$alias = $this->alias();
$query->select(['id' => "MIN($alias.id)"]);

谢谢@ndm。我确认这是一个输入错误,并且我已经正确地编写了func(),但仍然存在相同的问题。目前有什么解决办法吗?@Fury解决办法是传递一个硬编码的SQL片段,比如
'id'=>'MIN(paymentinstallaments.id)