Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Ruleset Validation Laravel Package

craftcms/laravel-ruleset-validation

Validate Laravel request data against Craft CMS field rulesets. Map Craft-style constraints (required, min/max, regex, etc.) into Laravel’s validator, keeping validation logic consistent between Craft and Laravel apps.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require craftcms/laravel-ruleset-validation
    

    No service provider registration needed (auto-discovery).

  2. First Use Case: Validate a DTO

    • Create a DTO implementing ValidatesWithRuleset:
      use CraftCms\RulesetValidation\Concerns\HasRuleset;
      use CraftCms\RulesetValidation\Contracts\ValidatesWithRuleset;
      use CraftCms\RulesetValidation\Attributes\Ruleset;
      
      #[Ruleset(CreateUserRuleset::class)]
      class CreateUserData implements ValidatesWithRuleset {
          use HasRuleset;
      
          public function __construct(
              public string $name,
              public string $email,
          ) {}
      
          public function validationData(): array {
              return [
                  'name' => $this->name,
                  'email' => $this->email,
              ];
          }
      }
      
    • Define the ruleset:
      use CraftCms\RulesetValidation\Ruleset;
      
      class CreateUserRuleset extends Ruleset {
          public function rules(): array {
              return [
                  'name' => ['required', 'string', 'max:255'],
                  'email' => ['required', 'email'],
              ];
          }
      }
      
    • Validate in a controller:
      $data = new CreateUserData('John', 'john@example.com');
      $validated = $data->ruleset->validate();
      
  3. First Use Case: Validate a Request

    • Inject a ruleset directly into a controller:
      use App\Rulesets\StoreUserRuleset;
      
      class UserController {
          public function store(StoreUserRuleset $ruleset) {
              $validated = $ruleset->validate();
              // ...
          }
      }
      

Where to Look First

  • Documentation: Laravel Form Request Validation (mirrored behavior).
  • Package Source: Focus on:
    • Ruleset class (core logic).
    • HasRuleset trait (for object-backed validation).
    • #[Ruleset] attribute (declarative ruleset binding).
  • Examples: Check the GitHub repo for real-world usage patterns.

Implementation Patterns

Core Workflows

1. Object-Backed Validation (DTOs, Domain Objects)

  • Pattern: Use ValidatesWithRuleset + #[Ruleset] for reusable validation logic tied to business objects.
  • Example:
    // Define ruleset
    class UpdateProfileRuleset extends Ruleset {
        public function rules(): array {
            return [
                'name' => ['sometimes', 'string', 'max:255'],
                'email' => ['sometimes', 'email'],
            ];
        }
    }
    
    // Attach to DTO
    #[Ruleset(UpdateProfileRuleset::class)]
    class UpdateProfileData implements ValidatesWithRuleset {
        use HasRuleset;
    
        public function __construct(
            public ?string $name,
            public ?string $email,
        ) {}
    
        public function validationData(): array {
            return [
                'name' => $this->name,
                'email' => $this->email,
            ];
        }
    }
    
    // Validate
    $data = new UpdateProfileData('Jane', 'jane@example.com');
    $validated = $data->ruleset->validate();
    
  • When to Use: When validation logic is tightly coupled to a domain object (e.g., User, Order, Post).

2. Request-Backed Validation (Replacing FormRequest)

  • Pattern: Inject rulesets directly into controllers to avoid FormRequest boilerplate.
  • Example:
    use App\Rulesets\CreatePostRuleset;
    
    class PostController {
        public function store(CreatePostRuleset $ruleset) {
            $validated = $ruleset->validate();
            // ...
        }
    }
    
  • When to Use: For APIs or controllers where you want validation without creating a dedicated FormRequest class.

3. Dynamic Rules with Scenarios

  • Pattern: Use scenarios to conditionally apply rules (e.g., "admin vs. user workflows").
  • Example:
    class PostRuleset extends Ruleset {
        public const SCENARIO_PUBLIC = 'public';
        public const SCENARIO_ADMIN = 'admin';
    
        public function rules(): array {
            return [
                'title' => [
                    Rule::requiredIf($this->inScenarios(self::SCENARIO_ADMIN)),
                    'string',
                    'max:255',
                ],
            ];
        }
    }
    
    // Usage
    $ruleset = new PostRuleset(subject: $request);
    $validated = $ruleset->useScenario(PostRuleset::SCENARIO_ADMIN)->validate();
    
  • When to Use: When validation rules change based on context (e.g., user roles, feature flags).

4. Nested/Composite Validation

  • Pattern: Combine rulesets for complex validation (e.g., "validate user + payment").
  • Example:
    class UserRuleset extends Ruleset {
        public function rules(): array { /* ... */ }
    }
    
    class PaymentRuleset extends Ruleset {
        public function rules(): array { /* ... */ }
    }
    
    class CheckoutRuleset extends Ruleset {
        public function rules(): array {
            return [
                'user' => [new UserRuleset],
                'payment' => [new PaymentRuleset],
            ];
        }
    }
    
  • When to Use: For multi-step forms or workflows with independent validation blocks.

Integration Tips

  1. Leverage Laravel’s Validation Pipeline:

    • Rulesets support all FormRequest features: authorize(), messages(), attributes(), prepareForValidation(), etc.
    • Example:
      class StorePostRuleset extends Ruleset {
          public function authorize(): bool {
              return auth()->check();
          }
      
          public function messages(): array {
              return [
                  'title.required' => 'A title is required!',
              ];
          }
      }
      
  2. Use with API Resources:

    • Validate API payloads before transforming them into resources:
      $data = new CreatePostData($request->all());
      $validated = $data->ruleset->validate();
      $post = new PostResource($validated);
      
  3. Combine with Form Requests:

    • Use rulesets for shared logic and FormRequest for request-specific behavior:
      class StorePostRequest extends FormRequest {
          public function rules(): array {
              return (new StorePostRuleset($this))->rules();
          }
      }
      
  4. Testing:

    • Mock rulesets in unit tests:
      $ruleset = $this->createMock(Ruleset::class);
      $ruleset->method('validate')->willReturn(['validated' => 'data']);
      $this->assertEquals(['validated' => 'data'], $data->ruleset->validate());
      
    • Test scenarios:
      $ruleset->useScenario(PostRuleset::SCENARIO_ADMIN);
      $this->assertTrue($ruleset->inScenarios(PostRuleset::SCENARIO_ADMIN));
      
  5. Performance:

    • Reuse ruleset instances where possible (e.g., container-bound rulesets).
    • Avoid recreating rulesets in loops (cache or reuse).

Gotchas and Tips

Pitfalls

  1. Ruleset Resolution Conflicts:

    • If a class uses both #[Ruleset] and a ruleset() method, the attribute takes precedence. Override in ruleset() if dynamic resolution is needed:
      public function ruleset(): string {
          return $this->isAdmin ? AdminRuleset::class : UserRuleset::class;
      }
      
  2. Scenario State Leaks:

    • useScenario() is sticky. Reset with withScenario() for one-off operations:
      $validated = $ruleset->withScenario(
          PostRuleset::SCENARIO_DRAFT,
          fn () => $ruleset->validate()
      );
      
  3. Request vs. Object Validation Quirks:

    • For request-backed rulesets, $this->input is available in rules(), but for objects, use validationData().
    • Example of mixing both:
      public function rules(): array {
          return [
              'email' => ['required', 'email'],
              'user_id' => Rule::exists('users')->where('active', true),
          ];
      }
      
  4. Serialization Issues:

    • Rulesets are serializable, but avoid serializing rulesets with closures or non-serializable dependencies (e.g., database connections).
  5. Attribute Caching:

    • The #[Ruleset] attribute is resolved once during class initialization. For dynamic rulesets, use
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
andydefer/laravel-task
helgesverre/toon
symfony/ai-mate
f1monkey/eve-esi-bundle
f-froehlich/symfony-validator
f-froehlich/api
ezsystems/templated-uri-bundle
ezsystems/stash-bundle
ezsystems/share-buttons-bundle
ezsystems/privacy-cookie-bundle
ezsystems/payment-paypal-bundle
ezsystems/payment-core-bundle
ezsystems/job-queue-bundle
ezsystems/hybrid-platform-ui
ezsystems/ezmigrationbundle
ezsystems/ezcommerce-econtent-installer
ezsystems/comment-bundle
ezsystems/apache-tika-bundle
ezar101/easyadmin-trix-extension-bundle
eyerim/oauth2-azure-bundle