Ecrire son propre système de tables de référence

Lorsqu'on définit une base de données, on a souvent dans les tables, des données qui permettent de faire le lien avec d'autres tables. Ces données sont les clés primaires des tables ainsi référencées.

 

Par exemple, dans une table des contacts, on aura la structure suivante:

Id_contact , clé primaire

Nom_contact

Prenom_contact

Id_societe_contact

Telephone_contact

.....

 

Le champ Id_societe_contact permet de faire le lien avec la table des sociétés ainsi constituée:

Id_societe , clé primaire

Nom_societe

Adresse_societe

....

 

On parle souvent de clé étrangère pour caractériser le fait que le champ Id_Societe_Contact est la clé primaire d'une autre table.

 

Dans le formulaire de saisie des contacts, l'utilisateur souhaitera évidemment pouvoir avoir la liste des sociétés afin de pouvoir sélectionner le bon identifiant. Une première solution consisterait à lui afficher dans la fiche de saisie des contacts la table des sociétés afin qu'il puisse choisir son identifiant. Outre l'aspect peu ergonomique de la solution (la liste des sociétés va occuper de façon permanente un espace de la fiche, même lorsque l'utilisateur n'en a pas besoin), il faudra programmer un contrôle vérifiant que l'identification saisie par l'utilisateur existe bien dans la table des sociétés.

 

Paradox fournit une solution plus élégante à travers la notion de table de référence. Il est en effet possible de décrire dans la structure de la table Contacts la notion de clé étrangère en précisant sur le champ concerné (Id_societe_contact) la table de référence (Societes). Ainsi lors de la saisie, Paradox contrôle que la donnée figure bien dans la table de référence et peut afficher, sur demande de l'utilisateur (touche ctrl+Espace) la liste des valeurs existant dans la table de référence. Autre avantage, la solution est opérationnelle aussi bien lors de la manipulation des tables directement avec Paradox que dans les fiches.

Les limites de la gestion des tables de référence par paradox

Limites fonctionnelles: La présentation des données affichées par Paradox dans la fenêtre 'Référence' ne peut être modifiée. Vous ne pouvez ainsi pas présenter les informations triées par nom de société plutôt que par identifiant (pratique si vous aves plusieurs milliers d'enregistrements), filtrer les informations par exemple sur un département, afficher d'autres informations (seules les 2 premiers champs sont affichés), par exemple le code postal et la ville.

 

Limites techniques:

- Paradox mémorise dans le fichier contacts.VAL les caractéristiques du lien entre les tables et notamment le chemin physique (même si vous utiulisez un alias) de la table de référence (ici societes). Dans le cas où le répertoire est le répertoire est le répertoire de travail, le chemin n'est pas stocké.

 

L'utilisation du chemin complet n'est pas compatible avec un fonctionnement en réseau de votre application. En effet, la localisation physique de votre base dépend de contraintes matérielles externes (place disponible sur les serveurs par exemple) et fluctuera probablement dans le temps. La notion d'alias permet normalement de rendre indépendant la localisation physique des données et leur utilisation dans les programmes mails les alias ne sont pas pris en compte ici.

 

Si vous changer la localisation d'une des tables, vous ne pourrez plus ouvrir la table référençante qu'en lecture seule. Paradox vous informe d'ailleurs du problème en vous affichant une boîte de dialogue vous indiquant que la table de référence n'a pas été trouvée ou est endommagée. Hélas, il ne vous permet pas de refaire le lien dynamiquement avec votre table de référence, et il est nécessaire pour pouvoir utiliser de nouveau votre table de :

- supprimer le fichier contacts.VAL.

- restructurer votre table contacts en indiquant de nouveau la table de référence sur le champ Id_Societe_contact

- remettre toutes les informations (valeurs mini, maxi, par défaut, modèle, ...) qui étaient stockées dans le fichier contacts.val et qui ont également été supprimées.

Les solutions

La première solution consite à toujours localiser vos tables dans un seul répertoire et de créer les références en ayant ce répertoire comme répertoire de travail. Ainsi, Paradox ne mémorise que le nom de la table et non le chemin complet et vous pourrez ensuite déplacer vos tables à votre gré (à condition bien sûr de les garder dans un même répertoire).

 

La seconde solution consiste à programmer votre propre gestion de tables de référence. Vous pourrez ainsi lever aussi bien les limlites fonctionnelles que les limites techniques. La suite de cette note présente la mise en oeuvre d'une telle solution.

Choix d'une valeur dans une boîte de dialogue secondaire

L'exemple est disponible dans la partie Téléchargements (gestion des tables de référence).

 

Dans la fiche Contacts qui permet à l'utilisateur de saisir les informations, nous allons ajouter un bouton appelé par exemple Id_societe_contactBtLookUp qui va contenir le code suivant dans la méthode pushbutton:

 

var
bddFM FORM
endvar
if not bddFM.open(":aliasDesProgrammes:SOC_L") then errorshoww() return endif
if bddFM.wait() then    si bouton Ok
;on valorise le champ de la fiche par la valeur courante de la boîte de dialogue
dmput("CONTACTS","ID_SOCIETE_CONTACT", bddFM.Id_societe.value)
; Id_Societe_contact.Value = bddFM.Id_societe.value   alternative au dmput mais on passe dans le changeValue du
 champ
;il est également possible de programmer la mise à jour dans la bdd appelée
;le bouton Ok comprend alors une programmation similaire à celle ci-dessus
  bddFM.close()    la fiche appelante ferme la boîte de dialogue
endif

Il faut également contrôler qu'une valeur saisie directement dans le champ de la fiche appartient bien à la table de référence. On pourra également intercepter l'action Ctrl+Espace en le traduisant dans le nouveau système. Tout ceci peut être effectué par la programmation suivante localisée dans le champ Id_Societe_contact:

var
 lookupTC Tcursor   ce tcursor sera utilisé pour le contrôle des valeurs saisies
endvar
method open     on ouvre le tcursor une seule fois pour des raisons de performances
 if not lookupTC.open(":aliasDesTables:SOCIETES.DB") then errorshow() return endif
endmethod
method close
 if lookupTC.is assigned() then lookupTC.close() endif
endmethod
method action   on traduit le contrôle espace
if eventinfo.id() = DataLookUp then
disabledefault
Id_societe_ContactBtlookUp.pushbutton()
endif
endmethod
method ChangeValue
if not (eventInfo.newValue() = blank()) then
if not lookupTC.qlocate(eventInfo.NewValue())) then
msginfo("Erreur","La veleur saisie n'existe pas dans la table de référence")
eventinfo.setErrorcode(CanNotDepart)
endif
endif
endmethod

Améliorations possibles

 

On voit ci-dessus qu'une programmation est à faire aussi bien dans le bouton que dans le champ concerné. On peut diminuer la programmation en effectuant le traitement du bouton dans le préfiltre de l'événement pushbutton. Pour cela nous pouvons normaliser notre nom de bouton en l'appelant Id_societe_contactBTLOOKUPsoc_L. Ceci va nous permettre de connaître à la fois le nom du champ concerné par la référence (Id_societe_contact) et le nom de la boîte à appeler (soc_L).

 

On programmera ainsi la méthode pushbutton de la fiche:


method pushButton(var eventInfo Event)
var
cibleUI UIOBJECT
NomBoutonST,
   NomChampST,
   NomFicheST String
   bddFM Form

endvar
if eventInfo.isPreFilter() then
;// Ce code s'exécute pour chaque objet de la fiche
      eventInfo.gettarget(cibleUI)
NomBoutonST = upper(cibleUI.Name)
      if NomBoutonST.match("..BTLOOKUP..",NomChampST,NomFicheST) then
Moveto(NomChampST) ; on se positionne sur le champ concerné
         if not isedit() then
            if not edit() then return endif
         endif
if not bddFM.open(":tbref:"+NomFicheST) then errorshow() return endif
if bddFM.wait() then    si bouton Ok
Active.Value = bddFM.Id.value   on nomme systématiquement Id le champ à récupérer
  bddFM.close()    la fiche appelante ferme la boîte de dialogue
         endif
      endif
else
;// Ce code s'exécute seulement pour la fiche

endIf
endMethod

Ceci permet de généraliser le traitement du bouton. On pourra faire de même pour la programmation des champs. Bien sûr, ces traitements pourront être remonter en librairie et on pourrrait gérer une table de paramétrage qui pourrait avoir par exemple la structure suivante:

Nom_Champ (clé primaire)

AliasEtTableRéférence

AliasEtFicheReference

ce qui permettrait de paramétrer aussi bien le contrôle que l'affichage de la liste des valeurs.

 

Bien sûr, on pourrait ajouter dans la clé le nom de la fiche appelante pour gérer des affichages différents selon le contexte ... et pour savoir tout ce qu'il est possible de faire avec un paramétrage, nous vous conseillons de jeter un oeil à la présentation de notre environnement de développement Paradox, qui comprend encore bien d'autres choses.