# 🏅 Day 39 - 實作新增貼文留言功能 練習整合運用先前提到的技巧: - router - Moogoose create() - req.params、req.body - JWT middleware 實作出設計稿 `3-2.全體動態牆-有留言` 頁面的「新增貼文留言功能」 ### 流程 當使用者點選「留言」按鈕時,可以將使用者輸入的留言新增進留言資料中 **實作前準備** 由於無法預期單則貼文的留言數量,因此留言的部分會另外使用 collection 存放 `models/commentsModel.js` ```javascript const commentSchema = new mongoose.Schema( { comment: { type: String, required: [true, 'comment can not be empty!'] }, createdAt: { type: Date, default: Date.now }, user: { type: mongoose.Schema.ObjectId, ref: 'User', require: ['true', 'user must belong to a post.'] }, post: { type: mongoose.Schema.ObjectId, ref: 'Post', require: ['true', 'comment must belong to a post.'], } }, { versionKey: false } ); ``` 由於 Comments 存放在另一個 collection,因此若取得貼文時需要顯示出相關留言串,需另外在 Post Schema 中加入**虛擬欄位** 而此虛擬欄位 comments 會關聯 Comment Model 的 post 欄位及 Post Model 的 _id 欄位,找出所有該貼文 ID 的留言 ```javascript= postSchema.virtual('comments', { ref: 'Comment', foreignField: 'post', localField: '_id' }); ``` 需注意當 document 被轉為 JSON 或 Object 資料時,是不包含虛擬欄位的,因此需記得在貼文 Schema 加入 ```javascript= toJSON: { virtuals: true }, toObject: { virtuals: true }, ``` 整體 Schema 設定可參考範例程式碼 [commentsModel.js](https://github.com/gonsakon/express-week4-sample/blob/week8/models/commentsModel.js)、[postsModel.js](https://github.com/gonsakon/express-week4-sample/blob/week8/models/postsModel.js) #### 參考資源 - [Mongoose v6.3.4: Mongoose Tutorials: Mongoose Virtuals](https://mongoosejs.com/docs/tutorials/virtuals.html) - [Mongoose Middleware - Pre](https://mongoosejs.com/docs/middleware.html#pre) ### 開始實作 **設計 POST `/posts/:id/comment` 路由** - 需登入通過 JWT 驗證才能請求 - 新增傳入的留言,以及留言者 user ID、貼文 ID 新增至 comments collection 中 - 需回傳使用者新增的留言 依照上方需求完整以下程式碼,補上 `...` 的部分 `app.js` ```javascript= const postsRouter = require('./routes/posts'); app.use('/posts', postsRouter); ``` `routes/posts.js` ```javascript= router....('...', ..., handleErrorAsync(async (req, res, next) => { const user = req.user.id; const post = req....; const { comment } = req....; const newComment = await Comment....({ user, post, comment }); res.status(201).json({ status: 'success', data: { comments: ... } }); })) ``` ## 回報流程 將答案寫在 CodePen 並複製 CodePen 連結貼至底下回報就算完成了喔! 解答位置請參考下圖(需打開程式碼的部分觀看) ![](https://i.imgur.com/vftL5i0.png) <!-- 解答: `app.js` ```javascript= const postsRouter = require('./routes/posts'); app.use('/posts', postsRouter); ``` `routes/posts.js` ```javascript= router.post('/:id/comment', isAuth, handleErrorAsync(async (req, res, next) => { const user = req.user.id; const post = req.params.id; const { comment } = req.body; const newComment = await Comment.create({ post, user, comment }); res.status(201).json({ status: 'success', data: { comments: newComment } }); })) ``` --> 回報區 --- <!-- 將答案貼至下方表格內,格式: | Discord 暱稱 | [CodePen](連結) | --> | Discord | CodePen / 答案 | |:-------------:|:-----------------:| | xxx | [CodePen]() | | william威良 | [CodePen](https://codepen.io/snowman12320/pen/WNBoExP) | | Chia Pin | [CodePen](https://codepen.io/joker-cat/pen/mdYOMaz) | | wei | [CodePen](https://hackmd.io/@xu7yoa5cSsqaron7h9XhUw/r1pqeZXER)| | 苡安 | [hackmd](https://hackmd.io/@L7K9-66lSeagS28AP0_GjQ/Byzs6eV40)| | runweiting | [CodePen](https://codepen.io/weiting14/pen/OJYWVVG)| | jenny7532 | [CodePen](https://codepen.io/wei-chen-wu/pen/ExzWVwO)| | mei | [CodePen](https://codepen.io/l_umei/pen/WNBpXvx)| | Hank | [CodePen](https://codepen.io/tw1720/pen/mdYdwNW)|