1- import { IContext , IValidationOptions , IValidationResult } from "../Interface" ;
1+ import {
2+ IContext ,
3+ ITraverseItem ,
4+ IValidationOptions ,
5+ IValidationResult ,
6+ } from "../Interface" ;
27import { getMessage } from "../Locale" ;
38import { Definition , ValidationResult } from "../Types" ;
49import { toRuleDefinition } from "../Factory" ;
5- import { getValueViaPath } from "./getValueViaPath" ;
610import { getOptions } from "../Options" ;
711
812export const validate = async (
@@ -29,6 +33,65 @@ export const validate = async (
2933 } ;
3034} ;
3135
36+ const toTraverseArray = ( data : any , definition : Definition ) => {
37+ function resolvePath ( data : any , path : string ) {
38+ const parts = path . split ( "." ) ;
39+ const result : Array < { path : string ; value : any } > = [ ] ;
40+
41+ function traverse (
42+ current : any ,
43+ index = 0 ,
44+ resolvedPath : Array < string | number > = [ ]
45+ ) {
46+ if ( index >= parts . length ) {
47+ result . push ( { path : resolvedPath . join ( "." ) , value : current } ) ;
48+ return ;
49+ }
50+
51+ const part = parts [ index ] ;
52+
53+ if ( part === "*" ) {
54+ if ( Array . isArray ( current ) ) {
55+ current . forEach ( ( item , i ) => {
56+ traverse ( item , index + 1 , [ ...resolvedPath , i ] ) ;
57+ } ) ;
58+ } else if ( current && typeof current === "object" ) {
59+ Object . keys ( current ) . forEach ( ( key ) => {
60+ traverse ( current [ key ] , index + 1 , [ ...resolvedPath , key ] ) ;
61+ } ) ;
62+ } else {
63+ result . push ( {
64+ path : [ ...resolvedPath , "*" ] . join ( "." ) ,
65+ value : current ,
66+ } ) ;
67+ }
68+ } else {
69+ if ( current && typeof current === "object" && part in current ) {
70+ traverse ( current [ part ] , index + 1 , [ ...resolvedPath , part ] ) ;
71+ } else {
72+ result . push ( {
73+ path : [ ...resolvedPath , part ] . join ( "." ) ,
74+ value : undefined ,
75+ } ) ;
76+ }
77+ }
78+ }
79+
80+ traverse ( data ) ;
81+ return result ;
82+ }
83+
84+ const checks : ITraverseItem [ ] = [ ] ;
85+
86+ // Example usage
87+ Object . entries ( definition ) . forEach ( ( [ path , rules ] ) => {
88+ const resolved = resolvePath ( data , path ) ;
89+ checks . push ( { path, rules, resolved } ) ;
90+ } ) ;
91+
92+ return checks ;
93+ } ;
94+
3295const getResults = async (
3396 data : any ,
3497 definition : Definition ,
@@ -38,69 +101,63 @@ const getResults = async (
38101 const fields : Record < string , boolean > = { } ;
39102 const results : ValidationResult = { } ;
40103
41- // Checking all validations
42- for ( const field in definition ) {
43- fields [ field ] = true ;
44- // Parsing the rules
45- const params = definition [ field ] ;
46- let ruleGroup : string = "" ;
47- if ( Array . isArray ( params ) ) {
48- ruleGroup = params . join ( "|" ) ;
49- } else {
50- ruleGroup = params ;
51- }
104+ const traverse = toTraverseArray ( data , definition ) ;
52105
53- const rules = toRuleNameArray ( ruleGroup ) . map ( toRuleDefinition ) ;
106+ for ( const item of traverse ) {
107+ const { path, rules, resolved } = item ;
108+ fields [ path ] = true ;
54109
55- // Getting the value by the path
56- const value = getValueViaPath ( data , field ) ;
110+ const rulesAsString = Array . isArray ( rules ) ? rules . join ( "|" ) : rules ;
111+
112+ const ruleDefinitions =
113+ toRuleNameArray ( rulesAsString ) . map ( toRuleDefinition ) ;
57114
58115 const context : IContext = {
59116 data,
60- field,
61- definition : ruleGroup ,
117+ field : path ,
118+ definition : rulesAsString ,
62119 } ;
63120
64- // Checking all rules one by one
65- for ( const rule of rules ) {
66- // If the value is empty but the rule is not required, we don't execute
67- // the rules
68- if ( rule . name !== "required" && ( value === null || value === undefined ) ) {
69- continue ;
70- }
71-
72- // Calling the rule function with the validation parameters
73- const isRuleValid = await rule . callback (
74- value ,
75- ...[ ...rule . params , context ]
76- ) ;
77-
78- // Is the value valid?
79- if ( isRuleValid === false ) {
80- if ( ! results [ field ] ) {
81- results [ field ] = [ ] ;
121+ for ( const check of resolved ) {
122+ // Checking all rules one by one
123+ for ( const rule of ruleDefinitions ) {
124+ // If the value is empty but the rule is not required, we don't execute
125+ // the rules
126+ if (
127+ rule . name !== "required" &&
128+ ( check . value === null || check . value === undefined )
129+ ) {
130+ continue ;
82131 }
83-
84- isValid = false ;
85- fields [ field ] = false ;
86-
87- // Setting the rule and the error message
88- results [ field ] . push ( {
89- rule : rule . name ,
90- message : getMessage (
91- rule . name ,
92- rule . params ,
93- options . language ,
94- options . translations || { }
95- ) ,
96- } ) ;
97-
98- if ( options . stopOnFail ) {
99- return {
100- isValid : false ,
101- fields,
102- results,
103- } ;
132+ // Calling the rule function with the validation parameters
133+ const isRuleValid = await rule . callback (
134+ check . value ,
135+ ...[ ...rule . params , context ]
136+ ) ;
137+ // Is the value valid?
138+ if ( isRuleValid === false ) {
139+ if ( ! results [ check . path ] ) {
140+ results [ check . path ] = [ ] ;
141+ }
142+ isValid = false ;
143+ fields [ path ] = false ;
144+ // Setting the rule and the error message
145+ results [ check . path ] . push ( {
146+ rule : rule . name ,
147+ message : getMessage (
148+ rule . name ,
149+ rule . params ,
150+ options . language ,
151+ options . translations || { }
152+ ) ,
153+ } ) ;
154+ if ( options . stopOnFail ) {
155+ return {
156+ isValid : false ,
157+ fields,
158+ results,
159+ } ;
160+ }
104161 }
105162 }
106163 }
@@ -114,5 +171,9 @@ const getResults = async (
114171} ;
115172
116173const toRuleNameArray = ( rules : string ) : string [ ] => {
174+ if ( Array . isArray ( rules ) ) {
175+ return rules ;
176+ }
177+
117178 return rules . split ( "|" ) ;
118179} ;
0 commit comments