1
votes

I am trying to write a trigger that will update a RTA field on a parent object with an image from a RTA field on the child object.

I am getting the below error when creating the child object

Apex trigger Updateparent caused an unexpected exception, contact your administrator: Updateparent: execution of AfterInsert caused by: System.StringException: Invalid id: (.....IMAGE IS DISPLAYED HERE....)External entry point.

Info

Man Utd is the child object Account is the parent object Image__c is the RTA field on the child object Copy_image__c is the RTA field on accounts

Here is the trigger code

trigger Updateparent on Man_Utd_1__c (after insert, after update) {

    Map<ID, Account> parentAccounts = new Map<ID, Account>();

    List<Id> listIds = new List<Id>();

    for (Man_Utd_1__c childObj : Trigger.new) {
        listIds.add(childObj.Image__c);
    }

    parentAccounts = new Map<Id, Account>([SELECT id, (SELECT ID, Image__c FROM Man_Utd_s__r) FROM Account WHERE ID IN :listIds]);

    for (Man_Utd_1__c manu : Trigger.new) {
        Account myParentAccounts = parentAccounts.get(manu.Image__c);
        myParentAccounts.Copy_image__c = manu.Image__c;
    }

    update parentAccounts.values();
}

Can anyone advise on how to rectify this or if it is even possible to do?

3

3 Answers

0
votes

EDIT to answer comments

This one works for me, there's a slight modification around the null check. Give it a go? The field is called Image__c both on Account and Contact.

trigger rollupImage on Contact (after insert, after update) {

    Set<Account> parents = new Set<Account>();
    for (Contact child : trigger.new) {
        if(child.AccountId != null && child.Image__c != null){
            parents.add(new Account(Id = child.AccountId, Image__c = child.Image__c));
        }
    }

    if(!parents.isEmpty()){
        List<Account> toUpdate = new List<Account>();
        toUpdate.addAll(parents);
        update toUpdate;
    }
}

ORIGINAL

The error

It's all written there ;)

List<Id> listIds = new List<Id>();

for (Man_Utd_1__c childObj : Trigger.new) {
    listIds.add(childObj.Image__c);    // boom happens here?
}

You're assigning rich text area value (very long string with encoded image in it) to Id variable (15 or 18 char special string)

Try like this:

Set<Id> ids = new Set<Id>(); // Set will ensure we won't have duplicate values
for (Man_Utd_1__c childObj : Trigger.new) {
    ids.add(childObj.Account__c);
}

Optimization - round 1

In the second loop you're setting the Copy_Image__c field but on the local copy of Account (local variable in the scope of the loop). I'm not sure setting it there will propagate the change to the parent acc (it might be a full copy and not a reference to the item in the map. You could put it back (parentAccounts.put(manu.Account__c, myParentAccounts)) to the map or just do the image assignment directly:

parentAccounts = new Map<Id, Account>([SELECT id FROM Account WHERE ID IN :ids]);
// I've removed the subquery, you don't need it, right?

for (Man_Utd_1__c manu : Trigger.new) {
    if(parentAccounts.containsKey(manu.Account__c)){
        parentAccounts.get(manu.Account__c).Copy_image__c = manu.Image__c;
    }
}
update parentAccounts.values();

Optimization - round 2

It looks bit stupid - we query for Accounts but all we need to fetch is the Id... But we know the Id already, right? So - behold this "pro trick" (shared just because my colleague is a big fan of Man U ;))

trigger Updateparent on Man_Utd_1__c (after insert, after update) {

    Set<Account> parents = new Set<Account>();
    for (Man_Utd_1__c child : trigger.new) {
        if(child.Account__c != null){
            parents.add(new Account(Id = child.Account__c, Copy_Image__c = child.Image__c));
        }
    }

    if(!parents.isEmpty()){
        List<Account> toUpdate = new List<Account>();
        toUpdate.addAll(parents);
        update toUpdate;
    }
}
0
votes

Note: Optimization is the key thing while writing trigger.

While writing trigger make sure you follow all the best practises:

Keynotes On Trigger:

  • OPTIMIZATION - I have specified it in Uppercase and in first line because its the key thing while writing logic
  • Proper indentation and code commenting, so that anyone who works on the same code will easily understand the logic by just glancing on the comments. Make sure you update lastModifiedBy line on initial comments so that it will be helpful to keep a trace who worked last/updated last and on what date. Better keep proper code comments on every line- IF NEEDED ONLY
  • Variables with proper names ex: if var of list type then 'lst' prefix or 'List' postfix and then the proper name ex: lstContactsToInsert OR lstContactsToUpdate
  • Always create a seperate handler class and don't write all the logics on trigger itself 'AS A BEST PRACTISE TO FOLLOW'
  • Have proper trigger contexts. Know when to use proper trigger contexts through salesforce documentations on trigger. Use 'Before' contexts if it can be handled using this, If Not then go for 'After' contexts so you limit DML's.

Keynotes On Handler:

  • The First 3 steps as mentioned for trigger applies here as well
  • Use common private methods where you write logic and use it in other methods

Keynotes On TestClass:

  • Always write a test class utilizing 'GENERIC TEST DATA CLASS' in your test class.
  • Use proper test method names like: test_Method_One 'OR' testImageUpdates as such
  • Use asserts and test all usecases - Both negative and positive tests

#1. Trigger

   /**
    *   @TriggerName : ContactTrigger
    *   @CreatedBy   : Rajesh Kamath
    *   @CreatedOn   : 30-Nov, 2016
    *   @Description : Trigger to update the image on parent object 'Account' based on the child object 'Contact' 
    *   @LastModified: None
    */  
    trigger ContactTrigger on Contact(after insert, after update) {

        //Instantiate the handler class
        ContactTriggerHandler objContactHandler = new ContactTriggerHandler();

        /* Trigger Context */
        if(Trigger.isAfter) {

            //Fire on Insert
            if(Trigger.isInsert) {

                objContactHandler.onAfterInsert(trigger.new); 
            }

            //Fire on Update
            if(Trigger.isUpdate) {

                objContactHandler.onAfterUpdate(trigger.new, trigger.oldMap);
            }
        }
    }

#2. Handler

   /**
     *   @HandlerName   : ContactTriggerHandler 
     *   @TriggerName   : ContactTrigger 
     *   @TestClassName : ContactTriggerHandlerTest [Make sure you create this with the steps at the end of this handler] 
     *   @CreatedBy   : Rajesh Kamath
     *   @CreatedOn   : 30-Nov, 2016
     *   @Description : Trigger to update the image on parent object 'Account' based on the child object 'Contact' 
     *   @LastModified: None
     */  
    public with sharing class ContactTriggerHandler {

        /*
           @MethodName : onAfterInsert
           @Parameters : List<Contact> lstNewContactRecords
           @LastModifiedBy : None [Make sure when anyone updates even this line will be updated for help for other users who work on later phase]
        */
        public void onAfterInsert(List<Contact> lstNewContactRecords){

            //Call a common method where we have logic
            reflectChildImageOnParent(lstNewContactRecords, null);      
        }

        /*
           @MethodName : onAfterUpdate
           @Parameters : List<Contact> lstContactRecords, Map<Id, Contact> mapOldContactRecords
           @LastModifiedBy : None [Make sure when anyone updates even this line will be updated for help for other users who work on later phase]
        */
        public void onAfterUpdate(List<Contact> lstContactRecords, Map<Id, Contact> mapOldContactRecords){

            //Call a common method where we have logic
            reflectChildImageOnParent(lstContactRecords, mapOldContactRecords);     
        }

        /*
           @MethodName : reflectChildImageOnParent
           @Parameters : List<Contact> lstContactRecords, Map<Id, Contact> mapOldContactRecords
           @LastModifiedBy : None [Make sure when anyone updates even this line will be updated for help for other users who work on later phase]
        */
        private static void reflectChildImageOnParent(List<Contact> lstContactRecords, Map<Id, Contact> mapOldContactRecords){

            /* Local variable declaration */
            List<Account> lstAccountsToUpdate = new List<Account>();

            for(Contact objContact : lstContactRecords) {

                //Execute on insert and update
                if((Trigger.isInsert || (Trigger.isUpdate && objContact.Child_Image__c != mapOldContactRecords.get(objContact.Id).Child_Image__c)) && objContact.Account__c != null) {

                    //List of accounts to update
                    lstAccountsToUpdate.add(new Account(Id = objContact.Account__c, Parent_Image__c = objContact.Child_Image__c));                  
                }
            }

            //Update the account records
            if(!lstAccountsToUpdate.isEmpty()) {

                update lstAccountsToUpdate;
            }
        }
    }

This can be referred for your usecase. Please mark as 'Best Answer' if this was helpful

-1
votes

loyalty - Child Obj

fields:

  1. lightingdata__Contact__c (relationship)
  2. lightingdata__imagenims__c // Image field

Contact - Parent Obj

field: lightingdata__Imagefromloyalty__c

trigger loyaltytocon on Loyalty__c (after insert, after update) {
    Map<id, Contact> conlist;
        List<Id> idlist=new List<id>();
        for (Loyalty__c loy:trigger.new) {    
            idlist.add(loy.lightingdata__Contact__c);
        }
        conlist= new Map<id, contact>([select id, lightingdata__Imagefromloyalty__c from contact where id In : idlist]);

        for(Loyalty__c lo:trigger.new){
            contact con = conlist.get(lo.lightingdata__Contact__c) ;
            system.debug('con data' + con);
            con.lightingdata__Imagefromloyalty__c = lo.lightingdata__imagenims__c;
        }
     update  conlist.values();
}

In pareant object field it will return that image filed have URL. Then you can create one formula filed and just refer (lightingdata__Imagefromloyalty__c) in formul it will display the image.