arondeparon/laravel-request-sanitizer
Sanitize incoming request data before validation in Laravel. Define reusable sanitizers to trim, cast, or transform input—individually or in pipelines—keeping controllers and form requests clean. Integrates smoothly with Laravel's validation lifecycle.
Install the package and confirm the service provider is auto-discovered (Laravel 5.5+):
composer require arondeparon/laravel-request-sanitizer
If the package ships a config file, publish it so you can register global sanitizers:
php artisan vendor:publish --tag=request-sanitizer-config
The first thing to look at is the package's base request class (typically Arondeparon\LaravelRequestSanitizer\Http\Requests\SanitizedFormRequest) and the Sanitizer contract it expects. That contract is the whole package: everything else is a mapping of field names to an ordered list of sanitizer classes that run before validation.
The fastest first use case is an email field that users paste with stray whitespace or capital letters, which then fails a unique rule:
namespace App\Http\Requests;
use Arondeparon\LaravelRequestSanitizer\Http\Requests\SanitizedFormRequest;
class StoreContactRequest extends SanitizedFormRequest
{
protected $sanitizers = [
'name' => [Trim::class, TitleCase::class],
'email' => [Trim::class, Lowercase::class],
];
public function rules(): array
{
return [
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'email', 'unique:users,email'],
];
}
}
Swap FormRequest for SanitizedFormRequest in the type-hint of your controller method and you are done — the sanitization happens inside the request lifecycle, so $request->validated() contains the cleaned values and your rules run against them.
Ordered pipeline per field. Each field maps to an array; sanitizers execute left-to-right. Put structural cleanups first (Trim, strip tags, normalize whitespace) and formatting last (TitleCase, phone formatting). The output of one feeds the next, so a sanitizer that returns null will short-circuit the rest of the chain.
Custom sanitizers. Write a small class implementing the package's sanitizer contract for domain-specific rules — normalizing Dutch postal codes, stripping currency symbols, converting comma decimals to dots:
class NormalizePostalCode implements Sanitizer
{
public function sanitize($value)
{
return strtoupper(preg_replace('/\s+/', '', (string) $value));
}
}
Keep these stateless and side-effect-free so they are trivial to unit test.
Shared sanitizer sets. If many requests clean the same fields (e.g. every form with a name and email), either declare a base request class with the common $sanitizers and extend it, or register them as global sanitizers in the config so they apply to every sanitized request. Global sanitizers are convenient but make behavior implicit — prefer explicit per-request declarations for anything domain-specific.
Arrays and nested fields. For array payloads, check whether the package supports wildcard keys (items.*.sku) before assuming it does. If it does not, sanitize the sub-values inside prepareForValidation() and leave the field-level sanitizers to the top-level keys.
Choosing between this and prepareForValidation. Both run before validation. Use the package when you want a declarative, reusable list shared across requests; use prepareForValidation when the transformation needs other request state (route model, auth user, headers). Don't do both for the same field.
Testing. Feature tests against the endpoint are the highest-value check: send a dirty payload, assert the validated data is clean, and assert the DB row has the normalized value. Add unit tests for custom sanitizers with edge inputs (null, empty string, unicode, already-clean values).
null or be skipped for missing keys depending on the package version. Guard custom sanitizers against null and non-strings; returning null unexpectedly can flip required/nullable validation results.string rules. Either keep sanitizers returning strings and cast later, or make sure your rules expect the type coming out.Trim before Lowercase is harmless; stripping tags before a max length rule is essential. If a length check fails after "cleaning," the chain order is usually the culprit.unique and exists use the sanitized value. That is usually the point, but be aware that Rule::unique(...)->ignore($id) and case-insensitive collations can still behave differently across databases — test against your actual driver." John " and it's stored as "John", that's fine; if a sanitizer turns "N/A" into "" and the field is required, the error message will confuse. Keep sanitizers narrowly scoped and predictable.dd() inside a sanitizer or a temporary dump($request->all()) at the top of rules(). If the values look right there but not in validated(), the issue is in the rules, not the sanitizers.php artisan config:cache after changes in production, and remember cached config will mask edits made directly on the server.How can I help you explore Laravel packages today?