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ência a outro elemento
Como argumento para uma regra ou condição, você também pode passar outro elemento de formulário. A regra usará então
o valor inserido posteriormente pelo usuário no navegador. Isso pode ser usado, por exemplo, para validar dinamicamente se
o elemento password
contém a mesma cadeia de caracteres que o elemento password_confirm
:
$form->addPassword('password', 'Password');
$form->addPassword('password_confirm', 'Confirm Password')
->addRule($form::Equal, 'The passwords do not match', $form['password']);
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"></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.