O Gerador De CPF O Gerador De CPF

Validar CPF em Laravel: Custom Rule com FormRequest

Validação de CPF em Laravel usando Custom Rule, FormRequest e exibição de erro no Blade. A lógica segue o algoritmo de módulo 11. Este artigo foca na integração com o framework.

Custom Rule

<?php

namespace App\Rules;

use Closure;
use Illuminate\Contracts\Validation\ValidationRule;

class CpfRule implements ValidationRule
{
    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        $cpf = preg_replace('/[^0-9]/', '', (string) $value);

        if (strlen($cpf) !== 11) {
            $fail('O :attribute não é um CPF válido.');
            return;
        }

        if (preg_match('/^(\d)\1{10}$/', $cpf)) {
            $fail('O :attribute não é um CPF válido.');
            return;
        }

        for ($t = 9; $t < 11; $t++) {
            $sum = 0;
            for ($c = 0; $c < $t; $c++) {
                $sum += (int) $cpf[$c] * (($t + 1) - $c);
            }
            $digit = ((10 * $sum) % 11) % 10;
            if ((int) $cpf[$t] !== $digit) {
                $fail('O :attribute não é um CPF válido.');
                return;
            }
        }
    }
}

Análise do código

A classe implementa ValidationRule, interface padrão do Laravel 11+ que substitui a antiga Rule. O método validate recebe o valor e uma closure $fail que é chamada apenas quando a validação falha. Se o método terminar sem chamar $fail, o valor é considerado válido.

preg_replace('/[^0-9]/', '', $value) remove pontos e traço, aceitando CPF com ou sem máscara. O regex ^(\d)\1{10}$ rejeita sequências de 11 dígitos iguais. O loop calcula ambos os dígitos verificadores usando a mesma lógica da implementação PHP pura.

FormRequest

<?php

namespace App\Http\Requests;

use App\Rules\CpfRule;
use Illuminate\Foundation\Http\FormRequest;

class StorePessoaRequest extends FormRequest
{
    public function authorize(): bool
    {
        return true;
    }

    public function rules(): array
    {
        return [
            'nome' => ['required', 'string', 'max:255'],
            'cpf'  => ['required', 'string', new CpfRule],
            'email' => ['required', 'email'],
        ];
    }

    public function messages(): array
    {
        return [
            'cpf.required' => 'O CPF é obrigatório.',
        ];
    }
}

No controller, basta type-hint o FormRequest. O Laravel valida automaticamente antes de executar o método:

public function store(StorePessoaRequest $request)
{
    // $request->validated() já contém apenas dados válidos
    $pessoa = Pessoa::create($request->validated());
    return redirect()->route('pessoas.show', $pessoa);
}

Exibição no Blade

<form method="POST" action="/pessoas">
    @csrf
    <div>
        <label for="cpf">CPF</label>
        <input
            type="text"
            name="cpf"
            id="cpf"
            inputmode="numeric"
            placeholder="000.000.000-00"
            value="{{ old('cpf') }}"
            class="@error('cpf') border-red-500 @enderror"
        />
        @error('cpf')
            <p class="text-red-500 text-sm mt-1">{{ $message }}</p>
        @enderror
    </div>
    <button type="submit">Salvar</button>
</form>

old('cpf') preserva o valor digitado após erro de validação. A diretiva @error exibe a mensagem retornada pelo $fail da Rule.

Testes

<?php

namespace Tests\Unit\Rules;

use App\Rules\CpfRule;
use PHPUnit\Framework\TestCase;

class CpfRuleTest extends TestCase
{
    private CpfRule $rule;
    private array $errors;

    protected function setUp(): void
    {
        $this->rule = new CpfRule;
        $this->errors = [];
    }

    private function validate(string $value): bool
    {
        $this->errors = [];
        $this->rule->validate('cpf', $value, function ($message) {
            $this->errors[] = $message;
        });
        return empty($this->errors);
    }

    public function testCpfValidoComMascara(): void
    {
        $this->assertTrue($this->validate('529.982.247-25'));
    }

    public function testCpfValidoSemMascara(): void
    {
        $this->assertTrue($this->validate('52998224725'));
    }

    public function testCpfComDigitosRepetidos(): void
    {
        $this->assertFalse($this->validate('111.111.111-11'));
    }

    public function testCpfComDigitoVerificadorIncorreto(): void
    {
        $this->assertFalse($this->validate('529.982.247-26'));
    }

    public function testCpfComTamanhoIncorreto(): void
    {
        $this->assertFalse($this->validate('123.456.789'));
    }

    public function testCpfComZerosAEsquerda(): void
    {
        $this->assertTrue($this->validate('000.000.001-91'));
    }
}

Use o gerador de CPF válido para criar números de teste em lote.

Veja também: validar CPF em PHP puro.