# 🏅 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 連結貼至底下回報就算完成了喔!
解答位置請參考下圖(需打開程式碼的部分觀看)

<!-- 解答:
`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)|