I. Prérequis▲
Certaines connaissances rudimentaires de base sont censées acquises : créer une table, construire une requête, un formulaire…
Ce texte s'adresse à des utilisateurs Access débutants.
II. Notre objectif▲
Construire un formulaire principal présenté en mode « simple » (un seul enregistrement affiché à la fois) et un sous-formulaire en mode continu (une liste d'autant de lignes que d'enregistrements) pour donner le détail.
Comme ceci :
Pour plus de lisibilité, le fond du formulaire principal est en vert. Celui du sous-formulaire est en jaune.
Ce formulaire est volontairement « minimaliste », j'aurais pu y ajouter :
- une image pour illustrer chaque plat ;
- l'accès en un clic à une vidéo logée sur Internet ;
- permettre des recherches multicritères : selon la catégorie, le nom du plat, un ingrédient…
J'ai voulu me focaliser sur « le » sujet et aussi éviter tout recours à la programmation : il n'y a pas une seule ligne de code VBA dans ce tutoriel.
Je me propose de traiter ces thèmes dans un tutoriel à venir. Vous y reviendrez, si ça vous intéresse, lorsque vous ne serez plus débutant… Mais si c'est urgent, voyez deux tutoriels de référence :
III. Organiser les données utiles à l'application▲
Pour la rédaction de ce chapitre, je me suis largement inspiré de l'article Office : « Concepts de base sur la conception d'une base de données ».
En vrac, les données utiles sont : la catégorie (entrée, plat principal…), la dénomination de la préparation, le texte de la recette, les quantités de chaque ingrédient et leur prix, le prix de revient par convive.
III-A. Classement « tout en une »▲
Dans un premier temps, nous pourrions imaginer une table dans laquelle on stocke toutes les informations nécessaires :
Une telle solution présente plusieurs inconvénients :
- il faut chaque fois répéter des données identiques : dénomination, le nom des ingrédients, leur prix ;
- tenir cette table à jour sera fastidieux : à chaque variation d'un prix unitaire, il faudra modifier à plusieurs endroits… sans en oublier ! ;
- combien de quadruplets Ingrédient/PU/Quantité/Coût faut-il prévoir ? D'une recette à l'autre, le nombre d'ingrédients varie : pour la recette d'un œuf dur, un quadruplet suffit, pour un plat plus élaboré, il en faudra une douzaine ! Et sans doute, plus tard le chef trouvera une recette pour laquelle il manque des cases : la loi de Murphy s'applique aussi en cuisine…
Dans cette approche, chaque ligne de la table contient des informations de plusieurs sujets :
- relatifs au service : entrée, plat principal ou dessert ;
- relatifs à la recette : son nom, la progression, le nombre de convives… ;
- relatifs à chaque ingrédient : son nom, son prix unitaire…
III-B. Classement « par sujet »▲
Dans notre exemple, nous avons trois sujets d'informations :
- les catégories : parle-t-on d'une entrée, d'un plat principal, d'un dessert ? ;
- les plats : comment les nomme-t-on ? Comment les prépare-t-on ? Pour combien de convives ? ;
- les ingrédients : comment les nomme-t-on ? Quel est leur prix ?
Nous constituons ces trois tables avec chacune une colonne du type NuméroAuto qui leur servira de clé primaire (un élément qui permet d'identifier de manière unique un enregistrement dans la table).
Les voici :
IV. Établir des relations entre les tables▲
Nous devons maintenant trouver un moyen de rassembler les sujets de façon cohérente.
Dans le formulaire que nous voulons construire :
- la donnée provient de la table tblCategories ;
- la donnée provient de la table tblPlats ;
- les données proviennent de la table tblIngredients ;
- les données sont le résultat d'une opération. En principe, on ne stocke pas ce genre d'information dans une table si on dispose déjà des éléments qui permettront à Access d'effectuer le calcul en temps opportun.
IV-A. Relation un-à-plusieurs▲
Si nous considérons tblPlats, nous constatons qu'à chaque ligne correspond une (et une seule) ligne dans la table tblCategories : chaque plat repris dans tblPlats est soit une entrée, un plat principal ou un dessert.
De manière symétrique, à chaque ligne de tblCategories correspondent plusieurs lignes de tblPlats.
Ce type de relation est appelé une relation « un-à-plusieurs ».
Pour matérialiser cette correspondance, nous allons ajouter une colonne dans tblPlats (le côté plusieurs) pour y mentionner la valeur de la clé primaire correspondante de tblCategories (le côté un).
Nous avons baptisé cette colonne dans tblPlats du même nom qu'elle porte dans tblCategories. Ce n'est pas obligatoire.
Par contre, les deux types de donnée doivent être identiques (un champ « NuméroAuto » est un « Entier long »).
Access pourra utiliser idCategorie de tblPlats pour trouver le nom de la catégorie correspondant à chaque plat.
La colonne idCategorie dans tblPlats est appelée une clé étrangère. Une clé étrangère est la clé primaire d'une autre table.
Voici la suite des opérations pour communiquer cette relation à Access :
- dans la barre des menus, cliquez sur « Outils » et ensuite sur « Relations… » :
- une fenêtre s'ouvre et vous invite à sélectionner les tables, en l'occurrence tblCategories et tblPlats :
vous fermez la fenêtre « Ajouter une table » ;
- vous cliquez sur la clé primaire (idCategorie dans tblCategories) et en maintenant la pression, vous faites glisser sur la clé étrangère (idCategorie dans tblPlats) :
- une nouvelle fenêtre s'ouvre :
et vous cochez « Appliquer l'intégrité référentielle ». Cela pour garantir la cohérence entre les enregistrements des tables liées : il sera impossible d'ajouter un enregistrement dans la table du côté « plusieurs » de la relation si la liaison ne peut s'établir avec une valeur de la clé de la table côté « un » de la relation.
Dans tCategories (côté « un » de la relation) idCategorie peut prendre les valeurs 1, 2 ou 3.
Conséquence de l'intégrité référentielle, dans tPlats (côté « plusieurs » de la relation), aucun enregistrement ne pourra être ajouté avec un idCategorie différent de 1, 2 ou 3.
De même, dans tCategories on ne pourra supprimer un enregistrement tant que des enregistrements existent dans tPlats avec cette valeur de idCategorie ;
- vous cliquez sur le bouton « Créer » et Access affiche la liaison comme ceci :
Pour expérimenter ce que signifie « Intégrité référentielle », essayez ceci :
- ouvrez la table tblCategories ;
- vous constatez une colonne supplémentaire ;
- cliquez sur un « + », Access vous affiche les enregistrements de tblPlats qui y sont liés :
- essayez de supprimer l'enregistrement 1, Access refuse :
- pressez la touche <ESC> pour annuler ;
- ouvrez tblPlats et dans la colonne idCategorie, essayez d'introduire « 4 », Access refuse :
- pressez la touche <ESC> pour annuler.
IV-B. Une relation « plusieurs-à-plusieurs »▲
Considérons maintenant tblPlats et tblIngredients.
Dans la plupart des cas, un plat comprendra plusieurs ingrédients et un ingrédient interviendra probablement dans plus d'un plat.
Nous sommes devant une relation « plusieurs-à-plusieurs ».
Cette fois, il ne suffit pas d'ajouter une clé étrangère dans tblPlats pour établir la liaison avec tblIngredients. Pour avoir plusieurs ingrédients dans un plat, vous devriez avoir plusieurs enregistrements par plat dans la table tblPlats, chacun avec l'idIngredient correspondant.
Et le même souci si on aborde par l'autre bout : si nous ajoutions la clé étrangère idPlat dans tblIngredients, il nous faudrait alors répéter les informations de l'ingrédient pour chaque plat différent.
IV-C. Une table de jointure pour scinder une relation plusieurs-à-plusieurs en deux relations un-à-plusieurs▲
La solution : créer une table supplémentaire, appelée « table de jointure », qui va substituer deux relations un-à-plusieurs à la relation plusieurs-à-plusieurs.
L'astuce consiste à ajouter la clé primaire de chacune des deux tables dans la nouvelle table et d'y stocker chaque occurrence de relation.
Dans la foulée, c'est dans cette tblPlatsIngredients que nous logerons l'information des quantités.
Réflexion sur le choix de la clé primaire
La clé primaire doit permettre d'identifier sans ambiguïté une ligne quelconque de la table.
Il faut donc garantir que sa valeur soit différente d'une ligne à l'autre.
Pour la table tblIngredients, nous aurions pu choisir le nom de l'ingrédient en clair comme clé primaire : « oignons kg », « boudoirs pq »… tous les ingrédients ont un nom différent.
Alors, pourquoi avoir choisi un NuméroAuto comme clé ? Parce que l'expérience montre que c'est plus pratique !
Cette clé primaire sert souvent de clé étrangère dans une autre table, c'est le cas dans notre exemple : l'information « boudoirs pq » est aussi incluse dans tblPlatsIngredients (il en faut pour le tiramisù et pour le Malakof).
Imaginez qu'au lieu de la valeur « 6 », nous ayons choisi la valeur « boudoirs pq » dans tblPlatsIngredients pour établir la relation. Cela fonctionnerait.
Mais si subitement, vous vouliez changer : au lieu de « boudoirs » vous vouliez « Madeira » ou plus générique « biscuits à la cuillère », non seulement il faudrait modifier dans tblIngredients mais aussi dans tblPlatsIngredients. Cela risque de vous demander des acrobaties : neutraliser temporairement l'intégrité référentielle, modifier partout sans rien oublier, rétablir l'intégrité…
Bref, comme clé primaire, il vaut mieux choisir une valeur qui ne risque pas d'évoluer dans le temps.
Choisir une valeur NuméroAuto comme clé primaire est une bonne garantie : Access attribue automatiquement une valeur à votre place. Un identificateur de ce type ne contient aucune information factuelle décrivant la ligne qu'il représente et vous ne serez jamais tenté de le modifier… vous n'y avez pas accès !
C'est une règle, évidemment, il y a des exceptions !
Dans tblPlatsIngredients, si nous considérons l'ensemble des valeurs des deux colonnes : IdPlat et IdIngredient, nous avons une combinaison unique qui ne risque pas de changer. Nous pouvons donc choisir cette combinaison comme clé primaire de notre table de liaison.
De plus, cela garantira qu'un même ingrédient n'est renseigné qu'une seule fois dans la composition d'une recette.
Lorsqu'une clé primaire se compose de plusieurs colonnes, on parle de clé composite.
Pour définir une clé composite :
- vous affichez la table en mode création ;
- vous sélectionnez les champs concernés et dans la barre des menus, vous cliquez sur l'icône .
IV-D. Compléter le modèle▲
Pour afficher les relations : dans la barre des menus, cliquez sur Outils/Relations…
Cliquez sur le bouton et ajoutez les tables tblPlatsIngredients et tblIngredients.
Faites glisser l'idPlat de tblPlats sur celui de tblPlatsIngrédients et dans la fenêtre « Modification des relations », cochez « Appliquer l'intégrité référentielle » et « Effacer en cascade les enregistrements correspondants » :
« Effacer en cascade les enregistrements correspondants », cela signifie que si l'utilisateur supprime un plat dans la table tPlats, tous les enregistrements de tPlatsIngredients qui portent l'idPlat en question seront automatiquement supprimés.
Logique ! Si nous décidons d'éliminer une recette de notre base de données, nous n'avons plus besoin de connaître les ingrédients qui permettent de la réaliser.
Si nous avions coché « Mettre à jour en cascade les champs correspondants », cela aurait voulu dire que si nous modifions une valeur d'idPlat dans tPlats, par exemple en remplaçant le idPlat « 2 » par la valeur « 28 », alors automatiquement dans tPlatsIngredients tous les idPlat « 2 » étaient modifiés en « 28 ».
Une telle fonctionnalité n'est pas utile dans notre cas : idPlat dans tPlats est du type NuméroAuto, il ne peut donc pas être modifié par l'utilisateur.
Procédons de même avec l'idIngredient de tblIngredients et celui de tblPlatsIngredients, le graphique des relations devient :
V. Première ébauche du formulaire fRecettes▲
Si vous voulez apprendre de bonnes pratiques pour la construction d'un formulaire, voyez ce tutoriel de Jean-Philippe AMBROSINO (argyronet). Concentrez-vous sur le chapitre « 2. Présentation du formulaire exemple ». (Il sera encore temps, lorsque vous aurez acquis davantage d'expérience, de découvrir les autres chapitres de son tutoriel.)
Prenez dès le début de bonnes habitudes de nommage. Si vous aimez la rigueur, voyez les Conventions typographiques du même auteur.
Passons en revue les différents contrôles de ce formulaire.
Pour examiner les propriétés, affichez le formulaire en mode « Création » et double-cliquez sur le carré supérieur gauche :
Cliquez ensuite sur un contrôle pour afficher ses propriétés.
Pour vous documenter sur une propriété de la liste, cliquez-la (elle se met en surbrillance et pressez la touche <F1> : l'aide Access s'ouvre alors à la bonne page.
V-A. Vue d'ensemble▲
V-B. Propriétés du formulaire fRecettes▲
Sa source est la table tPlats.
Il affichera les enregistrements de la table en « mode simple ».
De plus, nous fixons la propriété Cycle à « Enregistrement en cours » pour éviter le passage intempestif à l'enregistrement suivant lorsqu'on navigue dans les contrôles.
V-C. Propriétés de cboCategorie▲
Cette zone de liste modifiable sera alimentée (propriété « contenu ») par la table tCategories.
Dans « Nbre de colonnes », nous exprimons qu'il faut considérer les deux premières colonnes de la table (elle n'en contient d'ailleurs que deux !).
Dans « Largeurs de colonnes », nous définissons l'espace réservé à l'affichage des colonnes lors du déploiement de la liste. Dans ce cas particulier : 0 cm pour la 1re colonne signifie que nous voulons l'occulter.
Quand l'utilisateur aura choisi une ligne de la table, c'est la valeur de la première colonne à largeur non nulle qui sera affichée. Dans notre exemple, la deuxième colonne.
Par contre dans « Colonne liée », nous exprimons que la valeur du contrôle sera celle de la 1re colonne.
Pour vous en assurer, faites ce test :
- ouvrez fRecettes en mode « formulaire », le contrôle affiche « Dessert », cliquez sur le champ pour le rendre actif ;
- ouvrez la fenêtre d'exécution (<Ctrl> + G) et saisissez :
? forms!fRecettes!cboCategorie.value
et <Entrée> ;
De ce côté de la barrière, Access s'exprime dans la langue de Shakespeare ou plutôt de Jean-Claude Van Damme.
Traduction en français : montrer (?) le contenu du contrôle cboCategorie actuellement affiché dans le formulaire fRecettes parmi la collection des formulaires (forms) actuellement actifs dans la base de données.
- Access vous affiche « 3 » ;
- saisissez :
? forms!fRecettes!cboCategorie.text
- Access affiche « Dessert ».
Remarquez également la propriété « Limiter à liste » égale à « Oui » : l'utilisateur sera obligé de choisir l'une des trois lignes proposées lorsque la zone de liste modifiable est dépliée.
Si on veut pouvoir choisir « Entremets », il faudra d'abord modifier la table tCategories…
V-D. Propriétés de txtNbreConvives▲
La propriété « Source » est NbreConvives.
La propriété « Valeur par défaut » est 1.
La propriété « Valide si » est > 0 donc obligatoirement un nombre positif.
V-E. Propriétés de txtPrixRevient▲
Nous reviendrons plus tard sur la propriété « Source » de ce contrôle.
V-F. Propriétés de txtIdPlat▲
C'est le contrôle en rouge. Sa source est idPlat. Nous reviendrons plus tard sur l'utilité de ce contrôle.
V-G. L'aspect de fRecettes à ce stade▲
VI. Le formulaire sfPlatIngredients▲
VI-A. Vue d'ensemble▲
VI-B. Propriétés du formulaire sfPlatIngredients▲
Sa source est une requête dont on peut voir le graphique en cliquant sur la propriété et sur les qui apparaissent alors au bout de la ligne. La voici :
Nous considérons donc les tables liées tblIngredients et tblPlatsIngredients.
Sur la ligne Champ :
- nous prenons dans notre requête toutes les colonnes de tblPlatsIngredients ;
- nous ajoutons les colonnes « Ingredient » et « Prix » de tblIngredients ;
- nous construisons une colonne « Cout » en multipliant le champ Quantite par le Prix (remarquez les crochets qui encadrent le nom des colonnes).
Sur la ligne Tri :
- d'abord sur idPlat ;
- ensuite sur Ingredient (en clair).
Vue :
VI-C. Propriétés de txtIngredient▲
Sa sourceest idIngredient. Pourtant c'est l'ingrédient en « clair » qui sera affiché.
Voyez le contenu de la liste, c'est le SQL d'une requête : SELECT [tIngredients]…
Pour voir cette requête sous sa forme graphique, cliquez à l'endroit marqué , il vient :
Donc deux colonnes :
- la 1re, celle qui est liée, contient l'idIngredient qui sera affecté à la source du contrôle ;
- c'est la 2e qui sera affichée puisque les largeurs sont « 0cm;4cm ».
VI-D. Propriétés de txtPrix, txtCout▲
Les sources sont respectivement : Prix et Cout. Les autres propriétés n'appellent pas de commentaire.
VI-E. Propriétés de txtIdPlat▲
C'est le contrôle en rouge. Sa source est idPlat. Nous reviendrons plus tard sur l'utilité de ce contrôle.
VI-F. Propriétés de txtQuantite▲
La propriété « Valide si » > 0 pour empêcher :
- l'oubli (la valeur par défaut est 0, puisque de type numérique) ;
- l'introduction d'une quantité négative.
VI-G. Propriétés de txtTotal▲
La source est « =Somme([Cout]) », c'est-à-dire la somme de la colonne « Cout » de la requête.
N.B. [Cout] réfère à une colonne de la source du formulaire et non au contrôle (qui d'ailleurs s'appelle « txtCout »).
VI-H. L'aspect de sfPlatIngredients à ce stade▲
VI-I. Un petit bonus▲
Essayons d'ajouter un ingrédient pour l'idPlat N° 6, par exemple 1 kg d'amande pilée :
La ligne se complète mais le total reste inchangé. C'est parce que l'enregistrement dans la table n'est pas encore réalisé. D'ailleurs le en début de ligne est là pour vous le signaler.
Pour enregistrer vous pouvez soit :
- cliquer sur le sélecteur ;
- passer à un autre enregistrement du formulaire ;
- cliquer dans le formulaire ailleurs que sur la ligne courante ;
- passer à un autre plat ;
- dans la barre des menus : Enregistrement > Sauvegarder l'enregistrement ;
- utiliser le raccourci <Maj + Entrée>.
Si vous le faites, le total passe à 35,10 et disparaît.
N'oubliez pas de supprimer cet ingrédient de votre recette : c'était juste pour voir comment Access réagit !
Le bonus que je vous propose, c'est de rendre cette sauvegarde automatique.
Nous allons créer une macro qui va émuler la pression des touches <Maj + Entrée> et déclencher l'exécution de cette macro après chaque mise à jour du contrôle « cboIngredient » ou du contrôle « txtQuantite ».
Voici la macro (nous l'avons appelée « Sauvegarder ») :
Et pour provoquer son déclenchement après chaque mise à jour de cboIngredient ou txtQuantité, nous modifions la propriété « Après MAJ » de ces deux contrôles :
VII. Incorporation de sfPlatIngredients dans fRecettes▲
Nous allons intégrer sfPlatIngredients en tant que sous-formulaire de fRecettes.
Affichez à l'écran la fenêtre de la base de données à l'onglet « Formulaires » et fRecettes en mode « Création ».
Dans la fenêtre de la base de données, vous cliquez sur « sfPlatIngredients » en maintenant la pression sur le bouton de la souris et vous faites glisser-déposer dans fRecettes :
Quand vous relâchez la pression, fRecettes devient :
Access a intégré un nouveau contrôle dans fRecettes.
Affichez les propriétés de ce contrôle :
Constatez qu'il ne s'agit pas des propriétés de sfPlatIngredients !
Soyons précis : ce que nous avons fait, c'est ajouter à notre formulaire un contrôle Sous-formulaire/Sous-état, disons une espèce de container qui, dans notre exemple, pointera sur le formulaire sfPlatIngredients.
Lorsque vous sélectionnez ce contrôle, il affiche d'abord les propriétés du container.
Si ensuite vous cliquez sur l'un des contrôles du formulaire inclus, alors Access vous affiche les propriétés de ce contrôle comme si vous étiez dans le formulaire d'origine.
Pour plus de clarté, nous allons d'ailleurs modifier le nom qu'Access a attribué par défaut à ce contrôle. Nous l'appellerons « ctrSfPlatIngredients » :
Passons en revue quelques propriétés du container « ctrSfPlatIngredients » .
Dans « Champs fils » et « Champs pères », on indique le nom des colonnes des tables ou requêtes qui servent de source au formulaire « père » et au formulaire « fils ». Cela va servir à coordonner l'affichage du père et du fils : le formulaire « fils » montrera seulement les enregistrements de sa source qui sont en relation avec l'enregistrement actuellement actif dans le formulaire « père ».
Remarquez que les champs fils et pères sont les noms des colonnes des tables et non pas le nom des contrôles (ils ne sont d'ailleurs pas encadrés de crochets et de plus les contrôles s'appellent : « txtIdPlat »).
Dans notre exemple, ces noms sont identiques : ce n'est pas obligatoire, c'est la conséquence des nommages que nous avons choisis lors de la création des tables.
À ce stade voici comment se présente notre formulaire fRecettes :
Naviguez parmi les enregistrements : la liste des ingrédients est chaque fois limitée à ceux qui interviennent dans l'IdPlat affiché dans le formulaire principal (ici 4).
VIII. Quelques retouches▲
Dans les deux formulaires, nous avons un contrôle nommé txtIdPlat (ils s'affichent en rouge). Ils n'ont aucun intérêt pour l'utilisateur final. Je les avais prévus pour des raisons didactiques : pour montrer que la liste des ingrédients était limitée à ceux du plat.
Nous supprimons ces contrôles.
Nous supprimons l'étiquette, créée par défaut, du contrôle ctrSfPlatIngredients.
Nous agrandissons la hauteur du contrôle ctrSfPlatIngredients pour donner plus d'espace à l'affichage du sous-formulaire.
Il y a deux barres de boutons de déplacement :
- celle du formulaire principal : elle permet de se déplacer parmi les recettes ;
- celle du sous-formulaire : elle permet de se déplacer dans la liste des ingrédients.
Cette dernière n'est pas très utile, nous la supprimons.
Et enfin la source du contrôle txtPrixRevient.
C'est le résultat de la division :
- du montant affiché dans le contrôle txtTotal du sous-formulaire
par
- le montant affiché dans le contrôle txtNbreConvives du formulaire principal.
Voici l'explication de la syntaxe :
IX. En guise de conclusion▲
Pour que votre apprentissage soit plus efficace, je vous suggère : de copier les tables dans une base de données vierge et de reconstruire le formulaire vous-même, en suivant le tutoriel pas à pas.
Autre bon tuyau
Pour se documenter sur les propriétés d'un formulaire ou d'un état, ou de leurs contrôles :
- afficher l'objet en mode construction ;
- cliquer sur la propriété : elle se met en surbrillance ;
- enfoncer la touche <F1> : l'aide Access s'ouvre à la bonne page.
On peut aussi :
- ouvrir l'aide <F1>, choisir l'onglet « Aide intuitive » et suivre les instructions ;
- ouvrir la fenêtre d'exécution (<Ctrl>+G), saisir un mot-clé, y placer le curseur de la souris et enfoncer <F1>.
La démarche proposée dans ce tutoriel vaut aussi pour des états et sous-états.
Pour vous familiariser, voyez aussi Implanter un sous-état dans un état de Jean BALLAT.
Vous pouvez télécharger ici la base de données Access2000 qui m'a servi à construire l'exemple.
X. Remerciements▲
Merci à Pierre Fauconnier et Philippe Jochmans pour leurs remarques fort judicieuses pendant la mise au point technique de ce tutoriel.
Merci à f-leb pour la relecture : l'orthographe n'est pas son Achille's Talon…
Merci à _Max_ pour la correction des faux plis typographiques.
Merci à vous qui avez lu jusqu'ici.