##はじめに
この記事ではRAP(ABAP RESTful Application Programming Model)のDetermination, Validationを実装します。
シリーズの先頭はこちら☞ 【RAP】ADTのRAP Generatorを使ってみる
用語の説明
用語 | 説明 | ヘルプ(さらに詳しく) |
---|---|---|
Determination | ビジネスオブジェクトの項目に値を代入するためのもの | link |
Validation | ビジネスオブジェクトの整合性をチェックするためのもの | link |
##シナリオ
前回の記事で生成したビジネスオブジェクトに、以下の機能を追加します。
- Determinationを使ってEmailを自動設定
- Validationを使って必須チェック
###1. Determinationを使ってEmailを自動設定
FirstName, LastNameに入力された値を使ってEmailを自動設定します。
####1.1. Behavior Definitionの設定
Behavior Definitionに以下のコードを追加します。
{
field ( readonly )
PersonUUID,
CreatedAt,
CreatedBy,
LastChangedAt,
LocalLastChangedAt,
LastChangedBy;
field ( numbering : managed )
PersonUUID;
determination set_email on save {create;} //追加
Determinationの構文
Determinationの構文は以下のようになります。
determination DetName {on modify { CUD1; CUD2; ... }
| { field Field1, Field2, ... ; }}
|{on save { CUD1; CUD2; ... }
| { field Field1, Field2, ... ; }}
Determinationが動くタイミングは2つあり、一つはmodify(エンティティが変更される都度)、もう一つはsave(保存時)です。定義する際はどちらかを選択します。
CUD1; CUD2; ...では起動条件となるオペレーション(create, update, delete)を指定し、field Field1, Field2, ...ではどの項目が変更された場合に起動するかを指定します。オペレーションと項目はどちらかの一方の指定でも、両方指定してもよいです。両方指定する場合は以下のような書き方になります。
determination set_email on save {create; field FirstName, LastName;}
今回はシンプルに、「登録時に、保存したタイミングで動かす」こととしました。
####1.2. Behavior Implementationの実装
追加したDeterminationの上でCtrl + 1(Quick Fix)を実行します。
すると、"Add method to determination ..."というオプションが出てくるのでそれを選択します。
以下のようにBehevior ImplementationクラスのLocal Typesにメソッドが作られます。
CLASS LCL_HANDLER DEFINITION INHERITING FROM CL_ABAP_BEHAVIOR_HANDLER.
PRIVATE SECTION.
METHODS:
GET_GLOBAL_AUTHORIZATIONS FOR GLOBAL AUTHORIZATION
IMPORTING
REQUEST requested_authorizations FOR Person
RESULT result,
set_email FOR DETERMINE ON SAVE
IMPORTING keys FOR Person~set_email.
ENDCLASS.
CLASS LCL_HANDLER IMPLEMENTATION.
METHOD GET_GLOBAL_AUTHORIZATIONS.
ENDMETHOD.
METHOD set_email.
ENDMETHOD.
ENDCLASS.
set_email
を以下のように実装します。
1. 処理対象インスタンスの取得
まず、メソッドのインポートパラメータkeysを使い、処理対象のインスタンスからFirstName、LastNameを取得します。keys、および結果のpersonsはテーブル型ですが、実際には1行だけ取得されます。
"Get firsName, lastName from instance
READ ENTITIES OF zi_person_m IN LOCAL MODE
ENTITY Person
FIELDS ( FirstName LastName )
WITH CORRESPONDING #( keys )
RESULT DATA(persons).
ドキュメント:READ ENTITY, ENTITIES
なお、IN LOCAL MODE
というのは、権限チェックなどの制約を回避して何でも取ってこられるようにするためのおまじないです。
ドキュメント:IN LOCAL MODE
以下がデバッグで取得したkeys、およびpersonsです。この中身は、ドラフトの使用有無やDeterminationが動くタイミングによっても変わってくるので、一度デバッグで確認しておくとよいと思います。
2. Emailの編集
FirstName、LastNameをつなげてEmailを編集します。
DATA(email) = |{ persons[ 1 ]-FirstName }_{ persons[ 1 ]-LastName }@com|.
email = to_lower( email ).
3. Emailを設定
最後にMODIFY ENTITIES
命令を使いインスタンスを更新します。
MODIFY ENTITIESから返ってくるテーブルupdate_reportedには、更新が失敗した場合にエラーメッセージが入ってきます。それを、set_emailのアウトプットであるreportedに設定します。reportedはメソッドの定義にも出てこないので、どこから来たの?という感じですね。
"Set email
MODIFY ENTITIES OF zi_person_m IN LOCAL MODE
ENTITY Person
UPDATE
FIELDS ( Email )
WITH VALUE #( FOR person IN persons (
%tky = person-%tky
* PersonUUID = person-PersonUUID
* %is_draft = person-%is_draft
email = email
) )
REPORTED DATA(update_reported).
reported = CORRESPONDING #( DEEP update_reported ).
ドキュメント:MODIFY ENTITY, ENTITIES
%tkyについて
WITH VALUE
の中で更新対象のインスタンスを指定する方法に2つあり、一つは%tkyにエンティティの%tkyをそのまま指定する方法、もう一つは上記でコメントアウトしている部分ですが、キーを直接指定する方法です。ドラフトがあるエンティティは%is_draftも含めてキーとなります。
%tkyはデバッグで見るとpersonsの構造の中にはないのですが、不思議とちゃんと存在していて使うことができます。
%tkyを使うとドラフトかどうかも含めすべてのキーを自動的に指定できるので、こちらの方法がお勧めです。
####1.3. 動作確認
First Name、Last Nameを入力して"Create"を押します。
Emailが設定されました。
###2. 1. Validationを使って必須チェック
Emailを設定するために必要なFirstName, LastNameを必須項目にします。
####2.1. Behavior Definitionの設定
Behavior Definitionに以下のコードを追加します。
field ( readonly )
PersonUUID,
CreatedAt,
CreatedBy,
LastChangedAt,
LocalLastChangedAt,
LastChangedBy,
Email; //追加
field ( numbering : managed )
PersonUUID;
field ( mandatory ) //追加
FirstName,
LastName;
determination set_email on save {create; }
validation mandatory_check on save {create; update; } //追加
項目制御
field ( readonly )
では、照会モードにする項目を指定します。EmailはDeterminationで設定するためreadonlyにしました。
field ( mandatory )
では、必須にしたい項目を指定します。この設定により画面に必須マークが出ますが、このままではブランクのまま保存できてしまいます。
※項目制御の追加後、ブラウザをリフレッシュしても反映されなかったのですが、Service Bindingから立ち上げ直したところ反映されました。
Validationの構文
Validationの構文はDeterminationと基本的な考え方は同じですが、動くのはsave
のタイミングだけです。
validation ValName on save { CUD1; CUD2; ... }
| { field Field1, Field2, ... ; }
####2.3. メッセージクラスの登録
エラーメッセージを出力するため、メッセージクラスを登録します。パッケージの上で右クリックし、New>Other ABAP Repository Objectを選択します。
Message Classを選択します。
登録が完了すると、以下の画面が表示されます。ここからメッセージを登録していきます。
今回は以下の1件を登録しました。
Number: 001
Short Text: Enter &1
####2.2. Behavior Implementationの実装
Quick Fixでメソッドmandatory_checkをBehavior Implementationに追加し、以下のように実装します。
1. 処理対象インスタンスの取得
Determinationのときと同様に、処理対象のインスタンスを取得します。
"Get Mandatory Fields
READ ENTITIES OF zi_person_m IN LOCAL MODE
ENTITY Person
FIELDS ( FirstName LastName )
WITH CORRESPONDING #( keys )
RESULT DATA(persons).
2. チェックを実行
取得したインスタンスをループして(実際には一行ですが)、チェックを実行します。
チェックでエラーの場合にやることは2つです。
①failedという構造にエラーのあったインスタンスのキーを設定する
②reportedという構造にエラーメッセージを格納する
"Do check
LOOP AT persons INTO DATA(person).
IF person-FirstName IS INITIAL.
"Set failed keys
APPEND VALUE #( %tky = person-%tky )
TO failed-person.
"Set message
APPEND VALUE #( %tky = person-%tky
%element-FirstName = if_abap_behv=>mk-on
%msg = new_message(
id = 'ZRAP_MSG_YASU2122_2'
number = 001
severity = if_abap_behv_message=>severity-error
v1 = 'First Name'
) )
TO reported-person.
ENDIF.
IF person-LastName IS INITIAL.
"省略
ENDIF.
ENDLOOP.
reported-personを返すときに、以下の設定をすることにより、UI上でエラーを出したい項目に印がつきます。
%element-FirstName = if_abap_behv=>mk-on
####2.4. 動作確認
First Name、Last Nameのいずれかを未入力のまま"Create"を押すと、エラーになります。
####2.5. わからなかったこと - State Messageについて
RAPで出すメッセージの種類は2つあり、一つがTransition Message、もう一つがState Messageです。
ヘルプによれば、Transition Messageとはビジネスオブジェクトの(中身の)状態によらず出るメッセージのことで、例えば「伝票が他のユーザにロックされています」といったメッセージが該当します。これに対しState Messageとはビジネスオブジェクトそのものの状態によって出るメッセージで、バリデーションエラーなどはこれに相当します。よって、今回のValidationではState Messageを出すべきと考えられます。
State Messageとするには、reportedにメッセージを渡すときに以下のように%state_areaを指定します。
APPEND VALUE #( %tky = person-%tky
%element-FirstName = if_abap_behv=>mk-on
%state_area = 'mandatory_check'
%msg = new_message(
id = 'ZRAP_MSG_YASU2122_2'
number = 001
severity = if_abap_behv_message=>severity-error
v1 = 'First Name'
) )
TO reported-person.
ところが、このように設定したところ、Validationで以下のエラーが発生しました。
一方で、こちらのチュートリアルでは、%state_areaは指定していませんでした。
APPEND VALUE #( %key = ls_travel_result-%key
%msg = new_message( id = /dmo/cx_flight_legacy=>begin_date_before_system_date-msgid
number = /dmo/cx_flight_legacy=>begin_date_before_system_date-msgno
severity = if_abap_behv_message=>severity-error )
%element-begin_date = if_abap_behv=>mk-on
%element-end_date = if_abap_behv=>mk-on ) TO reported-travel.
この件は現在SAP Communityに問い合わせ中です。