Sunday, November 30, 2014

Dynamic Apex In Salesforce

Dynamic Apex In Salesforce 


Schema Describe:

  •   Is a way to programmatically learn about metadata of your datamodel with in Apex.
  • Schema Describe calls provides the ability to programitically 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> gd = Schema.getGlobalDescribe();
  •  We can also use schemaDescribe()  to create tree structure of all the objects and fields in the schema browser, to do this we need to use the codemap


         Map<String,Schema.SobjectType> gd = Schema.getGlobalDescribe();

Returns map of all Sobjects names or Keys to sObject tokens or values for the standard and custom objects defined in the organization.

Schema Class

The following are methods for Schema. All methods are static.

Returns a map of all sObject names (keys) to sObject tokens (values) for the standard and custom objects defined in your organization.

Returns a list of the category groups associated with the specified objects.

Describes metadata (field list and object properties) for the specified sObject or array of sObjects.

Returns information about the standard and custom apps available to the running user.

Returns available category groups along with their data category structure for objects specified in the request.


Schema Namespace

The Schema namespace provides classes and methods for schema metadata information.
The following are the classes in the Schema namespace.

ChildRelationship Class
Contains methods for accessing the child relationship as well as the child sObject for a parent sObject.

DescribeFieldResult Class
Contains methods for describing sObject fields.

DescribeSObjectResult Class
Contains methods for describing sObjects.

DescribeTabResult Class
Contains tab metadata information for a tab in a standard or custom app available in the Salesforce user interface.

 FieldSet Class
Contains methods for discovering and retrieving the details of field sets created on sObjects.

PicklistEntry Class
Represents a picklist entry.

RecordTypeInfo Class
Contains methods for accessing record type information for an sObject with associated record types.

SObjectField Class
A Schema.sObjectField object is returned from the field describe result using the getControler and getSObjectField methods.

SObjectType Class
A Schema.sObjectType object is returned from the field describe result using the getReferenceTo method, or from the sObject describe result using the getSObjectType method.

More about Schema Namespace:



1. Retrieve the objects from the salesforce Org.
Display all the Custom objects with “__C”  names in a picklist field:

page:
=====
<apex:page controller="ObjectRetrieve">
<apex:form >
<apex:outputlabel value="All Objects"/>&nbsp;&nbsp;
<apex:selectList size="1">
<apex:selectoptions value="{!objnames}"></apex:selectoptions>
</apex:selectList>
</apex:form>
</apex:page>

class:
======
public with sharing class ObjectRetrieve {

//Map<String, Schema.SObjectType> gd = Schema.getGlobalDescribe();

public List<SelectOption> getobjNames()
{
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().getName().contains('__c'))
options.add(new SelectOption(f.getDescribe().getName(),f.getDescribe().getName()));
}
return options;
}
}


2. Retrieveing all the field api names dynamically.The below samples helps to retrieve the field api names dynamically from the salesforce object.

page:
=====

<apex:page standardcontroller="Opportunity" extensions="DynamicVFClass">
<apex:form >
<apex:pageBlock >
<apex:pageBlockSection columns="1">
<apex:repeat value="{!fieldapis}" var="f">
<apex:inputField value="{!Opportunity[f]}"/>
</apex:repeat>
</apex:pageBlockSection>
</apex:pageBlock>
</apex:form>
</apex:page>
class:
======
public with sharing class DynamicVFClass {
public DynamicVFClass(ApexPages.StandardController controller) {
}
public List<String> getFieldapis() {
map<String, Schema.SObjectType> m1 = Schema.getGlobalDescribe() ;
SObjectType objToken1 = m1.get('Opportunity');
DescribeSObjectResult objDef1= objToken1.getDescribe();
map<String, SObjectField> fieldmap = objDef1.fields.getmap();
List<String> lstobjectfields1= new List<String>();
List<String> fieldLabels1= new List<String>();
map<String,String> fieldLabelNamemap1= new map<String,String>();
for (String fName1:fieldmap.keySet())
{
if(fieldmap.get(fName1).getDescribe().getType()!=Schema.DisplayType.Time &&
fieldmap.get(fName1).getDescribe().getType()!=Schema.DisplayType.anytype&&
fieldmap.get(fName1).getDescribe().getType()!=Schema.DisplayType.base64 &&
fieldmap.get(fName1).getDescribe().getType()!=Schema.DisplayType.EncryptedString &&
fieldmap.get(fName1).getDescribe().getType()!=Schema.DisplayType.Id &&
fieldmap.get(fName1).getDescribe().getType()!=Schema.DisplayType.multiPicklist &&

fieldmap.get(fName1).getDescribe().getType()!=Schema.DisplayType.TextArea)
{
fieldLabels1.add(fieldmap.get(fName1).getDescribe().getLabel());
fieldLabelNamemap1.put(fieldmap.get(fName1).getDescribe().getLabel(), fName1);
}
}
for (String fLabel1:fieldLabels1){
if(flabel1 !='Created Date'&& flabel1!='Last Activity Date' && flabel1!='Last modified Date' && flabel1!='Deleted' && flabel1!='System modstamp'&& flabel1!='')
{
//lstobjectfields.add(new selectOption(fieldLabelNamemap.get(flabel),fLabel));
lstobjectfields1.add(fieldLabelNamemap1.get(flabel1));
}
}
system.debug('#### All Fields are ####'+lstobjectfields1);
return lstobjectfields1;
}
}





(3). Retrieve the picklist values from the picklist field:
If we don't have standard controller then we have to use the following sample.
page:
=====
<apex:page controller="RetrievePicklistData">
<apex:form >
<apex:outputlabel value="Stage"/> &nbsp;&nbsp;
<apex:selectList size="1" >
<apex:selectOptions value="{!pickNames}">
</apex:selectOptions>
</apex:selectList>
</apex:form>
</apex:page>
 
class:
=======
public with sharing class RetrievePicklistData {

public List<SelectOption> getpicknames()
{
set<String> setobj = new set<string>();
List<SelectOption> options = new List<SelectOption>();
Schema.DescribeFieldResult ccity = Schema.sObjectType.Opportunity.fields.StageName.getSObjectField().getDescribe();
options.add(new SelectOption('--None--', '--None--'));
for(PicklistEntry ent:ccity.getpicklistvalues())
{
options .add(new SelectOption(ent.getValue(), ent.getLabel()));
setobj.add(ent.getvalue());
}
return options;
}
}

To get the Object properties such as keyfix, label,plural label etc…..:

Schema.DescribeSObjectResult sobjectResultTemp = Schema.sobjecttype.Account;
system.debug('==>Account Properties==>'+sobjectResultTemp);
System.debug('==>Account Prefix==>'+sobjectResultTemp.getKeyPrefix());
System.debug('==> Acount Plural label==>'+sobjectResultTemp.getLabelPlural());
System.debug('==> Account Label ==>'+sobjectResultTemp.getLabel());


Get all the required fields of sObject dynamically?

There is no direct property available in Apex dynamic API to represent the required field. However there is another way to know about it.
If any field have below three properties then it is mandatory field.

1.       If it is Creatable
2.       If it is not nillable and
3.       If it does not have any default value

Map<String, Schema.SObjectType> m  = Schema.getGlobalDescribe() ;
system.debug('==>m is==>'+m);
Schema.SObjectType s = m.get('Account') ;
system.debug('==>Sobject Type is ==>'+s);
Schema.DescribeSObjectResult r = s.getDescribe() ;
system.debug('==>DescribeSObjectResult==>'+r);
Map<String,Schema.SObjectField> fields = r.fields.getMap() ;
system.debug('==>fields==>'+fields);
List<String> lstrequiredfields=new List<String>();

for(String f : fields.keyset())
{
 Schema.DescribeFieldResult desribeResult = fields.get(f).getDescribe();
 if( desribeResult.isCreateable()  && !desribeResult.isNillable() && !desribeResult.isDefaultedOnCreate()
 {
//This is mandatory / required field

      lstrequiredfields.add(f);

 }
   
}
system.debug('==>lstrequiredfields==>'+lstrequiredfields);


Getting Childrelationships from an object using dynamic Apex

If an sObject is a parent object, you can access the child relationship as well as the child sObject using the ChildRelationship object methods.

A ChildRelationship object is returned from the sObject describe result using the getChildRelationship  method. For example:


Schema.DescribeSObjectResult   describeresult = Account.SObjectType.getDescribe();
//it gives Account  object properties or describe results;
List<Schema.ChildRelationship>   lstchildrelationships                      =describeresult.getChildRelationships();
//It gives you all the childrelationships associated with the account.To get relationship names from the above list;
for(Schema.ChildRelationship relname:lstchildrelationships){
      System.debug('Relationshipname:'+relname.getrelationshipname());
}


Limitation:
You can only use 100getChildRelationships method calls per Apex request.

Record Type Id without SOQL query:

We have getRecordTypeInfosByName() method to get record type id with out SOQL query.

We can avoid SOQL query on RecordType object with this method.The following example shows how to access Record Type information through dynamic apex.

Schema.DescribeSObjectResult resSchema = Account.sObjectType.getDescribe();
//getting all Recordtype  Account
if(resSchema.getRecordTypeInfosByName() !=null) {
Map<String,Schema.RecordTypeInfo> recordTypeInfo = resSchema.getRecordTypeInfosByName();
    system.debug('==>recordTypeInfo==>'+recordTypeInfo);
//Getting Business Record Type Id
 if(recordTypeInfo.containsKey('Business')) {
  Id rtId = recordTypeInfo.get('Business').getRecordTypeId();
     system.debug('==>Record Type id is ==>'+rtId);
 }   
   
}

Display Trigger names of selected objects:

List<String> triggerName = new List<String>();
  List<ApexTrigger> apexTriggers = new List<ApexTrigger>();
               
Schema.DescribeSObjectResult resSchema = Account.sObjectType.getDescribe();
system.debug('==>resSchema==>'+resSchema);
        for(ApexTrigger aptrig:[select id,name,Status,TableEnumOrId from apextrigger where TableEnumOrId=:resSchema.getName()]){
             triggerName.add(aptrig.name);
            apexTriggers.add(aptrig);
 }
system.debug('==>trigger Name==>'+triggerName);
System.debug('==>apexTriggers==>'+apexTriggers);


Access Salesforce app information

You can obtain describe information for standard and custom apps available in the Salesforce user interface. Each app corresponds to a collection of tabs. Describe information for an app includes the app’s label, namespace, and tabs. Describe information for a tab includes the sObject associated with the tab, tab icons and colors.

This example shows how to get the tab sets for each app. The example then obtains tab describe metadata information for the Sales app. For each tab, metadata information includes the icon URL, whether the tab is custom or not, and colors among others. The tab describe information is written to the debug output.

// Get tab set describes for each app
List<Schema.DescribeTabSetResult> tabSetDesc = Schema.describeTabs();

// Iterate through each tab set describe for each app and display the info
for(DescribeTabSetResult tsr : tabSetDesc) {
                String appLabel = tsr.getLabel();
    System.debug('Label: ' + appLabel);
                System.debug('Logo URL: ' + tsr.getLogoUrl());
                System.debug('isSelected: ' + tsr.isSelected());
                String ns = tsr.getNamespace();
                if (ns == '') {
        System.debug('The ' + appLabel + ' app has no namespace defined.');
                }
                else {
        System.debug('Namespace: ' + ns);
                }
               
                // Display tab info for the Sales app
if (appLabel == 'Sales') {
        List<Schema.DescribeTabResult> tabDesc = tsr.getTabs();
        System.debug('-- Tab information for the Sales app --');
        for(Schema.DescribeTabResult tr : tabDesc) {
                 System.debug('getLabel: ' + tr.getLabel());
            System.debug('getColors: ' + tr.getColors());
            System.debug('getIconUrl: ' + tr.getIconUrl());
            System.debug('getIcons: ' + tr.getIcons());
            System.debug('getMiniIconUrl: ' + tr.getMiniIconUrl());
            System.debug('getSobjectName: ' + tr.getSobjectName());
            System.debug('getUrl: ' + tr.getUrl());
            System.debug('isCustom: ' + tr.isCustom());
                }
                }              
}


Dynamic SOQL

Dynamic SOQL refers to the creation of a SOQL string at runtime with Apex code. Dynamic SOQL enables you to create more flexible applications. For example, you can create a search based on input from an end user, or update records with varying field names

To create a dynamic SOQL query at runtime, use the database query method, in one of the following ways:

• Return a single sObject when the query returns a single record:
sObject S = Database.query(string_limit_1);

• Return a list of sObjects when the query returns more than a single record:
List<sObject> L = Database.query(string);

The database query method can be used wherever an inline SOQL query can be used, such as in regular assignment statements and for loops. The results are processed in much the same way as static SOQL queries are processed.

Dynamic SOQL results can be specified as concrete sObjects, such as Account or MyCustomObject__c, or as the generic sObject data type. At runtime, the system validates that the type of the query matches the declared type of the variable. If the query does not return the correct sObject type, a runtime error is thrown. This means you do not need to cast from a generic sObject to a concrete sObject.




EX:-

String myTestString = 'TestName';
List<sObject> L = Database.query('SELECT Id FROM MyCustomObject__c WHERE Name = :myTestString');

Retrieving all fields without specifying field names in SOQL Query:

string fieldnames=' ';
List<sObject> lstaccount = new List<Account>();
Map<String, Schema.SObjectField> M = Schema.SObjectType.Account.fields.getMap();
for(Schema.SObjectField s:m.values()){
 Schema.DescribeFieldResult sfield=s.getDescribe();
 fieldnames+=s+',';  //Here we concatenating all field names with comma seperation for preparing SOQL query
}
system.debug('==>fieldnames==>+fieldnames);
//Fieldnames string contains all the fields with comma separation
 fieldnames=fieldnames.substring(0,fieldnames.length()-1);
system.debug('==>fieldnames==>+fieldnames);
 string query='select '+fieldnames+' from Account';
 //Here we are preparing string query(dynamic SOQL using "fieldnames" string)
 if(query!=null && query!=''){
  lstaccount=database.query(query);//Here we will get all field values from account.
 }
 system.debug('==>lstaccount==>'+lstaccount);

You CAN directly use primitive data types in SOQL.

So what does this means?
This means you can directly use any Integer, String , Date, Datetime,Double, Id variable along with Collection of this variable within the query.

For Example: 

a) You are allowed to do this

//initialize the Datetime with current time
DateTime now = System.now();
//query accounts by merging the variable name inside the query string
List<Account> accountList = Database.query('SELECT Id FROM Account WHERE CreatedDate =:now');

b)You can also include LISTs(Collections) in your queries

List<String> accNameList = new List<String>{'Acc1','Acc2'}
//query accounts by merging the variable name inside the query string
List<Account> accountList = Database.query('SELECT Id FROM Account WHERE Name IN:accNameList');

You CANNOT use complex types in a Dynamic SOQL directly.This means that you cannot use any Sobject, Apex Class or Any other user defined data type inside the Dynamic Query. In short you cannot use a dot (".") operator to specify a field value in a Dynamic query.

For Example :

a)  Using an Sobject inside the query String will not work
  //initialize a Account with a Name value for demo
Account acc = new Account(Name='MyAccount');
//query accounts by merging the variable name inside the query string

//This will not work
List<Account> accountList = Database.query('SELECT Id FROM Account WHERE Name =:acc.Name');

//But You can always make the above work by writing the code as.

  //initialize a Account with a Name value for demo
  Account acc = new Account(Name='MyAccount');
  String accountName = acc.Name
  //query accounts by merging the variable name inside the query string
   List<Account> accountList = Database.query('SELECT Id FROM Account WHERE                    Name =: accountName  ');

SOQL Injection

 SOQL injection is a technique by which a user causes your application to execute database methods you did not intend by passing SOQL statements into your code. This can occur in Apex code whenever your application relies on end user input to construct a dynamic SOQL statement and you do not handle the input properly.

To prevent SOQL injection, use the escapeSingleQuotes method. This method adds the escape character (\) to all single quotation marks in a string that is passed in from a user. The method ensures that all single quotation marks are treated as enclosing strings, instead of database commands.

More about soql Injection:

Dynamic SOSL:

 Dynamic SOSL refers to the creation of a SOSL string at runtime with Apex code. Dynamic SOSL enables you to create more flexible applications. For example, you can create a search based on input from an end user, or update records with varying field names.



To create a dynamic SOSL query at runtime, use the search query method. For example:

List<List <sObject>> myQuery = search.query(SOSL_search_string);

The following example exercises a simple SOSL query string.

String searchquery='FIND\'Edge*\'IN ALL FIELDS RETURNING Account(id,name),Contact, Lead';
List<List<SObject>>searchList=search.query(searchquery);

Dynamic SOSL statements evaluate to a list of lists of sObjects, where each list contains the search results for a particular sObject type. The result lists are always returned in the same order as they were specified in the dynamic SOSL query. From the example above, the results from Account are first, then Contact, then Lead.

The search query method can be used wherever an inline SOSL query can be used, such as in regular assignment statements and for loops. The results are processed in much the same way as static SOSL queries are processed.


Dynamic DML

In addition to querying describe information and building SOQL queries at runtime, you can also create sObjects dynamically, and insert them into the database using DML.

To create a new sObject of a given type, use the newSObject method on an sObject token. Note that the token must be cast into a concrete sObject type (such as Account). For example:

This example shows how to obtain the sObject token through the Schema.getGlobalDescribe method and then creates a new sObject using the newSObject method on the token. This example also contains a test method that verifies the dynamic creation of an account.


public class DynamicSObjectCreation {
                public static sObject createObject(String typeName) {
                Schema.SObjectType targetType = Schema.getGlobalDescribe().get(typeName);
                if (targetType == null) {
                // throw an exception
                }
                 
                // Instantiate an sObject with the type passed in as an argument   
           //  at run time.                
             return targetType.newSObject();
                }
}

@isTest
private class DynamicSObjectCreationTest {
                static testmethod void testObjectCreation() {
                String typeName = 'Account';
                String acctName = 'Acme';
                 
                // Create a new sObject by passing the sObject type as an argument.
                Account a = (Account)DynamicSObjectCreation.createObject(typeName);           
                System.assertEquals(typeName, String.valueOf(a.getSobjectType()));
                // Set the account name and insert the account.
                a.Name = acctName;
                insert a;

                // Verify the new sObject got inserted.
                Account[] b = [SELECT Name from Account WHERE Name = :acctName];
                system.assert(b.size() > 0);
                }
}

Or

// create instance of types dynamically
SObjectType accountType = Schema.getGlobalDescribe().get('Account');
SObject acc = accountType.newSObject();

// modify values using .put notation
acc.put('Name','Some Name');

insert acc;
id accId = acc.Id;

// read back dynamically and modify again using .put notation if necessary
acc = Database.query('Select Name From Account Where Id = :accId');

System.debug( acc.get('Name') );


More on Dynamic DML:


7 comments:

Post a Comment

 
| ,