Skip to content

Commit

Permalink
refactor: improvements to the unique rule (#1012)
Browse files Browse the repository at this point in the history
  • Loading branch information
Xstoudi authored Dec 4, 2024
1 parent 151f862 commit 62a1ba5
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 10 deletions.
15 changes: 15 additions & 0 deletions providers/database_provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { QueryClient } from '../src/query_client/index.js'
import { BaseModel } from '../src/orm/base_model/index.js'
import { DatabaseTestUtils } from '../src/test_utils/database.js'
import type { DatabaseConfig, DbQueryEventNode } from '../src/types/database.js'
import { DatabaseQueryBuilderContract } from '../src/types/querybuilder.js'

/**
* Extending AdonisJS types
Expand Down Expand Up @@ -49,6 +50,20 @@ declare module '@vinejs/vine' {
*/
unique(callback: (db: Database, value: string, field: FieldContext) => Promise<boolean>): this

/**
* Ensure the value is unique inside the database by table and column name.
* Optionally, you can define a filter to narrow down the query.
*/
unique(options: {
table: string
column?: string
filter?: (
db: DatabaseQueryBuilderContract,
value: unknown,
field: FieldContext
) => Promise<void>
}): this

/**
* Ensure the value is exists inside the database by self
* executing a query.
Expand Down
50 changes: 40 additions & 10 deletions src/bindings/vinejs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,54 @@

import vine, { VineNumber, VineString } from '@vinejs/vine'
import type { Database } from '../database/main.js'
import type { FieldContext } from '@vinejs/vine/types'
import { DatabaseQueryBuilderContract } from '../types/querybuilder.js'

/**
* Defines the "unique" and "exists" validation rules with
* VineJS.
*/
export function defineValidationRules(db: Database) {
const uniqueRule = vine.createRule<Parameters<VineString['unique'] | VineNumber['unique']>[0]>(
async (value, checker, field) => {
if (!field.isValid) {
return
const uniqueRule = vine.createRule<
| ((db: Database, value: string, field: FieldContext) => Promise<boolean>)
| {
table: string
column?: string
filter?: (
db: DatabaseQueryBuilderContract,
value: unknown,
field: FieldContext
) => Promise<void>
}
>(async (value, checkerOrOptions, field) => {
if (!field.isValid) {
return
}

const isUnique = await checker(db, value as string, field)
if (typeof checkerOrOptions === 'function') {
const isUnique = await checkerOrOptions(db, value as string, field)
if (!isUnique) {
field.report('The {{ field }} has already been taken', 'database.unique', field)
}
return
}
)

if (typeof value !== 'string') {
return
}

if (typeof field.name !== 'string') {
return
}

const { table, column = field.name, filter } = checkerOrOptions
const baseQuery = db.from(table).select(column).where(column, value)
await filter?.(baseQuery, value, field)
const row = await baseQuery.first()
if (row) {
field.report('The {{ field }} has already been taken', 'database.unique', field)
}
})

const existsRule = vine.createRule<Parameters<VineString['exists'] | VineNumber['exists']>[0]>(
async (value, checker, field) => {
Expand All @@ -41,14 +71,14 @@ export function defineValidationRules(db: Database) {
}
)

VineString.macro('unique', function (this: VineString, checker) {
return this.use(uniqueRule(checker))
VineString.macro('unique', function (this: VineString, checkerOrOptions) {
return this.use(uniqueRule(checkerOrOptions))
})
VineString.macro('exists', function (this: VineString, checker) {
return this.use(existsRule(checker))
})
VineNumber.macro('unique', function (this: VineNumber, checker) {
return this.use(uniqueRule(checker))
VineNumber.macro('unique', function (this: VineNumber, checkerOrOptions) {
return this.use(uniqueRule(checkerOrOptions))
})
VineNumber.macro('exists', function (this: VineNumber, checker) {
return this.use(existsRule(checker))
Expand Down

0 comments on commit 62a1ba5

Please sign in to comment.