[SOLVED] Accessing Relational Object Data

I have these relations defined in the model Number.php:




			'customers'=>array(self::HAS_MANY, 'Customer', 'numberId'),

			'tickets'=>array(self::HAS_MANY, 'Ticket', 'numberId'),

			'assignments'=>array(self::HAS_MANY, 'Assignment', 'numberId'),



I do a query in a controller action /number/history:




$numberHistory = Number::model()->with('customers.addresses','customers.notes','tickets.notes','assignments.card')->together()->findByPk(1);



In the view I do a foreach over $numberHistory to get at each customer record. For each customer record, I do another foreach to loop over their addresses and their notes.

I can loop over the addresses fine with:




<?php foreach($customer->addresses as $address): ?>



But using the same construct for ‘notes’ give me an error ‘Invalid argument supplied for foreach()’. Here’s the line for ‘notes’ looping:




<?php foreach($customer->notes as $note): ?>



Why does the loop for ‘addresses’ work but the one for ‘notes’ does not? They are sibling arrays…

Your usage of relations is little strange for me.

I usually to do like at the manual:




Post::model()->with(array(

    'author'=>array('select'=>'id, name'),

    'comments'=>array('condition'=>'approved=1', 'order'=>'createTime'),

))->findAll();



Have you already tried to output print_r($object) or count($object) the results? Do you have a MySQL Monitor?

I verified the resulting object with CVarDumper::dump and all the data is there.

My usage is also from the manual :)

Great CVarDumper! :D I didn’t know it!

You are SURE you are not trying to loop thought an empty array or your element are not a string. Are you?


   

if(empty($myArray))

   {

     print 'something';

   }



I don’t like debug way too much. :(

The first snippet shows the ‘addresses’ and ‘notes’ arrays as siblings:




array

(

    [0] => Customer#1

    (

        [CActiveRecord:_md] => CActiveRecordMetaData#2

        (

            [tableSchema] => CMysqlTableSchema(...)

            [columns] => array(...)

            [relations] => array(...)

            [attributeDefaults] => array(...)

            [CActiveRecordMetaData:_model] => Customer(...)

            [CActiveRecordMetaData:_validators] => null

        )

        [CActiveRecord:_new] => false

        [CActiveRecord:_attributes] => array

        (

            [id] => '1'

            [numberId] => '1'

            [fname] => 'Some'

            [lname] => 'Customer'

            [addressId] => '1'

            [altContant] => null

            [created] => '0000-00-00'

            [modified] => '0000-00-00'

            [notes] => null

            [active] => '1'

            [activationDate] => '2009-05-01'

            [cancelDate] => null

        )

        [CActiveRecord:_related] => array

        (

            [addresses] => array(...)

            [notes] => array(...)

        )

        [CActiveRecord:_c] => null

        [CModel:_errors] => array()

        [CModel:_va] => null

        [CModel:_se] => ''

        [CComponent:_e] => null

        [CComponent:_m] => null

    )

)



Here’s the content of the notes array:




 [notes] => array

            (

                [0] => Note#21

                (

                    [CActiveRecord:_md] => CActiveRecordMetaData#22

                    (

                        [tableSchema] => CMysqlTableSchema(...)

                        [columns] => array(...)

                        [relations] => array(...)

                        [attributeDefaults] => array(...)

                        [CActiveRecordMetaData:_model] => Note(...)

                        [CActiveRecordMetaData:_validators] => null

                    )

                    [CActiveRecord:_new] => false

                    [CActiveRecord:_attributes] => array

                    (

                        [id] => '1'

                        [customerId] => '1'

                        [ticketId] => null

                        [text] => 'This customer has seasonal LD changes during which he travels.'

                    )

                    [CActiveRecord:_related] => array()

                    [CActiveRecord:_c] => null

                    [CModel:_errors] => array()

                    [CModel:_va] => null

                    [CModel:_se] => ''

                    [CComponent:_e] => null

                    [CComponent:_m] => null

                )

            )



Could you please show your relations defined in Customer model?

Here are the relations in Customer.php




	public function relations()

	{

		return array(

			'addresses'=>array(self::HAS_MANY, 'Address', 'customerId'),

			'notes'=>array(self::HAS_MANY, 'Note', 'customerId'),

		);

	}



I personally hate pretty much every code that looks correct to me. :P

Are you sure you didn’t close the outer foreach accidentally? I simply can’t think of anything else.

Here’s my ugly test code in case you see something there ;) $history is the root object I pass into the array:




<pre><?php CVarDumper::dump($history->customers, 3, true); ?></pre>


<div>


	<?php foreach($history->customers as $customer): ?>

	

		<?php echo "$customer->fname $customer->lname"; ?>

		

		<?php foreach($customer->addresses as $address): ?>

			<?php echo $address->street; ?>

			<?php echo "PO Box ".$address->poBox; ?>

			<?php echo $address->city.','; ?>

			<?php echo " ".$address->state; ?>

			<?php echo "  ".$address->zipCode; ?>

		<?php endforeach; ?>

		

		<?php foreach($customer->notes as $note): ?>

			<?php echo $note->text; ?>

		<?php endforeach; ?>

		

	<?php endforeach; ?>

	

</div>



Could you past the log information that Yii gives you at the bottom of your page?

Here’s the output of CWebLogRoute with all levels:




13:04:39.213783	trace	system.web.CModule	

Loading "log" application component

13:04:39.216139	trace	system.web.CModule	

Loading "request" application component

13:04:39.218400	trace	system.web.CModule	

Loading "urlManager" application component

13:04:39.231743	trace	system.web.CModule	

Loading "db" application component

13:04:39.233192	trace	system.db.CDbConnection	

Opening DB connection

13:04:39.317886	trace	system.db.CDbCommand	

Querying SQL: SHOW COLUMNS FROM `Number`

13:04:39.319236	trace	system.db.CDbCommand	

Querying SQL: SHOW CREATE TABLE `Number`

13:04:39.327145	trace	system.db.CDbCommand	

Querying SQL: SHOW COLUMNS FROM `Customer`

13:04:39.328712	trace	system.db.CDbCommand	

Querying SQL: SHOW CREATE TABLE `Customer`

13:04:39.329436	trace	system.db.CDbCommand	

Querying SQL: SHOW COLUMNS FROM `Address`

13:04:39.330573	trace	system.db.CDbCommand	

Querying SQL: SHOW CREATE TABLE `Address`

13:04:39.331269	trace	system.db.CDbCommand	

Querying SQL: SHOW COLUMNS FROM `Note`

13:04:39.332479	trace	system.db.CDbCommand	

Querying SQL: SHOW CREATE TABLE `Note`

13:04:39.333303	trace	system.db.CDbCommand	

Querying SQL: SHOW COLUMNS FROM `Ticket`

13:04:39.334747	trace	system.db.CDbCommand	

Querying SQL: SHOW CREATE TABLE `Ticket`

13:04:39.335750	trace	system.db.CDbCommand	

Querying SQL: SHOW COLUMNS FROM `Assignment`

13:04:39.337606	trace	system.db.CDbCommand	

Querying SQL: SHOW CREATE TABLE `Assignment`

13:04:39.338529	trace	system.db.CDbCommand	

Querying SQL: SHOW COLUMNS FROM `Card`

13:04:39.339678	trace	system.db.CDbCommand	

Querying SQL: SHOW CREATE TABLE `Card`

13:04:39.340106	trace	system.db.ar.CActiveRecord	

Number.findByPk() eagerly

13:04:39.341799	trace	system.db.CDbCommand	

Querying SQL: SELECT `Number`.`id` AS `t0_c0`, `Number`.`number` AS

`t0_c1`, t1.`id` AS `t1_c0`, t1.`numberId` AS `t1_c1`, t1.`fname` AS

`t1_c2`, t1.`lname` AS `t1_c3`, t1.`addressId` AS `t1_c4`, t1.`altContant`

AS `t1_c5`, t1.`created` AS `t1_c6`, t1.`modified` AS `t1_c7`, t1.`notes`

AS `t1_c8`, t1.`active` AS `t1_c9`, t1.`activationDate` AS `t1_c10`,

t1.`cancelDate` AS `t1_c11`, t2.`id` AS `t2_c0`, t2.`customerId` AS

`t2_c1`, t2.`poBox` AS `t2_c2`, t2.`city` AS `t2_c3`, t2.`state` AS

`t2_c4`, t2.`zipCode` AS `t2_c5`, t2.`street` AS `t2_c6`, t3.`id` AS

`t3_c0`, t3.`customerId` AS `t3_c1`, t3.`ticketId` AS `t3_c2`, t3.`text` AS

`t3_c3`, t4.`id` AS `t4_c0`, t4.`numberId` AS `t4_c1`, t4.`customerId` AS

`t4_c2`, t4.`cardId` AS `t4_c3`, t5.`id` AS `t5_c0`, t5.`customerId` AS

`t5_c1`, t5.`ticketId` AS `t5_c2`, t5.`text` AS `t5_c3`, t6.`id` AS

`t6_c0`, t6.`cardId` AS `t6_c1`, t6.`active` AS `t6_c2`, t6.`pair` AS

`t6_c3`, t6.`ldCarrier` AS `t6_c4`, t6.`numberId` AS `t6_c5`, t6.`system`

AS `t6_c6`, t6.`channel` AS `t6_c7`, t6.`blade` AS `t6_c8`, t6.`port` AS

`t6_c9`, t6.`class` AS `t6_c10`, t6.`seiscor` AS `t6_c11`, t7.`id` AS

`t7_c0`, t7.`assignmentId` AS `t7_c1`, t7.`serialNumber` AS `t7_c2`,

t7.`status` AS `t7_c3` FROM `Number`  LEFT OUTER JOIN `Customer` t1 ON

(t1.`numberId`=`Number`.`id`) LEFT OUTER JOIN `Address` t2 ON

(t2.`customerId`=t1.`id`) LEFT OUTER JOIN `Note` t3 ON

(t3.`customerId`=t1.`id`) LEFT OUTER JOIN `Ticket` t4 ON

(t4.`numberId`=`Number`.`id`) LEFT OUTER JOIN `Note` t5 ON

(t5.`ticketId`=t4.`id`) LEFT OUTER JOIN `Assignment` t6 ON

(t6.`numberId`=`Number`.`id`) LEFT OUTER JOIN `Card` t7 ON

(t7.`id`=t6.`id`) WHERE (`Number`.`id`=1)

13:04:39.351234	trace	system.web.CModule	

Loading "clientScript" application component



Nothing stands out, no errors or warnings.

Ok. What do you see at the Stack Trace? Is it displayed in your page?

I don’t see anything with Stack Trace. What do I need to configure in addition to CWebLogRoute?

After rereading this topic several times and finally stopped banging my head to the wall, I can summarize my point of view in three words.

Oh. My. God.

To try out what works and what doesn’t, I’d remove the call to together() in the first place. If that’s no use, remove all with() requirements and let Yii populate activerecords in even more steps.

Of course this is not the desired result, but after all one cannot replace php interpreter to teach the bastard how to foreach an array in the right way.

Edit:

Is your application in debug stage (YII_DEBUG in entry script)?

Thats mine screen. I did a invalid foreach example:

Now I think that the foreach problem is not with notes…thats my reason to see the stack

oh, THAT stack trace ;)

Here:




#0 /srv/www/yii-1.0.8.r1317/framework/web/CBaseController.php(119): require()

#1 /srv/www/yii-1.0.8.r1317/framework/web/CBaseController.php(88): NumberController->renderInternal()

#2 /srv/www/yii-1.0.8.r1317/framework/web/CController.php(701): NumberController->renderFile()

#3 /srv/www/yii-1.0.8.r1317/framework/web/CController.php(640): NumberController->renderPartial()

#4 /srv/www/htdocs/protected/controllers/NumberController.php(14): NumberController->render()

#5 /srv/www/yii-1.0.8.r1317/framework/web/actions/CInlineAction.php(32): NumberController->actionHistory()

#6 /srv/www/yii-1.0.8.r1317/framework/web/CController.php(300): CInlineAction->run()

#7 /srv/www/yii-1.0.8.r1317/framework/web/CController.php(278): NumberController->runAction()

#8 /srv/www/yii-1.0.8.r1317/framework/web/CController.php(257): NumberController->runActionWithFilters()

#9 /srv/www/yii-1.0.8.r1317/framework/web/CWebApplication.php(332): NumberController->run()

#10 /srv/www/yii-1.0.8.r1317/framework/web/CWebApplication.php(120): CWebApplication->runController()

#11 /srv/www/yii-1.0.8.r1317/framework/base/CApplication.php(134): CWebApplication->processRequest()

#12 /srv/www/htdocs/index.php(11): CWebApplication->run()



@pestaa: Trying your suggestions now…

I removed together() and still no joy. Then I removed all the with() relations and populated individual objects with each table and they all work fine, ‘notes’ included.

Then I tried all combinations of relations with with() but no together() and they all work, except when ‘notes’ is present.

Then I thought maybe the code was only seeing the first array, so I tried looping over ‘notes’ first and then ‘addresses’ but that didn’t change anything.

I’m not savvy enough to know what else to try, but something’s funky.

PHP foreach can’t be broken. And the error points to ‘notes’, and ‘notes’ does exist.

The only thing this could be is ‘notes’ isn’t where it’s supposed to be in the object heirarchy, somewhere other than what the dumps show, although I can’t seem to access it in any way.

I can post the complete object dump if you think it’s worth it.

It looks like you have a notes attribute in the Customer record.

/Tommy

Tommy,

excellent job! I voted your entry up.