Laravel Validation: 5 dicas essenciais para evitar erros no seu projeto

PHP

Introdução

Bem-vindo, nesse post, vamos por passar por uma jornada de cinco práticas fundamentais de Laravel Validation. Você será guiado através do Laravel Validation- um dos pilares essenciais para a construção de aplicações web seguras e eficientes.

Uma das primeiras regras do web development é:

Nunca confie cegamente nos dados do usuário.

Validação de dados é como uma espécie de filtro de entrada antes de usar as informações que os usuários enviam através de formulários ou qualquer outro tipo de entrada de dados.

Por exemplo, você pode pedir ao Laravel para checar se um campo de e-mail está no formato correto ou se um número de telefone possui a quantidade exata de dígitos. Essa prática é incrivelmente útil para manter a segurança da sua aplicação, prevenindo que informações fora do padrão causem qualquer tipo de dano.

Essas regras podem se tornar um pouco complexas, mas não se preocupe, vamos simplificar e esclarecer mais adiante para que seja fácil de entender.

Laravel Validation: Por onde começar

Para trabalhar como validações em Laravel você vai precisar de pelo menos um rota e de um controller.

Criando as rotas

Vamos definir nossa rota no arquivo routes/web.php.

use App\Http\Controllers\UserController;

Route::post('/user', [UserController::class, 'store']);

Essa é uma rota do tipo POST para o método store do nosso controller UserController. Esse método é responsável por criar novos usuários para a plataforma.

Criando o Controller

Proximo passo é criar o nosso controller UserController. Vamos começar com um controller bem simples com o método store ainda vazio, e na sequencia vamos implementar os métodos de validação. Sinta-se a vontade para rodar esse comando na hora de criar o controller.

php artisan make:controller UserController

O comando vai gerar um controller completo com vários outros métodos, mas vamos focar apenas no método store.

<?php
 
namespace App\Http\Controllers;
 
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
 
class PostController extends Controller
{
     /**
     *  Cria um novo usuario
     */
    public function store(Request $request)
    {
        // Validar novo usuario
 
        $user = /** ... */
 
      return true;
    }
}

Processo de Validação, Respostas e Tratamento de Erros no Laravel

Pronto, agora estamos prontos para preencher nosso método store com a lógica para validar o novo usuário. Para fazer isso, usaremos o método validate fornecido pelo objeto Illuminate\Http\Request.

Laravel oferece “de fábrica” uma longa lista de regras de validação integradas pra você utilizar, elas abrangem praticamente todas as necessidades comuns e algumas mais complexas para validação de dados.

Se as regras de validação passarem, seu código continuará executando normalmente; no entanto, se a validação falhar, uma exceção Illuminate\Validation\ValidationException será lançada e a resposta de erro apropriada será automaticamente enviada de volta ao usuário.

Laravel detecta qual o tipo do request, e retorna a reposta adequada para cada um:

  • Se a validação falhar durante um pedido HTTP tradicional, será gerada uma resposta de redirecionamento para o URL anterior.
  • Se o pedido de entrada for um pedido XHR, será devolvida uma resposta JSON que contém as mensagens de erro de validação.

Vamos comentar sobre o primeiro cenário, mas nosso foco será o segundo.

1. Utilizando as Regras de Validação Integradas do Laravel

Vamos começar com uma validação simples, imagine que o nosso formulário de cadastro tem apenas email, senha e nome. Para validar esses campos faremos o seguinte:

<?php
 
namespace App\Http\Controllers;
 
use App\Http\Controllers\Controller;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Response;
 
class PostController extends Controller
{
     /**
     *  Cria um novo usuario
     */
    public function store(Request $request): JsonResponse {
        $validated = $request->validate([
            'email' => 'required|email|unique:users,email',
            'nome' => 'required|string|max:255',
            'senha' => ['required', Password::min(8)->letters()
                ->mixedCase()
                ->numbers()
                ->symbols()
                ->uncompromised()],
        ]);

        // Dados sao validos
        $user = User::create($validated);

        // Retorna o usuario
        return Response::json($user);
    }
}

Ok, tem bastante coisa acontecendo aqui, vamos explicar tudo com calma.

Para validar os dados nós estamos utilizando o método validate, esse método aceita um array com o campo que você quer validar como chave e as regras de validação como valor dessa chave.

Primeiro entenda que as regras para validação podem ser representadas de duas maneiras, tanto como uma string única separada por “pipes” | essas linhas verticais, ou como um array.

'required|string' ou ['required', 'string']

Na maioria dos casos, a escolha entre uma notação e outra é uma questão de preferência pessoal e não tem impacto significativo. No entanto, existem situações específicas em que a segunda notação é necessária, e vamos explorar esses casos mais adiante.

O primeiro campo que estamos validando é o email do usuário, perceba que estamos utilizando 3 regras. required, email e unique.

'email' => 'required|email|unique:users,email'

  • required, indica que esse campo precisa estar na request para que haja a validação, é um campo obrigatório.
  • email, indica que o campo deve ser formatado como um endereço email.
  • unique, estipula que esse email não deve existir na tabela users no campo email, ou seja o email deve ser único.

O segundo campo sendo validado é o nome do usuário, aqui usamos 3 regras também, required, string e max:255

Para não ficar me repetindo só vou citar as regras que ainda não falamos.

'nome' => 'required|string|max:255'

  • max:255, essa regra serve para indicar o campo sob validação deve ser menor ou igual a um valor máximo, nesse caso 255 caracteres.
  • string, indica que o campo deve ser uma cadeia de caracteres

Por ultimo, estamos validando a senha do usuário. Aqui estamos 6 regras de validação, em um formato um pouco diferente, mas você já vai entender.

'senha' => ['required', Password::min(8)->letters()->mixedCase()->numbers()->symbols()->uncompromised()];

Password é uma classe do próprio Laravel que ajuda personalizar os requisitos da complexidade da senha para os usuários. Então vamos ver qual é o requisito mínimo para a senha dos nossos novos usuários.

  • ->min(8), requer no mínimo 8 caracteres
  • ->letters(), requer no mínimo uma letra
  • ->mixedCase(), requer pelo menos uma letra maiúscula e uma letra minúscula
  • ->numbers(), requer pelo menos um número
  • ->symbols(), requer pelo menos um simbolo

Além disso, é possível empregar um método para assegurar que a senha não tenha sido comprometida em algum vazamento de dados, utilizando o seguinte método:

->uncompromised()

para validar apenas 3 campos, foram necessárias várias regras. A complexidade e extensão das regras tendem a crescer conforme o tamanho do seu formulário. É altamente recomendável explorar a documentação em busca de regras que se adequem ao seu caso. Se não encontrar, não se preocupe. Vou orientar sobre como criar suas próprias regras de validação.

2. Crie suas próprias regras de validação

Existe uma vasta variedade de Laravel Validation no entanto, é possível que você queira especificar algumas regras personalizadas para validação. Para registrar suas novas regras, você primeiro precisa gerar uma nova classe que conterá essa nova regra. Você pode fazer isso usando o comando:

php artisan make:rule Estados

Agora, vamos criar uma classe nova para conferir se o estado que o usuário deu como entrada está na lista dos 26 estados brasileiros mais o Distrito Federal. Se você rodou o comando, o Laravel vai jogar essa regra nova na pasta app/Rules. E se essa pasta ainda não existir, relaxa, o Laravel cria ela pra você quando você usar o comando Artisan para criar a sua regra.

Agora que temos a classe criada, é hora de dizer como ela deve agir. Dentro dessa classe, temos um único método chamado validate. Esse método recebe o nome do atributo, o valor dele e uma função de callback. Se algo der errado, essa função de callback é chamada e mostra a mensagem de erro de validação.

<?php

namespace App\Rules;

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

class Estados implements ValidationRule
{
    /**
     * Run the validation rule.
     */
    public function validate(string $attribute, mixed $value, Closure $fail) : void
    {
        // Valida se é um estado brasileiro
    }
}

Agora é só desenvolver o corpo do nosso método. Basicamente precisamos da lista de todos os estados brasileiros em um array, e vamos usar o método in_array para verificar se o $value (o valor vindo do usuário) está dentro do array.

<?php

namespace App\Rules;

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

class Estados implements ValidationRule
{
    /**
     * Run the validation rule.
     */
    public function validate(string $attribute, mixed $value, Closure $fail) : void
    {
        $estadosBrasileiros = [
            'AC' => 'Acre',
            'AL' => 'Alagoas',
            'AP' => 'Amapá',
            'AM' => 'Amazonas',
            'BA' => 'Bahia',
            'CE' => 'Ceará',
            'DF' => 'Distrito Federal',
            'ES' => 'Espírito Santo',
            'GO' => 'Goiás',
            'MA' => 'Maranhão',
            'MT' => 'Mato Grosso',
            'MS' => 'Mato Grosso do Sul',
            'MG' => 'Minas Gerais',
            'PA' => 'Pará',
            'PB' => 'Paraíba',
            'PR' => 'Paraná',
            'PE' => 'Pernambuco',
            'PI' => 'Piauí',
            'RJ' => 'Rio de Janeiro',
            'RN' => 'Rio Grande do Norte',
            'RS' => 'Rio Grande do Sul',
            'RO' => 'Rondônia',
            'RR' => 'Roraima',
            'SC' => 'Santa Catarina',
            'SP' => 'São Paulo',
            'SE' => 'Sergipe',
            'TO' => 'Tocantins'
        ];

        // Valida se é um estado brasileiro
        if(!in_array($value, $estadosBrasileiros)){
            $fail('O estado fornecido nao e um estado brasileiro valido');
        }
    }
}

Ok, agora com a nossa nova regra criada, precisamos utiliza-la em algum lugar, então nós vamos incluir “estado” como um novo campo para o cadastro do usuário, nosso método store vai ficar assim:

     /**
     *  Cria um novo usuario
     */
    public function store(Request $request): JsonResponse {
        $validated = $request->validate([
            'email' => 'required|email|unique:users,email',
            'nome' => 'required|string|max:255',
            'estado' => ['string', new Estados],
            'senha' => ['required', Password::min(8)->letters()
                ->mixedCase()
                ->numbers()
                ->symbols()
                ->uncompromised()],
        ]);

        // Dados sao validos
        $user = User::create($validated);

        // Retorna o usuario
        return Response::json($user);
    }

Bem simples de usar, basta incluir o novo campo e adicionar regra recém criada com a palavra-chave new, para criar uma nova instância dessa regra. Não se esqueça de importar a classe.

'estado' => ['string', new Estados]

As possibilidades aqui são infinitas, você pode criar regras customizadas para qualquer coisa que a sua plataforma exija.

3. Utilize Form Requests

Até agora estávamos fazendo a validação e adicionando as novas regras dentro do nosso Controller. Mas com mais e mais regras isso começa a ficar impraticável. O método dentro do controller não deveria ser responsável por lidar com todas essas regras e validações. Pensando nisso, Laravel tem uma classe especial chamada FormRequest.

Form Requests são classes personalizadas que encapsulam a sua própria lógica de validação e autorização.

Para criar uma nova Form Request, você pode rodar o comando abaixo:

php artisan make:request StoreUserRequest

A classe gerada será colocada no diretório app/Http/Requests. Se este diretório não existir, ele será criado quando você executar o comando make:request. Cada Form Request gerada pelo Laravel tem dois métodos: authorize e rules.

O método authorize é responsável por determinar se o usuário autenticado pode executar a ação, (mas no nosso caso como ainda estamos tentando cadastrar o usuário vamos apenas retornar true nessa função) enquanto o método rules devolve as regras de validação que devem ser aplicadas ao campos da request:

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StoreUserRequest extends FormRequest
{

    /**
     * Determina se o usuario esta autorizado
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Regras de validacao
     *
     * @return array
     */
    public function rules()
    {
        // Regras de validacao
    }

}

O que nós vamos fazer agora é mover todas as regras de validação do nosso usuário para essa classe, ficando assim:

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StoreUserRequest extends FormRequest
{

    /**
     * Determina se o usuario esta autorizado
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Regras de validacao
     *
     * @return array
     */
    public function rules()
    {
        return [
            'email' => 'required|email|unique:users,email',
            'nome' => 'required|string|max:255',
            'estado' => ['string', new Estados],
            'senha' => ['required', Password::min(8)->letters()
                ->mixedCase()
                ->numbers()
                ->symbols()
                ->uncompromised()],
        ];
    }
}

Já o nosso controller, podemos remover toda a parte de validação, como você pode ver o método store ficou bem mais limpo e fácil de ler.

    /**
     *  Cria um novo usuario
     */
    public function store(StoreUserRequest $request) : JsonResponse
    {
        $validated = $request->validated();

        // Dados sao validos
        $user = User::create($validated);

        // Retorna o usuario
        return Response::json($user);
    }

Duas mudanças importantes foram feitas aqui. Primeiro, trocamos a classe Request pelo StoreUserRequest no parâmetro do método store. StoreUserRequest é a nova classe que acabamos de criar.

A segunda mudança foi na forma como lidamos com a validação. Antes, tínhamos várias regras de validação, mas agora usamos o método ->validated(). Esse método nos dá todos os dados que foram validados no formato de um array.

Se algo der errado na validação (ou seja, os dados não são válidos) e a requisição veio de um XHR (uma requisição AJAX), enviamos de volta uma resposta HTTP com o código de estado 422. Além disso, incluímos uma representação em JSON dos erros de validação para que o usuário saiba o que precisa ser corrigido.

4. Filtre apenas o necessário

Você viu que ao utilizar Form Requests torna a vida do nosso controller muito mais simples? Além de manter nosso código mais organizado, transferindo toda a lógica de validação para fora do controller, as Form Requests oferecem uma série de outras vantagens que vamos explorar a seguir.

Por exemplo, você já viu que podemos usar o método ->validated() para acessar todos os dados validos. E se você tiver dados opcionais ou não precisar de todos os campos? O Laravel pensou em tudo isso e muito mais.

A nossa primeira alternativa, é poder chamar o método safe() em um Form Request. Este método devolve uma instância de Illuminate\Support\ValidatedInput. Este objeto expõe os métodos only, except e all para obter um subconjunto ou todo o array dos dados validos:

$validated = $request->safe()->only(['name', 'email']);
 
$validated = $request->safe()->except(['name', 'email']);
 
$validated = $request->safe()->all();

Além disso, a instância Illuminate\Support\ValidatedInput pode ser iterada como uma array:

// Os dados validos podem ser iterados...
foreach ($request->safe() as $key => $value) {
    // ...
}
 
// Os dados validos podem ser acessados como uma array...
$validated = $request->safe();
 
$email = $validated['email'];

Se você precisa incluir algum dado no array, talvez algum dado baseado na rota ou localização do usuário por exemplo, você pode usar o método merge().

$validated = $request->safe()->merge(['user_acquisition' => 'Google']);

5. Utilize mensagens de erro customizadas

Validar os dados é só uma parte da história. A outra parte é dar mensagens de erro que façam sentido e ajudem os usuários a entender e consertar o que deu errado.

O Laravel simplifica consideravelmente o processo de personalização dessas mensagens de erro de validação.

Você pode personalizar as mensagens de erro utilizadas pelo Form Request substituindo/adicionando o método messages(). Este método deve devolver um conjunto de pares atributo/regra com as mensagens de erro correspondentes:

    /**
     * Cria mensagens de erro customizadas
     *
     * @return array<string, string>
     */
    public function messages() : array
    {
        return [
            'email.required' => 'O email é necessário',
            'nome.required' => 'O seu nome é necessário',
            'senha.required' => 'A senha é necessária',
        ];
    }

No exemplo acima estamos alterando a mensagem da validação required dos campos email, nome e senha. Mas você pode (e deve) alterar todas as mensagens. O formato é bem simples, basta usar o nome do campo e o tipo da validação separado por .. Mais um exemplo, você também pode incluir:

'email.unique' => 'O endereço de email precisa ser único'

Se você precisa traduzir as mensagens de erro, aqui é o lugar certo. Mas se caso a sua plataforma tenha necessidade de utilizar mais de uma lingua, de uma olhada nessa outra feature do Laravel.

Conclusão

Adotar as práticas discutidas neste post não apenas aprimora a segurança e integridade do seu aplicativo Laravel, mas também eleva a qualidade e a eficiência do seu código. Utilizar as regras de validação integradas, criar validações personalizadas, aproveitar as Form Requests e outras estratégias oferece uma base sólida para o desenvolvimento de aplicativos robustos e escaláveis.

Ao entender e aplicar essas técnicas, você estará não apenas otimizando o fluxo de trabalho de desenvolvimento, mas também proporcionando uma melhor experiência para os usuários do seu aplicativo. Mantenha-se atualizado com as novidades e atualizações do Laravel para continuar aprimorando suas habilidades e construindo aplicativos de alta qualidade. Happy coding!

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

0 Comments