Méthode publique · ORM_CSHX2 · Persistance

mth_Enregistrer

Persiste l'objet courant en base. La méthode détermine automatiquement l'opération SQL à effectuer (INSERT, UPDATE ou DELETE) en fonction de l'état de la clé primaire de l'objet — un seul appel pour toutes les opérations d'écriture.

PUBLIQUE INSERT UPDATE DELETE
01

📋 Description

mth_Enregistrer est la méthode universelle de persistance d'ORM_CSHX2. Elle persiste un objet en base sans que l'appelant ait à choisir explicitement entre INSERT, UPDATE ou DELETE : la valeur de la clé primaire de l'objet détermine l'opération à effectuer.

Le tableau suivant résume cette logique de bascule :

État de la PKOpérationEffet
= 0INSERTCréation d'un nouvel enregistrement. La valeur de la PK est attribuée automatiquement par le moteur SQL (auto-incrément).
> 0UPDATEMise à jour de l'enregistrement existant ayant cet identifiant.
< 0DELETESuppression de l'enregistrement dont l'identifiant vaut Abs(PK). Comportement métier équivalent à mth_EffacerSelonID : cascade applicative, traductions, archivage trashcan, gestion automatique de transaction. Voir §03 pour la nuance sur l'état de l'instance après DELETE.
🎯 Convention de signe — pourquoi c'est utile

Cette convention permet d'unifier les opérations CRUD en un seul point d'entrée. Particulièrement précieux dans les flux où la liste des modifications à appliquer est construite progressivement (formulaire de saisie, batch d'import, synchronisation…) : le code appelant n'a pas à dispatcher entre trois méthodes différentes, il suffit de positionner correctement la PK.

C'est aussi ce qui rend mth_EnregistrerTableau capable de traiter, dans un même tableau, un mélange d'INSERT, d'UPDATE et de DELETE en une seule passe.

02

🔑 Signature

Déclaration
PROCÉDURE mth_Enregistrer() : (booléen, entier, chaîne, entier)
ÉlémentValeur
VisibilitéPUBLIQUE
Paramètresaucun
ℹ️ Pas de paramètre — l'état de l'objet est l'entrée

La méthode opère sur l'objet courant : ses membres mappés en base contiennent les valeurs à persister, et la PK détermine l'opération. Aucun paramètre à passer.

03

🔁 Comportement selon l'état de la PK

PK = 0 → INSERT

Cas du nouvel enregistrement. L'objet vient d'être instancié, ou a été remis à zéro via mth_RAZ. La PK n'a pas encore de valeur en base.

La méthode insère un nouveau tuple. Le moteur SQL attribue alors un nouvel identifiant via le mécanisme d'auto-incrément, et cet identifiant est automatiquement répercuté sur l'objet et retourné dans le 4ᵉ élément du résultat (nID).

// ── Création d'un nouveau client ──────────────────────────────── clClient est un Client // PK = 0 par défaut clClient:m_DENOMINATION = "Acme Corporation" clClient:m_VILLE = "Paris" (bProcessing, nErrorCode, sErrorMessage, nID) = clClient:mth_Enregistrer() // nID contient le nouvel identifiant attribué par la base // clClient:m_ID_CLIENT vaut désormais nID

PK > 0 → UPDATE

Cas de la modification. L'objet a été chargé précédemment (via mth_ChargerSelonID ou mth_ChargerSelonClauseWhere) ou sa PK a été positionnée explicitement.

La méthode met à jour le tuple correspondant en base. Toutes les colonnes mappées sont mises à jour avec les valeurs courantes des membres de l'objet.

// ── Modification d'un client existant ─────────────────────────── clClient:mth_ChargerSelonID(42) // PK = 42 après chargement clClient:m_VILLE = "Lyon" // modification d'un membre (bProcessing, nErrorCode, sErrorMessage, nID) = clClient:mth_Enregistrer() // nID = 42 (inchangé) en cas de succès
⚠️ Effet de p_sClauseSelect lors d'un UPDATE

Si l'objet a été chargé avec un p_sClauseSelect limitant la projection (ex : "clients.NOM"), seules les colonnes effectivement chargées seront mises à jour lors de l'UPDATE. Les autres colonnes restent inchangées en base, ce qui évite d'écraser des valeurs avec des chaînes vides ou des zéros par défaut.

Pour modifier d'autres colonnes que celles initialement chargées, recharger d'abord l'objet sans p_sClauseSelect (ou avec une liste élargie).

PK < 0 → DELETE

Cas de la suppression. La PK a été positionnée à une valeur négative pour signaler l'opération. L'identifiant réel à supprimer est Abs(PK).

Le comportement métier est équivalent à un appel direct à mth_EffacerSelonID(Abs(PK)) : cascade applicative selon l'analyse, suppression des traductions, archivage dans cshx2_trashcan, gestion automatique de transaction. La suppression est irréversible.

// ── Suppression via convention de signe ───────────────────────── clClient:mth_ChargerSelonID(42) // PK = 42 clClient:m_ID_CLIENT = -clClient:m_ID_CLIENT // PK = -42 (bProcessing, nErrorCode, sErrorMessage, nID) = clClient:mth_Enregistrer() // L'enregistrement n°42 et ses dépendances ont été supprimés // nID = 42 en cas de succès (Abs de la PK supprimée)
💡 Quand utiliser PK<0 plutôt que mth_EffacerSelonID

Pour une suppression isolée et explicite, mth_EffacerSelonID reste plus expressif et plus lisible.

La convention PK<0 prend tout son sens dans les flux unifiés où l'appelant traite une liste hétérogène d'opérations sans connaître à l'avance leur nature : formulaire de saisie multi-action, traitement d'un import, dans mth_EnregistrerTableau qui peut traiter INSERT, UPDATE et DELETE en un seul lot.

⚠️ Nuance par rapport à mth_EffacerSelonID — état de l'instance après DELETE

Si le résultat côté base de données est identique à mth_EffacerSelonID(Abs(PK)), l'état de l'instance en mémoire diverge légèrement après la suppression :

mth_EffacerSelonID réinitialise les membres mappés de l'instance (équivalent à un appel à mth_RAZ) puis pose p_bRecordDeleted = Vrai.

mth_Enregistrer avec PK<0 ne réinitialise pas les membres mappés — l'instance conserve les anciennes valeurs en mémoire — mais pose également p_bRecordDeleted = Vrai (alias français : p_bEnregistrementSupprimé).

Dans les deux cas, le drapeau p_bRecordDeleted permet aux méthodes d'agrégat (mth_SommeValeurs, mth_ValeurMoyenne, etc.) d'exclure les éléments supprimés du calcul. C'est cette différence de RAZ qui justifie de privilégier mth_EffacerSelonID pour les suppressions isolées et la convention PK<0 pour les flux unifiés (notamment mth_EnregistrerTableau).

04

📤 Valeur de retour

Quadruplet ORM élargi (bProcessing, nErrorCode, sErrorMessage, nID) :

ÉlémentTypeDescription
bProcessingbooléenVrai si l'enregistrement SQL s'est bien déroulé, Faux sinon.
nErrorCodeentier0 en cas de succès, code d'erreur négatif sinon.
sErrorMessagechaîneMessage d'erreur lisible en cas d'échec, vide sinon.
nIDentierIdentifiant de l'enregistrement traité — voir détails ci-dessous.

Convention sur nID

CasValeur de nID
Succès — INSERTNouvel identifiant attribué par la base (auto-incrément). Toujours positif.
Succès — UPDATEL'identifiant de l'enregistrement modifié (PK inchangée).
Succès — DELETEAbs(PK) — l'identifiant qui a été supprimé.
Échec SQL-1
✅ Récupération de l'ID après INSERT

Le 4ᵉ élément du retour est particulièrement utile après un INSERT pour récupérer l'identifiant nouvellement attribué :

(bProcessing, nErrorCode, sErrorMessage, nID) = clClient:mth_Enregistrer()
SI bProcessing ALORS
    // Utiliser nID pour les enregistrements liés ou la navigation
    clCommande:m_ID_CLIENT = nID
FIN

L'ID est aussi automatiquement répercuté sur la PK de l'objet courant — les deux valeurs sont équivalentes après l'appel.

ℹ️ Convention -1 pour signaler l'échec

En cas d'échec SQL, nID = -1 indique sans ambiguïté l'absence d'enregistrement valide en base. Tester bProcessing reste la façon canonique de détecter l'échec ; nID = -1 est une commodité pour les contextes où seul l'ID est manipulé.

05

⚠️ Gestion des erreurs

Les codes d'erreur retournés dépendent de l'opération effectuée :

En cas d'INSERT ou UPDATE

CodeConstanteCondition
-999ERR_SQL_INVALID_REQUESTErreur SQL générique (contrainte d'intégrité, type incompatible, valeur trop longue, etc.). Le détail est dans sErrorMessage.

En cas de DELETE (PK<0)

Mêmes codes que mth_EffacerSelonID, puisque le mécanisme est identique :

CodeConstanteCondition
-23099ERR_ORM_DELETE_VERROUILLEL'enregistrement à supprimer est verrouillé par un autre poste / utilisateur.
-23098ERR_ORM_DELETE_PROVIDERProvider SQL non géré pour la suppression de l'enregistrement principal.
-23097ERR_ORM_DELETE_TRAD_VERROUILLEUne traduction associée est verrouillée par un autre poste.
-23096ERR_ORM_DELETE_TRAD_PROVIDERProvider SQL non géré pour la suppression des traductions.
-999ERR_SQL_INVALID_REQUESTErreur SQL générique propagée par le moteur.
06

💡 Exemples

Mode 1 — création d'un nouvel enregistrement

// ── Création d'un nouveau client ──────────────────────────────── clClient est un Client clClient:m_DENOMINATION = "Acme Corporation" clClient:m_VILLE = "Paris" (bProcessing, nErrorCode, sErrorMessage, nID) = clClient:mth_Enregistrer() SI bProcessing ALORS Info("Client créé avec l'ID " + nID) SINON Erreur(sErrorMessage) FIN

Mode 2 — modification d'un enregistrement existant

// ── Charger, modifier, ré-enregistrer ─────────────────────────── (bProcessing, nErrorCode, sErrorMessage) = clClient:mth_ChargerSelonID(42) SI bProcessing ET clClient:p_nOccurrencesTrouvées = 1 ALORS clClient:m_VILLE = "Lyon" (bProcessing, nErrorCode, sErrorMessage,) = clClient:mth_Enregistrer() SI bProcessing = Faux ALORS Erreur(sErrorMessage) FIN FIN

Mode 3 — suppression via convention de signe

// ── Inverser le signe de la PK pour déclencher un DELETE ──────── clClient:mth_ChargerSelonID(42) clClient:m_ID_CLIENT = -clClient:m_ID_CLIENT (bProcessing, nErrorCode, sErrorMessage, nID) = clClient:mth_Enregistrer() // Équivalent strict à clClient:mth_EffacerSelonID(42)

Mode 4 — INSERT atomique avec enregistrements liés

Un cas courant : créer un client puis ses commandes liées. Si l'une des opérations échoue, on annule tout.

(bProcessing, nErrorCode, sErrorMessage) = ORM_TransactionBegin() SI bProcessing ALORS clClient:m_DENOMINATION = "Acme Corporation" (bProcessing, nErrorCode, sErrorMessage, nIDClient) = clClient:mth_Enregistrer() SI bProcessing ALORS clCommande:m_ID_CLIENT = nIDClient // nIDClient = nouvel ID auto-incrément clCommande:m_LIBELLE = "Première commande" (bProcessing, nErrorCode, sErrorMessage,) = clCommande:mth_Enregistrer() FIN SI bProcessing ALORS (bProcessing, nErrorCode, sErrorMessage) = ORM_TransactionCommit() SINON ORM_TransactionRollBack() FIN FIN

Mode 5 — diagnostic via mode verbose

Pour visualiser les requêtes SQL réellement exécutées (utile pour comprendre la différence INSERT/UPDATE, voir les colonnes mises à jour, etc.) :

ORM_VerboseMode(Vrai) clClient:mth_Enregistrer() ORM_VerboseMode(Faux)

Trace produite (cas INSERT) :

🛢️ INSERT INTO clients (DENOMINATION, VILLE, SQL_INSERTED, SQL_ID_USER_INSERT, SQL_UUID, ...) VALUES (...)

Trace produite (cas UPDATE) :

🛢️ UPDATE clients SET DENOMINATION = ..., VILLE = ..., SQL_UPDATED = ... WHERE ID_CLIENT = 42

Détails complets : ORM_VerboseMode.