Salesforce SOQL注意事項

-----------------------------
SOQL SOSL Injection
SOQL インジェクションの攻撃を回避するには、動的 SOQL クエリを使用しないようにします。代わりに、静的クエリとバインド変数を使用します。
動的 SOQL を使用する必要がある場合、escapeSingleQuotes メソッドを使用して、ユーザ指定の入力を削除します。
https://developer.salesforce.com/docs/atlas.ja-jp.pages.meta/pages/pages_security_tips_soql_injection.htm

-----------------------------
例１．お勧め
{code}
      String[] nameArray = new List<String>{'test1'};
      List<String> nameList = new List<String>{'test1'};
      Map<String, Account> nameMap = new Map<String, Account>();
      nameMap.put('test1', null);

      List<Account> accountList = [
        SELECT
          Name
        FROM Account
        WHERE Name IN :nameMap.keySet()
        or Name IN :nameArray
        or name in :nameList
      ];
{/code}

-----------------------------
例２．お勧め
{code}
        List<String> nameList = new List<String>();

        String appSqlCount = ' SELECT COUNT(Id) c FROM T_AttendanceApplication__c ';
        appSqlCount += ' WHERE Id IN :nameList ';

        LIST<AggregateResult> countList = Database.query(appSqlCount);

        return (Integer)countList[0].get('c');

{/code}

-----------------------------
例３．お勧めない
{code}
    String str1 = (this.type == CONST)
      ? 'testA'
      : '\'' + String.escapeSingleQuotes(this.type) + '\'';
    String queryStr =
      SQL_CONST_TEXT +
      'field1__c = \'value1\' and ' +
      'field2__c in ' +
      translateIdSet(new List<Id>(eids)) +
      ' and ' +
      'from__c < ' +
      this.to_date.format(DATETIME_FORMAT) +
      ' and ' +
      'to__c >= ' +
      this.from_date.format(DATETIME_FORMAT) +
      ' and ' +
      'field3__c in (' +
      str1 +
      ')' +
      ' and field4__c = false ' +
      ORDER_TEXT;
    List<CustomObject__c> objList = Database.query(queryStr);
{/code}


-----------------------------
** SELECT 処理はWITH SECURITY_ENFORCEDを付与する

** FLS Create　(Field Level Security 項目レベルセキュリティ 数据库字段级权限安全）
- 単一レコード：
{code}
 insert (【挿入対象】= (【挿入対象のクラスタイプ】) Security.stripInaccessible(AccessType.CREATABLE, new List<sObject>{ 【挿入対象】}).getRecords().get(0));
{/code}
- 複数レコード：
{code}
 insert (【挿入対象リスト】=  (【挿入対象のクラスタイプ】) Security.stripInaccessible(AccessType.CREATABLE, 【挿入対象リスト】).getRecords());
{/code}
- エラー中断しない： 
{code}
Database.insert(【挿入対象リスト】=  (【挿入対象のクラスタイプ】) Security.stripInaccessible(AccessType.CREATABLE, 【挿入対象リスト】).getRecords(), false);
{/code}
- ※対象作成や値設定する時、isCreateable()で権限チェックしないでも、「Force.com Code Scanner」をパスできる



-----------------------------
** FLS Update
- 単一レコード：
{code}
update Security.stripInaccessible(AccessType.UPDATABLE, new List<sObject>{ 【更新対象】}).getRecords();
{/code}
- 複数レコード：
{code}
update Security.stripInaccessible(AccessType.UPDATABLE, 【更新対象リスト】).getRecords();
{/code}
- エラー中断しない：
{code}
Database.update(Security.stripInaccessible(AccessType.UPDATABLE, 【更新対象リスト】).getRecords(), false);
{/code}
- ※値設定する時、isUpdateable()で権限チェックしないでも、「Force.com Code Scanner」をパスできる

** CRUD Delete
- 検索正規表現式：([dD]{1}[eE]{1}[lL]{1}[eE]{1}[tT]{1}[eE]{1}[ (\n]+)
- 単一レコード：
{code}
if (【削除対象】.getSObjectType().getDescribe().isDeletable()) {
  delete 【削除対象】;
}
{/code}
- 複数レコード：
{code}
if (【削除対象リスト】[0].getSObjectType().getDescribe().isDeletable()) {
  delete 【削除対象リスト】;
}
{/code}


-----------------------------
** コート品質
| | Queries With No Where Or Limit Clause | 避免无条件和限制的查询 |
| | SOSL SOQL Statments Inside Loops | 在FOR循环中避免SOQL查询 |
| | Hardcoding Ids | 避免对ID进行硬编码 |

Apex Design Best Practices
https://developer.salesforce.com/ja/wiki/apex_code_best_practices

【Salesforce】 Apex代码的最佳实践
https://zhuanlan.zhihu.com/p/361439549
https://zhuanlan.zhihu.com/p/361723054