Express、ORM & Sequelize 請賜給留言板力量


Posted by Christy on 2022-01-15

本文為 Lidemy [BE201] > [ORM 與 Sequelize] > [改造留言板系統] 的學習筆記,學習如何使用後端好朋友們 Express、ORM & Sequelize 打造留言板

零、內容摘要

  1. 前置作業 — 安裝環境

  2. 關聯 models

  3. 新增 controllers、views 資料夾,參考 後端好朋友 Express 基本介紹與操作

  4. 把留言板改成使用 Sequelize 的版本

一、前置作業設定

1. 前置作業

a. 初始化資料夾 $ npm init,接著全按 enter

b. 建 express 環境 + middlewares

$ npm install ejs body-parser express-session connect-flash bcrypt

c. 利用 Sequelize 建 ORM 環境

$ npm install --save sequelize mysql2

d. migrations

$ npm install --save-dev sequelize-cli

$ npx sequelize-cli init

e. 改 config 檔案 development

f.  建立 model

$ npx sequelize-cli model:generate --name User --attributes username:string,password:string,nickname:string

$ npx sequelize-cli model:generate --name Comment --attributes content:text

註:UserId 應該要在這裡也一起建,但不確定是要寫 UserId:integer 這樣嗎?

後來有查到這個參考資料:Data Types

g. Running Migrations

$ npx sequelize-cli db:migrate

2. models 加上關聯

static associate(models) {
  User.hasMany(models.Comment)
}

static associate(models) {
  Comment.belongsTo(models.User)
}

3. 把之前 controllers 的內容改一改

// comment.js

const db = require('../models')
const Comment = db.Comment

const commentController = {
  add: (req, res) => {
    const { username } = req.session
    const { content } = req.body
    if (!username || !content) {
      return res.redirect('/')
    }
  },

  index: (req, res) => {
    res.render('index', {
      comments: []
    })
  },

  delete: (req, res) => {
  },

  update: (req, res) => {
    res.render('update', {
      comment: {}
    })
  },

  handleUpdate: (req, res) => {
  }
}

module.exports = commentController
// user.js

const bcrypt = require('bcrypt')
const saltRounds = 10
const db = require('../models')
const User = db.User

const userController = {
  login: (req, res) => {
    res.render('user/login')
  },

  handleLogin: (req, res, next) => {
    const { username, password, nickname } = req.body
    if (!username || !password) {
      req.flash('errorMessage', 'Please fill in all the required fields.')
      return next()
    }

    User.findOne({
      where: {
        username
      }
    }).then(user => {
      if (!user) {
        req.flash('errorMessage', 'invalid user')
        return next()
      }

      bcrypt.compare(password, user.password, function(err, isSuccess) {
        if (err || !isSuccess) {
          req.flash('errorMessage', 'invalid password')
          return next()
        }
        req.session.username = user.username
        res.redirect('/')
      });

    }).catch(err => {
      req.flash('errorMessage', err.toString())
      return next()
    })
  },

  register: (req, res) => {
    res.render('user/register')
  },

  handleRegister: (req, res, next) => {
    const { username, password, nickname } = req.body
    if (!username || !password || !nickname) {
      req.flash('errorMessage', 'Please fill in all the required fields.')
      return next()
    }

    bcrypt.hash(password, saltRounds, function(err, hash) {
      if (err) {
        req.flash('errorMessage', 'user exists')
        return next()
      }

      User.create({
        username,
        password: hash,
        nickname
      }).then(() => {
        req.session.username = username
        res.redirect('/')
      }).catch((err) => {
        req.flash('errorMessage', 'user exists')
        return next()
      })
    });
  },

  logout: (req, res) => {
    req.session.username = null
    res.redirect('/')
  }
}

module.exports = userController

改到這裡可以先跑跑看,測試註冊跟登入功能都成功了。

4. 去 migrations 裡面的 comments 新增 UserId,重跑 migrations

Undoing Migrations

$ npx sequelize-cli db:migrate:undo

這樣 Comments 的表格會全部不見,然後再跑一次 migrations

$ npx sequelize-cli db:migrate

二、開始實作留言板 — 留言部份 12'50"

1. 新增留言

// controllers > comment.js

add: (req, res) => {
  const { userId } = req.session
  const { content } = req.body
  if (!userId || !content) {
    return res.redirect('/')
  }

  Comment.create({
    content,
    UserId: userId
  }).then(() => {
    res.redirect('/')
  })
},

除了修改新增留言功能以外,在 handleLogin & handleRegister 都要在抓到使用者時,一並拿到 req.session.userId = user.id

2. 抓取所有留言

// controllers > index.js

index: (req, res) => {
  Comment.findAll({
    include: User
  }).then((comments) => {
    res.render('index', {
      comments
    })
  })
},

除了 index 顯示留言以外,在 views > index.ejs 裡面跟使用者有關的,都要用到 comment.User.username or comment.User.nickname,因為 User 是從 model 裡面來的

3. 刪除留言

delete: (req, res) => {
  Comment.findOne({
    where: {
      id: req.params.id,
      UserId: req.session.userId
    }
  }).then((comment) => {
    return comment.destroy()
  }).then(() => {
    res.redirect('/')
  }).catch(() => {
    res.redirect('/')
  })
},

4. 編輯留言

update: (req, res) => {
  Comment.findOne({
    where: {
      id: req.params.id
    }
  }).then((comment) => {
    res.render('update', {
      comment
    })
  })
},

handleUpdate: (req, res) => {
  Comment.findOne({
    where: {
      id: req.params.id,
      UserId: req.session.userId
    }
  }).then((comment) => {
    return comment.update({
      content: req.body.content
    })
  }).then(() => {
    res.redirect('/')
  }).catch(() => {
    res.redirect('/')
  })
}

5. 留言由新到舊排序

在 findAll 裡面加上 order

index: (req, res) => {
  Comment.findAll({
    include: User,
    order: [
      ['updatedAt', 'DESC']
    ]
  }).then((comments) => {
    res.render('index', {
      comments
    })
  })
},

三、心得

使用這個方法好方便喔,跟之前第九週、第十一週比起來,簡直有天壤之別,就是一種工業革命大躍進的感覺,而且程式碼變得很好讀又簡潔❤️










Related Posts

Synthetic Control Unleashed: Crafting a Data Twin to Understand Interventions

Synthetic Control Unleashed: Crafting a Data Twin to Understand Interventions

系統架構 & 資料庫結構 筆記

系統架構 & 資料庫結構 筆記

CH8-1 for迴圈

CH8-1 for迴圈


Comments