The abstract approach to updating a related SObject is pretty simple
- Find the Id of the record you want to update
- Get an SObject instance for that Id
- Modify the instance
- Use DML to save your changes
Probably the most common approach is to gather the related record Ids, and then run a query to get SObject instances like
Set<Id> parentIds = new Set<Id>();
for(Child__c myChild :trigger.new){
parentIds.add(myChild.ParentId);
}
List<Parent__c> parentsToUpdate = new List<Parent__c>();
for(Parent__c myParent :[SELECT Id FROM Parent__c WHERE Id IN :parentIds]){
myParent.Field__c = 'value';
parentsToUpdate.add(myParent);
}
update parentsToUpdate;
That approach (and similar approaches) works fine if you're only working with records within one level of the "main" object you're working on...so either child -> parent, or parent -> child (and traversing up a hierarchy, i.e. child -> parent, is generally easier and safer)
When you start going out further into a relationship hierarchy, that starts to break down a little. Trigger context variables (TCVs for short), for example, don't contain any data from related records (i.e. if you need to use more than one period/dot/full-stop to reference the field, like Account.Owner.Name
, that isn't available in TCVs).
The pattern you'll need to use here includes an extra query.
Set<Id> parentIds = new Set<Id>();
for(Child__c myChild :trigger.new){
parentIds.add(myChild.ParentId);
}
Set<Id> grandparentIds = new Set<Id>();
for(Parent__c myParent :[SELECT Id, Grandparent__c FROM Parent__c WHERE Id IN :parentIds]){
grandparentIds.add(myParent.Grandparent__c);
}
List<Grandparent__c> grandparentsToUpdate = new List<Grandparent__c>();
for(Grandparent__c myGrandparent:[SELECT Id FROM Grandparent__c WHERE Id IN :grandparentIds]){
myGrandparent.field = 'value';
grandparentsToUpdate.add(myGrandparent);
}
update grandparentsToUpdate;
That isn't the only way to go about this, but this is a general pattern that can be adapted to your specific situation.
If you find yourself short on the number of queries you have left, you could, for example, make a modification to remove the second query
Set<Id> parentIds = new Set<Id>();
for(Child__c myChild :trigger.new){
parentIds.add(myChild.ParentId);
}
Set<Id> grandparentIds = new Set<Id>();
for(Parent__c myParent :[SELECT Id, Grandparent__c FROM Parent__c WHERE Id IN :parentIds]){
grandparentIds.add(myParent.Grandparent__c);
}
List<Grandparent__c> grandparentsToUpdate = new List<Grandparent__c>();
//for(Grandparent__c myGrandparent:[SELECT Id FROM Grandparent__c WHERE Id IN :grandparentIds]){
for(Id grandparentId :grandparentIds){
// In the constructor for SObjects, you can specify an id.
// This is really helpful when you want to avoid using queries
Grandparent__c aGrandparent = new Grandparent__c(
Id = grandparentId,
field = 'value'
);
//myGrandparent.field = 'value';
//grandparentsToUpdate.add(myGrandparent);
grandparentsToUpdate.add(aGrandparent);
}
update grandparentsToUpdate;