获取所有博客
posts.ejs
修改views/posts.ejs:
<%- include('header') %>
<% posts.forEach(function (post) { %>
<%- include('components/post-content', { post: post }) %>
<% }) %>
<%- include('footer') %>
posts.service.ts
修改src/posts/posts.service.ts,增加一个findAll方法:
async findAll(options: PopulateOptions = {}) {
const posts = await this.model.findAll();
if (options.isWithUserInfo) {
await Promise.all(
posts.map(async (post) => {
const user = await this.userService.getUserById(post.userId);
post.author = user;
}),
);
}
// 增加浏览次数
if (options.isIncrementPv) {
posts.forEach((post) => {
this.model.findByIdAndUpdate(post.id, {
pv: post.pv + 1,
}).catch(this.logger.error);
});
}
posts.forEach((post) => {
this.format(post);
});
return posts;
}
注意,这里我在for循环中调用getUserById,这在真实开发中是不允许的。
因为这是一个调用数据库的操作,我们的程序与数据库的交互与I/O一样,是很昂贵的。我们应该尽可能减少交互次数。也就是说,如果是调用数据库,这里应该是先得到userId的数组,再调用userService的接口,一次性拿到所有数据,然后进行组装。
顺便也提一句,如果读取本地磁盘的文件,通常的做法是先探测下(stat)这个文件存不存在,但当这个文件存在时就浪费了一次I/O,所以更优的方法是直接调用read方法,再处理失败信息。
所以,代码可以这样优化,在src/mod.ts中增加一个findByIds方法:
async findByIds(ids: string[]): Promise<U[]> {
const arr = await Promise.all(ids.map((id) => this.findById(id)));
return arr.filter(Boolean) as U[];
}
在src/user/user.service.ts中使用:
async getUsersByIds(ids: string[]) {
return this.userModel.findByIds(ids);
}
这段代码修改为:
const users = await this.userService.getUsersByIds(
posts.map((post) => post.userId),
);
posts.forEach((post) => {
post.author = users.find((user) => user.id === post.userId);
});
整体修改为:
async findAll(options: PopulateOptions = {}) {
const posts = await this.model.findAll();
await this.formatPosts(posts, options);
return posts;
}
private async formatPosts(
posts: ModelWithId<Post>[],
options: PopulateOptions = {},
) {
if (options.isWithUserInfo) {
const users = await this.userService.getUsersByIds(
posts.map((post) => post.userId),
);
posts.forEach((post) => {
post.author = users.find((user) => user.id === post.userId);
});
}
// 增加浏览次数
if (options.isIncrementPv) {
posts.forEach((post) => {
this.model.findByIdAndUpdate(post.id, {
pv: post.pv + 1,
}).catch(this.logger.error);
});
}
posts.forEach((post) => {
this.format(post);
});
}
posts.controller.ts
修改src/posts/posts.controller.ts的getAll:
@Get("/")
async getAll(@Render() render: Render) {
const posts = await this.postsService.findAll({
isWithUserInfo: true,
});
return await render("posts", {
posts,
});
}
验证
注册几个用户,发表不同的文章。 访问http://localhost:8000/posts, 看到所有博客数据:
每一篇标题也能点击进去,看到博客详情。
点击用户头像,页面会进行跳转:http://localhost:8000/posts?userId=gxIKUVw8E84Bxtr1QYWxC。这时应该只有该作者的博客。所以我们下来应该做下处理。
获取作者的博客
posts.service.ts
修改posts.service.ts,新增一个findByUserId方法:
async findByUserId(userId: string, options: PopulateOptions = {}) {
const posts = await this.model.findMany({
userId,
});
await this.formatPosts(posts, options);
return posts;
}
修改posts.controller.ts,修改getAll方法:
async getAll(@Query("userId") userId: string, @Render() render: Render) {
if (userId) {
const posts = await this.postsService.findByUserId(userId, {
isWithUserInfo: true,
});
return render("posts", {
posts,
});
} else {
const posts = await this.postsService.findAll({
isWithUserInfo: true,
});
return render("posts", {
posts,
});
}
}
验证
点击不同用户的头像,可以看到不同的页面,比如冯巩的:
比如天仙的:
作业
你会发现,新创建的博客在下面,这个不符合阅读习惯。试着按创建时间倒序排列。