Validação de Formulários

Controles requeridos

Os controles são marcados conforme necessário com o método setRequired(), cujo argumento é o texto da mensagem de erro que será exibida caso o usuário não a preencha. Se nenhum argumento for apresentado, é usada a mensagem de erro padrão.

$form->addText('name', 'Name:')
	->setRequired('Please fill your name.');

Regras

Acrescentamos regras de validação aos controles com o método addRule(). O primeiro parâmetro é a regra, o segundo é a mensagem de erro, e o terceiro é o argumento da regra de validação.

$form->addPassword('password', 'Password:')
	->addRule($form::MinLength, 'Password must be at least %d characters', 8);

**As regras de validação são verificadas somente se o usuário tiver preenchido o elemento.

O Nette vem com várias regras predefinidas cujos nomes são constantes da classe Nette\Forms\Form. Podemos aplicar essas regras a todos os elementos:

constante descrição argumentos
Required alias de setRequired()
Filled alias de setRequired()
Blank não deve ser preenchido
Equal o valor é igual ao parâmetro mixed
NotEqual o valor não é igual ao parâmetro mixed
IsIn o valor é igual a algum elemento da matriz array
IsNotIn valor não é igual a nenhum elemento da matriz array
Valid validação dos passes de entrada (para condições)

Entradas de texto

Para os elementos addText(), addPassword(), addTextArea(), addEmail(), addInteger(), addFloat(), algumas das regras a seguir também podem ser aplicadas:

MinLength comprimento mínimo das cordas int
MaxLength comprimento máximo das cordas int
Length comprimento no alcance ou comprimento exato par [int, int] ou int
Email endereço de e-mail válido
URL URL válida
Pattern corresponde ao padrão regular string
PatternInsensitive como Pattern, mas não sensível a maiúsculas e minúsculas string
Integer integer
Numeric alias de Integer
Float número inteiro ou de ponto flutuante
Min mínimo do valor inteiro int|float
Max máximo do valor inteiro int|float
Range valor na faixa par [int|float, int|float]

As regras Integer, Numeric a Float convertem automaticamente o valor em inteiro (ou float, respectivamente). Além disso, a regra URL também aceita um endereço sem esquema (por exemplo nette.org) e completa o esquema (https://nette.org). As expressões em Pattern e PatternInsensitive devem ser válidas para todo o valor, ou seja, como se estivesse embrulhado nos caracteres ^ and $.

Número de itens

Para os elementos addMultiUpload(), addCheckboxList(), addMultiSelect() você também pode usar as seguintes regras para limitar o número de itens selecionados ou arquivos carregados:

MinLength número mínimo int
MaxLength número máximo int
Length número no intervalo ou número exato pares [int, int] ou int

Upload de arquivos

Para controles addUpload(), addMultiUpload() as seguintes regras também podem ser usadas:

MaxFileSize tamanho máximo do arquivo em bytes int
MimeType tipo MIME, aceita wildcards ('video/*') string|string[]
Image o arquivo carregado é JPEG, PNG, GIF, WebP
Pattern o nome do arquivo corresponde à expressão regular string
PatternInsensitive como Pattern, mas não sensível a maiúsculas e minúsculas string

Os sites MimeType e Image requerem extensão PHP fileinfo. Se um arquivo ou imagem é do tipo requerido é detectado por sua assinatura. A integridade do arquivo inteiro não é verificada. Você pode descobrir se uma imagem não está corrompida, por exemplo, ao tentar carregá-la.

Mensagens de erro

Todas as regras predefinidas, exceto Pattern e PatternInsensitive, têm uma mensagem de erro padrão, portanto, ela é omitida. Entretanto, ao passar e formular todas as mensagens personalizadas, você tornará o formulário mais fácil de usar.

Você pode alterar as mensagens padrão em configuração, modificando os textos na matriz Nette\Forms\Validator::$messages ou usando o tradutor.

Os seguintes wildcards podem ser usados no texto das mensagens de erro:

%d substitui gradualmente as regras após os argumentos
%n$d substitui com o n-ésimo argumento de regra
%label substitui com etiqueta de campo (sem dois pontos)
%name substitui pelo nome do campo (ex: name)
%value substitui pelo valor inserido pelo usuário
$form->addText('name', 'Name:')
	->setRequired('Please fill in %label');

$form->addInteger('id', 'ID:')
	->addRule($form::Range, 'at least %d and no more than %d', [5, 10]);

$form->addInteger('id', 'ID:')
	->addRule($form::Range, 'no more than %2$d and at least %1$d', [5, 10]);

Condições

Além das regras de validação, podem ser estabelecidas condições. Elas são muito parecidas com regras, mas usamos addRule() em vez de addCondition() e, claro, deixamos isso sem uma mensagem de erro (a condição só pede):

$form->addPassword('password', 'Password:')
	// se a senha não for maior do que 8 caracteres ...
	->addCondition($form::MaxLength, 8)
		// ... então ele deve conter um número
		->addRule($form::Pattern, 'Deve conter número', '.*[0-9].*');

A condição pode ser ligada a um elemento diferente do atual usando addConditionOn(). O primeiro parâmetro é uma referência ao campo. No caso seguinte, o e-mail só será necessário se a caixa de seleção estiver marcada (ou seja, seu valor é true):

$form->addCheckbox('newsletters', 'send me newsletters');

$form->addEmail('email', 'Email:')
	// se a caixa de seleção estiver marcada ...
	->addConditionOn($form['newsletters'], $form::Igual, verdadeiro)
		// ... exigir e-mail
		->setRequired('Preencha seu endereço de e-mail');

As condições podem ser agrupadas em estruturas complexas com os métodos elseCondition() e endCondition().

$form->addText(/* ... */)
	->addCondition(/* ... */) // se a primeira condição for cumprida
		->addConditionOn(/* ... */) // e a segunda condição em outro elemento também
			->addRule(/* ... */) // exigem esta regra
		->elseCondition() // se a segunda condição não for cumprida
			->addRule(/* ... */) // exigem estas regras
			->addRule(/* ... */)
		->endCondition() // voltamos à primeira condição
		->addRule(/* ... */);

Em Nette, é muito fácil reagir ao cumprimento ou não de uma condição no lado JavaScript usando o método toggle(), veja Dynamic JavaScript.

Referências entre controles

A regra ou argumento de condição pode ser uma referência a outro elemento. Por exemplo, você pode validar dinamicamente que o text tem tantos caracteres quanto o valor do campo length:

$form->addInteger('length');
$form->addText('text')
	->addRule($form::Length, null, $form['length']);

Regras e condições personalizadas

Às vezes, chegamos a uma situação em que as regras de validação incorporadas em Nette não são suficientes e precisamos validar os dados do usuário à nossa própria maneira. Em Nette isto é muito fácil!

Você pode passar qualquer ligação de retorno como primeiro parâmetro para os métodos addRule() ou addCondition(). A chamada de retorno aceita o próprio elemento como o primeiro parâmetro e retorna um valor booleano indicando se a validação foi bem sucedida. Ao adicionar uma regra usando addRule(), argumentos adicionais podem ser passados, e estes são então passados como o segundo parâmetro.

O conjunto personalizado de validadores pode assim ser criado como uma classe com métodos estáticos:

class MyValidators
{
	// testa se o valor é divisível pelo argumento
	public static function validateDivisibility(BaseControl $input, $arg): bool
	{
		return $input->getValue() % $arg === 0;
	}

	public static function validateEmailDomain(BaseControl $input, $domain)
	{
		// validadores adicionais
	}
}

O uso é então muito simples:

$form->addInteger('num')
	->addRule(
		[MyValidators::class, 'validateDivisibility'],
		'The value must be a multiple of %d',
		8,
	);

Regras de validação personalizadas também podem ser adicionadas ao JavaScript. A única exigência é que a regra deve ser um método estático. Seu nome para o validador JavaScript é criado concatenando o nome da classe sem contrabarra \, the underscore _, e o nome do método. Por exemplo, escreva App\MyValidators::validateDivisibility como AppMyValidators_validateDivisibility e adicione-o ao objeto Nette.validators:

Nette.validators['AppMyValidators_validateDivisibility'] = (elem, args, val) => {
	return val % args === 0;
};

Evento sobreValidar

Após o envio do formulário, a validação é feita verificando as regras individuais adicionadas por addRule() e depois ligando para o evento onValidate. Seu manipulador pode ser usado para validação adicional, normalmente para verificar a combinação correta de valores em múltiplos elementos do formulário.

Se um erro for detectado, ele é passado para o formulário usando o método addError(). Isto pode ser chamado em um elemento específico ou diretamente no formulário.

protected function createComponentSignInForm(): Form
{
	$form = new Form;
	// ...
	$form->onValidate[] = [$this, 'validateSignInForm'];
	return $form;
}

public function validateSignInForm(Form $form, \stdClass $data): void
{
	if ($data->foo > 1 && $data->bar > 5) {
		$form->addError('This combination is not possible.');
	}
}

Erros de processamento

Em muitos casos, descobrimos um erro quando estamos processando um formulário válido, por exemplo, quando escrevemos uma nova entrada no banco de dados e encontramos uma chave duplicada. Neste caso, passamos o erro de volta para o formulário usando o método addError(). Isto pode ser chamado tanto em um item específico ou diretamente no formulário:

try {
	$data = $form->getValues();
	$this->user->login($data->username, $data->password);
	$this->redirect('Home:');

} catch (Nette\Security\AuthenticationException $e) {
	if ($e->getCode() === Nette\Security\Authenticator::InvalidCredential) {
		$form->addError('Invalid password.');
	}
}

Se possível, recomendamos adicionar o erro diretamente ao elemento do formulário, pois ele aparecerá ao lado dele ao utilizar o renderizador padrão.

$form['date']->addError('Sorry, this date is already taken.');

Você pode ligar para addError() repetidamente para passar múltiplas mensagens de erro a um formulário ou elemento. Você as recebe com getErrors().

Observe que $form->getErrors() retorna um resumo de todas as mensagens de erro, mesmo aquelas transmitidas diretamente a elementos individuais, e não apenas diretamente ao formulário. As mensagens de erro passadas apenas para o formulário são recuperadas via $form->getOwnErrors().

Modificando valores de entrada

Usando o método addFilter(), podemos modificar o valor inserido pelo usuário. Neste exemplo, toleraremos e removeremos espaços no código postal:

$form->addText('zip', 'Postcode:')
	->addFilter(function ($value) {
		return str_replace(' ', '', $value); // remover espaços do código postal
	})
	->addRule($form::Pattern, 'O código postal não tem cinco dígitos', '\d{5}');

O filtro é incluído entre as regras e condições de validação e, portanto, depende da ordem dos métodos, ou seja, o filtro e a regra são chamados na mesma ordem que é a ordem dos métodos addFilter() e addRule().

Validação JavaScript

A linguagem das regras e condições de validação é poderosa. Embora todas as construções funcionem tanto no lado do servidor quanto no lado do cliente, em JavaScript. As regras são transferidas em atributos HTML data-nette-rules como JSON. A validação em si é manipulada por outro script, que anilha todos os eventos submit do formulário, itera sobre todas as entradas e executa as respectivas validações.

Este roteiro é netteForms.js, que está disponível em várias fontes possíveis:

Você pode incorporar o script diretamente na página HTML a partir do CDN:

<script src="https://unpkg.com/nette-forms@3/src/assets/netteForms.js"></script>

Ou copiar localmente para a pasta pública do projeto (por exemplo, de vendor/nette/forms/src/assets/netteForms.min.js):

<script src="/path/to/netteForms.min.js"></script>

Ou instalar via npm:

npm install nette-forms

E depois carregar e correr:

import netteForms from 'nette-forms';
netteForms.initOnLoad();

Alternativamente, você pode carregá-lo diretamente da pasta vendor:

import netteForms from '../path/to/vendor/nette/forms/src/assets/netteForms.js';
netteForms.initOnLoad();

JavaScript dinâmico

Você só quer mostrar os campos de endereço se o usuário optar por enviar a mercadoria pelo correio? Não há problema. A chave é um par de métodos addCondition() & toggle():

$form->addCheckbox('send_it')
	->addCondition($form::Equal, true)
		->toggle('#address-container');

Este código diz que quando a condição for preenchida, ou seja, quando a caixa de seleção for marcada, o elemento HTML #address-container estará visível. E vice versa. Assim, colocamos os elementos do formulário com o endereço do destinatário em um container com essa identificação, e quando a caixa de seleção é clicada, eles são escondidos ou mostrados. Isto é tratado pelo script netteForms.js.

Qualquer seletor pode ser passado como argumento para o método toggle(). Por razões históricas, uma cadeia alfanumérica sem outros caracteres especiais é tratada como um elemento de identificação, o mesmo que se fosse precedida pelo # character. The second optional parameter allows us to reverse the behavior, i.e. if we used toggle('#address-container', false), o elemento só seria exibido se a caixa de seleção fosse desmarcada.

A implementação padrão do JavaScript muda a propriedade hidden para os elementos. Entretanto, podemos mudar facilmente o comportamento, por exemplo, adicionando uma animação. Basta anular o método Nette.toggle em JavaScript com uma solução personalizada:

Nette.toggle = (selector, visible, srcElement, event) => {
	document.querySelectorAll(selector).forEach((el) => {
		// hide or show 'el' according to the value of 'visible'
	});
};

Desabilitando a Validação

Em certos casos, é necessário desativar a validação. Se um botão submeter não deve executar a validação após a submissão (por exemplo Cancelar ou Prever botão), você pode desabilitar a validação ligando para $submit->setValidationScope([]). Você também pode validar o formulário parcialmente, especificando os itens a serem validados.

$form->addText('name')
	->setRequired();

$details = $form->addContainer('details');
$details->addInteger('age')
	->setRequired('age');
$details->addInteger('age2')
	->setRequired('age2');

$form->addSubmit('send1'); // valida o formulário completo
$form->addSubmit('send2')
	->setValidationScope([]); // Não valida nada
$form->addSubmit('send3')
	->setValidationScope([$form['name']]); // valida apenas o campo 'name' (nome)
$form->addSubmit('send4')
	->setValidationScope([$form['details']['age']]); // Valida apenas o campo 'age
$form->addSubmit('send5')
	->setValidationScope([$form['details'])); // valida 'details' container

O evento onValidate no formulário é sempre invocado e não é afetado pelo setValidationScope. onValidate O eventoonValidate no container é invocado somente quando este container é especificado para validação parcial.

versão: 4.0