RAP - Validierung
Schauen wir uns einmal die Validierungen beim RAP Business Objekt an und wie sie im Framework umgesetzt werden.
Inhaltsverzeichnis
Validierungen sorgen im Business Objekt dafür, dass die Daten zu jeder Zeit konsistent sind und zwar zentral über alle Aktionen die auf dem Objekt abgesetzt werden. In diesem Artikel schauen wir uns an, wie man solche Validierungen anlegt und wie diese genau funktionieren.
Allgemein
Validierungen gehören zur Verhaltensdefinition eines Business Objekts und werden an der entsprechenden Entität definiert, wo sie auch gelten sollen. Sie werden beim Speichern der Daten ausgelöst und können für die einzelnen Events, wie Create, Update und Delete registriert werden. Weiterhin besteht auch die Möglichkeit das nur bei der Änderung eines einzelnen Feldes eine Validierung ausgelöst wird.
Allgemeine Validierung
Die allgemeine Validierung bezieht sich auf Validierungen die beim Speichern auf gewisse Events (Create, Update, Delete) immer ausgelöst werden, dazu das folgende Beispiel.
Anlegen
Definieren wir dazu die erste Validierung in einem Beispiel. Dazu beginnen wir mit der Erweiterung der Verhaltensdefinition:
validation validateKeyIsFilled on save { create; }
Eine Validierung wird mit dem Schlüsselwort "validation" angelegt, danach folgt der Name der Validierung und das Event bei dem es ausgelöst wird. In fast allen Fällen ist das "on save", also beim Speichern der Daten in der Speichersequenz. In der geschweiften Klammer geben wir nun mit, dass das Event nur beim Anlegen eines Datensatz auftritt. Nach der Aktivierung der Verhaltensdefinition bekommen wir die folgende Warnmeldung in Eclipse:
Es fehlt noch die Implementierung der Methode in der Verhaltensimplementierung (Klasse). Diese kannst du ganz leicht anlegen, indem du den Cursor auf den Namen der Validierung stellst und mit STRG + 1 im Quick Assist die Methode anlegst. Du landest zum Abschluss automatisch in der Klasse und der angelegten Methode.
CLASS lhc_Partner DEFINITION INHERITING FROM cl_abap_behavior_handler.
PRIVATE SECTION.
METHODS validatekeyisfilled FOR VALIDATE ON SAVE
IMPORTING keys FOR partner~validatekeyisfilled.
ENDCLASS.
CLASS lhc_Partner IMPLEMENTATION.
METHOD validateKeyIsFilled.
ENDMETHOD.
ENDCLASS.
Implementieren
Bevor wir mit der Implementierung der Methode beginnen, wollen wir uns zunächst einmal die Schnittstelle etwas genauer ansehen. Wie du oben in der Definition siehst, wurde ein Parameter in der Methodenschnittstelle definiert, prüfen wir dies nun in der Signatur:
Neben den Keys, stehen dir auch noch "failed" und "reported" zur Verfügung, die indirekt zur Methode dazu gehören.
- KEYS - Übergabe der zu prüfenden Schlüssel an die Methode.
- FAILED - Rückgabe der Schlüssel die fehlerhaft sind.
- REPORTED - Rückgabe der fehlerhaften Schlüssel und Meldungen die den Fehler beschreiben.
Implementieren wir nun die Prüfung in der Methode. Da es sich hier um eine einfache Pflichtfeldprüfung handelt, ist die Logik entsprechend einfach und innerhalb des Loops erzeugen wir die Fehlermeldung:
LOOP AT keys INTO DATA(ls_key) WHERE PartnerNumber IS INITIAL.
INSERT VALUE #( PartnerNumber = ls_key-PartnerNumber ) INTO TABLE failed-partner.
INSERT VALUE #(
PartnerNumber = ls_key-PartnerNumber
%msg = new_message_with_text( severity = if_abap_behv_message=>severity-error text = 'PartnerNumber is mandatory' )
) INTO TABLE reported-partner.
ENDLOOP.
Für die Befüllung der Meldung gibt es zwei Hilfsmethoden, die durch die Vererbung zur Verfügung stehen. Mit "new_message" können wir eine T100 Nachricht erzeugen und mit "new_message_with_text" reicht ein einfacher Text, um eine Meldung auszugeben:
Die Ausgabe der Meldung kann dann direkt im UI erfolgen in der unteren linken Ecke. Entsprechend der Klassifizierung über "severity", können wir die Farbe und das Icon beeinflussen (wie auch in der SAP GUI):
Validierung Feldebene
Die nächste Validierung wird nur ausgelöst, wenn sich das entsprechende Feld ändert. Dies ist vor allem dann sinnvoll, wenn wir gegen Schnittstellen oder Tabellen prüfen, ob dieser Wert existiert, um damit Performance einzusparen.
Anlegen
Die Anlage erfolgt wie im ersten Beispiel des Artikels über die Verhaltensdefinition und die anschließende Generierung in der Verhaltensimplementierung. Dieses Mal prüfen wir immer dann, wenn die beiden Stammdatenfelder sich ändern. Aus Sicht der Performance sollten wir dann eher zwei Validierungen daraus machen:
validation validateCoreData on save { field Country, PaymentCurrency; }
Implementieren
Wir hatten uns die Schnittstelle der Methode bereits beim ersten Beispiel angeschaut, daher wird dir auffallen sein, dass nur die Schlüssel an die Methode übergeben werden. Um nun an die fehlenden Informationen zu gelangen, müssen wir die Daten per EML lesen. Im Anschluss können wir über die Daten loopen und gegen die Stammdaten vergleichen.
READ ENTITIES OF ZBS_I_RAPPartner IN LOCAL MODE
ENTITY Partner
FIELDS ( Country PaymentCurrency )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_partner_data)
FAILED DATA(ls_failed)
REPORTED DATA(ls_reported).
LOOP AT lt_partner_data INTO DATA(ls_partner).
SELECT SINGLE FROM I_Country
FIELDS Country
WHERE Country = @ls_partner-Country
INTO @DATA(ld_found_country).
IF sy-subrc <> 0.
INSERT VALUE #( PartnerNumber = ls_partner-PartnerNumber ) INTO TABLE failed-partner.
INSERT VALUE #(
PartnerNumber = ls_partner-PartnerNumber
%msg = new_message_with_text( text = 'Country not found in I_Country' )
%element-country = if_abap_behv=>mk-on
) INTO TABLE reported-partner.
ENDIF.
SELECT SINGLE FROM I_Currency
FIELDS Currency
WHERE Currency = @ls_partner-PaymentCurrency
INTO @DATA(ld_found_currency).
IF sy-subrc <> 0.
INSERT VALUE #( PartnerNumber = ls_partner-PartnerNumber ) INTO TABLE failed-partner.
INSERT VALUE #(
PartnerNumber = ls_partner-PartnerNumber
%msg = new_message_with_text( text = 'Currency not found in I_Currency' )
%element-paymentcurrency = if_abap_behv=>mk-on
) INTO TABLE reported-partner.
ENDIF.
ENDLOOP.
Im Beispiel haben wir ein paar Dinge benutzt, die wir hier noch einmal erklären wollen:
- READ - Beim Lesen haben wir den Schlüssel in der Feldauswahl nicht angegeben, dieser wird aber automatisch dazu gelesen, da er zur Identifizierung der Entität genutzt wird.
- LOCAL MODE - Die Verifizierung des Users und des Status entfällt, das System geht davon aus das dies zuvor gemacht wurde. Mögliche Sperren durch den User werden vermeiden.
- %ELEMENT - Beim Erzeugen der Fehlermeldungen haben wir zusätzlich das Element angegeben und aktiviert, dies sorgt dafür, dass das Feld noch zusätzlich markiert wird und die Fehlermeldung erscheint.
Vervollständigen
Wenn du die Validierung ausprobierst, wirst du feststellen, dass diese nicht beim Anlegen des Eintrags greift. Die eingestellte Validierung reagiert nur auf Änderung des Feldes, was bei der Anlage nicht der Fall ist, da es den Datensatz noch nicht gibt. Dazu müssen wir noch zusätzlich den Create mit aufnehmen, sodass zumindest auch bei der Anlage die Validierung durchlaufen wird.
validation validateCoreData on save { create; field Country, PaymentCurrency; }
Komplettes Beispiel
Das komplette Beispiel für die Verhaltensdefinition und die Implementierung findest du an dieser Stelle.
Verhaltensdefinition
managed implementation in class zbp_bs_demo_rappartner unique;
strict;
define behavior for ZBS_I_RAPPartner alias Partner
persistent table zbs_dmo_partner
lock master
authorization master ( instance )
{
create;
update;
delete;
validation validateKeyIsFilled on save { create; }
validation validateCoreData on save { create; field Country, PaymentCurrency; }
mapping for zbs_dmo_partner
{
PartnerNumber = partner;
PartnerName = name;
Street = street;
City = city;
Country = country;
PaymentCurrency = payment_currency;
}
}
Verhaltensimplementierung
CLASS lhc_Partner DEFINITION INHERITING FROM cl_abap_behavior_handler.
PRIVATE SECTION.
METHODS get_instance_authorizations FOR INSTANCE AUTHORIZATION
IMPORTING keys REQUEST requested_authorizations FOR Partner RESULT result.
METHODS validatekeyisfilled FOR VALIDATE ON SAVE
IMPORTING keys FOR partner~validatekeyisfilled.
METHODS validatecoredata FOR VALIDATE ON SAVE
IMPORTING keys FOR partner~validatecoredata.
ENDCLASS.
CLASS lhc_Partner IMPLEMENTATION.
METHOD get_instance_authorizations.
ENDMETHOD.
METHOD validateKeyIsFilled.
LOOP AT keys INTO DATA(ls_key) WHERE PartnerNumber IS INITIAL.
INSERT VALUE #( PartnerNumber = ls_key-PartnerNumber ) INTO TABLE failed-partner.
INSERT VALUE #(
PartnerNumber = ls_key-PartnerNumber
%msg = new_message_with_text( severity = if_abap_behv_message=>severity-error text = 'PartnerNumber is mandatory' )
) INTO TABLE reported-partner.
ENDLOOP.
ENDMETHOD.
METHOD validateCoreData.
READ ENTITIES OF ZBS_I_RAPPartner IN LOCAL MODE
ENTITY Partner
FIELDS ( Country PaymentCurrency )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_partner_data)
FAILED DATA(ls_failed)
REPORTED DATA(ls_reported).
LOOP AT lt_partner_data INTO DATA(ls_partner).
SELECT SINGLE FROM I_Country
FIELDS Country
WHERE Country = @ls_partner-Country
INTO @DATA(ld_found_country).
IF sy-subrc <> 0.
INSERT VALUE #( PartnerNumber = ls_partner-PartnerNumber ) INTO TABLE failed-partner.
INSERT VALUE #(
PartnerNumber = ls_partner-PartnerNumber
%msg = new_message_with_text( text = 'Country not found in I_Country' )
%element-country = if_abap_behv=>mk-on
) INTO TABLE reported-partner.
ENDIF.
SELECT SINGLE FROM I_Currency
FIELDS Currency
WHERE Currency = @ls_partner-PaymentCurrency
INTO @DATA(ld_found_currency).
IF sy-subrc <> 0.
INSERT VALUE #( PartnerNumber = ls_partner-PartnerNumber ) INTO TABLE failed-partner.
INSERT VALUE #(
PartnerNumber = ls_partner-PartnerNumber
%msg = new_message_with_text( text = 'Currency not found in I_Currency' )
%element-paymentcurrency = if_abap_behv=>mk-on
) INTO TABLE reported-partner.
ENDIF.
ENDLOOP.
ENDMETHOD.
ENDCLASS.
Fazit
Die Validierungen sind ein mächtiges Instrument, um deine Daten innerhalb des Business Objekts sauber zu halten und dafür zu sorgen, dass nur valide Informationen gespeichert werden. Entsprechend solltest du dir bei der Anlage deines RAP BOs überlegen, welche Informationen, zu welchem Zeitpunkt, validiert werden sollen.