Tuesday, September 27, 2016

Dynamic Apex - Sky is The Limit - Explained with Example


Today I am going to explain Dynamic Apex with examples. Dynamic Apex will help developer to create more flexible and robust application because with the help of Dynamic Apex, developer can access the sObject and field describe information, access Salesforce application information. Another great help is that with Dynamic Apex, developer can now write dynamic SOQL, SOSL and DML statements. So basically with dynamic apex, you have the magic wand in your hand.


So without any further delay, let's start the power of magic wand.

Basics of Apex Describe:
In order to get information about sObjects in Salesforce, you have two options - either use tokens or use describeSObjects Schema methods.

Apex provides two data structures and a method for fetching information about sObjects and fields. They are listed below -
  • Token - This is a lightweight, serializable reference to an sObject or a field that is validated at run time. Since they are lightweight, using them make your code faster and more efficient.
  • The describeSObjects method - This method from the Schema class will perform the describe on sObject Types.
  • Describe result - This is basically the object of  type Schema.DescribeSObjectResult which holds the result i.e. contains all the describe properties for the sObject or field. These objects are not serializable and validated always at runtime. 
Let's get into more details before we jump into the examples -
Schema Describe -
  • Schema Describe is a way to programmatically learn about the metadata of your datamodel.
  • Schema Describe calls provides the ability to describe the information about the current org schema such as list of top level objects including custom objects and their fields.
         

Map<String, Schema.SObjectType> schemaMap = Schema.getGlobalDescribe();
Schema Class -
Schema class comes with few static methods which are very important. Below are they -
  • getGlobalDescribe() - This method will return a map of all sObject names (keys) to sObject tokens (values) for the standard as well as custom objects present in the organization.
  • describeDataCategoryGroups(List) - This method will return a list of the category groups associated with the specified objects.
  • describeSObjects(List) - Describes the metadata (field list and object properties) for the specified objects or the array of objects.
  • describeTabs() - Returns the information about standard and custom apps available to the running user.
Schema Namespace -
The Schema Namespace provides classes and methods for getting schema metadata information. Below are the classes -
  • ChildRelationship class - This class contains the methods for accessing the child relationship as well as child sObject for a parent sObject.
  • DescribeFieldResult class - Contains methods for describing fields.
  • DescribeSObjectResult class -Contains methods for describing sObjects.
  • DescribeTabResult class - Returns tab metadata information for a tab in a standard or custom app available.
  • FieldSet class - This contains methods for discovering and retrieving the details of field sets created on sObjects.
  • SObjectField class - This is the return type of getSObjectField method,which basically returns the object of type Schema.SObjectField class.
  • SObjectType class - This is the return type of getSObjectType method,which basically returns the object of type Schema.SObjectType class.
  • RecordTypeInfo class - This contains the methods for accessing record type information associated with a sObject.
  • PicklistEntry class - This represents a picklist entry.
You can get more details about the Schema Namespace class @ 

Now let's play with Dynamic Apex -

Apex code to fetch all objects (Standard as well as Custom) from an organization -
VF Page ::
<apex:page controller="ObjectRetrievePractice">
    <apex:form id="TheForm">
        <apex:pageBlock title="All Objects Fetched Dynamically">
            <apex:outputLabel value="All Objects ">
                <apex:selectList size="1">
                    <apex:selectoptions value="{!allObjNames}"></apex:selectoptions>
                </apex:selectList>
            </apex:outputLabel>
        </apex:pageBlock> 
    </apex:form>
</apex:page>
Controller ::
public with sharing class ObjectRetrievePractice {
    //Method to fetch all objects present in the org
    public List<SelectOption> getAllObjNames(){
        //Get all Object names
 List<Schema.SObjectType> gd = Schema.getGlobalDescribe().Values();
  
 List<SelectOption> options = new List<SelectOption>();
 options.add(new SelectOption('--None','--None--'));
  
 for(Schema.SObjectType f : gd){
       options.add(new SelectOption(f.getDescribe().getName(),f.getDescribe().getName()));
 }
        return options;
    }
}

Apex code to fetch all Custom objects only from an organization -
VF Page ::
<apex:page controller="ObjectRetrievePractice">
    <apex:form id="TheForm">
        <apex:pageBlock title="All Custom Objects Fetched Dynamically">
            <apex:outputLabel value="All Custom Objects ">
                <apex:selectList size="1">
                    <apex:selectoptions value="{!allCustomObjNames}"></apex:selectoptions>
                </apex:selectList>
            </apex:outputLabel>
        </apex:pageBlock>
    </apex:form>
</apex:page>
Controller ::
public with sharing class ObjectRetrievePractice {
    //Method to fetch only custom objects from the org
    public List<SelectOption> getAllCustomObjNames(){
        //Get all Object names
        List<Schema.SObjectType> gd = Schema.getGlobalDescribe().Values();
        
        List<SelectOption> options = new List<SelectOption>();
        options.add(new SelectOption('--None','--None--'));
        
        for(Schema.SObjectType f : gd){
            if(f.getDescribe().isCustom())
                options.add(new SelectOption(f.getDescribe().getLabel(),f.getDescribe().getLabel()));
        }
        return options;
    }
}

Apex code to fetch all fields dynamically from an Object -
VF Page ::
<apex:page controller="ObjectRetrievePractice">
    <apex:form id="TheForm">
        <apex:pageBlock title="All Fields from Account Object">
            <apex:outputLabel value="All Account Fields ">
                <apex:selectList size="1">
                    <apex:selectoptions value="{!allAccountFields}"></apex:selectoptions>
                </apex:selectList>
            </apex:outputLabel>
        </apex:pageBlock>
    </apex:form>
</apex:page>
Controller ::
public with sharing class ObjectRetrievePractice {
    //Method to fetch Account fields dynamically
    public List<SelectOption> getAllAccountFields(){
        List<SelectOption> options = new List<SelectOption>();
        
        Map<String, Schema.SObjectField> fm = Schema.SObjectType.Account.fields.getMap();
        
        for(String token : fm.keyset()){
            options.add(new SelectOption(fm.get(token).getDescribe().getLabel(),fm.get(token).getDescribe().getLabel()));
        }
        return options;
    }
}

Now a little advance code snippet.
Apex code to first select the Object and then dynamically fetch fields from that object -
VF Page ::
<apex:page controller="ObjectRetrievePractice">
    <apex:form id="TheForm">
        <apex:pageBlock >
            <apex:pageBlockSection title="Select an Object" collapsible="false" columns="2">
                 <apex:pageBlockSectionItem >
                     <apex:outputLabel value="Select an object:"></apex:outputLabel>
                     <apex:selectList value="{!objectName}" size="1" multiselect="false">
                         <apex:selectOptions value="{!allObjNames}">
                         </apex:selectOptions>
                         <apex:actionSupport action="{!fetchFields}" event="onchange" reRender="fieldsPanel"/>  
                     </apex:selectList>
                 </apex:pageBlockSectionItem>
                 
                 <apex:pageBlockSectionItem >
                 <apex:outputPanel id="fieldsPanel">
                     <apex:outputLabel value="Select Fields:"> </apex:outputLabel>
                     <apex:selectList value="{!fields}" multiselect="true" size="3" >
                         <apex:selectOptions value="{!lsps}">
                         
                         </apex:selectOptions>
                     </apex:selectList>
                     </apex:outputPanel>
                 </apex:pageBlockSectionItem> 
             </apex:pageBlockSection>
         </apex:pageBlock>
    </apex:form>
</apex:page>
Controller ::
public with sharing class ObjectRetrievePractice {
    public String objectName { get; set; } 
    public list<SelectOption> lsps{get;set;}  
    public String fields { get; set; }
    
    //Method to fetch all objects present in the org
    public List<SelectOption> getAllObjNames(){
        //Get all Object names
  List<Schema.SObjectType> gd = Schema.getGlobalDescribe().Values();
  
  List<SelectOption> options = new List<SelectOption>();
  options.add(new SelectOption('--None','--None--'));
  
  for(Schema.SObjectType f : gd){
      options.add(new SelectOption(f.getDescribe().getName(),f.getDescribe().getName()));
  }
  return options;
 }
    
    //Method to fetch fields dynamically from the selected objects  
    public void fetchFields() {
        Map<String, Schema.SObjectType> schemaMap = Schema.getGlobalDescribe();
        lsps = new List<SelectOption>();
        
        if(objectName!=null || objectName!=''){
            Map<string,Schema.SObjectField> mpConField=schemaMap.get(objectName).getDescribe().fields.getMap();
            
            for(String token : mpConField.keyset()){
             lsps.add(new SelectOption(mpConField.get(token).getDescribe().getLabel(),mpConField.get(token).getDescribe().getLabel()));
            }
        }        
    }
}

Apex code to select required fields from Account Object -
In Dynamic Apex, we don't have any direct method available which can tell us whether a field is required or not. But there is another way to find it out. If any field satisfies below three properties, then it is a required field
  • If the field is createable
  • If the field is not nillable and
  • If the field does not have any default value.
Below code will show how we can find only the required fields from Account object

VF Page ::
<apex:page controller="ObjectRetrievePractice">
    <apex:form id="TheForm">
        <apex:pageBlock title="Display only the required fields Account Object">
            <apex:outputLabel value="All Required Fields from Account object ">
                <apex:selectList size="1">
                    <apex:selectoptions value="{!allAccountRequiredFields}"></apex:selectoptions>
                </apex:selectList>
            </apex:outputLabel>
        </apex:pageBlock>
    </apex:form>
</apex:page>
Controller ::
public with sharing class ObjectRetrievePractice {
    //Method to fetch Required fields from Account object dynamically
    public List<SelectOption> getAllAccountRequiredFields(){
        List<SelectOption> options = new List<SelectOption>();
        
        Map<String, Schema.SObjectField> fm = Schema.SObjectType.Account.fields.getMap();
        
        for(String token : fm.keyset()){
            Schema.DescribeFieldResult describeResult = fm.get(token).getDescribe();
            if(describeResult.isCreateable() && !describeResult.isNillable() && !describeResult.isDefaultedOnCreate()){
                options.add(new SelectOption(describeResult.getLabel(),describeResult.getLabel()));
            }
        }
        return options;
    }
}

Apex code to get Child Relationship from an object -
You can access the child relationship from a parent sObject and also the child sObject using the ChildRelationship object method.

A ChildRelationship object will be returned when you call the method getChildRelationship().
VF Page ::
<apex:page controller="ObjectRetrievePractice">
    <apex:form id="TheForm">
        <apex:pageBlock title="Display Account's Child Objects">
            <apex:outputLabel value="Child Objects from Account ">
                <apex:selectList size="1">
                    <apex:selectoptions value="{!allAccountChildObjects}"></apex:selectoptions>
                </apex:selectList>
            </apex:outputLabel>
        </apex:pageBlock>
    </apex:form>
</apex:page>
Controller ::
public with sharing class ObjectRetrievePractice {
    //Method to fetch Child Relationship from Account object dynamically
    public List<SelectOption> getAllAccountChildObjects(){
        List<SelectOption> options = new List<SelectOption>();
        
        Schema.DescribeSObjectResult describeResult = Account.SObjectType.getDescribe();
        List<Schema.ChildRelationship> lstchildrelationships = describeResult.getChildRelationships();
        
        for(Schema.ChildRelationship relname:lstchildrelationships){
             options.add(new SelectOption(relname.getChildSObject().getDescribe().getLabel(),relname.getChildSObject().getDescribe().getLabel()));
        }
        
        return options;
    }
}

Apex code to fetch/display Trigger Names of selected object -
List<String> triggerName = new List<String>();
               
Schema.DescribeSObjectResult result = Account.sObjectType.getDescribe();
for(ApexTrigger aptrig:[select id,name,Status,TableEnumOrId from apextrigger where TableEnumOrId=:result.getName()]){
    triggerName.add(aptrig.name);
}
System.debug('--Trigger Names :: '+triggerName);



Apex code to fetch Record Type ID without SOQL -
We can use getRecordTypeInfosByName() method to get the record type ids without using SOQL. This will help us to reduce the SOQL operation in the code.

Schema.DescribeSObjectResult result = Account.sObjectType.getDescribe();
//Getting all Recordtype from Account object
if(result.getRecordTypeInfosByName() != null) {
    Map<String,Schema.RecordTypeInfo> recordTypeInfo = result.getRecordTypeInfosByName();
    System.debug('-- recordTypeInfo:: '+recordTypeInfo);
 //Getting  Telecom Record Type Id. I have this record type in my org
 if(recordTypeInfo.containsKey('Telecom')) {
  Id rtId = recordTypeInfo.get('Telecom').getRecordTypeId();
  system.debug('-- Record Type id:: '+rtId);
 }   
}

With all the above code snippets, I hope you already realized the power of Dynamic Apex. So please go ahead and play as much as your want. Sky is the limit here.

I will really appreciate if you provide me your valuable feedback. Thanks.

0 comments:

Post a Comment