Dynamic Record Cloning
I’m building a custom cloning functionality that needs to meet a few objectives, most important of which is being dynamic. I don’t want to build these out for each object in our instance.
This is fine. What I’ve done is build a class that reads the meta data of the sObject I want to clone. The way to do this is to create a map of field names and field tokens and build up a SOQL query select statement with those fields.
Here’s a utility class with a method getCreatableFieldsSOQL() to accomplish just that:
public with sharing class Utils{ // Returns a dynamic SOQL statement for the whole object, includes only creatable fields since we will be inserting a cloned result of this query public static string getCreatableFieldsSOQL(String objectName, String whereClause){ String selects = ''; if (whereClause == null || whereClause == ''){ return null; } // Get a map of field name and field token Map<String, Schema.SObjectField> fMap = Schema.getGlobalDescribe().get(objectName.toLowerCase()).getDescribe().Fields.getMap(); list<string> selectFields = new list<string>(); if (fMap != null){ for (Schema.SObjectField ft : fMap.values()){ // loop through all field tokens (ft) Schema.DescribeFieldResult fd = ft.getDescribe(); // describe each field (fd) if (fd.isCreateable()){ // field is creatable selectFields.add(fd.getName()); } } } if (!selectFields.isEmpty()){ for (string s:selectFields){ selects += s + ','; } if (selects.endsWith(',')){selects = selects.substring(0,selects.lastIndexOf(','));} } return 'SELECT ' + selects + ' FROM ' + objectName + ' WHERE ' + whereClause; } }
Great! Now I’m ready to create a placeholder visual force page that I can call via url from a custom button. The url is simple enough (note I’m using relative links here):
/apex/CloneSpecialWastePWS?sid={!Special_Waste_Profile__c.Id}
Creating that Visual Force page is easy enough – an otherwise blank canvass with a simple call to a doit() method:
<apex:page Controller="CloneSpecialWasteProfile" action="{!doit}" showHeader="false" sidebar="false"> </apex:page>
And finally the controller for the Visual Force page. Note the constructor that calls the above utility method, and the doit() method that returns a page reference. Also, pay attention to how I grab the sid parameter from the URL:
public class CloneSpecialWasteProfile{ private final Special_Waste_Profile__c p; public CloneSpecialWasteProfile() { String soql = Utils.getCreatableFieldsSOQL('Special_Waste_Profile__c','id=\'' + ApexPages.currentPage().getParameters().get('sid') + '\''); p = (Special_Waste_Profile__c)Database.query(soql); } public PageReference doit() { Special_Waste_Profile__c p2 = p.clone(false, true); insert p2; p2.Approval_Date__c = null; //Reset all of the fields I don't want to copy values for update p2; PageReference pageRef = new PageReference('/' + p2.Id);// + '/e?retURL=' + p2.Id); pageRef.setredirect(true); return pageRef; } }
Then I add the button to the layout, and when it’s clicked I’m redirected to the view layout of a cloned record. Implementing this for another object is as simple as creating a new Visual Force page and controller, and linking to that page from a new custom button on the layout for the sObject.