feat: Add data structure for DictionnaryTable and DataTable

This commit is contained in:
Gauthier Daniels 2025-04-22 18:32:57 +02:00
parent 874113db96
commit 702b9da341
7 changed files with 466 additions and 87 deletions

View File

@ -1,87 +1,337 @@
export default function DictionnaryTable() { import { For } from "solid-js";
type DataTable = {
tableName: string;
hiddenInBasic?: boolean;
documentName: string;
columns: DataColumn[];
primaryKeys?: string[];
foreignKeys?: string[];
};
type DataColumnType =
| "Alphanumérique"
| "Alphabétique"
| "Numérique"
| "Date"
| "Logique";
type DataColumn = {
name: string;
logical: string;
dbmsType: string;
dbmsLength?: number;
length?: string | number;
type: DataColumnType;
hiddenInTechnical?: boolean;
hiddenInBasic?: boolean;
dbmsConstraints: string[];
constraints?: string[];
};
const data: DataTable[] = [
{
tableName: "musician",
documentName: "Musicien",
primaryKeys: ["id_musician"],
columns: [
{
name: "code musicien",
logical: "id_musician",
dbmsType: "INTEGER",
type: "Numérique",
hiddenInBasic: true,
dbmsConstraints: ["AUTO_INCREMENT", "NOT_NULL", "UNIQUE"],
},
{
name: "Nom",
logical: "lastname",
dbmsType: "VARCHAR",
type: "Numérique",
length: 30,
dbmsLength: 30,
dbmsConstraints: ["NOT_NULL"],
constraints: ["Obligatoire"],
},
{
name: "Prénom",
logical: "firstname",
dbmsType: "VARCHAR",
type: "Numérique",
length: 30,
dbmsLength: 30,
dbmsConstraints: ["NOT_NULL"],
constraints: ["Obligatoire"],
},
{
name: "Instruments",
logical: "instruments",
dbmsType: "VARCHAR[]",
type: "Alphabétique",
length: 30,
dbmsLength: 30,
dbmsConstraints: ["NOT_NULL"],
constraints: ["Obligatoire"],
},
{
name: "Adresse e-mail",
logical: "email",
dbmsType: "VARCHAR[]",
type: "Alphanumérique",
length: 50,
dbmsLength: 50,
dbmsConstraints: ["NOT_NULL", "UNIQUE"],
constraints: ["Obligatoire", "Unique"],
},
{
name: "Mot de passe",
logical: "password",
dbmsType: "VARCHAR",
type: "Alphanumérique",
length: "> 12",
dbmsLength: 32,
dbmsConstraints: ["NOT_NULL"],
constraints: ["Obligatoire"],
},
],
},
{
tableName: "musician_participates_event",
documentName: "",
hiddenInBasic: true,
primaryKeys: ["id_musician", "event_id"],
foreignKeys: ["id_musician", "event_id"],
columns: [
{
name: "",
logical: "event_id",
dbmsType: "INTEGER",
type: "Numérique",
dbmsConstraints: ["NOT_NULL"],
},
{
name: "Lieu",
logical: "location",
dbmsType: "VARCHAR",
type: "Alphabétique",
dbmsLength: 30,
dbmsConstraints: ["NOT_NULL"],
},
{
name: "Date et heure",
logical: "datetime",
dbmsType: "TIMESTAMPZ",
type: "Alphabétique",
dbmsLength: 30,
dbmsConstraints: ["NOT_NULL"],
},
],
},
{
tableName: "event",
documentName: "",
hiddenInBasic: true,
primaryKeys: ["id_event"],
columns: [
{
name: "",
logical: "id_event",
dbmsType: "INTEGER",
type: "Numérique",
dbmsConstraints: ["AUTO_INCREMENT", "NOT_NULL", "UNIQUE"],
},
{
name: "Lieu",
logical: "location",
dbmsType: "VARCHAR",
type: "Alphabétique",
dbmsLength: 30,
dbmsConstraints: ["NOT_NULL"],
},
{
name: "Date et heure",
logical: "datetime",
dbmsType: "TIMESTAMPZ",
type: "Alphabétique",
dbmsLength: 30,
dbmsConstraints: ["NOT_NULL"],
},
],
},
{
tableName: "concert",
documentName: "Concert",
primaryKeys: ["id_concert"],
foreignKeys: ["event_id"],
columns: [
{
name: "code concert",
logical: "id_concert",
dbmsType: "INTEGER",
type: "Numérique",
hiddenInBasic: true,
dbmsConstraints: ["AUTO_INCREMENT", "NOT_NULL", "UNIQUE"],
},
{
name: "Date et heure",
logical: "datetime",
dbmsType: "TIMESTAMPZ",
type: "Date",
hiddenInTechnical: true,
dbmsConstraints: ["NOT_NULL"],
constraints: ["Obligatoire"],
},
{
name: "Lieu",
logical: "location",
dbmsType: "VARCHAR",
type: "Alphabétique",
hiddenInTechnical: true,
dbmsConstraints: ["NOT_NULL"],
constraints: ["Obligatoire"],
},
{
name: "Tarif",
logical: "price",
dbmsType: "CURRENCY",
type: "Numérique",
dbmsConstraints: ["NOT_NULL"],
constraints: ["Obligatoire"],
},
{
name: "",
logical: "event_id",
dbmsType: "INTEGER",
type: "Numérique",
hiddenInBasic: true,
dbmsConstraints: ["NOT_NULL"],
},
],
},
{
tableName: "rehearsal",
documentName: "Répétition",
primaryKeys: ["id_rehearsal"],
foreignKeys: ["event_id"],
columns: [
{
name: "code répétition",
logical: "id_rehearsal",
dbmsType: "INTEGER",
type: "Numérique",
hiddenInBasic: true,
dbmsConstraints: ["AUTO_INCREMENT", "NOT_NULL", "UNIQUE"],
},
{
name: "Date et heure",
logical: "datetime",
dbmsType: "TIMESTAMPZ",
type: "Date",
hiddenInTechnical: true,
dbmsConstraints: ["NOT_NULL"],
constraints: ["Obligatoire"],
},
{
name: "Lieu",
logical: "location",
dbmsType: "VARCHAR",
type: "Alphabétique",
hiddenInTechnical: true,
dbmsConstraints: ["NOT_NULL"],
constraints: ["Obligatoire"],
},
{
name: "",
logical: "event_id",
dbmsType: "INTEGER",
type: "Numérique",
hiddenInBasic: true,
dbmsConstraints: ["NOT_NULL"],
},
],
},
];
type DictionnaryTableProps = {
isTechnical?: boolean;
};
export default function DictionnaryTable(props: DictionnaryTableProps) {
return ( return (
<For
each={data.filter((table) => props.isTechnical || !table.hiddenInBasic)}
>
{(table) => (
<table class="block max-w-full overflow-x-auto border-collapse text-sm"> <table class="block max-w-full overflow-x-auto border-collapse text-sm">
<thead> <thead>
<tr> <tr>
<th scope="col">Nom de la donnée</th> <th scope="col">
<th scope="col">Format</th> {props.isTechnical ? "Colonne" : "Nom de la donnée"}
</th>
<th scope="col">{props.isTechnical ? "Type" : "Format"}</th>
<th scope="col">Longueur</th> <th scope="col">Longueur</th>
<th scope="col">Contraintes</th> <th scope="col">Contraintes</th>
<th scope="col">Document</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<For
each={table.columns.filter(
(column) => props.isTechnical || !column.hiddenInBasic,
)}
>
{(column) => (
<tr> <tr>
<td>Nom</td> <td>{props.isTechnical ? column.logical : column.name}</td>
<td>Alphabétique</td> <td>{props.isTechnical ? column.dbmsType : column.type}</td>
<td>30</td> <td>
<td>Obligatoire</td> {props.isTechnical
<td>Musicien</td> ? column.dbmsLength || "-"
</tr> : column.length || "-"}
<tr> </td>
<td>Prénom</td> <td>
<td>Alphabétique</td> {props.isTechnical
<td>30</td> ? column.dbmsConstraints?.join(", ") || "-"
<td>Obligatoire</td> : column.constraints?.join(", ") || "-"}
<td>Musicien</td> </td>
</tr>
<tr>
<td>Instruments</td>
<td>Alphabétique</td>
<td>30</td>
<td>Obligatoire</td>
<td>Musicien</td>
</tr>
<tr>
<td>Adresse e-mail</td>
<td>Alphanumérique</td>
<td>50</td>
<td>Obligatoire, unique</td>
<td>Musicien</td>
</tr>
<tr>
<td>Mot de passe</td>
<td>Alphanumérique</td>
<td>&gt; 12</td>
<td>Obligatoire</td>
<td>Musicien</td>
</tr>
<tr>
<td>Date et heure de concert</td>
<td>Date</td>
<td>-</td>
<td>Obligatoire</td>
<td>Concert</td>
</tr>
<tr>
<td>Lieu de concert</td>
<td>Alphabétique</td>
<td>50</td>
<td>Obligatoire</td>
<td>Concert</td>
</tr>
<tr>
<td>Tarif</td>
<td>Numérique</td>
<td>-</td>
<td>-</td>
<td>Concert</td>
</tr>
<tr>
<td>Date et heure de répétition</td>
<td>Date</td>
<td>-</td>
<td>Obligatoire</td>
<td>Répétition</td>
</tr>
<tr>
<td>Lieu de répétition</td>
<td>Alphabétique</td>
<td>50</td>
<td>Obligatoire</td>
<td>Répétition</td>
</tr> </tr>
)}
</For>
</tbody> </tbody>
<tfoot>
{props.isTechnical && (
<>
{table.primaryKeys && table.primaryKeys.length > 0 && (
<tr>
<td class="text-left">
<strong>Clé primaire</strong>
</td>
<td colSpan={3} class="text-left">
({table.primaryKeys.join(", ")})
</td>
</tr>
)}
{table.foreignKeys && table.foreignKeys.length > 0 && (
<tr>
<td class="text-left">
<strong>Clé(s) étrangère(s)</strong>
</td>
<td colSpan={3} class="text-left">
{table.foreignKeys.join(", ")}
</td>
</tr>
)}
</>
)}
<tr>
<td colSpan={4} class="text-left font-bold">
{props.isTechnical
? `Table : ${table.tableName}`
: `Document : ${table.documentName}`}
</td>
</tr>
</tfoot>
</table> </table>
)}
</For>
); );
} }

View File

@ -12,7 +12,7 @@ Le MLD est la suite normale et directe du MCD dans le processus Merise. Son but
On parlera plus tard du MRD, mais globalement : c'est la même chose que le MLD ! On parlera plus tard du MRD, mais globalement : c'est la même chose que le MLD !
## Qu'est-ce que le MLD ? ## 🤔 Qu'est-ce que le MLD ?
Le **MLD** est un schéma qui va nous permettre de représenter les données que l'on a récupérées dans le MCD, mais en ajoutant des détails techniques. Le **MLD** est un schéma qui va nous permettre de représenter les données que l'on a récupérées dans le MCD, mais en ajoutant des détails techniques.
@ -20,7 +20,7 @@ Il va nous permettre de représenter les différentes données que l'on a, regro
Contrairement au MCD, le MLD n'est pas destiné à être compris par le client. Contrairement au MCD, le MLD n'est pas destiné à être compris par le client.
## Exemple de MLD ## 🔍 Exemple de MLD
Reprenons le premier exemple de MCD, dans l'article précédent : Reprenons le premier exemple de MCD, dans l'article précédent :

View File

@ -4,4 +4,68 @@ description: Apprenez à créer le MPD dans Merise, la dernière étape pour con
tags: [Backend, Merise, BDD, MCD, MLD, MPD, SQL] tags: [Backend, Merise, BDD, MCD, MLD, MPD, SQL]
--- ---
En cours de rédaction... import DictionnaryTable from "../mcd/DictionnaryTable";
import tabs from "./tabs";
Prêt·e pour la dernière étape de la méthodologie Merise ? 🎉
Alors sans plus tarder, accueillons comme il se doit le **MPD** _(Modèle Physique de Données)_ !
## 🤔 Qu'est-ce que le MPD ?
Le **MPD** est la dernière étape de la méthodologie Merise.
Il reprend les éléments du MLD en ajoutant les derniers **détails techniques** nécessaires
pour implémenter le modèle dans un **SGBD** _(Système de Gestion de Base de Données)_.
**Ces détails techniques sont** :
- Le type de données de chaque colonne _(ex: `VARCHAR`, `INT`, `DATE`, etc.)_
- La taille de chaque colonne _(ex: `VARCHAR(255)`, `INT(11)`, etc.)_
- Les contraintes d'intégrité _(ex: `NOT NULL`, `UNIQUE`, etc.)_
- Les clés étrangères _(ex: `FOREIGN KEY`, `REFERENCES`, etc.)_
## 🔍 Exemple de MPD
À partir du **MLD** _(et **MRD** si existant)_ et du **dictionnaire de données**, on va pouvoir créer le MPD.
Faisons la transition de l'exemple de MLD basique que l'on a vu précédemment :
![Exemple de MLD](/images/merise/mld-basic.webp)
En prenant en considération que l'application va être développée avec **PostgreSQL**, on prendra soin d'utiliser
les **types de données** et les **contraintes d'intégrité** qui lui sont propres.
Voici à quoi il ressemble :
![Exemple de MPD](/images/merise/mpd-basic.webp)
Avec ce schéma, il est facile d'identifier les différentes tables, leurs colonnes, les types de données,
les contraintes d'intégrité, ainsi que les clés primaires et étrangères qui relient les tables entre elles.
Une fois le MPD créé, il est possible de le traduire en **SQL** pour créer les tables dans le SGBD.
Voici un exemple de code SQL pour créer les tables correspondantes au MPD ci-dessus :
<tabs.sqlExample />
## 📊 Dictionnaire de données
Tu te souviens du **dictionnaire de données** que l'on a créé lors du brief avec le client ? Juste avant de passer au MCD ?
Il est maintenant possible de le mettre jour avec les informations du SGBD utilisé !
Bien entendu ce n'est pas obligatoire, mais c'est une bonne source d'informations pour
les développeurs et les administrateurs de la base de données 😉
Pour rappel, voici le dictionnaire de données **non technique** que nous avions fait :
<DictionnaryTable />
En ajoutant les informations techniques, il devient :
<DictionnaryTable isTechnical />
## 📜 Préparation du LDD
Le **MPD** est la dernière étape avant de passer à la phase de **LDD** _(Langage de Définition de Données)_.
Il est donc important de bien le préparer pour éviter les erreurs lors de la création des tables.
Le **LDD** correspond tout simplement à la création des tables dans le SGBD, par l'utilisation des commandes SQL
comme `CREATE TABLE`, `ALTER TABLE`, `DROP TABLE`, etc.

View File

@ -0,0 +1,65 @@
import { Snippet } from "@/components/Snippet";
const sqlExampleSnippets = [
{
name: "Exemple de LDD",
codeLanguage: "sql",
withLineNumbers: true,
code: `CREATE TABLE "table_1" (
"id_table_1" INTEGER NOT NULL UNIQUE,
"column_2" VARCHAR(50) NOT NULL,
"column_3" VARCHAR(50) NOT NULL,
"table_3_id" INTEGER NOT NULL,
PRIMARY KEY("id_table_1")
);
CREATE INDEX "table_1_index_0"
ON "table_1" ("id_table_3");
CREATE TABLE "table_3" (
"id_table_3" INTEGER NOT NULL UNIQUE GENERATED BY DEFAULT AS IDENTITY,
"column_2" VARCHAR(50) NOT NULL,
"column_3" VARCHAR(50) NOT NULL,
"table_3_id" INTEGER,
PRIMARY KEY("id_table_3")
);
CREATE INDEX "table_3_index_0"
ON "table_3" ("id_table_3");
CREATE TABLE "table_2" (
"id_table_2" INTEGER NOT NULL UNIQUE GENERATED BY DEFAULT AS IDENTITY,
"column_2" VARCHAR(50) NOT NULL,
"column_3" VARCHAR(50) NOT NULL,
PRIMARY KEY("id_table_2")
);
CREATE INDEX "table_2_index_0"
ON "table_2" ("id_table_2");
CREATE TABLE "table_1_contains_table_2" (
"table_1_id" INTEGER NOT NULL,
"table_2_id" INTEGER NOT NULL,
PRIMARY KEY("table_1_id", "table_2_id")
);
CREATE UNIQUE INDEX "table_1_contains_table_2_index_0"
ON "table_1_contains_table_2" ("table_1_id", "table_2_id");
ALTER TABLE "table_1"
ADD FOREIGN KEY("table_3_id") REFERENCES "table_3"("id_table_3")
ON UPDATE NO ACTION ON DELETE NO ACTION;
ALTER TABLE "table_3"
ADD FOREIGN KEY("id_table_3") REFERENCES "table_3"("table_3_id")
ON UPDATE NO ACTION ON DELETE NO ACTION;
ALTER TABLE "table_1"
ADD FOREIGN KEY("id_table_1") REFERENCES "table_1_contains_table_2"("table_1_id")
ON UPDATE NO ACTION ON DELETE NO ACTION;
ALTER TABLE "table_1_contains_table_2"
ADD FOREIGN KEY("table_2_id") REFERENCES "table_2"("id_table_2")
ON UPDATE NO ACTION ON DELETE NO ACTION;`,
},
];
export default {
sqlExample: () => <Snippet snippets={sqlExampleSnippets} />,
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB