【Salesforce】テンプレート集！

# トリガのテンプレート

```AccountTrigger.trigger
/**************************************************
 * @名前 AccountTrigger
 * @概要 取引先のトリガ
 * @場所 VFだったら書いておいた方が混乱しない
 * @履歴 YYYY/MM/DD XXXX保守 r.kobayashi ○○のため、××した
 * @履歴 YYYY/MM/DD XXXX保守 r.kobayashi ○○のため、××した
 **************************************************/
trigger AccountTrigger on Account(after delete, after insert, after undelete, after update, before delete, before insert, before update) {
    AccountTriggerHandler handler = new AccountTriggerHandler();

    if(Trigger.isInsert && Trigger.isBefore){
        handler.onBeforeInsert(Trigger.new);
    }else if(Trigger.isInsert && Trigger.isAfter){
        handler.onAfterInsert(Trigger.new);
    }else if(Trigger.isUpdate && Trigger.isBefore){
        handler.onBeforeUpdate(Trigger.new, Trigger.newMap, Trigger.old, Trigger.oldMap);
    }else if(Trigger.isUpdate && Trigger.isAfter){
        handler.onAfterUpdate(Trigger.new, Trigger.newMap, Trigger.old, Trigger.oldMap);
    }else if(Trigger.isDelete && Trigger.isBefore){
        handler.onBeforeDelete(Trigger.old, Trigger.oldMap);
    }else if(Trigger.isDelete && Trigger.isAfter){
        handler.onAfterDelete(Trigger.old, Trigger.oldMap);
    }else if(Trigger.isUnDelete){
        handler.onUnDelete(Trigger.new, Trigger.newMap);
    }
}
```

# ハンドラクラスのテンプレート
```AccountTriggerHandler.cls
/**
 * @description 概要
 * @author 会社名
 * @author 作成者
 * @readme メモ
 */
public class AccountTriggerHandler{
    /* hogehoge関連 START */
    public String id {get; set;} // 画面ID
    /* hogehoge関連 END */

    public void onBeforeInsert(Account[] newList){
        this.hogehoge(newList);
    }

    public void onAfterInsert(Account[] newList){
        try {
            // テストオブジェクトを更新する
            List<Test__c> testList = this.hogehoge(newList);
            if(!testList.isEmpty()){
                update testList;
            }
        } catch (Exception e) {
            Util.addErrorMsg(newList, e.getMessage());
        }
    }

    public void onBeforeUpdate(Account[] newList, Map<Id, Account> newMap, Account[] oldList, Map<Id, Account> oldMap){
    }

    public void onAfterUpdate(Account[] newList, Map<Id, Account> newMap, Account[] oldList, Map<Id, Account> oldMap){
    }

    public void onBeforeDelete(Account[] oldList, Map<Id, Account> oldMap){
    }

    public void onAfterDelete(Account[] oldList, Map<Id, Account> oldMap){
    }

    public void onUnDelete(Account[] newList, Map<Id, Account> newMap){
    }

    /**
     * @概要 取引先のトリガ
     * @引数 newMap 新しいバージョンの sObject レコードへの ID の対応付け
     * @引数 oldMap 古いバージョンの sObject レコードへの ID の対応付け
     * @戻り値 無し
     * @履歴 YYYY/MM/DD XXXX保守 r.kobayashi ○○のため、××した
     */
    private void hogehoge(Map<Id, Account> newMap, Map<Id, Account> oldMap){
        this.str = 'test';

        // ～～のため、～～を取得する
        List<Account> targetAccList = [
            SELECT
                Id
                ,Name
                ,hoge__c
            FROM Account
            WHERE
                Id = :this.accId
                AND Name != NULL
            ORDER BY Name DESC
            LIMIT 1000
        ];

        // ～～のため、～～を取得する
        Map<Id, Account> targetAccMap = new Map<Id, Account>([
            SELECT
                Id
                ,Name
                ,hoge__c
            FROM Account
            WHERE
                Id = :this.accId
                AND Name != NULL
            ORDER BY Name DESC
            LIMIT 1000
        ]);
    }
}
```

# ハンドラクラスのテストクラスのテンプレート
```TestAccountTriggerHandler.cls
/**
 * @description 概要
 * @author 会社名
 * @author 作成者
 * @readme メモ
 */
private class TestAccountTriggerHandler{

    /**************************************************
     * 概要 コンストラクタ
     * 引数 test なし
     * 戻り値 なし
     * 履歴 YYYY/MM/DD XXXX保守 r.kobayashi ○○のため、××した
     **************************************************/
    @isTest static void testHogehoge() {
    }
}
```

プロジェクトによってクラス名の[Test]が前だったり(TestAccountTriggerHandler)後ろ(AccountTriggerHandlerTest)だったりする！
前の場合は一覧で見た際にTestクラスがまとまっている、後ろの場合はテスト対象のクラスのすぐ下に表示されているから個人的に後ろのほうが修正しやすい(Ctrl + Pで呼び出せばいい話だけど)

メソッド名も[test対象の処理名]だったり、[test0001]だったりする！
個人的には[test対象の処理名]の方が保守がしやすいと思うなあ。

# バッチクラスのテンプレート
```Test_Batch.cls
/**
 * @description 概要
 * @author 会社名
 * @author 作成者
 * @readme メモ
 */
global class Test_Batch implements Database.Batchable<sObject>, Database.Stateful {

    private String query;
    public List<String> testList = new List<String>(); // executeメソッドで並列処理したデータを受け取ってfinishでなんかする

    // コンストラクタ
    global Test_Batch () {

        // memo Utilityクラスで項目全部取得のメソッドを作ってもいいかも
        this.query =
            ' SELECT ' +
            '     Id ' + // ID
            '     ,Name ' + // 取引先名
            ' FROM ' +
            '     Account' + // 取引先
            ' WHERE' +
            '     Name != null' + // 取引先名がnullではない
            '     AND CreatedDate = LAST_MONTH'; // 作成日が先月

        // テスト用
        if (Test.isRunningTest()) {
            // レコード数が膨大な場合、下記の処理が必要
            this.query += ' LIMIT 1';
        }
    }

    global Database.QueryLocator start(Database.BatchableContext BC){
        return Database.getQueryLocator(this.query);
    }

    global void execute(Database.BatchableContext BC, List<sObject> scope){
        
        // memo スケジューラクラスで渡されたバッチサイズで並列処理が行われる
        for(Account acc : (List<Account>) scope){
            testList .add(acc.Name);
        }
    }

    global void finish(Database.BatchableContext BC){
        for(String str : testList){
            // 先月に作成した取引先の名前をデバッグログに表示する
            System.Debug('★★ 変数 str ： ' + str);
        }

        // 他のバッチを連続で実行したい場合
        Database.executeBatch(new Test_Batch2(), BATCH_SIZE);
    }
}
```

# バッチテストクラスのテンプレート
```Test_BatchTest.cls
/**
 * @description 概要
 * @author 会社名
 * @author 作成者
 * @readme メモ
 */
@isTest
private class Test_BatchTest {

    static final Integer BATCH_SIZE = 200; // バッチサイズ

    static testMethod void Test_BatchTest01() {

        // テスト開始
        Test.startTest();

        Database.executeBatch(
            new Test_Batch(), BATCH_SIZE);

        // テスト終了
        Test.stopTest();

    }
}
```

# スケジューラクラスのテンプレート
```TestBatchScheduler.cls
/**
 * @description 概要
 * @author 会社名
 * @author 作成者
 * @readme メモ
 */
global class TestBatchScheduler implements Schedulable {

    // バッチサイズ
    global static final Integer BATCH_SIZE = 200;

    // コンストラクタ
    global TestBatchScheduler(){

    }

    // 実行メソッド
    global void execute(SchedulableContext sc) {
        // テスト用バッチを実行
        Database.executeBatch(new Test_Batch(), BATCH_SIZE);
    }
}
```

# スケジューラテストクラスのテンプレート
```TestBatchSchedulerTest.cls
@isTest
private class TestBatchSchedulerTest {

    static testMethod void TestBatchSchedulerTest01() {

        // テスト開始
        Test.startTest();

        // スケジュール起動
        String jobId = System.schedule(
            'TestBatchSchedulerTest',
            '0 0 * * * ?',
            new TestBatchScheduler()
        );

        // テスト終了
        Test.stopTest();
    }
}
```

# デバッグのテンプレート
```debug.txt
System.Debug('★★★★ 関数を開始する ： hogeMethod');
System.Debug('★★★★ 関数を終了する ： hogeMethod');

System.Debug('★★ コメント hoge');
System.Debug('★★ 変数 hoge ： ' + hoge);

System.Debug('★★ if文の条件 hoge ： ' + hoge);
System.Debug('★★ if文の結果 ： 処理対象');

System.Debug('★★ for文に渡すリスト hogeList ： ' + hogeList );
System.Debug('★★ for文に渡すリストのサイズ hogeList ： ' + hogeList.size());
System.Debug('★★ for文の現在値 ： ' + );

console.log("★★★★ 関数を開始する ： hogeMethod");
console.log("★★★★ 関数を終了する ： hogeMethod");

console.log("★★ コメント hoge");
console.log("★★ 変数 hoge ： ", hoge);

console.log("★★ if文の条件 hoge ： ", hoge);
console.log("★★ if文の結果 ： 処理対象");

console.log("★★ for文に渡すリスト hogeList ： ", hogeList);
console.log("★★ for文に渡すリストのサイズ hogeList.size() ： ", hogeList.size());
console.log("★★ for文の現在値 ： ", hoge);

// TODO: 後でやることを記載する
// README: descriptionに書くほどでもないけど、書いときたいこと
```

# Utilクラスのテンプレート(共通処理)
```Util.cls
public class Util{

    /**
     * @description エラー出力
     * @param sObjList エラーが出たリスト
     * @param msg エラーメッセージ
     * @return 無し
     */
    public static void addErrorMsg(List<sObject> sObjList, String msg) {
        for (sObject sObj : sObjList) {
            sObj.addError(msg);
        }
    }
}
```

~~何でもかんでもUtilクラスに頼っちゃうとわけわかんねえUtilクラスが出来上がっちゃうから、あんま作らないほうがいいみたいね・・・~~

# Constクラスのテンプレート(定数)
```Const.cls
public class Const{
    public final static String CRLF = '\r\n';
}
```

Salesforceで定数を使用する場合はカスタム表示ラベルを使用するほうがApexを修正せずに済みます。