Nestjs 最佳实践教程:4 排序,分页与过滤

2022-07-10 09:45:39 +08:00
 lichnow

另,本人在找工作中,希望能有远程工作匹配(无法去外地),有需要的老板可以看一下我的个人介绍: https://pincman.com/about

学习目标

预装依赖

~ pnpm add nestjs-typeorm-paginate sanitize-html deepmerge && pnpm add @types/sanitize-html -D

文件结构

创建文件

cd src/modules/content && \
mkdir subscribers && \
touch dtos/query-category.dto.ts \
dtos/query-post.dto.ts \
subscribers/post.subscriber.ts \
subscribers/index.ts \
services/sanitize.service.ts \
&& cd ../../../

与上一节一样,这一节的新增和修改集中于ContentModule

src/modules/content
├── constants.ts
├── content.module.ts
├── controllers
│   ├── category.controller.ts
│   ├── comment.controller.ts
│   ├── index.ts
│   └── post.controller.ts
├── dtos
│   ├── create-category.dto.ts
│   ├── create-comment.dto.ts
│   ├── create-post.dto.ts
│   ├── index.ts
│   ├── query-category.dto.ts
│   ├── query-post.dto.ts
│   ├── update-category.dto.ts
│   └── update-post.dto.ts
├── entities
│   ├── category.entity.ts
│   ├── comment.entity.ts
│   ├── index.ts
│   └── post.entity.ts
├── repositories
│   ├── category.repository.ts
│   ├── comment.repository.ts
│   ├── index.ts
│   └── post.repository.ts
├── services
│   ├── category.service.ts
│   ├── comment.service.ts
│   ├── index.ts
│   ├── post.service.ts
│   └── sanitize.service.ts
└── subscribers
    ├── index.ts
    └── post.subscriber.ts

应用编码

这节多了一个新的概念,即subscriber,具体请查阅typeorm文档,当然你也可以在模型中使用事件处理函数,效果没差别

模型

CategoryEntity

代码:src/modules/content/entities/category.entity.ts

PostEntity

代码: src/modules/content/entities/post.entity.ts

type字段的类型用enum枚举来设置,首先需要定义一个PostBodyTypeenum类型,可以添加一个constants.ts文件来统一定义这些enum和常量

存储类

CategoryRepository

代码: src/modules/content/repositories/category.repository.ts

因为CategoryRepository继承自TreeRepository,所以我们在typeorm源码中找到这个类,并对部分方法进行覆盖,如此我们就可以对树形分类进行排序,覆盖的方法如下

当然后面会讲到更加深入的再次封装,此处暂时先这么用

DTO 验证

新增QueryCategoryDtoQueryPostDto用于查询分类和文章时进行分页以及过滤数据和设置排序类型等

在添加DTO之前,现在添加几个数据转义函数,以便把请求中的字符串改成需要的数据类型

// src/core/helpers.ts

// 用于请求验证中的 number 数据转义
export function tNumber(value?: string | number): string |number | undefined
// 用于请求验证中的 boolean 数据转义
export function tBoolean(value?: string | boolean): string |boolean | undefined
// 用于请求验证中转义 null
export function tNull(value?: string | null): string | null | undefined

修改create-category.dto.tscreate-comment.dto.tsparent字段的@Transform装饰器

export class CreateCategoryDto {
...
    @Transform(({ value }) => tNull(value))
    parent?: string;
}

添加一个通用的DTO接口类型

// src/core/types.ts

// 分页验证 DTO 接口
export interface PaginateDto {
    page: number;
    limit: number;
}

QueryCategoryDto

代码: src/modules/content/dtos/query-category.dto.ts

QueryPostDto

除了与QueryCateogryDto一样的分页属性外,其它属性如下

orderBy字段是一个enum类型的字段,它的可取值如下

服务类

SanitizeService

代码: src/modules/content/services/sanitize.service.ts

此服务类用于clean html

sanitize方法用于对 HTML 数据进行防注入处理

CategoryService

代码:src/modules/content/services/category.service.ts

添加一个辅助函数,用于对打平后的树形数据进行分页

// src/core/helpers.ts
export function manualPaginate<T extends ObjectLiteral>(
    { page, limit }: PaginateDto,
    data: T[],
): Pagination<T>

新增paginate(query: QueryCategoryDto)方法用于处理分页

async paginate(query: QueryCategoryDto) {
    // 获取树形数据
    const tree = await this.findTrees();
    // 打平树形数据
    const list = await this.categoryRepository.toFlatTrees(tree);
    // 调用手动分页函数进行分页
    return manualPaginate(query, list);
}

PostService

代码:src/modules/content/services/post.service.ts

async paginate(params: FindParams, options: IPaginationOptions) {
    const query = await this.getListQuery(params);
    return paginate<PostEntity>(query, options);
}

订阅者

PostSubscriber

代码: src/modules/content/subscribers/post.subscriber.ts

一个需要注意的点是需要在subcriber类的构造函数中注入Connection才能获取链接

   constructor(
        connection: Connection,
        protected sanitizeService: SanitizeService,
    ) {
        connection.subscribers.push(this);
    }

注册订阅者

把订阅者注册成服务后,由于在构造函数中注入了connection这个连接对象,所以typeorm会自动把它加载到这个默认连接的subscribers配置中

// src/modules/content/subscribers/post.subscriber.ts
import * as SubscriberMaps from './subscribers';
const subscribers = Object.values(SubscriberMaps);
@Module({
    ....
    providers: [...subscribers, ...dtos, ...services],
})

控制器

CategoryController

代码: src/modules/content/controllers/category.controller.ts

    @Get()
    // 分页查询
    async list(
        @Query(
            new ValidationPipe({
                transform: true,
                forbidUnknownValues: true,
                validationError: { target: false },
            }),
        )
        query: QueryCategoryDto,
    ) {
        return this.categoryService.paginate(query);
    }

    // 查询树形分类
    @Get('tree')
    async index() {
        return this.categoryService.findTrees();
    }

PostController

代码: src/modules/content/controllers/post.controller.ts

修改index方法用于分页查询

// 通过分页查询数据
async index(
        @Query(
            new ValidationPipe({
                transform: true,
                forbidUnknownValues: true,
                validationError: { target: false },
            }),
        )
        { page, limit, ...params }: QueryPostDto,
    ) {
        return this.postService.paginate(params, { page, limit });
    }
3640 次点击
所在节点    Node.js
11 条回复
putaozhenhaochi
2022-07-10 10:10:34 +08:00
大哥 不在这搞 seo 行吗
golangLover
2022-07-10 10:17:54 +08:00
支持一下,辛苦了
dinjufen
2022-07-10 11:10:46 +08:00
网站订阅有点贵,但是内容又不多
lichnow
2022-07-10 11:29:16 +08:00
@dinjufen 还可以啦,教程每天都会更新,后面 react18 立马也上线了
Kipp
2022-07-10 11:59:30 +08:00
昨晚看还是全免费,今早就订阅者免费了,建议发推广
lichnow
2022-07-10 12:10:44 +08:00
@Kipp 没办法,跟合伙人闹掰辞职了,在找工作,缺钱啊😄
chenzhe
2022-07-10 23:38:53 +08:00
昨晚点开看了一下
说实在的,对于初学者来说,讲得太浅显,对于真正后端开发的熟手来说,好像又犯不着看这样的教程。
感觉定位有点儿尴尬。
而且这样的帖子明显是推广贴,应该换一个节点发。

真的做付费视频,希望能够讲的更加详细一些,带着新手一步一步把 nestjs 吃透。
数据库操作这边,选 mongoose 或者 typeorm 详细的讲一下。
lichnow
2022-07-11 10:11:55 +08:00
@chenzhe 视频才更到 5 啊,后面还有 30 集,很多东西得一步步深入不是?像 typeorm 5-10 是专门讲解的,用户系统 11-15 讲解,16-18 就讲 RABC,不可能几集就能全部讲完啊
lichnow
2022-07-11 10:20:15 +08:00
@chenzhe 并且看个视频肯定是无法直接吃透的,需要跟着视频里的做,然后有无法理解的东西在提问这样才行
chenzhe
2022-07-11 12:36:31 +08:00
@lichnow 的确才更新了一点儿,但是看前五集就能知道你这个视频并不是针对初学者的。至于跟着视频做,看了视频,连为啥这么做都不知道。
lichnow
2022-07-11 13:19:37 +08:00
@chenzhe 光看视频肯定不行,需要跟着源代码一步步实现,我每一集的源代码单独一个包,然后实现过程中有问题(比如不知道为啥这么做)可以群里或者问答频道提问,我会耐心解答

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/865211

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX