为博文点赞的功能支持两种信息的查询:某篇博文所得的赞的总数目,以及某个用户点过赞的所有博文的列表。为此,我们需要对用户和博文的模型做一些小改动。
修改模型
我们可以在用户模型里存储所有其点过赞的博文的ID,也可以反过来在博文模型里存储所有为其点过赞的用户的ID。由于我们只需为博文显示赞的个数,所以前一种方式显然更合适。
为用户模型添加其点过赞的博文的ID数组
打开models/User.js
,在UserSchema
的定义里加入一行代码:
hash: String,
// +++
favorites: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Article' }],
// +++
salt: String
按说用户点过赞的博文ID最好存储在一个集合里(比如ES6的Set
),然而Mongoose支持的模式类型里只有数组没有集合,所以在后面加入博文ID时,需要查重。
添加博文到点赞列表的方法
仍然在models/User.js
里,加入如下代码:
UserSchema.methods.favorite = function (id) {
if (this.favorites.indexOf(id) === -1)
this.favorites.push(id);
return this.save();
};
取消点赞的方法
继续加入下面的代码:
UserSchema.methods.unfavorite = function (id) {
this.favorites.remove( id );
return this.save();
};
检查是否为某篇博文点过赞的方法
UserSchema.methods.isFavorite = function (id) {
return this.favorites.some(fid => fid.toString() === id.toString());
};
前端为登录用户显示某篇博文时,后端会用到上面这个方法来检查该用户是否为该博文点过赞,并且得把这个信息同博文JSON对象一起返回给前端。
更新博文JSON对象
打开models/Article.js
,修改uArticleScheme.methods.toJSONFor
,加入一行代码:
ArticleSchema.methods.toJSONFor = function (user) {
return {
// ...
// +++
favorited: user ? user.isFavorite(this._id) : false,
// +++
favoritesCount: this.favoritesCount,
author: this.author.toProfileJSONFor(user)
};
};
统计点赞数的方法
接下来我们需要一个方法来统计博文获得的点赞数。
我们利用Mongoose的查询语句来做统计。
首先往models/Article.js
里导入User
模型:
const User = mongoose.model('User');
然后添加如下的代码:
ArticleSchema.methods.updateFavoritesCount = function () {
return User.count({favorites: {$in: [this._id]}})
.then(count => {
this.favoritesCount = count;
return this.save()
});
};
点赞功能所需的所有模型上的改动就全部完成了。
创建API端点
接下来我们用改动好的模型来所需的API端点。
为博文点赞的端点
POST /api/articles/:slug/favorite
,需要身份验证,不需要请求体,返回同一博文的JSON对象。
打开routes/api/articles.js
,加入如下代码:
// 点赞
router.post('/:slug/favorite', auth.required, function(req, res, next) {
res.locals.user.favorite(res.locals.article._id)
.then(() => res.locals.article.updateFavoritesCount())
.then(article => res.json({article: article.toJSONFor(res.locals.user)}))
.catch(next);
}, respondArticle);
取消点赞的端点
与点赞的端点十分相似,不过所用的HTTP方法为DELETE
。
完整的端点为DELETE /api/articles/:slug/favorite
,需要身份验证,不需要请求体,也是返回该博文的JSON对象。
// 取消点赞
router.delete('/:slug/favorite', auth.required, loadCurrentUser, (req, res, next) => {
res.locals.user.unfavorite(res.locals.article._id)
.then(() => res.locals.article.updateFavoritesCount())
.then(() => next())
.catch(next);
}, respondArticle);
以上就是为博文点赞功能的实现。
网友评论