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

标签 cakephp cakephp-3.x cakephp-3.3

我正在关注Using SQL Functions构建我的查询。我倾向于获得第一笔到期分期付款。在将 cakephp 更新到版本 3.3(由 Composer )之前,我的查询正在运行(在 cakephp3.1 上)。

在我的 Controller 中

$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"), 我收到此错误:

You are required to select the "PaymentInstalments.order_id" field(s)

如果我用这个

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

我收到此错误:

Error: SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '"id": 41408, "order_id": "10000", "instalment_num": 1, "amount": ' at line 2

请帮忙

最佳答案

您不能使用收集方法

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

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

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

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

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

Object of class Cake\Database\Expression\FunctionExpression could not be converted to string

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

该警告的原因与 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。

整个“以不同方式放置 key 和行为改变”仅在错误处理程序回调内部调用像 asort() 这样的数组函数时才会发生。它们被调用的数据并不重要,它不需要以任何方式连接到错误,它可能只是类似于 $foo = []; asort($foo);,这已经会导致奇怪的行为。

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

你一定会喜欢 PHP :)

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

作为修复 bug 之前的解决方法,您可以将 SQL 片段作为字符串传递,例如

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

最后但并非最不重要的

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

关于cakephp - 在 cakephp 中使用 SQL 函数返回错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39081359/

相关文章:

mysql - 蛋糕 3 : column of type JSON stores a NULL as string "null"

date - 如何更改 CakePHP 3 表单中出生日期字段的顺序

php - 如何在 url 中附加用户名而不是 cakephp 3 中的 user_id

validation - CakePHP3 中令人困惑的验证与应用程序规则

php - CakePHP 3 - 通过其他表连接?

php - 插件模型中的 CakePHP 3 defaultConnectionName 不起作用

php - CakePHP 3 中的自定义配置文件

mysql - cakePHP 中的子查询在其他表中查找

CakePHP - 基于 URL 的数据库配置

php - 如何阻止 CakePHP 在输出中放置注释标签