建立使用者功能與資料關聯

AC教材 todo list 練習。

當完成專案與認證功能後,會發現登入後大家看到的都是一樣的資訊,而沒有專屬自己的頁面。

  • 這時候就必須要將 model 中的 user 與資料建立起關聯。
  • 修改路由與 controller,讓 model 在查詢更新資料庫,只會取得/修改已經登入者的資料。

database 中的 user collection(有 User document) 和 data collention(有 data document)都是分開的我們要將它建立起關聯。

使用 User 的 _id 在 data 資料上做上記號,

關聯實作步驟

  1. 修改 data model 的 Schema
  2. 修改與 datas 有關的路由

將在 data model 中加入一個 userId的 key 用來儲存 user _id 以建立關聯。使用到 Mongoose 的 Populate 功能。

修改 model

data model

const mongoose = require('mongoose')
const Schema = mongoose.Schema

const todoSchema = new Schema({
name: {
type: String,
required: true
},
done: {
type: Boolean,
default: false
},
userId: { // 加入 userId,建立跟 User 的關聯
type: Schema.Types.ObjectId, // 定義為 Moongoose 的 ObjectId
ref: 'User', // 定義屬性從 user model 來
index: true, // 把 userId 設定成索引,使用 mongoDB支援 index 功能,增加讀取效能
required: true
}
})

修改路由

建立起關聯後,將路由在 Todo.find({}) 中,加入條件 Todo.find({ userId: req.user._id }),去讀取關聯資料。

  1. 修改 home.js 路由
  2. 修改 detail, edit, delete 路由

routers/home.js

router.get('/', authenticated, (req, res) => {
Todo.find({ userId: req.user._id }) // Todo.find({ 只找 userId 等於 req.user._id的文件 })
.sort({ name: 'asc' })
.exec((err, todos) => {
if (err) return console.error(err)
return res.render('index', { todos: todos })
})
})
module.exports = router

將 todo.js 路由的 action 從 Todo.findById 修改為 Todo.findOne({ _id: req.params.id, userId: req.user._id }),尋找在 data collection 中同時符合 data ID 以及 user ID 的資料。

routes/todo.js

const express = require('express')
const router = express.Router()
const Todo = require('../models/todo')

const { authenticated } = require('../config/auth')

// 總頁面
// 新增一筆頁面
// 詳細資料頁面 加入解構式
router.get('/:id', authenticated, (req, res) => {
Todo.findOne({ _id: req.params.id, userId: req.user._id}, (err, todo) =>{
if (err) return console.error(err)
return res.render('detail', { todo: todo })
}
})
// 新增一筆(行為)
router.post('/', authenticated, (req, res) => {
const todo = Todo({
name: req.body.name,
userId: req.user._id // 儲存userId
})
todo.save(err => {
if (err) return console.error(err)
return res.redirect('/') //正確存到資料庫並回到'/'
})
})
// 修改的資料頁面與 詳細資料頁面 相似差在 路徑
router.get('/:id/edit', authenticated, (req, res) => {
Todo.findOne({ _id: req.params.id, userId: req.user._id}, (err, todo) =>{
if (err) return console.error(err)
return res.render('edit', { todo: todo })
}
})
// 修改資料
router.post('/:id', authenticated, (req, res) => { //路徑用到/:id 主要是聯繫到資料庫這樣寫比較好
Todo.findOne({ _id: req.params.id, userId: req.user._id}, (err, todo) =>{ //先找出符合登入的使用者
if (err) return console.error(err)
todo.name == req.body.name //因為資料已經存在所以直接取出放入變數
if (req.body.done === 'on') {
todo.done = true
} else {
todo.done = false
}
todo.save(err => {
if (err) return console.error(err)
return res.redirect('/') //正確存到資料庫並回到'/'
})
})
})
// 刪除資料
router.delete('/:id/delete', authenticated, (req, res) => {
Todo.findOne({ _id: req.params.id, userId: req.user._id}, (err, todo) => {
if (err) return console.error(err)
todo.remove(err => {
if (err) return console.error(err)
return res.redirect('/')
})
})
})