Programming

Advanced CDS Functionality for ABAP Development

You may run into a scenario where requirements placed on your CDS data model are very difficult or impossible to meet with basic CDS functionality.

 

Examples of such requirements are:

  • Performing complex calculations: Calling function modules to determine field values (BAPI_SALESORDER_ SIMULATE, ADDRESS_INTO_PRINTLINE)
  • Mapping a recursive logic: Using data from other systems (via remote function call [RFC])

The use of virtual elements or custom entities allows you to implement such requirements for your data model. This blog post will introduce you to these concepts. In addition, the use of CDS table functions based on ABAP-managed database procedures and SQLScript is possible, but we don’t use them here.

 

Virtual Elements

Virtual elements are used if you want to include elements in your CDS data model that aren’t available in the persistence data model of the database, but are calculated using (complex) ABAP program logic. The calculation is performed in ABAP classes that implement interfaces provided for this purpose.

 

Calculations Outside the Database: The virtual elements are calculated on the ABAP application server or the ABAP platform. Note that this means you don’t use the code pushdown capability for these virtual elements.

 

You can define a virtual element in the data model by specifying a designated annotation. To specify the implementing class you need to specify another annotation. To illustrate this, we’ll extend the CDS view ZI_Flight- Detail with another field that should contain the day of the week for a flight date. The day of the week is determined using the function block GET_WEEKDAY_NAME.

 

@ObjectModel.virtualElement: true

@ObjectModel.virtualElementCalculatedBy:

                'ABAP:ZCL_FLIGHTDETAIL_CALC_EXIT'

cast ( '' as langt ) as FlightDateWeekday,

 

The first of the two annotations of the ObjectModel domain identifies the field as a virtual field; with the second annotation, you specify the ZCL_ FLIGHTDETAIL_CALC_EXIT class as the implementation class. In this class, you implement the interface IF_SADL_EXIT_CALC_ELEMENT_READ with the following methods:

  • GET_CALCULATION_INFO: You must use this method to specify which fields of the CDS view are needed to calculate the virtual element. These fields are available even if they aren’t requested by the caller (i.e., they have been hidden in an SAP Fiori interface, for example).
  • CALCULATE: This method is called after the data selection. Here, you calculate the value of the virtual element based on the passed data.

Below shows a possible implementation of the calculation.

 

CLASS zcl_flightdetail_calc_exit DEFINITION

   PUBLIC

   FINAL

   CREATE PUBLIC.

   PUBLIC SECTION.

       INTERFACES if_sadl_exit_calc_element_read.

*      INTERFACES if_sadl_exit_filter_transform.

*      INTERFACES if_sadl_exit_sort_transform.

   PROTECTED SECTION.

   PRIVATE SECTION.

ENDCLASS.

 

CLASS zcl_flightdetail_calc_exit IMPLEMENTATION.

 

   METHOD if_sadl_exit_calc_element_read~get_calculation_info.

       IF iv_entity <> 'ZI_FLIGHTDETAIL'.

           RAISE EXCEPTION TYPE zcx_sadl_exit.

       ENDIF.

 

LOOP AT it_requested_calc_elements

          ASSIGNING FIELD-SYMBOL(<fs_calc_element>).

      CASE <fs_calc_element>.

          WHEN 'FLIGHTDATEWEEKDAY'.

              APPEND 'FLIGHTDATE' TO et_requested_orig_elements.

         WHEN OTHERS.

             RAISE EXCEPTION TYPE zcx_sadl_exit.

       ENDCASE.

   ENDLOOP.

ENDMETHOD.

 

METHOD if_sadl_exit_calc_element_read~calculate.

   DATA lt_original_data

       TYPE STANDARD TABLE OF zi_flightdetail

       WITH DEFAULT KEY.

   lt _original_data = CORRESPONDING #( it_original_data ).

   LOOP AT lt_original_data

           ASSIGNING FIELD-SYMBOL(<fs_original_data>).

       CALL FUNCTION 'GET_WEEKDAY_NAME'

         EXPORTING

           date = <fs_original_data>-FlightDate

           language = sy-langu

         IMPORTING

           longtext = <fs_original_data>-FlightDateWeekday

       EXCEPTIONS

           calendar_id = 1

           date_error = 2

           not_found = 3

           wrong_input = 4

           OTHERS = 5.

   IF sy-subrc <> 0.

*         MESSAGE ID SY-MSGID TYPE SY-MSGTY NUMBER SY-MSGNO

*         WITH SY-MSGV1 SY-MSGV2 SY-MSGV3 SY-MSGV4.

       ENDIF.

   ENDLOOP.

 ct_calculated_data = CORRESPONDING #( lt_original_data ).

ENDMETHOD.

 

ENDCLASS.

 

Tip: Interfaces for Filter and Sort Functions: The interfaces IF_SADL_EXIT_FILTER_TRANSFORM and IF_SADL_EXIT_SORT_TRANSFORM are used to enable filter and sort functions for a virtual element. You can define the responsible implementation class via the annotations @ObjectModel.filter.transformedBy or @ObjectModel.sort.transformedBy.

 

Since the virtual element is calculated at the service level on the ABAP platform and not at the database level, a SELECT statement in ABAP SQL on the CDS view returns an initial value for the virtual element (the same applies to the data preview function in ADT). However, the annotations are evaluated by the SADL framework, so when queried via an OData entity based on the CDS data model, the computation is performed by the associated implementation class. Therefore, one way you can perform a test is to publish your CDS view as an OData service (with the annotation @OData.publish: true at the view level) and then test this OData service in the SAP Gateway client (Transaction /IWFND/MAINT_SERVICE). The figure below shows the result of this test in our example.

 

OData Service Call in the SAP Gateway Client

 

Typing a Virtual Element in a CDS Projection View: The labeling and typing of a virtual element in a CDS projection view in the ABAP RESTful application programming model differ slightly in their syntax from those described here. In the example, they would look as follows:

 

virtual FlightDateWeekday: langt

 

The annotation @ObjectModel.virtualElement: true is omitted.

 

CDS Custom Entities

For read access (i.e., a query) to CDS data models, the ABAP RESTful application programming model distinguishes between two approaches:

  • With a managed query, the data selection is forwarded to the database via a CDS view and a corresponding SQL query.
  • In contrast, the unmanaged query is based on a CDS custom entity that delegates the selection of data to an ABAP class.

Below compares these two concepts.

 

Managed Versus Unmanaged Query in the ABAP RESTful Application

 

CDS custom entities thus allow you to implement your own data selection in ABAP. At the CDS level, only the definition of the interface remains.

 

Typical use cases for a CDS custom entity are:

  • Access to external data source is necessary.
  • The data access is supposed to take place via an ABAP API.
  • CDS functionality is not sufficient for data selection.
  • The functional scope of the CDS access controls isn’t sufficient for the required authorization checks.

CDS custom entities are used in the RAP framework as the basis for data selection of unmanaged queries. However, they can also be used in transactional RAP applications within business object modeling. In ABAP CDS, CDS custom entities can be specified as the target of associations. However, they can’t be used as a data source for SELECT statements in other CDS entities.

 

In ABAP programs, a CDS custom entity can also not be used as a data source of SELECT statements. However, the structured type of the CDS custom entity is known in ABAP programs and can be used, for example, after the TYPE addition.

 

A CDS custom entity can be defined via the define custom entity statement in the DDL source code. The easiest way to do this is to use the Define Custom Entity with Parameters template in ADT. Below shows the syntax for defining a CDS custom entity using a simple example.

 

@EndUserText.label: 'CDS Custom Entity Demo'

@ObjectModel.query.implementedBy: 'ABAP:ZCL_CUST_ENTITY_DEMO'

define custom entity zcust_entity_demo

// with parameters parameter_name : parameter_type

{

// Element list, Element annotations

       @UI.selectionField: [{position: 10}]

       @UI.lineItem: [{position: 10}]

   key UserName : xubname;

       @UI.lineItem: [{position: 20}]

       FirstName : ad_namefir;

       @UI.lineItem: [{position: 30}]

       LastName : ad_namelas;

       FullName : ad_namtext;

}

 

The annotation @ObjectModel.query.ImplementedBy at the view level references the ABAP class in which the query is implemented. Since the data types can’t be derived from an underlying database table or CDS entity, the element list of a CDS custom entity must be typed. Optionally, you can declare view-level and element-level metadata, a parameter list, or associations in the element list.

 

The query implementation class implements the interface IF_RAP_QUERY_PROVIDER and its Select method. Note that all functions (e.g., sorting, filtering, paging) that are provided by default in a managed query must be explicitly programmed here. In the following example, SAP user data is supposed to be queried via the Business Application Programming Interface (BAPI) BAPI_USER_GETLIST. The listing below shows an excerpt of the implementation. For the complete implementation of the example, you should refer to the SAP documentation on the ABAP RESTful application programming model at http://s-prs.de/v868503.

 

CLASS zcl_cust_entity_demo DEFINITION

...

   PUBLIC SECTION.

       INTERFACES if_rap_query_provider.

...

ENDCLASS.

 

CLASS zcl_cust_entity_demo IMPLEMENTATION.

   METHOD if_rap_query_provider~select.

 

       DATA(lv_top) = io_request->get_paging( )->get_page_size( ).

       DATA(lv_skip) = io_request->get_paging( )->get_offset( ).

       DATA(lt_clause) = io_request->get_filter( )->get_as_sql_

string( ).

...

   DATA lt_userlist TYPE STANDARD TABLE OF bapiusname.

   DATA lt_result TYPE STANDARD TABLE OF zcust_entity_demo.

...

   TRY.

       DATA(lt_filter_cond) = io_request->get_filter( )->

                                                                                              get_as_ranges( ).

     CATCH cx_rap_query_filter_no_range INTO DATA(lx_no_sel_option).

   ENDTRY.

 

TRY.

 

               IF io_request->is_data_requested( ).

                   CALL FUNCTION 'BAPI_USER_GETLIST'

                       EXPORTING

                           with_username = abap_true

                       TABLES

                           userlist = lt_userlist

                       return = lt_bapiret.

* Filter

                LOOP AT lt_userlist INTO DATA(ls_userlist).

                 DATA(lv_tabix) = sy-tabix.

                 LOOP AT lt_filter_cond INTO DATA(ls_filter_cond).

                     CASE ls_filter_cond-name.

...

* Sorting

               IF lt_sort IS NOT INITIAL.

                   LOOP AT lt_sort INTO DATA(ls_sort).

...

* Paging

               IF lv_top > 0.

                   LOOP AT lt_userlist INTO ls_userlist FROM lv_skip

                                                                               + 1 TO ( lv_skip + lv_top ).

                       MOVE-CORRESPONDING ls_userlist TO ls_result.

                        APPEND ls_result TO lt_result.

...

* Count

               IF io_request->is_total_numb_of_rec_requested( ).

                   io_response->set_total_number_of_records( lines(

                                                                                                                 lt_result ) ).

               ENDIF.

               io_response->set_data( lt_result ).

           ELSE.

*              no data is requested

           ENDIF.

       CATCH cx_rap_query_provider INTO DATA(lx_exc). "error handling

   ENDTRY.

 

ENDMETHOD.

ENDCLASS.

 

After creating a service definition and a service binding, you can test the query directly in ADT. The final figure shows the result of the query in the data preview.

 

Preview of the OData Service Output for CDS Custom Entity ZCUST_ ENTITY_DEMO

 

Editor’s note: This post has been adapted from a section of the book ABAP RESTful Application Programming Model: The Comprehensive Guide by Lutz Baumbusch, Matthias Jäger, and Michael Lensch.

 

Recommendation

ABAP RESTful Application Programming Model
ABAP RESTful Application Programming Model

You’ve worked with ABAP, SAP Fiori, and core data services—now see how these technologies and more come together in the ABAP RESTful application programming model. Learn to develop applications optimized for SAP S/4HANA, whether your system is on-premise or in the cloud. Follow step-by-step instructions to build new ABAP applications, update legacy applications, and reuse existing source code. Make the new model work for you!

Learn More
SAP PRESS
by SAP PRESS

SAP PRESS is the world's leading SAP publisher, with books on ABAP, SAP S/4HANA, SAP CX, intelligent technologies, SAP Business Technology Platform, and more!

Comments