How to dynamically get a list of the entities of a RAP Business Object (or how to traverse a tree)?
A while ago I faced the problem that I had to retrieve information that was stored in a tree-like fashion and that could only be retrieved using a class that returned the child elements of an object.
Data like this could be HR data where a class would be used to return a list of direct reports of a manager. These employees could have direct reports themselves.
A RAP business object is also of a tree-like structure where the cds view that is marked as a root can have one or more child entities who again can have one or more child entities.
The concept how to traverse such a tree I found in the following wiki page.
There it is mentioned that “the inorder traversal of an N-ary tree is defined as visiting all the children except the last then the root and finally the last child recursively.
The constructor of the class takes the name of the root cds view as a parameter. The method get_entities( )returns the entities of the root bo including the root node as object of type
DATA(travel_bo_entities) = new zcl_af_get_nodes_from_rap_bo( '/DMO/R_TRAVEL_D' ). LOOP AT travel_bo_entities->get_entities( ) INTO DATA(entity). out->write( entity->name ). ENDLOOP.
How does it work?
traverse_child_entities( ) is the one that traverses our tree.
In the constructor an object of type
if_xco_data_definition is created for the root entitiy.
Then the method
traverse_child_entities( ) is called recursively. The child entities of the current entity are being retrieved using the method
METHOD traverse_child_entities. " syntax as found in " https://www.geeksforgeeks.org/inorder-traversal-of-an-n-ary-tree/ DATA current_child_number TYPE i. DATA(child_entities) = get_composition_childs( definition ). CHECK child_entities IS NOT INITIAL. DATA(number_of_child_entities) = lines( child_entities ). WHILE current_child_number < number_of_child_entities - 1. current_child_number += 1 . APPEND child_entities[ current_child_number ] TO entities. traverse_child_entities( child_entities[ current_child_number ] ). ENDWHILE. APPEND child_entities[ number_of_child_entities ] TO entities. "last child entity traverse_child_entities( child_entities[ number_of_child_entities ] ). ENDMETHOD.
I posted the complete source code on GitHub together with a second small class which actually calls the above mentioned demo class.
The code actually works for RAP business objects that are based on:
- classic CDS views (the ones that have an additional DDIC view, that we don’t recommend to use any more)
- new modern view entities
- and custom entities (which are used when data cannot be retrieved via CDS views but where an ABAP class is used to select the data)
For completeness I am showing the content of the method get_composition_childs( ) as well which used the XCO framework to retrieve the entities which are part of the composition tree of a RAP business object.
For custom entities I have to loop over all fields to identify those fields that are used to publish the compositions.
METHOD get_composition_childs. DATA compositions TYPE sxco_t_cds_compositions. DATA composition TYPE REF TO if_xco_cds_composition . DATA composition_targets TYPE STANDARD TABLE OF sxco_cds_object_name. DATA(view_type) = definition->get_type( ). CASE view_type. WHEN xco_cp_data_definition=>type->view_entity. compositions = definition->view_entity( )->compositions->all->get( ). WHEN xco_cp_data_definition=>type->custom_entity. DATA(lo_fields) = definition->custom_entity( )->fields->all->get( ). LOOP AT lo_fields INTO DATA(lo_field). IF lo_field->content( )->get( )-composition-target IS NOT INITIAL. APPEND lo_field->content( )->get( )-composition-target TO composition_targets. ENDIF. ENDLOOP. WHEN xco_cp_data_definition=>type->view. compositions = definition->view( )->compositions->all->get( ). WHEN OTHERS. "do nothing ENDCASE.