Development

Documentation/pt_BR/forms_in_action/1.2/01-Validacao-de-Formulario

You must first sign up to be able to contribute.

Version 2 (modified by raphox, 9 years ago)
correção ortográfica

Capítulo 2 - Validação de formulários

No capítulo 1 nos aprendemos como criar e exibir um formulário básico de contato. Neste capítulo iremos aprender como gerenciar validação de formulário.

Antes de começarmos

O formulário de contato no capítulo 1 não é completamente funcional ainda. O que acontece se um usuário submite um endereço de email inválido or se a mensagem que submetida for vazia? Neste caso, nos gostaríamos de mostrar mensagens de erro para requerer ao usuário a entrada correta, como mostra a Figura 2-1.

Figura 2-1 - Exibindo mensagens de erro.

Displaying Error Messages

Aqui esta as regras de validação de implemento para o fomulário de contato:

  • 'name' : opcional
  • 'email' : obrigatório, o valor deve ser um endereço de email válido
  • 'subject': obrigatório, o valor selecionado deve ser válido de uma lista de valores.
  • message: obrigatório, o tamanho da mensagem deve conter no mínimo 4 caracteres.

Note Porque nós precisamos validar o campo 'subject'? A tag <select> já esta limitando o usuário com valores pré-definidos. Uma parte dos usuários poem apenas uma das opções exibidas, mas outros valores podem ser submetidos utilizando ferramentas com o Firefox Developer Toolbar, ou através da simulação de um pedido com ferramentas como 'curl' ou 'wget'.

Listagem 2-1 mostra a template que usamos no capítulo 1.

Listagem 2-1 - A template do formulário 'Contact'

[php]
// apps/frontend/modules/contact/templates/indexSucces.php
<form action="<?php echo url_for('contact/index') ?>" method="POST">
  <table>
    <?php echo $form ?>
    <tr>
      <td colspan="2">
        <input type="submit" />
      </td>
    </tr>
  </table>
</form>

Figura 2-2 separa em partes a interação entre a aplicação e o usuário. O primeiro passo mostra o formulário ao usuário. Quando o usuário submete o formulário, no caso da entrada ser válida o usuário é redirecionado para a página de agradecimento, ou se a entrada incluir valores inválidos o formulário é mostrado novamente com mensagens de erros.

Figura 2-2 - Interação entre a Aplicação e o Usuário

Interaction between the Application and the User

Validadores

Um formulário Symfony é formado de campos. Cada campo pode ser idenficado por um nome único com nós observamos no capítulo 1. Nós conectamos um widget para cada campo na ordem de amostragem para o usuário, agora vamos ver como nós pode aplicar regras de validação para cada um dos campos.

A classe sfValidatorBase

A validação de cada campo é feita por objetos herdados da classe 'sfValidatorBase'. Na ordem de validação do formulário de contato, nós devemos definir objetos de validação para cada um dos quatro campos: 'name', 'email', 'subjetc' e 'message'. Listagem 2-2 mostra a implementação destes validadores na classe do formulário utilizando o método 'setValidators()'.

Listagem 2-2 - Adicionando validadores para classe 'ContactForm'

[php]
// lib/form/ContactForm.class.php
class ContactForm extends sfForm
{
  protected static $subjects = array('Subject A', 'Subject B', 'Subject C');

  public function configure()
  {
    $this->setWidgets(array(
      'name'    => new sfWidgetFormInput(),
      'email'   => new sfWidgetFormInput(),
      'subject' => new sfWidgetFormSelect(array('choices' => self::$subjects)),
      'message' => new sfWidgetFormTextarea(),
    ));
    $this->widgetSchema->setNameFormat('contact[%s]');

    $this->setValidators(array(
      'name'    => new sfValidatorString(array('required' => false)),
      'email'   => new sfValidatorEmail(),
      'subject' => new sfValidatorChoice(array('choices' => array_keys(self::$subjects))),
      'message' => new sfValidatorString(array('min_length' => 4)),
    ));
  }
}

Nós utilizamos três validadores distintos:

  • sfValidatorString: valida um string
  • sfValidatorEmail : valida um email
  • sfValidatorChoice: valida a valor de entrada vindo de uma lista pré-definida de opções

Cada validador recebe uma lista de opções como primeiro argumento. Como os widgets, algumas destas opções são obrigatórias, outras não. Por exemplo, o validador 'sfValidatorChoice' recebe uma opção obrigatória, 'choices' (escolhas). Cada validador pode também receber as opções 'required' (requerido) e 'trim' (preparo), definidos por padrão na classe 'sfValidaroBase':

| Opção | Valor Padrão | Descrição | ----------- | ----------------- | --------------- | required | true | Especifica se o campo é obrigatório | trim | false | Remove automaticamente espaços em branco no início e no final da string antes de ser feita a validação.

Vamos ver as opções disponíveis para os validadores que acabamos de utilizar.

| Validador | Opções obrigatórias | Opções opcionais | | ----------------- | --------------------- | -------------------- | | sfValidatorString | | max_length | | | | min_length | | sfValidatorEmail | | pattern | | sfValidatorChoice | choices | |

Se você tentar submeter o formulário com valores inválidos, você não vai ver nenhuma mudança no comportamento. Nós devemos atualizar o modulo 'contact' para validar os valores submetidos, como mostra a listagem 2-3.

Listagem 2-3 - Implementado Validação no Modulo 'contact'

[php]
class contactActions extends sfActions
{
  public function executeIndex($request)
  {
    $this->form = new ContactForm();

    if ($request->isMethod('post'))
    {
      $this->form->bind($request->getParameter('contact'));
      if ($this->form->isValid())
      {
        $this->redirect('contact/thankyou?'.http_build_query($this->form->getValues()));
      }
    }
  }

  public function executeThankyou()
  {
  }
}

A listagem 2-3 introduz você a vários conceitos novos:

  • No caso da requisição 'GET', o formulário é inicializado e passado à template para ser exibido ao usuário. O formulário esta então em um estado inicial:

    [php]
    $this->form = new ContactForm();
    
  • Quando o usuário submete o formulário com uma requisição 'POST', o method 'bind()' junta o formulário com os dados de entrada do usuário e dispara o mecanismo de validação. O formulário então muda para um estado de 'bound' (intermediário)

    [php]
    if ($request->isMethod('post'))
    {
      $this->form->bind($request->getParameter('contact'));
    
  • Uma fez o formulário em estado intermediário, é possível checar a validade utilizando o método 'isValid()':

    • Se o retorno for 'true':, o formulário é valido e o usuário pode ser redirecionado para página de agradecimentos.

      [php]
      if ($this->form->isValid())
      {
        $this->redirect('contact/thankyou?'.http_build_query($this->form->getValues()));
      }
      
    • Se não, a template 'indexSuccess' é exibida como inicialmente. O processo de validação adiciona as mensagens de erro dentro do formulário para ser exibida para o usuário.

Note Quando um formulário está em um estado inicial, o método 'isValid()' sempre retorna 'false' e o método 'getValues()' sempre retornará um array vazio.

A Figura 2-3 mostra o código que é executado durante a interação entre a aplicação e o usuário.

Figura 2-3 - Código executado durante a interação entre a aplicação e o usuário

Code executed during the Interaction between the Application and the User

O propósito dos validadores

Possívelmente você notou que durante o redirecionamento para a página de agradecimento, nos não estamos utilizando '$request->getParameter('contact')' mas '$this->form->getValues()'. De fato, $request->getParameter('contact') retorna os dados do usuário enquanto $this->form->getValues() retorna os dados validados.

Se o formulário é valido, porquê razão aquelas duas declarações não podem ser idênticas? Cada validador verdadeiramente tem duas tarefas: um validadtion task, mas também um cleaning task (validar e limpar). O método 'getValues()' é, de fato, a devolução dos dados validados e limpos.

O processo de limpeza tem duas principais ações: normalização and conversão dos dados de entrada.

Nos já vimos um caso de normalização com a opção 'trim'. Mas a ação de normalização é muito mais importante para um campo de data por exemplo. O sfValidatorDate valida uma data. Este validador recebe muitos formatos de entrada (um timestamp, um formato baseado em expressão regular, ...) Ao em vez de simplesmente devolver o valor de entrada, ele converte para um formato padrão na forma de 'Y-m-d H:i:s'. Por esta razão, o desenvolvedor está garantido de receber formatos estáveis, apesar da qualidade do formato de entrada. O sistema oferece muita flexibilidade para o usuário, e assegura consistência para o desenvolvedor.

Agora considere uma ação de conversão, como um upload de arquivo. Uma validação de arquivo pode ser feita utilizando o 'sfValidatorFile'. Uma vez o arquivo carregado, em vez de retornar o nome do arquivo, o validador retorna um objeto 'sfVAlidatedFile', tornando mais fácil lidar com as informações do arquivo. Nós veremos depois neste capítulo com utilizar este validador.

Tip O método getValues() retor um array com todos os dados validados e limpos. Mas como as vezes é útil recuperar apenas um valor, há também o método 'getValue()': $email = $this->form->getValue('email').

Formulário inválido

Toda vez que há um campo inválido no formulário, o template 'indexSucess' é exibido. Figura 2-4 mostra o que nós recebemos quando nós enviamos um formulário com dados inválidos.

Figura 2-4 - Formulário inválido

Invalid Form

A chamada para declaração '<?php echo $form ?>' leva automaticamente em consideração as mensagens de erros associados ao campos, e preencherá automaticamente os dados limpos da entrada do usuário.

Quando um formulário está vinculado a dados externos através do método 'bind()', o formulário muda para um estado intermediário e as seguintes ações são disparadas:

  • O processo de validação é executado

  • As mensagens de erros são armazenadas no formulário em ordem para serem apresentadas para a template

  • Os valores padrões do formulário são substituídos pelos dados limpos do usuário

As informações necessários para apresentar as mensagens de erro ou os dados do usuário, são facilmente acessadas utilizando a variável 'form' na template.

Atenção Com vimos no capítulo 1, nós podemos passar valores padrões para o construtor da classe do form. Após a submissão de um formulário inválido, estes valores padrões são substituidos pelos valores submetidos, de forma que o usuário possa corrigir os seus errors. Então, nunca utilize os dados de entrada como um valor padrão como neste exemplo: '$this->setDefaults($request->getParameter('contact'))'..

Personalização de validadores

Customizando mensagem de erros

Como você deve ter notado na Figura 2-4, mensagens de erro não são muito úteis. Vamos ver como fazer então para ser mais intuitiva.

Cada validador pode adicionar erros no formulário. Um erro consiste de um código e uma mensagem. Todo validador tem pelo menos o 'required' (requerido) e 'invalid' (inválido) erros definidos no sfValidatorBase:

| Código | Mensagem | Descrição | ------------ | --------------- | --------------- | required | Required. | o campo é obrigatório e o valor é vazio | invalid | Invalid. | O campo é inválido

Aqui estão os códigos de erros associados aos validadores já utilizados:

| Validador | Código do erro | | ----------------- | --------------- | | sfValidatorString | max_length | | | min_length | | sfValidatorEmail | | | sfValidatorChoice | |

Customização de mensagens de erro podem ser feitas passando um segundo argumento quando é criado o objeto de validação. A Listagem 2-4 customiza várias menssagens de erro e a Figura 2-5 mostra menssagens de erro customizadas em ação.

Lsitagem 2-4 - Customizando mensagens de erro

[php]
class ContactForm extends sfForm
{
  protected static $subjects = array('Subject A', 'Subject B', 'Subject C');

  public function configure()
  {
    // ...

    $this->setValidators(array(
      'name'    => new sfValidatorString(array('required' => false)),
      'email'   => new sfValidatorEmail(array(), array('invalid' => 'The email address is invalid.')),
      'subject' => new sfValidatorChoice(array('choices' => array_keys(self::$subjects))),
      'message' => new sfValidatorString(array('min_length' => 4), array('required' => 'The message field is required.')),
    ));
  }
}

Figura 2-5 - Mensagens de erro customizadas

Customized Error Messages

A Figura 2-6 mostra a mensagem de erro que você recebe se tentar submeter uma mensagem muito curta (nos atribuimos o minímo de 4 caracteres).

Figura 2-6 - Mensagem de erro para string muito curta

Too short Message Error

A mensagem de erro padrão associada a este código ('min_lenght') é diferente da mensagem que nos já vimos antes, uma vez implementado dois valores dinâmicos: o usuário insere o valor ('foo') e o minímo de caracteres permitidos para esse campo é ('4'). A Listagem 2-5 customiza esta mensagem utilizando estes valores dinâmicos e a Figura 2-7 mostra o resultado.

Listagem 2-5 - Customizando a mensagem de erro com valores dinâmicos

[php]
class ContactForm extends sfForm
{
  public function configure()
  {
    // ...

    $this->setValidators(array(
      'name'    => new sfValidatorString(array('required' => false)),
      'email'   => new sfValidatorEmail(array(), array('invalid' => 'Email address is invalid.')),
      'subject' => new sfValidatorChoice(array('choices' => array_keys(self::$subjects))),
      'message' => new sfValidatorString(array('min_length' => 4), array(
        'required'   => 'The message field is required',
        'min_length' => 'The message "%value%" is too short. It must be of %min_length% characters at least.',
      )),
    ));
  }
}

Figura 2-7 - Customizando mensagens de erro com valores dinâmicos

Customized Error Messages with Dynamic Values

Cada mensagem de erro pode utilizar valores dinâmicos, juntando o nome do valor com o caracter porcentagem (%). Os valores acessíveis são geralmente dados de entrada do usuário e valores da opção do validator relacionados ao erro.

Tip Se você quer recaptular todos os código de erros, options, e mensagens padrão de um validador, por favor recorra a documentação da API online (http://www.symfony-project.org/api/1_2/). Cada código, optição e mensagem de erro esta detalhado lá, junto com seus valores padrão (por exmeplo, o validador sfValidatorString` API esta disponível em http://www.symfony-project.org/api/1_1/sfValidatorString).

Validaores de segurança

Por padrão, um formulário é valido apenas se todos os campos submetido pelo usuário tiverem um validador. Isto garante que cada campo tenha um regra de validação e que não seja possível injetar valores para campos que não estejão definidos no formulário.

Para ajudar a entender esta regra de segura, vamos considerar um objeto usuário com mostra a Listagem 2-6.

Listagem 2-6 - A classe 'User'

[php]
class User
{
  protected
    $name = '',
    $is_admin = false;

  public function setFields($fields)
  {
    if (isset($fields['name']))
    {
      $this->name = $fields['name'];
    }

    if (isset($fields['is_admin']))
    {
      $this->is_admin = $fields['is_admin'];
    }
  }

  // ...
}

Um objeto User é composto por duas propriedades, a nome de usuário ('name'), e a booleana que armazera o status administrador ('is_admin'). O método setFields() atualiza ambas propriédades. A Listagem 2-7 mostra o fromulário relaciona a classe 'User', possibilitando o usuário modificar a propriedade 'name' apenas.

Listagem 2-7 - Formulário User

[php]
class UserForm extends sfForm
{
  public function configure()
  {
    $this->setWidgets(array('name' => new sfWidgetFormInputString()));
    $this->widgetSchema->setNameFormat('user[%s]');

    $this->setValidators(array('name' => new sfValidatorString()));
  }
}

A Listagem 2-8 mostra uma implementação do modulo 'user' utilizando o formulário previamente definido permitindo o usuário modificar o campo nome.

Listagem 2-8 - Implementação do Modulo user

[php]
class userActions extends sfActions
{
  public function executeIndex($request)
  {
    $this->form = new UserForm();

    if ($request->isMethod('post'))
    {
      $this->form->bind($request->getParameter('user'));
      if ($this->form->isValid())
      {
        $user = // retrieving the current user

        $user->setFields($this->form->getValues());

        $this->redirect('...');
      }
    }
  }
}

Sem nenhuma proteção, se o usuário submeter um formulário com um valor para o campo 'name', e também para o 'is_admin', então nosso código esta vunerável. Isto é facilmente efetuado utilizando uma ferramenta como Firebug. De fato, o valor 'is_admin' é sempre válido, por que o campo não tem nenhum validador associado a ele no formulário. Seja qual for o valor, o método 'setFields()' atualizará não apenas a propriedade 'name', mas também a propriedade 'is_admin'.

Se você testar este código passando um valor para ambos campos 'name' e 'is_admin', você receberá um mensagem de erro global "Extra field name" (Nome de campo extra), como mostrado na figura 2-8. O sistema gerou um erro porque alguns campos submetidos não tinha qualquer validador associado a si próprios; o campo 'is_admin' não foi definido no formulário 'UserForm'.

Figura 2-8 - Erro Validor Faltando

Missing Validator Error

Todos os validadores que vimos ate agora geram errors associados a campos. De onde vem este error global? Quando nos usamos o método 'setValidators()', o Symfony cria um objeto 'sfValidatorSchema'. O sfValidatorSchema define uma coleção de validadores. A chamada para setValidators() é equivalente ao seguinte código:

[php]
$this->setValidatorSchema(new sfValidatorSchema(array(
  'email'   => new sfValidatorEmail(),
  'subject' => new sfValidatorChoice(array('choices' => array_keys(self::$subjects))),
  'message' => new sfValidatorString(array('min_length' => 4)),
)));

O sfValidatorSchema tem duas regras de validação ativadas por padrão para proteger a coleção de validadores. Estas regras podem ser configuras com as opções allow_extra_fields e filter_extra_fields.

A opção allow_extra_fields, que é definida como 'false' por padrão, verifica que cada dado de entrada tem um validador. Se não, um erro global "Extra field name." é gerado, como mostrado no exemplo anterior. Ao desenvolver, isto permite que os desenvolvedores sejam alertado se esquecer de expecificar a validação de um campo.

Vamos voltar para o formulário de contato. Vamos trocar a regra de validação, trocando o campo 'name' por um campo obrigatório. Uma vez que o valor da opção 'require' é 'true', poderiamos mudar o validor do campo 'name' para:

[php]
$nameValidator = new sfValidatorString();

Este validador não tem impacto, uma vez que não tem a opção 'min_length' nem a 'max_lenght'. Neste caso, poderiamos também substituí-lo por um validador 'empty':

[php]
$nameValidator = new sfValidatorPass();

Em vez que definir um validdor 'empty', poderiamos nos livrar dele, mas a proteção padrão que vimos anteriormente nos empede de nos livrarmos dele. A listagem 2-9 mostra como desabilitar a proteção utilizando a opção 'allow_extra_fields'.

Listagem 2-9 - Desabilitando a protção allow_extra_fields

[php]
class ContactForm extends sfForm
{
  public function configure()
  {
    // ...

    $this->setValidators(array(
      'email'   => new sfValidatorEmail(),
      'subject' => new sfValidatorChoice(array('choices' => array_keys(self::$subjects))),
      'message' => new sfValidatorString(array('min_length' => 4)),
    ));

    $this->validatorSchema->setOption('allow_extra_fields', true);
  }
}

Você agora deve ser capaz de validar o formulário como mostra a Figura 2-9.

Figra 2-9 - Validatando com allow_extra_fields atribuida com true

![Validating with allow_extra_fields set to true](/images/forms_book/en/02_09.png "Validating with allow_extra_fields set to true")

Se você tiver um olhar mais atento, você irá notar que, mesmo se o formulário for válido, o valor do campo name está vazio na página de agradecimento, apesar de qualquer valor que foi apresentado. De fato, o valor ainda não foi atribuido ao array reenviado pelo '$this->fomr->getValues()'. Desabilitando a opção allow_extra_fields, vamos nos livrar do erro devido a falta do validador, mas a opção filter_extra_fields, a qual esta atribuida como true por padrão, filtra esses valores, retirando-os dos valores validados. Claro que é possível alterar este comportamento, como mostrado na Listagem 2-10.

Listagem 2-10 - Desabilitando a proteção filter_extra_fields

[php]
class ContactForm extends sfForm
{
  public function configure()
  {
    // ...

    $this->setValidators(array(
      'email'   => new sfValidatorEmail(),
      'subject' => new sfValidatorChoice(array('choices' => array_keys(self::$subjects))),
      'message' => new sfValidatorString(array('min_length' => 4)),
    ));

    $this->validatorSchema->setOption('allow_extra_fields', true);
    $this->validatorSchema->setOption('filter_extra_fields', false);
  }
}

Você agora de ser capaz de validar seu formulário e recuperar os valores de entrada na sua página de agradecimento.

Veremos no capítulo 4 que esta proteção pode ser utilizada com segurança em objetos serealizados do Propel vindo de valores do formulário.

Validadores Lógicos

Vários validadores podem ser definidos para apenas um campo utilizando validadores lógicos:

  • sfValidatorAnd: Para ser válido, o campo tem de passar por todos os validadores
  • sfValidatorOr : Para ser válido, o campo tem de passar por ao menos um validador

O construtor de um operador lógico requer uma lista de validores como seu primeiro argumento. A Listagem 2-11 utiliza o sfValidatorAnd para associar dois validores exigidos pelo campo 'name'.

Listagem 2-11 - Utilizando o validor 'sfValidatorAnd'

[php]
class ContactForm extends sfForm
{
 public function configure()
 {
    // ...

    $this->setValidators(array(
      // ...
      'name' => new sfValidatorAnd(array(
        new sfValidatorString(array('min_length' => 5)),
        new sfValidatorRegex(array('pattern' => '/[\w- ]+/')),
      )),
    ));
  }
}

Ao submeter o formulário, o valor de entrada do campo 'name' deve ter pelo menos 5 caracters and e corresponder com a empressão regular ([\w- ]+).

Como validores lógicos são validores de si mesmos, eles podem ser combinados para definir avançadas expressões lógicas como mostrado na Listagem 2-12.

Listagem 2-12 - Cobinando vários operadores lógicos

[php]
class ContactForm extends sfForm
{
 public function configure()
 {
    // ...

    $this->setValidators(array(
      // ...
      'name' => new sfValidatorOr(array(
        new sfValidatorAnd(array(
          new sfValidatorString(array('min_length' => 5)),
          new sfValidatorRegex(array('pattern' => '/[\w- ]+/')),
        )),
        new sfValidatorEmail(),
      )),
    ));
  }
}

Validadores Globais

Cada validador que vimos até agora são associados a um campo em espécifico e nos permite validar um valor por vez. Por padrão, eles se comportam ignorando outros dados apresentados pelo usário, mas as vezes a validação de um campo depende do contexto e depende de muitos outros valores do campo. Por exemplo, um validor global é necessário quando duas senhas devem ser iguais, ou quando uma data de início deve ser anterior a data final.

Em ambos os caso, nós precisamos utilizar validores globais pra validar os dados inseridos pelo usuário em seu contexto. Nós podemos inserir um validador global antes ou depois da validação do campo através da utilização de um pre-validator ou um post-validator respectivamente. Normalmente é melhor utilizar um post-validator, porque o valor já validada e limpa, ou seja, em formato normalizado. A Listagem 2-13 mostra como implementar a comparação de duas senha utilizando o validador sfValidatorSchemaCompare.

Listagem 2-13 - Utilizando o Validador sfValidatorSchemaCompare

[php]
$this->validatorSchema->setPostValidator(new sfValidatorSchemaCompare('password', sfValidatorSchemaCompare::EQUAL, 'password_again'));

A partir do Syfmony 1.2, você também pode utilizar operadores "natural" do PHP ao invez da constante de classe sfValidatorSchemaCompare. O exemplo anterior é equivalente a:

[php]
$this->validatorSchema->setPostValidator(new sfValidatorSchemaCompare('password', '==', 'password_again'));

Tip A classe sfValidatorSchemaCompare herda do validador sfValidatorSchema, como todos os validadores globais. sfValidatorSchema é ela própria um validor global, uma vez que valida todos os dados insedos pelo usuário, passando para outros validadores a validação de cada campo.

A Listagem 2-14 mostra como utilizar um simples validador para validar que uma data inicial esta antes de uma data final, customizando a mensagem de erro.

Listagem 2-14 - Utilizando o validador sfValidatorSchemaCompare

[php]
$this->validatorSchema->setPostValidator(
  new sfValidatorSchemaCompare('start_date', sfValidatorSchemaCompare::LESS_THAN_EQUAL, 'end_date',
    array(),
    array('invalid' => 'The start date ("%left_field%") must be before the end date ("%right_field%")')
  )
);

Utilizando um post-validador garante que a comparação de duas datas serão precisas. Seja qual foi o formato de entrado, a validação dos campo 'start_date' e 'end_date' irá sempre ser convertido para valores em formato compátivel com (Y-m-d H:i:s por padrão).

Por padrão, pre-validators and post-validators retorna erros globais para o formulário. Entretanto, alguns deles podem associar um erro para um campo específico. Por exemplo, a opção 'throw_global_error' do validador 'sfVAlidatorSchemaCompare' pode escolher entre um glogal error (Figura 2-10) ou um erro associado ao primeiro campo (Figure 2-11). A Listagem 2-15 mostra como utilizar a opção throw_global_error.

Listagem 2-15 - Utilizando a opção throw_global_error

[php]
$this->validatorSchema->setPostValidator(
  new sfValidatorSchemaCompare('start_date', sfValidatorSchemaCompare::LESS_THAN_EQUAL, 'end_date',
    array('throw_global_error' => true),
    array('invalid' => 'The start date ("%left_field%") must be before the end date ("%right_field%")')
  )
);

Figura 2-10 - Erro Global para um Validador Global

Global Error for a global Validator

Figura 2-11 - Erro Local para um Validador Global

Local Error for a global Validator

Finalmente, utilizando um validador lógico lhe permite combinar vários post-validators como mostra a Listagem 2-16.

Listagem 2-16 - Combinando vários Post-Validators com um Validador lógico

[php]
$this->validatorSchema->setPostValidator(new sfValidatorAnd(array(
  new sfValidatorSchemaCompare('start_date', sfValidatorSchemaCompare::LESS_THAN_EQUAL, 'end_date'),
  new sfValidatorSchemaCompare('password', sfValidatorSchemaCompare::EQUAL, 'password_again'),
)));

Upload de arquivo

Lidar com upload de arquivo, como em todas as linguagens orientadas da web, envolve manipulação de código HTML e no lado do servidor recuperação de arquivo. Nesta seção veremos as ferramente de formulário que o framework tem a oferecer para tornar sua vida mais fácil. Nós veremos também como não cair em armadilhas comuns.

Vamos mudar o formulário de contado para permitir anexar um arquivo a mensagem a ser enviada. Para fazer isto, iremos adicionar um campo 'file' como mostrado na Listagem 2-17.

Listagem 2-17 - Adicionando um Campo 'file' no formulário 'ContactForm'

[php]
// lib/form/ContactForm.class.php
class ContactForm extends sfForm
{
  protected static $subjects = array('Subject A', 'Subject B', 'Subject C');

  public function configure()
  {
    $this->setWidgets(array(
      'name'    => new sfWidgetFormInput(),
      'email'   => new sfWidgetFormInput(),
      'subject' => new sfWidgetFormSelect(array('choices' => self::$subjects)),
      'message' => new sfWidgetFormTextarea(),
      'file'    => new sfWidgetFormInputFile(),
    ));
    $this->widgetSchema->setNameFormat('contact[%s]');

    $this->setValidators(array(
      'name'    => new sfValidatorString(array('required' => false)),
      'email'   => new sfValidatorEmail(),
      'subject' => new sfValidatorChoice(array('choices' => array_keys(self::$subjects))),
      'message' => new sfValidatorString(array('min_length' => 4)),
      'file'    => new sfValidatorFile(),
    ));
  }
}

Quando há um widget sfWidgetFormInputFile em um formulário que permite o upload de um arquivo, precisamos também adicionar um atributo 'enctype' ao formulário como é mostrado na Listagem 2-18.

Listagem 2-18 - Modificando a template para ter o campo 'file' em seu contexto.

[php]
<form action="<?php echo url_for('contact/index') ?>" method="POST" enctype="multipart/form-data">
  <table>
    <?php echo $form ?>
    <tr>
      <td colspan="2">
        <input type="submit" />
      </td>
    </tr>
  </table>
</form>

Note Se você gerar dinâmicamente a template associado ao formulário, o método 'isMultipart()' do objeto do formulário retornará 'true', é necessário do atributo 'enctype'.

Informações sobre arquivos carregados não são armazenadas como os demais valores submetidos no PHP. Em seguida é necessário modificar a chamado do metodo 'bind()' para passar esta informação como um segundo arqumento, como mostrado na listagem 2-19.

Listagem 2-19 - Passando arquivos carregados para o método 'bind()'

[php]
class contactActions extends sfActions
{
  public function executeIndex($request)
  {
    $this->form = new ContactForm();

    if ($request->isMethod('post'))
    {
      $this->form->bind($request->getParameter('contact'), $request->getFiles('contact'));
      if ($this->form->isValid())
      {
        $values = $this->form->getValues();
        // do something with the values

        // ...
      }
    }
  }

  public function executeThankyou()
  {
  }
}

Agora que o formulário esta completamente funcional, ainda precisamos alterar a ação para armazer o arquivo carregado em disco. Como observamos no começo deste arquivo, o 'sfVAlidadorFile' converte as informações relacionadas de um arquivo carregado para um objeto 'sfValidatedFile'. A Listagem 2-20 mostra como lidar com esse objeto para armazer o arquivo no diretório 'web/uploads'.

Listagem 2-20 - Utilizando o objeto sfValidatedFile

[php]
if ($this->form->isValid())
{
  $file = $this->form->getValue('file');

  $filename = 'uploaded_'.sha1($file->getOriginalName());
  $extension = $file->getExtension($file->getOriginalExtension());
  $file->save(sfConfig::get('sf_upload_dir').'/'.$filename.$extension);

  // ...
}

A table a seguir lista todos os methdos do objeto 'sfVAlidatedFile':

| Método | Descrição | ---------------------- | --------------- | save() | Sava o arquivo carregado | isSaved() | Retorna 'true' se o arquivo foi salvo | getSavedName() | Retorno o nome do arquivo salvo | getExtension() | Return a extensão do arquivo, de acordo com o mime type | getOriginalName() | Retorno o nome do arquivo carregado | getOriginalExtension() | Retorna a extensão do nome do arquivo carregado | getTempName() | Retorna o caminho do arquivo temporário | getType() | Retorna o mime type do arquivo | getSize() | Retorna o tamanho do arquivo

Tip O mime type fornecido pelo navegador durante o carregamento do arquivo não é confiável. A fim de garantir uma máxima segurança, as funções 'finfo_open' and 'mime_content_type' e a ferramenta 'file' é utilizada durante a validação do arquivo. Como último recurso, se qualquer função não consiguir advinhar o mime type, ou se o sistema não prevê-lo, o browser mime type é tida como valor. Para adicionar ou alterar a função que advinha o mime type, basta passar a opção 'mime_type_guessers' para o construtor 'sfVAlidatorFile'.