lucid icon indicating copy to clipboard operation
lucid copied to clipboard

The unique rule from VineJS should be simpler?

Open thetutlage opened this issue 1 year ago • 7 comments

Discussed in https://github.com/adonisjs/core/discussions/4356

Originally posted by brunolipe-a January 26, 2024 I think that always use a query to check if the value is unique in database is to complex for something that we use very often.

Maybe we can have an uniqueRaw that uses the current code, and other that receives only table as required, something like that:

/**
 * Options accepted by the unique rule
 */
type Options = {
  table: string
  column?: string
  filter?: (db: DatabaseQueryBuilderContract, value: string, field: FieldContext) => Promise<void> 
}

thetutlage avatar Feb 02 '24 05:02 thetutlage

I made an implementation on a side project, but I don't know if it follows the VineJS best way of doing it:

import vine from '@vinejs/vine'
import { FieldContext } from '@vinejs/vine/types'

import db from '@adonisjs/lucid/services/db'
import { DatabaseQueryBuilderContract } from '@adonisjs/lucid/types/querybuilder'

/**
 * Options accepted by the uniqueSimple rule
 */
export type UniqueSimpleOptions = {
  table: string
  column?: string
  filter?: (db: DatabaseQueryBuilderContract, value: string, field: FieldContext) => Promise<void>
}

/**
 * Implementation
 */
async function uniqueSimple(value: unknown, options: UniqueSimpleOptions, field: FieldContext) {
  /**
   * We do not want to deal with non-string
   * values. The "string" rule will handle the
   * the validation.
   */
  if (typeof value !== 'string') {
    return
  }

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

  const columnName = options.column || field.name

  const baseQuery = db.from(options.table).select(columnName).where(columnName, value)

  await options.filter?.(baseQuery, value, field)

  const row = await baseQuery.first()

  if (row) {
    field.report('The {{ field }} has already been taken', 'database.unique', field)
  }
}

/**
 * Converting a function to a VineJS rule
 */
export const uniqueSimpleRule = vine.createRule(uniqueSimple)

brunolipe-a avatar Feb 02 '24 10:02 brunolipe-a

Happy the discussion exists as I was on my way to open an identical one.

To contribute a bit: v5 unique rule felt really straightforward and I think having it back will be a great QoL improvement: table name, field name and that's it.

Xstoudi avatar Feb 02 '24 23:02 Xstoudi

@thetutlage ; I found this implemention in adocasts repository:

https://github.com/adocasts/adocasts/blob/next/app/validators/helpers/db.ts

Used that way:

import { exists } from '#app/validators/helpers/db'

vine.string().exists(exists('table', 'field', { caseInsensitive: boolean }))

That finds my needs for the migration from @adonisjs/validation to @vinejs/vine.

I guess you could tweak it to be the exact same as the previous rule (object parameters).

Tahul avatar Feb 05 '24 23:02 Tahul

We really need simple validations, like in version 5 Please

EwertonDutra avatar Mar 11 '24 00:03 EwertonDutra

Can we have some PR for the same? Also, will be nice if you can also take care of the docs for the same

thetutlage avatar Mar 11 '24 05:03 thetutlage

Can do the PR tonight but don't want to lick the cookie so feel free to PR first. Just to clear things up, @thetutlage do you want it to fully replace the current implem or to live aside it?

Xstoudi avatar Mar 11 '24 12:03 Xstoudi

Live alongside the current API

thetutlage avatar Mar 11 '24 12:03 thetutlage

Fixed. https://lucid.adonisjs.com/docs/validation

thetutlage avatar Dec 19 '24 05:12 thetutlage