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
WillyumWillyum 

Grab the record name from any Id

I am trying to write a trigger that will look at a field where a salesforce Id has been loaded.  I want to then have the trigger populate another field with the record's name.  The catch here is that the Id can be from any object including chatter objects.  Is this something that can be done since the object itself is not defined? Also, these records would need to handle in batches since they will be dataloaded.  I'm am only just learning apex so any help from the community would be really appreciated.

Thanks
Best Answer chosen by Willyum
Glyn Anderson 3Glyn Anderson 3
Willyum,  I created my own Funky Object and made a trigger using the code.  I found a couple of typo/compile problems, which I have fixed below.  Also, for some reason, I had to manually replace a lot of the whitespace.  I suspect that when copying from the webpage, we're copying non-breaking spaces ( ), which don't compile.  We have to replace those with ordinary spaces.  The code below compiles for me.

<pre>
trigger GetRecordNameFromId on Funky_Object__c ( before insert, before update )
{
    // this is a map of "name" fields for objects that don't have one
    Map<String,String> nameFields = new Map<String,String>
    {   'Task' => 'Subject'
    ,   'FeedItem' => 'Title'
    // add more object and field names here
    };

    // this map will hold Record IDs grouped by object name
    Map<String,Set<String>> idsBySObjectName = new Map<String,Set<String>>();

    // this map will hold Funky Objects that look up to a specific record
    Map<Id,List<Funky_Object__c>> funkyObjectsByRecordId = new Map<Id,List<Funky_Object__c>>();

    for ( Funky_Object__c funkyObj : Trigger.new )
    {
        // no Record ID, no Record Name
        if ( funkyObj.Record_Id__c == null )
        {
            funkyObj.Record_Name__c = null;
            continue;
        }

        // use the Record ID to get the object name
        String sObjectName = String.valueOf( Id.valueOf( funkyObj.Record_Id__c ).getSObjectType() );

        // add the Record ID to the collection for that object name
        if ( ! idsBySObjectName.containsKey( sObjectName ) )
        {
            idsBySObjectName.put( sObjectName, new Set<String>() );
        }
        idsBySObjectName.get( sObjectName ).add( '\'' + funkyObj.Record_Id__c + '\'' );

        // add the Funky Object to the collection for the Record ID
        if ( ! funkyObjectsByRecordId.containsKey( funkyObj.Record_Id__c ) )
        {
            funkyObjectsByRecordId.put( funkyObj.Record_Id__c, new List<Funky_Object__c>() );
        }
        funkyObjectsByRecordId.get( funkyObj.Record_Id__c ).add( funkyObj );
    }

    for ( String sObjectName : idsBySObjectName.keySet() )
    {
        String nameField = nameFields.get( sObjectName );
        if ( nameField == null ) nameField = 'Name';

        // create a query for all records of a single type
        List<String> queryStrings = new List<String>
        {   'SELECT Id,'
        ,   namefield
        ,   'FROM'
        ,   sObjectName
        ,   'WHERE Id IN'
        ,   '(' + String.join( new List<String>( idsBySObjectName.get( sObjectName ) ), ',' ) + ')'
        };

        // for each record in the query results,
        // write its name to all the Funky Objects that look up to it
        for ( sObject obj : Database.query( String.join( queryStrings, ' ' ) ) )
        {
            Id recordId = (Id) obj.get( 'Id' );
            String name = (String) obj.get( nameField );
            for ( Funky_Object__c funkyObj : funkyObjectsByRecordId.get( recordId ) )
            {
                funkyObj.Record_Name__c = name;
            }
        }
    }
}
</pre>

All Answers

Asif Ali MAsif Ali M
Here is the sample code to get RecordName from Record Id. You need to add some error handling .
 
public static String getRecordName(Id recId) {

		object objName = recId.getSobjectType();
		system.debug(objName);
		String query = 'Select Name FROM '+objName+ ' WHERE Id=:recId LIMIT 1';
		Sobject myObj = Database.query(query);
		return String.valueOf(myObj.get('Name'));
	}

 
Glyn Anderson 3Glyn Anderson 3
Willyum,  This is a very interesting problem indeed.  Let me try.  I must assume that the field with the Salesforce ID is a Text field, as you can't define polymorphic look up fields (although some standard look up fields are polymorphic, like WhoId and WhatId on Task).  Some standard objects (like Task and FeedItem) do not have a "Name" field, so there is a mechanism to identify which field you want to use from those objects.  Note:  This code is untested and may contain typos.

<pre>
public trigger GetRecordNameFromId on Funky_Object__c ( before insert, before update )
{
    // build a map of all the Record ID Prefixes in this org
    Map<String,String> keyPrefixes = new Map<String,String>();
    for ( Schema.SObjectType sObjectType : Schema.getGlobalDescribe().values() )
    {
        Schema.DescribeSObjectResult objDescribe = sObjectType.getDescribe();
        keyPrefixes.put( objDescribe.getKeyPrefix(), objDescribe.getName() );
    }

    // this is a map of "name" fields for objects that don't have one
    Map<String,String> nameFields = new Map<String,String>
    {   'Task' => 'Subject'
    ,   'FeedItem' => 'Title'
    // add more object and field names here
    };

    // this map will hold Record IDs grouped by object name
    Map<String,Set<String>> idsBySObjectName = new Map<String,Set<Id>>();

    // this map will hold Funky Objects that look up to a specific record
    Map<Id,List<Funky_Object__c>> funkyObjectsByRecordId = new Map<Id,List<Funky_Object__c>>();

    for ( Funky_Object__c funkyObj : Trigger.new )
    {
        // no Record ID, no Record Name
        if ( funkyObj.Record_Id__c == null )
        {
            funkyObj.Record_Name__c = null;
            continue;
        }

        // use the Record ID prefix to look up the object name
        String sObjectName = keyPrefixes.get( funkyObj.Record_Id__c.left( 3 ) );

        // add the Record ID to the collection for that object name
        if ( ! idsBySObjectType.containsKey( sObjectName ) )
        {
            idsBySObjectType.put( sObjectName, new Set<String>() );
        }
        idsBySObjectType.get( sObjectName ).add( '\'' + funkyObj.Record_Id__c + '\'' );

        // add the Funky Object to the collection for the Record ID
        if ( ! funkyObjectsByRecordId.containsKey( funkyObj.Record_Id__c ) )
        {
            funkyObjectsByRecordId.put( funkyObj.Record_Id__c, new List<Funky_Object__c>() );
        }
        funkyObjectsByRecordId.get( funkyObj.Record_Id__c ).add( funkyObj );
    }

​    for ( String sObjectName : idsBySObjectName.keySet() )
    {
        String nameField = nameFields.get( sObjectName );
        if ( nameField == null ) nameField = 'Name';

        // create a query for all records of a single type
        String queryStrings = new List<String>
        {   'SELECT Id,'
        ,   namefield
        ,   'FROM'
        ,   sObjectName
        ,   'WHERE Id IN'
        ,   '(' + String.join( new List<String>( idsBySObjectName.get( sObjectName ) ), ',' ) + ')'
        };

        // for each record in the query results,
        // write its name to all the Funky Objects that look up to it
        for ( sObject obj : Database.query( String.join( queryStrings, ' ' ) )
        {
            Id recordId = (Id) obj.get( 'Id' );
            String name = (String) obj.get( nameField );
            for ( Funky_Object__c funkyObj : funkyObjectsByRecordId.get( recordId ) )
            {
                funkyObj.Record_Name__c = name;
            }
        }
    }
}
</pre>
 
Glyn Anderson 3Glyn Anderson 3
Typo on line 19.  It should be
<pre>
    Map<String,Set<String>> idsBySObjectName = new Map<String,Set<String>>();
</pre>
Glyn Anderson 3Glyn Anderson 3
Just saw Asif Ali M's solution.  I like his technique for getting the SObject name better than my technique of using the key prefix.  It's more efficient and doesn't require calling getDescribe() hundreds of times.  So here's my bulkified solution using his technique:

<pre>
public trigger GetRecordNameFromId on Funky_Object__c ( before insert, before update )
{
    // this is a map of "name" fields for objects that don't have one
    Map<String,String> nameFields = new Map<String,String>
    {   'Task' => 'Subject'
    ,   'FeedItem' => 'Title'
    // add more object and field names here
    };

    // this map will hold Record IDs grouped by object name
    Map<String,Set<String>> idsBySObjectName = new Map<String,Set<String>>();

    // this map will hold Funky Objects that look up to a specific record
    Map<Id,List<Funky_Object__c>> funkyObjectsByRecordId = new Map<Id,List<Funky_Object__c>>();

    for ( Funky_Object__c funkyObj : Trigger.new )
    {
        // no Record ID, no Record Name
        if ( funkyObj.Record_Id__c == null )
        {
            funkyObj.Record_Name__c = null;
            continue;
        }

        // use the Record ID to get the object name
        String sObjectName = String.valueOf( Id.valueOf( funkyObj.Record_Id__c ).getSObjectType() );

        // add the Record ID to the collection for that object name
        if ( ! idsBySObjectType.containsKey( sObjectName ) )
        {
            idsBySObjectType.put( sObjectName, new Set<String>() );
        }
        idsBySObjectType.get( sObjectName ).add( '\'' + funkyObj.Record_Id__c + '\'' );

        // add the Funky Object to the collection for the Record ID
        if ( ! funkyObjectsByRecordId.containsKey( funkyObj.Record_Id__c ) )
        {
            funkyObjectsByRecordId.put( funkyObj.Record_Id__c, new List<Funky_Object__c>() );
        }
        funkyObjectsByRecordId.get( funkyObj.Record_Id__c ).add( funkyObj );
    }

​    for ( String sObjectName : idsBySObjectName.keySet() )
    {
        String nameField = nameFields.get( sObjectName );
        if ( nameField == null ) nameField = 'Name';

        // create a query for all records of a single type
        String queryStrings = new List<String>
        {   'SELECT Id,'
        ,   namefield
        ,   'FROM'
        ,   sObjectName
        ,   'WHERE Id IN'
        ,   '(' + String.join( new List<String>( idsBySObjectName.get( sObjectName ) ), ',' ) + ')'
        };

        // for each record in the query results,
        // write its name to all the Funky Objects that look up to it
        for ( sObject obj : Database.query( String.join( queryStrings, ' ' ) )
        {
            Id recordId = (Id) obj.get( 'Id' );
            String name = (String) obj.get( nameField );
            for ( Funky_Object__c funkyObj : funkyObjectsByRecordId.get( recordId ) )
            {
                funkyObj.Record_Name__c = name;
            }
        }
    }
}
</pre>
 
WillyumWillyum
Thank you both.  I guess I'm stupid but I don't understand what is supposed to preceed the code to put in a trigger.  When I paste this into a new trigger, and the replace all instances of Funk_Code__c with the actual object, I get a compile error on line 1 at the word Trigger.  Is there some other code that needs to wrap around this?
Glyn Anderson 3Glyn Anderson 3
Willyum,  Try saving just an empty trigger successfully before adding the rest of the code.  Try creating the trigger in the Salesforce UI.  Go to the custom object; in the Triggers section, click new.  Modify the template code to look like the code below.

<pre>
public trigger GetRecordNameFromId on <your object> ( before insert, before update )
{
}
</pre>
 
WillyumWillyum
Hi Glyn,
I created a new trigger in the developer console.  I'm getting a compile error on line 43 : No viable alternative at character" 

That line reads:  ​for ( String sObjectName : idsBySObjectName.keySet() )
Glyn Anderson 3Glyn Anderson 3
Willyum,  I created my own Funky Object and made a trigger using the code.  I found a couple of typo/compile problems, which I have fixed below.  Also, for some reason, I had to manually replace a lot of the whitespace.  I suspect that when copying from the webpage, we're copying non-breaking spaces (&nbsp;), which don't compile.  We have to replace those with ordinary spaces.  The code below compiles for me.

<pre>
trigger GetRecordNameFromId on Funky_Object__c ( before insert, before update )
{
    // this is a map of "name" fields for objects that don't have one
    Map<String,String> nameFields = new Map<String,String>
    {   'Task' => 'Subject'
    ,   'FeedItem' => 'Title'
    // add more object and field names here
    };

    // this map will hold Record IDs grouped by object name
    Map<String,Set<String>> idsBySObjectName = new Map<String,Set<String>>();

    // this map will hold Funky Objects that look up to a specific record
    Map<Id,List<Funky_Object__c>> funkyObjectsByRecordId = new Map<Id,List<Funky_Object__c>>();

    for ( Funky_Object__c funkyObj : Trigger.new )
    {
        // no Record ID, no Record Name
        if ( funkyObj.Record_Id__c == null )
        {
            funkyObj.Record_Name__c = null;
            continue;
        }

        // use the Record ID to get the object name
        String sObjectName = String.valueOf( Id.valueOf( funkyObj.Record_Id__c ).getSObjectType() );

        // add the Record ID to the collection for that object name
        if ( ! idsBySObjectName.containsKey( sObjectName ) )
        {
            idsBySObjectName.put( sObjectName, new Set<String>() );
        }
        idsBySObjectName.get( sObjectName ).add( '\'' + funkyObj.Record_Id__c + '\'' );

        // add the Funky Object to the collection for the Record ID
        if ( ! funkyObjectsByRecordId.containsKey( funkyObj.Record_Id__c ) )
        {
            funkyObjectsByRecordId.put( funkyObj.Record_Id__c, new List<Funky_Object__c>() );
        }
        funkyObjectsByRecordId.get( funkyObj.Record_Id__c ).add( funkyObj );
    }

    for ( String sObjectName : idsBySObjectName.keySet() )
    {
        String nameField = nameFields.get( sObjectName );
        if ( nameField == null ) nameField = 'Name';

        // create a query for all records of a single type
        List<String> queryStrings = new List<String>
        {   'SELECT Id,'
        ,   namefield
        ,   'FROM'
        ,   sObjectName
        ,   'WHERE Id IN'
        ,   '(' + String.join( new List<String>( idsBySObjectName.get( sObjectName ) ), ',' ) + ')'
        };

        // for each record in the query results,
        // write its name to all the Funky Objects that look up to it
        for ( sObject obj : Database.query( String.join( queryStrings, ' ' ) ) )
        {
            Id recordId = (Id) obj.get( 'Id' );
            String name = (String) obj.get( nameField );
            for ( Funky_Object__c funkyObj : funkyObjectsByRecordId.get( recordId ) )
            {
                funkyObj.Record_Name__c = name;
            }
        }
    }
}
</pre>
This was selected as the best answer
WillyumWillyum
Works like a charm Glyn !   Thank you !!!