$ObjectType を使用したスキーマ詳細への動的参照
$ObjectType グローバル変数を使用すると、組織のオブジェクトに関するさまざまなスキーマ情報にアクセスできます。たとえば、オブジェクトの項目の名前、表示ラベル、データ型の参照に使用します。
$ObjectType は、「深い」グローバル変数であり、次のような「二重に動的」な参照に使用することができます。
1$ObjectType[sObjectName].fields[fieldName].Type次の例では、動的グローバル変数を使用して一般的なオブジェクトビューアーを提供します。最初に、DynamicObjectHandler という名前で新しいコントローラー (拡張ではない) を作成します。
このコントローラーでは、次の点に留意してください。
1public class DynamicObjectHandler {
2
3 // This class acts as a controller for the DynamicObjectViewer component
4
5 private String objType;
6 private List<String> accessibleFields;
7
8 public sObject obj {
9 get;
10 set {
11 setObjectType(value);
12 discoverAccessibleFields(value);
13 obj = reloadObjectWithAllFieldData();
14 }
15 }
16
17 // The sObject type as a string
18 public String getObjectType() {
19 return(this.objType);
20 }
21 public String setObjectType(sObject newObj) {
22 this.objType = newObj.getSObjectType().getDescribe().getName();
23 return(this.objType);
24 }
25
26 // List of accessible fields on the sObject
27 public List<String> getAccessibleFields() {
28 return(this.accessibleFields);
29 }
30
31 private void discoverAccessibleFields(sObject newObj) {
32 this.accessibleFields = new List<String>();
33 Map<String, Schema.SobjectField> fields =
34 newObj.getSObjectType().getDescribe().fields.getMap();
35 for (String s : fields.keySet()) {
36 if ((s != 'Name') && (fields.get(s).getDescribe().isAccessible())) {
37 this.accessibleFields.add(s);
38 }
39 }
40 }
41
42 private sObject reloadObjectWithAllFieldData() {
43 String qid = ApexPages.currentPage().getParameters().get('id');
44 String theQuery = 'SELECT ' + joinList(getAccessibleFields(), ', ') +
45 ' FROM ' + getObjectType() +
46 ' WHERE Id = :qid';
47 return(Database.query(theQuery));
48 }
49
50 // Join an Apex List of fields into a SELECT fields list string
51 private static String joinList(List<String> theList, String separator) {
52
53 if (theList == null) { return null; }
54 if (separator == null) { separator = ''; }
55
56 String joined = '';
57 Boolean firstItem = true;
58 for (String item : theList) {
59 if(null != item) {
60 if(firstItem){ firstItem = false; }
61 else { joined += separator; }
62 joined += item;
63 }
64 }
65 return joined;
66 }
67}- Visualforce コンポーネントはコントローラー拡張を使用できません。代わりに、このクラスはコントローラーとして記述されます。コンストラクターは定義されないため、このクラスではデフォルトのコンストラクターが使用されます。
- オブジェクトのメタデータを収集するには、コントローラーでオブジェクトを把握している必要があります。Visualforce コンストラクターは引数を取れないため、インスタンス化時に目的のオブジェクトを知る方法がありません。代わりに、公開プロパティ obj を設定することで、メタデータ検出がトリガーされます。
- このクラスでは複数のメソッドが、前の例より若干異なる方法でシステムスキーマ検出メソッドを使用しています。
次の例は、オブジェクトに関するスキーマ情報と、照会されるレコードの特定の値を表示する Visualforce コンポーネントです。次のコードを使用し、DynamicObjectViewer という名前で新規 Visualforce コンポーネントを作成します。
次の点を確認してください。
1<apex:component controller="DynamicObjectHandler">
2 <apex:attribute name="rec" type="sObject" required="true"
3 description="The object to be displayed." assignTo="{!obj}"/>
4
5 <apex:form >
6 <apex:pageBlock title="{!objectType}">
7 <apex:pageBlockSection title="Fields" columns="1">
8 <apex:dataTable value="{!accessibleFields}" var="f">
9 <apex:column >
10 <apex:facet name="header">Label</apex:facet>
11 <apex:outputText value="{!$ObjectType[objectType].fields[f].Label}"/>
12 </apex:column>
13 <apex:column >
14 <apex:facet name="header">API Name</apex:facet>
15 <apex:outputText value="{!$ObjectType[objectType].fields[f].Name}"/>
16 </apex:column>
17 <apex:column >
18 <apex:facet name="header">Type</apex:facet>
19 <apex:outputText value="{!$ObjectType[objectType].fields[f].Type}"/>
20 </apex:column>
21 <apex:column >
22 <apex:facet name="header">Value</apex:facet>
23 <apex:outputText value="{!obj[f]}"/>
24 </apex:column>
25 </apex:dataTable>
26 </apex:pageBlockSection>
27
28 <apex:pageBlockSection columns="4">
29 <apex:commandButton value="View"
30 action="{!URLFOR($Action[objectType].View, obj.Id)}"/>
31 <apex:commandButton value="Edit"
32 action="{!URLFOR($Action[objectType].Edit, obj.Id)}"/>
33 <apex:commandButton value="Clone"
34 action="{!URLFOR($Action[objectType].Clone, obj.Id)}"/>
35 <apex:commandButton value="Delete"
36 action="{!URLFOR($Action[objectType].Delete, obj.Id)}"/>
37 </apex:pageBlockSection>
38 </apex:pageBlock>
39 </apex:form>
40
41</apex:component>- このコンポーネントを使用するページでは、レコードを検索する必要があります。これを行うには、そのオブジェクトに標準コントローラーを使用し、URL でレコードの Id を指定します。たとえば、https://<Salesforce_instance>/apex/DynamicContactPage?id=003D000000Q5GHE です。
- 選択したレコードはすぐにコンポーネントの obj 属性に渡されます。このパラメーターは、すべてのオブジェクトメタデータ検出に使用されます。
- この 3 つの二重に動的な参照が、$ObjectType[objectType].fields[f] で開始し、各項目のメタデータを表示するのに対し、通常の動的参照は項目の実際の値を表示します。
- データ値の場合、値は、より自然な {!rec[f]} (コンポーネントへのパラメーター) ではなく {!obj[f]} で、コントローラー内で getter メソッドが使用されます。その理由は単純で、obj 属性はすべての項目のデータを読み込むように更新されていますが、rec は標準コントローラーで読み込まれたときと同じ状態のままであり、読み込まれるのが Id 項目のみであるためです。
最後に、新しいコンポーネントを使用して、任意の数の単純な Visualforce ページを作成できます。このページは、コンポーネントを使用して、次の 2 つのページのようなレコード詳細およびスキーマ情報ページを表示します。
1<apex:page standardController="Account">
2 <c:DynamicObjectViewer rec="{!account}"/>
3</apex:page>1<apex:page standardController="Contact">
2 <c:DynamicObjectViewer rec="{!contact}"/>
3</apex:page>