Files
abap-cheat-sheets/src/zbp_demo_abap_rap_ro_u.clas.locals_imp.abap

1022 lines
42 KiB
ABAP

***********************************************************************
*
* RAP BO provider (i. e. ABAP behavior pool/ABP)
* for a RAP demo scenario
*
* - RAP scenario: unmanaged RAP BO, external numbering
* - Data model: Consists of a root entity and one child entity. The BDEF
* defines the behavior for these two entities which are connected via
* a CDS composition relation. The definitions in the BDEF determine
* which methods must be implemented in this ABAP behavior pool (ABP).
*
* ----------------------------- NOTE -----------------------------------
* This simplified example is not a real life scenario and rather
* focuses on the technical side by giving an idea how the communication
* and data exchange between a RAP BO consumer, which is a class
* in this case, and RAP BO provider can work. Additionally, it shows
* how the methods for non-standard RAP BO operations might be
* self-implemented in an ABP. The example is intentionally kept
* short and simple and focuses on specific RAP aspects. For this reason,
* the example might not fully meet the requirements of the RAP BO contract.
*
* For demonstration purposes, some of the operations are
* impacted by feature controls and instance authorization as specified
* in the BDEF.
*
* The code presented in this class is only meant for supporting the ABAP
* cheat sheets. It is not intended for direct use in a
* production system environment. The code examples in the ABAP cheat
* sheets are primarily intended to provide a better explanation and
* visualization of the syntax and semantics of ABAP statements and not to
* solve concrete programming tasks. For production application programs,
* a dedicated solution should therefore always be worked out for each
* individual case. There is no guarantee for either the correctness or
* the completeness of the code. In addition, there is no legal
* responsibility or liability for possible errors or their consequences
* which occur through the use of the example code.
***********************************************************************
***********************************************************************
* Class lcl_buffer
*
* To have a self-contained and simple scenario, the transactional
* buffer is realized by internal tables here. These tables - one for
* the root entity, one for the child entity - are designed in a way
* to include RAP BO instance data as well content IDs and flags to
* specify if an instance is to be changed or deleted (which is relevant
* for the save method in this example).
*
* The purpose of this class is to create these internal tables and
* provide a method implementation for the preparation of the tables,
* i. e. to prepare the content of the transactional buffer which is
* accessed throughout the handler and saver method implementations.
* The method/s is/are called in the context of each EML request.
*
***********************************************************************
"Class that constitutes the transactional buffer
CLASS lcl_buffer DEFINITION.
PUBLIC SECTION.
"Structure and internal table types for the internal table serving
"as transactional buffers for the root and child entities
TYPES: BEGIN OF gty_buffer,
instance TYPE zdemo_abap_rap_ro_u,
cid TYPE string,
changed TYPE abap_bool,
deleted TYPE abap_bool,
END OF gty_buffer.
TYPES: BEGIN OF gty_buffer_child,
instance TYPE zdemo_abap_rap_ch_u,
cid_ref TYPE string,
cid_target TYPE string,
changed TYPE abap_bool,
END OF gty_buffer_child.
TYPES gtt_buffer TYPE TABLE OF gty_buffer
WITH EMPTY KEY.
TYPES gtt_buffer_child TYPE TABLE OF gty_buffer_child
WITH EMPTY KEY.
CLASS-DATA:
"Internal tables serving as transactional buffers for the root and child entities
root_buffer TYPE STANDARD TABLE OF gty_buffer WITH EMPTY KEY,
child_buffer TYPE STANDARD TABLE OF gty_buffer_child WITH EMPTY KEY.
"Structure and internal table types to include the keys for buffer preparation methods
TYPES: BEGIN OF root_keys,
key_field TYPE zdemo_abap_rap_ro_u-key_field,
END OF root_keys,
BEGIN OF child_keys,
key_field TYPE zdemo_abap_rap_ch_u-key_field,
key_ch TYPE zdemo_abap_rap_ch_u-key_ch,
full_key TYPE abap_bool,
END OF child_keys,
tt_root_keys TYPE TABLE OF root_keys WITH EMPTY KEY,
tt_child_keys TYPE TABLE OF child_keys WITH EMPTY KEY.
"Buffer preparation methods
CLASS-METHODS: prep_root_buffer IMPORTING keys TYPE tt_root_keys,
prep_child_buffer IMPORTING keys TYPE tt_child_keys.
ENDCLASS.
CLASS lcl_buffer IMPLEMENTATION.
"Buffer preparation for the root entity based on the requested key values
METHOD prep_root_buffer.
LOOP AT keys ASSIGNING FIELD-SYMBOL(<buffer>).
"Logic:
"- Line with the specific key values exists in the buffer for the root entity
"- If it is true: Do nothing, buffer is prepared for the specific instance.
"- Note: If the line is marked as deleted, the buffer should not be filled anew with the data.
IF line_exists( lcl_buffer=>root_buffer[ instance-key_field = <buffer>-key_field ] ).
"Do nothing, buffer is prepared for the specific instance.
ELSE.
"Checking if entry exists in the database table of the root entity based on the key value
SELECT SINGLE @abap_true
FROM zdemo_abap_rapt1
WHERE key_field = @<buffer>-key_field
INTO @DATA(exists).
IF exists = abap_true.
"If entry exists, retrieve it based on the shared key value
DATA line TYPE zdemo_abap_rap_ro_u.
SELECT SINGLE * FROM zdemo_abap_rapt1
WHERE key_field = @<buffer>-key_field
INTO CORRESPONDING FIELDS OF @line.
IF sy-subrc = 0.
"Adding line to the root buffer
APPEND VALUE #( instance = line ) TO lcl_buffer=>root_buffer.
ENDIF.
ENDIF.
ENDIF.
ENDLOOP.
ENDMETHOD.
"Buffer preparation for the child entity based on the requested key values
METHOD prep_child_buffer.
LOOP AT keys ASSIGNING FIELD-SYMBOL(<buffer_ch>).
"The full_key flag is in this example only relevant if a read operation is executed on the child entity directly
"and all key values should be considered for the data retrieval from the database table.
IF <buffer_ch>-full_key = abap_true.
"Logic:
"- Line with specific key values exists in the buffer for the child entity
"- If it is true: Do nothing, buffer is prepared for the specific instance.
IF line_exists( lcl_buffer=>child_buffer[ instance-key_field = <buffer_ch>-key_field
instance-key_ch = <buffer_ch>-key_ch ] ).
"Buffer is prepared for the instance.
ELSE.
"Checking if entry exists in the database table of the child entity based on the shared key value
SELECT SINGLE @abap_true
FROM zdemo_abap_rapt2
WHERE key_field = @<buffer_ch>-key_field
AND key_ch = @<buffer_ch>-key_ch
INTO @DATA(exists).
"If entry exists, retrieve all entries based on the key values
IF exists = abap_true.
DATA line_ch TYPE zdemo_abap_rap_ch_u.
SELECT SINGLE * FROM zdemo_abap_rapt2
WHERE key_field = @<buffer_ch>-key_field
AND key_ch = @<buffer_ch>-key_ch
INTO CORRESPONDING FIELDS OF @line_ch.
IF sy-subrc = 0.
"Adding line to the child buffer if no line exists with all key values
APPEND VALUE #( instance = line_ch ) TO lcl_buffer=>child_buffer.
ENDIF.
ENDIF.
ENDIF.
ELSE.
"Logic:
"- Line with specific keys exists in the buffer for the root entity and is marked for deletion
"- If all is true: Doing nothing, buffer is prepared for the specific instance.
"- Else: Retrieving all lines from the database table of the child entity having the shared key
IF line_exists( lcl_buffer=>root_buffer[ instance-key_field = <buffer_ch>-key_field ] )
AND VALUE #( lcl_buffer=>root_buffer[ instance-key_field = <buffer_ch>-key_field ]-deleted OPTIONAL ) IS NOT INITIAL.
"Buffer is prepared for the instance.
ELSE.
"Checking if entry exists in the database table of the child entity based on the shared key value
SELECT SINGLE @abap_true
FROM zdemo_abap_rapt2
WHERE key_field = @<buffer_ch>-key_field
INTO @DATA(exists_ch).
"If entry exists, retrieve all entries based on the shared key value
IF exists_ch = abap_true.
DATA ch_tab TYPE TABLE OF zdemo_abap_rap_ch_u WITH EMPTY KEY.
SELECT * FROM zdemo_abap_rapt2
WHERE key_field = @<buffer_ch>-key_field
INTO CORRESPONDING FIELDS OF TABLE @ch_tab.
IF sy-subrc = 0.
LOOP AT ch_tab ASSIGNING FIELD-SYMBOL(<ch>).
"Adding line to the child buffer if no line exists with all key values
IF NOT line_exists( lcl_buffer=>child_buffer[ instance-key_field = <ch>-key_field
instance-key_ch = <ch>-key_ch ] ).
APPEND VALUE #( instance = <ch> ) TO lcl_buffer=>child_buffer.
ENDIF.
ENDLOOP.
ENDIF.
ENDIF.
ENDIF.
ENDIF.
ENDLOOP.
ENDMETHOD.
ENDCLASS.
***********************************************************************
* Local handler class lhc_root
*
* Contains handler method definitions and implementations as defined
* in the CDS behavior definition (BDEF).
*
***********************************************************************
CLASS lhc_root DEFINITION INHERITING FROM cl_abap_behavior_handler.
PRIVATE SECTION.
METHODS get_global_authorizations FOR GLOBAL AUTHORIZATION
IMPORTING REQUEST requested_authorizations FOR root RESULT result.
METHODS create FOR MODIFY
IMPORTING entities FOR CREATE root.
METHODS read FOR READ
IMPORTING keys FOR READ root RESULT result.
METHODS lock FOR LOCK
IMPORTING keys FOR LOCK root.
METHODS update FOR MODIFY
IMPORTING entities FOR UPDATE root.
METHODS delete FOR MODIFY
IMPORTING keys FOR DELETE root.
METHODS rba_child FOR READ
IMPORTING keys_rba FOR READ root\_child FULL result_requested RESULT result LINK association_links.
METHODS cba_child FOR MODIFY
IMPORTING entities_cba FOR CREATE root\_child.
METHODS multiply_by_2 FOR MODIFY
IMPORTING keys FOR ACTION root~multiply_by_2.
METHODS get_instance_authorizations FOR INSTANCE AUTHORIZATION
IMPORTING keys REQUEST requested_authorizations FOR root RESULT result.
METHODS get_instance_features FOR INSTANCE FEATURES
IMPORTING keys REQUEST requested_features FOR root RESULT result.
METHODS multiply_by_3 FOR MODIFY
IMPORTING keys FOR ACTION root~multiply_by_3.
METHODS get_global_features FOR GLOBAL FEATURES
IMPORTING REQUEST requested_features FOR root RESULT result.
METHODS set_z FOR MODIFY
IMPORTING keys FOR ACTION root~set_z.
ENDCLASS.
CLASS lhc_root IMPLEMENTATION.
METHOD get_global_authorizations.
"No implementation on purpose. No global authorization restriction.
ENDMETHOD.
METHOD create.
"Preparing the transactional buffer based on the input BDEF derived type.
lcl_buffer=>prep_root_buffer( CORRESPONDING #( entities ) ).
"Processing requested entities sequentially
LOOP AT entities ASSIGNING FIELD-SYMBOL(<create>).
"Logic:
"- Line with the specific key does not exist in the buffer for the root entity
"- Line with the specific key exists in the buffer but it is marked as deleted
"- If it is true: Add new instance to the buffer and, if needed, remove the instance marked as deleted beforehand
IF NOT line_exists( lcl_buffer=>root_buffer[ instance-key_field = <create>-key_field ] )
OR line_exists( lcl_buffer=>root_buffer[ instance-key_field = <create>-key_field deleted = abap_true ] ).
"If it exists, removing instance that is marked for deletion from the transactional buffer since it gets replaced by a new one.
DELETE lcl_buffer=>root_buffer WHERE instance-key_field = VALUE #(
lcl_buffer=>root_buffer[ instance-key_field = <create>-key_field ]-instance-key_field OPTIONAL ) AND deleted = abap_true.
"Adding new instance to the transactional buffer by considering %control values
APPEND VALUE #( cid = <create>-%cid
instance-key_field = <create>-key_field
instance-field1 = COND #( WHEN <create>-%control-field1 NE if_abap_behv=>mk-off
THEN <create>-field1 )
instance-field2 = COND #( WHEN <create>-%control-field2 NE if_abap_behv=>mk-off
THEN <create>-field2 )
instance-field3 = COND #( WHEN <create>-%control-field3 NE if_abap_behv=>mk-off
THEN <create>-field3 )
instance-field4 = COND #( WHEN <create>-%control-field4 NE if_abap_behv=>mk-off
THEN <create>-field4 )
changed = abap_true
deleted = abap_false ) TO lcl_buffer=>root_buffer.
"Filling the MAPPED response parameter for the root entity
INSERT VALUE #( %cid = <create>-%cid
%key = <create>-%key ) INTO TABLE mapped-root.
ELSE.
"Filling FAILED and REPORTED response parameters
APPEND VALUE #( %cid = <create>-%cid
%key = <create>-%key
%create = if_abap_behv=>mk-on
%fail-cause = if_abap_behv=>cause-unspecific
) TO failed-root.
APPEND VALUE #( %cid = <create>-%cid
%key = <create>-%key
%create = if_abap_behv=>mk-on
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = 'Create operation failed.' )
) TO reported-root.
ENDIF.
ENDLOOP.
ENDMETHOD.
METHOD update.
"Preparing the transactional buffer based on the input BDEF derived type.
lcl_buffer=>prep_root_buffer( CORRESPONDING #( entities ) ).
"Processing requested entities sequentially
"Note:
"The example is implemented in a way that instances that failed in methods called before this method are not handled.
"The instances that have failed before this method call are not available in this method's input parameter.
"Hence, an adding to FAILED and REPORTED is not implemented here.
LOOP AT entities ASSIGNING FIELD-SYMBOL(<update>).
"Logic:
"- Line with the specific key exists in the buffer for the root entity and it is not marked as deleted
"- If it is true: Updating the buffer based on the input BDEF derived type and considering %control values
READ TABLE lcl_buffer=>root_buffer
WITH KEY instance-key_field = <update>-key_field
deleted = abap_false
ASSIGNING FIELD-SYMBOL(<fs_up>).
IF sy-subrc = 0.
<fs_up>-instance-field1 = COND #( WHEN <update>-%control-field1 NE if_abap_behv=>mk-off
THEN <update>-field1
ELSE <fs_up>-instance-field1 ).
<fs_up>-instance-field2 = COND #( WHEN <update>-%control-field2 NE if_abap_behv=>mk-off
THEN <update>-field2
ELSE <fs_up>-instance-field2 ).
<fs_up>-instance-field3 = COND #( WHEN <update>-%control-field3 NE if_abap_behv=>mk-off
THEN <update>-field3
ELSE <fs_up>-instance-field3 ).
<fs_up>-instance-field4 = COND #( WHEN <update>-%control-field4 NE if_abap_behv=>mk-off
THEN <update>-field4
ELSE <fs_up>-instance-field4 ).
<fs_up>-changed = abap_true.
<fs_up>-deleted = abap_false.
ENDIF.
ENDLOOP.
ENDMETHOD.
METHOD read.
"Preparing the transactional buffer based on the input BDEF derived type.
lcl_buffer=>prep_root_buffer( CORRESPONDING #( keys ) ).
"Processing requested keys sequentially
LOOP AT keys ASSIGNING FIELD-SYMBOL(<read>) GROUP BY <read>-%tky.
"Logic:
"- Line exists in the buffer and it is not marked as deleted
"- If it is true: Adding the entries to the buffer based on the input BDEF derived type and considering %control values
READ TABLE lcl_buffer=>root_buffer
WITH KEY instance-key_field = <read>-key_field
deleted = abap_false
ASSIGNING FIELD-SYMBOL(<fs_r>).
IF sy-subrc = 0.
APPEND VALUE #( %tky = <read>-%tky
field1 = COND #( WHEN <read>-%control-field1 NE if_abap_behv=>mk-off
THEN <fs_r>-instance-field1 )
field2 = COND #( WHEN <read>-%control-field2 NE if_abap_behv=>mk-off
THEN <fs_r>-instance-field2 )
field3 = COND #( WHEN <read>-%control-field3 NE if_abap_behv=>mk-off
THEN <fs_r>-instance-field3 )
field4 = COND #( WHEN <read>-%control-field4 NE if_abap_behv=>mk-off
THEN <fs_r>-instance-field4 )
) TO result.
ELSE.
"Filling FAILED and REPORTED response parameters
APPEND VALUE #( %tky = <read>-%tky
%fail-cause = if_abap_behv=>cause-not_found
) TO failed-root.
APPEND VALUE #( %tky = <read>-%tky
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = 'Read operation failed.' )
) TO reported-root.
ENDIF.
ENDLOOP.
ENDMETHOD.
METHOD lock.
TRY.
"Instantiating lock object
DATA(lo_lock) = cl_abap_lock_object_factory=>get_instance(
iv_name = 'EZDEMO_ABAP_LOCK' ).
"Processing requested keys sequentially
LOOP AT keys REFERENCE INTO DATA(lr_key).
TRY.
lo_lock->enqueue( it_parameter = VALUE #(
( name = 'KEY_FIELD' value = REF #(
lr_key->key_field ) ) ) ).
CATCH cx_abap_foreign_lock.
APPEND VALUE #( %key = lr_key->*
%fail-cause = if_abap_behv=>cause-locked )
TO failed-root.
CATCH cx_abap_lock_failure.
APPEND VALUE #( %key = lr_key->*
%fail-cause = if_abap_behv=>cause-unspecific )
TO failed-root.
ENDTRY.
ENDLOOP.
CATCH cx_abap_lock_failure.
APPEND VALUE #( %fail-cause = if_abap_behv=>cause-unspecific )
TO failed-root.
ENDTRY.
ENDMETHOD.
METHOD delete.
"Preparing the transactional buffer based on the input BDEF derived type.
lcl_buffer=>prep_root_buffer( CORRESPONDING #( keys ) ).
"Processing requested keys sequentially
"Note:
"The example is implemented in a way that instances that failed in methods called before this method are not handled.
"The instances that have failed before this method call are not available in this method's input parameter.
"Hence, an adding to FAILED and REPORTED is not implemented here.
LOOP AT keys ASSIGNING FIELD-SYMBOL(<delete>).
"Logic:
"- Line exists in the buffer and it is not marked as deleted
"- If it is true: Flagging the instance as deleted
READ TABLE lcl_buffer=>root_buffer
WITH KEY instance-key_field = <delete>-key_field
deleted = abap_false
ASSIGNING FIELD-SYMBOL(<fs_del>).
IF sy-subrc = 0.
<fs_del>-changed = abap_false.
<fs_del>-deleted = abap_true.
ENDIF.
ENDLOOP.
ENDMETHOD.
METHOD rba_Child.
"Preparing the transactional buffers for both the root and child entity based on the input BDEF derived type.
lcl_buffer=>prep_root_buffer( CORRESPONDING #( keys_rba ) ).
lcl_buffer=>prep_child_buffer( CORRESPONDING #( keys_rba ) ).
"Processing requested keys sequentially
LOOP AT keys_rba ASSIGNING FIELD-SYMBOL(<rba>) GROUP BY <rba>-key_field.
"Logic:
"- Line with the shared key value exists in the buffer for the root entity and it is not marked as deleted
"- Line with the shared key value exists in the child buffer
"- If it is true: Sequentially processing the child buffer entries (the example is set up in a way that there can be multiple entries)
IF line_exists( lcl_buffer=>root_buffer[ instance-key_field = <rba>-key_field deleted = abap_false ] )
AND line_exists( lcl_buffer=>child_buffer[ instance-key_field = <rba>-key_field ] ).
LOOP AT lcl_buffer=>child_buffer ASSIGNING FIELD-SYMBOL(<ch>) WHERE instance-key_field = <rba>-key_field.
"Filling the table for the LINK parameter
INSERT VALUE #( source-%tky = <rba>-%tky
target-%tky = VALUE #( key_field = <ch>-instance-key_field
key_ch = <ch>-instance-key_ch ) ) INTO TABLE association_links.
"Filling the table for the RESULT parameter based on the FULL parameter
"Note: If the FULL parameter is initial, only the LINK parameter should be provided
IF result_requested = abap_true.
APPEND VALUE #( %tky = <rba>-%tky
key_ch = COND #( WHEN <rba>-%control-key_ch NE if_abap_behv=>mk-off
THEN <ch>-instance-key_ch )
field_ch1 = COND #( WHEN <rba>-%control-field_ch1 NE if_abap_behv=>mk-off
THEN <ch>-instance-field_ch1 )
field_ch2 = COND #( WHEN <rba>-%control-field_ch2 NE if_abap_behv=>mk-off
THEN <ch>-instance-field_ch2 )
) TO result.
ENDIF.
ENDLOOP.
ELSE.
"Filling FAILED and REPORTED response parameters
APPEND VALUE #( %tky = <rba>-%tky
%fail-cause = if_abap_behv=>cause-not_found
%assoc-_child = if_abap_behv=>mk-on
) TO failed-root.
APPEND VALUE #( %tky = <rba>-%tky
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = 'RBA (parent to child) operation failed.' )
) TO reported-root.
ENDIF.
ENDLOOP.
"Removing potential duplicate entries
SORT association_links BY target ASCENDING.
DELETE ADJACENT DUPLICATES FROM association_links COMPARING ALL FIELDS.
SORT result BY %tky ASCENDING.
DELETE ADJACENT DUPLICATES FROM result COMPARING ALL FIELDS.
ENDMETHOD.
METHOD cba_Child.
"Preparing the transactional buffers for both the root and child entity based on the input BDEF derived type
lcl_buffer=>prep_root_buffer( CORRESPONDING #( entities_cba ) ).
lcl_buffer=>prep_child_buffer( CORRESPONDING #( entities_cba ) ).
"Processing requested entities sequentially
LOOP AT entities_cba ASSIGNING FIELD-SYMBOL(<cba>) GROUP BY <cba>-key_field.
"Logic:
"- Line with the shared key value exists in the buffer for the root entity and it is not marked as deleted
"- If it is true: Sequentially processing the instances in the %target table
IF line_exists( lcl_buffer=>root_buffer[ instance-key_field = <cba>-key_field deleted = abap_false ] ).
LOOP AT <cba>-%target ASSIGNING FIELD-SYMBOL(<ch>).
"Adding instance to child buffer if it does not exist there and considering %control values
"The example is implemented in a way that the RAP BO consumer need not specify the common key with the root entity.
"Plus, the keys of the child entity should not be initial.
IF NOT line_exists( lcl_buffer=>child_buffer[ instance-key_field = <cba>-key_field
instance-key_ch = <ch>-key_ch ] )
AND <ch>-key_ch IS NOT INITIAL.
APPEND VALUE #( cid_ref = <cba>-%cid_ref
cid_target = <ch>-%cid
instance-key_field = <cba>-key_field
instance-key_ch = <ch>-key_ch
instance-field_ch1 = COND #( WHEN <ch>-%control-field_ch1 NE if_abap_behv=>mk-off
THEN <ch>-field_ch1 )
instance-field_ch2 = COND #( WHEN <ch>-%control-field_ch2 NE if_abap_behv=>mk-off
THEN <ch>-field_ch2 )
changed = abap_true
) TO lcl_buffer=>child_buffer.
"Filling MAPPED response parameter
INSERT VALUE #( %cid = <ch>-%cid
%key = VALUE #( key_field = <cba>-key_field
key_ch = <ch>-key_ch ) ) INTO TABLE mapped-child.
ELSE.
"Filling FAILED and REPORTED response parameters
APPEND VALUE #( %cid = <cba>-%cid_ref
%tky = <cba>-%tky
%assoc-_child = if_abap_behv=>mk-on
%fail-cause = if_abap_behv=>cause-unspecific
) TO failed-root.
APPEND VALUE #( %cid = <cba>-%cid_ref
%tky = <cba>-%tky
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = 'Create-by-association (root to child) operation failed.' )
) TO reported-root.
APPEND VALUE #( %cid = <ch>-%cid
%key = VALUE #( key_field = <cba>-key_field key_ch = <ch>-key_ch )
%fail-cause = if_abap_behv=>cause-dependency
) TO failed-child.
APPEND VALUE #( %cid = <ch>-%cid
%key = VALUE #( key_field = <cba>-key_field key_ch = <ch>-key_ch )
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = 'Create-by-association (root to child) operation failed.' )
) TO reported-child.
ENDIF.
ENDLOOP.
ELSE.
"Filling FAILED and REPORTED response parameters
APPEND VALUE #( %cid = <cba>-%cid_ref
%tky = <cba>-%tky
%assoc-_child = if_abap_behv=>mk-on
%fail-cause = if_abap_behv=>cause-not_found
) TO failed-root.
APPEND VALUE #( %cid = <cba>-%cid_ref
%tky = <cba>-%tky
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = 'Create-by-association (root to child) operation failed.' )
) TO reported-root.
LOOP AT <cba>-%target ASSIGNING FIELD-SYMBOL(<target>).
APPEND VALUE #( %cid = <target>-%cid
%key = <target>-%key
%fail-cause = if_abap_behv=>cause-dependency
) TO failed-child.
APPEND VALUE #( %cid = <target>-%cid
%key = <target>-%key
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = 'Create-by-association (root to child) operation failed.' )
) TO reported-child.
ENDLOOP.
ENDIF.
ENDLOOP.
ENDMETHOD.
METHOD multiply_by_2.
"Retrieving instances based on requested keys
READ ENTITIES OF zdemo_abap_rap_ro_u IN LOCAL MODE
ENTITY root
FIELDS ( field3 field4 ) WITH CORRESPONDING #( keys )
RESULT DATA(result)
FAILED failed.
"If read result is initial, stop further method execution.
CHECK result IS NOT INITIAL.
"Setting %action value in failed response parameter
LOOP AT failed-root ASSIGNING FIELD-SYMBOL(<f>).
<f>-%action-multiply_by_2 = if_abap_behv=>mk-on.
ENDLOOP.
"Multiply integer values by 2
MODIFY ENTITIES OF zdemo_abap_rap_ro_u IN LOCAL MODE
ENTITY root
UPDATE FIELDS ( field3 field4 ) WITH VALUE #( FOR key IN result ( %tky = key-%tky
field3 = key-field3 * 2
field4 = key-field4 * 2 ) ).
ENDMETHOD.
METHOD get_instance_authorizations.
"Retrieving instances based on requested keys
READ ENTITIES OF zdemo_abap_rap_ro_u IN LOCAL MODE
ENTITY root
FIELDS ( field1 ) WITH CORRESPONDING #( keys )
RESULT DATA(status)
FAILED failed.
"If the read result is initial, stop further method execution.
CHECK status IS NOT INITIAL.
LOOP AT status ASSIGNING FIELD-SYMBOL(<auth>).
"If a specific field has a certain value, the deletion should be disallowed.
IF requested_authorizations-%delete = if_abap_behv=>mk-on.
APPEND VALUE #( %tky = <auth>-%tky
%op = VALUE #( %delete = COND #( WHEN <auth>-field1 = 'X'
THEN if_abap_behv=>auth-unauthorized
ELSE if_abap_behv=>auth-allowed ) )
) TO result.
ENDIF.
ENDLOOP.
ENDMETHOD.
METHOD get_instance_features.
READ ENTITIES OF zdemo_abap_rap_ro_u IN LOCAL MODE
ENTITY root
FIELDS ( field3 field4 ) WITH CORRESPONDING #( keys )
RESULT DATA(numbers)
FAILED failed.
"If the read result is initial, stop further method execution.
CHECK numbers IS NOT INITIAL.
LOOP AT numbers ASSIGNING FIELD-SYMBOL(<feat>).
"If two fields have certain values, the execution of an action should be disabled for the instance.
IF requested_features-%action-multiply_by_3 = if_abap_behv=>mk-on.
APPEND VALUE #( %tky = <feat>-%tky
%features-%action-multiply_by_3 = COND #( WHEN <feat>-field3 = 0 OR <feat>-field4 = 0
THEN if_abap_behv=>fc-o-disabled
ELSE if_abap_behv=>fc-o-enabled )
) TO result.
ENDIF.
ENDLOOP.
ENDMETHOD.
METHOD multiply_by_3.
"Retrieving instances based on requested keys
READ ENTITIES OF zdemo_abap_rap_ro_u IN LOCAL MODE
ENTITY root
FIELDS ( field3 field4 ) WITH CORRESPONDING #( keys )
RESULT DATA(result)
FAILED failed.
"Setting %action value in failed response parameter
LOOP AT failed-root ASSIGNING FIELD-SYMBOL(<f>).
<f>-%action-multiply_by_3 = if_abap_behv=>mk-on.
ENDLOOP.
"Multiply integer values by 3
MODIFY ENTITIES OF zdemo_abap_rap_ro_u IN LOCAL MODE
ENTITY root
UPDATE FIELDS ( field3 field4 ) WITH VALUE #( FOR key IN result ( %tky = key-%tky
field3 = key-field3 * 3
field4 = key-field4 * 3 ) ).
ENDMETHOD.
METHOD get_global_features.
"The execution of an action should be disabled based on a certain time frame.
DATA(time1) = CONV t( '070000' ).
DATA(time2) = CONV t( '120000' ).
result = VALUE #( %action-set_z = COND #( WHEN cl_abap_context_info=>get_system_time( ) BETWEEN time1 AND time2
THEN if_abap_behv=>fc-o-enabled
ELSE if_abap_behv=>fc-o-disabled )
).
IF result-%action-set_z = if_abap_behv=>fc-o-disabled.
APPEND VALUE #( %msg = new_message_with_text( text = 'Execution of action currently not allowed.'
severity = if_abap_behv_message=>severity-error )
%global = if_abap_behv=>mk-on ) TO reported-root.
ENDIF.
ENDMETHOD.
METHOD set_z.
"Retrieving instances based on requested keys
READ ENTITIES OF zdemo_abap_rap_ro_u IN LOCAL MODE
ENTITY root
FIELDS ( field3 field4 ) WITH CORRESPONDING #( keys )
RESULT DATA(result)
FAILED failed.
"Setting %action value in failed response parameter
LOOP AT failed-root ASSIGNING FIELD-SYMBOL(<f>).
<f>-%action-set_z = if_abap_behv=>mk-on.
ENDLOOP.
"Setting a field value
MODIFY ENTITIES OF zdemo_abap_rap_ro_u IN LOCAL MODE
ENTITY root
UPDATE FIELDS ( field2 ) WITH VALUE #( FOR key IN result ( %tky = key-%tky
field2 = 'Z' ) ).
ENDMETHOD.
ENDCLASS.
***********************************************************************
* Local saver class lsc_zdemo_abap_rap_ro_u
*
* Contains saver method definitions and implementations. The only
* methods that are implemented in this example are the save method (to
* persist the data to the database) and cleanup methods (to clear the
* transactional buffer).
*
* The save method is implemented in a way, that the database tables
* are modified based on the entries in the transactional buffer tables
* and the flags for changed and deleted.
* If the flag for changed is not initial, the instance gets created
* or updated respectively in the database table.
* If the flag for deleted is not initial, the database table entry
* is deleted.
*
***********************************************************************
CLASS lsc_zdemo_abap_rap_ro_u DEFINITION INHERITING FROM cl_abap_behavior_saver.
PROTECTED SECTION.
METHODS finalize REDEFINITION.
METHODS check_before_save REDEFINITION.
METHODS save REDEFINITION.
METHODS cleanup REDEFINITION.
METHODS cleanup_finalize REDEFINITION.
ENDCLASS.
CLASS lsc_zdemo_abap_rap_ro_u IMPLEMENTATION.
METHOD finalize.
ENDMETHOD.
METHOD check_before_save.
ENDMETHOD.
METHOD save.
"Processing the saving of create and update operations
"Only those entries should be saved to the database table whose flag for "changed" is not initial.
DATA mod_tab TYPE TABLE OF zdemo_abap_rap_ro_u.
IF line_exists( lcl_buffer=>root_buffer[ changed = abap_true ] ).
LOOP AT lcl_buffer=>root_buffer ASSIGNING FIELD-SYMBOL(<cr>) WHERE changed = abap_true AND deleted = abap_false.
APPEND CORRESPONDING #( <cr>-instance ) TO mod_tab.
ENDLOOP.
MODIFY zdemo_abap_rapt1 FROM TABLE @( CORRESPONDING #( mod_tab ) ).
ENDIF.
"Processing the saving of delete operations
"Only those entries should be deleted from the database table whose flag "deleted" is not initial.
DATA del_tab TYPE lcl_buffer=>tt_root_keys.
IF line_exists( lcl_buffer=>root_buffer[ deleted = abap_true ] ).
LOOP AT lcl_buffer=>root_buffer ASSIGNING FIELD-SYMBOL(<del>) WHERE deleted = abap_true.
APPEND CORRESPONDING #( <del>-instance ) TO del_tab.
ENDLOOP.
DELETE zdemo_abap_rapt1 FROM TABLE @( CORRESPONDING #( del_tab ) ).
ENDIF.
"Processing the saving of create-by-association operations.
DATA cba_tab TYPE TABLE OF zdemo_abap_rap_ch_u.
IF line_exists( lcl_buffer=>child_buffer[ changed = abap_true ] ).
LOOP AT lcl_buffer=>child_buffer ASSIGNING FIELD-SYMBOL(<cba>) WHERE changed = abap_true.
APPEND CORRESPONDING #( <cba>-instance ) TO cba_tab.
ENDLOOP.
MODIFY zdemo_abap_rapt2 FROM TABLE @( CORRESPONDING #( cba_tab ) ).
ENDIF.
ENDMETHOD.
METHOD cleanup.
"Clearing the transactional buffer.
CLEAR lcl_buffer=>root_buffer.
CLEAR lcl_buffer=>child_buffer.
ENDMETHOD.
METHOD cleanup_finalize.
ENDMETHOD.
ENDCLASS.
***********************************************************************
* Local handler class for the child entity lhc_child
*
* Contains handler method definitions and implementations for read
* and read-by-association operations.
*
***********************************************************************
CLASS lhc_child DEFINITION INHERITING FROM cl_abap_behavior_handler.
PRIVATE SECTION.
METHODS read FOR READ
IMPORTING keys FOR READ child RESULT result.
METHODS rba_Parent FOR READ
IMPORTING keys_rba FOR READ child\_Parent FULL result_requested RESULT result LINK association_links.
ENDCLASS.
CLASS lhc_child IMPLEMENTATION.
METHOD read.
"Preparing the transactional buffer for child entity based on the input BDEF derived type.
"Here, the full_key flag is set to consider all key values.
"Purpose: The preparation method is set up to also consider the entries in the root buffer
"when dealing with by-association operations which is not relevant in this case.
lcl_buffer=>prep_child_buffer( VALUE #( FOR wa IN keys ( key_field = wa-key_field
key_ch = wa-key_ch
full_key = abap_true ) ) ).
"Processing the requested keys sequentially
LOOP AT keys ASSIGNING FIELD-SYMBOL(<read>) GROUP BY <read>-%tky.
"Logic:
"- Line with the requested key values exists in the child buffer
"- If it is true: Adding the line to the RESULT parameter considering %control values.
READ TABLE lcl_buffer=>child_buffer
WITH KEY instance-key_field = <read>-key_field
instance-key_ch = <read>-key_ch
ASSIGNING FIELD-SYMBOL(<fs_rc>).
IF sy-subrc = 0.
APPEND VALUE #( %tky = <read>-%tky
field_ch1 = COND #( WHEN <read>-%control-field_ch1 NE if_abap_behv=>mk-off
THEN <fs_rc>-instance-field_ch1 )
field_ch2 = COND #( WHEN <read>-%control-field_ch2 NE if_abap_behv=>mk-off
THEN <fs_rc>-instance-field_ch2 )
) TO result.
ELSE.
"Filling FAILED and REPORTED response parameters
APPEND VALUE #( %tky = <read>-%tky
%fail-cause = if_abap_behv=>cause-not_found
) TO failed-child.
APPEND VALUE #( %tky = <read>-%tky
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = 'Read operation failed (child entity).' )
) TO reported-child.
ENDIF.
ENDLOOP.
ENDMETHOD.
METHOD rba_Parent.
"Preparing the transactional buffers for both the root and child entity based on the input BDEF derived type.
lcl_buffer=>prep_root_buffer( CORRESPONDING #( keys_rba ) ).
lcl_buffer=>prep_child_buffer( CORRESPONDING #( keys_rba ) ).
LOOP AT keys_rba ASSIGNING FIELD-SYMBOL(<rba>) GROUP BY <rba>-%tky.
"Logic:
"- Line with the shared key value exists in buffer for the root entity and is not marked as deleted
"- Line with the full key exists in the child buffer
"- If it is true: Adding the instance to the RESULT parameter considering %control values
IF line_exists( lcl_buffer=>root_buffer[ instance-key_field = <rba>-key_field deleted = abap_false ] )
AND line_exists( lcl_buffer=>child_buffer[ instance-key_field = <rba>-key_field instance-key_ch = <rba>-key_ch ] ).
"Filling the LINK parameter
INSERT VALUE #( target-%tky = <rba>-%tky
source-%tky = VALUE #( key_field = <rba>-key_field
key_ch = <rba>-key_ch )
) INTO TABLE association_links.
IF result_requested = abap_true.
READ TABLE lcl_buffer=>root_buffer
WITH KEY instance-key_field = <rba>-key_field
ASSIGNING FIELD-SYMBOL(<fs_rp>).
IF sy-subrc = 0.
APPEND VALUE #( %tky = <rba>-%tky
field1 = COND #( WHEN <rba>-%control-field1 NE if_abap_behv=>mk-off
THEN <fs_rp>-instance-field1 )
field2 = COND #( WHEN <rba>-%control-field2 NE if_abap_behv=>mk-off
THEN <fs_rp>-instance-field2 )
field3 = COND #( WHEN <rba>-%control-field3 NE if_abap_behv=>mk-off
THEN <fs_rp>-instance-field3 )
field4 = COND #( WHEN <rba>-%control-field4 NE if_abap_behv=>mk-off
THEN <fs_rp>-instance-field4 )
) TO result.
ENDIF.
ENDIF.
ELSE.
"Filling FAILED and REPORTED response parameters
APPEND VALUE #( %tky = <rba>-%tky
%assoc-_parent = if_abap_behv=>mk-on
%fail-cause = if_abap_behv=>cause-not_found
) TO failed-child.
APPEND VALUE #( %tky = <rba>-%tky
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = 'RBA (child to parent) failed.' )
) TO reported-child.
ENDIF.
ENDLOOP.
"Removing potential duplicate entries.
SORT association_links BY target ASCENDING.
DELETE ADJACENT DUPLICATES FROM association_links COMPARING ALL FIELDS.
SORT result BY %tky ASCENDING.
DELETE ADJACENT DUPLICATES FROM result COMPARING ALL FIELDS.
ENDMETHOD.
ENDCLASS.