labunix's blog

labunixのラボUnix

SalesforceのApexでラベル名やリレーション名を取得してみる。

■SalesforceのApexでラベル名やリレーション名を取得してみる。
 どこのサイトを見てもコードが長い。そんなはずないと思うので書いてみた。

 開発者コンソールの「Open Execute Anonymous Window」で「Open Log」にチェックを入れて実行
 「Debug Only」でフィルタする方法についてのお話。

■欲しいメインの処理は以下を使う。

 DescribeFieldResult クラス
 https://developer.salesforce.com/docs/atlas.ja-jp.apexcode.meta/apexcode/apex_methods_system_fields_describe.htm

■そのために必要な、あるオブジェクトの全項目を取得するには、以下のようにたどればよい。

// Schema.getGlobalDescribe()
// 全オブジェクトを取得
// 
// Schema.getGlobalDescribe().get('User')
// 全オブジェクトから指定したオブジェクトを取得
//
// Schema.getGlobalDescribe().get('User').getDescribe()
// SObjectの情報を取得
//
// Schema.getGlobalDescribe().get('User').getDescribe().fields.getMap()
// 指定したSObjectの全項目を取得

■それならこれでいける。
 contains()は文字列検索。「__c」なのでカスタム項目のみ

Map<String, Schema.SObjectField> fields = Schema.getGlobalDescribe().get('User').getDescribe().fields.getMap();

for (Schema.SObjectField field : fields.values()) {
    Schema.DescribeFieldResult dfr = field.getDescribe();
      if (dfr.getName().contains('__c')){
        System.debug(dfr.getName() + ',' + dfr.getLabel());
      }
}

■すべての項目のラベル名を取得
 if文をコメントアウトするだけ。

Map<String, Schema.SObjectField> fields = Schema.getGlobalDescribe().get('User').getDescribe().fields.getMap();

for (Schema.SObjectField field : fields.values()) {
    Schema.DescribeFieldResult dfr = field.getDescribe();
      // if (dfr.getName().contains('__c')){
        System.debug(dfr.getName() + ',' + dfr.getLabel());
      //}
}

■「getRelationshipName()」で親子関係を検索、「getReferenceTo()」でどのオブジェクトを参照しているか確認する。
 ※contains()用のif文をコメントアウト。nullはリレーション名がないものなので除外

Map<String, Schema.SObjectField> fields = Schema.getGlobalDescribe().get('User').getDescribe().fields.getMap();

for (Schema.SObjectField field : fields.values()) {
    Schema.DescribeFieldResult dfr = field.getDescribe();
    if (!dfr.getReferenceTo().isEmpty() ||  dfr.getRelationshipName() != null ){ // if (dfr.getName().contains('__c')){
        System.debug(dfr.getName() + ',' + dfr.getLabel() + ',' + dfr.getRelationshipName() + ',' + dfr.getReferenceTo() );
    }
}

// getRelationshipName() がnull でgetReferenceTo() で参照している項目は「.」を使った参照ができない。
02:17:51:168 USER_DEBUG [6]|DEBUG|UserRoleId,Role ID,UserRole,(UserRole)
02:17:51:176 USER_DEBUG [6]|DEBUG|ProfileId,Profile ID,Profile,(Profile)
02:17:51:179 USER_DEBUG [6]|DEBUG|DelegatedApproverId,Delegated Approver ID,null,(Group, User)
02:17:51:180 USER_DEBUG [6]|DEBUG|ManagerId,Manager ID,Manager,(User)
02:17:51:182 USER_DEBUG [6]|DEBUG|CreatedById,Created By ID,CreatedBy,(User)
02:17:51:183 USER_DEBUG [6]|DEBUG|LastModifiedById,Last Modified By ID,LastModifiedBy,(User)
02:17:51:224 USER_DEBUG [6]|DEBUG|ContactId,Contact ID,Contact,(Contact)
02:17:51:224 USER_DEBUG [6]|DEBUG|AccountId,Account ID,Account,(Account)
02:17:51:225 USER_DEBUG [6]|DEBUG|CallCenterId,Call Center ID,null,(CallCenter)
02:17:51:234 USER_DEBUG [6]|DEBUG|IndividualId,Individual ID,Individual,(Individual)

■「DelegatedApprover.Name」や「CallCenter.Name」は参照できないが、
 「Manager.Name」や「CreatedBy.Name」は参照できる。

■このリレーション名があるから、簡単に例えばユーザライセンスの使用中の数を集計したいと思ったら、
 SOQL文で以下のように書ける。

 複数項目「Name」や「~.Name」があると、開発者コンソールでは別名をつける必要がある。※エラーになる。
 反対にデータローダでは別名を付けるとエラーになる。

select Profile.UserLicense.Name License, Count(Id) from User
 where IsActive = true
 group by Profile.UserLicense.Name
 order by Profile.UserLicense.Name

■機能ライセンスはgroup byできないけど、ラベル名を知りたいのであれば「UserPermissions」で検索すればよい。
 ※ロケール言語に依存。日本語のラベル名も表示できるけど、以下は英語環境。

Map<String, Schema.SObjectField> fields = Schema.getGlobalDescribe().get('User').getDescribe().fields.getMap();

for (Schema.SObjectField field : fields.values()) {
    Schema.DescribeFieldResult dfr = field.getDescribe();
    if (dfr.getName().contains('UserPermissions')){ //if (!dfr.getReferenceTo().isEmpty() ||  dfr.getRelationshipName() != null){ 
        System.debug(dfr.getName() + ',' + dfr.getLabel() + ',' + dfr.getRelationshipName() + ',' + dfr.getReferenceTo() );
    }
}

//いっぱいあるので、一部だけ。
02:30:13:181 USER_DEBUG [6]|DEBUG|UserPermissionsMarketingUser,Marketing User,null,()
02:30:13:182 USER_DEBUG [6]|DEBUG|UserPermissionsOfflineUser,Offline User,null,()
02:30:13:182 USER_DEBUG [6]|DEBUG|UserPermissionsCallCenterAutoLogin,Auto-login To Call Center,null,()
02:30:13:183 USER_DEBUG [6]|DEBUG|UserPermissionsSFContentUser,Salesforce CRM Content User,null,()
02:30:13:183 USER_DEBUG [6]|DEBUG|UserPermissionsKnowledgeUser,Knowledge User,null,()
...

■「Marketing User」のライセンス消費ユーザの集計は例えば以下でできる。
 ユーザライセンス毎に使える機能ライセンスの組み合わせに制限があるので、
 ユーザライセンス毎に集計した方が良いでしょう。

select Profile.UserLicense.Name, Count(Id) from User
 where IsActive = true
 and UserPermissionsMarketingUser = true
 group by Profile.UserLicense.Name

■Userオブジェクトの項目を見てもあまり面白くはないので、
 権限セットライセンスを最後に。

Map<String, Schema.SObjectField> fields = Schema.getGlobalDescribe().get('PermissionSetLicenseAssign').getDescribe().fields.getMap();

for (Schema.SObjectField field : fields.values()) {
    Schema.DescribeFieldResult dfr = field.getDescribe();
    if (!dfr.getReferenceTo().isEmpty() ||  dfr.getRelationshipName() != null){ // if (dfr.getName().contains('UserPermissions')){
        System.debug(dfr.getName() + ',' + dfr.getLabel() + ',' + dfr.getRelationshipName() + ',' + dfr.getReferenceTo() );
    }
}

02:34:41:093 USER_DEBUG [6]|DEBUG|CreatedById,Created By ID,CreatedBy,(User)
02:34:41:094 USER_DEBUG [6]|DEBUG|LastModifiedById,Last Modified By ID,LastModifiedBy,(User)
02:34:41:095 USER_DEBUG [6]|DEBUG|PermissionSetLicenseId,Permission Set License ID,PermissionSetLicense,(PermissionSetLicense)
02:34:41:095 USER_DEBUG [6]|DEBUG|AssigneeId,User ID,Assignee,(User)

■権限セットライセンスもユーザライセンスに紐づけて集計した方がよいので。
 AssgineeIdがUserオブジェクトを参照しているのは調べた通り。

select PermissionSetLicense.MasterLabel, Assignee.Profile.UserLicense.Name, Count(AssigneeId) from PermissionSetLicenseAssign
 where Assignee.IsActive = true
 group by PermissionSetLicense.MasterLabel, Assignee.Profile.UserLicense.Name

■オブジェクトマネージャからは見えないようなオブジェクト、例えばログイン履歴(LoginHistory)を参照
 UserIdのリレーションは無くて困るなー、2つのオブジェクトを持ってきてVLOOKUPかーとか。
 LoginGeoへの参照はあるんだー、それでCountryIsoの値を持ってきてるのねーとか。

Map<String, Schema.SObjectField> fields = Schema.getGlobalDescribe().get('LoginHistory').getDescribe().fields.getMap();

for (Schema.SObjectField field : fields.values()) {
    Schema.DescribeFieldResult dfr = field.getDescribe();
    if (!dfr.getReferenceTo().isEmpty() ||  dfr.getRelationshipName() != null ){ // if (dfr.getName().contains('__c')){
        System.debug(dfr.getName() + ',' + dfr.getLabel() + ',' + dfr.getRelationshipName() + ',' + dfr.getReferenceTo() );
    }
}

// dfr.getRelationshipName() がnull なのに、dfr.getReferenceTo() で参照している項目は「.」を使った参照ができない。
02:13:38:155 USER_DEBUG [6]|DEBUG|UserId,User ID,null,(User)
02:13:38:157 USER_DEBUG [6]|DEBUG|NetworkId,Network ID,null,(Network)
02:13:38:158 USER_DEBUG [6]|DEBUG|AuthenticationServiceId,Authentication Service ID,null,(AuthProvider, SamlSsoConfig)

//dfr.getRelationshipName() がnull ではないので、参照できる。
02:13:38:158 USER_DEBUG [6]|DEBUG|LoginGeoId,Login Geo Data ID,LoginGeo,(LoginGeo)

■地味に重宝するのは子-親参照ができるカスタム項目をとれること。
 カスタムオブジェクト「Travel_Request__c」はTrailheadやってれば出てくる。
 「__c」を「__r」に変えてもダメじゃんではなくて、「__r」できる項目がどれかがきちんとわかる。

Map<String, Schema.SObjectField> fields = Schema.getGlobalDescribe().get('Travel_Request__c').getDescribe().fields.getMap();

for (Schema.SObjectField field : fields.values()) {
    Schema.DescribeFieldResult dfr = field.getDescribe();
    if (!dfr.getReferenceTo().isEmpty() ||  dfr.getRelationshipName() != null){ // if (dfr.getName().contains('UserPermissions')){
        System.debug(dfr.getName() + ',' + dfr.getLabel() + ',' + dfr.getRelationshipName() + ',' + dfr.getReferenceTo() );
    }
}

02:49:02:156 USER_DEBUG [6]|DEBUG|OwnerId,Owner ID,Owner,(Group, User)
02:49:02:158 USER_DEBUG [6]|DEBUG|CreatedById,Created By ID,CreatedBy,(User)
02:49:02:160 USER_DEBUG [6]|DEBUG|LastModifiedById,Last Modified By ID,LastModifiedBy,(User)
02:49:02:162 USER_DEBUG [6]|DEBUG|Approver__c,Approver,Approver__r,(User)

■レコードがあればだけど、以下で承認者の氏名が取れる。

select Approver__r.Name from Travel_Request__c

■あとは、該当しない項目をフィルタしたい時にはサブクエリを考える。