The ability to create custom fields is a key feature when it comes to customising. Shortly followed by using features like Validation Rules, Workflow, Process Builder, Flow etc. Have you ever developed a solution that also offers the capability to consume user defined fields dynamically? If so you will have used a Custom Object or Custom Setting with a text field to store the field name/s to use.
The Problem:
Using text fields to capture field names has the following challenges and downsides:
- Poor user experience. Unless you develop a Visualforce or Lightning UI users have to know the field API name and enter it correctly in the text field.
- Validation code. While optional, its worth adding some validation to ensure the user enters the correct API name during configuration.
- Changes to field definitions. Once configured there is no guarantee the admin or another user will not inadvertently rename or even delete a field your config references. Resulting in runtime problems when the user next runs your code.
- Migrating configuration. If admins need to move configurations between sandbox and production or developers simply want to prepackage configurations they have to make sure they manually include any custom fields referenced.
- Reading configuration. Reading your configuration requires using the Apex Describe API, which requires a good understanding of namespaces, something not always apparent. Basically its much more complex than things can first appear!
In contrast when using platform tools to reference fields and you attempt to delete a field you are prevented from doing so, if for example a Workflow currently references it. Its also possible to rename fields without breaking references to them. Change Sets and Packaging also automatically discover referenced fields for you.
The Solution:
Using Field Relationships you can now reference custom fields in the same way platform tools do and resolve the above problems with zero code! First head on over to the Setup menu and create yourself a Custom Metadata Type. Next you need to create two Metadata Relationship fields to support your field reference.
The next screen shows a list of either other Custom Metadata Types in the org plus Entity Definition and/or Field Definition. These latter two are two critical objects that have mostly been hiding in the background for several years. They first surfaced in the Tooling API documentation here and here, but are now (in part) accessible from Apex. These are essentially objects who’s records represent all objects (entities) and fields in the org.
You must create at least one Entity Definition relationship field before creating a Field Definition relationship field (the option will not show in the drop down until you do). Creating your Entity Definition relationship field is much like creating lookup fields. When you create a FieldDefinition relationship field, you must state which Entity Definition relationship field controls it (a bit like related pick lists).
In order to explore this i recreated my Rollup Summary custom metadata type from the rollup tool. This currently uses text fields to let the admin user define the parent object and child objects used in the rollup and the related fields. And so i wanted to see what it would look like with this new feature. The resulting user experience is very pleasing! The following screenshot is the native UI, no custom Visualforce code at all!
Firstly when creating or editing custom metadata records object and field drop down lists are rendered for you. When you change a controlling Entity Definition (Object) relationship field (e.g. the Parent Object field) the related Field Definition relationship fields controlled by that field are dynamically updated (e.g. Aggregate Result Field and Relationship Field fields) to list the corresponding fields of the selected object.
Secondly when you save and view custom metadata records the relationships can be navigated via links, so the user can go direct to the object or field definition, very cool!
So far we have addressed issues 1 and 2 from our list above without writing any code! Next lets try to break things. Try to delete the Number of Locations custom field on the Account object and see what happens.
Also try to rename the API name of the Number of Locations field. Basically the platform allows this to happen and yet if you reload the custom metadata record referencing it, things still appear to be intact! This is due to the fact that the relationship is not using the API name, but the field ID, which of course does not change. So thats issue 3, solved!
Next lets try to Package just our custom metadata record above and see what happens. Although i have not tested it i expect the same to be true when using Change Sets. As you can see the platform has figured out through the relationships that the package needs to include the referenced Number of Locations custom field as well. Nice!
NOTE: Take a look at this blog for an example of packaging custom metadata type records based on a packaged custom metadata type.
So thats issue 4 solved, what about the coding challenge 5? Surely thats still business as usual right? Nope, basically by leveraging the QualifiedAPIName field on the EntityDefinition and FieldDefinition objects we can quickly resolve the fully qualified name (including namespace) of the objects and fields being referenced. Note that this will always be valid (since its not stored), so no need to revalidate this before using it!
for(LookupRollupSummary__mdt rollup : [SELECT ParentObject__r.QualifiedAPIName, ChildObject__r.QualifiedAPIName, RelationshipField__r.QualifiedAPIName, AggregateResultField__r.QualifiedAPIName, FieldToAggregate__r.QualifiedAPIName FROM LookupRollupSummary__mdt]) { System.debug( 'Relationship Field: ' + rollup.ChildObject__r.QualifiedAPIName + '.' + rollup.AggregateResultField__r.QualifiedAPIName); }
This outputs the following in the debug log.
DEBUG|Relationship Field: Opportunity.NumberofLocations__c
If you are not yet familiar with Custom Metadata take a look at my earlier blog and related links below. You can also check out the second edition of my book out Friday 31st March, where i cover a number of Custom Metadata configuration use cases throughout. Enjoy!
- Custom Metadata Relationship Considerations
- Custom Metadata Field Relationships Spring’17
- Introducing custom metadata types: the app configuration engine for Force.com
- How to use custom metadata types to save years of development on app configurations
- Testing Custom Metadata Types
- SFDCConfig (Sample Configuration App by Salesforce)
- Custom Metadata Loader
- Sample application for custom metadata types: “Reusable picklists” framework and tooling.
- Community Chatter Group
- Implementation Guide
- Summer’15 Release Notes
March 27, 2017 at 4:03 am
Is there anything else required to activate the field definitions .It only shows entity definitions and I am on Spring 17 org
March 27, 2017 at 4:05 am
Ah got it .Re read the post it says we need one entity relationship for field definitions .Thanks great post as usual
March 27, 2017 at 7:05 am
Hi Andy,
Thank you for this great article on this feature. Also thanks for the various links which are highly useful for understanding the concept. I was always wondering why did Salesforce release Custom Metadata when there is Custom Settings; now I have stronger points to support.
March 27, 2017 at 6:07 pm
No worries Abhilash, glad it help express the value for you. Metadata Relationships have really given the feature a “value” boost for sure!
March 28, 2017 at 4:45 am
Another cool use case for this is to define a list of fields via a set of custom metadata type rows that should be cleared on a clone operation. Often one has ‘system-y’ type fields created by triggers/workflows that simply don’t apply on a clone operation (for example, cloning an Opportunity and clearing the PO_Number__c field). The admin can add to the list of __mdt rows without ever having to touch the underlying apex code
March 28, 2017 at 10:58 pm
Nice! Thanks for sharing!
May 31, 2017 at 9:59 pm
Hi Andy,
I am unable to have the look up icon shoe up on the custom UI page, the doesnt seem to work. Also I am trying to show the Developer name of in the reference field instead of the record ID. Any thoughts?
June 1, 2017 at 1:26 am
Sorry I am not sure of the context of your question here? What are you doing?
June 5, 2017 at 2:14 pm
I am so sorry that my comment was not neatly constructed.
I am unable to have the look up icon show on the custom UI page. The binding doesn’t seem to work. I am being forced to use while attempting to show the Developer name of the reference field instead of the record ID which is currently showing up. Further, on save or update on the record from the UI, it throws an error, invalid reference field and is unable to save the record(But the ID in the reference field is the correct ID to the related metadata type).
I have built my UI based off one of ur posts about custom UI for Custom Metadata Type. My understanding is the code in that post was to support v34.0 and relationships came out with v39.0. We are trying to generate a new Metadata Service Class from the Metadata API, but are having trouble saving it. Do you have any ideas on on implementing the look up icon on the VF page?
June 10, 2017 at 8:18 am
I see, so sadly VF apex:inputField is not supported with Custom Metadata Typre fields. So you cannot get the usual lookup icon to display with MD relationships. You will have to build your own drop down list box.
October 24, 2017 at 2:01 am
There is still some limitations re metadata relationships. When you need to query with WHERE criterion, filtering just by object works well, but filtering by field name (alone or in junction with object condition) throws an error: ‘Query is either selecting too many fields or the filter conditions are too complicated.’
Example of a query I’m talking about:
select Id from CustomLookupName__mdt where Object__r.QualifiedAPIName = ‘AccountAddress__c’ AND Field__r.QualifiedAPIName = ‘AddressLine1__c’
So we need to consider all pros and cons before switching from text fields to metadata relationships. In my case it doesn’t work, because of this limitation, that’s why I still stick just to text fields.
October 25, 2017 at 5:15 pm
Thanks for sharing! 👍🏻
December 21, 2017 at 4:55 am
Hey Andy,
Need your suggestion on this one!
I have a requirement to auto-populate a few fields from a record in its record name. I am doing this via a custom metadata and apex.
There are multiple record types for this record and each record type has a set of different fields that need to be populated in its name.
For example:
Record Type1 should have Name as Field1 – Field2 – Field3
Record Type2 should have Name as Field4 – Field5 – Field6
The problem is when I create a record for the custom metadata, I can only create one record per object. But I need to create two records per object for each record type. How do I do this? Below is the error I get. Many thanks Andy 🙂
Custom Object Naming Setting
Error: Invalid Data.
Review all error messages below to correct your data.
Label: Micro Account Name
Custom Object Naming Setting Name: Micro_Account_Name
Object Name : Products_Services__c
Error: Duplicate value on record: Energy Account Name
Fields: Supply_Address_New__c;Generation_Type__c
Record Type Names: Feed in Tariff;Forest Green Sun
‘Energy Account Name’ is a record I created for this metadata to populate different fields for a different record type.
December 28, 2017 at 10:44 pm
You will need to set unique DeveloperName field value for each record. It’s not clear how your building your UI for this, is it intended to edit multiple records at a time?
December 6, 2019 at 12:55 pm
Hi Andrew! Hoping you can help. I have a controller extension that is querying for my custom metadata type records.
I’m using apex:repeat in my vf page to reference the field relationship api name. However, when I load my vf page I’m getting a message that SObject row was retrieved via SOQL without querying the requested field: [field api name]. I can see that this is because I need to update my class to query for the actual fields on my object that have those names. But I’m not sure exactly how to add that to my controller. Here’s my controller so far:
public class ChecklistItemsController {
private final pba__listing__c listing;
private ApexPages.StandardController stdController;
public ChecklistItemsController(ApexPages.StandardController stdController) {
this.listing = (pba__listing__c)stdController.getRecord();
this.stdController = stdController;
}
public list getChecklistItems(){
return [Select Checkbox_Label__c, Description__c, Display_Order__c, Map_to_Listing_Checkbox_Field__r.QualifiedAPIName, Related_to_Listing_Object__c, id from Listing_Checklist_Item__mdt ];
}
}
Can you give me any pointers for how to do that?
Thanks,
Emily
December 28, 2019 at 7:41 pm
Try this https://developer.salesforce.com/docs/atlas.en-us.pages.meta/pages/apex_ApexPages_StandardController_addFields.htm
Pingback: Understand A Salesforce Feature #1 – Custom Metadata Types (CMDT) – LetsAutomate
October 18, 2021 at 4:48 am
I am trying to write dynamic SOQL against MDT records (to clone an MDT) such that apex does not know the MDT field definition before hand. the Query results of an MDT record looks like . if you do not use QualifiedApiName. But The GetDescribe of an MDT field Entity/Field type does not really look any different from a String type so I am not sure how to dynamiccally append the ‘.QualifiedApiName’. Any ideas?
November 14, 2021 at 10:49 am
Sorry I am not quite following can you elaborate?