Exploration des nouvelles syntaxes ABAP pour le développement d'applications modernes
L'évolution du langage ABAP intègre de nombreux éléments syntaxiques modernes, améliorant la lisibilité et la concision du code. Cet article passe en revue les fonctionnalités clés, avec des exemples pratiques pour les illustrer.
Génération de données avec VALUE # et FOR
La construction de structures et de tables internes peut être réalisée de manière déclarative. L'exemple suivant génère une table d'étudiants à l'aide d'une boucle et de l'opérateur VALUE.
*&---------------------------------------------------------------------*
*& Report ZNEW_SYNTAX_DEMO
*&---------------------------------------------------------------------*
REPORT znew_syntax_demo.
TYPES: BEGIN OF ty_student,
student_id TYPE n LENGTH 8,
full_name TYPE c LENGTH 30,
gender TYPE c LENGTH 4,
age TYPE i,
score TYPE p DECIMALS 2,
performance TYPE c LENGTH 10,
END OF ty_student.
DATA: gt_learners TYPE STANDARD TABLE OF ty_student WITH EMPTY KEY,
gs_learner TYPE ty_student.
* Génération de données de test de manière concise
gt_learners = VALUE #(
LET ln_prefix = 'Learner'
rnd_gen = cl_abap_random_int=>create( min = 0 max = 100 ) IN
FOR idx = 1 UNTIL idx > 8
( student_id = idx
full_name = |{ ln_prefix }_{ idx ALPHA = OUT }|
gender = COND #( WHEN idx MOD 2 = 0 THEN 'Male' ELSE 'Female' )
age = 15 + idx
score = rnd_gen->get_next( )
performance = COND #( WHEN rnd_gen->get_next( ) >= 50 THEN 'Pass' ELSE 'Fail' )
) ).
cl_demo_output=>display( gt_learners ).
Déclarations en ligne et accès aux données
Les déclarations en ligne (inline declarations) simplifient le code en déduisant le type au moment de l'affectation. Elles fonctionnent avec READ TABLE, LOOP AT, SELECT et les symboles de champ.
* Déclarations en ligne simples
DATA(lv_user_name) = 'Jean'.
DATA(lv_user_age) = 25.
DATA(lv_city_str) = CONV string( 'Paris' ).
* Utilisation dans READ TABLE
READ TABLE gt_learners INTO DATA(ls_first_record) INDEX 1.
* Utilisation dans LOOP
LOOP AT gt_learners ASSIGNING FIELD-SYMBOL(<fs_learner>).
" Traitement de chaque <fs_learner>
ENDLOOP.
* Accès direct aux données de la base
SELECT SINGLE carrname FROM scarr
WHERE carrid = 'LH'
INTO @DATA(lv_carrier_name).
* Requête avec plusieurs colonnes
SELECT carrid, carrname FROM scarr
INTO TABLE @DATA(lt_carriers_info)
UP TO 5 ROWS.
Expressions de table internes (Table Expressions)
Les expressions de table offrent un accès direct aux lignes par index ou par clé, avec des mécanismes pour gérer les lignes manquantes sans provoquer d'exceptions fatales.
* Accès par index et par clé (risque d'exception si non trouvé)
IF 1 = 0. " Bloc pour éviter l'exécution
DATA(ls_by_index) = gt_learners[ 1 ].
DATA(ls_by_key) = gt_learners[ student_id = '00000005' age = 20 ].
ENDIF.
* Méthodes pour éviter les exceptions
ASSIGN gt_learners[ student_id = '00000003' ] TO FIELD-SYMBOL(<fs_found>).
IF sy-subrc = 0.
" Ligne trouvée
ENDIF.
IF line_exists( gt_learners[ 2 ] ).
lv_user_name = gt_learners[ 2 ]-full_name.
ENDIF.
* Valeurs par défaut avec VALUE # et OPTIONAL
DATA(ls_safe_access) = VALUE #( gt_learners[ 99 ] OPTIONAL ).
DATA(ls_with_default) = VALUE #( gt_learners[ 99 ] DEFAULT VALUE #( student_id = '00000010' ) ).
* Gestion des exceptions avec TRY-CATCH
TRY.
DATA(ls_from_try) = gt_learners[ 99 ].
CATCH cx_sy_itab_line_not_found INTO DATA(lo_exception).
" Gestion de l'erreur
ENDTRY.
Opérateurs d'affectation modernes : VALUE et CORRESPONDING
L'opérateur VALUE initialise des variables, structures et tables. CORRESPONDING effectue un mapping de champs entre structures de types différents, avec l'option BASE pour préserver les données existantes.
* Initialisation avec VALUE
DATA(lv_counter) = VALUE i( ).
gs_learner = VALUE #( full_name = 'Marie' age = 28 ).
DATA(lt_new_learners) = VALUE ty_student_t( ( full_name = 'Luc' ) ( full_name = 'Sophie' ) ).
* CORRESPONDING pour mapper des structures
TYPES: BEGIN OF ty_person_info,
first_name TYPE c LENGTH 20,
last_name TYPE c LENGTH 20,
birth_year TYPE i,
END OF ty_person_info.
DATA: ls_source_info TYPE ty_person_info VALUE ( first_name = 'Alice' birth_year = 1995 ),
ls_target_info TYPE ty_learner.
* Mapping simple (les champs non mappés sont réinitialisés)
ls_target_info = CORRESPONDING #( ls_source_info ).
* Mapping avec BASE pour préserver les valeurs de la cible
DATA(ls_preserved) = VALUE ty_learner( full_name = 'OldName' age = 40 ).
ls_preserved = CORRESPONDING #( BASE ( ls_preserved ) ls_source_info ).
* Mapping avec MAPPING et EXCEPT pour un contrôle fin
DATA(ls_custom_mapping) = CORRESPONDING #( ls_source_info MAPPING student_id = birth_year EXCEPT birth_year ).
Expression conditionnelle : SWITCH et COND
Ces opérateurs permettent d'affecter des valeurs basées sur des conditions, de manière similaire à une instruction CASE ou à une expresssion IF.
* SWITCH pour un test simple sur une valeur
DATA(lv_gender_code) = 'F'.
DATA(lv_gender_text) = SWITCH string( lv_gender_code
WHEN 'M' THEN 'Homme'
WHEN 'F' THEN 'Femme'
ELSE 'Inconnu' ).
* COND pour des conditions complexes
DATA(lv_result_score) = 82.
DATA(lv_result_level) = COND string( WHEN lv_result_score >= 90 THEN 'Excellent'
WHEN lv_result_score >= 75 THEN 'Bien'
WHEN lv_result_score >= 60 THEN 'Suffisant'
ELSE 'Insuffisant' ).
Déclarations locales avec LET et boucles avec FOR
LET définit des variables locales au sein d'une expression complexe. FOR réalise des boucles, souvent pour construire des tables internes.
* Utilisation combinée de LET, VALUE et FOR
TYPES: BEGIN OF ty_product,
id TYPE i,
price TYPE p DECIMALS 2,
END OF ty_product.
DATA lt_products TYPE STANDARD TABLE OF ty_product WITH EMPTY KEY.
lt_products = VALUE #(
LET base_price = 100 IN
FOR i = 1 THEN i + 10 UNTIL i > 50
( id = i
price = base_price + i ) ).
* Filtrage d'une table avec FOR...WHERE
DATA(lt_expensive_products) = VALUE ty_product_t( FOR wa_product IN lt_products WHERE ( price > 120 ) ( wa_product ) ).
Opérateurs de transformation : CONV et REDUCE
CONV convertit explicitement des types. REDUCE agrège les valeurs d'une table pour produire un résultat unique (somme, comptage, texte concaténé...).
* Conversion de type avec CONV
DATA(lv_numeric_str) = '12345'.
DATA(lv_as_integer) = CONV i( lv_numeric_str ).
* Agrégation avec REDUCE pour compter et sommer
DATA(lv_total_learners) = REDUCE i( INIT cnt = 0
FOR wa IN gt_learners
NEXT cnt = cnt + 1 ).
DATA(lv_total_age) = REDUCE i( INIT sum_age = 0
FOR wa IN gt_learners
NEXT sum_age = sum_age + wa-age ).
* Construction d'une chaîne agrégée
DATA(lv_summary) = REDUCE string( INIT txt = ``
FOR wa IN gt_learners
NEXT txt = txt && wa-full_name && ` | ` ).
Filtrage avec FILTER
L'opérateur FILTER extrait les lignes d'une table interne qui correspondent à une condition, créant une nouvelle table.
* Définition d'une table triée pour l'exemple
DATA gt_males TYPE STANDARD TABLE OF ty_student WITH EMPTY KEY.
DATA gt_females TYPE STANDARD TABLE OF ty_student WITH EMPTY KEY.
* Filtrage basique
gt_males = FILTER #( gt_learners WHERE gender = 'Male' ).
gt_females = FILTER #( gt_learners WHERE gender = 'Female' ).
Regroupement avec GROUP BY
La boucle LOOP AT avec GROUP BY permet de trraiter des sous-ensembles de données regroupées selon un ou plusieurs critères.
* Regroupement par genre pour traitement séparé
LOOP AT gt_learners INTO DATA(ls_group_item)
GROUP BY ( gender = ls_group_item-gender ) INTO DATA(ls_group_key).
" Traitement pour chaque groupe (ex: ls_group_key-gender = 'Male')
LOOP AT GROUP ls_group_key ASSIGNING FIELD-SYMBOL(<fs_member>).
" Traitement de chaque membre du groupe
ENDLOOP.
ENDLOOP.
Découpe de chaînes avec SEGMENT
La fonction SEGMENT extrait une partie d'une chaîne en fonction d'un délimiteur et d'un index.
DATA lv_full_date TYPE string VALUE '2023-12-31'.
* Extraction du mois
DATA(lv_month) = segment( val = lv_full_date index = 2 sep = '-' ).
* Boucle pour extraire tous les segments
DATA lv_index TYPE i VALUE 1.
WHILE lv_index > 0.
TRY.
DATA(lv_part) = segment( val = lv_full_date index = lv_index sep = '-' ).
lv_index = lv_index + 1.
CATCH cx_sy_strg_par_val.
lv_index = 0. " Fin de la chaîne
ENDTRY.
ENDWHILE.
Évolutions de l'Open SQL
Les requêtes SQL modernes dans ABAP supportent les expressions, les fonctions et les expressions de table dans la clause FROM, simplifiant le code d'accès aux données.
* Sélection avec expressions dans la liste des colonnes
SELECT carrid,
carrname,
'Aucun' AS country_default,
@( sy-datum ) AS today
FROM scarr
WHERE carrid LIKE 'L%'
INTO TABLE @DATA(lt_sql_results).
* Utilisation de fonctions SQL pour le traitement de chaînes
SELECT id,
name,
concat( id , '_' ) AS full_key,
upper( name ) AS name_upper
FROM zsome_table
INTO TABLE @DATA(lt_processed_data).
* Utilisation de fonctions SQL pour les dates
SELECT id,
dats_is_valid( date_column ) AS is_valid,
dats_days_between( date_column, @( sy-datum ) ) AS age_in_days
FROM zdate_table
INTO TABLE @DATA(lt_date_info).
* Utilisation de fonctions mathématiques
SELECT id,
score,
abs( score - 50 ) AS deviation,
round( score, 1 ) AS rounded_score
FROM zscore_table
INTO TABLE @DATA(lt_math_data).
Ces nouvelles constructions syntaxiques rendent le développement ABAP plus expressif, plus robuste et plus facile à maintenir, alignant le langage sur les standards modernes de programmation.