Salesforce の開発メモ

メインは Web アプリケーションの開発ですが最近 Salesforce (以下 SF) の開発も関わるようになっていて、徐々に知見が溜まってきたので整理がてらメモしていきます。

## キャッチアップ

Trailhead とドキュメント。どちらも量が凄いので圧倒される。

### Trailhead

- SF を使っていく上での教材らしい。自習用の SF 環境も提供されて、自由に使える。
- 全部やる必要はなく、管理者向け、開発者向けなどコンテンツが別れている。
- 地味に時間を食う。概念や背景の説明だったりサンプルの事例を読ませたりしてくるので。
- 必要そうな単元をつまみ食いしてるけど通しでちゃんとやるのがよさそうな気はする。
- ぐぐって引っ掛かったページにアクセスした際、 Trailhead へのログインのセッションが切れているとトップページに戻されてしまうのが困る。

### 公式ドキュメント

- 読んでいくのは辛いので、必要な時に必要な場所だけ読む。
- でも表示に時間が掛かるので頻繁な利用はストレスになる。
- Trailhead に無い情報を探す時、 SF の機能の確認、 Class や Object の仕様を調べる時などに使ってる。

## 開発環境

### VSCode + SFDX

- `SFDX` という CLI と VSCode への `Salesforce Extension Pack` のインストールにより、 SF の sandbox 環境上にあるコードの取得とデプロイが楽になる。
  - [Salesforce CLI のインストール \| Salesforce DX 設定ガイド \| Salesforce Developers](https://developer.salesforce.com/docs/atlas.ja-jp.sfdx_setup.meta/sfdx_setup/sfdx_setup_install_cli.htm)
  - [Salesforce Extension Pack \- Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=salesforce.salesforcedx-vscode)
- npm scripts と組み合わせてビルド即デプロイも可能。
- まだちゃんと使いこなせていない。

## バックエンド (Apex)

### 言語

- Apex という言語で開発をする。古い Java っぽいらしい。 Java はよく知らないから今のところふんいきでやってる。
- 静的片付け言語なのでコードの読み書き自体は楽。
- 本番リリースをするためには、テストコードを作成して Apex コード全体に対しカバレッジが 75% を越えている必要がある。

### Apex 内での DML 実行とコールアウト

同一トランザクション中で DML 実行後にコールアウト (Web API の利用など) を行うことは出来ない。
コールアウトを行ってからの DML 操作は出来る。

(参考: [「You have uncommitted work pending\. Please commit or rollback before calling out」エラーについて](https://help.salesforce.com/articleView?id=000326129&language=ja&type=1&mode=1))

## SQL (SOQL)

### アスタリスクが無い

`SELECT * FROM Account` みたいなことが出来ないので、カラムは指定しなきゃいけない。
負荷対策で出来なくしていると考えれば腑に落ちる。
Apex でオブジェクトの項目名を取得する事も出来るみたいなので、同様の事をやりたければこれになるのかも。(参考: [Apex上でSelect \*を実装してみた \- Qiita](https://qiita.com/arufian/items/dd376e23dd3e568b9f1a))

### クエリへの Apex 変数の組み込み

そのまま

```java
String id = getAccountId();
Account acc = [SELECT Id, Name FROM Account WHERE Id = :id LIMIT 1];
```

文字列の連結

```java
String keyword = 'foo';
List<Account> accList = [SELECT Id, Name FROM Account WHERE Name LIKE :('%' + keyword + '%')];
```

## フロントエンド (Visualforce)

Visualforce (以下 VF) を使ったフロントエンドのマークアップ、開発について。

### 言語

- 言語というか、仕組み？組み込みコンポーネント？
- 何かを表示するための基本的な画面を作る際にこの仕組みを使う。
- HTMLと必要に応じて組み込みコンポーネントを組み合わせて記述する。

### VF の組み込みコンポーネント

VF には組み込みコンポーネントがあり、これを利用すると Salesforce の UI を楽に利用できる。
当然 VF で提供されている物に限るので、凝ったことをしたい場合は自前での開発が必要になる。

以下の例だと、 `table` タグによるレイアウト構築と、 `Account` オブジェクトの `Name` フィールドのラベルと値の入出力のための `input` タグが出力される。  

```html
<apex:page standardController="Account">
  <apex:pageBlock>
    <apex:pageBlockSection>
      <apex:pageBlockSectionItem>
        <apex:outputLabel value="{! Name }" />
        <apex:inputField value="{! Name }" />
      </apex:pageBlockSectionItem>
    </apex:pageBlockSection>
  </apex:pageBlock>
</apex:page>
```

どのコンポーネントがどんな HTML を吐き出すのかはドキュメントには書いてないため、実際に出力させてみる必要がある。

### レコード詳細ページのアクションボタンから、カスタムコントローラを使う VF ページに移りたい

遷移先の VF でカスタムコントローラは使えないけど、スタンダードコントローラを拡張してそれを使わせる事は出来る。

- Visualforce の `apex:page` で対象のオブジェクトの `standardController` を使用する。
- `standardController` で指定したコントローラを拡張する自前の Apex Class を `extension` 属性に指定する。

こんな感じになる。

```html
<apex:page standardController="Account" extensions="MyAccountExtension">
  ...
</apex:page>
```

standard controller の拡張については以下を参照。
[コントローラ拡張の作成 \| Visualforce 開発者ガイド \| Salesforce Developers](https://developer.salesforce.com/docs/atlas.ja-jp.pages.meta/pages/pages_controller_extension.htm)

### VF から Apex Controller のメソッドを実行した際に別の VF 画面に遷移したい

メソッドから `PageReferense` を返せばいい。

```java
class SomeController {
  // 'SomeVfPage' という名前の VF ページに遷移する
  public PageReference gotoNext() {
    return Page.SomeVfPage;
  }
}
```

### VF 間での値の引き継ぎ

それぞれの VF で同一のコントローラを利用すれば、特に何もしなくてもメンバ変数の値を引き継ぐことができる。
そうでない場合は `apex:form` でパラメータを渡し、受け取り側コントローラで次のようなコードでパラメータを取り出せる。

```html
<apex:page controller="FirstPageController">
  <apex:form>
    <input type="text" name="foo" value="bar" />
    <apex:commandButton value="送信" action="{! goToNextPage }" />
  </apex:form>
</apex:page>
```

```java
// 遷移先 VF のコントローラ
public with sharing class NextPageController {
  public NextPageController() {
    String fooValue = (String) ApexPages.currentPage().getParameters().get('foo'); // => "bar"
  }
}
```

(参考: [apex \- How to display the POST requested parameter in Visualforce page during load? \- Salesforce Stack Exchange](https://salesforce.stackexchange.com/questions/199361/how-to-display-the-post-requested-parameter-in-visualforce-page-during-load))


### VF ページで PDF をプレビューしたい

`<apex:iframe>` が使える。多分普通に `iframe` でも問題ない。
(参考: [Preview PDF file on visualforce page \- Salesforce Stack Exchange](https://salesforce.stackexchange.com/questions/179370/preview-pdf-file-on-visualforce-page))

### VF と開発 FW との組み合わせ

Vue.js の利用を想定。

#### VF は HTML ではない

VF には VF で許容されたマークアップしかリリースできない。  
例えば Vue.js の以下の構文を VF に書き込むと構文エラーとして扱われてしまう。

- `vue-on:click="myFunc"` (または `@click="myFunc"`)
- `vue-bind:foo="bar"` (または `:foo="bar"`)

Vue.js を使う場合は root の要素だけ空で作って、そこへコンポーネントをマウントする形になる。

#### ビルドファイル、バンドルファイルの利用方法

静的リソースにアップロードする。
複数ある場合は ZIP としてアップするといい感じ。開発資材やビルド元ファイルは含めず、出力されたファイルのみ含める。

わざわざ ZIP 形式に固める必要もなく、メタデータファイルと、メタデータに対応する名前のディレクトリを作って、そこに出力ファイルを放り込んでおけば SFDX がよしなにしてくれる。
開発ディレクトリは `node_modules` ディレクトリ等があるので SF にはアップしない方がよさそう。そのため開発資材は別ディレクトリで独自で管理する事になる。

以下は VF から参照する際のイメージ。

ディレクトリ構成:

```
~/
  └ force-app/
      └ main/
          └ default/
              └ staticresources/
                 ├ MyApp.resource-meta.xml
                 └ MyApp/
                     ├ app.css
                     └ app.js
```

適当な VF ページ:

```html
<apex:page>
  <div id="app"></div>
  <apex:stylesheet value="{! URLFOR($Resource.MyApp, 'app.css') }" />
  <apex:includeScript value="{! URLFOR($Resource.MyApp, 'app.js') }" loadOnReady="true"/>
</apex:page>
```

MyApp メタデータファイル:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<StaticResource xmlns="http://soap.sforce.com/2006/04/metadata">
    <cacheControl>Public</cacheControl>
    <contentType>application/zip</contentType>
    <description>ビルドしたファイルです</description>
</StaticResource>
```

開発資材の管理、共有はどうするのがベストなのかまだ分からない。
「スクラッチ組織」を理解すれば解決するのかもしれない。

#### VF 画面上での Vue.js アプリケーションのデバッグ

SF の lightning から呼び出された VF 画面で VueDevtools は動かない。 (classic から呼ばれた場合は試してない)
Vue.js での開発に十分慣れていないと大変かも。
そういうのもあるので、大規模な開発は避けた方がよさそうだと感じた。

#### ルーティング

- JS でルーティングの管理は試してないけど URL に `#` が入っていたりするので SF 側で何か管理してそう。
- なので、必要に迫られない限りルーティングは VF, Apex に任せるのが無難と考えてる。

#### JavaScript から Apex メソッドの呼び出し

JavaScript Remoting という仕組みがあって、Controller に `@RemoteAction` でメソッドを定義する事で API を生やせる。
Web API のように URL 指定で利用するのではなく、 `global` 変数に専用の呼び出し機構が定義されているのでそれを使っていく。

実装の際のイメージはこちらを見ると分かりやすい。
[JavaScript Remoting の例 \| Visualforce 開発者ガイド \| Salesforce Developers](https://developer.salesforce.com/docs/atlas.ja-jp.pages.meta/pages/pages_js_remoting_example.htm)

ただし、上記ページにある以下の方法は VF ページに直接 JS を書いていく場合にしか動作しない。

```javascript
Visualforce.remoting.Manager.invokeAction(
  '{! $RemoteAction.MyController.MyMethod }',
  'myArg',
  function(result, event) {/* ... */},
  {escape: true}
)
```

ビルドを伴う開発スタイルの場合は次のように呼び出す。

```typescript
window.MyController.MyMethod(
  'myArg', 
  function(result, event) {/* ... */},
  {escape: true}
)
```

戻り値はコールバック関数で受け取る事になるので、 `Promise` で返すラッパーを作るといい感じ。

JS 側の詳細な挙動や引数、戻り値については公式の情報が微妙に少ないので、呼び出し時に実行されるコードや実際のレスポンスを見るしかない。
JavaScript Remoting にはガバナ制限が無いので気楽に使えるけど、レスポンスサイズやタイムアウトに制限がある。（参考: [JavaScript Remoting の制限および考慮事項 \| Visualforce 開発者ガイド \| Salesforce Developers](https://developer.salesforce.com/docs/atlas.ja-jp.pages.meta/pages/pages_js_remoting_limits.htm)）

## その他

### `Attachment` オブジェクトに保存したファイルを URL で参照したい

```
/servlet/servlet.FileDownload?file=ATTACHMENT_ID
```

(参考: [\[salesforce\]メモ&添付ファイルに添付されたファイルのURLを取得する \- KayaMemo](http://kayakuguri.github.io/blog/2014/12/25/get-attachment-url/))

これも参考になりそう。
[カスタマーポータルサイトとForce\.comサイトから 添付ファイル\(Attachment\)を参照したい \- Salesforce Developer Community](https://developer.salesforce.com/forums/?id=906F00000009B6XIAU)