# 🏅 Day 38 - 實作追蹤/取消追蹤使用者功能 練習整合運用先前提到的技巧: - router - Moogoose updateOne() - req.params - JWT middleware 實作出設計稿 `9.個人牆 / 9-2.個人牆-取消追蹤` 頁面的「追蹤/取消追蹤使用者功能」 ### 流程 A 使用者對其他使用者(如 B) 點擊追蹤時,會分別在 A 使用者的 user 資料 followings 欄位加入 B 的 ID,B 使用者的 user 資料 followers 欄位也會加入 A 的 ID 若執行取消追蹤則從 A 使用者的 user 資料 followings 欄位、B 使用者的 user 資料 followers 欄位移除 ID > **實作前準備** > 需先於 user Model 中加入 followers 與 follwing 欄位,預計會加入使用者 ID 及加入時間 > ```javascript > followers: [ > { > user: { type: mongoose.Schema.ObjectId, ref: 'User' }, > createdAt: { > type: Date, > default: Date.now > } > } > ], > following: [ > { > user: { type: mongoose.Schema.ObjectId, ref: 'User' }, > createdAt: { > type: Date, > default: Date.now > } > } > ] > ``` ### 開始實作 - 設計追蹤路由 POST`users/:id/follow`、取消追蹤路由 DELETE `users/:id/unfollow` - 需登入通過 JWT 驗證才能請求 - 不能追蹤或取消追蹤帳號本人 - 追蹤與取消追蹤都需調整追蹤者與被追蹤者雙方的 user 資料 使用 updateOne() 搭配 `$addToSet` `$pull` 將使用者 ID 加入或移除 依照上方需求完整以下程式碼,補上 `...` 的部分 `app.js` ```javascript= const usersRouter = require('./routes/users'); app.use('/users', usersRouter); ``` `routes/users.js` ```javascript= // 追蹤 router....('...', ..., handleErrorAsync(async (req, res, next) => { if (....id === req.user....) { return next(appError(401, '您無法追蹤自己', next)); } await User....( { _id: req.user.id, 'following.user': { $ne: req.params.id } }, { $addToSet: { following: { user: ... } } } ); await User....( { _id: req.params.id, 'followers.user': { $ne: req.user.id } }, { $addToSet: { followers: { user: ... } } } ); res.status(200).json({ status: 'success', message: '您已成功追蹤!' }); })) // 取消追蹤 router....('...', ..., handleErrorAsync(async (req, res, next) => { if (....id === req.user....) { return next(appError(401, '您無法取消追蹤自己', next)); } await User....( { _id: req.user.id }, { $pull: { following: { user: ... } } } ); await User....( { _id: req.params.id }, { $pull: { followers: { user: ... } } } ); res.status(200).json({ status: 'success', message: '您已成功取消追蹤!' }); })) ``` ## 回報流程 將答案寫在 CodePen 並複製 CodePen 連結貼至底下回報就算完成了喔! 解答位置請參考下圖(需打開程式碼的部分觀看) ![](https://i.imgur.com/vftL5i0.png) <!-- 解答: `app.js` ```javascript= const usersRouter = require('./routes/users'); app.use('/users', usersRouter); ``` `routes/users.js` ```javascript= // 追蹤 router.post('/:id/follow', isAuth, handleErrorAsync(async (req, res, next) => { if (req.params.id === req.user.id) { return next(appError(401, '您無法追蹤自己', next)); } await User.updateOne( { _id: req.user.id, 'following.user': { $ne: req.params.id } }, { $addToSet: { following: { user: req.params.id } } } ); await User.updateOne( { _id: req.params.id, 'followers.user': { $ne: req.user.id } }, { $addToSet: { followers: { user: req.user.id } } } ); res.status(200).json({ status: 'success', message: '您已成功追蹤!' }); })) // 取消追蹤 router.delete('/:id/unfollow', isAuth, handleErrorAsync(async (req, res, next) => { if (req.params.id === req.user.id) { return next(appError(401, '您無法取消追蹤自己', next)); } await User.updateOne( { _id: req.user.id }, { $pull: { following: { user: req.params.id } } } ); await User.updateOne( { _id: req.params.id }, { $pull: { followers: { user: req.user.id } } } ); res.status(200).json({ status: 'success', message: '您已成功取消追蹤!' }); })) ``` 可參考完整範例程式碼:https://github.com/gonsakon/express-week4-sample/blob/48dfb5706546f6b70fd01570313b333e528cd247/routes/users.js#L90-L144 --> 回報區 --- <!-- 將答案貼至下方表格內,格式: | Discord 暱稱 | [CodePen](連結) | --> | Discord | CodePen / 答案 | |:-------------:|:-----------------:| | xxx | [CodePen]() | | 苡安 | [hackmd](https://hackmd.io/@L7K9-66lSeagS28AP0_GjQ/BJkDi8ZNA) | | william威良 | [CodePen](https://codepen.io/snowman12320/full/ZENBEQq) | | wei | [CodePen](https://hackmd.io/@xu7yoa5cSsqaron7h9XhUw/ryewzJG4A)| | runweiting | [CodePen](https://codepen.io/weiting14/pen/MWdJYdK)| | jenny7532 | [CodePen](https://codepen.io/wei-chen-wu/pen/LYoWYXP)| | mei | [CodePen](https://codepen.io/l_umei/pen/vYwxevv)| | Hank | [CodePen](https://codepen.io/tw1720/pen/oNRNwKb)|