Yeashy Compliance is an elegant pipeline-based validation system for Laravel, designed to extend FormRequest validation with domain-driven, reusable, and expressive rule objects.
- ✅ Seamless integration with Laravel
FormRequest - 🧩 Pipeline-based rule processing
- 🎯 HTTP-method-specific rule handling (
GET,POST, etc.) - 🔁 Composable and testable rule classes
- 📦 Fully compatible with standard Laravel validation
Install the package via Composer:
composer require yeashy/complianceNo additional service provider registration is required.
To use Compliance in your requests, simply extend the base ComplianceRequest instead of Laravel’s FormRequest.
A compliance rule is a class that extends Yeashy\Compliance\Rules\ComplianceRule and implements the validate() method.
use Yeashy\Compliance\Rules\ComplianceRule;
class EmailIsNotBlocked extends ComplianceRule
{
protected string $key = 'email';
protected string $message = 'This email address is blocked.';
protected function validate(): void
{
if ($this->data->email === 'blocked@example.com') {
$this->invalidateAndExit(); // Add error and stop the pipeline
}
}
}Extend ComplianceRequest and define the rules in the $compliances property, or specify them per HTTP method ($postCompliances, $getCompliances, etc.).
use Yeashy\Compliance\Requests\ComplianceRequest;
class RegisterRequest extends ComplianceRequest
{
protected array $postCompliances = [
\App\Compliance\Rules\EmailIsNotBlocked::class,
];
public function rules(): array
{
return [
'email' => ['required', 'email'],
'password' => ['required'],
];
}
}That’s it! The request will now go through both Laravel’s standard validation and your custom compliance pipeline.
Each rule receives an object containing:
- All input data from the request
(Request::all()) - Route parameters
The validate() method inside each rule is executed in sequence. You can:
- Use
invalidate($key, $message)to add an error and continue to the next rule. - Use
invalidateAndExit()to add an error and stop the pipeline immediately. - Use
skipNext()to stop the pipeline without adding an error (optional shortcut).
Compliance rules can be defined globally for all methods:
protected array $compliances = [...];`Or scoped to specific HTTP verbs:
protected array $postCompliances = [...];
protected array $getCompliances = [...];
// Supported: get, post, put, patch, delete, head, optionsCompliance errors are automatically merged with Laravel’s validation errors and returned in the standard format.
If a compliance rule sets a custom $statusCode, it will override the default HTTP 422 status.
- Rules should follow the Single Responsibility Principle: one rule — one concern.
- Use meaningful keys (
$key) to ensure correct field mapping in error responses. - Localize messages using
__('...')whenever possible. - Prefer
invalidateAndExit()for critical rules (e.g., credential checks). - Use
skipNext()if the current rule makes the remaining ones irrelevant.
Rule: Ensure the user is not blocked
use Illuminate\Support\Facades\Auth;
use Yeashy\Compliance\Rules\ComplianceRule;
class UserIsNotBlocked extends ComplianceRule
{
protected string $key = 'user';
protected string $message = 'Your account has been blocked.';
public int $statusCode = 403;
protected function validate(): void
{
$user = Auth::user();
if (! $user || $user->is_blocked) {
$this->invalidateAndExit();
}
}
}use Yeashy\Compliance\Requests\ComplianceRequest;
class DashboardRequest extends ComplianceRequest
{
protected array $compliances = [
\App\Compliance\Rules\UserIsNotBlocked::class,
\App\Compliance\Rules\AccountIsVerified::class,
];
protected array $getCompliances = [
\App\Compliance\Rules\SubscriptionIsActive::class,
\App\Compliance\Rules\UserHasAccessToResource::class,
];
public function rules(): array
{
return [
'resource_id' => ['required', 'uuid'],
];
}
}In this example:
$compliancesdefines global rules that apply to all HTTP methods.$getCompliancesdefines additional rules applied only forGETrequests.- All rules are processed in order, and execution stops if a rule uses
invalidateAndExit().
You can test compliance rules in isolation:
public function test_email_is_not_blocked()
{
$rule = new EmailIsNotBlocked();
$data = (object) ['email' => 'blocked@example.com'];
$rule->handle($data, fn($data) => $data);
$this->assertNotEmpty($data->__errorMessages);
}The MIT License (MIT). Please see License File for more information.
Contributions, issues and feature requests are welcome! Feel free to submit a PR or open an issue.