0

I have three objects:
equipments, maintenance, Organization

If a 'Maintenance' record gets a date marked, I want it to update on the organization a checkbox that = true if any equipment gets a date. The relationship goes like this:

  • mainetenance has a lookup to equipment
  • then equipment has a lookup to organization.

So parent= organization, then the child is equipment, then grandchild is maintenance I basically just want to update the parent record when any of the grandchildren records has the check date filled out.

public static void markCompleted(Map<Id, Maintenance__c> newMain, Map<Id, Maintenance__c> oldMain){
    Set<Id> equipmentRelated = new Set<Id>();
    for(Id id : newMain.keyset()){
        if(newMain.get(id).Check_Date__c !=  null && newMain.get(id).Check_Date__c != newMain.get(id).Check_Date__c){
            equipmentRelated.add(newMain.get(id).Equipment__c);
        }
    }
    if(equipmentRelated.size() >0){

I've started the class. I've written things where it would go from parent to child to grandchild and update that grandchild record but never the opposite direction where we update the parent.

2
  • This might be a little more clear if you were to use the terms "Parent", "Child", and "Grandchild" (or "Child", "Parent", and "Grandparent").
    – Derek F
    Jul 5, 2018 at 19:19
  • Updated, never though of referring it that way. Thanks! Jul 5, 2018 at 19:30

3 Answers 3

0

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;
0
0

Final code should look like this:

public static void markCompleted(Map<Id, Maintenance__c> newMain, Map<Id, Maintenance__c> oldMain){
    Set<Id> equipmentRelated = new Set<Id>();
    for(Id id : newMain.keyset()){
        if(newMain.get(id).Check_Date__c !=  null && newMain.get(id).Check_Date__c != newMain.get(id).Check_Date__c){
     equipmentRelated.add(newMain.get(id).Equipment__c);
     }
   }
   List<Equipment__c> equipmentsToUpdate = new List<Equipment__c>();
   List<Organization__c> organizationsToBeUpdated = new List<Organization__c>();

   if(equipmentRelated.size() >0){
     for(Id equipmentId : equipmentRelated){
        equipmentsToUpdate.add(new Equipment__c(Id=equipmentId , Checkbox_to_be_Checked__c = true ));
     }
     update equipmentsToUpdate;
     for(Equipment__c eq : [Select ID , Organization__c From Equipment__c WHERE Id IN:equipmentRelated AND Organization__c != null]){      
        organizationsToBeUpdated.add(new Organization__c( Id =eq.Organization__c, Checkbox_to_be_Checked__c = true ));
     }
     update organizationsToBeUpdated;
   }
 }
3
  • 6
    Teach a man to fish...pure code dumps with no explanation are low quality answers. We would typically rather see you explain how to achieve what the OP is after, and use code to illustrate where necessary.
    – Adrian Larson
    Jul 5, 2018 at 19:13
  • 4
    This answer would be improved if you went into detail about why your approach works. Code dumps tend not to be very useful to other people with similar (but different) problems, and have a tendency to generate a lot of "that didn't work"/"how about this [more code]" back-and-forth comments (which are even less helpful to others)
    – Derek F
    Jul 5, 2018 at 19:14
  • I Appreciate the code, but I'm not sure why I need to update equipment...there is no checkbox that needs to be updated on equipment at all only on the organization level. Jul 5, 2018 at 19:36
0

When you have Id, you have everything you need to update. Check this example:

update new Account(Id = '001a0000da3ad', Name = 'Changed Name');

And thats all. By specyfing Id in new instance of record (Account in that case) you can insert diffrent values to fields and call update, and in result exisitng SObject with this Id will be updated, without querying it. SOQL saved updating SObjects using that approach.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .