Welcome back to the "Tasting the mix of PHP and SAP" weblog series. This time, we're going to develop an SM30 Simulator, which is the next logical step after having developed and Tasting the mix of PHP and SAP.
Remember that this app is just for testing purposes, don't implement this on a production system...unless you want to get fired.
Ok, now we can start. You may remember, in the firt volume of the series, I used RFC_READ_TABLE RFC in order to read the FIELDS and DATA from any table. It works fine, however I have two problems with it. You don't know which fields are primary keys and of course it's not released for customer use (see SAP Note 382318). Both of these are bad; for an SM30 the fact that I can't find the primary key is particularly bad. So, my first task was to modify the FM, by creating my own version in the "customer namespace" thus I also avoid using the one from SAP that could be changed, removed or what not at anytime. Here's the code:
ZRFC_READ_TABLE
FUNCTION ZRFC_READ_TABLE . *"----""Interfase local *" IMPORTING *" VALUE(QUERY_TABLE) LIKE DD02L-TABNAME *" VALUE(DELIMITER) LIKE SONV-FLAG DEFAULT SPACE *" VALUE(NO_DATA) LIKE SONV-FLAG DEFAULT SPACE *" VALUE(ROWSKIPS) LIKE SOID-ACCNT DEFAULT 0 *" VALUE(ROWCOUNT) LIKE SOID-ACCNT DEFAULT 0 *" TABLES *" OPTIONS STRUCTURE RFC_DB_OPT *" FIELDS STRUCTURE RFC_DB_FLD *" DATA STRUCTURE TAB512 *" KEYS STRUCTURE DD03P *" EXCEPTIONS *" TABLE_NOT_AVAILABLE *" TABLE_WITHOUT_DATA *" OPTION_NOT_VALID *" FIELD_NOT_VALID *" NOT_AUTHORIZED *" DATA_BUFFER_EXCEEDED *"---- CALL FUNCTION 'VIEW_AUTHORITY_CHECK' EXPORTING VIEW_ACTION = 'S' VIEW_NAME = QUERY_TABLE EXCEPTIONS NO_AUTHORITY = 2 NO_CLIENTINDEPENDENT_AUTHORITY = 2 NO_LINEDEPENDENT_AUTHORITY = 2 OTHERS = 1. IF SY-SUBRC = 2. RAISE NOT_AUTHORIZED. ELSEIF SY-SUBRC = 1. RAISE TABLE_NOT_AVAILABLE. ENDIF.
DATA BEGIN OF TABLE_STRUCTURE OCCURS 10. INCLUDE STRUCTURE DFIES. DATA END OF TABLE_STRUCTURE. "DATA TABLE_HEADER LIKE X030L. DATA TABLE_TYPE TYPE DD02V-TABCLASS. CALL FUNCTION 'DDIF_FIELDINFO_GET' EXPORTING TABNAME = QUERY_TABLE
DATA NUMBER_OF_FIELDS TYPE I. DESCRIBE TABLE FIELDS LINES NUMBER_OF_FIELDS. IF NUMBER_OF_FIELDS = 0. LOOP AT TABLE_STRUCTURE. MOVE TABLE_STRUCTURE-FIELDNAME TO FIELDS-FIELDNAME. APPEND FIELDS. ENDLOOP. ENDIF.
DATA: BEGIN OF FIELDS_INT OCCURS 10, FIELDNAME LIKE TABLE_STRUCTURE-FIELDNAME, TYPE LIKE TABLE_STRUCTURE-INTTYPE, DECIMALS LIKE TABLE_STRUCTURE-DECIMALS, LENGTH_SRC LIKE TABLE_STRUCTURE-INTLEN, LENGTH_DST LIKE TABLE_STRUCTURE-LENG, OFFSET_SRC LIKE TABLE_STRUCTURE-OFFSET, OFFSET_DST LIKE TABLE_STRUCTURE-OFFSET, END OF FIELDS_INT, LINE_CURSOR TYPE I. LINE_CURSOR = 0.
for each field which has to be read ...
LOOP AT FIELDS. READ TABLE TABLE_STRUCTURE WITH KEY FIELDNAME = FIELDS-FIELDNAME. IF SY-SUBRC NE 0. RAISE FIELD_NOT_VALID. ENDIF.
compute the place for field contents in DATA rows:
if not first field in row, allow space for delimiter
IF LINE_CURSOR <> 0. IF NO_DATA EQ SPACE AND DELIMITER NE SPACE. MOVE DELIMITER TO DATA+LINE_CURSOR. ENDIF. LINE_CURSOR = LINE_CURSOR + STRLEN( DELIMITER ). ENDIF.
... copy structure information into tables FIELDS_INT
output data only if NO_DATA equals space (otherwise the structure
information in FIELDS is the only result of the module)
IF NO_DATA EQ SPACE. DATA: BEGIN OF WORK, BUFFER(30000), END OF WORK. FIELD-SYMBOLS: WHERE (OPTIONS). IF SY-DBCNT GT ROWSKIPS.
copy all relevant fields into DATA (output) table
LOOP AT FIELDS_INT. IF FIELDS_INT-TYPE = 'P'. ASSIGN COMPONENT FIELDS_INT-FIELDNAME OF STRUCTURE +FIELDS_INT-OFFSET_DST(FIELDS_INT-LENGTH_DST). ENDLOOP.
end of loop at FIELDS_INT
APPEND DATA. IF ROWCOUNT > 0 AND SY-DBCNT GE ROWCOUNT. EXIT. ENDIF. ENDIF. ENDSELECT. ENDIF.
As you can see, I just add a new table named KEYS and called FM DDIF_TABL_GET. Now, we know which fields are primary keys.
Next thing we need is a custom FM that allows our application to INSERT, UPDATE or DELETE any record from ANY TABLE (be very careful here as many tables are cluster tables or otherwise complex). Sounds cool right? Yeah...it's really cool.
You can create the ZTABLE_OPERATION, just using an 255 characters domain data.
ZRFC_TABLE_OPERATIONS_LINE
FUNCTION ZRFC_TABLE_OPERATIONS_LINE. *"----""Interfase local *" IMPORTING *" VALUE(QUERY_TABLE) LIKE DD02L-TABNAME *" VALUE(OPERATION) LIKE BDLDATCOL-TYPE *" VALUE(FIELDS) LIKE ZTABLE_OPERATION-FIELDS *" VALUE(DATA) LIKE ZTABLE_OPERATION-DATA *" EXCEPTIONS *" RECORD_NOT_INSERTED *" RECORD_NOT_UPDATED *" RECORD_NOT_DELETED *" NOT_AUTHORIZED *" TABLE_NOT_AVAILABLE *"---- CALL FUNCTION 'VIEW_AUTHORITY_CHECK' EXPORTING VIEW_ACTION = 'S' VIEW_NAME = QUERY_TABLE EXCEPTIONS NO_AUTHORITY = 2 NO_CLIENTINDEPENDENT_AUTHORITY = 2 NO_LINEDEPENDENT_AUTHORITY = 2 OTHERS = 1. IF SY-SUBRC = 2. RAISE NOT_AUTHORIZED. ELSEIF SY-SUBRC = 1. RAISE TABLE_NOT_AVAILABLE. ENDIF. *=======================================================================
Types.
*======================================================================= TYPES: X_LINES TYPE STRING. *=======================================================================
Internal Tables.
*======================================================================= DATA: DESCR_STRUCT_REF TYPE REF TO CL_ABAP_STRUCTDESCR, DATAREF TYPE REF TO DATA, WA_FCAT TYPE LVC_S_FCAT, IT_FIELDCATALOG TYPE LVC_T_FCAT, T_LINES TYPE STANDARD TABLE OF X_LINES, T_FIELDS TYPE STANDARD TABLE OF X_LINES WITH HEADER LINE, NEW_LINE TYPE REF TO DATA. DATA: W_LINES TYPE STRING. *=======================================================================
Variables.
*======================================================================= DATA: W_TABIX LIKE SY-TABIX. *=======================================================================
*======================================================================= CREATE DATA DATAREF TYPE (QUERY_TABLE). ASSIGN DATAREF->* TO CREATE_DYNAMIC_TABLE EXPORTING IT_FIELDCATALOG = IT_FIELDCATALOG IMPORTING EP_TABLE = DATAREF.
EXCEPTIONS
GENERATE_SUBPOOL_DIR_FULL = 1
OTHERS = 2.
ASSIGN DATAREF->* TO . *=======================================================================
Fill the dynamic internal table.
*======================================================================= SPLIT DATA AT '/' INTO TABLE T_LINES. SPLIT FIELDS AT '/' INTO TABLE T_FIELDS. LOOP AT T_LINES ASSIGNING . *============================== ========================================
Perform task depending of the choose operation.
*======================================================================= CASE OPERATION. WHEN 'I'. MODIFY (QUERY_TABLE) FROM TABLE