De vez em quando, você se depara com uma situação em que precisa validar uma entrada de solicitação como booleana e o valor de entrada é 'true'
ou 'false'
(observe que coloquei os valores entre aspas simples para indicar que na verdade são strings).
Você esperaria, como eu fiz , que isso funcionaria imediatamente e o validador do Laravel os veria apenas como booleanos. Mas, esse não é o caso, em vez disso, você será atingido por esta bela mensagem de erro'The inputName field must be true or false.'
Para entender profundamente por que isso está acontecendo, vamos passar por um exemplo, digamos que temos uma solicitação de formulário que chamamos PostRequest
.
Solicitações de formulário são classes de solicitação personalizadas que encapsulam sua própria lógica de validação e autorização.
– Documentos Laravel.
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class PostRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'is_published' => 'required|boolean',
];
}
}
PostRequest
, para fins de simplicidade, possui uma única entrada de solicitação que, primeiro, é obrigatória e, em segundo lugar, é boolean , e essa entrada é denominada is_published
. Digamos também que o is_published
valor que nos vem do cliente é 'true'
ou 'false'
.
Neste ponto já sabemos que a validação não passaria. Então, como podemos lidar com isso?
Antecipadamente
Antes de mergulhar, quero mostrar a lógica que o Laravel usa para validar booleanos internamente, você pode ver isso no validateBoolean()
método localizado em Illuminate/Validation/Concerns/ValidatesAttributes
.
<?php
namespace Illuminate\Validation\Concerns;
/**
* Validate that an attribute is a boolean.
*
* @param string $attribute
* @param mixed $value
* @return bool
*/
public function validateBoolean($attribute, $value)
{
$acceptable = [true, false, 0, 1, '0', '1'];
return in_array($value, $acceptable, true);
}
Também podemos ver isso nos testes de unidade.
<?php
use Illuminate\Validation\Validator;
// ...
public function testValidateBoolean()
{
$trans = $this->getIlluminateArrayTranslator();
// ...
$v = new Validator($trans, ['foo' => 'false'], ['foo' => 'Boolean']);
$this->assertFalse($v->passes());
$v = new Validator($trans, ['foo' => 'true'], ['foo' => 'Boolean']);
$this->assertFalse($v->passes());
// ...
}
Fonte: https://github.com/laravel/framework/blob/8.x/tests/Validation/ValidationValidatorTest.php
Soluções
A primeira abordagem - use o método prepareForValidation
Vamos começar com o que eu acho que é uma abordagem mais fácil de implementar, é o que o Laravel chama de “Preparando Entrada para Validação”, e isso é feito usando o prepareForValidation()
método. Como o próprio nome revela, esse método nos permite adicionar novas entradas de solicitação ou atualizar entradas de solicitação existentes antes de passar pelas regras de validação.
Portanto, em nosso pequeno exemplo aqui, tentaremos converter o is_published
valor em um booleano real e mesclá-lo de volta à solicitação original.
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class PostRequest extends FormRequest
{
// ...
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'is_published' => 'required|boolean',
];
}
/**
* Prepare inputs for validation.
*
* @return void
*/
protected function prepareForValidation()
{
$this->merge([
'is_published' => $this->toBoolean($this->is_published),
]);
}
/**
* Convert to boolean
*
* @param $booleable
* @return boolean
*/
private function toBoolean($booleable)
{
return filter_var($booleable, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
}
}
FILTER_VALIDATE_BOOLEAN
tenta ser inteligente, reconhecendo palavras como 'Yes'
, 'No'
, 'Off'
, e 'On'
, e não diferencia maiúsculas de minúsculas ao validar strings.'true'
'false'
FILTER_VALIDATE_BOOLEAN
retorna verdadeiro para '1'
, e . 'true'
_ Retorna false caso contrário.'on'
'yes'
Quando FILTER_NULL_ON_FAILURE
sinalizador é definido, false é retornado SOMENTE para '0'
, 'false'
, 'off'
, 'no'
e ''
, e null é retornado para todos os valores não booleanos.
Compreender como o FILTER_NULL_ON_FAILURE
sinalizador afeta a filter_var
função é essencial, especialmente ao abordar a segunda abordagem, como veremos mais adiante.
Por esse motivo, deixe-me fornecer alguns exemplos para demonstrar como o toBoolean
método se comporta em diferentes casos de uso.
$this->toBoolean('1'); // true
$this->toBoolean('true'); // true
$this->toBoolean('on'); // true
$this->toBoolean('yes'); // true
$this->toBoolean('0'); // false
$this->toBoolean('false'); // false
$this->toBoolean('off'); // false
$this->toBoolean('no'); // false
$this->toBoolean('not a boolean'); // null
Até aqui faz todo o sentido, booleanos “true
verdadeiros” são , booleanos “falsos” são false
, outros são apenas null
. Perfeito!
$this->toBoolean(''); // false
Aqui, é onde se interessa, este último caso de uso pode realmente ser confuso, eu mesmo estava esperando null
como valor de retorno, mas recebemos um boolean
em vez disso ( false
neste caso) .
Isso causará uma validação falsa , pois a string vazia será avaliada como a boolean
, o que fará a validação passar.
Observe que isso nunca será o caso em nosso exemplo, pois temos uma required
regra, se a entrada da solicitação ( is_published
) for uma string vazia, a validação falhará antes mesmo de atingir a boolean
regra.
Achei importante trazer isso à tona.
Com isso dito, vamos pular direto para a segunda abordagem.
A segunda abordagem - use uma regra de validação personalizada
Embora a primeira abordagem funcione perfeitamente, existe uma maneira “elegante” de validar a entrada como booleana, e isso é criar regras de validação personalizadas usando objetos de regra.
O Laravel fornece uma variedade de regras de validação úteis; no entanto, você pode querer especificar algumas de sua preferência. Um método de registro de regras de validação personalizadas é usar objetos de regra. Para gerar um novo objeto de regra, você pode usar o comando make:rule Artisan.
– Documentos Laravel.
Vamos usar este comando para gerar uma regra que valida um valor de string de true
e false
como booleano.
php artisan make:rule Boolean
O Laravel colocará a nova regra no
app/Rules
diretório. Se este diretório não existir, o Laravel irá criá-lo quando você executar o comando Artisan para criar sua regra.
– Documentos Laravel.
Como prometido, uma Boolean
classe é criada no app/Rules
namespace, e aqui está o que parece:
<?php
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
class Boolean implements Rule
{
/**
* Create a new rule instance.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Determine if the validation rule passes.
*
* @param string $attribute
* @param mixed $value
* @return bool
*/
public function passes($attribute, $value)
{
//
}
/**
* Get the validation error message.
*
* @return string
*/
public function message()
{
return 'The validation error message.';
}
}
Uma vez que a Boolean
regra foi criada, estamos prontos para definir seu comportamento.
Um objeto de regra contém dois métodos: passes
e message
. O método passes recebe o valor do atributo e o nome e deve retornar true
ou false
dependendo se o valor do atributo é válido ou não. O método de mensagem deve retornar a mensagem de erro de validação que deve ser usada quando a validação falha.
Pequeno turno - faça funções auxiliares globais
Mas, antes de fazer isso, pode ser útil extrair toBoolean
de antes para sua própria função e disponibilizá-la globalmente.
Uma maneira fácil e eficiente de criar funções globais no Laravel é carregá-las automaticamente diretamente do Composer . A seção de autoload do composer aceita um files
array que é carregado automaticamente.
- Crie um
helpers.php
arquivo onde quiser. Eu costumo manter meus ajudantes globais emapp/Support/helpers.php
. -
Adicione suas funções auxiliares, não há necessidade de especificar nenhum namespace (para que não precisemos usá
use function
-las para chamá-las) .if (!function_exists('to_boolean')) { /** * Convert to boolean * * @param $booleable * @return boolean */ function to_boolean($booleable) { return filter_var($booleable, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); } }
-
Dentro
composer.json
daautoload
seção adicione a seguinte linha"files": ["app/Support/helpers.php"]
."autoload": { "psr-4": { "App\\": "app/", "Database\\Factories\\": "database/factories/", "Database\\Seeders\\": "database/seeders/" }, "files": ["app/Support/helpers.php"] }
- Corre
composer dump-autoload
Agora nossa to_boolean
função pode ser chamada em qualquer lugar em nosso projeto.
De volta à nossa Boolean
regra.
<?php
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
class Boolean implements Rule
{
/**
* Determine if the validation rule passes.
*
* @param string $attribute
* @param mixed $value
* @return bool
*/
public function passes($attribute, $value)
{
return is_bool(to_boolean($value));
}
/**
* Get the validation error message.
*
* @return string
*/
public function message()
{
return __('validation.boolean');
}
}
Para o nosso caso, podemos remover com segurança o construtor.
A is_bool
função é uma função nativa do php, ela descobre se uma variável é booleana.
A __
função é um auxiliar de strings do Laravel, ele traduz a string de tradução ou chave de tradução fornecida usando seus arquivos de localização.
Está quase pronto, tudo o que precisamos fazer agora é atualizar nosso PostRequest
para implementar o objeto de regra personalizado Boolean
assim:
<?php
use App\Rules\Boolean;
// ...
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'is_published' => ['required', new Boolean],
];
}
Conclusão
Finalmente, nosso post chegou ao fim. Então, uma rápida recapitulação, descrevemos duas maneiras, abordagens, se você quiser, para validar 'true'
e 'false'
como booleano com o validador Laravel.
A primeira abordagem é preparar a entrada para validação ao longo do uso do prepareForValidation
método fornecido a nós por FormRequest
.
A segunda abordagem é usar regras de validação personalizadas, mais precisamente objetos de regras, para isso criamos nosso próprio Boolean
objeto para fazer o trabalho.
Eu sei que eu disse que a primeira abordagem é mais fácil de implementar, mas agora que eu uso o objeto de regra com mais frequência, acho que é mais simples e limpo, a abstração no objeto de regra é mais “amigável ao desenvolvedor” por assim dizer, a primeira abordagem é, sem dúvida, mais detalhada. De qualquer forma, é bom conhecer os dois, use o que for mais adequado ao seu caso de uso ou à sua preferência pessoal.
Referências
- php.net - is_bool
- php.net - valida filtros
- php.net - filter_var - notas de contribuição do usuário
- stackoverflow.com - Como faço para fazer funções auxiliares globais em laravel
- stackoverflow.com - O valor de entrada de false para FILTER_VALIDATE_BOOLEAN
- github.com - validação booleana não aceita “true” e “false”, mas aceita “1”, “0”