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