laravellegends/pt-br-validator
Brazilian Portuguese validation rules for Laravel, adding CPF, CNPJ, CEP, phone, and other pt-BR formats as native validators so you can validate local documents and data with simple, familiar rule strings.
laravellegends/pt-br-validator adds Brazilian document validation (CPF, CNPJ, CPF/CNPJ, CNH, PIS, Título de Eleitor, Cartão de Crédito, Boleto Bancário, Inscrição Estadual) to Laravel's validator, plus a Formatter helper for masking values.
composer require laravellegends/pt-br-validator
There is no required setup: the service provider registers the rule names with Laravel's validator, so they work as plain strings inside rules() arrays. If you want to customise messages, publish/override the language lines in lang/{locale}/validation.php using the rule name as the key ('cpf' => 'O campo :attribute não é um CPF válido.').
First use case — a registration FormRequest:
public function rules(): array
{
return [
'name' => ['required', 'string', 'max:255'],
'cpf' => ['required', 'cpf'],
'rg' => ['nullable', 'string'],
];
}
protected function prepareForValidation(): void
{
$this->merge([
'cpf' => preg_replace('/\D/', '', (string) $this->input('cpf')),
]);
}
Before going further on 13.0.0, read the 12.2.0...13.0.0 comparison on GitHub and check the new composer.json constraints — the release notes are only a changelog link with no entries, and a major bump of this magnitude is normally a framework/PHP support floor change. Treat the upgrade as a compatibility check first, feature change second.
Where to look first in the source: src/Rules/ (one class per document, each a ValidationRule), src/Validar.php (static helpers), src/Formatter.php (masking), and the provider that maps rule strings to classes.
String rules in FormRequests. The lowest-friction path; keep the raw rule name and let the provider resolve it. Use cpf_cnpj for a single "documento" field so the same input accepts either PF or PJ:
'documento' => ['required', 'cpf_cnpj'],
Rule objects when you need per-field messages or conditional instantiation. The rule classes are instantiable, so you can attach a custom message or build the rule array dynamically (e.g. only require CNPJ when tipo_pessoa === 'pj'):
'cpf' => ['required_if:tipo_pessoa,pf', new Cpf()],
Validar:: for checks outside the request lifecycle. Import jobs, CSV ingestion, artisan commands, seeders and service-layer guards can call the static helpers directly (Validar::cpf($value), Validar::cnpj($value), Validar::cpfOuCnpj($value), Validar::cnh($value), Validar::tituloEleitor($value), Validar::pis($value), …) instead of spinning up Validator::make() just to test one string.
Normalise in, format out. Combine Formatter with prepareForValidation() so the persistence layer only ever sees digits, and mask on the presentation side (Blade component, API resource, or a getCpfFormatadoAttribute accessor). Never store masks in the database.
// storage
$user->cpf = preg_replace('/\D/', '', $request->validated('cpf'));
// presentation
Formatter::cpf($user->cpf); // 000.000.000-00
Pair with fakerphp/faker's pt_BR provider in tests and factories. Use fake('pt_BR')->cpf(false) (digits only) to generate valid fixtures and assert your FormRequest passes; add a hardcoded invalid fixture (e.g. 111.111.111-11) to assert rejection.
Layered domain rules. Wrap the package rule in your own Rule when the business constraint is stricter than the format — e.g. CpfValidoEDisponivel that runs the package check and then a Rule::unique('users', 'cpf'). This keeps controllers/requests readable and the domain semantics testable in isolation.
123.456.789-09 and 12345678909 are not interchangeable for every rule. Strip non-digits in prepareForValidation() rather than hoping the rule does it for you, and be consistent about it.01234567890) silently lose a character, and validation then fails on the round-tripped value. Use char(11) / char(14) or string.cpf through an accessor that returns the formatted value, an update() request built from $model->toArray() re-validates a masked string. Read getRawOriginal('cpf') when hydrating forms.cnh is a checksum-only rule with known false positives. It is useful as a format gate, never as identity verification. Same caution applies to any document where the issuing authority has additional registry state (Inscrição Estadual is UF-dependent — check whether the rule variant you're using needs the UF parameter, and validate per-state rather than globally).cpf_cnpj accepts either, so it never tells you which. If downstream logic branches on PF vs PJ (tax treatment, invoice fields, nome fantasia), derive the type explicitly from the digit count and store it — don't re-infer it later.lang/{locale}/validation.php keyed by the rule name. If you use rule objects instead of strings, Laravel cannot resolve the translation key — set the message in the rule constructor or in the request's messages() method instead.Validar::cpf($value) (or Validator::make(['cpf' => $value], ['cpf' => 'cpf'])->errors() in tinker). That distinguishes "wrong value" from "value never reached the rule" — usually a prepareForValidation or $request->validated() mistake.^13.0 only after composer why-not laravellegends/pt-br-validator 13.0.0 and a full test-suite run. Because the release ships no changelog entries, diff the tagged source against 12.2.0 for changes in rule signatures, message keys and namespace moves — those are the three things that silently break call sites. If your app is still on an older Laravel/PHP, stay on 12.x.How can I help you explore Laravel packages today?