PHPackages                             occ-therapist/form-request-validation-for-filament - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Validation &amp; Sanitization](/categories/validation)
4. /
5. occ-therapist/form-request-validation-for-filament

ActiveLibrary[Validation &amp; Sanitization](/categories/validation)

occ-therapist/form-request-validation-for-filament
==================================================

Use Laravel Form Request validation rules inside Filament schemas.

v1.2.0(1mo ago)31.1k↓40%MITPHPPHP ^8.2

Since Jul 8Pushed 1mo agoCompare

[ Source](https://github.com/OccTherapist/form-request-validation-for-filament)[ Packagist](https://packagist.org/packages/occ-therapist/form-request-validation-for-filament)[ RSS](/packages/occ-therapist-form-request-validation-for-filament/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (6)Dependencies (10)Versions (7)Used By (0)

Form Request Validation for Filament
====================================

[](#form-request-validation-for-filament)

[![Latest Version on Packagist](https://camo.githubusercontent.com/c38d1d672db276e1da61f22647fcdb0f772b12de8a458387a78540af24457f15/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f762f72656c656173652f4f63635468657261706973742f666f726d2d726571756573742d76616c69646174696f6e2d666f722d66696c616d656e743f7374796c653d666c61742d737175617265)](https://packagist.org/packages/occ-therapist/form-request-validation-for-filament)[![Total Downloads](https://camo.githubusercontent.com/980b9378805da954f172d7c0ba6315111ac75ad1b65b73c594bf4e714d672cd2/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6f63632d7468657261706973742f666f726d2d726571756573742d76616c69646174696f6e2d666f722d66696c616d656e743f7374796c653d666c61742d737175617265)](https://packagist.org/packages/occ-therapist/form-request-validation-for-filament)[![License](https://camo.githubusercontent.com/0c57421bbbb6aaea14a4e9f44169289f898893e30f2b9a537c650ebf7294d683/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f4f63635468657261706973742f666f726d2d726571756573742d76616c69646174696f6e2d666f722d66696c616d656e743f7374796c653d666c61742d737175617265)](https://opensource.org/licenses/MIT)[![GitHub stars](https://camo.githubusercontent.com/ad1e76f2d03b5ef0bb3a15aa6f6df6a2245f3267da6939fc61c56ac1c50ff3ad/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f73746172732f4f63635468657261706973742f666f726d2d726571756573742d76616c69646174696f6e2d666f722d66696c616d656e743f7374796c653d666c61742d737175617265)](https://github.com/OccTherapist/form-request-validation-for-filament)

[![Form Request Validation for Filament](thumbnail.jpg)](thumbnail.jpg)

Use Laravel [Form Request](https://laravel.com/docs/validation#form-request-validation) validation inside Filament — define your rules, messages, and attributes once, reuse them across API routes, controllers, and Filament UI.

Why this plugin?
----------------

[](#why-this-plugin)

Filament validates fields through dedicated methods like `->required()` and `->email()` on each input. That works well for simple forms, but validation logic often belongs in a central place — especially when the same rules already exist in a Form Request.

This plugin bridges that gap: attach a Form Request to your schema or table filters and its validation rules are automatically applied to the matching fields.

Features
--------

[](#features)

- **Form Request integration** — `rules()`, `messages()`, and `attributes()`
- **Automatic field mapping** — by field name, dot notation, and wildcards (`items.*.name`)
- **Array &amp; pipe syntax** — `'email' => ['required', 'email']` and `'email' => 'required|email'`
- **Context-aware** — different Form Requests per page via callback (create vs. edit)
- **Dynamic rules** — re-resolved on every validation (`required_if`, `Rule::unique()`, etc.)
- **Request simulation** — route parameters and input context for Form Requests
- **Smart rule merging** — Form Request rules take precedence on conflicts
- **Orphan rule handling** — unmatched rules show a Filament notification
- **`getFormRequestValidated()`** — optional helper trait for validated data
- **Filament 4 &amp; 5** — single package, internal adapter layer

Supported contexts
------------------

[](#supported-contexts)

ContextAPIResource Create / Edit`Schema::formRequest()`Standalone forms / Settings pages`Schema::formRequest()`Action modals`Schema::formRequest()`Wizards (per-step validation)`Schema::formRequest()`Relation managers`Schema::formRequest()`Table filters`Table::filtersFormRequest()`Requirements
------------

[](#requirements)

DependencyVersionPHP8.2+Laravel11, 12, or 13Filament4.x or 5.xInstallation
------------

[](#installation)

```
composer require occ-therapist/form-request-validation-for-filament
```

Auto-registers via Laravel package discovery. No manual setup required.

Quick start
-----------

[](#quick-start)

### 1. Create a Form Request

[](#1-create-a-form-request)

```
// app/Http/Requests/StoreUserRequest.php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StoreUserRequest extends FormRequest
{
    public function rules(): array
    {
        return [
            'name' => ['required', 'string', 'max:255'],
            'email' => ['required', 'email', 'unique:users'],
        ];
    }

    public function messages(): array
    {
        return [
            'email.unique' => 'This email address is already registered.',
        ];
    }

    public function attributes(): array
    {
        return [
            'name' => 'full name',
        ];
    }
}
```

### 2. Attach it to your Filament schema

[](#2-attach-it-to-your-filament-schema)

```
use App\Http\Requests\StoreUserRequest;
use App\Http\Requests\UpdateUserRequest;
use Filament\Resources\Pages\CreateRecord;
use Filament\Resources\Pages\EditRecord;
use Filament\Schemas\Schema;

public function form(Schema $schema): Schema
{
    return $schema
        ->components([
            TextInput::make('name'),
            TextInput::make('email'),
        ])
        ->formRequest(
            class: fn () => $this instanceof EditRecord
                ? UpdateUserRequest::class
                : StoreUserRequest::class,
        );
}
```

> **Important:** Call `->formRequest()` **after** `->components()` so the validation hook is appended correctly.

Validation errors appear directly on the matching input fields.

API reference
-------------

[](#api-reference)

### `Schema::formRequest()`

[](#schemaformrequest)

```
$schema->formRequest(
    class: fn (Component $livewire): string => StoreUserRequest::class,
    mergeInput: fn (array $state, Component $livewire): array => $state,
);
```

ParameterTypeRequiredDescription`class``Closure`YesReturns the Form Request class. Receives `$livewire` via dependency injection.`mergeInput``Closure`NoMerges additional data into the simulated request input. Receives `$state` and `$livewire`.#### Context selection

[](#context-selection)

```
->formRequest(
    class: fn () => match (true) {
        $this instanceof CreateRecord => StorePostRequest::class,
        $this instanceof EditRecord => UpdatePostRequest::class,
        default => StorePostRequest::class,
    },
)
```

#### Enriching input data

[](#enriching-input-data)

```
->formRequest(
    class: fn () => UpdateUserRequest::class,
    mergeInput: fn (array $state) => [
        ...$state,
        'role' => $this->record?->role,
        'tenant_id' => filament()->getTenant()?->id,
    ],
)
```

Useful when Form Requests depend on record data, route parameters, or values not visible in the form.

### `Table::filtersFormRequest()`

[](#tablefiltersformrequest)

```
$table->filtersFormRequest(
    class: fn () => FilterCustomersRequest::class,
    mergeInput: fn (array $state, Component $livewire): array => $state,
);
```

Same parameters as `Schema::formRequest()`. Attaches validation to the table's filter form.

Filter modeWhen validation runsDeferred (default)When the user clicks **Apply**Live (`->deferFilters(false)`)On every filter changeUsage by context
----------------

[](#usage-by-context)

### Resource pages

[](#resource-pages)

```
public function form(Schema $schema): Schema
{
    return $schema
        ->components([/* fields */])
        ->formRequest(class: fn () => StoreUserRequest::class);
}
```

### Action modals

[](#action-modals)

```
Action::make('invite')
    ->schema(fn (Schema $schema) => $schema
        ->components([
            TextInput::make('email'),
            TextInput::make('role'),
        ])
        ->formRequest(class: fn () => InviteUserRequest::class))
    ->action(function (array $data): void {
        // ...
    });
```

### Wizards

[](#wizards)

One Form Request for the entire wizard — attach it to the root schema:

```
return $schema
    ->components([
        Wizard::make([
            Step::make('Account')->schema([
                TextInput::make('email'),
            ]),
            Step::make('Profile')->schema([
                TextInput::make('name'),
            ]),
        ]),
    ])
    ->formRequest(class: fn () => StoreUserRequest::class);
```

Only fields in the **active step** are validated per step. Use `mergeInput` if rules depend on data from other steps:

```
mergeInput: fn (array $state, Component $livewire): array => [
    ...($livewire->data ?? []),
    ...$state,
],
```

### Relation managers

[](#relation-managers)

```
class PostsRelationManager extends RelationManager
{
    public function form(Schema $schema): Schema
    {
        return $schema
            ->components([
                TextInput::make('title'),
            ])
            ->formRequest(class: fn () => StorePostRequest::class);
    }
}
```

For modal actions on the table, attach `formRequest()` inside the action's schema callback.

### Table filters

[](#table-filters)

```
use Filament\Tables\Filters\Filter;
use Filament\Tables\Table;

public function table(Table $table): Table
{
    return $table
        ->filters([
            Filter::make('created')
                ->schema([
                    DatePicker::make('from'),
                    DatePicker::make('until'),
                ]),
        ])
        ->filtersFormRequest(class: fn () => FilterCustomersRequest::class);
}
```

```
// app/Http/Requests/FilterCustomersRequest.php
public function rules(): array
{
    return [
        'created.from' => ['nullable', 'date'],
        'created.until' => ['nullable', 'date', 'after_or_equal:created.from'],
    ];
}
```

Field mapping
-------------

[](#field-mapping)

Rules are matched to fields **automatically by name** — no per-field configuration needed.

Form Request keyFilament fieldMatch type`email``TextInput::make('email')`Exact`address.street``TextInput::make('address.street')`Dot notation`items.*.name`Repeater child `TextInput::make('name')`Wildcard`created.from`Filter field `DatePicker::make('from')` in `Filter::make('created')`Dot notation### State path normalization

[](#state-path-normalization)

Filament uses prefixed state paths internally. The plugin strips these automatically:

Internal state pathResolved key`data.email``email``mountedActions.0.data.email``email``tableDeferredFilters.created.from``created.from`Rule merging
------------

[](#rule-merging)

When a field already has Filament validation rules, both sets are merged:

```
TextInput::make('email')->email()->nullable()
```

```
// Form Request
'email' => ['required', 'email', 'unique:users']
```

**Result:** `string`, `required`, `email`, `unique:users`

- Form Request rules take **precedence** on conflicts (`nullable` vs. `required`)
- Identical rules are **deduplicated**
- Non-conflicting field rules are **kept**

Orphan rules
------------

[](#orphan-rules)

Rules without a matching schema field still run on submit. Failures show a **Filament notification** instead of an inline field error.

To show the error on a field, add a matching input (visible or hidden):

```
Checkbox::make('terms_accepted')->label('I accept the terms'),
// or
Hidden::make('terms_accepted'),
```

Accessing validated data
------------------------

[](#accessing-validated-data)

```
use OccTherapist\FormRequestValidationForFilament\Concerns\InteractsWithFormRequestValidation;

class CreateUser extends CreateRecord
{
    use InteractsWithFormRequestValidation;

    protected function handleRecordCreation(array $data): Model
    {
        $data = $this->getFormRequestValidated();

        return User::create($data);
    }
}
```

How it works
------------

[](#how-it-works)

```
formRequest() / filtersFormRequest()
        │
        ▼
  Form Request resolved with simulated HTTP request
  (form state + optional mergeInput + route parameters)
        │
        ▼
  rules(), messages(), attributes() extracted
        │
        ▼
  Rules mapped to fields (exact + wildcard + state path normalization)
        │
        ▼
  Merged with existing field rules → applied on validation
        │
        ▼
  Orphan rule failures → Filament notification

```

Limitations
-----------

[](#limitations)

Form Request featureStatus`rules()`Supported`messages()`Supported`attributes()`Supported`authorize()`Not supported — use Filament policies`prepareForValidation()`Not supported`withValidator()`Not supported`passedValidation()` / `failedValidation()`Not supportedTesting
-------

[](#testing)

```
composer test
```

Contributing
------------

[](#contributing)

Contributions are welcome! Please open an issue or pull request on [GitHub](https://github.com/OccTherapist/form-request-validation-for-filament).

License
-------

[](#license)

MIT © [occTherapist](https://github.com/OccTherapist)

###  Health Score

46

—

FairBetter than 92% of packages

Maintenance90

Actively maintained with recent releases

Popularity24

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity50

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

6

Last Release

48d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/28587659?v=4)[Igor Clauss](/maintainers/OccTherapist)[@OccTherapist](https://github.com/OccTherapist)

---

Top Contributors

[![OccTherapist](https://avatars.githubusercontent.com/u/28587659?v=4)](https://github.com/OccTherapist "OccTherapist (10 commits)")

---

Tags

laravelvalidationfilamentform-request

###  Code Quality

TestsPest

### Embed Badge

![Health badge](/badges/occ-therapist-form-request-validation-for-filament/health.svg)

```
[![Health](https://phpackages.com/badges/occ-therapist-form-request-validation-for-filament/health.svg)](https://phpackages.com/packages/occ-therapist-form-request-validation-for-filament)
```

###  Alternatives

[laravel/mcp

Rapidly build MCP servers for your Laravel applications.

80427.1M249](/packages/laravel-mcp)[psalm/plugin-laravel

Psalm plugin for Laravel

3345.4M354](/packages/psalm-plugin-laravel)[defstudio/telegraph

A laravel facade to interact with Telegram Bots

818355.4k3](/packages/defstudio-telegraph)[rawilk/profile-filament-plugin

Profile &amp; MFA starter kit for filament.

3915.5k](/packages/rawilk-profile-filament-plugin)[sandermuller/laravel-fluent-validation

Fluent validation rule builders for Laravel

21428.9k6](/packages/sandermuller-laravel-fluent-validation)[simplestats-io/laravel-client

Server-side analytics for Laravel that follows the full funnel from visit to registration to payment, attributed to the channel that drove it. Revenue, MRR, churn and ad-spend profit (ROAS/CAC) per channel. GDPR compliant, ad-blocker proof.

5226.7k](/packages/simplestats-io-laravel-client)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
