From a733febe68b47a0a92a3f7d804b5da10b844d7a3 Mon Sep 17 00:00:00 2001 From: Imani Manyara Date: Mon, 26 Oct 2026 10:09:38 -0400 Subject: [PATCH 1/6] Raise driftingly/rector-laravel to ^2.7.1 for Rector 2.8 rector 2.8.0 removed Rector\Transform\ValueObject\ArrayDimFetchToMethodCall; rector-laravel 2.7.0, still allowed by ^2.3, references it, so the required rector check failed with a class-not-found error. 2.7.1 does not. --- CHANGELOG.md | 4 ++++ composer.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a7e06a6..4835bd5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,10 @@ Entries below `Unreleased` are written by CI from the GitHub release body — se ## [Unreleased] ### Fixed +- **`composer rector` failed with `Class "Rector\Transform\ValueObject\ArrayDimFetchToMethodCall" not found`.** + rector/rector 2.8.0 removed that class, and driftingly/rector-laravel 2.7.0, which `^2.3` still allowed beside + it, uses it in its array-access set. The floor is now `^2.7.1`, the first release without it, so the required + `rector` check cannot resolve the broken pair. - **The code-style CI job was formatting against Pint's defaults, not the shared laranail config.** `fix-php-code-style-issues.yml` ran `aglipanci/laravel-pint-action` with `configPath: pint.json` - a file this package does not ship. Pint handed a config path that does not exist falls back to its diff --git a/composer.json b/composer.json index 2245960..f2a5821 100644 --- a/composer.json +++ b/composer.json @@ -87,7 +87,7 @@ "symfony/process": "^7.4 || ^8.0" }, "require-dev": { - "driftingly/rector-laravel": "^2.3", + "driftingly/rector-laravel": "^2.7.1", "laranail/phone": "^0.1", "laranail/validation-js": "^0.1", "larastan/larastan": "^3.9.3", From b0a62743466e3f81acc21f08855f7855030a49b4 Mon Sep 17 00:00:00 2001 From: imanimanyara <19682005+imanimanyara@users.noreply.github.com> Date: Mon, 26 Oct 2026 14:10:44 +0000 Subject: [PATCH 2/6] chore: rector --- src/Commands/DoctorCommand.php | 2 +- src/Exceptions/TypedBuilderHint.php | 4 +-- tests/BatchDatabaseValidationTest.php | 5 ---- tests/BatchValidationGuardsTest.php | 10 -------- .../AuthorizedEachFluentFormRequest.php | 10 +------- .../Fixtures/BailMaxEachFluentFormRequest.php | 10 +------- .../BailMaxExistsFluentFormRequest.php | 9 +------ tests/Fixtures/MergeRulesBaseRequest.php | 5 ---- tests/Fixtures/MergeSchemaBaseRequest.php | 5 ---- tests/FluentFormRequestTest.php | 20 --------------- tests/FluentSchemaTest.php | 25 ------------------- tests/Pest.php | 10 -------- tests/Rules/Markup/XmlTest.php | 3 ++- tests/SchemaRulesMergeTest.php | 10 -------- 14 files changed, 8 insertions(+), 120 deletions(-) diff --git a/src/Commands/DoctorCommand.php b/src/Commands/DoctorCommand.php index 18f1771..b93f4e8 100644 --- a/src/Commands/DoctorCommand.php +++ b/src/Commands/DoctorCommand.php @@ -40,7 +40,7 @@ public function handle(RuleRegistrar $registrar): int return is_array($config) && $config !== [] ? ['OK', 'flat laranail.validation key present'] - : ['FAIL', 'config(\'laranail.validation\') is empty — provider not registered?']; + : ['FAIL', "config('laranail.validation') is empty — provider not registered?"]; }], ['rule registry', function () use ($registrar): array { $count = count($registrar->classes()); diff --git a/src/Exceptions/TypedBuilderHint.php b/src/Exceptions/TypedBuilderHint.php index 4f72b49..88dc9bc 100644 --- a/src/Exceptions/TypedBuilderHint.php +++ b/src/Exceptions/TypedBuilderHint.php @@ -113,9 +113,9 @@ public static function for(string $method): ?string private static function specialCase(string $method): ?string { return match ($method) { - 'accepted' => 'Use `FluentRule::accepted()` for permissive accepted values (`\'yes\'`, `\'on\'`, `\'1\'`, `true`). Avoid `FluentRule::boolean()->accepted()` — the `boolean` base rule rejects `\'yes\'`/`\'on\'`.', + 'accepted' => "Use `FluentRule::accepted()` for permissive accepted values (`'yes'`, `'on'`, `'1'`, `true`). Avoid `FluentRule::boolean()->accepted()` — the `boolean` base rule rejects `'yes'`/`'on'`.", 'declined' => "Use `FluentRule::boolean()->declined(...)` for strict boolean-only input. If HTML form values (`'no'`, `'off'`) need to pass, use `->rule('declined')` on the untyped builder instead.", - 'size' => 'No `size()` method — use `->exactly(...)` on a typed builder. (This package renames Laravel\'s `size:` rule to `exactly()` for clarity.)', + 'size' => "No `size()` method — use `->exactly(...)` on a typed builder. (This package renames Laravel's `size:` rule to `exactly()` for clarity.)", 'gt', 'gte', 'lt', 'lte' => 'No `gt()`/`lt()` methods — use `FluentRule::numeric()->greaterThan(FIELD)`, `->greaterThanOrEqualTo(FIELD)`, `->lessThan(FIELD)`, `->lessThanOrEqualTo(FIELD)`.', 'alphaNum' => 'No `alphaNum()` method — use `FluentRule::string()->alphaNumeric(...)`.', 'contains' => 'Use `FluentRule::array()->contains(...)` (this package exposes `contains` on `array()`, not `string()`).', diff --git a/tests/BatchDatabaseValidationTest.php b/tests/BatchDatabaseValidationTest.php index a7248bb..1baa970 100644 --- a/tests/BatchDatabaseValidationTest.php +++ b/tests/BatchDatabaseValidationTest.php @@ -804,11 +804,6 @@ public function rules(): array { return self::$testRules; } - - public function authorize(): bool - { - return true; - } }; $formRequest::$testRules = $rules; diff --git a/tests/BatchValidationGuardsTest.php b/tests/BatchValidationGuardsTest.php index c95fd53..1a50b5e 100644 --- a/tests/BatchValidationGuardsTest.php +++ b/tests/BatchValidationGuardsTest.php @@ -61,11 +61,6 @@ public function rules(): array { return self::$testRules; } - - public function authorize(): bool - { - return true; - } }; $formRequest::$testRules = $rules; @@ -983,11 +978,6 @@ public function rules(): array ]; } - public function authorize(): bool - { - return true; - } - protected function failedValidation(Validator $validator): void { // Failed-validation hook fires on standard ValidationException only. diff --git a/tests/Fixtures/AuthorizedEachFluentFormRequest.php b/tests/Fixtures/AuthorizedEachFluentFormRequest.php index 0478a25..5cb29f5 100644 --- a/tests/Fixtures/AuthorizedEachFluentFormRequest.php +++ b/tests/Fixtures/AuthorizedEachFluentFormRequest.php @@ -16,19 +16,11 @@ */ final class AuthorizedEachFluentFormRequest extends FluentFormRequest { - public function authorize(): bool - { - return true; - } - /** @return array */ public function rules(): array { return [ - 'items' => FluentRule::array()->required()->each([ - 'name' => FluentRule::string('Name')->required()->max(255), - 'qty' => FluentRule::numeric('Quantity')->required()->integer()->min(1), - ]), + 'items' => FluentRule::array()->required()->each(['name' => FluentRule::string('Name')->required()->max(255), 'qty' => FluentRule::numeric('Quantity')->required()->integer()->min(1)]), ]; } } diff --git a/tests/Fixtures/BailMaxEachFluentFormRequest.php b/tests/Fixtures/BailMaxEachFluentFormRequest.php index 8f8ccd1..14df1ca 100644 --- a/tests/Fixtures/BailMaxEachFluentFormRequest.php +++ b/tests/Fixtures/BailMaxEachFluentFormRequest.php @@ -16,19 +16,11 @@ */ final class BailMaxEachFluentFormRequest extends FluentFormRequest { - public function authorize(): bool - { - return true; - } - /** @return array */ public function rules(): array { return [ - 'actions' => FluentRule::array()->bail()->required()->max(50)->each([ - 'action' => FluentRule::string()->bail()->required()->in(['bookmark', 'favorite', 'rate']), - 'article_id' => FluentRule::integer()->bail()->required(), - ]), + 'actions' => FluentRule::array()->bail()->required()->max(50)->each(['action' => FluentRule::string()->bail()->required()->in(['bookmark', 'favorite', 'rate']), 'article_id' => FluentRule::integer()->bail()->required()]), ]; } } diff --git a/tests/Fixtures/BailMaxExistsFluentFormRequest.php b/tests/Fixtures/BailMaxExistsFluentFormRequest.php index 012701d..e808af1 100644 --- a/tests/Fixtures/BailMaxExistsFluentFormRequest.php +++ b/tests/Fixtures/BailMaxExistsFluentFormRequest.php @@ -19,18 +19,11 @@ */ final class BailMaxExistsFluentFormRequest extends FluentFormRequest { - public function authorize(): bool - { - return true; - } - /** @return array */ public function rules(): array { return [ - 'actions' => FluentRule::array()->bail()->required()->max(5)->each([ - 'id' => FluentRule::integer()->bail()->required()->exists('testing.widgets', 'id'), - ]), + 'actions' => FluentRule::array()->bail()->required()->max(5)->each(['id' => FluentRule::integer()->bail()->required()->exists('testing.widgets', 'id')]), ]; } } diff --git a/tests/Fixtures/MergeRulesBaseRequest.php b/tests/Fixtures/MergeRulesBaseRequest.php index 414a31e..93a623f 100644 --- a/tests/Fixtures/MergeRulesBaseRequest.php +++ b/tests/Fixtures/MergeRulesBaseRequest.php @@ -25,9 +25,4 @@ public function rules(): array 'base_only' => FluentRule::string()->required(), ]; } - - public function authorize(): bool - { - return true; - } } diff --git a/tests/Fixtures/MergeSchemaBaseRequest.php b/tests/Fixtures/MergeSchemaBaseRequest.php index 34b7c24..6f3c04b 100644 --- a/tests/Fixtures/MergeSchemaBaseRequest.php +++ b/tests/Fixtures/MergeSchemaBaseRequest.php @@ -26,9 +26,4 @@ public function schema(FluentSchema $rules): array 'base_only' => $rules->string()->required(), ]; } - - public function authorize(): bool - { - return true; - } } diff --git a/tests/FluentFormRequestTest.php b/tests/FluentFormRequestTest.php index e6aae0f..bb41d4e 100644 --- a/tests/FluentFormRequestTest.php +++ b/tests/FluentFormRequestTest.php @@ -1270,11 +1270,6 @@ public function rules(): array 'email' => FluentRule::email()->required(), ]; } - - public function authorize(): bool - { - return true; - } }; $request = Request::create('/test', 'POST', []); @@ -1324,11 +1319,6 @@ public function rules(): RuleSet { return self::$testRuleSet; } - - public function authorize(): bool - { - return true; - } }; $formRequest::$testRuleSet = RuleSet::from([ @@ -1368,11 +1358,6 @@ public function rules(): RuleSet ->field('name', FluentRule::string()->required()) ->merge(['email' => FluentRule::email()->required()]); } - - public function authorize(): bool - { - return true; - } }; $request = Request::create('/test', 'POST', ['name' => 'Ada', 'email' => 'a@b.test']); @@ -1402,11 +1387,6 @@ public function rules(): array { return self::$testRules; } - - public function authorize(): bool - { - return true; - } }; $formRequest::$testRules = $rules; diff --git a/tests/FluentSchemaTest.php b/tests/FluentSchemaTest.php index 4948424..b5dc515 100644 --- a/tests/FluentSchemaTest.php +++ b/tests/FluentSchemaTest.php @@ -55,11 +55,6 @@ public function rules(): array { return ['value' => FluentRule::string()->required()->in(['from-rules'])]; } - - public function authorize(): bool - { - return true; - } }; return bootFormRequest($formRequest, $data); @@ -313,11 +308,6 @@ public function rules(): array { return ['value' => FluentRule::string()->required()->in(['from-rules'])]; } - - public function authorize(): bool - { - return true; - } }; $request = bootFormRequest($formRequest, ['value' => 'from-rules']); @@ -340,11 +330,6 @@ public function schema(FluentSchema $rules): array 'author' => $rules->string()->required(), ]; } - - public function authorize(): bool - { - return true; - } }; expect(bootFormRequest($request, ['title' => 'hello', 'author' => 'me'])->rules()) @@ -360,11 +345,6 @@ public function schema(FluentSchema $rules): RuleSet { return RuleSet::from(['title' => $rules->string()->required()]); } - - public function authorize(): bool - { - return true; - } }; expect(bootFormRequest($request, ['title' => 'x'])->rules())->toBeInstanceOf(RuleSet::class); @@ -382,11 +362,6 @@ public function schema(FluentSchema $rules): array { return ['title' => $rules->string()->required()]; } - - public function authorize(): bool - { - return true; - } }; expect($request->rules())->toHaveKey('title'); diff --git a/tests/Pest.php b/tests/Pest.php index cb308d8..7e43444 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -48,11 +48,6 @@ public function rules(): array { return self::$testRules; } - - public function authorize(): bool - { - return true; - } }; $formRequest::$testRules = $rules; @@ -81,11 +76,6 @@ public function schema(FluentSchema $rules): array { return (self::$testSchema)($rules); } - - public function authorize(): bool - { - return true; - } }; $formRequest::$testSchema = $schema; diff --git a/tests/Rules/Markup/XmlTest.php b/tests/Rules/Markup/XmlTest.php index b7cacbd..55374f0 100644 --- a/tests/Rules/Markup/XmlTest.php +++ b/tests/Rules/Markup/XmlTest.php @@ -2,6 +2,7 @@ declare(strict_types=1); +use Illuminate\Support\Facades\File; use Illuminate\Contracts\Translation\Translator; use Simtabi\Laranail\Validation\Rules\Markup\Xml; use Illuminate\Translation\PotentiallyTranslatedString; @@ -69,7 +70,7 @@ ->and(ruleAccepts($rule, 'ten'))->toBeFalse() ->and(ruleAccepts($rule, '10.50'))->toBeFalse(); - @unlink($schema); + File::delete($schema); }); it('blames the deployment, not the document, when the schema is missing', function (): void { diff --git a/tests/SchemaRulesMergeTest.php b/tests/SchemaRulesMergeTest.php index 3fe0bda..427c5c3 100644 --- a/tests/SchemaRulesMergeTest.php +++ b/tests/SchemaRulesMergeTest.php @@ -66,11 +66,6 @@ public function rules(): array 'body_only' => FluentRule::string()->required(), ]; } - - public function authorize(): bool - { - return true; - } }; } @@ -175,11 +170,6 @@ public function rules(): RuleSet { return RuleSet::from(['from_rules' => FluentRule::string()->required()]); } - - public function authorize(): bool - { - return true; - } }; $booted = bootFormRequest($request, ['from_schema' => 'a', 'from_rules' => 'b']); From bb998cef67e1f4977e7baf89917004621f44e6e8 Mon Sep 17 00:00:00 2001 From: Imani Manyara Date: Mon, 26 Oct 2026 10:17:47 -0400 Subject: [PATCH 3/6] Revert "chore: rector" This reverts commit b0a62743466e3f81acc21f08855f7855030a49b4. --- src/Commands/DoctorCommand.php | 2 +- src/Exceptions/TypedBuilderHint.php | 4 +-- tests/BatchDatabaseValidationTest.php | 5 ++++ tests/BatchValidationGuardsTest.php | 10 ++++++++ .../AuthorizedEachFluentFormRequest.php | 10 +++++++- .../Fixtures/BailMaxEachFluentFormRequest.php | 10 +++++++- .../BailMaxExistsFluentFormRequest.php | 9 ++++++- tests/Fixtures/MergeRulesBaseRequest.php | 5 ++++ tests/Fixtures/MergeSchemaBaseRequest.php | 5 ++++ tests/FluentFormRequestTest.php | 20 +++++++++++++++ tests/FluentSchemaTest.php | 25 +++++++++++++++++++ tests/Pest.php | 10 ++++++++ tests/Rules/Markup/XmlTest.php | 3 +-- tests/SchemaRulesMergeTest.php | 10 ++++++++ 14 files changed, 120 insertions(+), 8 deletions(-) diff --git a/src/Commands/DoctorCommand.php b/src/Commands/DoctorCommand.php index b93f4e8..18f1771 100644 --- a/src/Commands/DoctorCommand.php +++ b/src/Commands/DoctorCommand.php @@ -40,7 +40,7 @@ public function handle(RuleRegistrar $registrar): int return is_array($config) && $config !== [] ? ['OK', 'flat laranail.validation key present'] - : ['FAIL', "config('laranail.validation') is empty — provider not registered?"]; + : ['FAIL', 'config(\'laranail.validation\') is empty — provider not registered?']; }], ['rule registry', function () use ($registrar): array { $count = count($registrar->classes()); diff --git a/src/Exceptions/TypedBuilderHint.php b/src/Exceptions/TypedBuilderHint.php index 88dc9bc..4f72b49 100644 --- a/src/Exceptions/TypedBuilderHint.php +++ b/src/Exceptions/TypedBuilderHint.php @@ -113,9 +113,9 @@ public static function for(string $method): ?string private static function specialCase(string $method): ?string { return match ($method) { - 'accepted' => "Use `FluentRule::accepted()` for permissive accepted values (`'yes'`, `'on'`, `'1'`, `true`). Avoid `FluentRule::boolean()->accepted()` — the `boolean` base rule rejects `'yes'`/`'on'`.", + 'accepted' => 'Use `FluentRule::accepted()` for permissive accepted values (`\'yes\'`, `\'on\'`, `\'1\'`, `true`). Avoid `FluentRule::boolean()->accepted()` — the `boolean` base rule rejects `\'yes\'`/`\'on\'`.', 'declined' => "Use `FluentRule::boolean()->declined(...)` for strict boolean-only input. If HTML form values (`'no'`, `'off'`) need to pass, use `->rule('declined')` on the untyped builder instead.", - 'size' => "No `size()` method — use `->exactly(...)` on a typed builder. (This package renames Laravel's `size:` rule to `exactly()` for clarity.)", + 'size' => 'No `size()` method — use `->exactly(...)` on a typed builder. (This package renames Laravel\'s `size:` rule to `exactly()` for clarity.)', 'gt', 'gte', 'lt', 'lte' => 'No `gt()`/`lt()` methods — use `FluentRule::numeric()->greaterThan(FIELD)`, `->greaterThanOrEqualTo(FIELD)`, `->lessThan(FIELD)`, `->lessThanOrEqualTo(FIELD)`.', 'alphaNum' => 'No `alphaNum()` method — use `FluentRule::string()->alphaNumeric(...)`.', 'contains' => 'Use `FluentRule::array()->contains(...)` (this package exposes `contains` on `array()`, not `string()`).', diff --git a/tests/BatchDatabaseValidationTest.php b/tests/BatchDatabaseValidationTest.php index 1baa970..a7248bb 100644 --- a/tests/BatchDatabaseValidationTest.php +++ b/tests/BatchDatabaseValidationTest.php @@ -804,6 +804,11 @@ public function rules(): array { return self::$testRules; } + + public function authorize(): bool + { + return true; + } }; $formRequest::$testRules = $rules; diff --git a/tests/BatchValidationGuardsTest.php b/tests/BatchValidationGuardsTest.php index 1a50b5e..c95fd53 100644 --- a/tests/BatchValidationGuardsTest.php +++ b/tests/BatchValidationGuardsTest.php @@ -61,6 +61,11 @@ public function rules(): array { return self::$testRules; } + + public function authorize(): bool + { + return true; + } }; $formRequest::$testRules = $rules; @@ -978,6 +983,11 @@ public function rules(): array ]; } + public function authorize(): bool + { + return true; + } + protected function failedValidation(Validator $validator): void { // Failed-validation hook fires on standard ValidationException only. diff --git a/tests/Fixtures/AuthorizedEachFluentFormRequest.php b/tests/Fixtures/AuthorizedEachFluentFormRequest.php index 5cb29f5..0478a25 100644 --- a/tests/Fixtures/AuthorizedEachFluentFormRequest.php +++ b/tests/Fixtures/AuthorizedEachFluentFormRequest.php @@ -16,11 +16,19 @@ */ final class AuthorizedEachFluentFormRequest extends FluentFormRequest { + public function authorize(): bool + { + return true; + } + /** @return array */ public function rules(): array { return [ - 'items' => FluentRule::array()->required()->each(['name' => FluentRule::string('Name')->required()->max(255), 'qty' => FluentRule::numeric('Quantity')->required()->integer()->min(1)]), + 'items' => FluentRule::array()->required()->each([ + 'name' => FluentRule::string('Name')->required()->max(255), + 'qty' => FluentRule::numeric('Quantity')->required()->integer()->min(1), + ]), ]; } } diff --git a/tests/Fixtures/BailMaxEachFluentFormRequest.php b/tests/Fixtures/BailMaxEachFluentFormRequest.php index 14df1ca..8f8ccd1 100644 --- a/tests/Fixtures/BailMaxEachFluentFormRequest.php +++ b/tests/Fixtures/BailMaxEachFluentFormRequest.php @@ -16,11 +16,19 @@ */ final class BailMaxEachFluentFormRequest extends FluentFormRequest { + public function authorize(): bool + { + return true; + } + /** @return array */ public function rules(): array { return [ - 'actions' => FluentRule::array()->bail()->required()->max(50)->each(['action' => FluentRule::string()->bail()->required()->in(['bookmark', 'favorite', 'rate']), 'article_id' => FluentRule::integer()->bail()->required()]), + 'actions' => FluentRule::array()->bail()->required()->max(50)->each([ + 'action' => FluentRule::string()->bail()->required()->in(['bookmark', 'favorite', 'rate']), + 'article_id' => FluentRule::integer()->bail()->required(), + ]), ]; } } diff --git a/tests/Fixtures/BailMaxExistsFluentFormRequest.php b/tests/Fixtures/BailMaxExistsFluentFormRequest.php index e808af1..012701d 100644 --- a/tests/Fixtures/BailMaxExistsFluentFormRequest.php +++ b/tests/Fixtures/BailMaxExistsFluentFormRequest.php @@ -19,11 +19,18 @@ */ final class BailMaxExistsFluentFormRequest extends FluentFormRequest { + public function authorize(): bool + { + return true; + } + /** @return array */ public function rules(): array { return [ - 'actions' => FluentRule::array()->bail()->required()->max(5)->each(['id' => FluentRule::integer()->bail()->required()->exists('testing.widgets', 'id')]), + 'actions' => FluentRule::array()->bail()->required()->max(5)->each([ + 'id' => FluentRule::integer()->bail()->required()->exists('testing.widgets', 'id'), + ]), ]; } } diff --git a/tests/Fixtures/MergeRulesBaseRequest.php b/tests/Fixtures/MergeRulesBaseRequest.php index 93a623f..414a31e 100644 --- a/tests/Fixtures/MergeRulesBaseRequest.php +++ b/tests/Fixtures/MergeRulesBaseRequest.php @@ -25,4 +25,9 @@ public function rules(): array 'base_only' => FluentRule::string()->required(), ]; } + + public function authorize(): bool + { + return true; + } } diff --git a/tests/Fixtures/MergeSchemaBaseRequest.php b/tests/Fixtures/MergeSchemaBaseRequest.php index 6f3c04b..34b7c24 100644 --- a/tests/Fixtures/MergeSchemaBaseRequest.php +++ b/tests/Fixtures/MergeSchemaBaseRequest.php @@ -26,4 +26,9 @@ public function schema(FluentSchema $rules): array 'base_only' => $rules->string()->required(), ]; } + + public function authorize(): bool + { + return true; + } } diff --git a/tests/FluentFormRequestTest.php b/tests/FluentFormRequestTest.php index bb41d4e..e6aae0f 100644 --- a/tests/FluentFormRequestTest.php +++ b/tests/FluentFormRequestTest.php @@ -1270,6 +1270,11 @@ public function rules(): array 'email' => FluentRule::email()->required(), ]; } + + public function authorize(): bool + { + return true; + } }; $request = Request::create('/test', 'POST', []); @@ -1319,6 +1324,11 @@ public function rules(): RuleSet { return self::$testRuleSet; } + + public function authorize(): bool + { + return true; + } }; $formRequest::$testRuleSet = RuleSet::from([ @@ -1358,6 +1368,11 @@ public function rules(): RuleSet ->field('name', FluentRule::string()->required()) ->merge(['email' => FluentRule::email()->required()]); } + + public function authorize(): bool + { + return true; + } }; $request = Request::create('/test', 'POST', ['name' => 'Ada', 'email' => 'a@b.test']); @@ -1387,6 +1402,11 @@ public function rules(): array { return self::$testRules; } + + public function authorize(): bool + { + return true; + } }; $formRequest::$testRules = $rules; diff --git a/tests/FluentSchemaTest.php b/tests/FluentSchemaTest.php index b5dc515..4948424 100644 --- a/tests/FluentSchemaTest.php +++ b/tests/FluentSchemaTest.php @@ -55,6 +55,11 @@ public function rules(): array { return ['value' => FluentRule::string()->required()->in(['from-rules'])]; } + + public function authorize(): bool + { + return true; + } }; return bootFormRequest($formRequest, $data); @@ -308,6 +313,11 @@ public function rules(): array { return ['value' => FluentRule::string()->required()->in(['from-rules'])]; } + + public function authorize(): bool + { + return true; + } }; $request = bootFormRequest($formRequest, ['value' => 'from-rules']); @@ -330,6 +340,11 @@ public function schema(FluentSchema $rules): array 'author' => $rules->string()->required(), ]; } + + public function authorize(): bool + { + return true; + } }; expect(bootFormRequest($request, ['title' => 'hello', 'author' => 'me'])->rules()) @@ -345,6 +360,11 @@ public function schema(FluentSchema $rules): RuleSet { return RuleSet::from(['title' => $rules->string()->required()]); } + + public function authorize(): bool + { + return true; + } }; expect(bootFormRequest($request, ['title' => 'x'])->rules())->toBeInstanceOf(RuleSet::class); @@ -362,6 +382,11 @@ public function schema(FluentSchema $rules): array { return ['title' => $rules->string()->required()]; } + + public function authorize(): bool + { + return true; + } }; expect($request->rules())->toHaveKey('title'); diff --git a/tests/Pest.php b/tests/Pest.php index 7e43444..cb308d8 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -48,6 +48,11 @@ public function rules(): array { return self::$testRules; } + + public function authorize(): bool + { + return true; + } }; $formRequest::$testRules = $rules; @@ -76,6 +81,11 @@ public function schema(FluentSchema $rules): array { return (self::$testSchema)($rules); } + + public function authorize(): bool + { + return true; + } }; $formRequest::$testSchema = $schema; diff --git a/tests/Rules/Markup/XmlTest.php b/tests/Rules/Markup/XmlTest.php index 55374f0..b7cacbd 100644 --- a/tests/Rules/Markup/XmlTest.php +++ b/tests/Rules/Markup/XmlTest.php @@ -2,7 +2,6 @@ declare(strict_types=1); -use Illuminate\Support\Facades\File; use Illuminate\Contracts\Translation\Translator; use Simtabi\Laranail\Validation\Rules\Markup\Xml; use Illuminate\Translation\PotentiallyTranslatedString; @@ -70,7 +69,7 @@ ->and(ruleAccepts($rule, 'ten'))->toBeFalse() ->and(ruleAccepts($rule, '10.50'))->toBeFalse(); - File::delete($schema); + @unlink($schema); }); it('blames the deployment, not the document, when the schema is missing', function (): void { diff --git a/tests/SchemaRulesMergeTest.php b/tests/SchemaRulesMergeTest.php index 427c5c3..3fe0bda 100644 --- a/tests/SchemaRulesMergeTest.php +++ b/tests/SchemaRulesMergeTest.php @@ -66,6 +66,11 @@ public function rules(): array 'body_only' => FluentRule::string()->required(), ]; } + + public function authorize(): bool + { + return true; + } }; } @@ -170,6 +175,11 @@ public function rules(): RuleSet { return RuleSet::from(['from_rules' => FluentRule::string()->required()]); } + + public function authorize(): bool + { + return true; + } }; $booted = bootFormRequest($request, ['from_schema' => 'a', 'from_rules' => 'b']); From f111ce1b30e280a343d424efbb11ee4915d7e034 Mon Sep 17 00:00:00 2001 From: Imani Manyara Date: Mon, 26 Oct 2026 10:17:47 -0400 Subject: [PATCH 4/6] Skip rector-laravel's RemoveDefaultAuthorizeTrueFromFormRequestRector rector-laravel 2.7 deletes an authorize() that returns true. The test fixtures define it on purpose, to cover the path where the method exists, so the Rector job's auto-commit removed that coverage from 13 test files. Reverted and skipped. --- rector.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/rector.php b/rector.php index 008a7b7..a1af80e 100644 --- a/rector.php +++ b/rector.php @@ -5,6 +5,7 @@ use Rector\Config\RectorConfig; use RectorPest\Set\PestSetList; use RectorLaravel\Set\LaravelSetList; +use RectorLaravel\Rector\Class_\RemoveDefaultAuthorizeTrueFromFormRequestRector; use RectorPest\Rules\UseToMatchRector; use Rector\Caching\ValueObject\Storage\FileCacheStorage; use Rector\CodeQuality\Rector\If_\ExplicitBoolCompareRector; @@ -81,6 +82,11 @@ // links without changing their subject — both produce failing // (or nonsensical) assertions. mrpunyapal/rector-pest 0.2.17. UseToMatchRector::class, + // rector-laravel 2.7 deletes an authorize() that returns true. Laravel + // treats a missing authorize() as authorized, so src behaves the same, + // but the test fixtures define it on purpose (AuthorizedEach..., + // MergeRulesBaseRequest) to cover the path where the method exists. + RemoveDefaultAuthorizeTrueFromFormRequestRector::class, // Hot-path closure allocates a literal array on every invocation // when in_array() is used. Explicit === comparisons avoid that. RepeatedOrEqualToInArrayRector::class => [ From da4bac4b224a2f9b28105d3fb40eeaaa92f87136 Mon Sep 17 00:00:00 2001 From: imanimanyara <19682005+imanimanyara@users.noreply.github.com> Date: Mon, 26 Oct 2026 14:18:26 +0000 Subject: [PATCH 5/6] chore: rector --- src/Commands/DoctorCommand.php | 2 +- src/Exceptions/TypedBuilderHint.php | 4 ++-- tests/Rules/Markup/XmlTest.php | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Commands/DoctorCommand.php b/src/Commands/DoctorCommand.php index 18f1771..b93f4e8 100644 --- a/src/Commands/DoctorCommand.php +++ b/src/Commands/DoctorCommand.php @@ -40,7 +40,7 @@ public function handle(RuleRegistrar $registrar): int return is_array($config) && $config !== [] ? ['OK', 'flat laranail.validation key present'] - : ['FAIL', 'config(\'laranail.validation\') is empty — provider not registered?']; + : ['FAIL', "config('laranail.validation') is empty — provider not registered?"]; }], ['rule registry', function () use ($registrar): array { $count = count($registrar->classes()); diff --git a/src/Exceptions/TypedBuilderHint.php b/src/Exceptions/TypedBuilderHint.php index 4f72b49..88dc9bc 100644 --- a/src/Exceptions/TypedBuilderHint.php +++ b/src/Exceptions/TypedBuilderHint.php @@ -113,9 +113,9 @@ public static function for(string $method): ?string private static function specialCase(string $method): ?string { return match ($method) { - 'accepted' => 'Use `FluentRule::accepted()` for permissive accepted values (`\'yes\'`, `\'on\'`, `\'1\'`, `true`). Avoid `FluentRule::boolean()->accepted()` — the `boolean` base rule rejects `\'yes\'`/`\'on\'`.', + 'accepted' => "Use `FluentRule::accepted()` for permissive accepted values (`'yes'`, `'on'`, `'1'`, `true`). Avoid `FluentRule::boolean()->accepted()` — the `boolean` base rule rejects `'yes'`/`'on'`.", 'declined' => "Use `FluentRule::boolean()->declined(...)` for strict boolean-only input. If HTML form values (`'no'`, `'off'`) need to pass, use `->rule('declined')` on the untyped builder instead.", - 'size' => 'No `size()` method — use `->exactly(...)` on a typed builder. (This package renames Laravel\'s `size:` rule to `exactly()` for clarity.)', + 'size' => "No `size()` method — use `->exactly(...)` on a typed builder. (This package renames Laravel's `size:` rule to `exactly()` for clarity.)", 'gt', 'gte', 'lt', 'lte' => 'No `gt()`/`lt()` methods — use `FluentRule::numeric()->greaterThan(FIELD)`, `->greaterThanOrEqualTo(FIELD)`, `->lessThan(FIELD)`, `->lessThanOrEqualTo(FIELD)`.', 'alphaNum' => 'No `alphaNum()` method — use `FluentRule::string()->alphaNumeric(...)`.', 'contains' => 'Use `FluentRule::array()->contains(...)` (this package exposes `contains` on `array()`, not `string()`).', diff --git a/tests/Rules/Markup/XmlTest.php b/tests/Rules/Markup/XmlTest.php index b7cacbd..55374f0 100644 --- a/tests/Rules/Markup/XmlTest.php +++ b/tests/Rules/Markup/XmlTest.php @@ -2,6 +2,7 @@ declare(strict_types=1); +use Illuminate\Support\Facades\File; use Illuminate\Contracts\Translation\Translator; use Simtabi\Laranail\Validation\Rules\Markup\Xml; use Illuminate\Translation\PotentiallyTranslatedString; @@ -69,7 +70,7 @@ ->and(ruleAccepts($rule, 'ten'))->toBeFalse() ->and(ruleAccepts($rule, '10.50'))->toBeFalse(); - @unlink($schema); + File::delete($schema); }); it('blames the deployment, not the document, when the schema is missing', function (): void { From f09338cc5fa2a8dc338463c6ec84f44f5f6db4b7 Mon Sep 17 00:00:00 2001 From: imanimanyara <19682005+imanimanyara@users.noreply.github.com> Date: Mon, 26 Oct 2026 14:18:42 +0000 Subject: [PATCH 6/6] chore: pint --- rector.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rector.php b/rector.php index a1af80e..b77ab92 100644 --- a/rector.php +++ b/rector.php @@ -5,7 +5,6 @@ use Rector\Config\RectorConfig; use RectorPest\Set\PestSetList; use RectorLaravel\Set\LaravelSetList; -use RectorLaravel\Rector\Class_\RemoveDefaultAuthorizeTrueFromFormRequestRector; use RectorPest\Rules\UseToMatchRector; use Rector\Caching\ValueObject\Storage\FileCacheStorage; use Rector\CodeQuality\Rector\If_\ExplicitBoolCompareRector; @@ -18,6 +17,7 @@ use Rector\CodeQuality\Rector\BooleanOr\RepeatedOrEqualToInArrayRector; use Rector\CodeQuality\Rector\ClassMethod\InlineArrayReturnAssignRector; use Rector\Privatization\Rector\ClassMethod\PrivatizeFinalClassMethodRector; +use RectorLaravel\Rector\Class_\RemoveDefaultAuthorizeTrueFromFormRequestRector; use Rector\TypeDeclaration\Rector\ArrowFunction\AddArrowFunctionReturnTypeRector; return RectorConfig::configure()