99use Illuminate \Contracts \Http \Kernel as KernelContract ;
1010use Illuminate \Contracts \Validation \Factory ;
1111use Illuminate \Routing \Router ;
12+ use Illuminate \Support \Str ;
13+ use Illuminate \View \Compilers \BladeCompiler ;
14+ use Laragear \Meta \Attributes \RegisterRule ;
1215use Laragear \Meta \Http \Middleware \MiddlewareDeclaration ;
16+ use ReflectionClass ;
1317
1418use function array_fill ;
1519use function array_fill_keys ;
1923
2024trait BootHelpers
2125{
26+ /**
27+ * Cached closures for registered validation rules.
28+ *
29+ * @var array<class-string, \Closure>
30+ */
31+ protected static array $ cachedValidationRules = [];
32+
2233 /**
2334 * Extends a manager-like service.
2435 *
@@ -53,7 +64,7 @@ protected function withValidationRule(
5364 string $ rule ,
5465 callable |string $ callback ,
5566 callable |string |null $ message = null ,
56- bool $ implicit = false
67+ bool $ implicit = false ,
5768 ): void {
5869 $ this ->callAfterResolving (
5970 'validator ' ,
@@ -63,7 +74,50 @@ static function (Factory $validator, Application $app) use ($message, $callback,
6374 $ implicit
6475 ? $ validator ->extendImplicit ($ rule , $ callback , $ message )
6576 : $ validator ->extend ($ rule , $ callback , $ message );
66- }
77+ },
78+ );
79+ }
80+
81+ /**
82+ * Registers all Validation Rules found in a directory with a message from a translation prefix.
83+ *
84+ * @param class-string|class-string[] $classes
85+ * @param string $keyPrefix If you register a translation key as "my-package", the validation
86+ * rules will use "my-package::validation.{rule}".
87+ */
88+ protected function withValidationRulesFrom (string |array $ classes , string $ keyPrefix ): void
89+ {
90+ $ this ->callAfterResolving (
91+ 'validator ' ,
92+ static function (Factory $ validator ) use ($ classes , $ keyPrefix ): void {
93+ foreach ((array ) $ classes as $ class ) {
94+ if (! isset (static ::$ cachedValidationRules [$ class ])) {
95+ static ::$ cachedValidationRules [$ class ] = [];
96+
97+ foreach ((new ReflectionClass ($ class ))->getMethods () as $ method ) {
98+ /** @var \Laragear\Meta\Attributes\RegisterRule|null $attribute */
99+ if (
100+ $ method ->isPublic () && $ method ->isStatic () &&
101+ $ attribute = Attr::of ($ method )->first (RegisterRule::class)
102+ ) {
103+ static ::$ cachedValidationRules [$ class ][$ attribute ->name ] = [
104+ $ method ->getClosure (),
105+ "$ keyPrefix::validation. " .(
106+ $ attribute ->translationKey ?: Str::snake ($ method ->getName ())
107+ ),
108+ $ attribute ->implicit ,
109+ ];
110+ }
111+ }
112+ }
113+
114+ foreach (static ::$ cachedValidationRules [$ class ] as $ name => [$ callback , $ message , $ implicit ]) {
115+ $ implicit
116+ ? $ validator ->extendImplicit ($ name , $ callback , $ message )
117+ : $ validator ->extend ($ name , $ callback , $ message );
118+ }
119+ }
120+ },
67121 );
68122 }
69123
@@ -78,7 +132,7 @@ static function (Factory $validator, Application $app) use ($message, $callback,
78132 protected function withMiddleware (string $ class ): MiddlewareDeclaration
79133 {
80134 return new MiddlewareDeclaration (
81- $ this ->app ->make (Router::class), $ this ->app ->make (KernelContract::class), $ class
135+ $ this ->app ->make (Router::class), $ this ->app ->make (KernelContract::class), $ class,
82136 );
83137 }
84138
@@ -137,7 +191,6 @@ protected function withPolicy(string $model, string $policy): void
137191 * Schedule a Job or Command using a callback.
138192 *
139193 * @param callable(\Illuminate\Console\Scheduling\Schedule):mixed $callback
140- * @return void
141194 *
142195 * @see https://www.laravelpackage.com/06-artisan-commands/#scheduling-a-command-in-the-service-provider
143196 */
@@ -160,8 +213,57 @@ protected function withPublishableMigrations(array|string $directories, array|st
160213 $ directories = (array ) $ directories ;
161214
162215 $ this ->publishesMigrations (array_fill_keys (
163- $ directories , array_fill (0 , count ($ directories ), $ this ->app ->databasePath ('migrations ' ))
216+ $ directories , array_fill (0 , count ($ directories ), $ this ->app ->databasePath ('migrations ' )),
164217 ), $ groups );
165218 }
166219 }
220+
221+ /**
222+ * Registers a simple Blade directive.
223+ *
224+ * @param array<string, callable>|string $name
225+ * @param ($name is string ? callable : null) $handler
226+ */
227+ protected function withBladeDirectives (string |array $ name , ?callable $ handler = null ): void
228+ {
229+ $ name = $ handler ? [$ name => $ handler ] : $ name ;
230+
231+ $ this ->callAfterResolving (
232+ BladeCompiler::class,
233+ static function (BladeCompiler $ blade ) use ($ name ): void {
234+ foreach ($ name as $ key => $ handler ) {
235+ $ blade ->directive ($ key , $ handler );
236+ }
237+ },
238+ );
239+ }
240+
241+ /**
242+ * Registers a directory of Blade components under a prefix.
243+ */
244+ protected function withBladeComponents (string $ path , string $ prefix ): void
245+ {
246+ $ this ->callAfterResolving (
247+ BladeCompiler::class,
248+ static function (BladeCompiler $ blade ) use ($ path , $ prefix ): void {
249+ $ blade ->componentNamespace ($ path , $ prefix );
250+ },
251+ );
252+ }
253+
254+ /**
255+ * Returns the cached validation rules.
256+ */
257+ public static function cachedValidationRules (): array
258+ {
259+ return static ::$ cachedValidationRules ;
260+ }
261+
262+ /**
263+ * Flushes cached validation rules retrieved by reflection.
264+ */
265+ public static function flushCachedValidationRules (): void
266+ {
267+ static ::$ cachedValidationRules = [];
268+ }
167269}
0 commit comments