@@ -12,6 +12,7 @@ protected function prepareForValidation()
1212 {
1313 parent ::prepareForValidation ();
1414 $ this ->route ('id ' ) && $ this ->merge (['id ' => $ this ->route ('id ' )]);
15+ $ this ->autoCast ();
1516 }
1617
1718 protected function idRule (): array
@@ -20,4 +21,68 @@ protected function idRule(): array
2021 'id ' => ['required ' , 'integer ' , 'gt:0 ' ],
2122 ];
2223 }
24+
25+ private function autoCast (): void
26+ {
27+ $ casts = $ this ->inferCastsFromRules ();
28+
29+ $ this ->merge (
30+ collect ($ this ->all ())
31+ ->mapWithKeys (fn ($ value , $ key ) => [
32+ $ key => $ this ->castValue (
33+ $ key ,
34+ $ value ,
35+ $ casts [$ key ] ?? null
36+ ),
37+ ])
38+ ->toArray ()
39+ );
40+ }
41+
42+ private function inferCastsFromRules (): array
43+ {
44+ return collect ($ this ->rules ())
45+ ->mapWithKeys (fn ($ rules , $ key ) => [
46+ $ key => $ this ->parseTypeFromRules ((array ) $ rules ),
47+ ])
48+ ->filter ()
49+ ->toArray ();
50+ }
51+
52+ private function parseTypeFromRules (array $ rules ): ?string
53+ {
54+ foreach ($ rules as $ rule ) {
55+ if ($ type = $ this ->matchPrimitiveType ($ rule )) {
56+ return $ type ;
57+ }
58+ }
59+
60+ return null ;
61+ }
62+
63+ private function matchPrimitiveType ($ rule ): ?string
64+ {
65+ return match (true ) {
66+ $ rule === 'integer ' => 'int ' ,
67+ $ rule === 'boolean ' => 'bool ' ,
68+ $ rule === 'numeric ' => 'float ' ,
69+ $ rule === 'array ' => 'array ' ,
70+ default => null
71+ };
72+ }
73+
74+ private function castValue (string $ key , mixed $ value , ?string $ type ): mixed
75+ {
76+ if ($ type === null || $ value === null ) {
77+ return $ value ;
78+ }
79+
80+ return match ($ type ) {
81+ 'int ' => (int ) $ value ,
82+ 'float ' => (float ) $ value ,
83+ 'bool ' => filter_var ($ value , FILTER_VALIDATE_BOOLEAN ),
84+ 'array ' => (array ) $ value ,
85+ default => $ value
86+ };
87+ }
2388}
0 commit comments