Salesforce カスタムコントローラと拡張コントローラの違いと使い方

# これは何？
この記事は「[24日後に立派なSalesforceエンジニアになるWEBエンジニア Advent Calendar 2022](https://qiita.com/advent-calendar/2022/kakakaori)」の23日目の記事です2日後に立派なSalesforceエンジニアになるために今日は昨日に引き続きVisualforceのコントローラの種類ということでカスタムコントローラとコントローラ拡張についてみていこうと思います。

# カスタムコントローラとコントローラ拡張
## どんな時に使うか
標準コントローラでは各オブジェクトに対してSalesforceが事前に用意しているアクションを指定することができたが、それ以外の処理をしたい場合はカスタムコントローラや拡張コントローラを使用する。

## 実行モードの違い
カスタムコントローラを使うと**全てシステムモードで実行**される。そのためカスタムコントローラを指定した場合は、現在のユーザの権限と項目レベルのセキュリティが適用されない。
一方拡張コントローラで標準コントローラを拡張した場合、**ユーザモードで実行**され、現在のユーザの権限、項目レベルのセキュリティ、および共有ルールが適用される。

## どちらを使うべきか
カスタムコントローラとコントローラ拡張どちらを使うべきかは上述した実行モードの違いで判断するのが良い。
また拡張コントローラはその名の通り標準/カスタムコントローラの”拡張”なので、新しいアクションを追加したい場合や、標準コントローラのアクションを1つ上書きしたい時などには拡張コントローラを選択すると良いだろう。

# カスタムコントローラ
カスタムコントローラの実態はApexクラスである。作成したカスタムコントローラはVisualforce内から以下のようにして呼び出すことができる。
```html:
<apex:page controller="MyCustomController">
</apex:page>
```
## サンプル
[カスタムコントローラの作成](https://developer.salesforce.com/docs/atlas.ja-jp.pages.meta/pages/pages_controller_custom.htm)を参考にサンプルコードを見ていく。

```apex:MyCustomController.cls
public with sharing class MyCustomController {
    private final Account account;

    public MyCustomController() {
        account = [SELECT Id, Name, Site, isPersonAccount FROM Account 
                   WHERE Id = :ApexPages.currentPage().getParameters().get('id')];
    }

    public Account getAccount() {
        return account;
    }

    public PageReference save() {
        update account;
        return null;
    }
}
```

```html:test.page
<apex:page controller="myCustomController" tabStyle="Account">
    <apex:form>
        <apex:pageBlock title="Congratulations {!$User.FirstName}">
            You belong to Account Name: <apex:inputField value="{!account.name}"/>
            <apex:commandButton action="{!save}" value="save"/>
        </apex:pageBlock>
    </apex:form>
</apex:page>
```
実行結果は以下のようになる。
![スクリーンショット 2022-12-19 22.06.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/200812/4545bcda-0ce8-984f-7c53-44fc121fb932.png)
Apexクラスでカスタムコントローラである`MyCustomController`を作成してVisualforce側から`<apex:page controller="myCustomController">`で呼び出している。
コントローラ内ではURLからgetで渡されたidパラメータを受け取ってそれを元にAccountレコードを取得。
コントローラ内で取得したレコードは getter メソッドを通じてVisualforce側に渡される。今回の例で言うとVisualforceで`{!account.name}`と書くことでコントローラの`getAccount()`メソッドが呼び出されて、取得した`account`の`name`項目が表示される。

# コントローラ拡張
メインコントローラに機能を追加するときに使う。例えば標準コントローラに変数やメソッドを少しだけ追加したいときに、コントローラ拡張を使ってやれば、カスタムコントローラほどしっかりと処理を作り込まなくても、意図した変数やメソッドやだけを追加すると言うことができる。
またメインコントローラは1つのページにつき1つしか定義できないのに対して、コントローラ拡張は複数定義することができる。
コントローラ拡張の定義は以下のように記述する。
```html:
<apex:page standardController="MainController" 
           extensions="myControllerExtension1,myControllerExtension2">
</apex:page>
```
## サンプル
カスタムコントローラ同様にコントローラ拡張についてもサンプルを見ていく。

```apex:MyControllerExtension.cls
public class MyControllerExtension {

    private final Account acct;

    // 初期化の処理で、標準コントローラからgetRecordを使って取引先の情報を取得しておく
    public MyControllerExtension(ApexPages.StandardController stdController) {
        this.acct = (Account)stdController.getRecord();
    }

    public String getGreeting() {
        return 'Hello ' + acct.name + ' (' + acct.id + ')';
    }
}
```

```html:test.page
<apex:page standardController="Account" extensions="myControllerExtension">
    {!greeting} <p/>
    <apex:form>
        <apex:inputField value="{!account.name}"/> <p/>
        <apex:commandButton value="Save" action="{!save}"/>
    </apex:form>
</apex:page>
```
実行結果は以下のようになる。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/200812/23d4de7b-8baa-0793-fe74-65676a73f87c.png" width="250">

Apexクラスで拡張コントローラとなる`MyControllerExtension`を作成してVisualforce側から標準取引先コントローラと合わせて`<apex:page standardController="Account" extensions="myControllerExtension">`のような形でコントローラの拡張を行なっている。
コントローラからVisualforceへの値の渡し方は今までと同じで getter を用意してVisualforce側で`{!greeting}`のように書くとコントローラの`getGreeting()`メソッドが呼び出される。

## サンプル（拡張コントローラを複数定義）
メインコントローラは1つしか指定できないが、拡張コントローラは複数指定することができるので、その時の挙動についてみていく。

```apex:ExtOne.cls
public class ExtOne {
    public ExtOne(ApexPages.StandardController acon) { }

    public String getFoo() {
        return 'foo-One';
    }
}
```

```apex:ExtTwo.cls
public class ExtTwo {
    public ExtTwo(ApexPages.StandardController acon) { }

    public String getFoo() {
        return 'foo-Two';
    }
    public String getBar() {
        return 'bar-Two';
    }
}
```

```html:test.page
<apex:page standardController="Account" 
    extensions="ExtOne,ExtTwo" showHeader="false">
    <apex:outputText value="{!foo}" />
    <apex:outputText value="{!bar}" />
</apex:page>
```
これを実行すると以下のような画面になる。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/200812/3d2290cb-4d3b-8fc8-3c9f-aa2e341560d7.png" width="200"> 
`{!foo}`を表示するための`getFoo`はExtOne,ExtTwoどちらの拡張にもメソッドが書かれているが、その場合は`extensions="ExtOne,ExtTwo"`のリストで左（はじめ）に定義したもので上書きされることがわかる。
また`{!bar}`を表示する`getBar`はExtTwoにしかないが、この場合はそのメソッドがそのまま呼び出される。

# 最後に
Visualforceを触り始めた時は標準コントローラが便利で基本的にはそれを使おうと思っていたものの、やはり実際の運用をしていくとそれでは実装しきれないものも出てくるので、そういった時にはカスタムコントローラやコントローラ拡張を使えるようになっていると良さそうです。
明日は24日クリスマスイブということでクリスマス感溢れるSalesforce組織を作っていこうと思います。