【Apex+Visualforce】標準コントローラの拡張をやってみた。アクションの追加と上書き。

Salesforce

公式ドキュメントを読んでいて、コントローラ拡張っていうのは、標準コントローラーを拡張するのが主な用途なのかな?と思ったので、標準コントローラの拡張をやってみた。

【Apex+Visualforce】コントローラ拡張の用途。標準ボタン、標準リンクの上書き。

アクションの追加

まず前提として、標準コントローラーで提供されるアクションは下記です。
標準コントローラで提供されるアクション(公式サイト)

保存(save)や編集(edit)、削除(delete)などの標準的なアクションですね。
アクションが実行された後の挙動も決まっています。

ここで、標準コントローラーでは提供されていないやり直し(redo)というアクションを追加したい場合(やり直しボタンを押すと、画面で編集中の内容が取り消されて初期表示の状態に戻る)。
こんな感じで実装する。
まずはコントローラークラス。

/** 取引先の標準コントローラー拡張 */
public class AccountExtensionsController {

    /** 取引先 */
    public Account acct{get; set;}
    
    /**
     * コンストラクタ.
     * @param stdController 標準コントローラ
     */
    public AccountExtensionsController(ApexPages.StandardController stdController) {
        this.acct = (Account)stdController.getRecord();
    }
    
    /**
     * やり直しボタン押下時.
     * ※標準コントローラには無いactionの追加.
     */
    public void redo() {
        Account acct = [
            SELECT
            	 id, Name, Phone, Fax, Description
            FROM
            	Account
            WHERE
	            Id =: this.acct.Id
        ];
        this.acct.Name = acct.Name;
        this.acct.Phone = acct.Phone;
        this.acct.Fax = acct.Fax;
        this.acct.Description = acct.Description;
    }
}


続いてVisualforce画面。

<!-- 取引先の標準コントローラーを使用した、取引先入力画面 -->
<apex:page standardController="Account" extensions="AccountExtensionsController">
    <apex:form >
        <apex:pageBlock >
            <apex:pageBlockSection title="取引先情報">
                <apex:inputField value="{!Account.id}" />
                <apex:inputField value="{!Account.Name}" />
                <apex:inputField value="{!Account.Phone}" />
                <apex:inputField value="{!Account.Fax}" />
                <apex:inputField value="{!Account.Description}" />
            </apex:pageBlockSection>
            <apex:pageBlockButtons >
                <apex:commandButton value="保存" action="{!save}"/>
                <apex:commandButton value="やり直し" action="{!redo}"/>
            </apex:pageBlockButtons>
        </apex:pageBlock>
    </apex:form>
</apex:page>

やり直しボタンを押すと、こんな感じの挙動になる。

アクションの上書き

続いて、もともと標準コントローラーに備わっているアクションに機能を追加したい場合。
今回は、保存時に「説明(Account.Description)」が未入力だった場合、”(自動入力)”という文字列を自動で設定するという動きを追加することにする。

こんな感じね。


Javaとかで言うとオーバーライド的な発想ですけど、実装的にはオーバーライドとは違うので”上書き”という言葉を使いました。

コントローラークラスの実装例はこんな感じです。
redoアクションを追加した時のソースとの差分をハイライトしてます。

public class AccountExtensionsController {

    /** 取引先 */
    public Account acct{get; set;}
    
    /** 標準コントローラー */
    private ApexPages.StandardController stdController;
    
    /**
     * コンストラクタ.
     * @param stdController 標準コントローラ
     */
    public AccountExtensionsController(ApexPages.StandardController stdController) {
        this.stdController = stdController;
        this.acct = (Account)stdController.getRecord();
    }
    
    /**
     * やり直しボタン押下時.
     * ※標準コントローラには無いActionの追加.
     */
    public void redo() {
        Account acct = [
            SELECT
            	 id, Name, Phone, Fax, Description
            FROM
            	Account
            WHERE
	            Id =: this.acct.Id
        ];
        this.acct.Name = acct.Name;
        this.acct.Phone = acct.Phone;
        this.acct.Fax = acct.Fax;
        this.acct.Description = acct.Description;
    }
    
    /**
     * 保存ボタン押下時.
     * ※標準コントローラに存在するAction.
     */
    public PageReference save() {
        if (String.isEmpty(this.acct.Description)) {
            this.acct.Description = '(自動入力)';
        }
        return this.stdController.save();
    }
}

ポイントとしては45行目ですかね。
標準コントローラーでも提供されているsaveメソッドと同じ名前のメソッドを作ったので(41行目)、標準コントローラのsaveメソッドは実行されずに、上記拡張クラスのsaveメソッドが実行される。
でも今回やりたかったのは標準コントローラの保存処理の前に少し処理を追加したかっただけなので(42~44行目)、標準コントローラのsaveメソッドも処理したい。なので45行目のコードになっています。

補足

ちなみに標準コントローラのsaveメソッドは、保存時に画面遷移する仕様となっています。
保存した際に画面遷移したくないのであればquicksaveメソッドを使います。
標準コントローラで提供されるアクション(公式サイト)

しかーし!ApexPages.StandardControllerクラスにはquicksaveメソッドがない。
StandardControllerクラス(公式サイト)
作っておいてくれてもいいのに・・
とりあえず下記のように実装すればquicksaveと同じような(たぶん同じ)動きになります。

まずはコントローラ。

public class AccountExtensionsController {

    /** 取引先 */
    public Account acct{get; set;}
    
    /** 標準コントローラー */
    private ApexPages.StandardController stdController;
    
    /**
     * コンストラクタ.
     * @param stdController 標準コントローラ
     */
    public AccountExtensionsController(ApexPages.StandardController stdController) {
        this.stdController = stdController;
        this.acct = (Account)stdController.getRecord();
    }
    
    /**
     * やり直しボタン押下時.
     * ※標準コントローラには無いActionの追加.
     */
    public void redo() {
        Account acct = [
            SELECT
            	 id, Name, Phone, Fax, Description
            FROM
            	Account
            WHERE
	            Id =: this.acct.Id
        ];
        this.acct.Name = acct.Name;
        this.acct.Phone = acct.Phone;
        this.acct.Fax = acct.Fax;
        this.acct.Description = acct.Description;
    }
    
    /**
     * 保存ボタン押下時.
     * ※標準コントローラに存在するAction.
     */
    public void quicksave() {
        if (String.isEmpty(this.acct.Description)) {
            this.acct.Description = '(自動入力)';
        }
        this.stdController.save();
    }
}

次にVisualforce画面。

<apex:page standardController="Account" extensions="AccountExtensionsController">
    <apex:form >
        <apex:pageBlock >
            <apex:pageBlockSection title="取引先情報">
                <apex:inputField value="{!Account.id}" />
                <apex:inputField value="{!Account.Name}" />
                <apex:inputField value="{!Account.Phone}" />
                <apex:inputField value="{!Account.Fax}" />
                <apex:inputField value="{!Account.Description}" />
            </apex:pageBlockSection>
            <apex:pageBlockButtons >
                <apex:commandButton value="保存" action="{!quicksave}"/>
                <apex:commandButton value="やり直し" action="{!redo}"/>
            </apex:pageBlockButtons>
        </apex:pageBlock>
    </apex:form>
</apex:page>

コメント

タイトルとURLをコピーしました