SalesforceのVisualforceでカスタムオブジェクトの「コピー」画面を作る

少し複雑な構造や入力規則を持っているカスタムオブジェクトを利用している場合には、
登録、編集画面をVisualforceで作成する場合があります。

私の参加するプロジェクトでもそのようなことをしていたのですが、既にあるレコード
を複製して新しいレコードを作る場合に、少し問題が起こったので、調査したことと、
解決策をメモしておきます。

問題の原因は、カスタムオブジェクトの設定「標準ボタンと標準リンク」のところに、
新規(New)と編集(Edit)にはVisualforce ページが設定してあったのに、コピー(Clone)
のところには標準 Salesforce.com ページが設定してあったために、オブジェクトの
コピーがきちんと行われていなかったためでした。

Cloneが中途半端に行われていたため、空欄の項目ができてしまったいたようです。

そのため、VisualforceとApexコードでコピー機能を実装することに。。

オブジェクトの複製(クローン)について

Salesforceでは、以下のようなコードでオブジェクト、カスタムオブジェクトの複製が可能です。

Account originAccount = [SELECT id, name FROM Account LIMIT 1];
Account cloneAccount = new Account();
cloneAccount = originAccount.clone(false,true);

clone(false,true)

1つ目の引数は、Salesforceのオブジェクトidをコピーするか/しないかを指定します。
・trueの場合・・・idをコピーします。
・falseの場合・・・idをコピーせず、それ以外の項目をコピーします。

2つめの引数は、新しいオブジェクトとしてクローン作成するかどうかを指定します。
・trueの場合・・・作成元のオブジェクトの参照は引き継がず、値だけをコピーして新しいオブジェクトを作成します。
・falseの場合・・・作成元のオブジェクトの参照を引き継いで新しいオブジェクトを作成します。

今回は、既存のレコードを元に「複製」するので、idはコピーしないよう、1つ目の引数はfalseとしました。
trueとしてしまうと、元のidのオブジェクトに変更を加えることになりますので、「更新」になってしまいます。

また、作成元のオブジェクトの参照を引き継いで新しいオブジェクトを作成した場合、
新しいオブジェクトの値を変更すると作成元のオブジェクトの値も変更されてしまいますので、2つめの引数はtrueとしました。

呼ばれたボタンが、編集なのか、コピーなのかを判断する。

今回、編集時とコピー時は同じVisualforceの画面を利用しようと思います。

そのためには、画面がどのボタンを押して呼ばれたのかを判断しなければいけませんね。
そこで、以下のログを仕込んで、呼ばれた時のパラメータの違いを見てみました。

for (string key: ApexPages.currentPage().getParameters().keySet()){
    if (ApexPages.currentPage().getParameters().get(key) != null){
        system.debug(Logginglevel.INFO,'■' + key + ' = ' + ApexPages.currentPage().getParameters().get(key));
    }
}

すると、

新規の場合
14:43:32.057 (57825000)|USER_DEBUG|[30]|INFO|■retURL = /aXX/o
14:43:32.057 (57927000)|USER_DEBUG|[30]|INFO|■save_new = 1
14:43:32.058 (58008000)|USER_DEBUG|[30]|INFO|■sfdc.override = 1
 
編集の場合
14:35:25.114 (114702000)|USER_DEBUG|[30]|INFO|■id = aXXX0000000PXXX
14:35:25.114 (114812000)|USER_DEBUG|[30]|INFO|■retURL = /aXXX0000000PXXX
14:35:25.114 (114897000)|USER_DEBUG|[30]|INFO|■scontrolCaching = 1
14:35:25.114 (114986000)|USER_DEBUG|[30]|INFO|■sfdc.override = 1
 
コピーの場合
14:35:30.093 (93392000)|USER_DEBUG|[30]|INFO|■clone = 1
14:35:30.093 (93496000)|USER_DEBUG|[30]|INFO|■id = aXXX0000000PXXX
14:35:30.093 (93583000)|USER_DEBUG|[30]|INFO|■retURL = /aXXX0000000PXXX
14:35:30.093 (93665000)|USER_DEBUG|[30]|INFO|■scontrolCaching = 1
14:35:30.093 (93744000)|USER_DEBUG|[30]|INFO|■sfdc.override = 1

のようなログが出力されます。

コピーの場合は「clone = 1」というパラメータが渡っているようですね。

そのため、以下のように実装しました。

if(Apexpages.currentPage().getParameters().get('clone')=='1'){
    system.debug(Logginglevel.INFO,'★コピー');
    SO = cloneObj;
 
}else{
    system.debug(Logginglevel.INFO,'★コピー以外(新規または編集)');
    SO = originalObj;
}

こうすると、編集の時には、idを引き継いでいるため更新となり、コピーの時には、idが新しく採番されるので、複製となります。