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
Sfdc AdminSfdc Admin 

How to clone any Sobject with Related lists(including chatter) and attachments in Salesforce Lightning

Hi Everyone,
I have a requirement to deep clone/ super clone a custom object with it's related lists (related lists includes chatter), and attachments links that directs to original attachment( don't want to duplicate attachments). For this requirement I used a custom code that supports on any sobject but, it's doing just basic clone not deep/super clone.

Please see below:
 
public class CloneSingleRecord {
    @AuraEnabled
    public static String cloneAnySobjet(String recordId){
        Map<String, Schema.SObjectType> schemaMap = Schema.getGlobalDescribe();
        
          List<Attachment> attachments = new List<Attachment>();
          list<Attachment> attachlist = new list<Attachment>();
          
        String objectAPIName = '';
        String keyPrefix = recordId.substring(0,3);
        for( Schema.SObjectType obj : schemaMap.Values() ){
            String prefix = obj.getDescribe().getKeyPrefix();
            if(prefix == keyPrefix){
                objectAPIName = obj.getDescribe().getName();
                break;
            }
        }
        Set <String> fieldMap = schemaMap.get(objectAPIName).getDescribe().fields.getMap().keySet();
        String soqlQuery = 'SELECT ' ; 
        for (String s :fieldMap ){
            if(schema.getGlobalDescribe().get(objectAPIName).getDescribe().fields.getMap().get(s).getDescribe().isAccessible()){
                soqlQuery +=  + s+',';
            }
        }
        soqlQuery =  soqlQuery.removeEnd(',');
        soqlQuery += ' FROM ' +objectAPIName +' WHERE ID = \'' + recordId +'\'' ;
        System.debug('soqlQuery'+soqlQuery);
        SObject record = Database.query(soqlQuery);
        SObject clondedParentRecordID= record.clone(false, true, true, false);
        try{
            insert clondedParentRecordID ;
            return clondedParentRecordID.id ;
        }catch(Exception e){
            return '' ;
        }
           }
}
How can I modify this code to get my requirement done ?

 
Best Answer chosen by Sfdc Admin
Sfdc AdminSfdc Admin
Hi Deepali,

Thank you for your time, but I have used https://rajvakati.com/2018/10/17/lightning-component-clone-with-related-records/ . Because our team decided not to built a nw visualforce page. 
Thank you so much for your help and time.

All Answers

Deepali KulshresthaDeepali Kulshrestha
Hi ,

I've gone through your requirement and you can refer the below code for creating the clone button on yusing vf page:
VF Page--->

<apex:page controller="CloneWithChildrenCtrl">
<apex:pageMessages />
<apex:form >
    <apex:pageBlock >
        
        <apex:pageBlockSection title="Parent Record">
            <apex:outputText value="{!parentObjName}"/>
        </apex:pageBlockSection>
        
        <apex:pageBlockSection title="Child Records">
            
            <apex:inputCheckbox >
                <apex:outputText >Select All</apex:outputText>
                <apex:actionSupport event="onclick" action="{!checkAll}"/>
            </apex:inputCheckbox>
            <apex:outputText />
            
            <apex:pageBlockTable value="{!childObjWrapperList}" var="child" columns="3">
                <apex:column headerValue="Need to Clone?">
                    <apex:inputCheckbox value="{!child.isSelected}"/>
                </apex:column>
                <apex:column value="{!child.objName}" headerValue="Object"/>
                <apex:column value="{!child.relationshipName}" headerValue="Related to Field"/>
            </apex:pageBlockTable>
        </apex:pageBlockSection>
        
        <apex:pageBlockButtons >
            <apex:commandButton title="Clone" value="Clone" action="{!cloneWithChildren}"/>
            <apex:commandButton value="Cancel" action="{!cancel}"/>
        </apex:pageBlockButtons>
        
    </apex:pageBlock>
</apex:form>
</apex:page>


COntroller-->

public with sharing class CloneWithChildrenCtrl {

// Id of parent object which needs to be cloned
private Id recordId {get;set;}

// Name of the parent object which needs to be cloned
public String parentObjName {get;set;}

// List of wrapper object (child records) which needs to be displayed on page
public List<ChildObjectWrapper> childObjWrapperList {get;set;}

// List of selected wrapper object (child records) which needs to be passed to controller
public List<ChildObjectWrapper> slctdChildObjWrapperList {get;set;}

public CloneWithChildrenCtrl(){
    childObjWrapperList = new List<ChildObjectWrapper>();
    slctdChildObjWrapperList = new List<ChildObjectWrapper>();
    recordId = ApexPages.currentPage().getParameters().get('Id');
    parentObjName = UtilityClass.getObjectNameFromId(recordId);
    List<Schema.ChildRelationship> childObjList = UtilityClass.getChildRelationshipsFromId(recordId);
    for(Schema.ChildRelationship child : childObjList){
        if(child.getChildSObject().getDescribe().isQueryable()){
            if(child.getField().getDescribe().isCreateable())
                childObjWrapperList.add(new ChildObjectWrapper(child.getChildSObject().getDescribe().getName(),false,recordId,String.valueOf(child.getField())));
        }
    }
}

public class ChildObjectWrapper{
    
    public String objName {get;set;}
    public boolean isSelected {get;set;}
    public String recordId {get;set;}
    public String relationshipName {get;set;}
    
    ChildObjectWrapper(String objName, Boolean isSelected,String recordId, String relationshipName){
        this.objName = objName;
        this.isSelected = isSelected;
        this.recordId = recordId;
        this.relationshipName = relationshipName;
    }
    
}

public PageReference cancel(){
    return new PageReference('/'+recordId);
}


public PageReference cloneWithChildren(){
    for(ChildObjectWrapper chldObj : childObjWrapperList){
        if(chldObj.isSelected){
            slctdChildObjWrapperList.add(chldObj);
        }
    }
    // Clone parent object record first
    List<Sobject> clonedParentObj = UtilityClass.cloneObject(parentObjName,recordId,true,null,null);
    insert clonedParentObj;
    // Clone child records
    List<sobject> childObjList = new List<sobject>();
    if(slctdChildObjWrapperList.size() > 0){
        for(ChildObjectWrapper obj : slctdChildObjWrapperList){
            List<Sobject> clonedChildObj = UtilityClass.cloneObject(obj.objName,obj.recordId,false,obj.relationshipName,clonedParentObj.get(0).Id);
            if(clonedChildObj.size() > 0){
                childObjList.addAll(clonedChildObj);
            }
        }
    }
    Database.insert(childObjList,false);
    return new PageReference('/'+clonedParentObj.get(0).Id);
}


public PageReference checkAll(){
    for(ChildObjectWrapper chldObj : childObjWrapperList){
        chldObj.isSelected = true;
    }
    return null;
}

}

public static String getObjectNameFromId(Id recordId){
    Schema.DescribeSObjectResult dr = UtilityHelper.getDescribeSObjectResultFromId(recordId);
    return dr.getName(); 
}


public static List<Schema.ChildRelationship> getChildRelationshipsFromId(Id recordId){
    Schema.DescribeSObjectResult dr = UtilityHelper.getDescribeSObjectResultFromId(recordId);
    return dr.getChildRelationships();
}

public static List<Sobject> cloneObject(String objName, Id orgRecordId, Boolean isSelfId,String relationshipField, String parentRecordId){
    SObjectType objToken = Schema.getGlobalDescribe().get(objName);
    DescribeSObjectResult objDef = objToken.getDescribe();
    Map<String, SObjectField> fields = objDef.fields.getMap();
    List<String> fieldslist = new List<String>();
    for(String fieldName : fields.keySet()) {
        if(fields.containsKey(fieldName) && fields.get(fieldName).getDescribe().isCreateable()){
            fieldslist.add(fieldName);
        }
    }
    

    if(objName.equals('OpportunityLineItem')){
        // Convert List to Set as List does not have contains method
        Set<String> fieldsSet = new Set<String>(fieldslist);
        if(fieldsSet.contains('unitprice') && fieldsSet.contains('totalprice')){
            // remove either TotalPrice or UnitPrice
            fieldsSet.remove('totalprice');
            fieldslist.clear();
            fieldslist.addAll(fieldsSet);
        }
    }
    
    String query;
    if(isSelfId){
        query   = 'SELECT ' + String.join(fieldslist, ',') + ' FROM '+ objName +' WHERE Id =\''+ orgRecordId +'\' LIMIT 1';
    }else{
        query   = 'SELECT ' + String.join(fieldslist, ',') + ' FROM '+ objName +' WHERE '+ relationshipField +' =\''+ orgRecordId +'\'';
    }
    List<SObject> sObjList = new List<SObject>();
    if(fieldslist.size() > 0){
        sObjList = Database.query(query); 
    }
    List<SObject> clonedObjList = new List<SObject>();
    SObject clonedSobj;
    for(Sobject obj : sObjList){
        clonedSobj  = obj.clone(false, false, false, false);
        if(!isSelfId && clonedSobj!=null){
            // update relationshipField with parentRecordId
            if(String.isNotBlank(relationshipField) && String.isNotBlank(parentRecordId)){
                clonedSobj.put(relationshipField,parentRecordId);
            }
        }
        clonedObjList.add(clonedSobj);
    }
    return clonedObjList;
}
}


public class UtilityHelper {

/** Method name : getDescribeSObjectResultFromId
 * Arguments : Id recordId
 * Return type : Schema.DescribeSObjectResult
 * Description : Returns the describe information about the passed record id
**/
public static Schema.DescribeSObjectResult getDescribeSObjectResultFromId(Id recordId){
    Schema.SObjectType token = recordId.getSObjectType();
    return token.getDescribe();
}

}

I hope you find the above solution helpful. If it does, please mark as Best Answer to help others too.

Thanks and Regards,
Deepali Kulshrestha
www.kdeepali.com
Sfdc AdminSfdc Admin
Hi Deepali,

Thank you so much for taking time and looking into the code, I will try your method and check. Meanwhile can I know if there is any possibility to clone attachments link from parent Id rather than cloning entire attachment and creating duplicates.
Thanks in advance.
Sfdc AdminSfdc Admin
Hi Deepali,

Thank you for your time, but I have used https://rajvakati.com/2018/10/17/lightning-component-clone-with-related-records/ . Because our team decided not to built a nw visualforce page. 
Thank you so much for your help and time.
This was selected as the best answer