Validations Beyond Presence: Conditional Rules, Custom Validators, and Enum Integration #3270
bpamiri
announced in
Announcements
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
The first validation everyone writes is
validatesPresenceOf("email"). It's the right first move — but presence is the floor, not the ceiling. The day you ship a real signup form you need the email to look like an email, to be unique within an account (not globally), to match a confirmation field but only on create, and you needstatusto be one of a closed set — neverwat.This post walks the entire Wheels 4.0 validation surface through one worked
User/Postmodel.Read: https://blog.wheels.dev/posts/validations-conditional-enum
What's in it
The validator surface. All the high-level validators side by side in one model, with a table mapping each to its exact behavior and default message:
validatesUniquenessOfwithscopefor unique-per-tenant fields — and why it's an application-levelfindAll(), not a DB constraint (back it with a real unique index).validatesFormatOfin both modes:regExfor arbitrary patterns,typefor the built-inIsValid()checks (email, URL, UUID, creditcard, ssn, zipcode, and the rest).validatesLengthOfwithwithin="3,20", and why a0bound means "not set."validatesInclusionOf/validatesExclusionOf(both requirelist).validatesConfirmationOf— and the surprise that its error lands on the virtual<property>Confirmationproperty, not the base one.Conditional validation.
condition,unless, andwhen. The post is emphatic about one thing:conditionandunlessare a fixed-grammar string parser (this.prop,this.method(), baremethod(),!negation,eq/neq/lt/lte/gt/gtecomparisons) — not CFML'sEvaluate()and not a closure. A malformed expression throwsWheels.InvalidValidationConditionin development but is logged and silently skipped in production, which means the validation quietly stops running.Custom validators.
validate(),validateOnCreate(),validateOnUpdate()register a method name; the method fails by callingaddError()/addErrorToBase(). The method's return value is ignored —return falseon bad data does nothing. This is the single most common mistake.enum() integration. One call auto-registers
validatesInclusionOf(withallowBlank=true), generatesis<Name>()boolean checkers, and creates a parameterized query scope per value. Because it allows blanks, you pair it withvalidatesPresenceOfwhen the column is required. Registration-time guards reject bad property names and stored values outside[A-Za-z0-9_- .].Reading errors correctly.
errorsOn()returns an array of{property, message, name}structs, not strings — loop and read.message. PluserrorsOnBase,allErrors,hasErrors,errorCount, andclearErrorswith their property/name filters, and how the optionalnametag lets you surgically clear one error.It ends with a sharp-edges section drawn straight from the framework source: scoped uniqueness races, presence being skipped on columns with a DB default,
allowBlankdefaulting to false everywhere it exists (and not existing on presence/confirmation), and the all-named-args rule that prevents the #1 Wheels error.Discussion
validatesUniquenessOf(scope=...)plus a composite unique index, or something else?conditionexpression? Would a louder failure mode (or a startup-time grammar check on every condition string) be worth it?enum()for closed-set fields, or still hand-writevalidatesInclusionOf+ scopes? What's missing fromenum()that would tip you over?Read the full post: https://blog.wheels.dev/posts/validations-conditional-enum
Beta Was this translation helpful? Give feedback.
All reactions