この文章は Salesforce 機械翻訳システムを使用して翻訳されました。詳細はこちらをご参照ください。
英語に切り替える

もうひとこと: コードについて

Visualforce カスタムアクションの背後には 2 つのコードオブジェクト (Apex クラス QuickOrderController と Visualforce ページ QuickOrderPage) があります。

Apex クラスは、Visualforce ページのコントローラで、メソッドの @RemoteAction アノテーションを使用します。このアノテーションを使用すると、Visualforce ページは JavaScript 対応の方法でロジックをラップします。これは、Visualforce Remoting と呼ばれます。

Visualforce Remoting を使用すると、Apex と JavaScript 間を迅速かつ緊密に統合できます。この通信モデルは、従来の Visualforce/Apex MVC パラダイムの同期モデルと異なり、非同期で動作します。そのため、パラメータをコントローラに渡したら、DOM 操作を実行して、モバイルテンプレートやフレームワークを使用してページを作成する前に、レスポンスハンドラ関数から結果を取得し、追加のクライアント側ロジックを記述できます。

Visualforce Remoting は、Salesforce オブジェクトへのサーバ側の直接アクセスを簡素化し、迅速なプラットフォーム開発のための Apex ツール (SOQL や Apex メソッドなど) を使用できるため、Salesforce App Cloud のモバイル開発者に最適です。また、ビューステートを処���する必要がないため、ページのパフォーマンスが向上します。

Apex クラス QuickOrderController

このクラスは Visualforce Remoting を使用します。また、このクラスには、倉庫を検索して注文および品目を作成するために Visualforce ページによってコールされるロジックが含まれています。

1global class QuickOrderController{
2    public static List<Merchandise__c> merchandise;
3    public static Line_Item__c quickOrder;
4    
5    
6    public QuickOrderController(ApexPages.
7        StandardController controller){  
8    }
9    
10    @RemoteAction
11    global static List<Merchandise__c> findWarehouses(String accId,
12        String merchName, String warehouseDist){
13         merchandise = new List<Merchandise__c>();
14         String queryString = '';
15         String queryName = '%' + merchName + '%';
16         
17         Account acc = [Select Location__Longitude__s, 
18         Location__Latitude__s, Name, Id 
19         from Account where Id =: accId];
20         
21         //Finds warehouses nearby if you have location 
22         //specified on the Account
23         if(acc.Location__Latitude__s != null && 
24             acc.Location__Longitude__s != null){
25             queryString = 'SELECT Id, (SELECT Id, Name, Quantity__c,
26                 Warehouse__r.Name, Warehouse__r.Id, 
27                 Warehouse__r.Street_Address__c, 
28                 Warehouse__r.City__c '+
29                 'FROM Merchandise__r WHERE Name 
30                 like :queryName) '
31                 +'FROM Warehouse__c WHERE '
32                 +'DISTANCE(Location__c, GEOLOCATION('
33                 +acc.Location__Latitude__s+','
34                 +acc.Location__Longitude__s+'), \'mi\')';
35             if(warehouseDist != null){
36                 queryString += ' <'+ warehouseDist;
37             }
38             
39         }
40         //If no location defined on the Account, this will run 
41         //query against the merchandise name only
42         else {
43             queryString = 'SELECT Id, Name, 
44                 Location__Longitude__s, 
45                 Location__Latitude__s, '
46                 +'(SELECT Id, Name, Warehouse__r.Name, 
47                 Quantity__c 
48                 FROM Merchandise__r WHERE Name 
49                 like :queryName) '
50                 +'FROM Warehouse__c limit 25';
51             
52         }
53         
54         //This creates a list of merchandise 
55         //to display in the search results
56         Warehouse__c[] warehouses = Database.Query(queryString);
57         for(Warehouse__c warehouse : warehouses){
58             Merchandise__c[] merch = 
59                 warehouse.getSObjects('Merchandise__r');
60                 if (merch != null) {
61                    for (Merchandise__c m : merch){ 
62                        merchandise.add(m);
63                    }
64                 }    
65         }
66         return merchandise;
67         
68    }
69    
70    //This remote action creates the invoice for the quick order
71    @RemoteAction
72    global static Line_Item__c createQuickOrder(
73        String accId, String merchandiseId){
74        Invoice__c newInvoice = new Invoice__c();
75        newInvoice.Account__c = accId;
76        insert newInvoice;
77        
78        quickOrder = new Line_Item__c();
79        Merchandise__c m = [Select Id, Name from Merchandise__c 
80            where Id=: merchandiseId limit 1];
81        quickOrder.Merchandise__c = m.Id;
82        quickOrder.Invoice__c = newInvoice.Id;
83        
84        return quickOrder;
85    }
86    
87    //This remote action creates the line item related to the 
88    //invoice for the quick order
89    @RemoteAction
90    global static Boolean insertQuickOrder(String o, String q){
91        try {
92            Integer quantity = integer.valueof(q);
93        
94        Line_Item__c order = new Line_Item__c();
95        /* The order variable being passed in as a param is being 
96        passed in the form of a JSON object. You need to use 
97        the JSON deserialize method in Apex to convert it 
98        into a SObject */
99        order = (Line_Item__c)JSON.deserialize(
100            o, Line_Item__c.class);
101        
102        order.Quantity__c = quantity;
103        insert order;
104
105    //Need to requery for the name for the post to chatter 
106    //since it wasn't explicitly specified
107    Line_Item__c li = [Select Name, Merchandise__r.Name, Id, 
108        Quantity__c, Invoice__c from Line_Item__c 
109        where Id =: order.Id];
110            
111        FeedItem post = new FeedItem();
112        post.ParentId = aId;
113        post.Body = UserInfo.getName() + ' just created a quick order';
114        post.type = 'LinkPost'; 
115        post.LinkUrl = '/' + li.Invoice__c;
116        post.Title = li.Merchandise__r.Name + ': ' + li.quantity__c;
117        insert post; 
118    } catch(System.Exception ex) {
119        system.debug(ex.getMessage());
120    }
121        return true;
122    }
123    
124    //This remote action handles deleting the invoice if 
125    //the user doesn't want to insert the line item
126    @RemoteAction
127    global static Boolean goBack(String invoiceId){
128        // Delete created invoice and return to original 
129        //search screen
130        Invoice__c cancelledInvoice = [select Id from Invoice__c 
131            where Id=: invoiceId];
132        delete cancelledInvoice;
133        
134        return true;
135    }
136    
137}

静的クエリとバインド変数を使用して SOQL インジェクション攻撃を防止します。たとえば、\'%'+merchName+'%\' ではなく :queryName を使用します。詳細は、Salesforce ヘルプの「Apex 開発および Visualforce 開発のセキュリティのガイドライン」を参照してください。

メモ

次のコードスニペットに示すように、Apex コントローラには、取引先フィードの新しい注文に関するフィード項目を作成する insertQuickOrder メソッドもあります。フィード項目は、請求書にリンクするリンク投稿です。
1FeedItem post = new FeedItem();
2    post.ParentId = aId;
3    post.Body = UserInfo.getName() + ' just created a quick order';
4    post.type = 'LinkPost'; 
5    post.LinkUrl = '/' + li.Invoice__c;
6    post.Title = li.Merchandise__r.Name + ': ' + li.quantity__c;
7    insert post;

Visualforce ページ QuickOrderPage

このページは、ユーザ入力を使用してコントローラをコールし、商品および倉庫情報をユーザに表示します。ユーザが注文を作成する場合、このページはコントローラをコールし、カスタマー取引先に関連付けられている注文を作成して品目を追加します。また、コードはページの最初で Salesforce モバイル設計テンプレートを使用して、ページのスタイル設定も行います。

1<apex:page standardController="Account" 
2    extensions="QuickOrderController" docType="html-5.0"
3    standardStylesheets="false"     showheader="false" sidebar="false">
4     
5    <!--Include stylesheets for the mobile look and feel -->
6    <apex:stylesheet value="{!URLFOR(
7        $Resource.Mobile_Design_Templates, 
8        'Mobile-Design-Templates-master/
9            common/css/app.min.css')}"/>
10    <apex:includeScript value="{!URLFOR(
11        $Resource.Mobile_Design_Templates, 
12        'Mobile-Design-Templates-master/common/js/
13            jQuery2.0.2.min.js')}"/>
14    <apex:includeScript value="{!URLFOR(
15        $Resource.Mobile_Design_Templates, 
16        'Mobile-Design-Templates-master/common/js/
17            jquery.touchwipe.min.js')}"/>
18    <apex:includeScript value="{!URLFOR(
19        $Resource.Mobile_Design_Templates, 
20        'Mobile-Design-Templates-master/common/
21            js/main.min.js')}"/>
22      
23      <style>
24          /* Default S1 color styles */
25          .list-view-header, .data-capture-buttons a {
26                background: -webkit-linear-gradient(
27                    #2a93d5,#107abb);
28                background: linear-gradient(#2a93d5,#107abb);
29                box-shadow: 0 1px 3px rgba(0,0,0,.2),
30                    inset 0 1px 0 rgba(255,255,255,.21);
31                color: white;
32                font-weight: bold;
33          }
34          
35          #resultPage, #searchPage {
36              padding-bottom: 50px;
37          }
38      </style>

また、QuickOrderPage は、Force.com Canvas SDK をコールし、パブリッシャーの [登録] ボタンを有効にして、パブリッシャーウィンドウを閉じます。

まず、SDK への参照を指定します。
1<!-- This needs to be included so the publisher can be used 
2    to submit the action -->
3<script type='text/javascript' src='/canvas/sdk/js/publisher.js'></script>
次に、setValidForSubmit メソッドをコールして、パブリッシャーの [登録] ボタンを有効にします。
1//This method will activate the publish button 
2//so the form can be submitted 
3Sfdc.canvas.publisher.publish({
4    name: "publisher.setValidForSubmit", 
5    payload:"true"});
setValidForSubmit がコールされて、ユーザが [登録] をクリックすると、この subscribe メソッドが起動します。このメソッドは、JavaScript Remoting を使用して品目を挿入し (これにより、簡易注文が完了する)、フィード項目を取引先に投稿する最終的な JavaScript 関数を呼び出します。
1<script type='text/javascript'>  
2    Sfdc.canvas.publisher.subscribe({name: "publisher.post", 
3        onData:function(e) {
4    //This subscribe fires when the user hits 
5    //Submit in the publisher
6    insertQuickOrder();
7    }});
8</script>
最後に、Remoting メソッドからのコールバックが正常に返されたら、このメソッドはパブリッシャーウィンドウを閉じます。
1// Success - close the publisher and refresh the feed
2Sfdc.canvas.publisher.publish({name: "publisher.close", 
3    payload:{ refresh:"true"}});