resultsystems/validation
ResultSystems Validation extends Laravel's validator with Brazilian-oriented rules for CPF, CNPJ, CEP, phone numbers, and similar local formats, making it easier to validate documents and user input in PHP forms and APIs.
The package registers Brazilian document/formatting validation rules (cpf, cnpj, cpf_cnpj, cep, phone, cellphone, placa, uf, pis, cnh, titulo_eleitor, and a few date/money helpers) into Laravel's Validator. It is not a general-purpose validation library—it exists to remove hand-rolled regex check-digit logic from Brazilian forms.
Minimum steps:
composer require resultsystems/validationValidationServiceProvider is auto-discovered. On older 5.x versions add Resultsystems\Validation\ValidationServiceProvider::class to config/app.php.pt-BR messages, publish them with php artisan vendor:publish (check the README for the exact tag) and confirm they are not overwriting your own lang/pt_BR/validation.php.First use case—registration form for a Brazilian customer:
$request->validate([
'name' => 'required|max:255',
'document' => 'required|cpf', // or cnpj / cpf_cnpj
'zip' => 'required|cep',
'mobile' => 'required|cellphone',
]);
Where to look first: the README rule table for exact rule names and argument formats, then the individual rule classes under src/ (e.g. Cpf, Cnpj, Cep) if you need to call the check-digit logic directly outside of validation (e.g. in an importer).
Use it inside FormRequests, not controllers. Document validation is business logic that belongs in a reusable StoreCustomerRequest. That makes the rules testable in isolation and keeps controllers thin.
Pair document rules with normalization. These rules validate digits/format—they do not strip masks. Normalize input first, then validate:
protected function prepareForValidation(): void
{
$this->merge([
'document' => preg_replace('/\D/', '', $this->document ?? ''),
'zip' => preg_replace('/\D/', '', $this->zip ?? ''),
]);
}
Compose with unique/exists. The package only checks that a CPF/CNPJ is structurally valid—it does not check that it is not already registered:
'document' => ['required', 'cpf', Rule::unique('customers', 'document')],
Reuse the same rule for registration and login-by-document flows. cpf_cnpj is useful when a single "document" field accepts either, but keep cpf/cnpj in strict contexts where the type matters (e.g. invoicing requires CNPJ).
Call the validator programmatically in importers and console commands—Validator::make($row, ['cpf' => 'cpf'])—to reject bad rows in bulk ingestion rather than duplicating check-digit math.
Test rule behavior with datasets. Use #[DataProvider] in PHPUnit with known-valid and known-invalid documents (including repeated-digit fakes like 111.111.111-11) so your tests survive package updates.
123.456.789-09 and cpf will likely fail. Strip punctuation in prepareForValidation() or a middleware before validation runs.pt-BR only. If your app runs en or es, the attribute names will fall back to your own validation.php; publish and translate or supply messages() in your FormRequest.cpf_cnpj is ambiguous by design. If downstream code branches on document type (e.g. NFe emission) do not use it—validate cpf and cnpj in separate fields or infer length after stripping.cep validates format, not existence. It does not hit Correios/ViaCEP. If you need address lookup, do it in a service after validation, and treat lookup failures as a separate concern from cep failing.Validator::extend calls can cause "validation rule does not exist" errors. Register it early or keep custom rules in a single provider.composer.json, run composer test in CI against the Laravel versions you support, and be prepared to vendor the rule classes if a future Laravel release breaks the Validator::extend signature. The check-digit logic is small and easy to copy into app/Rules/ as Rule objects if you need long-term stability.Validator::extend in the provider's boot(). If you need different messages per rule or extra documents (CNH variants, state registration numbers), wrap the existing class in your own Rule implementation rather than re-registering the same rule name.How can I help you explore Laravel packages today?