Perform Save-operations using a wire transaction handler in the FPM
In the previous blog post I figured out how wiring based on business objects might work in the Floorplan manager. The example application provides a Search component to select records based on the table SFLIGHT. Once a result entry has been selected, the flight data record is shown in a detail view below the result list. While the result list is implemented by a List-UIBB, the detail view is implemented by a Form-UIBB.
Any follow-up steps have not been implemented then, this includes also the possibility to save the edited record back to the database.
The subject of this blog post is how a Save-operation could be implemented based on a wire transaction handler.
I decided to not use the interface IF_FPM_TRANSACTION which can be implemented by any UIBB in the application, rather, I decided to use a Transaction Handler for wire models. The drawback of this approach is that a wire transaction handler cannot serve as a target for wires, hence, has usually no access to Business objects.
One opportunity to pass the business object to the handler might be to share it as a singleton instance. This requires the existence of a registry or any other singleton mechanism. Another possibility is the data exchange based on events.
In this very blog post, the business object is going to be shared as a parameter of the FPM_SAVE- event.
For now, let’s again recap the wires which are in use by the demo application. However besides this introduction it is strongly recommended to read the previous blog post if you did not yet read it.
The existing application
The selection criteria and the selection of the flight list is implemented by the list selection wire (ZCL_FPM_DEMO_WIRE_FLIGHT_SEL which implements ZIF_FPM_DEMOWIRE_SFLIGHT_SEL).
The selected flight data record is represented by an instance of type ZIF_FPM_DEMO_WIRE_SFLIGHT.
Both wires have specific operations offered to the UIBBs or OVP Exit which use these wires.
In this blog post, the first wire is going to be extended to hold a lead selection instance of the second wire. The second wire which represents a single flight data record will be enhanced with a Save-operation. In combination with some other changes, this allows the user to edit the flight data and save it back to the database.
Sharing the business object
A new Save-Button will be added to the application’s OVP toolbar.
In order to allow the wire transaction handler to access the business object, the OVP exit will catch the Save-Event which is triggered by the save- button and will enrich the event data with a new Name-Value Pair which represents an instance of a saveable business object, that is, the wire which represents a single entry of the flight data list (It would be a good idea to work with an abstraction to the specific flight data instance as well, so we can possibly reuse the wire transaction handler later on in another application)
Since the OVP exit has access to the list selection instance only we need to provide access to the selected business object through the list selection wire.
Enhancements to the current application
First of all, we include an OVP-toolbar button to allow the user to trigger the Save-operation.
The list selection wire will need to store the selected flight record to allow the OVP Exit to get access to the currently selected record. Therefore the methods GET_LEAD_SELECTION and SET_LEAD_SELECTION are added to the interface ZIF_FPM_DEMO_WIRE_FLIGHT_SEL which describes, what operations have to be supported by this wire. In the implementing class, a new attribute MO_LEAD_SELECTION is included and the corresponding Getter- and Setter method are implemented.
Now we need to call SET_LEAD_SELECTION in ZCL_FPM_DEMO_FLIGHT_LIST, method IF_FPM_GUIBB_LIST~PROCESS_EVENT, which handles the lead selection event in the list.
METHOD if_fpm_guibb_list~process_event. DATA lo_datacontainer TYPE REF TO zif_fpm_demo_wire_flight_sel … lo_datacontainer ?= mo_connector->get_output( ). CASE io_event->mv_event_id. … WHEN 'FPM_GUIBB_LIST_ON_LEAD_SELECTI'. CHECK lo_datacontainer IS NOT INITIAL. READ TABLE mt_sflight INTO ls_sflight INDEX iv_lead_index. IF sy-subrc = 0. CREATE OBJECT mo_sflight TYPE zcl_fpm_demo_wire_sflight. mo_sflight->set_sflight( ls_sflight ). lo_datacontainer->set_lead_selection( mo_sflight ). ENDIF. ENDCASE. ENDMETHOD.
The selected flight record is show in the detail section of the screen. Its feeder does not yet have a FLUSH-implementation which needs to be implemented. Its purpose would be to transfer the entered values back to the flight record instance.
Therefore the form feeder class ZCL_FPM_DEMO_FORM_SFLIGHT_DET receives a FLUSH-method implementation:
METHOD if_fpm_guibb_form~flush. CHECK mo_connector IS NOT INITIAL. mo_sflight ?= mo_connector->get_output( ). CHECK mo_sflight IS NOT INITIAL. FIELD-SYMBOLS <ls_data> TYPE any. ASSIGN is_data->* TO <ls_data>. DATA ls_sflight_key LIKE ms_sflight. MOVE-CORRESPONDING <ls_data> TO ms_sflight. MOVE-CORRESPONDING <ls_data> TO ls_sflight_key. ms_sflight-carrid = ls_sflight_key-carrid. ms_sflight-connid = ls_sflight_key-connid. ms_sflight-fldate = ls_sflight_key-fldate. mo_sflight->set_sflight( ms_sflight ). ENDMETHOD.
Now, the OVP exit has also access to the currently selected flight record instance through its configured list selection wire. The OVP exit is implemented by the webdynpro component ZWDYN_FPM_FLIGHT_OVP_EXIT. The implementation of method OVERRIDE_EVENT_OVP now needs to try to read the flight record instance from the wire model and enrich the Save-Event with the flight record data object. Please note that we pass an instance of type ZIF_FPM_DEMO_SAVEABLE rather than ZIF_FPM_DEMO_WIRE_SFLIGHT.
METHOD override_event_ovp. wd_this->mo_ovp = io_ovp. DATA lo_event TYPE REF TO cl_fpm_event. DATA: lo_datacontainer TYPE REF TO zif_fpm_demo_wire_flight_sel, lo_saveable TYPE REF TO zif_fpm_demo_saveable. DATA lv_port_identifier TYPE fpm_model_port_identifier. lv_port_identifier = zif_fpm_demo_wire_flight_sel=>gc_name. lo_event = io_ovp->get_event( ). CASE lo_event->mv_event_id. WHEN if_fpm_constants=>gc_event-save OR if_fpm_constants=>gc_event-save_and_back_to_main. TRY. lo_datacontainer ?= wd_this->mo_feeder_model->get_outport_data( iv_port_type = 'LS' iv_port_identifier = lv_port_identifier ). CHECK lo_datacontainer IS BOUND. lo_saveable ?= lo_datacontainer->get_lead_selection( ). lo_event->mo_event_data->set_value( iv_key = zif_fpm_demo_saveable=>gc_event_parameter_id iv_value = lo_saveable ). CATCH cx_sy_move_cast_error. ENDTRY. ENDCASE. ENDMETHOD.
ZIF_FPM_DEMO_SAVEABLE serves as a generic interface for a save operation. It is implemented by the wire which represents the flight record (ZCL_FPM_DEMO_WIRE_SFLIGHT)
The class ZCL_FPM_DEMO_WIRE_MODEL_TRANS serves as the application wire transaction handler. It implements the standard FPM interface IF_FPM_WIRE_MODEL_TRANSACTION and therefore takes part in the event loop. The method AFTER_PROCESS_EVENT is implemented and handles the SAVE-Event which will later on be triggered once the Save-Button has been pressed. The method implementation tries to read an instance of type ZIF_FPM_DEMO_SAVEABLE from the event data and eventually call the SAVE-Method.
METHOD if_fpm_wire_model_transaction~after_process_event. DATA: lo_savable TYPE REF TO zif_fpm_demo_saveable. CASE io_event->mv_event_id. WHEN if_fpm_constants=>gc_event-save OR if_fpm_constants=>gc_event-save_and_back_to_main. TRY. io_event->mo_event_data->get_value( EXPORTING iv_key = zif_fpm_demo_saveable=>gc_event_parameter_id IMPORTING ev_value = lo_savable ). CHECK lo_savable IS NOT INITIAL. lo_savable->save( ). CATCH cx_sy_move_cast_error. ENDTRY. ENDCASE. ENDMETHOD.
Once the transaction handler class has been implemented, we register the transaction handler in the OVP-Component FPM_OVP_COMPONENT, Configuration ZFPM_DEMO_OVP_FLIGHT_SEARCH
Why should we not simply enhance the OVP-Exit?
The reason why I didn’t just implement the Save-operation directly in the OVP-Exit is the Separation of concerns design principle.
In the case of having an exit only and no transaction handler, the OVP Exit would have to evaluate the save event and trigger the SAVE-Method, besides its task of updating the OVP floorplan title with the number of results. Also, an OVP Exit is not reusable in OIF or GAF floorplans, and this would also apply to its transaction logic, if coupled too strongly.
Right now, with the current implementation, it only does, what an exit is intended to do: It catches certain events like the Save-event and enriches it with some data, if necessary. The actual SAVE-Method call is triggered by another component which actually does not care about how or by whom the event has been enriched – it just expects the business object along with the event data. This decouples the execution of the SAVE-Command from other aspects of the application and eventually leads to a better design and maintainability. There is no real benefit yet, but in case a more complex transaction logic would be needed, we could easily provide the implementation of these aspects, like commits or rollbacks, in a specific transaction handler, not within the OVP Exit.