What would be the best approach to get the recordtype id

  • I know of two approaches to get the recordtype id of a particular record type

    1. Use SOQL

      RecordType RecType = [Select Id From RecordType  Where SobjectType = 'Account' and DeveloperName = 'Business'];
      

    Benifit here is if the admin changes the name of the record type, the code still works as we can use developer name

    1. Using Describe

      Map<String,Schema.RecordTypeInfo> rtMapByName = d.getRecordTypeInfosByName();
      Schema.RecordTypeInfo rtByName =  rtMapByName.get('Business');
      

    Saves on SOQL limits but uses a describe call.

    What would be your choice and why

    EDIT : Usage is for setting up recordtype while inserting records in apex or trigger

    I'm not sure there is a "best" answer, I think it will depend on the reason that you need it, and other constraints you may have.

    The new correct answer is to use `getRecordTypeInfosByDeveloperName`

  • zachelrath

    zachelrath Correct answer

    8 years ago

    If you want to develop an industrial-strength Force.com application, i.e. an AppExchange app, you CANNOT rely on either the Id or Name fields of RecordTypes as a means of getting at a desired RecordType for a given object. Why? Because the Id will be different in every customer's org, and the Name may be different in every customer's org AND for each Running User, depending on the user's language.

    Therefore, it is ESSENTIAL to request RecordTypes by DeveloperName.

    HOWEVER, it is also essential to note that there are TWO pieces of information about a RecordType that you need to know about: is it Active in the org (meaning, anyone in the org could potentially use it), and is it Available to the running user (a Profile/Permission Set setting). The IsActive field is available on the RecordType object, as it is global, and user/profile agnostic. The isAvailable property is available through the RecordTypeInfo object, and therefore should be acquired through the Schema methods.

    Here is a utility method, VERY suitable for caching, that, for a given Schema.SObjectType, returns a Map of that Object's Active AND Available RecordType Ids, keyed by DeveloperName:

    //Record types cache
    private static Map<Schema.SObjectType,Map<String,Id>> rtypesCache;
    private static List<sObject> results;
    static {
        rtypesCache = new Map<Schema.SObjectType,Map<String,Id>>();//convenient map, formatted from results.
        results = new List<sObject>();//contains all recordtypes retrieved via SOQL
    }
    
    // Returns a map of active, user-available RecordType IDs for a given SObjectType,
    // keyed by each RecordType's unique, unchanging DeveloperName 
    private static Map<String, Id> getRecordTypeMapForObjectGeneric(Schema.SObjectType token) {
        // Do we already have a result? 
        Map<String, Id> mapRecordTypes = rtypesCache.get(token);
        // If not, build a map of RecordTypeIds keyed by DeveloperName
        if (mapRecordTypes == null) {
            mapRecordTypes = new Map<String, Id>();
            rtypesCache.put(token,mapRecordTypes);
        } else {
           // If we do, return our cached result immediately!
           return mapRecordTypes;
        }
    
        // Get the Describe Result
        Schema.DescribeSObjectResult obj = token.getDescribe();
    
    
        //Check if we already queried all recordtypes.
        if (results == null || results.isEmpty()) {
        // Obtain ALL Active Record Types
        // (We will filter out the Record Types that are unavailable
        // to the Running User using Schema information)
            String soql = 'SELECT Id, Name, DeveloperName, sObjectType FROM RecordType WHERE IsActive = TRUE';
            try {
                results = Database.query(soql);
            } catch (Exception ex) {
                results = new List<SObject>();
            }
        }
    
        // Obtain the RecordTypeInfos for this SObjectType token
        Map<Id,Schema.RecordTypeInfo> recordTypeInfos = obj.getRecordTypeInfosByID();
        // Loop through all of the Record Types we found,
        // and weed out those that are unavailable to the Running User
        for (SObject rt : results) { 
            if (recordTypeInfos.get(rt.Id) != null) {
                if (recordTypeInfos.get(rt.Id).isAvailable()) {
                    // This RecordType IS available to the running user,
                    //      so add it to our map of RecordTypeIds by DeveloperName
                    mapRecordTypes.put(String.valueOf(rt.get('DeveloperName')),rt.Id);
                }
                else {
                    System.debug('The record type ' + rt.get('DeveloperName') + ' for object ' + rt.get('sObjectType') + ' is not availiable for the user.');
                }
            }
        }
    
        return mapRecordTypes;
    }
    

    TO use this to accomplish your need within a Trigger or other Apex scenario, you can just do this:

    // Get all Active Account RecordTypes that are available to the running user
    Map<String,Id> accountTypes = Utils.GetRecordTypeIdsByDeveloperName(Account.SObjectType);
    for (Account a : Trigger.new) {
        // ATTEMPT to assign a different RecordType to this Account
        // if it has tons of employees.
        // We can ONLY attempt, because the RecordType may have been deactivated,
        // and the RecordType may not be available to the Running User,
        // depending on Profile settings.
        if (a.NumberOfEmployees > 10000 
        && accountTypes.containsKey('Really_Stinking_Big_Account')) {
             a.RecordTypeId = accountTypes.get('Really_Stinking_Big_Account');
        } else if (accountTypes.containsKey('Small_Peanuts_Account')) {
             a.RecordTypeId = accountTypes.get('Small_Peanuts_Account');
        }
    }
    

    Notice that we do NOT assume that a given RecordType is either Active or Available to the running user --- we dynamically check. This is essential to consider when leveraging record types in Managed Package code.

    Also notice that because we look for RecordTypes by DeveloperName, this code is completely organization and user agnostic. It will work no matter which org we are in, no matter how the org's admins have overridden or customized your expected use of your installed record types, and no matter which Translations of your RecordTypes' Names are being used.

    Finally, notice that because we do a Dynamic SOQL query, this code will allow your AppExchange app to be used in Professional Edition or Group Edition, which do not have RecordTypes. Any hard-coded references to non-supported objects will cause your app to be disqualified from org editions which do not support such objects --- but use of Apex system classes and methods is fine no matter what features your org supports, therefore getRecordTypeInfos() can be safely used.

    Shouldn't the `GetRecordTypeIdsByDeveloperName` method return immediatelly in case of cache hit? Otherwise what is the purpose of using static map to cache record types?

    @Ivan Hah! Wow. That is exactly what it should be doing. I've edited to correct. Good catch!

    also shouldn't the rtypescache = **new** Map>(); ?

    @Xtremefaith good catch, I corrected this.

    Do you have a test class you can share to go with this?

    This is great, but no longer needed. In Summer '18 (v43.0) Salesforce added the much-needed `getRecordTypeInfosByDeveloperName`. See my solution to this question for an example.

License under CC-BY-SA with attribution


Content dated before 7/24/2021 11:53 AM