Introduction Sequelize Et ORM Dans Node - Js
Introduction Sequelize Et ORM Dans Node - Js
Introduction Sequelize Et ORM Dans Node - Js
dans Node.js
Qu'est-ce qu'un ORM ?
En termes simples, un système ORM (Object-relational Mapper) est une technique dans
laquelle vous utilisez un paradigme orienté objet pour créer un mappage entre l'application
et la base de données afin d'effectuer directement la manipulation des données et la
requête.
Lorsqu'il s'agit de récupérer et d'insérer des jointures et des relations, il suivra le même
paradigme pour manipuler ou interroger les données liées aux opérations.
Ce tutoriel vous présentera Sequelize ORM et vous aidera à créer votre première
application Sequelize NodeJS en utilisant l'approche code-first. Il y a 4
questions/exercices dans ce document, assurez-vous d'y répondre.
$ npm init
La première étape à faire est d'écrire du code pour connecter Sequelize à la base de
données. Nous allons d'abord créer un fichier index.js, le point d'entrée de notre
application. Créez ensuite un dossier "database" et un fichier db.js à l'intérieur de ce
dossier.
$ touch index.js
$ mkdir database
$ touch database/db.js
Dans le fichier db.js, importez "sequelize", puis créez une nouvelle instance de sequelize
en appelant new Sequelize. La fonction constructeur a besoin de certaines options telles
que votre base de données, votre nom d'utilisateur, votre mot de passe, votre port et votre
hôte. Assurez-vous de définir la configuration correcte.
Définir un modèle
Les modèles sont l'essence de Sequelize. Un modèle est une abstraction qui représente
une table dans votre base de données. Dans Sequelize, c'est une classe qui étend Model.
Le modèle indique à Sequelize plusieurs informations sur l'entité qu'il représente, telles
que le nom de la table dans la base de données et les colonnes qu'elle contient (et leurs
types de données).
Un modèle dans Sequelize a un nom. Ce nom ne doit pas nécessairement être le même
que celui de la table qu'il représente dans la base de données. Habituellement, les
modèles ont des noms au singulier (tels que User) tandis que les tables ont des noms au
pluriel (tels que Users), bien que cela soit entièrement configurable.
Nous allons maintenant définir un modèle Client qui représente une table "Client" dans
notre base de données. Créez un répertoire "models" et à l'intérieur, créez le fichier
javascript client.js.
$ mkdir models
$ touch models/client.js
Les modèles peuvent être définis de deux manières équivalentes dans Sequelize. Une
façon consiste à appeler sequelize.define(modelName, attributs, options).
Pour apprendre avec un exemple, nous considérerons que nous voulons créer un modèle
pour représenter les clients, qui ont un identifiant, un prénom et un nom. Nous voulons
que notre modèle s'appelle Client et que la table qu'il représente s'appelle "Client" dans la
base de données.
Cette façon de définir ce modèle est illustrée ci-dessous. Après avoir été défini, nous
pouvons accéder à notre modèle avec sequelize.models.Client.
● id
○ Clé primaire
○ Type entier
○ non nul
○ devrait être unique
● firstname
○ Type String
○ non nul
● lastname
○ Type String
○ non nul
Synchronisation des définitions JS avec la base de données
Sequelize peut créer des tables pour vous et il vous suffit de dire à sequelize de le faire.
Dans ce projet, nous le ferons dans le fichier index.js. Là-dedans, nous voulons nous
assurer que tous nos modèles sont essentiellement transférés dans des tables ou obtenir
une table qui leur appartient chaque fois que nous démarrons notre application.
Et si la table existe déjà, sequelize ne la remplacera bien sûr pas par défaut bien que nous
puissions lui dire de le faire.
Avant de synchroniser le modèle créé, nous allons créer un serveur express dans index.js
et essayer de nous authentifier auprès de la base de données.
Lorsque vous définissez un modèle, vous indiquez à Sequelize quelques informations sur
sa table dans la base de données. Cependant, que se passe-t-il si la table n'existe même
pas dans la base de données ? Que se passe-t-il si elle existe, mais qu'elle a des
colonnes différentes, moins de colonnes ou toute autre différence ?
● Client.sync() - Cela crée la table si elle n'existe pas (et ne fait rien si elle existe
déjà)
● Client.sync({ force: true }) - Cela crée la table, en la supprimant d'abord si elle
existait déjà
● Client.sync({ alter: true }) - Cela vérifie quel est l'état actuel de la table dans la
base de données (quelles colonnes elle a, quels sont leurs types de données, etc.),
puis effectue les modifications nécessaires dans la table pour faire il correspond au
modèle.
Exemple:
Lorsque vous exécutez index.js, vous devriez avoir une sortie dans votre terminal similaire
à ceci :
Que faire si vous avez plusieurs modèles et que vous souhaitez synchroniser tous les
modèles en même temps ? Vous pouvez utiliser sequelize.sync() pour synchroniser
automatiquement tous les modèles. Exemple:
Les associations
Sequelize prend en charge les associations standard : One-To-One, One-To-Many et
Many-To-Many.
Pour cela, Sequelize propose quatre types d'associations qu'il convient de combiner pour
les créer :
● L'association HasOne
● L'association BelongsTo
● L'association HasMany
● L'association BelongsToMany
Association One-to-One
Supposons que chaque client a un passeport. Dans ce cas, la table clients dans la base
de données serait la table parent et sa clé primaire apparaît comme clé étrangère dans la
table enfant.
Dans le répertoire "models", créez un fichier passeport.js pour ajouter un nouveau modèle:
$ touch models/passport.js
Créons un fichier index.js dans le dossier "models" pour importer toutes les entités de
modèles.
Nous pouvons maintenant ajouter les associations dans notre fichier index.js (le point
d'entrée de l'application) comme suit :
Lors de l'exécution de "node index.js", vous pouvez vérifier la base de données et voir la
table nouvellement créée "passports" qui contient la clé étrangère clientId.
Nous allons maintenant ajouter une fonction seed_data() qui va insérer deux clients et
deux passeports dans la base de données. Ensuite, cette fonction liera chaque client à
son passeport à l'aide d'une fonction setter. Lors de l'association d'un client à un
passeport à l'aide d'une association one-to-one, Sequelize crée automatiquement des
fonctions getters et setters telles que setPassport et getPassport. Notez que le nom de
ces fonctions dépend du nom des modèles (singulier ou pluriel par exemple).
Copiez et collez ce code :
Vous pouvez vérifier les deux tables après avoir exécuté index.js :
Pour rendre les choses plus excitantes, créons une route dans notre fichier index.js
permettant à l'utilisateur de publier des données relatives aux utilisateurs et aux
passeports (HTTP POST). Dans ce projet, par souci de démonstration, nous n'utiliserons
pas le modèle Routeurs-Contrôleurs-Modèles/Services. Nous mettrons toutes les
routes dans notre fichier index.js (ce qui n'est pas recommandé lorsque l'on travaille sur
une vraie application).
1- Ajoutez une route /clients qui accepte les messages HTTP POST. En utilisant la
fonction .create, nous allons créer un nouveau client. Sequelize a créé la fonction
.createPassport lorsque nous avons fait les associations afin que nous puissions l'utiliser
pour créer un passeport lié à un certain client.
Association One-to-Many
Prenons le cas où les articles sont vendus. Nous pouvons immédiatement identifier deux
entités : SALE et ITEM. Une vente (SALE) peut contenir de nombreux articles (ITEM) et
un article peut apparaître dans de nombreuses ventes. Chaque vente est réalisée par un
client, et un client peut réaliser plusieurs ventes. Intéressons-nous maintenant à la relation
entre un client et une vente.
Les associations One-To-Many connectent une source à plusieurs cibles, alors que toutes
ces cibles ne sont connectées qu'à cette seule source. Cela signifie que, contrairement à
l'association One-To-One, dans laquelle nous devions choisir où placer la clé étrangère, il
n'y a qu'une seule option dans les associations One-To-Many. Par exemple, si un client a
plusieurs ventes (et de cette façon chaque vente appartient à un client), alors la seule
implémentation sensée est d'avoir une colonne clientId dans la table SALES. L'inverse est
impossible, puisqu'un Client a plusieurs Ventes.
Dans cet exemple, nous avons les modèles Client et Sale. Nous voulons dire à Sequelize
qu'il existe une relation un à plusieurs entre eux, ce qui signifie qu'un Client a plusieurs
Sales, tandis que chaque Sale appartient à un seul Client.
$ touch models/sale.js
Dans index.js (point d'entrée de l'application), assurez-vous d'importer également "Sale"
en haut du fichier :
Exécutez votre fichier index.js et vérifiez la table dans votre base de données :
Notez que, dans les deux modèles ci-dessus (Client et Sale), le nom de la table
(clients/sales) n'a jamais été explicitement défini. Cependant, le nom du modèle a été
donné (client/sale). Par défaut, lorsque le nom de la table n'est pas donné, Sequelize
met automatiquement au pluriel le nom du modèle et l'utilise comme nom de table.
Exercice 1 :
Créez une route POST /sales dans laquelle vous pouvez insérer une nouvelle vente dans
la base de données avec une requête HTTP Post. Remplissez le tableau des ventes
(sales) avec des données factices (au moins 2 ventes pour chaque utilisateur).
Testons d'abord le Lazy Loading. Créez une route GET /clients/lazy/:id pour obtenir
toutes les informations relatives à un client avec un identifiant donné. Notez que dans
l'exemple ci-dessous, nous avons fait deux requêtes SQL, ne récupérant les ventes
associées que lorsque nous voulions les utiliser. Cela peut être particulièrement utile si
nous pouvons ou non avoir besoin des ventes, peut-être voulons-nous les récupérer sous
condition, seulement dans quelques cas ; de cette façon, nous pouvons gagner du temps
et de la mémoire en ne le récupérant que lorsque cela est nécessaire.
Testons maintenant le Eager Loading. Créez une route GET /clients/eager/:id pour
obtenir toutes les informations relatives à un client avec un identifiant donné. Eager
Loading est effectué dans Sequelize à l'aide de l'option include. Observez dans
l'exemple ci-dessous qu'une seule requête a été effectuée sur la base de données (qui
apporte les données associées avec l'instance).
Exercice 2 :
Créez une route PUT /clients/:id qui prend l'id du client dans les paramètres et ses
prénom et nom dans le corps (body) et mettez à jour la table clients. Pour cette requête,
vous utiliserez la méthode update et la clause where avec sequelize.
Voici la syntaxe de mise à jour d'une requête extraite de la documentation de sequelize :
Vous devriez avoir des résultats similaires :
Exercice 3 :
créer une route DELETE /sales/:id qui prend l'id d'une vente dans les paramètres et
supprimer la vente de la table "sales". Pour cette requête, vous utilisez la méthode
"destroy" et la clause where avec Sequelize.
Voici la syntaxe d'une opération de suppression tirée de la documentation sequelize:
Vous devriez avoir des résultats similaires :
Faisons une requête intéressante avec Sequelize. Créez une route GET /sales/topclient.
Nous voulons renvoyer le client qui a les ventes les plus élevées et dont les ventes sont
supérieures à 2.
Supposons que dans votre table des ventes (sales), vous ayez ces enregistrements ou
quelque chose de similaire (un client qui a plus de 2 ventes):
Dans votre répertoire "models", créez un nouveau modèle javascript "item.js". Chaque
article aura un numéro d'article, un nom d'article, un type et une couleur.
$ touch models/item.js
$ touch models/saleitem.js
Mettez à jour le fichier models/index.js comme suit :
Dans index.js (point d'entrée de l'application), assurez-vous d'importer également "Item"
et “SaleItem” en haut du fichier :
Pour cet exemple, nous allons considérer les modèles Sale et Item. Une vente peut
contenir de nombreux articles et un article peut être impliqué dans de nombreuses ventes.
La table de jonction qui gardera une trace des associations s'appellera SaleItem, qui
contiendra les clés étrangères saleno et itemno.
let dummy_sale_items = [
{quantity: 2, price: 10, saleno: 1, itemno: 3},
{quantity: 3, price: 24, saleno: 1, itemno: 1},
{quantity: 1, price: 8, saleno: 2, itemno: 1},
{quantity: 2, price: 10, saleno: 3, itemno: 3},
{quantity: 3, price: 60, saleno: 3, itemno: 5},
{quantity: 1, price: 20, saleno: 4, itemno: 4},
{quantity: 1, price: 10, saleno: 4, itemno: 2},
{quantity: 2, price: 10, saleno: 5, itemno: 3}
]
await SaleItem.bulkCreate(dummy_sale_items);
}
Défi : créez une route HTTP GET /items/lowestup qui envoie l'article au prix le plus bas.
Il existe une fonctionnalité intéressante dans Sequelize appelée Virtual fields. Les
champs virtuels sont des champs que Sequelize remplit, mais en réalité ils n'existent
même pas dans la base de données. Par exemple, disons que nous avons les attributs de
prix et de quantité pour un SaleItem. Ce serait bien d'avoir un moyen simple d'obtenir
directement le prix unitaire (unitprice) ! Nous pouvons combiner l'idée des getters avec le
type de données spécial que Sequelize fournit pour ce genre de situation :
DataTypes.VIRTUAL.
Le prix unitaire d'un article peut être calculé en divisant le prix par la quantité dans la table
“saleItem”. En SQL, nous pouvons simplement le faire en ajoutant une colonne calculée
et en lui donnant un alias. Dans Sequelize, pour ce faire, ouvrez votre modèle SaleItem
(models/saleitem.js) et ajoutez un attribut "unitprice" au modèle et dans la fonction
getter, vous pouvez renvoyer le résultat du prix/quantité comme suit. Le champ
VIRTUAL n'entraîne pas l'existence d'une colonne dans la table. En d'autres termes, le
modèle ci-dessous n'aura pas de colonne de prix unitaire. Cependant, il semblera l'avoir!
Notez que pour pouvoir obtenir le prix unitaire à partir de la fonction findAll(), vous devez
vous assurer que les colonnes utilisées pour obtenir ce champ (dans ce cas la quantité et
le prix) sont incluses dans les attributs renvoyés.
Après avoir mis à jour le modèle SaleItem, accédez à votre fichier index.js (où se trouve
le serveur Express), et ajoutez la route suivante pour obtenir l'article au prix le plus bas.
Testez la route et assurez-vous qu'elle fonctionne.
Exercice 4 : Créez une route qui retourne le client qui a dépensé le plus d'argent. La sortie
pourrait ressembler à ceci :
Exercice 5 : Chaque article (item) a un type d'article. Lorsque nous avons créé le modèle
"Item", nous avons défini le type de chaque article (itemtype) sous forme de texte codé
en dur.
1. Mettez à jour le modèle "item" actuel et créez le modèle "itemType", puis associez
les deux modèles l'un à l'autre.
2. Mettez à jour la fonction seed_data(), ajoutez des types d'articles factices et liez-les
aux articles.
a. Lier un article (item) à un type d'article (itemType) doit être aléatoire. Vous
parcourez les articles et, pour chaque item, vous sélectionnez un itemType
aléatoire dans la base de données (recherchez comment utiliser une fonction
random avec Sequelize) et liez le type d'article renvoyé à l'article.
3. Créez la route GET /itemtypes/:id/items qui renvoie tous les articles appartenant à
un type d’article donné.
4. Créez la route GET /itemtypes/bestsale qui renvoie le type d'articles qui a le plus
grand nombre d'articles vendus (utilisez le champ de quantité).
Un exemple de sortie de l'exercice 5.3 :