' P '

whatever I will forget

Aura コンポーネント サーバ側コントローラを使用してSalesforceと接続

trailhead.salesforce.com

背景

画像はTrailheadから拝借...

View-Controller-Controller-databaseモデル

  • Aura Componentのモデル図は下記のようになっていたことを再度認識する
    f:id:mankozooyork:20220210115911p:plain
    Aura Componentのモデル図

これまで作成していたもの

f:id:mankozooyork:20220210120031p:plain
1. Createボタンがクリックされれば、Controller(Javascript)がcallされる
2. Controller(Javascript)側からView(Component)の要素を取得
3. Controller(Javascript)側からView(Component)に値をセット

これから作成するもの: サーバー側 Controller(Apex)も使用する

f:id:mankozooyork:20220210120244p:plain
1. Createボタンがクリックされれば、Controller(Javascript)がcallされる
2. Controller(Javascript)側からView(Component)の要素を取得
3. Controller(Javascript)側からController(Apex)のCallBack関数の呼び出し
4. Controller(Javascript)側からController(Apex)関数の呼び出し
5. Controller(Apex)側で処理を実行
6. Controller(Apex)側でCallBack処理を実行し、View(Component)に値をセット
- 重要なポイント
手順5の処理を待たずにクライアント側を動かすために、非同期処理が行われる。
そのためのCallBack関数。サーバー側の処理が終わってからCallBack処理を実施することで、クライアント側からサーバーの処理が終わるまで待機する必要がなくなる。

Controller (Apex) の作法

public with sharing class ExpensesController {
    // STERN LECTURE ABOUT WHAT'S MISSING HERE COMING SOON
    @AuraEnabled
    public static List<Expense__c> getExpenses() {
        return [SELECT Id, Name, Amount__c, Client__c, Date__c,
                       Reimbursed__c, CreatedDate
                FROM Expense__c];
    }
    @AuraEnabled
    public static Expense__c saveExpense(Expense__c expense) {
        // Perform isUpdateable() checking first, then
        upsert expense;
        return expense;
    }
}
  • AuraコンポーネントからApex関数を呼び出す場合は@AuraEnabledを記述する
  • メソッドはstaticを記載し、publicまたはglobalで適用されるように定義する
  • セキュリティの観点から、public with sharing classでメソッドを定義する
    • これを使用することにより、このメソッドで使用できるレコードに組織の共有ルールが自動的に適用される

セキュリティ対策を最小限に行なったApex Method例

    @AuraEnabled
    public static List<Expense__c> getExpenses() {
        // Check to make sure all fields are accessible to this user
        String[] fieldsToCheck = new String[] {
            'Id', 'Name', 'Amount__c', 'Client__c', 'Date__c',
            'Reimbursed__c', 'CreatedDate'
        };
        Map<String,Schema.SObjectField> fieldDescribeTokens =
            Schema.SObjectType.Expense__c.fields.getMap();
        for(String field : fieldsToCheck) {
            if( ! fieldDescribeTokens.get(field).getDescribe().isAccessible()) {
                throw new System.NoAccessException();
            }
        }
        // OK, they're cool, let 'em through
        return [SELECT Id, Name, Amount__c, Client__c, Date__c,
                       Reimbursed__c, CreatedDate
                FROM Expense__c];
    }
  • 概要レベルで説明すると、ユーザが参照/変更対象のレコードにアクセス権があるかどうか確認する
  • getDescribe()メソッドはコストが高くなるため頻繁に使用するのはやめた方が良い

Aura Component側の作法

<aura:component controller="ExpensesController">
<aura:attribute name="foo"/>
...
<aura:handler name="init" action="{!c.doInit}" value="{!this}"/>
  • Apexクラスの名前を上記のように記載することで、Component/Apex間の関連を示す
  • アクションハンドラを指定してコンポーネントが読み込まれた場合にController(Javascript)をcallする

Controller(Javascript)側の作法

    // Load expenses from Salesforce
    doInit: function(component, event, helper) {
        // Create the action
        let action = component.get("c.getExpenses");
        // Add callback behavior for when response is received
        action.setCallback(this, function(response) {
            let state = response.getState();
            if (state === "SUCCESS") {
                component.set("v.expenses", response.getReturnValue());
            }
            else {
                console.log("Failed with state: " + state);
            }
        });
        // Send action off to be executed
        $A.enqueueAction(action);
    },
})
  • 関連付けされたApexのメソッド(@AuraEnabled)を呼び出すのは、下記
        // Create the action
        let action = component.get("c.getExpenses");
  • $A.enqueueAction(action);によってサーバー要求をキューに登録する。
    • コントローラーアクションはこの行で終了となる。
    • いつ戻ってくるのか、または戻ってくるのかは不明(え?)
  • component.set("v.expenses", response.getReturnValue());getReturnValue()で返却される型について
    • Apex側の定義: public static List<Expense__c> getExpenses() {
    • Component側の定義: <aura:attribute name="expenses" type="Expense__c[]"/>
    • 上記のように型が一致している場合は特に考慮不要。
({
    createExpense: function(component, expense) {
        let action = component.get("c.saveExpense");
        action.setParams({
            "expense": expense
        });
        action.setCallback(this, function(response){
            let state = response.getState();
            if (state === "SUCCESS") {
                let expenses = component.get("v.expenses");
                expenses.push(response.getReturnValue());
                component.set("v.expenses", expenses);
            }
        });
        $A.enqueueAction(action);
    },
})
  • action.setParamsが重要。パラメータ名: パラメータ値で定義する.
    • ここでのパラメータ名はApex側の引数名と合わせておく必要がある。
    • 参考 public static Expense__c saveExpense(Expense__c expense) {

値プロパイダについて

識別子 コンテキスト 意味
c. コンポーネントマークアップ クライアント側コントローラ
c. コントローラコード サーバ側コントローラ
c. マークアップ デフォルトの名前空間

Challenge

上記を理解していれば今回は難しくない.

github.com