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

时间:2016-08-22 13:51:54

标签: cakephp cakephp-3.x cakephp-3.3

我正在关注Using SQL Functions来构建我的查询。我倾向于获得第一笔到期分期付款。在将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();

在我的paymentInstalments表

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"场(S)

如果我使用这个

'id' => $query->func()->min(function($row){
    return $row->id;
}),`

我收到此错误:

  

错误:SQLSTATE [42000]:语法错误或访问冲突:1064您   您的SQL语法有错误;检查对应的手册   您的MySQL服务器版本,以便在'" id"附近使用正确的语法:   41408," order_id":" 10000"," instalment_num":1," amount":'在   第2行

请帮助

2 个答案:

答案 0 :(得分:1)

您不能使用收集方法

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

参见 Cookbook > Database Access & ORM > Query Builder > Queries Are Collection Objects

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

核心中存在关于函数表达式的错误

话虽如此,您遇到的是the foreign key presence existence check中存在的错误,并且是通过在选择列表中包含表达式来触发的。您应该看到更多错误,分别是

等警告
  

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

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

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

PHP很奇怪

现在出现了一个有趣的怪癖,没有它你只会看到一个警告,但查询运行正常。

在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() 找到它,尽管抛出字符串转换错误,查询仍会正常执行。但这不是全部,不是不,如果没有真正奇怪的事情发生,它就不会是PHP。

整个“以不同方式放置密钥并且行为更改”仅在错误处理程序回调内调用asort()之类的数组函数时才会发生。它们被调用的数据无关紧要,它不需要以任何方式连接到错误,它可能只是$foo = []; asort($foo);,这已经导致了这种奇怪的行为。

<强> https://3v4l.org/2nT5M

你必须爱上PHP:)

使用硬编码的SQL片段作为解决方法

作为解决方法直到修复错误,您可以将SQL片段作为字符串传递,例如

'id' => 'MIN(PaymentInstalments.id)'

最后但并非最不重要

请将字符串转换问题报告为错误over at GitHub

答案 1 :(得分:1)

你可以通过硬编码暂时让它工作:

@echo off
REM // https://stackoverflow.com/a/39084914/122687
set QT_OPENGL=angle
start "D:\ProgramFilesOnD_NoSpaces\Qt\Qt5.7.0\Tools\QtCreator\bin\qtcreator.exe"