- 视频地址: https://www.bilibili.com/vide...
有问题请扫描视频中qq群二维码交换
另,自己在找工作中,心愿能有近程工作匹配(无奈去当地),有须要的老板能够看一下我的集体介绍:pincman.com/about
学习指标
- 重载TreeRepository自带办法来对树形构造的数据进行扁平化解决
- 对Typeorm查问出的数据列表进行分页解决
- 通过申请中的query查问对数据进行筛选解决,比方排序,过滤等
- 实现公布文章和勾销公布的性能
- Typeorm 模型事件和Subscriber(订阅者)的应用
- 应用
sanitize-html
对文章内容进行防注入攻打解决
预装依赖
- nestjs-typeorm-paginate实现分页
- sanitize-html过滤
html
标签,防注入攻打 - deepmerge深度合并对象
~ 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
- 增加
order
字段用于排序 - 增加
level
属性(虚构字段)用于在打平树形数据的时候增加以后项的等级
PostEntity
代码: src/modules/content/entities/post.entity.ts
type
字段的类型用enum
枚举来设置,首先须要定义一个PostBodyType
的enum
类型,能够增加一个constants.ts
文件来对立定义这些enum
和常量
- 增加
publishedAt
字段用于管制公布工夫和公布状态 - 增加
type
字段用于设置公布类型 - 增加
customOrder
字段用于自定义排序
存储类
CategoryRepository
代码: src/modules/content/repositories/category.repository.ts
因为CategoryRepository
继承自TreeRepository
,所以咱们在typeorm
源码中找到这个类,并对局部办法进行笼罩,如此咱们就能够对树形分类进行排序,笼罩的办法如下
当然前面会讲到更加深刻的再次封装,此处临时先这么用
findRoots
为根分类列表查问增加排序createDescendantsQueryBuilder
为子孙分类查询器增加排序createAncestorsQueryBuilder
为先人分类查询器增加排序
DTO验证
新增QueryCategoryDto
和QueryPostDto
用于查问分类和文章时进行分页以及过滤数据和设置排序类型等
在增加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// 用于申请验证中本义nullexport function tNull(value?: string | null): string | null | undefined
批改create-category.dto.ts
和create-comment.dto.ts
的parent
字段的@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
page
属性设置以后分页limit
属性设置每页数据量
QueryPostDto
除了与QueryCateogryDto
一样的分页属性外,其它属性如下
orderBy
用于设置排序类型isPublished
依据公布状态过滤文章category
过滤出一下分类及其子孙分类下的文章
orderBy
字段是一个enum
类型的字段,它的可取值如下
CREATED
: 依据创立工夫降序UPDATED
: 依据更新工夫降序PUBLISHED
: 依据公布工夫降序COMMENTCOUNT
: 依据评论数量降序CUSTOM
: 依据自定义的order
字段升序
服务类
SanitizeService
代码: src/modules/content/services/sanitize.service.ts
此服务类用于clean html
sanitize
办法用于对HTML数据进行防注入解决
CategoryService
代码:src/modules/content/services/category.service.ts
增加一个辅助函数,用于对打平后的树形数据进行分页
// src/core/helpers.tsexport 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
getListQuery
: 用于构建过滤与排序以及通过分类查问文章数据等性能的query
构建器paginate
: 调用getListQuery
生成query
,并作为nestjs-typeorm-paginate
的paginate
的参数对数据进行分页
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
beforeInsert
(插入数据前事件): 如果在增加文章的同时公布文章,则设置以后工夫为公布工夫beforeUpdate
(更新数据前事件): 更改公布状态会同时更新公布工夫的值,如果文章更新为未公布状态,则把公布工夫设置为nullafterLoad
(加载数据后事件): 对HTML类型的文章内容进行去标签解决避免注入攻打
一个须要留神的点是须要在subcriber
类的构造函数中注入Connection
能力获取链接
constructor( connection: Connection, protected sanitizeService: SanitizeService, ) { connection.subscribers.push(this); }
注册订阅者
把订阅者注册成服务后,因为在构造函数中注入了connection
这个连贯对象,所以typeorm
会主动把它加载到这个默认连贯的subscribers
配置中
// src/modules/content/subscribers/post.subscriber.tsimport * as SubscriberMaps from './subscribers';const subscribers = Object.values(SubscriberMaps);@Module({ .... providers: [...subscribers, ...dtos, ...services],})
控制器
CategoryController
代码: src/modules/content/controllers/category.controller.ts
list
: 通过分页来查找扁平化的分类列表index
: 把url设置成@Get('tree')
@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 }); }