本文為 Lidemy [BE201] > [ORM 與 Sequelize] > [改造留言板系統] 的學習筆記,學習如何使用後端好朋友們 Express、ORM & Sequelize 打造留言板
零、內容摘要
前置作業 — 安裝環境
關聯 models
新增 controllers、views 資料夾,參考 後端好朋友 Express 基本介紹與操作
把留言板改成使用 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
$ 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
    })
  })
},
三、心得
使用這個方法好方便喔,跟之前第九週、第十一週比起來,簡直有天壤之別,就是一種工業革命大躍進的感覺,而且程式碼變得很好讀又簡潔❤️



