From 8369f19e0714d0cc504cd4bac55f0876c698576f Mon Sep 17 00:00:00 2001 From: Xavier Stouder Date: Mon, 11 Mar 2024 23:22:42 +0100 Subject: [PATCH 1/3] feat: uniqueRaw and unique --- providers/database_provider.ts | 19 +++++++++- src/bindings/vinejs.ts | 68 ++++++++++++++++++++++++++-------- 2 files changed, 71 insertions(+), 16 deletions(-) diff --git a/providers/database_provider.ts b/providers/database_provider.ts index 3c8164c2..141444af 100644 --- a/providers/database_provider.ts +++ b/providers/database_provider.ts @@ -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 @@ -47,7 +48,23 @@ declare module '@vinejs/vine' { * - The callback must return "true", if the value is unique (does not exist). * - The callback must return "false", if the value is not unique (already exists). */ - unique(callback: (db: Database, value: string, field: FieldContext) => Promise): this + uniqueRaw( + callback: (db: Database, value: string, field: FieldContext) => Promise + ): 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( + table: string, + column?: string, + filter?: ( + db: DatabaseQueryBuilderContract, + value: unknown, + field: FieldContext + ) => Promise + ): this /** * Ensure the value is exists inside the database by self diff --git a/src/bindings/vinejs.ts b/src/bindings/vinejs.ts index 1db97322..48a614df 100644 --- a/src/bindings/vinejs.ts +++ b/src/bindings/vinejs.ts @@ -9,24 +9,56 @@ import vine, { VineNumber, VineString } from '@vinejs/vine' import type { Database } from '../database/main.js' +import { DatabaseQueryBuilderContract } from '../types/querybuilder.js' +import type { FieldContext } from '@vinejs/vine/types' /** - * Defines the "unique" and "exists" validation rules with + * Defines the "uniqueRaw", "unique" and "exists" validation rules with * VineJS. */ export function defineValidationRules(db: Database) { - const uniqueRule = vine.createRule[0]>( - async (value, checker, field) => { - if (!field.isValid) { - return - } + const uniqueRawRule = vine.createRule< + Parameters[0] + >(async (value, checker, field) => { + if (!field.isValid) { + return + } - const isUnqiue = await checker(db, value as string, field) - if (!isUnqiue) { - field.report('The {{ field }} has already been taken', 'database.unique', field) - } + const isUnique = await checker(db, value as string, field) + if (!isUnique) { + field.report('The {{ field }} has already been taken', 'database.unique', field) + } + }) + + const uniqueRule = vine.createRule<{ + table: string + column?: string + filter?: ( + db: DatabaseQueryBuilderContract, + value: unknown, + field: FieldContext + ) => Promise + }>(async (value, { table, column, filter }, field) => { + if (!field.isValid) { + return } - ) + + if (typeof value !== 'string') { + return + } + + if (typeof field.name !== 'string') { + return + } + + const columnName = column ?? field.name + const baseQuery = db.from(table).select(columnName).where(columnName, 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[0]>( async (value, checker, field) => { @@ -41,14 +73,20 @@ export function defineValidationRules(db: Database) { } ) - VineString.macro('unique', function (this: VineString, checker) { - return this.use(uniqueRule(checker)) + VineString.macro('uniqueRaw', function (this: VineString, checker) { + return this.use(uniqueRawRule(checker)) + }) + VineString.macro('unique', function (this: VineString, table, column, filter) { + return this.use(uniqueRule({ table, column, filter })) }) 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('uniqueRaw', function (this: VineNumber, checker) { + return this.use(uniqueRawRule(checker)) + }) + VineNumber.macro('unique', function (this: VineNumber, table, column, filter) { + return this.use(uniqueRule({ table, column, filter })) }) VineNumber.macro('exists', function (this: VineNumber, checker) { return this.use(existsRule(checker)) From f70bdce10dfc2d48a0890ab5f852350dc924b4c1 Mon Sep 17 00:00:00 2001 From: Xavier Stouder Date: Mon, 11 Mar 2024 23:34:05 +0100 Subject: [PATCH 2/3] fix: use type import --- src/bindings/vinejs.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bindings/vinejs.ts b/src/bindings/vinejs.ts index 48a614df..7b4c5066 100644 --- a/src/bindings/vinejs.ts +++ b/src/bindings/vinejs.ts @@ -9,7 +9,7 @@ import vine, { VineNumber, VineString } from '@vinejs/vine' import type { Database } from '../database/main.js' -import { DatabaseQueryBuilderContract } from '../types/querybuilder.js' +import type { DatabaseQueryBuilderContract } from '../types/querybuilder.js' import type { FieldContext } from '@vinejs/vine/types' /** From 7888b6c3cac1d2a7a35e87fed153e824c3ac84ab Mon Sep 17 00:00:00 2001 From: Xavier Stouder Date: Tue, 12 Mar 2024 18:30:46 +0100 Subject: [PATCH 3/3] fix: use overload --- providers/database_provider.ts | 12 +++---- src/bindings/vinejs.ts | 58 +++++++++++++++------------------- 2 files changed, 30 insertions(+), 40 deletions(-) diff --git a/providers/database_provider.ts b/providers/database_provider.ts index 141444af..5c1eacf4 100644 --- a/providers/database_provider.ts +++ b/providers/database_provider.ts @@ -48,23 +48,21 @@ declare module '@vinejs/vine' { * - The callback must return "true", if the value is unique (does not exist). * - The callback must return "false", if the value is not unique (already exists). */ - uniqueRaw( - callback: (db: Database, value: string, field: FieldContext) => Promise - ): this + unique(callback: (db: Database, value: string, field: FieldContext) => Promise): 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( - table: string, - column?: string, + unique(options: { + table: string + column?: string filter?: ( db: DatabaseQueryBuilderContract, value: unknown, field: FieldContext ) => Promise - ): this + }): this /** * Ensure the value is exists inside the database by self diff --git a/src/bindings/vinejs.ts b/src/bindings/vinejs.ts index 7b4c5066..87f84c50 100644 --- a/src/bindings/vinejs.ts +++ b/src/bindings/vinejs.ts @@ -9,37 +9,35 @@ import vine, { VineNumber, VineString } from '@vinejs/vine' import type { Database } from '../database/main.js' -import type { DatabaseQueryBuilderContract } from '../types/querybuilder.js' import type { FieldContext } from '@vinejs/vine/types' +import { DatabaseQueryBuilderContract } from '../types/querybuilder.js' /** - * Defines the "uniqueRaw", "unique" and "exists" validation rules with + * Defines the "unique" and "exists" validation rules with * VineJS. */ export function defineValidationRules(db: Database) { - const uniqueRawRule = vine.createRule< - Parameters[0] - >(async (value, checker, field) => { + const uniqueRule = vine.createRule< + | ((db: Database, value: string, field: FieldContext) => Promise) + | { + table: string + column?: string + filter?: ( + db: DatabaseQueryBuilderContract, + value: unknown, + field: FieldContext + ) => Promise + } + >(async (value, checkerOrOptions, field) => { if (!field.isValid) { return } - const isUnique = await checker(db, value as string, field) - if (!isUnique) { - field.report('The {{ field }} has already been taken', 'database.unique', field) - } - }) - - const uniqueRule = vine.createRule<{ - table: string - column?: string - filter?: ( - db: DatabaseQueryBuilderContract, - value: unknown, - field: FieldContext - ) => Promise - }>(async (value, { table, column, filter }, field) => { - if (!field.isValid) { + 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 } @@ -51,8 +49,8 @@ export function defineValidationRules(db: Database) { return } - const columnName = column ?? field.name - const baseQuery = db.from(table).select(columnName).where(columnName, value) + 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) { @@ -73,20 +71,14 @@ export function defineValidationRules(db: Database) { } ) - VineString.macro('uniqueRaw', function (this: VineString, checker) { - return this.use(uniqueRawRule(checker)) - }) - VineString.macro('unique', function (this: VineString, table, column, filter) { - return this.use(uniqueRule({ table, column, filter })) + 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('uniqueRaw', function (this: VineNumber, checker) { - return this.use(uniqueRawRule(checker)) - }) - VineNumber.macro('unique', function (this: VineNumber, table, column, filter) { - return this.use(uniqueRule({ table, column, filter })) + VineNumber.macro('unique', function (this: VineNumber, checkerOrOptions) { + return this.use(uniqueRule(checkerOrOptions)) }) VineNumber.macro('exists', function (this: VineNumber, checker) { return this.use(existsRule(checker))