Ma première base de données avec Delphi

Laurent Berne

Le but de ce tutoriel est la réalisation d'une petite application incorporant une base de données. J'ai choisi aléatoirement comme sujet de faire un petit carnet d'adresses. Il s'agit en fait de la première application que j'ai faite avec Delphi (à l'époque en version 4) pour interfacer une base de données. Le format de base de données choisi est Paradox, parce qu'il est à mon avis le plus simple à mettre en œuvre pour un débutant.

- Titre : Ma première base de données avec Delphi

- Auteur : Laurent Berne

- Parution : 26 avril 2002

- Mise à jour : 2 janvier 2020

- Licence : Ce document est issu de https://www.developpez.com et reste la propriété exclusive de son auteur. La copie, modification et/ou distribution par quelque moyen que ce soit est soumise à l'obtention préalable de l'autorisation de l'auteur.

Sommaire

- I. Introduction

- II. Première étape  : la création du fichier Paradox

- III. Deuxième étape : la création du projet et sa mise en forme

- IV. Troisième étape  : codage de la partie fonctionnelle

I - Introduction

Les prérequis pour ce tutoriel sont : savoir créer et sauvegarder un projet, la connaissance des composants TButton, TEdit, TMaskEdit, TPanel, et TMainMenu, la classe AnsiString, avoir entendu parler de base de données et si possible en avoir utilisé.

Il est nécessaire d'avoir le BDE d'installé (pour Delphi 5, il faut donc la version professionnelle minimum).

Dans ce tutoriel, je n'utiliserai pas les contrôles visuels orientés « Base de données » comme les TDBEdit, TDBMemo, TDBNavigator etc. Néanmoins j'y ai placé un TDBGrid, mais en lecture seule, qui ne sert qu'à visualiser les opérations que je fais sur la table. Le but de ce tutoriel est de voir comment on manipule par du code pascal une base de données, par l'intermédiaire du composant TTable.

Rappel sur les bases de données

C'est une façon de stocker l'information et d'établir des relations entre les différentes données. Par exemple on a tous pesté par les publicités que l'on reçoit dans notre boîte aux lettres, nous précisant que l'on vient de gagner une machine à laver… Ce qui est surprenant, c'est que la société émettrice possède nos coordonnées précises, ainsi que celles de nos voisins, amis, familles, etc., etc.

Dans sa forme la plus basique, nous pouvons l'imaginer comme un tableau. Chaque colonne serait un renseignement comme « Nom, Prénom, Adresse… » et chaque ligne correspondrait à une personne. D'un point de vue informatique, une base de données est constituée d’un ou plusieurs fichiers constitués d’une ou plusieurs tables (= nos tableaux vus plus haut). Car suivant la complexité des renseignements et leur caractère répétitif, on peut facilement envisager de relier deux tables entre elles. On parle alors de base de données relationnelle. Bien sûr il n'existe pas un seul format de base de données sur le marché. Interbase, Oracle, Access, Paradox sont des noms très répandus dans les entreprises. On les choisit suivant le degré de complexité de la base, le nombre d'utilisateurs simultanés à utiliser la base et bien sûr la grosseur de notre porte-monnaie !

Pour notre exemple j'ai choisi Paradox pour sa simplicité : un fichier correspond à une seule et unique table, au contraire d'Access par exemple où toutes les tables sont stockées dans le même fichier. Une base de données Paradox est constituée d'un ou plusieurs fichiers Paradox, normalement situés dans le même répertoire. Parfois suivant les ouvrages consacrés aux produits Borland, on parlera d'Alias de base de données. C'est une notion que j'ai choisi d'ignorer, car avant de jongler avec les différentes identités d'une base de données, il est préférable de voir comment elle fonctionne.

II - Première étape  : la création du fichier Paradox

Pour cela nous utiliserons l'utilitaire fourni par Borland, appelé « Module Base de données » (pour ceux qui ont une version anglaise : Database Desktop).

On choisit le menu « Fichier->Nouveau->Table… ». On doit avoir l'écran suivant :

On choisit le format Paradox 7 et on valide.

Une nouvelle boîte de dialogue apparaît :

Elle sert à spécifier les champs de notre base de données.
Il faut la remplir de manière à obtenir ceci :

Une fois encore, j'ai choisi que tous les champs soient du type alphanumérique dans un souci de facilité.
Seul le premier champ est du type « numérique auto-incrémenté ».
On enregistre la table sous le nom « Data.db » et on la place dans le même répertoire que notre futur projet.

III - Deuxième étape : la création du projet et sa mise en forme

Sous Delphi, on crée un nouveau projet et tout de suite on l'enregistre (un bon réflexe !). J'ai nommé le projet « Carnet », mais chacun fait ce qu'il veut… L'unité principale est sauvegardée sous « UnitMain ». Ma Form s'appelle « Main ».

Voici son aspect général :

Voici la déclaration des composants telle qu'on pourrait la voir dans le fichier « UnitMain.pas » :

Type
TMain = class(TForm)
DBGrid1: TDBGrid;
DataSource1: TDataSource;
GroupBox1 : TGroupBox;
Menu : TMainMenu;
Table: TTable;
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
Label4: TLabel;
Label5: TLabel;
Label6: TLabel;
Label7: TLabel;
Label8: TLabel;
BoutonRecherche: TButton;
BoutonModifier: TButton;
BoutonNouveau: TButton;
BoutonSupprimer: TButton;
BoutonAnnulerSaisie: TButton;
BoutonValider: TButton;
FiltreNom: TEdit;
RechercheNomEdit :TEdit;
AdresseEdit: TEdit;
PrenomEdit: TEdit;
NomEdit: TEdit;
VilleEdit: TEdit;
TelMaskEdit: TMaskEdit;
CPMaskEdit: TMaskEdit;
Nouveau1: TMenuItem;
Fichier1: TMenuItem;
Modifier1: TMenuItem;
Quitter1: TMenuItem;
Aide1: TMenuItem;
Apropos1: TMenuItem;
BoutonSuivant: TButton;
BoutonPrecedant: TButton;
BoutonFirst: TButton;
BoutonLast: TButton;
...

Je recommande vivement pour la compréhension du tutoriel de nommer les composants de la même façon.

La Form Main fait 650 en largeur (Width) et 230 en hauteur (Heigth).

Les propriétés importantes du TTable sont :

- Active : cette propriété détermine si effectivement l'application est reliée sur la table ou non ;

- DatabaseName : c'est le nom de la base de données ou de son Alias ;

- TableName : c'est le nom de la table. Chaque composant TTable ne peut être relié qu'à une seule et unique table. Dans notre cas (le format Paradox) la table correspond à un fichier.

Pour paramétrer notre TTable (« Table ») :

1. On détermine le nom de notre base de données dans la propriété DatabaseName. Par souci de simplicité, nous allons laisser la base de données dans le même répertoire que notre application. Cette propriété restera donc vide, ce qui signifie au BDE qu'il doit chercher la base de données dans le répertoire de l'application ;

2. On choisit la table que l'on va contrôler par le composant. Si notre propriété DatabaseName est correctement remplie, le nom de notre fichier créé précédemment devrait apparaître dans la listbox. On le sélectionne ;

3. On met la propriété Active à true.

Si tout est correctement configuré, il ne se passe… rien (par contre il y aura des messages d'erreur dans le cas contraire). Voici une vue de l'inspecteur d'objet sur le TTable :

On en profite pour paramétrer le TDBGrid qui va nous servir à visualiser nos manipulations. Pour cela, il faut paramétrer d'abord le TDataSource ! Il sert de lien entre les composants visuels comme les TDBEdit, TDBMemo… et les dérivés de TDataSet comme TTable, TQuery, TADOTable…

Les propriétés importantes du TDataSource sont :

- AutoEdit : sert à déterminer si tous les contrôles visuels peuvent ou non modifier la base de données. Dans notre cas la réponse est false, car la TDBGrid ne sert qu'à visualiser ;

- DataSet : sert à déterminer le composant qui est « relié » à la table, dans notre cas le TTable. Il doit logiquement apparaître dans la listbox. Il faut le sélectionner bien sûr.

Enfin dans la TDBGrid, il faut lui signaler par sa propriété « DataSource » sur quel composant DataSource elle doit se connecter. Prudence étant mère de sûreté, j'ai mis la propriété ReadOnly de la DBGrid sur true. Sur l'exemple j'ai « enjolivé » la grille en modifiant ses propriétés dans ses propriétés Columns et Options. Là le bon goût étant subjectif, je laisse à chacun le soin de faire les modifications qu'il jugera nécessaire…

Voilà la partie visuelle est terminée, on va pouvoir passer à la troisième étape.

IV - Troisième étape  : codage de la partie fonctionnelle

Voyons un peu les principes de fonctionnement de l'application. Son but est de gérer un petit agenda avec adresses et numéros de téléphone.
Nous devons donc prévoir :

- un moyen de saisir les données (pour chaque nouvelle personne que nous rencontrons…) ;

- un moyen de supprimer les données (en cas de brouilles mortelles et irrémédiables…) ;

- un ou plusieurs moyens de retrouver les données (si on ne peut pas le consulter, un carnet ne sert à rien) ;

- un moyen de modifier les données (en cas de déménagement…).

Je rappelle que j'ai fait cette petite application dans un but didactique, mais aussi dans l'optique de m'en servir (et elle m'a servi longtemps…). J'avais donc ajouté quelques fioritures, comme rétracter la zone de saisie.

C'est pour cela que j'ai placé dans l'évènement OnCreate de la Form le code suivant :

procedure TMain.FormCreate(Sender: TObject);
begin
Height:=235; // Rétracte la fenêtre ..
FiltreNom.Text:=''; // Vide le texte de FiltreNom
RechercheNomEdit.Text :=''; // Vide le texte de RechercheNomEdit
end;

Notre application doit comporter des méthodes de modification, insertion, navigation dans les données.

Avant d'aller plus loin, il est intéressant de comprendre comment le TTable accède aux données.
Le TTable est un objet qui dérive de l'Objet TDataSet. Il implémente donc des procédures de navigation et d'ajout/suppression d'enregistrements.

Comme on l'a vu dans notre rappel sur les bases de données, on peut facilement visualiser une table comme un tableau ou les titres des colonnes seraient le nom des champs. C'est ainsi que notre composant TDBGrid représente note fichier Paradox. Sur cette même TDBGrid, on remarque sur la gauche du composant un petit curseur. Il indique l'enregistrement sur lequel pointe le TTable. Car le composant TTable ne pointe que sur un seul et unique enregistrement. Donc il nous faudra impérativement positionner le TTable sur l'enregistrement que l'on veut modifier. Il y a pour cela plusieurs façons de faire. La TDBGrid en implémente une : il suffit de sélectionner l'enregistrement pour que le TTable se positionne dessus. Toutefois si cette méthode est simple à mettre en œuvre, elle est frustrante pour le développeur qui ne maîtrise pas le comportement du TTable.
Sur la Form, nous avons placé quatre boutons : Suivant, Précédent, Premier Enregistrement et Dernier Enregistrement, respectivement nommés : BoutonSuivant, BoutonPrecedant, BoutonFirs et BoutonLast.

Voici le code qu'il faut placer dans chaque évènement OnClick de ces boutons :

procedure TMain.BoutonSuivantClick(Sender: TObject);
begin
Table.Next ; // Déplace le curseur sur l'enregistrement suivant
end;

procedure TMain.BoutonPrecedantClick(Sender: TObject);
begin
Table.Prior ;// Déplace le curseur sur l'enregistrement précédent
end;

procedure TMain.BoutonFirstClick(Sender: TObject);
begin
Table.First; // Déplace le curseur sur le premier enregistrement
end;

procedure TMain.BoutonLastClick(Sender: TObject);
begin
Table.Last; // Déplace le curseur sur le dernier enregistrement
end;

À l'exécution (et sous réserve que votre table contienne des enregistrements !), on visualise très bien le résultat de ces méthodes grâce au curseur de la TDBGrid. Les méthodes Next et Prior servent à se déplacer d'enregistrement en enregistrement dans le sens croissant ou décroissant. Les méthodes First et Last quant à elle servent à se positionner respectivement sur le premier et le dernier enregistrement.

Maintenant que nous savons nous déplacer dans les données, nous allons pouvoir y accéder, c'est-à-dire récupérer les valeurs pour les utiliser dans nos programmes. Pour cela, regardons de plus près le code placé dans l'évènement OnClick de BoutonModifier. Dans notre petit programme, ce bouton sert à afficher les données que nous voulons modifier.

procedure TMain.BoutonModifierClick(Sender: TObject);
begin
// Boucle qui enclenche la procédure de modification d'une fiche client
if((Table.RecordCount=0) and (Table.State <> dsInsert))
// quitte la procédure si la table est vide et qu'on n'est pas en insertion
then exit;
// Activation du bouton Supprimer
BoutonSupprimer.Enabled := true;
// Désactive le bouton OK
BoutonNouveau.Enabled:= false;
// Désactive le menu fichier
Fichier1.Enabled := false;
// Agrandit la fenêtre
Height := 340;
// Récupère le nom et le place dans le TEdit
NomEdit.Text:=Table.FieldByName('Nom').AsString;
// Récupère le prénom et le place dans le TEdit
PrenomEdit.Text:=Table.FieldByName('Prenom').AsString;
// Récupère l'adresse et la place dans le TEdit
AdresseEdit.Text:=Table.FieldByName('Adresse').AsString;
// Récupère la ville et la place dans le TEdit
VilleEdit.Text:=Table.FieldByName('Ville').AsString;
// Récupère le code postal et le place dans le TMaskEdit
TelMaskEdit.Text := Table.FieldByName('Tel').AsString;
// Récupère le téléphone et le place dans le TMaskEdit
CPMaskEdit.Text := Table.FieldByName('CP').AsString;
end;

Cette partie du code sert uniquement à remplir les TEdit avec le contenu des champs. Pour cela, on utilise la méthode FieldByName de la classe TTable. Elle renvoie un pointeur sur un objet TField. Comme l'objet TField sert à accéder à un champ d'une base de données, à partir de cet objet TField on récupère la propriété AsString que l'on copie dans la propriété Text du TEdit (ou TMaskEdit).

Cela sous-entend que l'on s'est positionné avant de vouloir lire les données.

Bien sûr il y a aussi quelques petites fioritures aussi avec les activations de boutons et l'agrandissement de la fenêtre pour afficher les zones de modifications.
On remarquera aussi le test qui vérifie qu'il y a quelque chose à modifier. La propriété RecordCount donne le nombre d'enregistrements dans la table. Si la table est vide, il n'y a rien à modifier, sauf si on insère le premier enregistrement vide. C'est pour cela que l'on teste aussi la propriété Table.State pour savoir si on est en mode insertion… Dans le cas contraire, on quitte la boucle purement et simplement.

Une fois les données affichées et les modifications faites, il nous faut enregistrer notre travail. Pour cela nous allons ajouter sur l'évènement OnClick de BoutonValider le code suivant :

procedure TMain.BoutonValiderClick(Sender: TObject);
begin
// Positionne la table en mode Edition
Table.Edit;
//Place le nom dans le TEdit dans la table
Table.FieldByName('Nom').Value := NomEdit.Text;
//Place le prénom dans le TEdit dans la table
Table.FieldByName('Prenom').Value := PrenomEdit.Text ;
//Place l'adresse dans le TEdit dans la table
Table.FieldByName('Adresse').Value := AdresseEdit.Text ;
//Place la ville dans le TEdit dans la table
Table.FieldByName('Ville').Value := VilleEdit.Text;
//Place le téléphone dans le TEdit dans la table
Table.FieldByName('Tel').Value := TelMaskEdit.Text;
//Place le code postal dans le TEdit dans la table
Table.FieldByName('Cp').Value := CPMaskEdit.Text;
// Valide l'édition dans la base de données
Table.Post;
// Active le menu
Fichier1.Enabled := true;
//Active le bouton Supprimer
BoutonSupprimer.Enabled := true;
//Active le bouton Nouveau
BoutonNouveau.Enabled := true;
//Active le bouton Modifier
BoutonModifier.Enabled := true;
// Rétracte la fenêtre
Height := 235;
end;

Ici nous accédons au champ par la propriété Value. Ce qu'il faut surtout remarquer, c'est que l'on place d'abord la table en mode « édition » avec la méthode Table.Edit. On signifie ainsi que les valeurs que nous plaçons dans les champs vont remplacer celles qui étaient stockées dans le fichier. Une fois que l'on a placé toutes nos valeurs, on signale au composant TTable qu'il doit valider les changements par la méthode Table.Post.

Occupons-nous de la partie « Création d'un nouvel enregistrement ». Pour cela sur l'événement OnClick de BoutonNouveau, on ajoute la procédure suivante :

procedure TMain.BoutonNouveauClick(Sender: TObject);
begin
//Ajoute un enregistrement vide et se positionne dessus
Table.Append;
//Appelle la méthode de modification
BoutonModifierClick(self);
end;

La méthode Append ajoute un enregistrement vide à la fin de la table et surtout positionne le curseur sur le nouvel enregistrement (c'est d'un compliqué…).
Une fois le nouvel enregistrement créé, on appelle la méthode BoutonModifierClick qu'on a définie plus haut.

Enfin ajoutons maintenant la suppression d'enregistrements. On place dans l'évènement OnClick de BoutonSupprimer le code suivant :

procedure TMain.BoutonSupprimerClick(Sender: TObject);
var rep : integer;
begin
rep:= MessageBox(Handle,'Êtes-vous sûr de vouloir supprimer cette personne ?',
'Avertissement',MB_OKCANCEL or MB_ICONSTOP ) ;
if (rep =IDOK)
//Si réponse OK Suppression de l'enregistrement
then Table.Delete;
//Activation du bouton nouveau
BoutonNouveau.Enabled:= true;
//Active l'item du menu Fichier
Fichier1.Enabled := true;
//Désactive le Bouton Supprime
BoutonSupprimer.Enabled := false;
//Taille de la fiche à 235 pixels
Height := 235;
end;

La suppression de l'enregistrement se fait par la méthode Table.Delete. Bien évidemment il faut se positionner d'abord sur l'enregistrement à supprimer.
De plus, le BDE ne demandant aucune confirmation pour éviter les erreurs, c'est à nous de la coder ! Pour cela on utilise la fonction de l'API MessageBox(…) qui invoque une boîte de dialogue.

Il existe deux autres fonctionnalités intéressantes du composant TTable. Imaginons que nous avons un carnet d'adresses de ministre. Pour retrouver le numéro de téléphone du coiffeur, s’il faut appuyer quelques dizaines de fois sur le bouton « Suivant », on va se lasser très vite.

On va donc étudier la méthode Locate(…) à l'aide du code suivant que l'on saisit dans l'évènement OnClick de BoutonRecherche (celui où c'est marqué Go ! ).

procedure TMain.BoutonRechercheClick(Sender: TObject);
var Option : TLocateOptions;
begin
//Test pour vérifier si le Edit est vide
if RechercheNomEdit.Text <> '' then
begin
//Ajout de l'option "ne fait pas attention à la casse" des caractères
Option := [loPartialKey];
//Recherche l'enregistrement suivant les critères
Table.Locate('Nom',RechercheNomEdit.Text,Option);
end;
end;

La méthode Locate permet de trouver un enregistrement à partir d'un champ. Ici nous recherchons un nom. Donc quand on va appeler la méthode Locate, le composant TTable va se positionner sur le premier enregistrement dont le champ « Nom » a pour valeur le nom que l'on a saisi dans le TEdit . La partie la plus délicate est l'instanciation d'un objet TLocateOptions. Cet objet sert à déterminer les options de tris. Deux choix sont possibles : ignorer la casse, et regarder partiellement la valeur. Les deux choix peuvent être simultanés.

La deuxième fonctionnalité intéressante est le filtre. Nous allons implémenter dans notre petit programme un filtre sur les noms. Plaçons ce code dans les événements OnChange et OnExit du TEdit FiltreNom.

procedure TMain.FiltreNomChange(Sender: TObject);
var Filtre : string;
begin
// Construction du Filtre avec le texte saisi dans la zone TEdit
Filtre := 'Nom =''' + FiltreNom.Text + '*''';
// Teste s'il n'y a rien dans le TEdit
if FiltreNom.Text =''  then
begin
// Arrête le filtrage de la table
Table.Filtered:=false;
// Quitte la méthode
exit;
end;
// Mise en place du filtre
Table.Filter := Filtre;
// Activation du filtre
Table.Filtered := true;
end;

La construction du filtre se base sur le modèle suivant : Nom du champ, Opérateur de comparaison, Valeur de test.
Concrètement le champ est une chaîne de caractères AnsiString que l'on place dans la propriété Filter du composant TTable.
Une fois la propriété Filter renseignée, il faut activer le filtre en mettant la propriété Filtered à true.
On remarquera ici l'usage du caractère '*' dans le filtre qui est un caractère générique.

Et voilà il n'y a plus qu'à compiler le tout et observer.

Normalement on doit obtenir ceci :

On va maintenant remplir notre carnet d'adresses, on clique sur le bouton Nouveau :

Une fois que nous aurons entré plus d'enregistrements, nous pourrons tester une à une les méthodes que nous avons implémentées.

Bon courage

Sources

Laurent BERNE, consultant
(TeamB-fr)