Créer une application Perl autour de MySQL : Mise en place (1/3)

Magazine
Marque
GNU/Linux Magazine
Numéro
168
|
Mois de parution
février 2014
|
Domaines


Résumé
C'est l'histoire d'un hacker à qui on a demandé de créer des pages web pour le suivi de la production logicielle de la société. Historiquement, toutes les informations étaient gérées à la main, ce qui est une solution inacceptable pour tout hacker qui se respecte.

Body

Le but de cette série d'articles est de vous montrer comment on peut utiliser Perl, MySQL, phpMyAdmin, les modules DBIx::Class, DBIx::Class::Schema::Loader et HTML::Tiny pour gérer ces informations d'une manière plus efficace. Ces articles vont donner une vision d'ensemble sur une solution mettant en jeu plusieurs technologies au lieu de se focaliser sur un domaine bien précis.

Ce premier article va expliquer les besoins de l'application, la structure de la base de données et comment créer cette structure dans une base MySQL avec phpMyAdmin. Au passage, on verra les principes de base des relations entre les tables et l'utilisation des index et des clés étrangères (foreign keys) pour que MySQL assure la cohérence des entités de l'application.

1. Introduction

Le problème à résoudre : comme toute société informatique qui se respecte, nous produisons plusieurs produits logiciels. Chaque produit évolue et cette évolution est traduite en termes de version. Et comme dans toute société, les noms de code et numéros de version utilisés pendant le développement correspondent rarement avec ceux décidés (tardivement) par le marketing. Devant cette profusion de noms de codes et de versions, il est difficile de s'y retrouver. J'ai donc été chargé d'écrire une application qui fournit des pages d'information résumant la situation.

Donc, pour générer ces pages, il faudra gérer la correspondance entre :

- un produit tel qu'il est vu par le marketing (et par le client) avec :

-- un nom de produit en général évocateur de simplicité et de performances pour appâter le client,

-- quelques informations supplémentaires (release notes) ;

- une version de produit dont le numéro est en général décidé tard dans le cycle de développement ;

- Cette version de produit est déclinée en fonction de l'environnement cible : distribution (RHEL 5 ou RHEL 6) et architecture (i386 ou x86_64) et est livrée dans un fichier tar.

- Un ensemble de paquets RPM contenus dans ces fichiers tar.

En termes de relations, on va avoir :

- un produit va contenir plusieurs versions marketing,

- chaque version marketing va contenir plusieurs fichiers tar,

- chaque fichier tar va contenir plusieurs paquets (RPM dans ce cas de figure),

- un paquet peut faire partie de plusieurs fichiers tar.

Une fois qu'on a tout ça en place, le but est de remplir les données des versions avec Perl et DBIx::Class, puis de produire des pages HTML décrivant chaque release et ses différents composants.

Pour ne pas compliquer cet article, on va ignorer le problème de suivi des bogues et des corrections associés à chaque version.

2. La base de données

2.1 Structure des données

On a vu dans l'introduction que nous avons affaire à des données structurées avec des relations entre les différentes entités :

- relation « un avec plusieurs » (one-to-many) entre les produits, versions marketing et fichiers tar ;

- relation « plusieurs avec plusieurs » (many-to-many) entre les fichiers tar et les paquets.

On verra comment mettre en place ces relations dans la base de données.

3. Installation de tout le bazar

Sous Debian, faites :

sudo aptitude install mysql-server phpmyadmin

Notez qu'un serveur web va être installé par le jeu des dépendances.

aptitude va vous proposer d'installer une base de données dédiée à phpMyAdmin. Celle-ci servira à stocker des informations relatives aux structures de vos bases de données et aux fonctions avancées de phpMyAdmin. Accepter cette option est conseillé.

Vérifiez aussi que l'extension de sécurité suhosin (paquet php5-suhosin sous Debian) n'est pas installée. Celle-ci pose des problèmes au bon fonctionnement de phpMyAdmin.

Une fois qu'aptitude a fini son boulot, votre phpMyAdmin tout neuf est accessible avec votre butineur favori à l'adresse : http://locahost/phpmyadmin.

3.1 Création de la base de données et de ses utilisateurs

Une fois dans l'interface de phpMyAdmin, cliquez sur Databases pour créer la base de données « Integ ».

Ensuite, il nous faut deux utilisateurs :

- integ_user, qui pourra lire les données et se connecter à la DB sans utiliser de mot de passe ;

- integ_mgr, qui pourra mettre à jour les données et devra utiliser un mot de passe pour se connecter.

Les utilisateurs sont ajoutés en deux étapes :

- Création avec l'onglet Privileges et Add a new user. Si nécessaire, des privilèges globaux (c'est-à-dire pour toutes les bases du serveur) peuvent être attribués.

- Réglage fin des privilèges base par base. Ce réglage est accessible dans le tableau User overview dans l'onglet Privileges.

Vous pouvez vérifier le résultat de la configuration des privilèges :

- Soit dans l'onglet Databases et le lien Check Privileges de la ligne integ. Dans le tableau Users having access to "Integ", integ_user doit avoir juste un privilège « SELECT ».

- Soit avec la commande :

$ mysql -u integ_user Integ

[ ... ]

mysql>

La création de l'utilisateur integ_mgr se fera avec une procédure similaire, mais en mettant un mot de passe et en attribuant les privilèges : « SELECT », « INSERT », « UPDATE » et « DELETE » pour la base « Integ ».

4. Création des tables

4.1 La table « Products »

Une fois le lien « Integ » cliqué dans le menu de gauche, phpMyAdmin va vous montrer la structure (vide) de la base « Integ » et vous inviter à créer votre première table, « Products » avec 3 colonnes :

- La colonne « id », qui va être la clef primaire de cette table. « id » est de type entier (« INT »), avec un index « PRIMARY », avec « AUTO INCREMENT » sélectionné ;

- La colonne « name », pour le nom marketing du produit. « name » est de type « VARCHAR », de 32 caractères de longueur, index « UNIQUE ». Pourquoi cet index ? Le marketing ne sortira jamais simultanément deux produits différents ayant le même nom. Cet index « UNIQUE » permet de faire respecter cette règle par la DB. Ça vous permet aussi de ne pas vérifier cette contrainte dans votre code (ou dans toute autre application qui voudrait exploiter cette base) ;

- La colonne « home_page » pour les informations internes sur le produit. « home_page » est de type « VARCHAR », de 256 caractères de longueur, et potentiellement « NULL » (des fois que le produit soit créé dans la base avant que sa home page ne soit disponible).

Derniers points importants :

- Le module DBIx::Class utilisé dans la 2e partie de cet article ne fonctionne pas si le nom de la colonne contient des espaces. C'est pourquoi un souligné («_») est utilisé.

- Le « Storage engine » doit être « InnoDB ». (On verra dans cet article pourquoi).

Enfin, n'oubliez pas de cliquer sur Save en bas de page.

4.2 Mais pourquoi 2 colonnes ?

Vu que le nom du produit doit être unique, on pourrait être tenté de s'en servir comme clef primaire et de se débarrasser de la colonne id. C'est une solution, certes fonctionnelle, mais qui présente un inconvénient majeur : le nom du produit devra aussi être utilisé dans les tables qui le référencent. Pour cet article, il devrait ainsi être utilisé dans la table qui contiendra les versions de produit. Si jamais le nom du produit change, il faudra retrouver et modifier toutes ces utilisations du nom du produit.

4.3 La table « ProductVersions »

Cette table a besoin d'une clef primaire « id », d'un champ « version » et d'un champ « date ». La création de ces colonnes est similaire aux colonnes de la table « Products ».

4.4 Relier un produit avec ses versions

On a maintenant deux jolies tables, mais elles ne sont pas reliées. Il faut créer la relation entre le produit (singulier) et ses versions (pluriel). Il s'agit d'une relation « 1 vers n ». En termes de base de données, cette relation est établie en créant dans la table « ProductVersions » une colonne « product_id » qui contiendra l'« id » du produit correspondant (« product_id » doit être de type « INT » avec Index à « INDEX »). Dans cette table, plusieurs versions pourront référencer le même « id », donc le même produit. Mais une version ne peut référencer qu'un seul produit. On a bien établi une relation « 1 vers n ».

Mais, va-t-on devoir coder de quoi vérifier la cohérence entre « product_id » et les « id » disponibles dans la table « Products » ?

4.5 Mieux relier le produit et ses versions : les clefs étrangères

J'ai pratiqué dans une vie antérieure le codage de cette vérification. Eh bien, ce n'est guère passionnant. Heureusement, les bases de données modernes fournissent maintenant un mécanisme assurant cette cohérence : les clefs étrangères (foreign keys, dans la langue de Shakespeare).

On peut déclarer à la DB que la valeur de « product_id » doit être présente dans la colonne « id » de la table « Products ». Ceci fait, la cohérence est assurée. Mais il y a encore mieux pour la maintenance des données par la suite.

Qu'advient-il si un produit est supprimé du catalogue ? Devra-t-on supprimer une par une les versions de la DB avant de pouvoir supprimer le produit ? Eh bien, non. MySQL offre aussi la possibilité de paramétrer le comportement en cas de destruction d'une ligne produit : soit interdire, soit passer à « NULL » (mettre le « product_id » à 0, rendant la version orpheline), soit propager (toutes les versions du produit sont détruites).

C'est cette dernière option qui nous intéresse.

Notez que les foreign keys sont disponibles avec le « Storage engine » « InnoDB », mais pas avec « MyISAM », qui est proposé par défaut. Si vous avez oublié ce détail, ce n'est pas grave, vous pouvez le changer en allant modifier ce paramètre dans l'onglet Operations de la table restée coincée en « MyISAM » (cherchez « Storage Engine »).

Trêve de théorie, on peut ajouter cette relation dans la page « Structure » de la table « ProductVersions » avec le lien « Relation View » (si absent, voir le problème « InnoDB »). Si vous ne voyez pas de ligne « product_id », ajoutez un index sur cette colonne). Et là, mettez en place la « relation interne » (utilisé par phpMyAdmin) et la « Foreign key » (pour MySQL) avec l'option « ON DELETE: CASCADE ».

On peut aussi ajouter cette relation avec l'interface graphique « designer ». Cette interface est disponible en retournant dans la page de la base « Integ », onglet More, bouton designer.

Vous devriez voir les deux tables non reliées. Cliquez sur le bouton Create relation et suivez les instructions :

- Sélectionnez la clef référencée : « id » dans la table « Products »,

- Sélectionnez la clef étrangère : « product_id » de la table « ProductVersions »,

- Choisissez « cascade » pour l'option « on delete ».

Et devant vos yeux émerveillés, voyez ceci apparaître :

products-productversions

(L'auteur admet ne pas aimer PHP, adorer phpMyAdmin et assumer ses contradictions...).

4.6 Blinder un peu plus

MySQL peut aussi assurer l'unicité de chaque couple produit-version. Il suffit de déclarer un index de type unique pour les deux colonnes « product_id » et « version ». Ça vous évitera de créer des lignes inutiles si votre code utilise au mauvais endroit une instruction de type « créer » au lieu du type « chercher ou créer ». (L'utilisation de ces fonctions sera expliquée plus en détails dans un article ultérieur).

4.7 La table « TarBalls »

Elle est créée de la même façon que la table « ProductVersions » avec une clef primaire « id », une « url » de type « varchar/128 », index « UNIQUE » et enfin, une clef étrangère « product_version_id » pointant sur l'id de la table « ProductVersions ».

4.8 La table « Packages » et la relation m-vers-n

Elle contient la colonne « id » et le nom du paquet (triplet nom-version.arch.rpm).

Mais là, ça se complique : un fichier tar peut contenir plusieurs paquets et chaque paquet peut faire partie de plusieurs fichiers tar. C'est une relation m-vers-n. Une seule colonne tarball_id dans la table « Packages » ne suffit pas.

La technique traditionnelle est d'utiliser une table intermédiaire pour relier tarball et paquet avec deux colonnes : une pour stocker l'id du tarball et une pour stocker l'id du paquet. Par exemple, si on crée un tarball (avec un id 42) contenant deux paquets (un déjà connu avec un id à 99 et un nouveau avec un id à 1024), on ajoutera dans la base :

- une ligne avec un id 42 dans la table « TarBalls »,

- une ligne avec un id 1024 pour le nouveau paquet dans la table « Packages »,

- deux lignes (42-99 et 42-1024) pour matérialiser les deux relations paquets-tarballs dans la table intermédiaire (nommée « TarballPackages »)

Cette table intermédiaire a :

- une colonne « tarball_id » de type entier, clef étrangère sur la colonne « id » de la table « TarBalls »,

- une colonne « package_id » de type entier, clef étrangère sur la colonne « id » de la table « Packages »,

- une clef primaire constituée des deux colonnes « tarball_id » et « package_id ».

Ce dernier point est important pour la suite avec le module Perl DBIx::Class::Schema::Loader. Ce module utilise des heuristiques assez contraignantes pour essayer d'interpréter correctement les relations entre les tables. Pour générer une relation « many-to-many » entre 2 classes DBIx::Class, la table de relation doit respecter ces règles.

Voici le schéma complet présenté par le « designer » de phpMyAdmin (les tables sont un peu maigres, mais c'est pour le bien de cet article) :

complete-schema

5. Suite au prochain épisode

Maintenant que la persistance des données est assurée par la base de donnée « Integ », il reste à voir dans un prochain article :

- Comment utiliser dbcidump et DBIx::Class::Schema::Loader pour mettre en place une interface entre un programme Perl et la base de données en écrivant un minimum de code ;

- Le module DBIx::Class pour effectuer des requêtes sur la base de données et exploiter les relations mises en place dans cet article.


Sur le même sujet

Godot pour coder des jeux, mais pas seulement !

Magazine
Marque
GNU/Linux Magazine
HS n°
Numéro
109
|
Mois de parution
juillet 2020
|
Domaines
Résumé

Godot peut être employé pour développer des jeux en 2D ou en 3D, mais il est également possible de réaliser des interfaces pour des applications plus professionnelles. Le « petit » plus sera de ne développer qu'une seule fois pour de multiples systèmes...

Créez une fake webcam pour modifier l'image de vos visioconférences

Magazine
Marque
GNU/Linux Magazine
Numéro
239
|
Mois de parution
juillet 2020
|
Domaines
Résumé

Avec le confinement, le nombre de visioconférences a augmenté de manière exponentielle. Malheureusement, seul Zoom propose de modifier l'arrière-plan de ce que filme votre caméra, masquant ainsi votre capharnaüm. Je vous propose donc d'utiliser Python et OpenCV pour créer un faux périphérique de webcam utilisable avec n'importe quelle application et sur lequel nous pourrons modifier l'image...

Premiers pas avec GDScript et Godot

Magazine
Marque
GNU/Linux Magazine
HS n°
Numéro
109
|
Mois de parution
juillet 2020
|
Domaines
Résumé

Le langage de scripting par défaut de Godot est le GDScript. Plutôt que de se lancer dans du C# ou encore du Python comme cela est possible, autant utiliser le langage natif du logiciel (surtout quand il s'inspire de Python…).

Écran e-paper NFC : une histoire d'exploration et de code

Magazine
Marque
Hackable
Numéro
34
|
Mois de parution
juillet 2020
|
Domaines
Résumé

Il est difficile de trouver un titre adéquat pour le contenu qui va suivre, car le matériel dont il sera question est tout aussi atypique qu'absolument captivant en termes de fonctionnement. Pire encore, ce n'est pas tant le matériel qui importe que l'approche nécessaire pour obtenir exactement le comportement attendu. Il sera donc ici question de NFC, de papier électronique (e-paper), de C et de la maxime voulant « qu'à cœur vaillant, rien d'impossible ». Sans attendre, embarquez avec moi dans une petite aventure qui, je l'espère, vous apprendra autant qu'elle m'a appris...

À nous, Markdown !

Magazine
Marque
GNU/Linux Magazine
HS n°
Numéro
109
|
Mois de parution
juillet 2020
|
Domaines
Résumé

Le Markdown est devenu incontournable. Il est facilement manipulable, stockable, versionnable et partageable. Un moteur de rendu automatique peut transformer toute la substantifique moelle de l’auteur en un ouvrage broché digne de la Bible de Gutenberg. Alors, pourquoi ne pas appliquer ce modèle à l’écriture de nouveaux articles pour notre cher magazine ?

Par le même auteur

Créer une application Perl autour de MySQL : Intégration avec Mojolicious, HTML::Tiny et HTML::FormHandler (3/3)

Magazine
Marque
GNU/Linux Magazine
Numéro
169
|
Mois de parution
mars 2014
|
Domaines
Résumé
Reprenons l'histoire du hacker à qui on a demandé de créer des pages web pour le suivi de la production logicielle de la société. Comme tout hacker qui se respecte, il préfère que la machine fasse son boulot à sa place. Pour ce faire, il a mis en place une base de données MySQL, un ORM (DBIx:Class). Reste maintenant à fournir un serveur web pour exploiter les données de cette base.

Créer une application Perl autour de MySQL : DBIx::Class (2/3)

Magazine
Marque
GNU/Linux Magazine
Numéro
168
|
Mois de parution
février 2014
|
Domaines
Résumé

Reprenons l'histoire du hacker à qui on a demandé de créer des pages web pour le suivi de la production logicielle de la société. Comme tout hacker qui se respecte, il préfère que la machine fasse son boulot à sa place. Pour ce faire, il a mis en place une base de données MySQL. Reste maintenant à utiliser Perl et DBIx::Class pour accéder aux données de cette base.

P... de proxy, le retour

Magazine
Marque
GNU/Linux Magazine
Numéro
152
|
Mois de parution
septembre 2012
|
Domaines
Résumé
L'article « P... de proxy» a expliqué comment utiliser tinyproxy pour se simplifier la vie sur un ordinateur nomade. Cette suite (c'est la mode au cinéma) va expliquer comment étendre la solution proposée pour gérer d'autres proxys (genre proxy-cache pour serveur de paquets) et surtout faire une commutation automatique de proxy pour atteindre le Saint-Graal des hackers : la machine s'occupe de tout et moi de rien :-)

Config::Model - Créer un éditeur graphique de configuration avec Perl (2e partie)

Magazine
Marque
GNU/Linux Magazine
Numéro
120
|
Mois de parution
octobre 2009
|
Résumé
Dans un article précédant [GLMF], nous avons vu comment créer la partie graphique d'un éditeur de configuration en précisant la structure et les contraintes des données du fichier /etc/ssh/sshd_config. Config::Model va utiliser cette structure (le modèle de la configuration de sshd_config) pour générer l'interface graphique. Mais il reste à pouvoir charger les données du fichier et les ré-écrire. Nous allons voir dans cette deuxième partie comment utiliser l'API de Config::Model pour lire et écrire les données de sshd_config.

Config::Model – Créer un éditeur graphique de configuration avec Perl (1ère partie)

Magazine
Marque
GNU/Linux Magazine
Numéro
117
|
Mois de parution
juin 2009
|
Résumé
La configuration d'une application est très souvent le premier obstacle que doit franchir un utilisateur avant de pouvoir utiliser une application. Le plus souvent, l'utilisateur est dirigé vers un fichier qu'il doit éditer avec « son éditeur favori ». Peu d'applications proposent une interface plus conviviale. Pour combler cette lacune, cet article décrit comment créer un éditeur de configuration d'une manière simple et maintenable. Dans la première partie de cet article, nous allons spécifier le modèle de sshd_config, c'est-à-dire sa structure et ses contraintes. Ce modèle permettra à Config::Model de générer l'interface graphique. Nous verrons dans une seconde partie comment lire et écrire les données de sshd_config pour les charger dans l'interface.