mth_ChargerSelonClauseWhere
Charge en mémoire un ou plusieurs enregistrements depuis la base via une clause WHERE SQL, avec options avancées : ORDER BY, verrouillage pessimiste, LIMIT / OFFSET. Hydrate l'objet courant et le tableau interne m_tabResults.
📋 Description
mth_ChargerSelonClauseWhere est le point d'entrée principal pour tout chargement conditionnel d'objets ORM_CSHX2. La méthode construit dynamiquement la requête SELECT à partir des clauses positionnées sur l'objet, exécute la requête, puis hydrate les résultats dans le tableau interne m_tabResults (et l'objet courant pour le premier tuple).
Tous les paramètres sont optionnels et suivent un mécanisme de résolution en deux temps :
• Si un paramètre est laissé à sa valeur par défaut ("" pour les chaînes, 0 pour nLimit/nOFfSet, NoLock pour nTypeLock) → la méthode n'écrase pas le membre correspondant déjà positionné sur l'objet.
• Si un paramètre est renseigné → il est appliqué et écrit dans le membre.
Cela permet deux styles d'appel équivalents : tout passer en paramètres, ou pré-positionner les membres puis appeler la méthode sans argument.
En sortie de méthode, et uniquement en cas de succès, les 9 clauses SELECT (p_sClauseWhere, p_sClauseOrderBy, p_nClauseLock, p_nClauseLimit, p_nClauseOffset, p_sClauseSelect, p_sClauseNotSelect, p_sClauseWith, p_sClauseJoin) sont remises à zéro pour garantir qu'aucun état ne fuit entre deux appels successifs.
En cas d'échec, les clauses sont préservées pour permettre le diagnostic côté appelant (logs, retry…).
La méthode supporte les CTE (WITH … AS) via le membre p_sClauseWith, à pré-positionner avant l'appel. La jointure sur la CTE se fait via p_sClauseJoin (clause JOIN brute).
📖 MySQL 8.4 — WITH (Common Table Expressions) · PostgreSQL — WITH Queries
🔑 Signature
LOCAL sClauseWhere est une chaîne = "",
LOCAL sClauseOrderBy est une chaîne = "",
LOCAL nTypeLock est un entier = NoLock,
LOCAL nLimitNombreTuples est un entier = 0,
LOCAL nOFfSet est un entier = 0
) : (booléen, entier, chaîne)
| Élément | Valeur |
|---|---|
| Visibilité | PUBLIQUE |
Paramètres directs — chaque paramètre, quand il est non-défaut, écrase le membre correspondant.
| Paramètre | Type | Défaut | Membre associé | Rôle |
|---|---|---|---|---|
sClauseWhere | chaîne | "" | p_sClauseWhere | Condition SQL de filtrage (sans le mot WHERE). Vide → réutilise p_sClauseWhere. |
sClauseOrderBy | chaîne | "" | p_sClauseOrderBy | Tri des résultats (sans le mot ORDER BY). Vide → réutilise p_sClauseOrderBy, puis tri par défaut sur la clé primaire ASC. |
nTypeLock | entier | NoLock | p_nClauseLock | Verrouillage pessimiste : NoLock · LockInShareMode · LockForUpdate. Une transaction doit être ouverte par l'appelant pour tout verrou ≠ NoLock. |
nLimitNombreTuples | entier | 0 | p_nClauseLimit | Nombre maximum de lignes (LIMIT SQL). 0 → pas de limite. |
nOFfSet | entier | 0 | p_nClauseOffset | Décalage de pagination (OFFSET SQL). 0 → pas de décalage. |
Membres pré-positionnables — sans paramètre direct, à renseigner avant l'appel.
| Membre | Type | Défaut | Rôle |
|---|---|---|---|
p_bClauseRAZ | booléen | Vrai | Vide m_tabResults et réinitialise les membres mappés avant le chargement. À laisser à Vrai dans la quasi-totalité des cas. |
p_sClauseSelect | chaîne | "" | Liste explicite des colonnes à projeter au format TABLE.COLONNE séparées par ,. Vide → toutes les colonnes mappées. |
p_sClauseNotSelect | chaîne | "" | Liste de colonnes à exclure du SELECT — complémentaire de p_sClauseSelect. |
p_sClauseWith | chaîne | "" | Définition CTE complète (WITH nom AS (SELECT …)), préfixée au SELECT. |
p_sClauseJoin | chaîne | "" | Jointure SQL brute supplémentaire — utile pour joindre une CTE. |
Préférer la forme sans paramètre, plus lisible et cohérente avec les membres qui ne disposent pas de paramètre direct (p_sClauseSelect, p_sClauseWith, p_sClauseJoin…) :
:p_sClauseWhere = "..."
(bProcessing, nErrorCode, sErrorMessage) = :mth_ChargerSelonClauseWhere()
Si p_sClauseSelect est utilisé pour ne charger qu'un sous-ensemble de colonnes, un appel ultérieur à mth_Enregistrer() sur cet objet ne mettra à jour que les colonnes effectivement chargées — les autres restent inchangées en base.
C'est le comportement attendu : il évite d'écraser silencieusement avec des valeurs vides les colonnes qui n'ont pas été lues. Pour modifier d'autres colonnes, recharger d'abord l'objet sans p_sClauseSelect (ou avec une liste élargie).
Les colonnes listées dans m_sListeRubriquesTraduites (déclaré dans le constructeur de la classe métier, format TABLE.COLONNE,TABLE.COLONNE) sont automatiquement traduites via la table système cshx2_traductions si la langue affichée diffère de la langue par défaut.
L'ID de la langue affichée est alimenté automatiquement depuis la session ORM_CSHX2 — aucune action côté appelant.
📦 Résultats hydratés — m_tabResults & objet courant
Les résultats du SELECT sont restitués directement sous forme d'objets WLangage, prêts à être consommés sans intermédiaire. L'appelant n'a jamais à manipuler de source de données SQL ni à recopier des champs un à un.
La structure exacte d'une classe métier (mapping, colonnes framework, déclaration de m_tabResults, constructeur, jointures) est détaillée dans la section 📐 Anatomie d'une classe métier ci-dessous.
Règle d'hydratation
Après un appel réussi à mth_ChargerSelonClauseWhere() :
| Cible | Contenu |
|---|---|
m_tabResults | Tableau d'instances de la classe métier — une instance par tuple SQL retourné. Indexé à partir de 1. |
Objet courant (:m_xxx) | Hydraté avec les valeurs du premier tuple (équivalent à m_tabResults[1]). Pratique pour les recherches mono-résultat. |
p_nOccurrencesTrouvées | Propriété renvoyant le nombre d'éléments dans m_tabResults. |
Trois cas d'usage selon le nombre de résultats
Cette double hydratation (objet courant + tableau) permet d'écrire un code uniforme : pour les recherches qui ramènent typiquement zéro ou un résultat (recherche par code unique, par GUID…), l'appelant peut utiliser directement :m_xxx sans avoir à écrire :m_tabResults[1]:m_xxx. Pour les listes, on itère sur m_tabResults.
Si p_nOccurrencesTrouvées > 1 et que vous lisez :m_xxx directement (l'objet courant), vous n'obtenez que les valeurs du premier tuple. Pour traiter tous les résultats d'une requête multi-lignes, itérer obligatoirement sur m_tabResults.
📐 Anatomie d'une classe métier
Toute classe métier accessible via l'ORM doit hériter de la classe de base xFrameWork_CSHX2. Cet héritage fournit l'ensemble des méthodes mth_* et les membres internes nécessaires au mapping objet ↔ SQL.
Une classe métier complète comprend trois parties : la déclaration (mapping de table, colonnes métier et framework, objets liés, tableau de résultats), un constructeur (initialisation, jointures, traductions), et éventuellement un destructeur. La déclaration et le constructeur sont décrits ci-dessous ; la liste détaillée des colonnes framework et des conventions de nommage fera l'objet d'une page dédiée.
Déclaration de classe
Exemple basé sur la table niveau_1 avec deux objets liés (niveau_1_bis et niveau_2) :
1. Mapping de table — l'attribut <MAPPING=niveau_1> sur la déclaration de classe lie cette classe à la table SQL niveau_1.
2. Bloc <MAPPING> ... <FIN> — délimite le code entièrement régénéré par le framework lors de la synchronisation avec l'analyse. Ne rien écrire à la main dans ce bloc, tout serait perdu à la prochaine génération.
3. Clé primaire — annotée clé unique. Une et une seule colonne porte cette annotation.
4. Colonnes framework (SQL_*) — à déclarer par le développeur dans la table SQL et dans la classe. Leurs noms sont personnalisables au démarrage du framework ; les valeurs SQL_* montrées ici ne sont que les valeurs par défaut. Le framework valorise automatiquement leur contenu lors des INSERT / UPDATE.
5. Objets liés — déclarés hors du bloc auto-généré. Leur nom doit correspondre exactement à la valeur passée à sObjectName dans le constructeur (voir ci-dessous).
6. m_tabResults — tableau typé sur la classe elle-même, dimensionné à 0. Hydraté par les méthodes de chargement.
Les noms par défaut (SQL_UUID, SQL_INSERTED, SQL_LOCKED…) peuvent être remplacés par les conventions de nommage maison via la structure stConfig.MetadataColumns, fournie au démarrage du framework. Si un nom personnalisé y est fourni, il remplace le nom par défaut ; sinon, le défaut est conservé.
Champs disponibles : sUUID · sID_USER_INSERT · sID_USER_UPDATE · sIP_USER · sLOCKED · sINSERTED · sUPDATED · sEXE · sPROCEDURE_INSERT · sPROCEDURE_UPDATE · sSTATUS.
Détails complets dans la page dédiée aux colonnes framework.
Constructeur type
Le constructeur initialise l'objet, déclare les colonnes traduites et définit les jointures via m_TabJoinDefinitions. Le pattern recommandé expose un paramètre booléen par jointure pour permettre à l'appelant de choisir lesquelles activer :
Champs de m_TabJoinDefinitions
Chaque ligne du tableau décrit une jointure entre la table parente et une table liée :
| Champ | Type | Rôle |
|---|---|---|
sLeftTable | chaîne | Table jointe (côté gauche du JOIN, généralement la table enfant qui porte la clé étrangère). |
sLeftField | chaîne | Colonne de jointure côté gauche (FK vers la table parente). |
sRightTable | chaîne | Table parente (côté droit du JOIN, en général la table de la classe courante). |
sRightField | chaîne | Colonne de jointure côté droit (typiquement la clé primaire de la table parente). |
sJoinType | chaîne | Type SQL de la jointure : "LEFT" · "INNER" · "RIGHT". "LEFT" est le défaut le plus sûr (retourne l'objet parent même sans enfant). |
sObjectName | chaîne | Nom exact du membre objet déclaré dans la classe qui recevra l'objet joint. Sert à la récursion automatique de l'analyseur de jointures. |
La valeur de sObjectName doit correspondre caractère pour caractère au nom du membre objet déclaré dans la classe. Une faute de frappe casse silencieusement la récursion sur les jointures (l'objet imbriqué ne sera pas hydraté).
Dans l'exemple : "m_clNiveau2" ↔ membre m_clNiveau2 est un objet Mniveau_2.
Le constructeur expose un paramètre par jointure, avec une valeur par défaut adaptée à l'usage le plus courant. L'appelant active uniquement les jointures dont il a besoin pour la requête en cours :
clObjet est un Mniveau_1(bJointureNiveau2 = Vrai)
// → niveau_2 ET niveau_1_bis seront jointes
📤 Valeur de retour
Triplet standard ORM (bProcessing, nErrorCode, sErrorMessage) :
| Retour | Condition |
|---|---|
(Vrai, 0, "") | La requête SQL s'est exécutée sans erreur. Ne garantit pas qu'un enregistrement a été trouvé — consulter p_nOccurrencesTrouvées pour le nombre de lignes chargées. |
(Faux, <code>, sMsg) | Échec de validation ou erreur SQL — voir la section ⚠️ Gestion des erreurs. |
bProcessing = Vrai confirme uniquement que la requête s'est exécutée sans erreur SQL — un SELECT retournant zéro ligne reste un succès.
C'est p_nOccurrencesTrouvées qui valide la présence effective de données. À tester systématiquement après un appel réussi pour distinguer : 0 (introuvable) · 1 (cas nominal) · N (liste à itérer).
⚠️ Gestion des erreurs
Les contrôles de validation sont exécutés après résolution des paramètres, avant tout accès SQL.
| Code | Constante | Condition |
|---|---|---|
-21099 | ERR_ORM_SELECT_LOCK_INVALIDE | p_nClauseLock ne vaut ni NoLock, ni LockInShareMode, ni LockForUpdate. |
-21098 | ERR_ORM_SELECT_LOCK_SANS_TRANSACTION | Verrou demandé (p_nClauseLock ≠ NoLock) mais aucune transaction active. La transaction doit être ouverte par l'appelant via ORM_TransactionBegin(). |
-21097 | ERR_ORM_SELECT_LIMIT_INVALIDE | p_nClauseLimit < 0 |
-21096 | ERR_ORM_SELECT_OFFSET_INVALIDE | p_nClauseOffset < 0 |
-21095 | ERR_ORM_SELECT_ENREG_VERROUILLE | Un enregistrement chargé en mode LockForUpdate est déjà verrouillé par un autre poste / utilisateur. |
-21094 | ERR_ORM_SELECT_EXCEPTION | Exception non gérée pendant la construction ou l'exécution de la requête. |
-999 | ERR_SQL_INVALID_REQUEST | Erreur SQL générique propagée par le moteur (syntaxe, contrainte, etc.). Le détail est dans sErrorMessage. |
Si un verrou est demandé (LockForUpdate ou LockInShareMode), une transaction doit être préalablement ouverte via ORM_TransactionBegin(). Si aucune transaction n'est active au moment de l'appel, la méthode retourne immédiatement (Faux, -21098, "...") sans exécuter le SELECT.
En cas d'échec, les 9 clauses SELECT positionnées sur l'objet sont préservées (la RAZ post-succès n'est pas exécutée). L'appelant peut donc inspecter :p_sClauseWhere, :p_nClauseLock, etc. pour logger ou tenter un retry sans avoir à tout reconstruire.
💡 Exemples
Mode 1 — pré-positionnement des membres (style recommandé)
Mode 2 — paramètres directs (style hérité)
Mode 3 — projection partielle avec p_sClauseSelect
Mode 4 — verrou pessimiste avec transaction
Mode 5 — CTE simple
Mode 6 — cas avancé : CTE d'agrégation + multi-jointures + tri sur agrégat + LIMIT
Cas réaliste combinant tous les leviers de la méthode. Objectif : récupérer les 2 villes les plus polyvalentes (mesurées par le nombre de catégories distinctes qu'elles offrent) parmi celles qui contiennent au moins un Monument. La CTE calcule l'indicateur, les jointures filtrent sur la catégorie, le tri exploite l'agrégat exposé par la CTE.
Il combine en un seul appel les cinq leviers de la méthode : p_sClauseWith (CTE d'agrégation), p_sClauseJoin (jointures multiples sur la CTE et les tables métier), p_sClauseWhere (filtre métier sécurisé via ORM_Quote), p_sClauseOrderBy (tri sur l'agrégat de la CTE) et p_nClauseLimit (Top N).
Le résultat est un objet hydraté (clObjNiveau1 + son m_tabResults) prêt à être consommé sans avoir à manipuler de source de données SQL côté appelant.