Um formulário HTML é uma seção de documento que possui elementos especiais (caixa de seleção, botões de rádio, menus, etc.). Veja na Listagem 1 o exemplo de formulário simples.

Listagem 1. Formulário em HTML utilizando botões de rádio e botões de seleção.


  <FORM action="index.php" method="post">
      <P>
      <LABEL for="firstname">Primeiro nome: </LABEL>
                <INPUT type="text" id="firstname"><BR>
      <LABEL for="lastname">Último nome: </LABEL>
                <INPUT type="text" id="lastname"><BR>
      <LABEL for="email">Email: </LABEL>
                <INPUT type="text" id="email"><BR>
      <INPUT type="radio" name="sex" value="Masc"> Masculino<BR>
      <INPUT type="radio" name="sex" value="Fem"> Feminino<BR>
      <INPUT type="submit" value="Send"> <INPUT type="reset">
      </P>
   </FORM>

A coleta dos dados fornecidos pelo usuário por meio dos formulários é uma das principais tarefas que o desenvolvedor web se depara no seu cotidiano. Além da criação desses formulários, os desenvolvedores precisam se certificar que o usuário irá fornecer informações com dados ou valores existentes, além de validar os campos e exibir, sempre que necessário, mensagens de erro para entradas inválidas e por fim salvar os dados fornecidos. Ao utilizarmos o Framework Yii iremos notar o quanto nosso trabalho será reduzido por sua facilidade de desenvolvimento baseado na arquitetura MVC.

Para se trabalhar com formulários em Yii são necessários três passos:

  1. Crie uma classe modelo que representa os campos de dados a serem coletados;
  2. Crie um controlador de ação com o código que responde à submissão do formulário;
  3. Crie um arquivo de visão do formulário associado com o controlador de ação.

Criando um Modelo

Antes de criarmos o HTML do formulário devemos definir que tipos de dados desejamos que o usuário forneça e quais regras eles devem seguir.

Para registrarmos essas informações podemos utilizar uma classe modelo, que segundo a documentação do Yii, é o lugar central para manter as informações fornecidas pelo usuário e validá-las.

Nossa classe irá variar de acordo com o propósito do sistema: os dados coletados serão processados e logo após descartados, então utilizaremos um modelo de formulário (form model) onde os dados fornecidos pelo usuário devem ser armazenados em um banco de dados. Para todo esse processo devemos utilizar um active record.

Definindo uma Classe Modelo3

Na Listagem 2 criaremos uma classe modelo chamada de LoginForm que utilizaremos para coletar os dados informados pelo usuário em uma tela de login. Como usaremos essa informação apenas para a autenticação do usuário, sem a necessidade de armazenar essa informação em uma base de dados, então criaremos essa classe como um modelo de formulário.

Listagem 2. Implementação da Classe LoginForm que será utilizada para coletar as informações fornecidas pelo usuário no momento do login.


  class LoginForm extends CFormModel 
  { 
              public $username; 
              public $password; 
              public $rememberMe=false; 
  }

Na listagem acima declaramos três atributos: $username, $password, $rememberMe. Eles serão utilizados para armazenar o nome de usuário e senha informados no formulário, tal como a possibilidade de que o sistema se lembre de seu login. Podemos ver que, por padrão, a variável $rememberMe recebe false, portanto a opção correspondente no formulário de login estará desmarcada.

Regras de Validação

Após o usuário enviar seus dados e os mesmos serem recuperados pelo modelo, devemos ter a certeza de que essas informações serão validadas antes de serem efetivamente utilizadas. Para isso, definiremos um conjunto de regras que serão testadas contra os dados fornecidos. Para isso utilizaremos o método rules(), que deve retornar um array contendo as informações de regras, como mostra a Listagem 3.

Listagem 3. Implementação dos métodos rules() e authenticate.


  class LoginForm extends CFormModel 
  { 
              public $username; 
              public $password; 
              public $rememberMe=false; 
              
              public function rules() 
              { 
                          return array( 
                                     array('username, password', 'required'), 
                                     array('password', 'authenticate'), 
                          ); 
              } 
              public function authenticate($attribute,$params) 
              { 
                          if(!$this->hasErrors()) 
                          // devemos autenticar o usuário somente se não existir erros de validação 
                          { 
                                     $identity=new UserIdentity($this->username,$this->password); 
                                     if($identity->authenticate()) 
                                     { 
                                                 $duration=$this->rememberMe ? 3600*24*15 : 0; // 15 dias 
                                                 Yii::app()->user->login($identity,$duration); 
                                     } 
                                     else 
                                                 $this->addError('password','Senha Incorreta.'); 
                          } 
              } 
  }

Na listagem especificamos que o password e o username são obrigatórios (require). Além disso, informamos que password deve ser autenticado (authenticate).

As regras retornadas pelo método rules() devem seguir o formato apresentado pela Listagem 4.

Listagem 4. Formato que o método rules() deve seguir.

array('Atributos', 'Validador', 'on'=>'ListaDeCenarios', ...opções adicionais)

Os Atributos devem ser uma string contendo todos, separados por vírgula, que devem ser validados de acordo com a regra. O validador define que tipo de validação deverá ser efetuada, onde o parâmetro on é opcional e é utilizado para especificar os cenários onde a regra deve ser aplicada.

Existem algumas maneiras de especificar o validador em uma regra. Numa delas, o validador pode ser um método na classe do modelo, como o authenticate da Listagem 4. Observe a Listagem 5.

Listagem 5. Assinatura do método validador.


/** 
 * @param string o nome do atributo a ser validado 
 * @param array opções especificadas na regra de validação 
 */ 
public function nomeDoValidador($atributo,$parametros) { ... }

Outra maneira é quando o validador pode ser o nome de uma classe validadora. Dessa forma, quando uma regra é aplicada, uma instância dessa classe será criada para efetuar a validação. A classe validadora deve estender a classe CValidator.

Uma terceira maneira é quando o validador pode ser um apelido (alias) pré-definido para uma classe validadora. No exemplo da Listagem 4, o nome require funcionou como um apelido (alias) para a classe CRequireValidator, que valida se o valor do atributo não estiver vazio. Abaixo veremos uma lista completa dos apelidos (aliases) predefinidos:

  • boolean: apelido para CBooleanValidator, garantindo que o valor de um atributo seja somente CBooleanValidator::trueValue ou CBooleanValidator::falseValue;
  • captcha: apelido para CCapthcaValidator, garantindo que o atributo é igual ao código de verificação exibido em um CAPTCHA;
  • compare: apelido para CCompareValidator, garantindo que o atributo é igual a outro atributo ou a uma constante;
  • email: apelido para CEmailValidator, garantindo que o atributo é um endereço de email válido;
  • default: apelido para CDefaultValueValidator, utilizado para atribuir um valor padrão (default) aos atributos especificados;
  • exist: apelido para CExistValidator, garantindo que o valor do atributo existe na coluna da tabela informada;
  • file: apelido para CFileValidator, garantindo que o atributo contém o nome de um arquivo enviado via upload;
  • filter: apelido para CFilterValidator que modifica o atributo com um filtro;
  • in: apelido para CRangeValidator, garantindo que o dado informado está entre uma lista específica de valores;
  • length: apelido para CStringValidator, garantindo que o tamanho do dado está dentro de um tamanho específico;
  • match: alias para CRegularExpressionValidator, garantindo que o dado informado casa com um expressão regular;
  • numerical: apelido para CNumberValidator, garantindo que o dado informado é um número válido;
  • required: apelido para CRequiredValidator, garantindo que o valor do atributo não está vazio.
  • type: apelido para CTypeValidator, garantindo que o atributo é de um tipo específico.
  • unique: apelido para CUniqueValidator, garantindo que o dado informado é único na coluna da tabela do banco de dados informada.

Veja alguns exemplos de validadores predefinidos na Listagem 6.

Listagem 6. Exemplos de validadores predefinidos.


              // username é obrigatório 
              array('username', 'required'), 
              
              // username deve ter entre 5 e 12 caracteres 
              array('username', 'length', 'min'=>5, 'max'=>12), 
              
              // quando estiver no cenário register, password deve ser igual password2 
              array('password', 'compare', 'compareAttribute'=>'password2', 
              'on'=>'register'), 
              
              // quando estiver no cenário login, password deve ser autenticado 
              array('password', 'authenticate', 'on'=>'login'),

Atribuição Segura de Atributos

A atribuição de atributos baseada em cenários está disponível a partir da versão 1.0.2 do framework.

Depois que uma instância da classe modelo é criada, é necessário popular seus atributos com as informações enviadas pelo usuário no ato do login. Isso pode ser feito utilizando a atribuição em massa, como pode ser visto na Listagem 7.

Listagem 7. Exemplo de atribuição em massa.


  $model=new LoginForm; 
  $model->scenario='login'; 
  if(isset($_POST['LoginForm'])) 
              $model->attributes=$_POST['LoginForm'];

Nota: A propriedade scenario está disponível a partir da versão 1.0.4. A atribuição em massa irá utilizar o valor dessa propriedade para determinar quais atributos devem ser atribuídos dessa maneira. Nas versões 1.0.2 e 1.0.3, para fazer a atribuição em massa em um cenário específico, deveríamos proceder da seguinte maneira:

$model->setAttributes($_POST['LoginForm'], 'login');

Disparando a Validação

Uma vez que o modelo tenha recuperado os dados enviados pelo usuário, podemos executar o método CModel::validate() que dispara o processo de validação. Esse método irá retornar um valor indicando o sucesso ou não da validação.

Ao chamarmos o CModel::validate(), temos a opção de especificar um parâmetro com o nome de um cenário. Dessa forma, somente as regras desse cenário serão aplicadas na validação. É importante ressaltar que uma regra só será aplicada a um cenário, caso não exista a opção on nela, ou, caso exista, seu valor corresponda ao cenário especificado.

O código da Listagem 8 irá executar a validação ao registrar um usuário.

Listagem 8. Código que executa a validação de um usuário ao se registrar.


$model->scenario='register'; 
$model->validate();

Se fossem em versões anteriores, o código deveria ser o mesmo da Listagem 9.

Listagem 9. Exemplo de como informar o cenário nas versões 1.0.2 e 1.0.3.

$model->validate('register');

Na classe do modelo de formulário devemos declarar as regras de validação da mesma forma que o apresentado pela Listagem 10.

Listagem 10. Declaração das regras de validação.


  public function rules() 
  { 
       return array( 
         array('username, password', 'required'), 
         array('password_repeat', 'required', 'on'=>'register'), 
         array('password', 'compare', 'on'=>'register'), 
       ); 
  } 

O resultado será que a primeira regra será aplicada a todos os cenários, já as outras duas serão aplicadas somente ao cenário register.

Nota: Validação baseada em cenários está disponível desde a versão 1.0.1.

Recuperando Erros de Validação

Para verificar se existe algum erro de validação podemos utilizar o método CModel::hasErrors(). Caso exista, podemos utilizar o método CModel::getErrors() para obter as mensagens de erro.

Rótulos de Atributos

Quando criamos um formulário, normalmente precisamos exibir um rótulo para cada campo, que por sua vez irá indicar ao usuário que tipo de informação o programa espera que ele informe naquele campo. Embora possamos escrever esses rótulos na camada de visão, torna-se mais flexível e conveniente especificá-los diretamente no modelo.

Como padrão, a classe CModel irá retornar o nome do atributo como seu rótulo. No entanto, essa característica pode ser alterada sobrescrevendo o método attributeLabels(). Como veremos adiante, especificar rótulos nos modelos nos permite criar formulários poderosos de uma maneira bem mais rápida que o convencional.

Criando uma Ação

Agora que já estamos com um modelo pronto, devemos começar a escrever a lógica necessária para poder manipula-lo. Para isso devemos criar uma ação no controle onde iremos colocar toda a lógica. Para o exemplo do login, a Listagem 11é necessária.

Listagem 11. Código de implementação da ação actionLogin.


  public function actionLogin() 
  { 
              $model=new LoginForm; 
              if(isset($_POST['LoginForm'])) 
              { 
                          // coleta a informação inserida pelo usuário 
                          $model->attributes=$_POST['LoginForm']; 
                          // valida a entrada do usuário e redireciona para a página anterior, caso valide 
                          if($model->validate()) 
                                     $this->redirect(Yii::app()->user->returnUrl); 
              } 
                          // exibe o formulário de login 
              $this->render('login',array('model'=>$model)); 
  }

Inicialmente criamos uma instância de um LoginForm. Se a requisição for do tipo POST (indicando que um formulário de login foi enviado), populamos o $model com os dados enviados via $_POST['LoginForm']. Logo após, validamos os dados e caso a validação ocorra com sucesso, redirecionamos o navegador para a página que requisitou a autenticação. Caso contrário, se a validação falhar ou se for o primeiro acesso a essa ação, renderizamos o conteúdo da visão 'login', que será descrita logo mais.

Dica: Na ação login, foi utilizada a propriedade Yii::app()->user->returnUrl para pegar a URL da página que necessitou da autenticação. O componente Yii::app()->user é do tipo CWebUser que representa a sessão com as informações do usuário.

Criando um Formulário

Iremos escrever uma visão para o login, que é algo bem simples. Iniciaremos com uma tag form, cujo atributo action deve ser a URL da ação do login, descrita anteriormente. Em seguida, iremos inserir os rótulos e os campos para os atributos declarados na classe LoginForm. Por fim, colocaremos um botão de envio (submit) que irá ser utilizado para enviar o formulário. Para a construção desse formulário iremos necessitar apenas de HTML.

O Yii nos oferece alguns métodos que facilitam a composição da visão, dentre eles podemos usar o CHtml::textField() para criar uma caixa de texto, e o CHtml::dropDownList(), para criar uma lista do tipo top-down.

Importante: O interessante de se utilizar esses métodos do Yii é que eles não geram apenas o código HTML. O código da Listagem 12, por exemplo, gera uma caixa de texto que dispara o envio do formulário caso seu valor seja alterado pelo usuário.

Listagem 12. Exemplo de método Yii para composição da visão.

CHtml::textField($name,$value,array('submit'=>''));

De outra forma, seria necessário escrever um monte de código JavaScript espalhado por todo o nosso programa.

No próximo exemplo utilizaremos a classe CHtml para criar um formulário de login. Assumiremos que a variável $model representa uma instância da classe LoginForm, como mostra Listagem 13.

Listagem 13. Formulário de login.


  <div class="form">
  <?php echo CHtml::beginForm(); ?>
              <?php echo CHtml::errorSummary($model); ?>
              <div class="row">
                          <?php echo CHtml::activeLabel($model,'username'); ?>
                          <?php echo CHtml::activeTextField($model,'username') ?>
              </div>
              <div class="row">
                          <?php echo CHtml::activeLabel($model,'password'); ?>
                          <?php echo CHtml::activePasswordField($model,'password') ?>
              </div>
              <div class="row rememberMe">
                          <?php echo CHtml::activeCheckBox($model,'rememberMe'); ?>
                          <?php echo CHtml::activeLabel($model,'rememberMe'); ?>
              </div>
              <div class="row submit">
                          <?php echo CHtml::submitButton('Login'); ?>
              </div>
  <?php echo CHtml::endForm(); ?>
  </div><!-- form →

Se utilizarmos a folha de estilo form.css, fornecido como padrão pelo script do Yii, o formulário assumirá a aparência mostrada nas Figuras 1 e 2.

Página de
Login

Figura 1. Página de Login

Página de
Login com Erros

Figura 2. Página de Login com Erros

Objetivando a melhora na criação de formulários, a partir da versão 1.1.1, foi disponibilizado um novo widget chamado CActiveForm. Com ele podemos realizar validações de forma consistente e transparente. Utilizando o código do último exemplo, poderíamos reescreve-lo coo apresentado na Listagem 14.

Listagem 14. Formulário utilizando o widget CActiveForm.


  <div class="form">
  <?php $form=$this->beginWidget('CActiveForm'); ?>
              <?php echo $form->errorSummary($model); ?>
              <div class="row">
                          <?php echo $form->label($model,'username'); ?>
                          <?php echo $form->textField($model,'username') ?>
              </div>
              <div class="row">
                          <?php echo $form->label($model,'password'); ?>
                          <?php echo $form->passwordField($model,'password') ?>
              </div>
              <div class="row rememberMe">
                          <?php echo $form->checkBox($model,'rememberMe'); ?>
                          <?php echo $form->label($model,'rememberMe'); ?>
              </div>
              <div class="row submit">
                          <?php echo CHtml::submitButton('Login'); ?>
              </div>
  <?php $this->endWidget(); ?>
  </div><!-- form -->

A coleta de vários dados fornecidos ao mesmo tempo pelo usuário, ou seja, informações vindas de diversas instâncias de modelos e o envio de todos de uma única vez é conhecido como Entrada Tabular, pois normalmente seus campos irão ser apresentados em forma de tabela HTML

Para trabalharmos com esse tipo, inicialmente devemos criar e preencher um vetor de instâncias de modelos recuperando as entradas do usuário a partir da variável $_POST e atribuindo-as para cada modelo. Quando utilizamos um único modelo para entrada, a recuperação dos dados é feita através do código da Listagem 15.

Listagem 15. Código de implementação da actionBatchUpdate().


  public function actionBatchUpdate()
   
  {
   
              // recupera os itens para atualização em lote assumindo que cada item é instância de um  
              // Item
   
              $items=$this->getItemsToUpdate();
   
              if(isset($_POST['Item']))
   
              {
   
                          $valid=true;
   
                          foreach($items as $i=>$item)
   
                          {
                                     if(isset($_POST['Item'][$i]))
   
                                                 $item->attributes=$_POST['Item'][$i];
   
                                                 $valid=$valid && $item->validate();
                                     }
   
                                     if($valid)
   
                                     // todos os itens são validos faça determinada ação
   
                          }
   
                          // exibe a visão para coletar as entradas tabulares
   
                          $this->render('batchUpdate',array('items'=>$items));
   
              }  

Agora podemos criar a visão batchUpdate para retornar os campos em uma tabela HTML, como mostra a Listagem 16.

Listagem 16. Visão da batchUpdate.


  <div class="yiiForm">
  <?php echo CHtml::beginForm(); ?>
  <table>
  <tr><th>Name</th><th>Price</th><th>Count</th><th>Description</th></tr>
  <?php foreach($items as $i=>$item): ?>
  <tr>
  <td><?php echo CHtml::activeTextField($item,"[$i]name"); ?></td>
  <td><?php echo CHtml::activeTextField($item,"[$i]price"); ?></td>
  <td><?php echo CHtml::activeTextField($item,"[$i]count"); ?></td>
  <td><?php echo CHtml::activeTextArea($item,"[$i]description"); ?></td>
  </tr>
  <?php endforeach; ?>
  </table>
  <?php echo CHtml::submitButton('Save'); ?>
  <?php echo CHtml::endForm(); ?>
  </div><!-- yiiForm →

Finalizamos assim esse artigo sobre a utilização de formulários com o Framework Yii. A partir de agora poderemos trabalhar com essa ferramenta de forma mais consciente e obteremos resultados mais expressivos em menos tempo.

Espero que tenham gostado. Deixe aqui seu comentário com dúvidas e sugestões. Até a próxima!

Links

Documentação do Yii
http://www.yiiframework.com/doc/guide/1.1/en/form.view