Help With Model Relations "Trying To Access a Non-Object"

I inherited an unfinished cake project at work that is a total mess so since I am going to rebuild it from scratch I thought I would sink my teeth into Yii.

I am not sure if I have been staring at this too long or I just don’t understand how this is suppose to really work but I am having some strange issues with model relations.

I am trying access a defined relation in a view and I am getting a trying to access a non object error.

Here is the code for my view. Notice how I am using a relation to access the organization’s name and that works fine. But when I try and get the contact name I get the error. They appear to be the same thing to me and I not sure what I am doing wrong.

Any help to point me in the right direction would greatly be appreciated




<div class="view">


	<h3><?php echo CHtml::encode($data->job_title); ?></h3>

	<h4><?php //echo CHtml::encode($data->organization->name); ?></h4> // this one works

	<p><?php echo CHtml::encode($data->job_description); ?></p>


	<p>Contact</p>


	<p><?php //echo CHtml::encode($data->contact->first_name); ?></p> // this one doesn't

</div>



opportunities model




	return array(

			'organization' => array(self::BELONGS_TO, 'Organizations', 'organization_id'),

			'contact' => array(self::BELONGS_TO, 'Contacts', 'contact_id'),

	...




Opportunities Table


CREATE TABLE IF NOT EXISTS `opportunities` (

  `id` int(11) NOT NULL AUTO_INCREMENT,

  `organization_id` int(11) NOT NULL COMMENT 'CONSTRAINT FOREIGN KEY (organization_id) REFERENCES organizations(id)',

  `contact_id` int(11) NOT NULL COMMENT 'CONSTRAINT FOREIGN KEY (contact_id) REFERENCES contacts(id)',

...



Contacts Table




CREATE TABLE IF NOT EXISTS `contacts` (

  `id` int(11) NOT NULL AUTO_INCREMENT',

  `organization_id` int(11) NOT NULL COMMENT 'COMMENT "CONSTRAINT FOREIGN KEY (organization_id) REFERENCES organizations(id)"',

  `first_name` varchar(200) NOT NULL,

  `last_name` varchar(200) NOT NULL,

  `phone` char(12) NOT NULL,

  `email` varchar(200) NOT NULL,

  `primary_contact` tinyint(4) NOT NULL DEFAULT '0',

  `created` datetime NOT NULL,

  `modified` datetime NOT NULL,

  PRIMARY KEY (`id`)

) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=334 ;



How do you populate $data? Try this first (will load primary and all related records in the same query)


$data = Opportunities::model()->with('organization', 'contact')->together()->findall();

/Tommy

Thanks for the quick reply.

I am not sure how data gets populated. I am using the code created by gii CRUD. Here is the widget from the index.php file




<?php $this->widget('zii.widgets.CListView', array(

	'dataProvider'=>$dataProvider,

	'itemView'=>'_view',

)); ?>



At any rate I put your code at the top my _view file and the same error occurred but for the first item

[font="Arial"]$data-[/font][font="Arial"]>job_title

I then tried your code [/font]but renamed it to $data2 and used $data2->contact->first_name and got the original error.

The weird thing is that I have done the exact same thing using the organization table with a relation to the contact table and it works fine.

Oh Well. thanks again for your quick response

Then it would be populated by a CDataProvider in the search() method of the Opportunities model.

That’s expected because you don’t have this column in DB. I couldn’t decide if you declared it as property in the model, though.

This is weird if you really use Opportunities as primary. Double check the generated attributes in the model (not sure what happens if you use underscore in column name).

I’m confused (since the attribute name seems to be OK). You have to tell us more about which models are involved and which relationships you want to use in your query. Is it A belongs to B and A belongs to C. Or is it A belongs to B which belongs to C (you have multiple constraint hints in the tables). In the latter case (if using eager loading) you should use the format


with('B', 'B.C')

instead of


with('B', 'C')

.

/Tommy

I am a little confused by some of your answers and I think I am not explaining myself very good. So I am going to try to explain again.

I have 3 tables opportunities, contacts, organizations ( I have more tables but these are the ones that apply to this problem).

here are the model realtions. I have removed the ones that don’t apply to this problem

model/opportunity.php




	return array(


			'organization' => array(self::BELONGS_TO, 'Organizations', 'organization_id'),

			'contact' => array(self::BELONGS_TO, 'Contacts', 'contact_id', 'together'=>true),

        	...



model/organization.php




   return array(

  		...

        	'primary' => array(self::BELONGS_TO, 'Contacts', 'primary_contact'),

		);



model/contacts.php




   	return array(

       		'organization' => array(self::BELONGS_TO, 'Organizations', 'organization_id'),

           	'opp'=> array(self::HAS_MANY, 'Opportunities', 'contact_id'),

		);

 

organization controller




 $dataProvider =  new CActiveDataProvider('Organizations');

    	$this->render('index',array('dataProvider'=>$dataProvider,));



organization view/index




<?php $this->widget('zii.widgets.CListView', array(

	'dataProvider'=>$dataProvider,

	'id'=>'organizations-list',

	'itemView'=>'_view',

	'template'=>"{summary}{sorter}{pager}{items}\n{pager}",

  

)); ?>



organization view/_view

I am able to print the contacts first name by using the relation primary.




<div class="view">


	<h3><?php echo CHtml::encode($data->name); ?></h3>

   <p><?php echo CHtml::encode($data->mission_statement); ?></p>

   <h4>Contact Info</h4>

   [b]<?php  echo $data->primary->first_name; echo ' '.$data->primary->last_name; ?>[/b]

	- <?php echo CHtml::encode($data->phone); ?>

	<br />

	<?php echo CHtml::encode($data->primary->email); ?><br />

	<?php echo CHtml::link(CHtml::encode('View'), array('view', 'id'=>$data->id)); ?>



Now when I try and do the exact same thing but with the opportunities_view I get the non-object error





<div class="view">

	<h3><?php echo CHtml::encode($data->job_title); ?></h3>

	<h4><?php echo CHtml::encode($data->organization->name); ?></h4>

	<p><?php echo CHtml::encode($data->job_description); ?></p>


	<p>Contact</p>


	<p><?php echo CHtml::encode($data->contact->first_name);?></p>




I don’t understand how trying to access the contacts first_name field the exact same way I did for the organization _view will give me a accessing a non object error. To me they look pretty much the same so I am confused why one would work while the other doesn’t.

here are the tables

Opportunities Table




CREATE TABLE IF NOT EXISTS `opportunities` (

  `id` int(11) NOT NULL AUTO_INCREMENT,

  `organization_id` int(11) NOT NULL COMMENT 'CONSTRAINT FOREIGN KEY (organization_id) REFERENCES organizations(id)',

  `contact_id` int(11) NOT NULL COMMENT 'CONSTRAINT FOREIGN KEY (contact_id) REFERENCES contacts(id)',

  `contact_hours` varchar(200) NOT NULL,

  `street_address` varchar(200) NOT NULL,

  `city` varchar(100) NOT NULL,

  `job_description` text NOT NULL,

  `job_category_id` int(11) NOT NULL COMMENT 'CONSTRAINT FOREIGN KEY (job_category_id) REFERENCES job_categories(id)',

  `vehicle` tinyint(1) NOT NULL DEFAULT '0',

  `background_check` tinyint(1) NOT NULL DEFAULT '0',

  `wheelchair` tinyint(1) NOT NULL DEFAULT '0',

  `transit` tinyint(1) NOT NULL DEFAULT '0',

  `removal_date` date DEFAULT NULL,

  `work_with_age_id` int(11) NOT NULL COMMENT 'CONSTRAINT FOREIGN KEY (work_with_age_id) REFERENCES ages(id)',

  `time_of_day_time_id` int(11) NOT NULL COMMENT 'CONSTRAINT FOREIGN KEY (time_of_day_id) REFERENCES time_of_days(id)',

  `age_id` int(11) NOT NULL COMMENT 'CONSTRAINT FOREIGN KEY (age_id) REFERENCES ages(id)',

  `post_length_id` int(11) NOT NULL COMMENT 'CONSTRAINT FOREIGN KEY (post_length_id) REFERENCES post_lengths(id)',

  `created` datetime NOT NULL,

  `modified` datetime NOT NULL,

  `job_title` varchar(255) NOT NULL,

  `number_of_volunteers` int(11) NOT NULL,

  `time_of_day_day_id` int(11) NOT NULL COMMENT 'CONSTRAINT FOREIGN KEY (time_of_day_day_id) REFERENCES time_of_days(id)',

  `transportation` tinyint(1) NOT NULL,

  PRIMARY KEY (`id`)

) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=106 ;



Contacts Table




CREATE TABLE IF NOT EXISTS `contacts` (

  `id` int(11) NOT NULL AUTO_INCREMENT,

  `organization_id` int(11) NOT NULL COMMENT 'CONSTRAINT FOREIGN KEY (organization_id) REFERENCES organizations(id)',

  `first_name` varchar(200) NOT NULL,

  `last_name` varchar(200) NOT NULL,

  `phone` char(12) NOT NULL,

  `email` varchar(200) NOT NULL,

  `primary_contact` tinyint(4) NOT NULL DEFAULT '0',

  `created` datetime NOT NULL,

  `modified` datetime NOT NULL,

  PRIMARY KEY (`id`)

) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=334 ;




Organization table




CREATE TABLE IF NOT EXISTS `organizations` (

  `id` int(11) NOT NULL AUTO_INCREMENT,

  `user_id` int(11) NOT NULL COMMENT 'CONSTRAINT FOREIGN KEY (user_id) REFERENCES users(id)',

  `primary_contact` int(11) NOT NULL COMMENT 'CONSTRAINT FOREIGN KEY (primary_contact) REFERENCES contact(id)',

  `name` varchar(255) NOT NULL,

  `mission_statement` text NOT NULL,

  `phone` char(12) NOT NULL,

  `fax` char(12) DEFAULT NULL,

  `street_address` varchar(200) NOT NULL,

  `postal_code` char(7) NOT NULL,

  `city` varchar(100) NOT NULL,

  `province_id` int(11) NOT NULL COMMENT 'CONSTRAINT FOREIGN KEY (province_id) REFERENCES provinces(id)',

  `approved` tinyint(1) NOT NULL,

  `created` datetime NOT NULL,

  `modified` datetime NOT NULL,

  `website` varchar(200) DEFAULT NULL,

  `renew_date` date NOT NULL,

  PRIMARY KEY (`id`)

) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=218 ;




So you probably have this code in opportunities controller




  $dataProvider =  new CActiveDataProvider('Opportunities');

  $this->render('index',array('dataProvider'=>$dataProvider,));



See the the similarities to my previous suggestion in the example in the CActiveDataProvider API reference.

http://www.yiiframework.com/doc/api/1.1/CActiveDataProvider

In your case, something similar to




  $dataProvider =  new CActiveDataProvider('Opportunities' array(

  ...

  'with'=>array('organization', 'contact'),

  'together'=>true, // may be optional

  ...

));

$this->render('index',array('dataProvider'=>$dataProvider,));



If still no luck, it’s a good idea too enable logging and examine the generated SQL

http://www.yiiframework.com/doc/guide/1.1/en/topics.logging

(not tested)

/Tommy

I do have this in the opportunities controller.

I did try the using "with" in the CActiveDataProvider but I got the same error

Here is the query from the log which work fine when in phpMyAdmin when I substitute :yp10 for a contact id.




SELECT `contact`.`id` AS `t1_c0`, `contact`.`organization_id`

AS `t1_c1`, `contact`.`first_name` AS `t1_c2`, `contact`.`last_name` AS

`t1_c3`, `contact`.`phone` AS `t1_c4`, `contact`.`email` AS `t1_c5`,

`contact`.`primary_contact` AS `t1_c6`, `contact`.`created` AS `t1_c7`,

`contact`.`modified` AS `t1_c8` FROM `contacts` `contact`  WHERE

(`contact`.`id`=:ypl0)



BTW. Thanks again for all your effort.

Well I moved my web app from local wamp 2.0 server running on windows 7 to my hostgator account on the internet and everything is working fine. There is 6 hours I will never get back. I guess it is a small consolation to know I am not crazy and I did learn a lot of other things reading posts and tutorials. Special thanks to tri for your efforts. I will try and pay if forward to someone else one day.

Buddy, I think your problem states in the declaration of the foreign keys of the model class Contact:




//contact 

'organization' => array(self::BELONGS_TO, 'Organizations', 'organization_id'),


//organization

'contact' => array(self::BELONGS_TO, 'Contacts', 'contact_id', 'together'=>true)



should be:




// contact

'organization' => array(self::HAS_ONE, 'Organizations', 'primary_contact')

// or -if a contact can be in many organizations

'organization' => array(self::HAS_MANY, 'Organizations', 'primary_contact')


// organization

'contact' => array(self::BELONGS_TO, 'Contacts', 'primary_contact')



Nevertheless, I recommend you that you review your DB structure as I believe that an organization should have MANY contacts and not one contact HAS ONE or HAS MANY organizations. This is the reason why was confusing at first.

IMHO I will put an organization_id FOREING_KEY to contacts and relate to organization table. Then on your relations you can create the relations according to TWO rules: a HAS_MANY contacts and HAS_ONE primary contact, and then in contacts do BELONGS_TO.

Its just a suggestion…

regards

I just want to add that the difference could be that the contact name is null for one or more records.

That would produce that error.

The solution is to wrap the output statement in a isset block.

Just adding it here, because it’s not uncommon to overlook the blatant errors. ;)

I had moved on because it worked on a test directory on my live server. But I woke up this morning to find your great advice and it worked. I very happy to be back on my localhost test server. If I could give you a hundred votes for this post I would.

You definitely win my hero of the day award.

Now my joy is fading into shame and I feel stupid. Oh well I guess I will never make that mistake again. Thanks again jacmoe and everyone else who tried to help.