[RESUELTO] Como acceder a una propiedad creada desde la consulta

Estuve leyendo un rato sobre como utilizar SUM() desde las relaciones mediante STAT, pero tuve un par de problemas ya que no resulto realizarse una suma completa de todos los registros encontrados ya que el framework agrupa los resultados…

El codigo en mi modelo


class Lag extends CActiveRecord

{

  public function relations()

  {

    return array(

      'Total'=>array(self::STAT,'LagConcepts','lag_id','select'=>'SUM(tb_LagConcepts.amount)'),

    );

  }

}

El codigo en mi controlador


$Total = Lag::model()->with('Total')->findAll();

La consulta generada




SELECT `t`.`lag_id` AS `t0_c0` FROM `tb_lags` `t`


SELECT `lag_id` AS `c`,

SUM(tb_LagConcepts.amount) AS `s` 

FROM `tb_LagConcepts` WHERE (`tb_LagConcepts`.`lag_id` IN (1, 2))

GROUP BY `lag_id`


--+---

c | s

--+---

1 | 5

2 | 15

donde ‘c’ es la agrupacion que realiza por tabla_id y ‘s’ la suma… pero lo que busco es obtener un solo resultado, que seria la suma total de 5+15 = 20…

mmm, pero eso no es un sum de una relación, sino un sum de todos los items en total…

sin importar el ID…

Yo lo haría directo así (no se exacto si esto sale, pero es algo por el estilo):




LagConcepts::model()->find(array('select'=>'sum(amount) as qty');



En el modelo create una variable de clase $qty;




public $qty;



[color=#1C2837][size=2][color=#008800]LagConcepts[/color][/size][/color]

Hay algo que no me cuadra en lo que muestras y es que




SELECT `t`.`lag_id` AS `t0_c0` FROM `tb_lags` `t`


SELECT `lag_id` AS `c`,

SUM(tb_LagConcepts.amount) AS `s` 

FROM `tb_LagConcepts` WHERE (`tb_LagConcepts`.`budget_id` IN (1, 2)) //<-- la relacion es asi? budget_id IN?

GROUP BY `lag_id`



No deberia ser?

‘tb_LagConcepts’.id IN (1,2) ?

Can we see the relation rules on LagConcepts???

Lo siento pero acabo de testear con mis modelos y funcion a la perfeccion, pienso sinceramente que tu problema es de mal declaradas relaciones:

  1. Comprueba las relaciones entre tus modelos, si son ONExMANY, MANYxMANY, o de otra clase

  2. Chequea que LagConcept y Lag tenga sus reglas de relacion bien.

  3. Luego tu consulta en relations() esta bien…

Pero si las relaciones de tus objetos no son las correctas, olvídate… es normal que ocurra eso.

espero haber podido ayudar. si no lo consigues, siempre te queda escribir una funcion total() pero estas en el camino correcto. solo haz especial hincapie en tus relaciones, que estas esten creadas correctamente.

Un saludo!

La solucion es buena, pero en las relaciones Yii las agrupa correctamente por identificadores, no hace falta darles un identificador a no ser que el objeto sea de otra tabla no relacionada.

un saludo!

Pues bien, siendo que al utilizar array(self::STAT) y SUM() decidi seguir la recomendacion de Sebas y hacer una consulta por separado… la cual queda de la siguiente manera…

Desde este controlador invoco el modelo Lag y lo relaciono con LagConcepts, la consulta generada la realiza bien y hasta aqui sin problemas.




class TaskController extends Controller

{

  ...

  public function actionView()

  {

    $Total = Lag::model()->with('LagConcepts')->findAll(array(

      'select'=>'SUM(LagConcepts.amount) AS total',

    ));


    $this->render('view',array(

      'Cost'=>$Total,

    ));

  }

  ...

}



Segun indicaciones se debe de crear una propiedad publica en el modelo del cual estoy generando la consulta, en este caso Lag. Esta propiedad es el alias del resultado de la suma.




class Lag extends CActiveRecord

{

  public $total;

  ...

}



Y aqui es donde ya no comprendo… porque al querer ver el resultado me genera un error porque no puedo acceder a total




Trying to get property of non-object

$Cost->total



Saben que me estoy perdiendo o que me falta ??

Fijate que lo que yo hago lo hago sobre lag concepts y no sobre lag.

Saludos,

Además al hacer un findAll te devuelve un array, no un objeto… Por eso uso el find solo

También fijate de hacerlo sobre LagConcept directamente.

Al final lo unico que requeri hacer fue cambiar findAll() por un find() y la propiedad publica estaba bien en el modelo Lag, no fue necesario agregar una propiedad publica en el modelo LagConcepts.

Muchas gracias Sebas, el codigo ha funcionado muy bien… y a ti tambien Antonio por tomarte el tiempo de revisar como resolverlo…

Al parecer he encontrado un bug o error en el framework…

Pues sucede que despues de resolver el problema de utilizar la fx SUM() mi consulta no estaba lista para retornar datos validos… Me refiero a que la consulta funcionaba bien siempre y cuando retornara un valor… pero si no se retorna un valor el resultado sera nulo y ahi es donde me ocurre un error.

Entonces para evitar que la consulta retorne un valor nulo decidi utilizar IFNULL() y es donde se veo que algo anda mal…

Mas claro… veamos el codigo…




class TaskController extends Controller

{

  ...

  public function actionView()

  {

    $Total = Lag::model()->with('LagConcepts')->findAll(array(

      'select'=>'IFNULL(SUM(LagConcepts.amount),0) AS total', // esta linea es la nueva

    ));


    $this->render('view',array(

      'Cost'=>$Total,

    ));

  }

  ...

}



Originalmente la consulta se construia bien antes de agregarle IFNULL y mas o menos quedaba asi…


SELECT SUM(LagConcepts.amount) AS total ...

Ahora despues de la modificacion parece faltarle algo


SELECT IFNULL(SUM(LagConcepts.amount) AS total

Claro esto deberia de quedar de la siguiente manera… y lo que le falta es ",0)"


SELECT IFNULL(SUM(LagConcepts.amount),0) AS total

Es error mio, falta agregarle algo o hay algo con el framework que anda mal con la construccion de las consultas…

yo creo que tu consulta es la correcta (— aqui una variante: SUM(IF(columna IS NULL,0,columna)) —)… por lo que dices parece que cuando es NULL ocurre un error, podemos ver el resultado de ese error? sería interesante verlo

Antonio, intente resolver el problema implementando tu solucion pero no funciono… genera exactamente el mismo error.

El codigo nuevamente modificado con tu propuesta


class TaskController extends Controller

{

  ...

  public function actionView()

  {

    $Total = Lag::model()->with('LagConcepts')->findAll(array(

      'select'=>'SUM(IF(LagConcepts.amount IS NULL,0,LagConcepts.amount)) AS total', //otra vez modifique esta linea

    ));


    $this->render('view',array(

      'Cost'=>$Total,

    ));

  }

  ...

}

Supuestamente la consulta deberia de quedar asi y no generar ningun error


SELECT SUM(IF(LagConcepts.amount IS NULL,0,LagConcepts.amount)) AS total ...

Pero la salida original del framework es esta y claro con un error de que la columna a la que estoy haciendo select no existe. Y pues no existe porque hace falta " IS NULL,0,LagConcepts.amount)) AS total"


Active record "Lag" is trying to select an invalid column "SUM(IF(LagConcepts.amount". Note, the column must exist in the table or be an expression with alias.

Ahora… otra solucion que encontre fue dejar el codigo como inicialmente estaba funcionando y verificar que la variable retornada del select existiera, si no existe es porque un null es retornado por lo que forzo la variable total a tomar 0 e hice lo siguiente…




if (!isset($Cost->total))

  $Cost->total = 0;


$this->render('view',array(

  'Cost'=>$Cost,

));



Ok, esto funciona bien… pero tendran alguna otra implementacion mas elegante y menos parchosa ??

Es normal que te de error:




public function actionView()

  {

    $Total = Lag::model()->with('LagConcepts')->findAll(array(

      'select'=>'SUM(IF(LagConcepts.amount IS NULL,0,LagConcepts.amount)) AS total', //otra vez modifique esta linea

    ));


    $this->render('view',array(

      'Cost'=>$Total,

    ));

  }



Estas creando un criterio sobre Lag que no es aplicable en este tipo de consulta puesto que es para LagConcepts. Creo que estamos liando la perdiz y podríamos hacerlo de otra manera muuuucho mas sencilla. Hazlo de la siguiente manera a ver que tal:




public function actionView()

  {

    // solo queremos el count estadistico, sin otros objetos (mucho mas rapido que de la otra forma)

    $dataReader = Yii::app()->db->createCommand('SELECT SUM({{lagconcepts}}.amount) AS total FROM {{lagconcepts}}')->query();

    

    // leemos la fila

    $row = $dataReader->read();


    // muestro en view

    $this->render('view',array(

      'Cost'=>$row['total'],

    ));

  }




Seguro que asi te funciona de maravilla

Probe tu codigo y si… si funciono solo que tuve que realizar toda la consulta yo mismo y pues usando un framework es de esas cosas en las que casi no deberia de intervenir…

En fin… deje la solución que encontre inicialmente… aunque ahora queda registrada que se puede lograr de ambas maneras…

Muchas gracias por las respuestas, saludos