LoginSignup
1
1

More than 1 year has passed since last update.

【RAP】Determination, Validationを追加する

Last updated at Posted at 2021-12-22

はじめに

この記事ではRAP(ABAP RESTful Application Programming Model)のDetermination, Validationを実装します。

シリーズの先頭はこちら☞ 【RAP】ADTのRAP Generatorを使ってみる

用語の説明

用語 説明 ヘルプ(さらに詳しく)
Determination ビジネスオブジェクトの項目に値を代入するためのもの link
Validation ビジネスオブジェクトの整合性をチェックするためのもの link

シナリオ

前回の記事で生成したビジネスオブジェクトに、以下の機能を追加します。

  1. Determinationを使ってEmailを自動設定
  2. Validationを使って必須チェック

今回設定するオブジェクトの関係は以下のようになっています。
image.png

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, ... ; }}

参考:CDS BDL - determinations

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)を実行します。
image.png
すると、"Add method to determination ..."というオプションが出てくるのでそれを選択します。
image.png
以下のように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が動くタイミングによっても変わってくるので、一度デバッグで確認しておくとよいと思います。
image.png
image.png

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を使うとドラフトかどうかも含めすべてのキーを自動的に指定できるので、こちらの方法がお勧めです。
image.png

1.3. 動作確認

First Name、Last Nameを入力して"Create"を押します。
image.png
Emailが設定されました。
image.png

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から立ち上げ直したところ反映されました。
image.png

Validationの構文
Validationの構文はDeterminationと基本的な考え方は同じですが、動くのはsaveのタイミングだけです。

validation ValName on save { CUD1; CUD2; ... }
                         | { field Field1, Field2, ... ; }

参考:CDS BDL - validations

2.3. メッセージクラスの登録

エラーメッセージを出力するため、メッセージクラスを登録します。パッケージの上で右クリックし、New>Other ABAP Repository Objectを選択します。
image.png
Message Classを選択します。
image.png
image.png
登録が完了すると、以下の画面が表示されます。ここからメッセージを登録していきます。
image.png
今回は以下の1件を登録しました。
Number: 001
Short Text: Enter &1
image.png

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"を押すと、エラーになります。
image.png

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で以下のエラーが発生しました。
image.png

一方で、こちらのチュートリアルでは、%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に問い合わせ中です。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1