Skip to content

Commit

Permalink
feat: add unique and exists validation rules
Browse files Browse the repository at this point in the history
  • Loading branch information
thetutlage committed Nov 28, 2023
1 parent 0ebd2ee commit ef6fbc3
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 0 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
"@types/pluralize": "^0.0.33",
"@types/pretty-hrtime": "^1.0.3",
"@types/qs": "^6.9.10",
"@vinejs/vine": "^1.7.0",
"better-sqlite3": "^9.1.1",
"c8": "^8.0.1",
"chance": "^1.1.11",
Expand Down
31 changes: 31 additions & 0 deletions providers/database_provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,30 @@ declare module '@adonisjs/core/types' {
export default class DatabaseServiceProvider {
constructor(protected app: ApplicationService) {}

/**
* Registers repl bindings when running the application
* in the REPL environment
*/
protected async registerReplBindings() {
if (this.app.getEnvironment() === 'repl') {
const { defineReplBindings } = await import('../src/bindings/repl.js')
defineReplBindings(this.app, await this.app.container.make('repl'))
}
}

/**
* Registers validation rules for VineJS
*/
protected async registerVineJSRules(db: Database) {
if (this.app.usingVineJS) {
const { defineValidationRules } = await import('../src/bindings/vinejs.js')
defineValidationRules(db)
}
}

/**
* Invoked by AdonisJS to register container bindings
*/
register() {
this.app.container.singleton(Database, async (resolver) => {
const config = this.app.config.get<DatabaseConfig>('database')
Expand All @@ -46,8 +70,15 @@ export default class DatabaseServiceProvider {
this.app.container.alias('lucid.db', Database)
}

/**
* Invoked by AdonisJS to extend the framework or pre-configure
* objects
*/
async boot() {
const db = await this.app.container.make('lucid.db')
BaseModel.$adapter = new Adapter(db)

await this.registerReplBindings()
await this.registerVineJSRules(db)
}
}
78 changes: 78 additions & 0 deletions src/bindings/vinejs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* @adonisjs/lucid
*
* (c) AdonisJS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

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

/**
* The callback function to check a row inside the database
*/
type DatabaseRowChecker = (db: Database, value: string, field: FieldContext) => Promise<boolean>

declare module '@vinejs/vine' {
export interface VineString {
/**
* Ensure the value is unique inside the database by self
* executing a query.
*
* - 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: DatabaseRowChecker): this

/**
* Ensure the value is exists inside the database by self
* executing a query.
*
* - The callback must return "true", if the value exists.
* - The callback must return "false", if the value does not exist.
*/
exists(callback: DatabaseRowChecker): this
}
}

/**
* Defines the "unique" and "exists" validation rules with
* VineJS.
*/
export function defineValidationRules(db: Database) {
const uniqueRule = vine.createRule<DatabaseRowChecker>(
async (value, optionsOrCallback, field) => {
if (!field.isValid) {
return
}

const isUnqiue = await optionsOrCallback(db, value as string, field)
if (!isUnqiue) {
field.report('The {{ field }} has already been taken', 'database.unique', field)
}
}
)

const existsRule = vine.createRule<DatabaseRowChecker>(
async (value, optionsOrCallback, field) => {
if (!field.isValid) {
return
}

const exists = await optionsOrCallback(db, value as string, field)
if (!exists) {
field.report('The selected {{ field }} is invalid', 'database.exists', field)
}
}
)

VineString.macro('unique', function (this: VineString, optionsOrCallback) {
return this.use(uniqueRule(optionsOrCallback))
})
VineString.macro('exists', function (this: VineString, optionsOrCallback) {
return this.use(existsRule(optionsOrCallback))
})
}

0 comments on commit ef6fbc3

Please sign in to comment.