r64/nova-cpf-cnpj-field
A Laravel Nova field for Brazilian CPF and CNPJ tax IDs, with input masking, validation, and formatted display in Nova forms and resources. Simplifies adding and managing CPF/CNPJ fields in admin panels.
This package is a Laravel Nova field that adds a Brazilian CPF/CNPJ input — typically a masked text field with check-digit validation, formatting on display, and digit-only storage. With 1 star and 0 dependents, treat it as a small utility you will likely own rather than a supported dependency. Verify the source before adopting.
Minimal steps:
composer.json (supported Nova/PHP versions), src/, and the compiled dist/js assets. A Nova field is just a PHP Field subclass plus a Vue component, so a 200-line repo is readable in ten minutes. Confirm it declares a Nova version constraint compatible with yours (Nova 4 uses Laravel\Nova\Fields\Field with a different asset registration path than Nova 3).composer require r64/nova-cpf-cnpj-field
php artisan vendor:publish --provider="..." # only if a provider/config exists
If it is not on Packagist under that name, add the GitHub repo as a VCS repository in composer.json.field.value handling — that tells you whether it stores masked (123.456.789-09) or raw digits, which drives everything downstream.Customer resource with one cpf_cnpj column.
use R64\NovaCpfCnpjField\CpfCnpj; // verify actual namespace
CpfCnpj::make('CPF/CNPJ', 'cpf_cnpj')
->rules('required', 'max:18')
->hideFromIndex(),
Then submit the form once and inspect the persisted value in Tinker. Do this before writing any other code.Normalize on write, format on read. The cleanest day-to-day pattern with any CPF/CNPJ field is a single source of truth: store digits only in the database, mask only in the UI.
// Model
protected function cpfCnpj(): Attribute
{
return Attribute::make(
set: fn ($v) => preg_replace('/\D/', '', (string) $v),
get: fn ($v) => $v, // keep raw for API/consumers
);
}
If the field's Vue component already strips punctuation, do not double-handle it — verify and then rely on it. If it does not, wrap with ->fillUsing() or the model mutator above.
Validation belongs server-side, not just in the component. Nova fields' client-side mask/validation is a UX nicety; the request can bypass it. Mirror the rule set:
->rules('required', 'string', 'max:18', new CpfOrCnpj) // your own Rule object
Write the check-digit logic yourself (~30 lines) or pull laravel-validation-rules/cpf-cnpj. This also gives you a reusable rule for FormRequests and seeders.
One column vs two. CPF and CNPJ are different lengths and semantics. If you need to distinguish a person from a company (tipo_pessoa), store cpf_cnpj plus a derived type field, or two nullable columns. The single-column approach is simpler for search but complicates "is this a company?" queries — decide before migrating.
Display by context:
CpfCnpj::make('CPF/CNPJ')
->rules(...)
->sortable() // only if the DB column is digits-only
->hideFromIndex() // masks make index columns noisy
->help('Somente números ou com máscara'),
sortable() on a masked column sorts lexically, not numerically — almost always wrong. Sort on the raw column.
Pair with unique carefully. unique:customers,cpf_cnpj will only work if the stored value is normalized to digits. Otherwise 123.456.789-09 and 12345678909 both pass. Normalize first, then validate.
Seeders and factories bypass Nova entirely, so seed digits-only values and let the model mutator/accessor handle presentation. Add a fake()->cpf() (via fakerphp/faker pt_BR locale) helper to keep fixtures valid.
API/Inertia consumers should receive raw digits and format client-side; do not leak the Nova field's mask into your JSON contracts.
composer.json Nova constraint, whether dist/js is committed (needed if you cannot npm run dev inside the package). If any of these fail, budget half a day to fork it into app/Nova/Fields or write a ~60-line field yourself — the Vue side is a single masked input.laravel-nova build tooling. A package built for Nova 2 will not "just work." Confirm the nova constraint and the component's mix/vite build path."" or "__.___.___-__" placeholders. Add 'nullable' and a prepareForValidation/middleware trim, or you will get not-null violations and phantom unique conflicts.strlen === 14 && ctype_digit. Prefer a rule you control so you can update it in one place.nova:publish/vendor:publish for the field's dist, the browser console for 404s, and php artisan nova:asset. In Nova 4+, Nova::script()/Nova::style() registration must run from the service provider.resolve/fill for storage normalization, or override the Vue component entirely (->component('my-cpf-field')). Forking is a legitimate strategy here — you gain the validation rule and the component, and you control the maintenance.WHERE cpf_cnpj LIKE '%123%' on masked data misses digit-only inputs.How can I help you explore Laravel packages today?