@adocasts.com/dto

中文文档
0.0.10发布于 2025/06/23MITES Module近 30 天下载 6673

从 Lucid 模型轻松地制作和生成 DTO

中文使用文档
文档目录11 节
  1. 安装
  2. 生成 DTOs 命令
  3. 制作 DTO 命令
  4. 如果没有账户模型呢?
  5. 制作校验器命令
  6. 生成验证器命令
  7. BaseDto 帮助器
  8. 需要注意的东西
  9. 示例
  10. 示例验证器
  11. 使用生成验证器

@ adocasts.com/dto (英语)

从 Lucid Models 轻松地制作和生成 DTO 和验证器

gh-workflow-image npm-image license-image

将 Lucid Models 转换为 DTO 文件可能是一个乏味的任务. 这个套装旨在让它少一点 读取您模型的属性定义并将其移植到 DTO. 会是完美的吗? 可能不会,但应该可以帮助削减 需要重复完成这项任务.

安装

您可以通过 Ace CLI 轻松地安装和配置 add 命令.

node ace add @adocasts.com/dto
手动安装配置( C)

如果您愿意,也可以手动安装和配置

npm install @adocasts.com/dto
node ace configure @adocasts.com/dto
定义 DTO 导入路径

生成的 DTO 将使用 #dtos/* 用于DTO内部的关系进口. 因此,我们建议在您 package.json

"imports": {
  "#dtos/*": "./app/dtos/*.js"
}

生成 DTOs 命令

想要为您的模型生成 DTO 吗 ? 这是命令!

node ace generate:dtos

这将读取您所有的模型文件, 收集其属性和类型 . 然后把这些地产的种类转换成系列安全类型 及其与DTO表述的关系.

您也可以通过使用 DTO 生成验证符和 DTO --validator 旗帜 :

node ace generate:dtos --validator
File Tree                       Class
------------------------------------------------
└── app/
    ├── dtos/
    │   ├── account.ts          AccountDto
    │   ├── account_group.ts    AccountGroupDto
    │   ├── account_type.ts     AccountTypeDto
    │   ├── income.ts           IncomeDto
    │   ├── payee.ts            PayeeDto
    │   └── user.ts             UserDto
    └── models/
        ├── account.ts          Account
        ├── account_group.ts    AccountGroup
        ├── account_type.ts     AccountType
        ├── income.ts           Income
        ├── payee.ts            Payee
        └── user.ts             User
  • 从定义的位置获取您的模型文件列表 adonisrc.ts 文件
  • 将这些文件读取为纯文本, 拖放为仅属性定义
  • 确定属性名称、类型、关系是否以及是否可选修改 ?
  • 将这些模型类型转换成序列化表示(目前转换非常松散)
    • 注意,目前这不包括模型属性上定义的序列化行为(如: serializeAs(中文(简体) )
  • 从这些转换创建 DTO 属性定义
  • 为每个财产编制构造器值设置符
  • 收集关系所需进口物品
  • 生成 DTO 文件
    • 注意, 如果一个文件已经存在于指定的 DTO 位置, 它将被跳过

制作 DTO 命令

要制作一个普通的DTO文件, 还是从一个单一的模型中制作一个单独的DTO? 这是命令!

要制作一个 DTO 命名 AccountDto 在一个位于 dto/account.ts,我们可以运行如下:

node ace make:dto account

这将检查是否有一个模型命名 Account。 。 。 . 如果找到一个模型,它将使用该模型的属性定义生成 AccountDto。 。 。 . 否则,它只会产生一个 AccountDto 带有空类的文件.

您也可以通过使用 DTO 生成一个验证符 --validator 旗帜 :

node ace make:dto account --validator

这将创建一个DTO和一个账户模型的验证器.

File Tree                       Class
------------------------------------------------
└── app/
    ├── dtos/
    │   ├── account.ts          AccountDto
    └── models/
        ├── account.ts          Account

如果没有账户模型呢?

如上所述,一个平地 AccountDto 类将在新的 dto/account.ts 文档,它看起来像下面.

export default class AccountDto {}

指定不同的模式

如果 DTO 和 Model 名称不匹配, 您可以指定一个特定的模型, 通过 : --model 旗帜.

node ace make:dto account --model=main_account

现在,而不是寻找一个模型 命名 Account 它会代替 寻找 MainAccount 并使用它创建命名为 DTO AccountDto。 。 。 .

制作校验器命令

想做个模型的验证员吗? 此命令的工作与 make:dto 命令 :

node ace make:validators account

这将检查是否有一个模型命名 Account。 。 。 . 如果找到一个模型,它将使用该模型的属性定义生成 accountValidator。 。 。 . 否则,它只会生成一个带有空子图案的普通验证器文件.

File Tree                       Variable
------------------------------------------------
└── app/
    ├── validators/
    │   ├── account.ts          accountValidator
    └── models/
        ├── account.ts          Account

指定不同的模式

就像DTO一样,你可以指定一个不同的模型:

node ace make:validators account --model=main_account

生成验证器命令

想要为您所有模型生成验证器吗 ? 此命令的作用类似 generate:dtos数字 :

node ace generate:validators

这将读取您所有的模型文件, 收集其属性和类型 . 然后把这些财产的种类转换成VineJS验证规则.

File Tree                       Variable
------------------------------------------------
└── app/
    ├── validators/
    │   ├── account.ts          accountValidator
    │   ├── account_group.ts    accountGroupValidator
    │   ├── account_type.ts     accountTypeValidator
    │   ├── income.ts           incomeValidator
    │   ├── payee.ts            payeeValidator
    │   └── user.ts             userValidator
    └── models/
        ├── account.ts          Account
        ├── account_group.ts    AccountGroup
        ├── account_type.ts     AccountType
        ├── income.ts           Income
        ├── payee.ts            Payee
        └── user.ts             User

BaseDto 帮助器

在v0.0.4中新添加的,我们现在包括a BaseDto 或者说 BaseModelDto 考虑我们是否从模型中生成你的DTO. 这两个基地都有一个叫 fromArray。通过此程序,您可以在一系列源对象中通过. 然后我们绕过它们 然后把每个都传到一个新的建筑师身上 假设您将把属性 包含在您的 DTO 构造器中.

举个例子

class Test extends BaseModel {
  @column()
  declare id: number
}

class TestDto extends BaseModelDto {
  declare id: number

  constructor(instance: Test) {
    super()
    this.id = instance.id
  }
}

const tests = await Test.createMany([{ id: 1 }, { id: 2 }, { id: 3 }])

const dtoArray = TestDto.fromArray(tests)
// [TestDto, TestDto, TestDto]

此外, BaseModelDto 还包括a fromPaginator 帮手 这样你就可以通过一个实例 ModelPaginator 改为a SimplePaginatorDto 我们已经在这个一揽子方案中界定。 您也可以通过一个 URL 范围的开始和结束, 我们会为您生成这些 URL 在转换期间.

举个简单的例子

class Test extends BaseModel {
  @column()
  declare id: number
}

class TestDto extends BaseModelDto {
  declare id: number

  constructor(instance: Test) {
    super()
    this.id = instance.id
  }
}

const tests = await Test.createMany([{ id: 1 }, { id: 2 }, { id: 3 }])

const paginator = await Test.query().paginate(1, 2)
const paginatorDto = TestDto.fromPaginator(paginator, { start: 1, end: 2 })
/**
 * {
 *    data: TestDto[],
 *    meta: SimplePaginatorDtoMetaContract
 * }
 */

const paginationUrls = paginatorDto.meta.pagesInRange
/**
 * [
 *    {
 *      url: '/?page=1',
 *      page: 1,
 *      isActive: true
 *    },
 *    {
 *      url: '/?page=2',
 *      page: 2,
 *      isActive: false
 *    },
 * ]
 */

举例来说,如果您在使用Inertia之类的东西,那么您可以按此输入道具

import { SimplePaginatorDtoContract } from '@adocasts.com/dto/types'
import DifficultyDto from '#dtos/difficulty'

const props = defineProps<{
  paginated: SimplePaginatorDtoContract<DifficultyDto>
}>()

const rows = props.paginated.data
const info = props.paginated.meta
const urls = props.paginated.meta.pagesInRange

需要注意的东西

  • 目前我们从模型的文件名中假定模型的名字.
  • 目前没有更改 DTO 输出目录的设置
  • 由于反射的限制,我们把模型读作"平正文字". 我不是TypeScript wiz, 所以,如果你知道更好的方法,我都是耳朵!
    • 既然我们读成简写
      • 目前我们忽略了装饰师和他们的选择

示例

所以,我们用账号作为我们的例子 在整个指南, 让我们看看这个账户模型是什么样子!

账户模式
// app/models/account.ts

import { DateTime } from 'luxon'
import { BaseModel, belongsTo, column, computed, hasMany, hasOne } from '@adonisjs/lucid/orm'
import User from './user.js'
import type { BelongsTo, HasMany, HasOne } from '@adonisjs/lucid/types/relations'
import AccountType from '#models/account_type'
import Payee from '#models/payee'
import Stock from '#models/stock'
import Transaction from '#models/transaction'
import AccountTypeService from '#services/account_type_service'
import { columnCurrency } from '#start/orm/column'
import type { AccountGroupConfig } from '#config/account'

export default class Account extends BaseModel {
  // region Columns

  @column({ isPrimary: true })
  declare id: number

  @column()
  declare userId: number

  @column()
  declare accountTypeId: number

  @column()
  declare name: string

  @column()
  declare note: string

  @column.date()
  declare dateOpened: DateTime | null

  @column.date()
  declare dateClosed: DateTime | null

  @columnCurrency()
  declare balance: number

  @columnCurrency()
  declare startingBalance: number

  @column.dateTime({ autoCreate: true })
  declare createdAt: DateTime

  @column.dateTime({ autoCreate: true, autoUpdate: true })
  declare updatedAt: DateTime

  // endregion

  // region Unmapped Properties

  aggregations: Record<string, number> = {}

  // endregion

  // region Relationships

  @belongsTo(() => User)
  declare user: BelongsTo<typeof User>

  @belongsTo(() => AccountType)
  declare accountType: BelongsTo<typeof AccountType>

  @hasOne(() => Payee)
  declare payee: HasOne<typeof Payee>

  @hasMany(() => Stock)
  declare stocks: HasMany<typeof Stock>

  @hasMany(() => Transaction)
  declare transactions: HasMany<typeof Transaction>

  // endregion

  // region Computed Properties

  @computed()
  get accountGroup(): AccountGroupConfig {
    return AccountTypeService.getAccountTypeGroup(this.accountTypeId)
  }

  @computed()
  get isCreditIncrease(): boolean {
    return AccountTypeService.isCreditIncreaseById(this.accountTypeId)
  }

  @computed()
  get isBudgetable() {
    return AccountTypeService.isBudgetable(this.accountTypeId)
  }

  @computed()
  get balanceDisplay() {
    return '$' + this.balance.toLocaleString('en-US')
  }

  // endregion
}

有个

  • 列属性
  • 无效属性
  • 未映射的属性, 包含默认值
  • 计数器
  • 关系

让我们看看,当我们生成我们的DTO!

node ace make:dto account
账户 DTO
import { BaseModelDto } from '@adocasts.com/dto/base'
import Account from '#models/account'
import UserDto from '#dtos/user'
import AccountTypeDto from '#dtos/account_type'
import PayeeDto from '#dtos/payee'
import StockDto from '#dtos/stock'
import TransactionDto from '#dtos/transaction'
import { AccountGroupConfig } from '#config/account'

export default class AccountDto extends BaseModelDto {
  declare id: number
  declare userId: number
  declare accountTypeId: number
  declare name: string
  declare note: string
  declare dateOpened: string | null
  declare dateClosed: string | null
  declare balance: number
  declare startingBalance: number
  declare createdAt: string
  declare updatedAt: string
  aggregations: Record<string, number> = {}
  declare user: UserDto | null
  declare accountType: AccountTypeDto | null
  declare payee: PayeeDto | null
  declare stocks: StockDto[]
  declare transactions: TransactionDto[]
  declare accountGroup: AccountGroupConfig
  declare isCreditIncrease: boolean
  declare isBudgetable: boolean
  declare balanceDisplay: string

  constructor(account?: Account) {
    super()

    if (!account) return
    this.id = account.id
    this.userId = account.userId
    this.accountTypeId = account.accountTypeId
    this.name = account.name
    this.note = account.note
    this.dateOpened = account.dateOpened?.toISO()!
    this.dateClosed = account.dateClosed?.toISO()!
    this.balance = account.balance
    this.startingBalance = account.startingBalance
    this.createdAt = account.createdAt.toISO()!
    this.updatedAt = account.updatedAt.toISO()!
    this.aggregations = account.aggregations
    this.user = account.user && new UserDto(account.user)
    this.accountType = account.accountType && new AccountTypeDto(account.accountType)
    this.payee = account.payee && new PayeeDto(account.payee)
    this.stocks = StockDto.fromArray(account.stocks)
    this.transactions = TransactionDto.fromArray(account.transactions)
    this.accountGroup = account.accountGroup
    this.isCreditIncrease = account.isCreditIncrease
    this.isBudgetable = account.isBudgetable
    this.balanceDisplay = account.balanceDisplay
  }
}

它拥有

  • 需要的进口(它将试图通过参考模型的进口来获取全部的进口)
  • 从我们的模型中列属性
  • 无效财产无效
  • 我们模型中未显示的属性, 加上默认值
  • 转换成DTO表示的关系
  • 获取器及其类型,当指定。 如果推断类型, 如果变量名称开始, 类型会默认为字符串或布尔 is
  • 上述所有项目的构造值设置器
  • 帮助方法 fromArray 如果需要的话, 将常态化为空数组

示例验证器

让我们看看当我们生成一个验证器 当我们的账户模型:

node ace make:validator account
账户验证人
import vine from '@vinejs/vine'
import Account from '#models/account'
import { AccountGroupConfig } from '#config/account'

export const accountValidator = vine.compile(
  vine.object({
    id: vine.number(),
    userId: vine.number(),
    accountTypeId: vine.number(),
    name: vine.string().trim(),
    note: vine.string().trim(),
    dateOpened: vine.string().datetime().optional(),
    dateClosed: vine.string().datetime().optional(),
    balance: vine.number(),
    startingBalance: vine.number(),
    createdAt: vine.string().datetime(),
    updatedAt: vine.string().datetime(),
    user: vine.object({}),
    accountType: vine.object({}),
    payee: vine.object({}),
    stocks: vine.array(vine.object({})),
    transactions: vine.array(vine.object({})),
    accountGroup: vine.object({}),
    isCreditIncrease: vine.boolean(),
    isBudgetable: vine.boolean(),
    balanceDisplay: vine.string().trim()
  })
)

其经有:

  • 需要从模型进口
  • 所有模型属性的验证规则
  • 适当的类型转换( 如 DateTime to string (. datetime ()) )
  • 无效属性的可选修改符
  • 关系对象和数组验证符

使用生成验证器

一旦您生成了验证器, 您可以在您的控制器或路由中使用它来验证输入的数据 :

import { accountValidator } from '#validators/account'
import { HttpContext } from '@adonisjs/core/http'

export default class AccountsController {
  async store({ request, response }: HttpContext) {
    try {
      // Validate the request data
      const data = await accountValidator.validate(request.all())

      // Create the account
      const account = await Account.create(data)

      // Return the account as a DTO
      return response.created(new AccountDto(account))
    } catch (error) {
      // Handle validation errors
      if (error.code === 'E_VALIDATION_ERROR') {
        return response.unprocessableEntity(error.messages)
      }

      throw error
    }
  }
}

版本与发布记录

@adocasts.com/dto 共发布 10 个版本,首次发布于 2024/06/10;过去 12 个月发布了 0 个版本。

版本发布日期距今
0.0.10latest2025/06/231.4 年前
0.0.92024/11/142 年前
0.0.82024/11/142 年前
0.0.72024/11/082 年前
0.0.62024/07/252 年前
0.0.5-02024/07/252 年前

关于 @adocasts.com/dto 的常见问题

@adocasts.com/dto 是什么?

从 Lucid 模型轻松地制作和生成 DTO

如何安装 @adocasts.com/dto?

在项目目录运行 npm install @adocasts.com/dto,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:@adonisjs/core(^6.2.0)、@adonisjs/lucid(^21.1.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@adocasts.com/dto 支持 TypeScript 吗?

该包的 package.json 没有声明类型入口。需要类型提示时,可以在 npm 上查找社区维护的 @types/adocasts.com__dto 是否存在。

@adocasts.com/dto 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@adocasts.com/dto 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=20.6.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@adocasts.com/dto 可以商用吗?

@adocasts.com/dto 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@adocasts.com/dto 的下载量有多少?

根据 npm 官方下载统计,@adocasts.com/dto 在 2026/10/05 – 2026/11/03 这 30 天内被下载 6,673 次,日均约 222 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@adocasts.com/dto 还在维护吗?

最新版本 0.0.10 发布于 2025/06/23(1.4 年前);至今共发布 10 个版本,其中过去 12 个月发布了 0 个;首次发布于 2024/06/10。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。