function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
[Lindsey Kiken][Lindsey Kiken] 

Apex: Splitting a Text String

I have a need to allow Community Portald users to add in comma delimited values to a CC_List__c long text field on the Case object, and then have the following occur:
  • Text string in the CC_List__c is split by the comma symbol
  • Newly added or updated delimited values found in the CC_List__c field are then entered into a child object called CC_List_Object__c as new related records
  • Values found in the related CC_List_Object__c's child list that are no longer listed in the CC_List__c  are deleted
I know that my starting point is listed below, but I am admittedly lost on where to take the trigger from there:
String[]  strs =  valstring.spilt(',');
Thoughts?
Best Answer chosen by [Lindsey Kiken]
[Lindsey Kiken][Lindsey Kiken]
I just figured it out. By wiping the CC List before I run the rest of my code and deleting the delete function, I achieve my desired results. 

Final code:
 
trigger CCList on Case (after insert, after update, after delete) {

    class CaseTriggerUtil {
        void handleCCFieldChangeEvent(Case [] argCases, Boolean argIsDelete) {
            Set<ID> lDeadParentIDSet = new Set<ID>();
            Set<String> lUniqueCCIDSet = new Set<String>();
            List<CC_List__c> lNewCCRecords = new List<CC_List__c>();

    delete [SELECT id FROM CC_List__c WHERE Related_Case__c IN:argCases];

            for(Case lCase : argCases) {
                if(argIsDelete) {
                    lDeadParentIDSet.add(lCase.id);
                }
                else {
                    if(lCase.CC_List__c == null || lCase.CC_List__c.trim() == '') {
                        lDeadParentIDSet.add(lCase.id);
                        continue;
                    }

                    List<String> lCCValues = lCase.CC_List__c.trim().split(',');
                    
                    for(String lStrCC : lCCValues) {
                        String lStrChildRecUniqueID = lCase.id + '_' + lStrCC.trim();
                        lNewCCRecords.add(new CC_List__c(Related_Case__c=lCase.id, My_ID__c = lStrChildRecUniqueID, Email_Address__c = lStrCC.trim()));
                        lUniqueCCIDSet.add(lStrChildRecUniqueID);
                    }
                }
            }

            if(!argIsDelete) {
                Database.insert(lNewCCRecords, false);
            }
        }
    }

    List<Case> lCases = (Trigger.isDelete ? Trigger.old : Trigger.new);
    new CaseTriggerUtil().handleCCFieldChangeEvent(lCases, Trigger.isDelete);
}

All Answers

Rajiv Penagonda 12Rajiv Penagonda 12
Lindsey, though on the surface the problem statement which you have specified seems simple, it is actually far more complex because to solve it, it would involves extensive DML operations within loops, which you can guess is a bad design.

I have proposed a solution below, which avoids DML operations inside loop. Its too hard to explain in words so I have put up a reference code (I see no reason why it will not compile). To avoid complex DML operations within loops you basically have to create two additional fields in your child object "CC_List_Object__c". One field "Case__c" which looks upto Case and another "My_ID__c" which is a text type field marked as external ID (to avoid issues related to large data later on).

Refer the sample code below:
 
trigger CaseTrigger on Case (after insert, before update, before delete) {
	class CaseTriggerUtil {
		void handleCCFieldChangeEvent(Case [] argCases) {
			Set<ID> lDeadParentIDSet = new Set<ID>();
			Set<String> lUniqueCCIDSet = new Set<String>();

			List<Case> lCases = (Trigger.isDelete ? Trigger.old : Trigger.new);
			List<CC_List_Object__c> lNewCCRecords = new List<CC_List_Object__c>();

			for(Case lCase : lCases) {
				if(Trigger.isDelete) {
					lDeadParentIDSet.add(lCase.id);
				}
				else {
					if(lCase.CC_List__c == null || lCase.CC_List__c.trim() == '') {
						lDeadParentIDSet.add(lCase.id);
						continue;
					}

					List<String> lCCValues = lCase.CC_List__c.trim().split(',');
					
					for(String lStrCC : lCCValues) {
						lStrCC = lStrCC.trim();
						String lStrChildRecUniqueID = lCase.id + '_' +  lStrCC;
						lNewCCRecords.add(new CC_List_Object__c(Name='Dummy', Case__c=lCase.id, My_ID__c = lStrChildRecUniqueID));
						lUniqueCCIDSet.add(lStrChildRecUniqueID);
					}
				}
			}

			if(!Trigger.isDelete) {
				Database.insert(lNewCCRecords, false);
			}

			delete [SELECT id FROM CC_List_Object__c WHERE Case__c IN:lDeadParentIDSet OR (Case__c IN:lCases AND My_ID__C NOT IN:lUniqueCCIDSet)];
		}
	}

	new CaseTriggerUtil().handleCCFieldChangeEvent(Trigger.new);
}

You may separate the CaseTriggerUtil class out and make the method handleCCFieldChangeEvent static, to have a clean trigger with just one line.

Hope this helps.

 
Rajiv Penagonda 12Rajiv Penagonda 12
Refer this new code. Made a couple of fixes.
 
trigger CaseTrigger on Case (after insert, before update, before delete) {
	class CaseTriggerUtil {
		void handleCCFieldChangeEvent(Case [] argCases, Boolean argIsDelete) {
			Set<ID> lDeadParentIDSet = new Set<ID>();
			Set<String> lUniqueCCIDSet = new Set<String>();
			List<CC_List_Object__c> lNewCCRecords = new List<CC_List_Object__c>();

			for(Case lCase : argCases) {
				if(argIsDelete) {
					lDeadParentIDSet.add(lCase.id);
				}
				else {
					if(lCase.CC_List__c == null || lCase.CC_List__c.trim() == '') {
						lDeadParentIDSet.add(lCase.id);
						continue;
					}

					List<String> lCCValues = lCase.CC_List__c.trim().split(',');
					
					for(String lStrCC : lCCValues) {
						String lStrChildRecUniqueID = lCase.id + '_' + lStrCC.trim();
						lNewCCRecords.add(new CC_List_Object__c(Name='Dummy', Case__c=lCase.id, My_ID__c = lStrChildRecUniqueID));
						lUniqueCCIDSet.add(lStrChildRecUniqueID);
					}
				}
			}

			if(!argIsDelete) {
				Database.insert(lNewCCRecords, false);
			}

			delete [SELECT id FROM CC_List_Object__c WHERE Case__c IN:lDeadParentIDSet OR (Case__c IN:argCases AND My_ID__C NOT IN:lUniqueCCIDSet)];
		}
	}

	List<Case> lCases = (Trigger.isDelete ? Trigger.old : Trigger.new);
	new CaseTriggerUtil().handleCCFieldChangeEvent(lCases, Trigger.isDelete);
}

 
[Lindsey Kiken][Lindsey Kiken]
Rajiv,

Please see below for my updated code:
 
trigger CCList on Case (after insert, before update, before delete) {
    class CaseTriggerUtil {
        void handleCCFieldChangeEvent(Case [] argCases, Boolean argIsDelete) {
            Set<ID> lDeadParentIDSet = new Set<ID>();
            Set<String> lUniqueCCIDSet = new Set<String>();
            List<CC_List__c> lNewCCRecords = new List<CC_List__c>();

            for(Case lCase : argCases) {
                if(argIsDelete) {
                    lDeadParentIDSet.add(lCase.id);
                }
                else {
                    if(lCase.CC_List__c == null || lCase.CC_List__c.trim() == '') {
                        lDeadParentIDSet.add(lCase.id);
                        continue;
                    }

                    List<String> lCCValues = lCase.CC_List__c.trim().split(',');
                    
                    for(String lStrCC : lCCValues) {
                        String lStrChildRecUniqueID = lCase.id + '_' + lStrCC.trim();
                        lNewCCRecords.add(new CC_List__c(Related_Case__c=lCase.id, My_ID__c = lStrChildRecUniqueID, Email_Address__c = lStrCC.trim()));
                        lUniqueCCIDSet.add(lStrChildRecUniqueID);
                    }
                }
            }

            if(!argIsDelete) {
                Database.insert(lNewCCRecords, false);
            }

            delete [SELECT id FROM CC_List__c WHERE Related_Case__c IN:lDeadParentIDSet OR (Related_Case__c IN:argCases AND My_ID__C NOT IN:lUniqueCCIDSet)];
        }
    }

    List<Case> lCases = (Trigger.isDelete ? Trigger.old : Trigger.new);
    new CaseTriggerUtil().handleCCFieldChangeEvent(lCases, Trigger.isDelete);
}

I ended up doing the following:

Changed the object name "CC_List_Object__c" to "CC_List__c"
Created an autonumber for CC_List__c Name versus text
Changed the field name "Case__c" to "Related_Case__c"
Added a new Email_Address__c email address field to the CC_List__c object
Added in the following additional code to line 22:
Email_Address__c = lStrCC.trim()
The code is firing brilliantly, adding in each parsed CC record to the CC_List__c object upon save of the related case. With that said, each time I save the case, it resaves all values instead of just the new additions. Example:

If I enter in: 
CC_List__c (field) = abc@corp.com (text)
Then the object spits out:
CC_List__c (object) = abc@corp.com (record)
If I update the same case to:
CC_List__c (field) = abc@corp.com,xyz@corp.com (text)
Then the object spits out:
CC_List__c (object) = abc@corp.com,abc@corp.com,xyz@corp.com (records)
As shown above, the abc@corp.com is listed twice within the CC list, despite only be listed once in the CC_List__c field. Each time I save the record, the CC_List__c field inserts records into the CC_List__c objects as if they were new.

Any thoughts on how to handle? One thought I has was that each time the related case record is saved, it deletes all CC_List__c object records that have it's Case listed in the Related_Case__c field. After the deletion, all records can be re-added into the object like new. If I went this route, I would update the auto text Name value on the CC_List__c object to text with a single dummy value as you have originally shown in order to avoid confusion.

Thoughts? 
 
[Lindsey Kiken][Lindsey Kiken]
I just figured it out. By wiping the CC List before I run the rest of my code and deleting the delete function, I achieve my desired results. 

Final code:
 
trigger CCList on Case (after insert, after update, after delete) {

    class CaseTriggerUtil {
        void handleCCFieldChangeEvent(Case [] argCases, Boolean argIsDelete) {
            Set<ID> lDeadParentIDSet = new Set<ID>();
            Set<String> lUniqueCCIDSet = new Set<String>();
            List<CC_List__c> lNewCCRecords = new List<CC_List__c>();

    delete [SELECT id FROM CC_List__c WHERE Related_Case__c IN:argCases];

            for(Case lCase : argCases) {
                if(argIsDelete) {
                    lDeadParentIDSet.add(lCase.id);
                }
                else {
                    if(lCase.CC_List__c == null || lCase.CC_List__c.trim() == '') {
                        lDeadParentIDSet.add(lCase.id);
                        continue;
                    }

                    List<String> lCCValues = lCase.CC_List__c.trim().split(',');
                    
                    for(String lStrCC : lCCValues) {
                        String lStrChildRecUniqueID = lCase.id + '_' + lStrCC.trim();
                        lNewCCRecords.add(new CC_List__c(Related_Case__c=lCase.id, My_ID__c = lStrChildRecUniqueID, Email_Address__c = lStrCC.trim()));
                        lUniqueCCIDSet.add(lStrChildRecUniqueID);
                    }
                }
            }

            if(!argIsDelete) {
                Database.insert(lNewCCRecords, false);
            }
        }
    }

    List<Case> lCases = (Trigger.isDelete ? Trigger.old : Trigger.new);
    new CaseTriggerUtil().handleCCFieldChangeEvent(lCases, Trigger.isDelete);
}
This was selected as the best answer
Rajiv Penagonda 12Rajiv Penagonda 12
Lindsey, in the original code I provided you, the field "My_ID__c" was marked as unique at field level. Somehow I missed that in my original response.

So the code at line 29: Database.insert(lNewCCRecords, false); would actually insert only the new entries, and the ones already existing in database fail silently because the unique condition was not met.

Anyway, glad I could help. tc.
[Lindsey Kiken][Lindsey Kiken]
Thank you again, Rajiv!