+ Start a Discussion
Kenji775Kenji775 

Spidering relationship returns nulls

Hey all.

Create a new trigger that should create a task for a person when another type of object is created. Problem is, when I attempt to get values from a related object that I need to create the task, they always come back as null. The relationship field itself has a value, but reading and values by accessing that relationship is null.

 

EX

object.relationship__c = a0UR0000002QiRH

object.relationship__r.id = null

 

Those should both point to the same thing! Why is the latter null? 

 

 

trigger createChangeOrderTask on Project_Change_Order__c (after insert) 
{
	//List of tasks to create
	List<Task> tasks = new List<Task>();
	
	//List to hold user IDs
	Map<string,id> userMap = new map<string,id>();
	
	//Find all users and add their email and ID to a map (so we can find user ID based on email later)
	for(user u : [select Id, email from user])
	{
		userMap.put(u.email,u.id);	
	}
	
	system.debug('Users:' + userMap);
	
	//For all the change orders that got passed in, iterate
	for(Project_Change_Order__c co : Trigger.new)
	{

		Task thisTask = new Task();
        
        //The project manager field is a contact, but a task requires a user. So based on the users email
        //field, find the user with the same email, and use that ID		
		thisTask.OwnerId = userMap.get(co.bid__r.Project_Manager__r.email);
		system.debug('Related To Bid:' + co.bid__c);
		
		//All these debugs return null. WHAT?! That is impossible.
		system.debug('Bid ID (read from object):' + co.bid__r.id);		
		system.debug('Bid Name:' + co.bid__r.name);
		system.debug('Project Manager ID:' + co.bid__r.Project_Manager__c);
		system.debug('Project Manager Email:' + co.bid__r.Project_Manager__r.email);
		system.debug('Found Owner ID:' + thisTask.OwnerId);
		
		//If we were able to find a person to assign the task to, populate the rest of the taks data
		if(thisTask.OwnerId != null)
		{
			thisTask.subject = 'New Change Order For '+co.bid__r.name;
			
			//Create a date based on the datetime
			thisTask.ActivityDate = Date.newInstance(co.Due_Date__c.year(),co.Due_Date__c.month(),co.Due_Date__c.day());
			thisTask.Description = co.Detail__c;
			thisTask.ReminderDateTime = co.Due_Date__c;
			thisTask.WhoId = co.Client_contact__c;
			thisTask.IsReminderSet = true;
			
			tasks.add(thisTask);
		}
		else
		{
			system.debug('Could not find user with matching email as project manager. No task created.');
		}
	}
	insert tasks;
	
}

 

 

 

 

Ritesh AswaneyRitesh Aswaney

In a before or after insert trigger, the relationship has not been committed to the db, so the __r evaluates to null.

 

You'd have to create a list to aggregate the references and then do a select query to retrieve the related objects.

something like this :

 

List<Id> bidsIds = new List<Id>{};

 

for(Project_Change_Order__c projChangeO : trigger.new)

bidsIds.add(co.Bid__c);

 

Map<Id, Bid__c> bidsMap = new Map<Id, Bid__c> ([Select Id, Name, Project_Manager__c, Project_Manager__r.Name from Bid__c where Id in : bidsIds]);

 

for(Project_Change_Order__c projChangeO : trigger.new)

{

......

Bid__c currBid = bidsMap.get(projChangeO.Bid__c);

 

//You can then use this currBid reference to acess Project_Manager__c, Project_Manager__r.Name et all

Kenji775Kenji775

Really? That's crazy, I could have sworn relationships were established in an after insert trigger. Seems I was wrong. Thanks for the heads up, I guess I'll get refactoring. Thanks!

Ritesh AswaneyRitesh Aswaney

Yep, strange but true, or so I've learnt from experience !

If this were a before or after update trigger __r would evaulate to the reference.

forecast_is_cloudyforecast_is_cloudy

A minor correction. The reason that you don't have access to parent relationship fields (i.e. '....__r.Some_Field__c') in a trigger (before or after, insert or update) is not related to whether or not the parent record has been committed to the DB - it has. The reason has to do with efficiency. The system has no way of knowing which parent fields a particular trigger might be interested in. Therefore it would have to internally query and make available all the fields values on every parent relationship in a trigger context. Since any object can potentially have hundreds of fields (and there can be many parent relationships on a given object) that would not be very efficient/perfomant. 

 

Therefore, no relationship field values are passed in by default and you have to explicitly query whichever parent field you're interested in via a separate SOQL query (as Ritesh demonstrated in his code).

 

You could imagine that someday we'd be able to introspect the trigger code to see which (if any) relationship fields are being referenced and provide just those values. However its tricky to determine which fields are being referenced (especially with dynamic SOQL) and so for now you have to explicitly query any relationship fields (other than the Id of course which is available in the respective '__c' field) that you need in your trigger.

 

Hope this clarifies somewhat.

DodiDodi

use the platform....build a formula field into the child object to pull down the field you need and reference that formula field in your apex....

 

unless you want to build additional code you dont need for doing the queries: )