This is a test message to test the length of the message box.
Login
ABAP RAP Custom Entity
Erstellt von Software-Heroes

RAP - Custom Entity Wertehilfe (Deep Dive)

239

Mit der Custom Entity hast du in RAP die meisten Freiheiten in der Entwicklung von ABAP Cloud Anwendungen, doch wie sieht es mit potentiellen Fehlern aus?

Werbung


In diesem Artikel wollen wir eine Wertehilfe mit einer Custom Entity implementieren. Dabei werden wir Schritt für Schritt auf die unterschiedlichen Fallstricke eingehen und was du bei der Entwicklung beachten solltest.

 

Einleitung 

Die Custom Entity haben wir bereits in einem älteren Artikel beschrieben und die Funktionsweise geschildert. Die Custom Entity stellt eine Struktur zur Verfügung und implementiert die Logik über eine ABAP Klasse. Die Entität selbst enthält keine Daten, sondern muss immer erst durch das SADL Framework aufgerufen werden. Deshalb kommen Custom Entitys vor allem in RAP Anwendungen zum Einsatz.

In diesem Beispiel wollen wir unsere einfache RAP Anwendung um eine Suchhilfe für die Städte erweitern und gehen noch einmal alles Schritt für Schritt durch.

 

Anlage

Im ersten Schritt beginnen wir mit der Anlage des Core Data Services und der ABAP Klasse.

 

Klasse

Da bei der Anlage der Custom Entity, auch die Klasse in der Annotation geprüft wird, implementieren wir die leere Klasse zuerst. Über das Kontextmenü im "Project Explorer" können wir den Wizard starten.

 

Dazu legen wir die Klasse an und verwenden dazu das Interface IF_RAP_QUERY_PROVIDER, welches die Custom Entity erwartet.

 

Die Klasse sieht nun wie folgt aus, hier wurde aber auch der ABAP Cleaner einmal ausgeführt:

CLASS zcl_bs_demo_city_query DEFINITION
  PUBLIC FINAL
  CREATE PUBLIC.

  PUBLIC SECTION.
    INTERFACES if_rap_query_provider.
ENDCLASS.


CLASS zcl_bs_demo_city_query IMPLEMENTATION.
  METHOD if_rap_query_provider~select.
  ENDMETHOD.
ENDCLASS.

 

Core Data Service

Im nächsten Schritt legen wir den Core Data Service über das Kontextmenü an. Dabei verwenden wir das CDS Template "defineCustomEntityWithParameters" als Vorlage:

 

Hast du bei der Anlage das falsche Template gewählt, musst du das Objekt nicht löschen, sondern kannst ein anderes CDS Template wählen. Wir definieren zwei Felder, einmal die Stadt, die wir suchen und einmal einen kurzen Schlüssel, über den wir ebenfalls suchen können. Da wir eingebaute Datentypen verwenden, setzen wir noch das Label und die QuickInfo für den Anwender, damit die Texte in der UI erscheinen.

@EndUserText.label: 'City Value Help'
@ObjectModel.query.implementedBy: 'ABAP:ZCL_BS_DEMO_CITY_QUERY'
define custom entity ZBS_I_RAPCityVH
{
      @EndUserText.label: 'City'
      @EndUserText.quickInfo: 'Name of the City'
  key City      : abap.char(60);
      @EndUserText.label: 'City (Short)'
      @EndUserText.quickInfo: 'Short name of the City'
      CityShort : abap.char(10);
}

 

Implementierung

Wir haben nun die Grundelemente angelegt, die wir für die Suchhilfe benötigen. In diesem Abschnitt implementieren wir die nötigen Logiken, um Daten in der UI zu sehen.

 

Einbindung

Binden wir dazu die Suchhilfe an das Feld im Projection View ZBS_C_RAPPartner, dazu verwenden wir die Annotation "Consumption.valueHelpDefinition":

@Consumption.valueHelpDefinition: [{ entity: { name: 'ZBS_I_RAPCityVH', element: 'City' } }]
City,

 

Da auch Wertehilfen nach Außen freigegeben werden müssen, solltest du den View in deine Servicedefinition mit aufnehmen. Im Preview funktioniert es meist auch ohne Freigabe, spätestens nach dem Deployment wird der fehlende Zugriff auffallen.

@EndUserText.label: 'Simple Partner Service'
define service ZBS_SIMPLE_PARTNER {
  expose ZBS_C_RAPPartner as Partner;
  expose ZBS_I_RAPCityVH;
}

 

Query Klasse

Erweitern wir nun die Query Klasse um die Logik, die wir brauchen um die Wertehilfe einfach verwenden zu können. Dazu benötigen wir eine interne Tabelle mit dem Datentyp der Custom Entity. Diese befüllen wir mit den entsprechenden Daten.

DATA lt_values TYPE STANDARD TABLE OF ZBS_I_RAPCityVH WITH EMPTY KEY.

lt_values = VALUE #( ( City = 'Walldorf' CityShort = 'DE' )
                     ( City = 'Redmond' CityShort = 'US' )
                     ( City = 'Menlo Park' CityShort = 'US' )
                     ( City = 'Hangzhou' CityShort = 'CN' )
                     ( City = 'Munich' CityShort = 'DE' )
                     ( City = 'Vevey' CityShort = 'CH' )
                     ( City = 'Sankt Petersburg' CityShort = 'RU' )
                     ( City = 'Seattle' CityShort = 'US' )
                     ( City = 'Wolfsburg' CityShort = 'DE' )
                     ( City = 'Cologne' CityShort = 'DE' ) ).

 

Um die Daten zurückzugeben, müssen wir noch die passenden Methoden des REPSONSE aufrufen. Hier ist es wichtig, die Methoden nur aufzurufen, wenn es vom Framework angefordert wird, da es sonst zu einem Fehler kommt. Daher fragen wir zuerst, ob die Felder angefragt wurden und rufen dann die Methode auf. Wichtig ist auch, die beiden Methoden müssen immer implementiert werden und sollten zum Beispiel nicht in einem TRY/CATCH sein, wo sie bei einem Fehler vielleicht nicht aufgerufen werden.

IF io_request->is_data_requested( ).
  io_response->set_data( lt_values ).
ENDIF.

IF io_request->is_total_numb_of_rec_requested( ).
  io_response->set_total_number_of_records( lines( lt_values ) ).
ENDIF.

 

Rufen wir jetzt die Wertehilfe auf, erhalten wir leider kein Ergebnis, die Liste bleibt leer.

 

Das Problem finden wir in den ABAP Development Tools über den "Feed Reader". Im Gateway Log tauchen dazu zwei Fehlermeldungen auf.

 

Interessant ist hier der "Backend Error", da wir hier weiterführende Informationen zu unserem Problem finden. Wir haben die Methode GET_SORT_ELEMENTS nicht aufgerufen. Dies wird aber weder im Interface noch an der Methode beschrieben.

 

Neben dieser Methode müssen wir auch die Methode GET_PAGING aufrufen, für die wir einen ähnlichen Fehler erhalten. Der Aufruf der Methoden muss innerhalb des Methodenaufrufs implementiert werden.

io_request->get_sort_elements( ).
io_request->get_paging( ).

 

Führen wir noch einmal die Suchhilfe aus, werden die Daten angezeigt und wir können Einträge für den Filter übernehmen. Damit ist ein sehr einfache Suchhilfe ohne viel Zusatz und als "Festwerthilfe" definiert.

 

Erweiterung

Die Suchhilfe funktioniert, allerdings keine zusätzlichen Funktionen wie Filterung, Sortierung oder Paging, welches von außen an uns übergeben wird. In diesem Abschnitt werden wir auch noch diese grundsätzlichen Funktionen implementieren.

 

Classic ABAP

Im klassischen ABAP gibt es die Hilfeklasse /IWBEP/CL_MGW_DATA_UTIL, diese besitzt so weit alle Methoden um Daten zu filtern. Diese ist allerdings in ABAP Cloud und im ABAP Environment nicht verfügbar.

 

Kopie

Wenn du die Klasse zuvor verwendest, musst du allerdings auch die RAP Objekte und Strukturen zuerst in die Gateway Strukturen konvertieren. Damit wir auch in der Cloud mit dieser Funktion arbeiten können, kopieren wir die Gateway-Klasse und führen ein Refactoring durch. Schauen wir uns dazu die Methode ORDERBY an.

DATA: lt_otab  TYPE abap_sortorder_tab,
      ls_oline TYPE abap_sortorder.
DATA: ls_order LIKE LINE OF it_order.

LOOP AT it_order INTO ls_order.
  ls_oline-name = ls_order-property.
  TRANSLATE ls_oline-name TO UPPER CASE. 
  IF ls_order-order = gcs_sorting_order-descending.
    ls_oline-descending = 'X'.
  ENDIF.
  APPEND ls_oline TO lt_otab.
  CLEAR ls_oline.
ENDLOOP.

SORT ct_data BY (lt_otab).

 

Nach dem Vergleich der Zielstruktur und der Verwendung von Inline-Deklaration, sowie Modern ABAP, verkleinert sich die Logik auf zwei Zeilen:

DATA(lt_sort) = CORRESPONDING abap_sortorder_tab( it_sort MAPPING name = element_name ).
SORT ct_data BY (lt_sort).

 

Die vollständige Logik der Klasse findest du im Commit weiter unten oder direkt im GitHub Repository auf dem aktuellsten Stand.

 

Request

Nun haben wir drei Methoden und ein Request Objekt, dazu erstellen wir noch eine Methode, die die Arbeit übernimmt, alle nötigen Schritte durchzuführen und die Methoden in der richtigen Reihenfolge aufzurufen. Dazu lesen wir zuerst alle nötigen Einschränkungen aus dem REQUEST Objekt, wie die Sortierung, die Pagination und die Filter

DATA(lt_sort) = io_request->get_sort_elements( ).
DATA(ld_offset) = io_request->get_paging( )->get_offset( ).
DATA(ld_page_size) = io_request->get_paging( )->get_page_size( ).
TRY.
    DATA(lt_filter) = io_request->get_filter( )->get_as_ranges( ).
  CATCH cx_rap_query_filter_no_range.
    CLEAR lt_filter.
ENDTRY.

 

Dann rufen wir die Methoden in der richtigen Reihenfolge auf. Die Pagination sollte zum Schluss erfolgen, da wir zuerst einmal die richtige Reihenfolge und den Filter benötigen, bevor wir dann einen Ausschnitt der Daten erzeugen können.

filter_data( EXPORTING it_filter = lt_filter
             CHANGING  ct_data   = ct_data ).

order_data( EXPORTING it_sort = lt_sort
            CHANGING  ct_data = ct_data ).

page_data( EXPORTING id_offset    = ld_offset
                     id_page_size = ld_page_size
           CHANGING  ct_data      = ct_data ).

 

Als letzten Schritt müssen wir noch die Logik in unserer Query Implementierung aufrufen. Dazu merken wir uns zuerst die Anzahl aller vorhandenen Datensätze, bevor wird die Daten verändern.

DATA(ld_all_entries) = lines( lt_values ).
NEW zcl_bs_demo_adjust_data( )->adjust_via_request( EXPORTING io_request = io_request
                                                    CHANGING  ct_data    = lt_values ).

 

Ergebnis

Damit haben wir die Grundfunktionen der Suchhilfe implementiert und können die Klasse wiederverwenden. Hier einmal eine kurze Demo der Funktion FILTER und SORT in der Suchhilfe.

 

Zusammenfassung

Hier noch einmal eine kurze Zusammenfassung des Artikels und der wichtigsten Learnings aus der Implementierung.

  • Schnelle Umsetzung - Die Umsetzung einer einfachen Suchhilfe ohne viel Zusatz ist mit zwei Objekten und zwei Anpassungen durchgeführt.
  • Service - Aufnahme der Entität in die Servicedefinition, da sonst später im Test die Suchhilfe nicht aufgerufen werden kann.
  • Versteckte Pflichtfelder - Nicht ganz so durchschaubar sind die eigentlichen "Pflichten" bei der Implementierung einer Custom Entity Query. Hier solltest du darauf achten bei Request und Response die entsprechenden Methoden zu rufen.
  • Zusatzfunktionen - Die Implementierung wie Filterung, Sortieren und Paging bedeutet viel manueller Aufwand in der Entwicklung, bei Core Data Services wird bereits alles geschenkt.

 

Beispiel

Alle Anpassungen findest du als Commit in unserem GitHub Repository und kannst dir dort die gemachten Änderungen und neuen Objekte noch einmal im Detail anschauen.

 

Fazit

In diesem Artikel hast du gelernt die Custom Entity auch für eigene Suchhilfen zu verwenden und welche Fallstricke bei der Implementierung auf dich warten können. Das sollte dich allerdings nicht davor abschrecken, das Objekt zu verwenden, da es viele Flexibilitäten gewährt.


Enthaltene Themen:
RAPBTPCustom EntityWertehilfe
Kommentare (2)



Und weiter ...

Bist du zufrieden mit dem Inhalt des Artikels? Wir posten jeden Freitag neuen Content im Bereich ABAP und unregelmäßig in allen anderen Bereichen. Schaue bei unseren Tools und Apps vorbei, diese stellen wir kostenlos zur Verfügung.


RAP - Semantischer Schlüssel

Kategorie - ABAP

Wofür benötigst du den semantischen Schlüssel und wie wird er im ABAP RESTful Programming Model dargestellt? Hier gibt es mehr dazu.

13.12.2024

RAP - Upload von Dateien (Stream)

Kategorie - ABAP

Wie kannst du einfach Dateien in deine RAP Entität laden und diese in ABAP zur Verfügung stellen? Hier schauen wir uns einmal die Details an.

10.12.2024

RAP - Report Pattern

Kategorie - ABAP

Wie ist das Report Pattern in RAP aufgebaut und was kannst du damit machen? Mehr dazu in diesem ABAP Artikel.

06.12.2024

RAP - Übersetzungs-App (Beispiel)

Kategorie - ABAP

Schauen wir uns einmal ein praktisches Beispiel für die Entwicklung einer RAP Anwendung im ABAP Environment an und wie du mit wenig Aufwand eine App erstellst.

02.08.2024

RAP - Deep Action in OData v4

Kategorie - ABAP

In diesem Artikel schauen wir uns einmal Aktionen mit tiefen Strukturen an, wie wir sie erzeugen und Daten an einen API Endpunkt übergeben können.

24.05.2024