Использование функций SQL в cakephp возвращает ошибку
Я следую Использование функций SQL для построения моего запроса. Я обычно получаю первый взнос. Мой запрос работал (на cakephp3.1) до обновления cakephp до версии 3.3 (составитель).
В моем контроллере
$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": "в строке 2
Любая помощь, пожалуйста
2 ответа
Вы не можете использовать методы сбора
$query->min()
является методом сбора, соответственно выполнит запрос и вызовет метод для результирующего набора результатов, то есть он не имеет ничего общего с функциями SQL. Просто посмотрите на результаты отладки, в вашем SQL-запросе есть данные набора результатов.
$query->func()->min()
это путь, это то, что генерирует объект выражения функции SQL.
В ядре есть ошибка, связанная с выражениями функций
При этом, то, что вы испытываете, - это ошибка, которая присутствует в проверке наличия присутствия внешнего ключа и запускается при наличии выражений в списке выбора. Вы должны увидеть еще больше ошибок, соответственно, как предупреждения
Объект класса 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);
, это уже вызвало бы это странное поведение.
Ты должен любить PHP:)
Используйте жестко закодированный фрагмент SQL в качестве обходного пути
В качестве обходного пути, пока ошибка не будет устранена, вы можете вместо этого передать фрагмент SQL в виде строки, например
'id' => 'MIN(PaymentInstalments.id)'
Последний, но тем не менее важный
Пожалуйста, сообщите о проблеме преобразования строк как об ошибке на GitHub.
Вы можете временно заставить его работать, жестко запрограммировав его:
$alias = $this->alias();
$query->select(['id' => "MIN($alias.id)"]);