Estoy siguiendo Uso de funciones SQL para crear mi consulta. Tiendo a recibir la primera cuota pagadera. Mi consulta estaba funcionando (en cakephp3.1) antes de actualizar cakephp a la versión 3.3 (por el compositor).

En mi controlador

$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();

En mi tabla de pagos

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;
}

Aquí está la impresión de mi consulta.

'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)'

El problema es que si uso "id" => $query->func()->min("$alias.id"), obtengo este error:

Debe seleccionar el campo (s) "PaymentInstalments.order_id"

Y si uso esto

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

Recibo este error:

Error: SQLSTATE [42000]: Error de sintaxis o infracción de acceso: 1064 Tiene un error en su sintaxis SQL; Consulte el manual que corresponde a la versión de su servidor MySQL para conocer la sintaxis correcta para usar cerca de '"id": 41408, "order_id": "10000", "instalment_num": 1, "amount":' en la línea 2

Cualquier ayuda por favor

2
Fury 22 ago. 2016 a las 16:51

2 respuestas

La mejor respuesta

No puedes usar métodos de recolección

$query->min() es un método de colección, respectivamente ejecutará la consulta y llamará al método en el conjunto de resultados resultante, es decir, no tiene nada que ver con funciones SQL. Solo mire su salida de depuración, hay datos del conjunto de resultados en su consulta SQL.

Consulte Libro de recetas> Acceso a la base de datos y ORM> Creador de consultas> Las consultas son objetos de colección

$query->func()->min() es el camino a seguir, eso es lo que genera un objeto de expresión de función SQL.

Hay un error en el núcleo con respecto a las expresiones de funciones.

Dicho esto, lo que está experimentando es un error que está presente en la comprobación de existencia de presencia de clave externa , y se activa al tener expresiones en la lista de selección. Debería ver aún más errores, respectivamente advertencias como

El objeto de la clase Cake \ Database \ Expression \ FunctionExpression no se pudo convertir a una cadena

¡Asegúrate de incluir siempre cosas como esas en tus preguntas! Si no lo ve, intente aumentar su nivel de informe de errores.

La causa de esa advertencia, combinada con un error de PHP, es la fuente de todo el problema, la lista de selección no se pasa a array_diff(), que usa la comparación de cadenas, es decir, (string)$elementA === (string)$elementB, que fallará ya que El objeto de expresión no se puede convertir en una cadena.

PHP es raro

Y ahora viene una peculiaridad interesante, sin eso, solo verías una advertencia, pero la consulta funcionaría bien.

Antes de PHP 7, si coloca la clave que se está buscando, es decir, order_id, directamente después de la expresión de la función en la lista de selección, entonces array_diff() no la encontrará y dirá que falta . Sin embargo, si tiene al menos un elemento adicional entre él y la expresión de la función, es decir, algo como

'id' => $query->func()->min("$alias.id"),
// instead of here
'amount',
'order_id', // put it here
'date_due',
'transaction_id'

Entonces array_diff() lo encontrará, y la consulta se ejecutará bien a pesar de que se arroje el error de conversión de cadena. Pero eso no es todo, no, no, no sería PHP si no estuvieran sucediendo cosas realmente extrañas.

Todo " coloca la clave de manera diferente y cambia el comportamiento " solo ocurre cuando se invocan funciones de matriz como asort() dentro de la devolución de llamada del controlador de errores. No importa qué datos se estén invocando, no necesita estar conectado con el error de ninguna manera, podría ser algo como $foo = []; asort($foo);, que ya causaría ese comportamiento extraño.

https://3v4l.org/2nT5M

Tienes que amar PHP :)

Utilice un fragmento de SQL codificado como solución alternativa

Como solución alternativa hasta que se solucione el error, puede pasar un fragmento SQL como una cadena, como

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

Última pero no menos importante

Informe el problema de conversión de cadenas como un error en GitHub .

1
ndm 22 ago. 2016 a las 17:53

Puede hacer que funcione temporalmente codificándolo:

$alias = $this->alias();
$query->select(['id' => "MIN($alias.id)"]);
1
BadHorsie 22 ago. 2016 a las 17:36