LILINE’S LAB

Projet : Simple Blog From Scratch Part2

par | Fév 20, 2024 | Développement, Projets, Sequelize, Simple Blog From Scratch

Dans cet article nous allons voir comment créer les tables nécessaires à notre blog en utilisant l’ORM Sequelize.

Mais qu’est-ce que Sequelize ?
C’est un ORM (Object-Relational Mapping) c’est à-dire un outil de programmation qui va faciliter la conversion entre les données stockées dans une base de données relationnelle et les objets utilisés dans le code de l’application.

Les modèles

Dans le dossier models de l’application nous allons créer un fichier nommé index.js dans lequel nous allons écrire le code qui va permettre de configurer la connexion à la base de données, importer dynamiquement les modèles se trouvant dans ce même dossier, associer ces modèles puis exporter des objets qui seront utilisés dans l’application.

JavaScript
'use strict';

const fs = require('fs');
const path = require('path');
const Sequelize = require('sequelize');
const basename = path.basename(__filename);
const db = {};

require('dotenv').config();

let sequelize = new Sequelize(process.env.DB_NAME, process.env.DB_USERNAME, process.env.DB_PASSWORD, {
  host: process.env.DB_HOST,
  port: process.env.DB_PORT,
  dialect: process.env.DB_DIALECT,
  define: {
    underscored: true,
    freezeTableName: true, 
    timestamps: true,  
  },
  dialectOptions: {
    useUTC: false, 
    dateStrings: true,
    typeCast: function (field, next) {
      if (field.type === 'DATETIME') {
        return field.string()
      }
        return next()
    },
  },
  timezone: process.env.TIMEZONE
});

fs
  .readdirSync(__dirname)
  .filter(file => {
    return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js');
  })
  .forEach(file => {
    const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes);
    db[model.name] = model;
  });

Object.keys(db).forEach(modelName => {
  if (db[modelName].associate) {
    db[modelName].associate(db);
  }
});

db.sequelize = sequelize;
db.Sequelize = Sequelize;

module.exports = db;

Les variables d’environnements sont récupérées via le module dotenv installé dans la première partie et se trouvent dans le fichier .env présent à la racine du projet.
Voici pour les variables utilisées dans le code ci-dessus :

JavaScript
DB_NAME="<yourvalue>"
DB_USERNAME="<yourvalue>"
DB_PASSWORD="<yourvalue>"
DB_HOST="<yourvalue>"
DB_PORT="<yourvalue>"
DB_DIALECT="mysql"
TIMEZONE="<your timezone>"

Les modèles utilisés dans l’application sont les suivants
user.js
image.js
post.js

JavaScript
// user.js 
'use strict';
const {
  Model, Sequelize
} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
  // Assiciation du modèle User aux modèles Post et Image 
  class User extends Model {
    static associate(models) {
        models.User.hasMany(models.Post);
        models.User.hasMany(models.Image);
    }
  };
  // Définition des propriétés du modèle User
  User.init({
    id: {type: DataTypes.UUID, allowNull: false, defaultValue: Sequelize.UUIDV4, primaryKey: true },
    username: { type: DataTypes.STRING, allowNull: false, unique: true },
    email: { type: DataTypes.STRING, allowNull: false, unique: true },
    password: { type: DataTypes.STRING, allowNull: false },
    otp: { type: DataTypes.STRING },
    otpcreated : { type: DataTypes.DATE, defaultValue: DataTypes.NOW }, 
    otpexpires : { type: DataTypes.DATE, defaultValue: DataTypes.NOW }, 
  }, {
    sequelize,
    modelName: 'User',
  });
  return User;
};

JavaScript
// post.js

'use strict';
const {
  Model
} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
  class Post extends Model {
    // Association du modèle Post au modèle User 
    static associate(models) {
      models.Post.belongsTo(models.User, {
        foreignKey: {
          allowNull: false
        }, onDelete:'CASCADE',
      })
    }
  };
  // Définition des propriétés du modèle User
  Post.init({
    title: { type: DataTypes.STRING, allowNull: false },
    content: { type: DataTypes.TEXT('long'), allowNull: false },
    createdBy: { type: DataTypes.STRING, allowNull: true },
    isArchived: { type: DataTypes.BOOLEAN, allowNull: false, defaultValue: false }
  }, {
    sequelize,
    modelName: 'Post',
  });
  return Post;
};

JavaScript
// image.js 

'use strict';
const {
  Model
} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
  // Associaition du modèle Image au modèle User
  class Image extends Model {
    static associate(models) {
      models.Image.belongsTo(models.User, {
        foreignKey: {
          allowNull: false
        }, onDelete:'CASCADE',
      })
    }
  };
  // Définition des propriétés du modèle Image
  Image.init({
    imageUrl: { type: DataTypes.STRING, allowNull: true },
    imageTitle: { type:DataTypes.STRING, allowNull: true}
  }, {
    sequelize,
    modelName: 'Image',
  });
  return Image;
};

Les seeders

Un seeder permet de peupler les données d’une tables immédiatement après la création de cette table.
Par exemple pour le seeder firstuser.js.

JavaScript
const db = require("../models");
const bcrypt = require("bcrypt");
require('dotenv').config();

function firstUser(req, res) {
  // On vérifie si l'utilisateur existe déjà a
  db.User.findOne({ where: { username: process.env.FIRSTUSERUSERNAME } })
    .then((user) => {
      // S'il n'existe pas l'utilisateur sera créé
      if (!user) {
          let pswdFormat = /^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$ %^&*-]).{12,}$/
          let pswd = process.env.FIRSTUSERPASSWORD
          if(pswd !== '' && pswd.match(pswdFormat)) {
            bcrypt.hash(pswd, 10)
            .then((hash) => {
              db.User.create({
                username: process.env.FIRSTUSERUSERNAME,
                email: process.env.FIRSTUSEREMAIL,
                password: hash,
              })
                // Puis l'on créé un premier article pour le blog
                .then((account) => {
                  console.log(`Le compte ${account.username} a été créé!`)
                  db.Post.create({
                    title: "First Post",
                    content: "First Post Content",
                    createdBy: account.username,
                    UserId: account.id
                  })
                })
                .catch((error) => { 
                  console.log(error);
                  res.status(400).json({ error });
                });
            })
            .catch((error) => {
              console.log(error);
              res.status(500).send({ error });
            });
          }
          // Si le mot de passe indiqué dans .env n'est pas conforme à ce qu'attend le code on renvoi une erreur
          else{
              console.log('Le mot de passe doit contenir au moins 12 caractères avec une majuscule, une minuscule, un chiffre et un caractère spécial');
          }
        // Si le compte existe déjà on l'indique   
      } else {
        console.log("le compte existe déjà");
      }
    })
    .catch((error) => {
      console.log(error);
    });
}
module.exports = firstUser();

Modification app.js

Les tables pourront être créées et peuplées lors du lancement de l’application via le ligne ci-dessous rajoutée au fichier app.js.

JavaScript
const db = require("./models/index")
db.sequelize.sync().then(function () {
  require("./seeders/firstuser");
})

Retrouvez le premier article du projet ici ⬇️

Simple Blog From Scratch Part1


logo du site

   


Consultante spécialisée dans les technologies Microsoft, je justifie de plus de 14 ans d’expérience dans le secteur IT.
Mon parcours professionnel, riche et varié, m’a permis de développer de solides compétences, tant techniques qu’humaines. Je suis reconnue pour ma capacité à analyser rapidement les enjeux techniques et à concevoir des solutions pertinentes, y compris sur des problématiques complexes.
Mon approche proactive, alliée à mes compétences en développement, me permet de proposer des évolutions efficaces, toujours en cohérence avec les objectifs métiers.
En parallèle de mon activité professionnelle, je prends plaisir à approfondir mes compétences techniques en réalisant des projets personnels. Je développe notamment des jeux web basés sur React et PHP, ainsi que des applications orientées métier avec SPFx.