Eager Loading in 1.1.x

Hi team,

It’s been a while since I have used yii and I am having some issues upgrading my old applications to yii 1.1.2.

The issue I am having is when using eager loading, for example;

I am loading a record like this;


$criteria=new CDbCriteria;

        $criteria->condition = 't.is_active=1 AND e.is_primary=1';

        $success = PUser::model()->with('emails')->findByAttributes(array('nonce'=>$_GET['t']), $criteria);

        $email = $success=>emails->email;

I get this error;


PHP Error


Description


Trying to get property of non-object


Source File


C:\wamp\www\test\protected\controllers\AccountController.php(256)


00244: 

00245:     /**

00246:      * Reset a user password.

00247:      */

00248:     public function actionReset()

00249:     {


00256:         $email = $success->emails->email;

Please advise what I am doing wrong.

I simply want to get the user email address and store it, as I use this later in my behaviour.

Thanks

ooaat

bump…

anyone got a tip.

Either $success is empty (findByAttributes didn’t find anything) or $succcess->emails is empty (no related emails). So check those values to debug:


if ($success===null)

  die('success is empty');

elseif ($success->emails===null)

  die('success->emails is empty');



Also enable CWebLogRoute + param logging to check your queries.

[s]Oh, and findByAttributes doesn’t expect a CDbCriteria as second argument. So you should rewrite that to something like:


$criteria=new CDbCriteria;

$criteria->condition = 't.is_active=1 AND e.is_primary=1 AND nonce=:nonce';

$criteria->params=array(':nonce'=>$_GET['t']);

$success = PUser::model()->with('emails')->find($criteria);



[/s]

Forget that - it can be a criteria :)

I enabled CWebLogRoute and param logging;

$success shouldn’t be empty. Here is the sql when queried;


SELECT `t`.`id` AS `t0_c0`, `t`.`is_active` AS `t0_c1`,

`t`.`role_id` AS `t0_c2`, `t`.`password` AS `t0_c3`, `t`.`nonce` AS

`t0_c4`, `t`.`last_visit` AS `t0_c5`, `t`.`is_super_admin` AS `t0_c6`,

`t`.`expire_at` AS `t0_c7`, `t`.`created_at` AS `t0_c8`, `t`.`updated_at`

AS `t0_c9`, `t`.`created_by` AS `t0_c10`, `t`.`updated_by` AS `t0_c11`,

`e`.`user_id` AS `t1_c0`, `e`.`email` AS `t1_c1`, `e`.`label` AS `t1_c2`,

`e`.`is_primary` AS `t1_c3`, `e`.`is_active` AS `t1_c4`, `e`.`nonce` AS

`t1_c5`, `e`.`expire_at` AS `t1_c6`, `e`.`created_at` AS `t1_c7`,

`e`.`updated_at` AS `t1_c8`, `e`.`created_by` AS `t1_c9`, `e`.`updated_by`

AS `t1_c10` FROM `User` `t`  LEFT OUTER JOIN `Email` `e` ON

(`e`.`user_id`=`t`.`id`) WHERE (`t`.`nonce`=:yp0 AND (t.is_active=1 AND

e.is_primary=1)). Bind with parameter :yp0='qveq5fu6cv5p'

When copied into phpmyadmin and replacing :ypo with parameter;


t0_c0	t0_c1	t0_c2	t0_c3	t0_c4	t0_c5	t0_c6	t0_c7	t0_c8	t0_c9	t0_c10	t0_c11	t1_c0	t1_c1	t1_c2	t1_c3	t1_c4	t1_c5	t1_c6	t1_c7	t1_c8	t1_c9	t1_c10

53	1	1	a750680f04a89838a4af45310c5360970e08b325	qveq5fu6cv5p	1277874243	0	1278227250	1277874243	1277881650	0	0	53	test@test.com	NULL	1	1	jtotzov7nb2g	1278219843	1277874243	1277874262	0	0

t1_c1 being column ‘email’

Why is it not parsing the information

Is it because both tables have columns of the same name? Would that be throwing a spanner in the works?


Trying to get property of non-object

(C:\wamp\www\test\protected\controllers\AccountController.php:256)


Stack trace:

#0 C:\wamp\www\Yii\framework\web\filters\CFilterChain.php(129):

AccountController->runAction()

#1 C:\wamp\www\Yii\framework\web\filters\CFilter.php(41):

CFilterChain->run()

#2 C:\wamp\www\Yii\framework\web\CController.php(999):

CAccessControlFilter->filter()

#3 C:\wamp\www\Yii\framework\web\filters\CInlineFilter.php(59):

AccountController->filterAccessControl()

#4 C:\wamp\www\Yii\framework\web\filters\CFilterChain.php(126):

CInlineFilter->filter()

#5 C:\wamp\www\Yii\framework\web\CController.php(283): CFilterChain->run()

#6 C:\wamp\www\Yii\framework\web\CController.php(257):

AccountController->runActionWithFilters()

#7 C:\wamp\www\Yii\framework\web\CWebApplication.php(320):

AccountController->run()

#8 C:\wamp\www\Yii\framework\web\CWebApplication.php(120):

CWebApplication->runController()

#9 C:\wamp\www\Yii\framework\base\CApplication.php(135):

CWebApplication->processRequest()

#10 C:\wamp\www\test\index.php(12): CWebApplication->run()

REQUEST_URI=/test/account/reset?t=qveq5fu6cv5p

in C:\wamp\www\test\protected\controllers\AccountController.php (256)

in C:\wamp\www\test\index.php (12)

Is emails a HAS_MANY relation in user? If so, $success->emails would be an array. And again: what exactly is empty? $success or $success->emails?

emails is a HAS_MANY relation in PUser model.

So yes, $success->emails is an array

I did your debug and no errors, so neither is empty.

All I am trying to do is join two tables;

PUser

PEmail

I then want to filter results by $_GET[‘t’] value which is represented in PUser model by column ‘nonce’

Now that results are filtered I want to get the users email address which is stored in Model PEmail.

In previous version of yii, I just would have done;

$success->emails->email

So, as the error states, you try to access a property of a non object: $success->emails is an array, that has no properties. You should fetch the first entry from that array to get your desired record. Quick way to make this safe:


$email=isset($success->emails[0]) ? $success->emails[0]->email : '';

Thanks for all your help mike.

I thought the idea of using a ‘with’ statement in active record simplified and made it easy to be able to work with the joined data.

Can someone show me script on how they grab a column data from a joined table using active record. I swear I used to be able to do this in yii 1.0.

Thanks

I fixed this by changing the models around;


$criteria=new CDbCriteria;

		$criteria->condition = 'u.is_active=1 AND t.is_primary=1 AND u.nonce=:nonce';

		$criteria->params=array(':nonce'=>$_GET['t']);

		$success = PEmail::model()->with('user')->find($criteria);

As a result the relation was HAS_ONE

Per definition a HAS_MANY relation has many related objects. An Array is already the easiest representation of such a list. If that’s still too complicated, you can define another relation for the primary email to the same table:


public function relations() {

    return array(

        'primary_email'=>array(self::HAS_ONE,'PEmail','user_id',array(

            'condition'=>'primary_email.id_active=1 AND e.is_primary=1',

        )),

        ...

    );

}




Thanks Mike… That makes perfect sense to me now. I didn’t think to do that, cheers for your explanation