# Record collection (JavaScript)

Création d'une petite base de données pour trier votre discothèque
Projet de Documentation GNOME
gnome-doc-list@gnome.org
Johannes Schmid
jhs@gnome.org
Marta Maria Casetti
mmcasettii@gmail.com 2013

# Record collection

      Dans ce tutoriel, vous allez apprendre :

        comment vous connecter à une base de données en utilisant « libgda »
        comment insérer et parcourir des enregistrements dans une table de base de données

# Introduction

Cet exemple utilise le langage Javascript. Nous allons vous montrer comment vous connecter à une base de données et l'utiliser à partir d'un programme GTK en utilisant la bibliothèque GDA (GNOME Data Access). Vous devez bien sûr avoir déjà installé cette dernière.

La bibliothèque GDA (GNOME Data Access) vous permet d'accéder de manière universelle à différentes sortes et types de sources de données, depuis les plus traditionnels systèmes de bases de données relationnelles, jusqu'à des sources aussi diverses qu'un serveur de messagerie, un répertoire LDAP, etc. Pour de plus amples informations et pour une documentation et l'API complète, consultez le Site Web de GDA.

Bien que la plus grosse partie du programme se réfère à l'interface utilisateur (GUI), nous allons orienter notre tutoriel sur les fonctions bases de données (avec des explications sur certaines autres fonctions que nous jugeons pertinentes). Pour de plus amples informations sur les programmes GNOME en Javascript, consultez le tutoriel Programme visionneur d'images.

# Création d'un projet dans Anjuta

Avant de commencer à programmer, vous devez ouvrir un nouveau projet dans Anjuta. Ceci crée tous les fichiers qui vous sont nécessaires pour construire et exécuter votre programme plus tard. C'est aussi utile pour tout regrouper en un seul endroit.

Lancez Anjuta et cliquez sur Fichier Nouveau Projet pour ouvrir l'assistant de création de projet.

Cliquez sur l'onglet JS, choisissez JavaScript générique, cliquez sur Continuer et renseignez les champs requis avec vos informations. Mettez discotheque (sans accent) comme nom du projet et répertoire.

Cliquez sur Appliquer et votre projet est créé. Ouvrez src/main.js depuis l'onglet Projet ou l'onglet Fichiers. Il contient un exemple de programme très basique.

# Structure du programme

Cet exemple est une application GTK simple (avec une seule fenêtre) pouvant insérer des enregistrements dans une table de base de données et également consulter tous les enregistrements de cette table. Cette table possède deux champs : `id`, un entier et `name`, une variable de type caractère. La première section de l'application (en haut) vous permet d'insérer un enregistrement dans la table. La dernière (en bas) vous permet d'afficher tous les enregistrements de cette table. Son contenu est mis à jour à chaque fois qu'un nouvel enregistrement est inséré et au démarrage de l'application.

# Début du plaisir

Commençons par examiner le squelette du programme :

const GLib = imports.gi.GLib; const Gtk = imports.gi.Gtk; const Gda = imports.gi.Gda; const Lang = imports.lang;

function Demo () { this._init (); }

Demo.prototype = {

_init: function () { this.setupWindow (); this.setupDatabase (); this.selectData (); } }

Gtk.init (null, null);

var demo = new Demo ();

Gtk.main ();

Lignes 1‒4 : importations initiales. Faites surtout attention à la ligne 3, qui commande à Javascript l'importation de la bibliothèque GDA, la cible de ce tutoriel.

Lignes 6‒17 : définition de la classe `Demo`. Faites surtout attention aux lignes 13‒15, où nous appelons 3 méthodes qui font tout le travail. Elles sont détaillées plus bas.

Lignes 19‒23 : lancement de l'application.

# Conception de l'application

Examinons la méthode `setupWindow`. C'est elle qui crée l'interface utilisateur. Comme l'interface utilisateur n'est pas notre sujet, nous en expliquons seulement les parties pertinentes.

setupWindow: function () { this.window = new Gtk.Window ({title: "Data Access Demo", height_request: 350}); this.window.connect ("delete-event", function () { Gtk.main_quit(); return true; });

// main box var main_box = new Gtk.Box ({orientation: Gtk.Orientation.VERTICAL, spacing: 5}); this.window.add (main_box);

// first label var info1 = new Gtk.Label ({label: "<b>Insert a record</b>", xalign: 0, use_markup: true}); main_box.pack_start (info1, false, false, 5);

// "insert a record" horizontal box var insert_box = new Gtk.Box ({orientation: Gtk.Orientation.HORIZONTAL, spacing: 5}); main_box.pack_start (insert_box, false, false, 5);

// ID field insert_box.pack_start (new Gtk.Label ({label: "ID:"}), false, false, 5); this.id_entry = new Gtk.Entry (); insert_box.pack_start (this.id_entry, false, false, 5);

// Name field insert_box.pack_start (new Gtk.Label ({label: "Name:"}), false, false, 5); this.name_entry = new Gtk.Entry ({activates_default: true}); insert_box.pack_start (this.name_entry, true, true, 5);

// Insert button var insert_button = new Gtk.Button ({label: "Insert", can_default: true}); insert_button.connect ("clicked", Lang.bind (this, this._insertClicked)); insert_box.pack_start (insert_button, false, false, 5); insert_button.grab_default ();

// Browse textview var info2 = new Gtk.Label ({label: "<b>Browse the table</b>", xalign: 0, use_markup: true}); main_box.pack_start (info2, false, false, 5); this.text = new Gtk.TextView ({editable: false}); var sw = new Gtk.ScrolledWindow ({shadow_type:Gtk.ShadowType.IN}); sw.add (this.text); main_box.pack_start (sw, true, true, 5);

this.count_label = new Gtk.Label ({label: "", xalign: 0, use_markup: true}); main_box.pack_start (this.count_label, false, false, 0);

this.window.show_all (); },

Lignes 22 et 27 : création des 2 entrées (pour les 2 champs) dans lesquelles l'utilisateur saisit les choses à insérer dans la base de données.

Lignes 31‒34 : création du bouton « Insert ». Le signal `clicked` de ce bouton est connecté à la méthode privée `_insertClicked` de la classe. Cette méthode est décrite ci-dessous.

Ligne 39 : création de l'élément graphique (`TextView`) où nous affichons le contenu de la table.

Ligne 44 : création de l'étiquette où nous affichons le nombre d'enregistrements dans la table. Au début, elle est vide et est mise à jour ultérieurement.

# Connexion à la base de données et initialisation

Le code permettant de se connecter à la base de données est dans la méthode `setupDatabase` ci-dessous :

setupDatabase: function () { this.connection = new Gda.Connection ({provider: Gda.Config.get_provider("SQLite"), cnc_string:"DB_DIR=" + GLib.get_home_dir () + ";DB_NAME=gnome_demo"}); this.connection.open ();

try { var dm = Gda.execute_select_command (this.connection, "select * from demo"); } catch (e) { Gda.execute_non_select_command (this.connection, "create table demo (id integer, name varchar(100))"); } },

Lignes 2‒3 : création de l'objet `Connection` de GDA. Il faut transmettre quelques propriétés à son constructeur :

`provider` : l'un des fournisseurs reconnus par GDA. GDA prend en charge SQLite, MySQL, PostgreSQL, Oracle et beaucoup d'autres. Pour les besoins de l'exemple, nous utilisons une base de données SQLite car elle est déjà incluse par défaut dans la plupart des distributions et elle est facile à utiliser (elle n'a besoin que d'un fichier comme base de données).

`cnc_string` : la chaîne de connexion. Elle peut être différente en fonction du fournisseur. La syntaxe pour SQLite est : `DB_DIR=CHEMIN;DB_NAME=NOM2FICHIER`. Dans cet exemple, nous accédons à une base de données nommée gnome_demo dans le dossier personnel de l'utilisateur (notez l'appel à la fonction `get_home_dir` de GLib).

Si le fournisseur n'est pas reconnu par GDA, ou si la chaîne de connexion n'est pas complète, la ligne 2 provoque une exception. Donc, en pratique, il faut gérer cette exception grâce à l'instruction JavaScript `try`...`catch`.

Ligne 4 : ouverture de la connexion. Pour le fournisseur SQLite, si la base de donnée n'existe pas, elle est créée à cette étape.

Lignes 6‒10 : tentative de faire une simple instruction « select » pour vérifier que la table existe (ligne 7). Si elle n'existe pas (parce que la base de données vient juste d'être créée), cette instruction provoque une exception qu'il faut gérer grâce au bloc `try`...`catch`. Dans ce cas, il faut exécuter l'instruction « create table » pour créer la table (ligne 9).

Pour exécuter les commandes SQL ci-dessus, nous utilisons les fonctions globales de GDA, `execute_select_command` et `execute_non_select_command`. Elles sont simples d'utilisation et ne requièrent que deux arguments : l'objet `Connection` et la commande SQL à analyser.

À ce niveau, nous avons configuré la base de données et cette dernière est fonctionnelle.

# Sélection

Après connexion à la base de données, le constructeur de notre exemple appelle la méthode `selectData`. C'est elle qui récupère tous les enregistrements de la table et qui les affiche dans l'élément graphique `TextView`. Regardons à quoi elle ressemble :

selectData: function () { var dm = Gda.execute_select_command (this.connection, "select * from demo order by 1, 2"); var iter = dm.create_iter ();

var text = "";

while (iter.move_next ()) { var id_field = Gda.value_stringify (iter.get_value_at (0)); var name_field = Gda.value_stringify (iter.get_value_at (1));

text += id_field + "\t=>\t" + name_field + '\n'; }

this.text.buffer.text = text; this.count_label.label = "<i>" + dm.get_n_rows () + " record(s)</i>"; },

Ligne 2 : la commande `SELECT`. Nous utilisons la fonction globale `execute_select_command` de GDA pour cela. Elle retourne un objet `DataModel` qui servira plus tard à récupérer les lignes.

Ligne 3 : création d'un objet `Iter` qui sert à itérer sur les enregistrements de `DataModel`.

Ligne 7 : Itération sur tous les enregistrements en les récupérant à l'aide de l'objet `Iter`. À cet endroit, la variable `Iter` contient les informations trouvées. La méthode `move_next` retourne le message `false` quand elle atteint le dernier enregistrement.

Lignes 8‒9 : nous faisons deux choses dans chaque ligne :

Utilisez la méthode `get_value_at` d'`Iter` qui n'a besoin que d'un argument : le numéro de colonne à récupérer, en commençant par 0. Comme notre commande `SELECT` ne retourne que deux colonnes, nous récupérons donc les colonnes 0 et 1.

La méthode `get_value_at` retourne l'information sur les champs au format `GValue` de GLib. Pour convertir ce format en chaîne de caractères, utilisez simplement la fonction globale `value_stringify` de GDA. C'est ce que nous faisons ici puis nous enregistrons les résultats dans les variables `id_field` et `name_field`.

Ligne 11 : concaténation des deux chaînes pour former une seule ligne de texte en les séparant par `"=>"` et enregistrement dans la variable `text`.

Ligne 14 : à la fin de la boucle, tous les enregistrements sont formatés dans la variable `text`. À cette ligne, nous attribuons le contenu de cette variable au `TextView`.

Ligne 15 : affichage du nombre d'enregistrements dans la table avec la fonction `get_n_rows` de `DataModel`.

# Insertion

Bien, nous savons comment nous connecter à une base de données et comment sélectionner des lignes dans une table. Il est temps maintenant d'apprendre à faire une insertion à l'aide de la commande `INSERT` dans la table. Souvenez-vous, dans la méthode `setupWindow`, nous avions relié le signal `clicked` du bouton Insert à la méthode `_insertClicked`. Voyons la mise en place de cette méthode.

_insertClicked: function () { if (!this._validateFields ()) return;

// Gda.execute_non_select_command (this.connection, // "insert into demo values ('" + this.id_entry.text + "', '" + this.name_entry.text + "')");

var b = new Gda.SqlBuilder ({stmt_type:Gda.SqlStatementType.INSERT}); b.set_table ("demo"); b.add_field_value_as_gvalue ("id", this.id_entry.text); b.add_field_value_as_gvalue ("name", this.name_entry.text); var stmt = b.get_statement (); this.connection.statement_execute_non_select (stmt, null);

this._clearFields (); this.selectData (); },

Nous avons appris comment utiliser les fonctions bien pratiques `execute_select_command` et `execute_non_select_command` de GDA pour appliquer rapidement des commandes SQL sur la base de données. GDA nous permet aussi de construire une instruction SQL indirectement en utilisant son objet `SqlBuilder`. Quels en sont les avantages ? GDA crée l'instruction SQL dynamiquement et celle-ci est compatible avec le fournisseur de connexion utilisé (elle utilise le même langage SQL que celui du fournisseur). Voyons en détail le programme :

Lignes 2‒3 : vérification du bon renseignement par l'utilisateur de tous les champs. Le code de la méthode privée `_validateFields` est vraiment simple et vous pouvez le consulter dans le programme source complet de l'exemple.

Ligne 5 : la façon la plus rapide pour faire l'insertion à l'aide de la commande `INSERT`. Celle-ci est mise en commentaire car nous voulons vous montrer comment utiliser l'objet `SqlBuilder` pour construire une instruction SQL qui soit indépendante de la base de données.

Ligne 7 : création de l'objet `SqlBuilder`. Nous devons transmettre le type d'instruction que nous voulons construire. Cela peut être `SELECT`, `UPDATE`, `INSERT` ou `DELETE`.

Ligne 8 : détermination du nom de la table sur laquelle l'instruction construite va agir (le code `INSERT INTO demo` est généré)

Lignes 9‒10 : détermination des champs et de leurs valeurs qui font partie de l'instruction. Le premier argument est le nom du champ (comme dans la table). Le second est la valeur pour ce champ.

Ligne 11 : fabrication de l'objet `Statement` généré dynamiquement et qui représente une instruction SQL.

Ligne 12 : enfin, exécution de l'instruction SQL (`INSERT`).

Ligne 14 : effacement des champs « id » et « name » à l'écran. Le code de la méthode privée `_clearFields` est vraiment simple et vous pouvez le consulter dans le programme source complet de l'exemple.

Ligne 15 : mise à jour de l'affichage à l'écran en faisant un nouveau `SELECT`.

Vous pouvez aussi faire usage de paramètres pour construire l'instruction. En utilisant les objets `SqlBuilder` et des paramètres, vous êtes moins exposé à des attaques du type injection SQL. Consultez la documentation GDA pour plus d'informations sur les paramètres.

# Exécution de l'application

Tout le programme nécessaire doit maintenant être en place, donc essayez de l'exécuter. Vous disposez maintenant d'une base de donnée pour votre discothèque !

# Implémentation de référence

Si vous rencontrez des difficultés avec ce tutoriel, comparez votre programme à ce programme de référence.