Zend\Form
O Form é uma classe da Zend Framework 2 que nos permite trabalhar de maneira OO com formulários, ou seja, inserir outros objetos como campos e relacioná-lo com uma entidade e depois exibí-lo em uma view.
Criando um pequeno Form
Para criar o nosso primeiro form devemos inicialmente fazer com que nossa classe herde da classe Zend\Form. Após isso devemos ter em mente alguns métodos presentes na classe Form, como por exemplo, add(), onde passaremos nossos fields, como input, img e etc, ou conjunto de fields chamados de fieldSet.
/**
* Add an element or fieldset
*
* If $elementOrFieldset is an array or Traversable, passes the argument on
* to the composed factory to create the object before attaching it.
*
* $flags could contain metadata such as the alias under which to register
* the element or fieldset, order in which to prioritize it, etc.
*
* @param array|Traversable|ElementInterface $elementOrFieldset
* @param array $flags
*@return \Zend\Form\Fieldset|\Zend\Form\FieldsetInterface|\Zend\Form\FormInterface
*/
$form->add($elementOrFieldset, array $flags = array())
|
Outro método importante é o setAttribute(), onde passaremos atributos da tag form, tais como method, id e outros.
/**
* Set a single element attribute
*
* @param string $key
* @param mixed $value
* @return Element|ElementInterface
*/
$form->setAttribute($key, $value);
|
Segue abaixo um exemplo de um form que faz uso desses métodos.
namespace Application\Form;
use Zend\Form;
class MyForm extends Form {
function __contruct() {
$this->setAttribute(‘method’, ‘post’);
$this->setAttribute(‘id’, ‘my-form’);
$this->add(
array(
‘name’ => ‘sex’,
‘type’ => ‘Zend\Form\Element\Select’,
‘options’ => array(
‘label’ => ‘Sex:’,
“value_options” => array(
“” => “- Choose your sex -“,
“0” => “Male”,
“1” => “Female”,
),
),
‘attributes’ => array(
‘style’ => ‘width: 350px’
)
)
);
$this->add(
array(
‘name’ => ‘name’,
‘options’ => array(
‘label’ => ‘Name:’
),
‘attributes’ => array(
‘class’ => ‘biggest’,
)
)
);
$this->add(
array(
‘name’ => ‘btnsubmit’,
‘options’ => array(
‘label’ => ‘Submit’
),
‘attributes’ => array(
‘class’ => ‘btn’,
‘type’ => ‘submit’
)
)
);
}
}
|
Exibindo o Form na View
Nosso pequeno formulário está pronto, agora podemos exibí-lo. Mas antes disso vamos instância-lo em um controller qualquer e enviá-lo para a view.
class ExampleController extends AbstractActionController
{
public function indexAction() {
$form = new Form\MyForm
return array(‘form’ => $form);
}
}
|
Agora na view o código fica dessa maneira:
$form->prepare();
echo $this->form()->openTag($form);
echo $this->formRow($form->get(‘name’));
echo $this->formRow($form->get(‘sex’));
?>
formButton($form->get(‘btnsubmit’)); ?>
form()->closeTag(); ?>
Primeiro ponto a ser visto é que estamos chamando o método prepare() do nosso form. O que esse método faze é dar certeza ao objeto que está tudo pronto para ser usado, como por exemplo, os filtros de validação, que veremos mais a frente, mensagens de erros e os próprios campos.
Após isso, temos o ViewHelper Form tendo seu método openTag() sendo chamado e passamos para ele nosso objeto $form. O resultado desse método é a tag
sendo gerada com os atributos que criamos, como por exemplo, method=“post” e id=“my-form”.
Outro ViewHelper sendo chamado é o FormRow, responsável por exibir na tela corretamente em modo de linha todos os nossos fields. Para esse ViewHelper estamos passando cada campo de nosso formulário através do método get(nomeDoCampo) do objeto formulário, que será transformado em uma tag html pelo ViewHelper.
Um terceiro ViewHelper é o FormButton que age da mesma maneira do FormRow. E para concluir, temos o método closeTag() do ViewHelper form(), que, nada mais do que óbio, gera a tag html de fechamento de um form (
).
Elemento CSRF
O elemento CSRF é um elemento de segurança que ao ser exibido, preferencialmente em modo “hidden”, gera um código e será enviado junto com os outros dados digitados pelo usuário quando houver submissão.
Ao verificarmos se os dados dentro do formulário estão válidos, haverá uma verificação desse código de segurança, e caso ele não exista ou seja diferente do esperado seu formulário não passará na validação.
Esse elemento impede que formulários falsos e maliciosos sejam criados e apontem seus posts para nossa aplicação, gerando assim inserções ou alterações sem segurança alguma. Com esse código, torna-se impossível ser aceito uma submissão que não seja do nosso próprio formulário.
Abaixo o nosso exemplo de formulário, mas agora com o elemento CSRF.
namespace Application\Form;
use Zend\Form;
class MyForm extends Form {
function __contruct() {
$this->setAttribute(‘method’, ‘post’);
$this->setAttribute(‘id’, ‘my-form’);
$this->add(
array(
‘name’ => ‘sex’,
‘type’ => ‘Zend\Form\Element\Select’,
‘options’ => array(
‘label’ => ‘Sex:’,
“value_options” => array(
“” => “- Choose your sex -“,
“0” => “Male”,
“1” => “Female”,
),
),
‘attributes’ => array(
‘style’ => ‘width: 350px’
)
)
);
$this->add(
array(
‘name’ => ‘name’,
‘options’ => array(
‘label’ => ‘Name:’
),
‘attributes’ => array(
‘class’ => ‘biggest’,
)
)
);
$this->add(
array(
‘name’ => ‘btnsubmit’,
‘options’ => array(
‘label’ => ‘Submit’
),
‘attributes’ => array(
‘class’ => ‘btn’,
‘type’ => ‘submit’
)
)
);
$this->add(
array(
‘type’ => ‘Zend\Form\Element\Csrf’,
‘name’ => ‘security’,
)
);
}
}
|
Para exibí-lo na view basta usarmos o ViewHelper FormHidden, que escreverá a tag do nosso elemento em um campo hidden, ou seja, de modo invisível aos usuários. Veja abaixo.
$form->prepare();
echo $this->form()->openTag($form);
echo $this->formRow($form->get(‘name’));
echo $this->formRow($form->get(‘sex’));
echo $this->formHidden($form->get(‘security’));
?>
formButton($form->get(‘btnsubmit’)); ?>
form()->closeTag(); ?>
ValidationGroup
Porém, precisamos informar ao form que ele deve verificar o nosso campo security, ou seja, devemos colocá-lo no grupo de campos que serão validados pelo formulário. E sendo assim, temos o chamado ValidationGroup.
A classe form tem um método chamado setValidationGroup(array), que recerá um vetor contendo o nome dos campos que serão verificados quando fizermos $form->isValid(). Segue abaixo o exemplo.
namespace Application\Form;
use Zend\Form;
class MyForm extends Form {
function __contruct() {
$this->setAttribute(‘method’, ‘post’);
$this->setAttribute(‘id’, ‘my-form’);
//..Add fields with $this->add()
$this->setValidationGroup(
array(
‘security’
)
);
}
}
|
Uma outra e importante informação é que campos que não forem informados no ValidationGroup não estarão no POST de um formulário. Por isso, quando pensar em Validation Group, pense em campos que serão validados e campos que deverão estar no POST de submissão.
InputFilter
E se quiséssemos fazer com que nossos campos sexo e nome sejam também validados pelo form? Se inserirmos ele no ValidationGroup, então será validado, mas o que será verificado se até agora não específicamos nada? A solução para essas questões vem do uso do InputFilter
Como serão as validações de cada campo, como por exemplo, tamanho do dado, se é obrigatório ou não e outros, devem ser reunidos em um InputFilter. E tudo começa com a ideia de nossa classe MyForm implementando a interface InputFilterAwareInterface.
Se isso ocorresse, a classe MyForm deveria implementar o método setInputFilter(InputFilterInterface $inputFilter) e um atributo que referencie o InputFilter, porém a classe Form já possui essa interface implementada, métodos e atributos existentes. Além disso, a classe Form possui segundo e próprio método para trabalharmos com InputFilter chamado setInputFilter(InputFilterInterface $inputFilter).
Um outro detalhe importante é que os InputFilters são criados à partir de uma factory de inputs chamado Zend\InputFilter\Factory. Essa factory possui um método chamado createInputFilter(array) onde passaremos um vetor com nossos filtros.
namespace Application\Form;
use Zend\Form;
class MyForm extends Form {
function __contruct() {
$this->setAttribute(‘method’, ‘post’);
$this->setAttribute(‘id’, ‘my-form’);
//..Add fields with $this->add()
$this->setInputFilter(
new Factory()->createInputFilter(
array(
‘sex’ => array (
‘required’ => true
),
‘name’ => array (
‘allow_empty’ => false
),
)
)
);
$this->setValidationGroup(
array(
‘security’,
‘sex’,
‘name’,
)
);
}
}
|
Após nosso InputFilter pronto, não podemos esquecer de inserir nossos campos no ValidationGroup, como fizemos. Assim, temos a combinação de validações em cada campo e os campos que deverão ser validados.
Repare que a biblioteca do framework já possui alguns validators e estamos utilizado-os em nosso InputFilter, como por exemplo, o required e allow_empty. Mas e se ocorresse de precisarmos de um tipo de filtro ou validação que não existe na biblioteca? Para isso podemos criar nossos próprios validators.
Custom Validator
Validators são classes que serão usadas para verificar se os valores contidos em cada campo estão de acordo com regras pré-estipuladas. A biblioteca do framework já possui alguns validators prontos para uso, mas as vezes é necessário implementarmos os nossos.
Um validator qualquer deve sempre herdar da classe \Zend\Validator\AbstractValidator e consequentemente implementar o método isValid($value). Esse método que será chamado pelo framework quando for verificar a validade do campo caso ele esteja no ValidationGroup e passando como parâmetro o valor que está no campo. Vamos ver um exemplo de um validator customizado.
namespace Application\Form\Validator
use \Zend\Validator\AbstractValidator
class MyValidator extends AbstractValidator {
/**
* Returns true if and only if $value meets the validation requirements
*
* If $value fails validation, then this method returns false, and
* getMessages() will return an array of messages that explain why the
* validation failed.
*
* @param mixed $value
* @return bool
* @throws Exception\RuntimeException If validation of $value is impossible
*/
public function isValid($value)
{
if (empty($value)) {
return false;
}
return true;
}
}
|
Mensagens de erro
Nosso validator verifica se o valor é falso, 0, null ou “”(vazio) através do método empty(), e caso seja, ele retornará falso e fará consequentemente com que o método isValid() do formulário retorne falso mesmo que outros validators tenha resultado em verdadeiro.
Os validators também permitem que você retorne um mensagem de erro, que por padrão aparecerá abaixo do campo e em cores vermelhas. Para que isso ocorra devemos primeiros criar as mensagens sobrescrevendo o atributo $messageTemplates passando um vetor contendo mensagens, que no nosso caso só será uma: “Este campo não pode ser vazio!”.
Criada nossa mensagem podemos executá-la quando quisermos através do método error() da classe AbstractValidator e passando como parâmetro a key do vetor referente a mensagem que gostariamos de exibir. Sendo assim ficaria assim a exibição de erros.
namespace Application\Form\Validator
use \Zend\Validator\AbstractValidator
class MyValidator extends AbstractValidator {
const IS_EMPTY = ‘isEmpty’;
/**
* @var array
*/
protected $messageTemplates = array(
self::IS_EMPTY => “Este campo não pode ser vazio!”,
);
/**
* Returns true if and only if $value meets the validation requirements
*
* If $value fails validation, then this method returns false, and
* getMessages() will return an array of messages that explain why the
* validation failed.
*
* @param mixed $value
* @return bool
* @throws Exception\RuntimeException If validation of $value is impossible
*/
public function isValid($value)
{
if (empty($value)) {
$this->error(self::IS_EMPTY);
return false;
}
return true;
}
}
|
Criado nosso validator, podemos inserí-lo agora no InputFilter.
namespace Application\Form;
use Zend\Form;
class MyForm extends Form {
function __contruct() {
$this->setAttribute(‘method’, ‘post’);
$this->setAttribute(‘id’, ‘my-form’);
//..Add fields with $this->add()
$myValidator = new Validator\MyValidator();
$this->setInputFilter(
new Factory()->createInputFilter(
array(
‘sex’ => array (
‘required’ => true,
‘validators’ => array(
$myValidator
)
),
‘name’ => array (
‘allow_empty’ => false,
‘validators’ => array(
array(‘name’ => ‘Application\Validator\Form\MyValidator’)
)
),
)
)
);
$this->setValidationGroup(
array(
‘security’,
‘sex’,
‘name’,
)
);
}
}
|
Veja que isso pode ser feito de duas maneira, ou diretamente, que é recomendado quando você tem um validator que exige algum tipo de parâmetro no construtor, ou por modo indireto, fica factory.
Criando e Validando o Form em um Controller
class ExampleController extends AbstractActionController
{
public function indexAction() {
$form = new Form\MyForm;
$request = $this->getRequest();
if($request->isPost()) {
$form->setData($request->getPost());
if($form->isValid()) {
//…
}
}
return array(‘form’ => $form);
}
}
|
Perceba que caso o usuário esteja fazendo um POST, nos iremos pegar os valores que estão no POST e inserir no formulário através do método setData(), que espera como parâmetro um vetor de informações.
Com isso, nosso formulário está com seus campos preenchidos e prontos para serem validados pelo método isValid(), que irá capturar o InputFilter do formulário, passar para ele os valores que estão preenchendo os campos e verificar se estão corretos.
Note também que mesmo que não estejam validos, o usuário não precisará redigitar as informações nos campos, pois elas se manterão graças ao setData recebendo o POST.
setData()
O método para se popular um formulário com um vetor de valores é através do método setData(). Esse método relacionará as keys do vetor com nome dos campos e copiará os valores do vetor para os campos. Lembrando que o mesmo pode acontecer através do bind(), porém, não será com um vetor, mas com um objeto. Vamos ver um exemplo.
$form->setData(
array(
‘name’ => ‘Tássio’
‘surname’ => ‘Auad’
)
)
|
Quando exibirmos esse formulário em uma view, o campo name estará preenchido com o valor ‘Tássio’ e o campo surname, com o valor ‘Auad’.
isValid()
Tentando descrever o que basicamente acontece dentro do isValid() para facilitar a visão de formulários, pois tudo gira entorno desse método, vamos criar uma algoritmo.
Captura InputFilter da entidade. Caso não haja, captura o InputFilter do Form. Caso não haja, instancia um InputFilter.
Envia os dados do form para dentro do InputFilter
Envia o ValidationGroup para dentro do InputFilter
Executa o método isValid() do Input Filter
Se for válido, popula a entidade com os dados do form
|
Veja que é a mesma coisa que:
$inputFilter = $form->getInputFilter();
$inputFilter->setData($form->getData());
$inputFilter->setValidationGroup($form->getValidationGroup());
if($inputFilter->isValid()) {
$object = new Entity\MyEntity();
$hydrate = new Zend\Stdlib\Hydrator\ClassMethods(true);
$hydrate->hydrate($row->getArrayCopy(), $objeto);
}
|
Ou:
$object = new Entity\MyEntity();
$inputFilter = $object>getInputFilter();
$inputFilter->setData($form->getData());
$inputFilter->setValidationGroup($form->getValidationGroup());
if($inputFilter->isValid()) {
$hydrate = new Zend\Stdlib\Hydrator\ClassMethods(true);
$hydrate->hydrate($row->getArrayCopy(), $objeto);
}
|
Form Vinculado a uma Entidade
Para um sistema que faz uso do paradigma de orientação a objetos, o uso de entidades é fundamental. Um formulário mais simples possível reune em si os papéis de adicionar campos (add(field)), adicionar quais campos serão validados (ValidationGroup) e como serão validados (InputFilter). Mas e como ficaria a entidade nesse contexto?
Se quiséssemos usar uma entidade nesse contexto teríamos que pegar os dados do formulário e passá-los a ela manualmente, e perderíamos a produtividade. Além disso, é interessante que o tipo da validação de cada campo esteja na entidade, já que cada campo representaria um atributo, e cada atributo tem suas restrições de valor. Podemos melhorar essa situação.
Para prosseguirmos com detalhes da vinculação entre um formulário e uma entidade, devemos ter antes em mente um formulário de exemplo. Abaixo segue o exemplo de formulário simples, sem fieldsets e vínculo com entidades.
namespace Application\Form;
use Zend\Form;
class MyForm extends Form {
function __contruct() {
$this->setAttribute(‘method’, ‘post’);
$this->setAttribute(‘id’, ‘my-form’);
$this->add(
array(
‘name’ => ‘sex’,
‘type’ => ‘Zend\Form\Element\Select’,
‘options’ => array(
‘label’ => ‘Sex:’,
“value_options” => array(
“” => “- Choose your sex -“,
“0” => “Male”,
“1” => “Female”,
),
),
‘attributes’ => array(
‘style’ => ‘width: 350px’
)
)
);
$this->add(
array(
‘name’ => ‘name’,
‘options’ => array(
‘label’ => ‘Name:’
),
‘attributes’ => array(
‘class’ => ‘biggest’,
)
)
);
$this->add(
array(
‘name’ => ‘btnsubmit’,
‘options’ => array(
‘label’ => ‘Submit’
),
‘attributes’ => array(
‘class’ => ‘btn’,
‘type’ => ‘submit’
)
)
);
$this->add(
array(
‘name’ => ‘btnsubmit’,
‘options’ => array(
‘label’ => ‘Submit’
),
‘attributes’ => array(
‘class’ => ‘btn’,
‘type’ => ‘submit’
)
)
);
$this->setInputFilter(
new Factory()->createInputFilter(
array(
‘sex’ => array (
‘required’ => true
),
‘name’ => array (
‘allow_empty’ => false
),
)
)
);
$this->setValidationGroup(
array(
‘security’,
‘sex’,
‘name’,
)
);
}
}
|
Entidade
Antes tinhamos o InputFilter sendo criado dentro na classe do formulário, mas agora quem passa a possuir esse papel. Então, a mudança principal será com a nossa classe entidade que será criada e que passa a implementar a interface Zend\InputFilter\InputFilterAwareInterface e possuir um atributo que referenciará ao InputFilter, diferente do formulário que herda de Form, pois a classe pai já implementa e utiliza esse detalhes.
Como a entidade representa uma tabela do banco de dados, então é interessante que as restrições de cada coluna estejam representadas nela. Sendo assim, a nossa entidade de exemplo fica dessa maneira.
namespace Application\Entity;
use Zend\InputFilter\InputFilterAwareInterface;
public class MyEntity implements InputFilterAwareInterface{
private $name;
private $sex;
private $inputFilter;
//Getters and Setters..
/**
* Retrieve input filter
*
* @return InputFilterInterface
*/
public function getInputFilter()
{
$myValidator = new Validator\MyValidator();
if (null === $this->inputFilter) {
$this->inputFilter = new Factory()->createInputFilter(
array(
‘sex’ => array (
‘required’ => true,
‘validators’ => array(
$myValidator
)
),
‘name’ => array (
‘allow_empty’ => false,
‘validators’ => array(
array(‘name’ => ‘Application\Validator\Form\MyValidator’)
)
),
)
);
}
return $this->inputFilter;
}
}
|
Vínculo com a Entidade
Com essas mudanças, o nosso form deixa de possuir o InputFilter e cria um vínculo com a entidade. O exemplo de como vincular uma entidade à um form segue abaixo.
namespace Application\Form;
use Zend\Form;
class MyForm extends Form {
function __contruct() {
$this->setAttribute(‘method’, ‘post’);
$this->setAttribute(‘id’, ‘my-form’);
$this->setObject(new Entity\MyEntity());
//…Add Fields
$this->setValidationGroup(
array(
‘security’,
‘sex’,
‘name’,
)
);
}
}
|
O vínculo então é gerado à partir do método setObject() que espera como parâmetro a nossa Entity contendo o InputFilter.
isValid()
Mas então o método isValid() irá usar o ValidationGroup() do Form para ver quais campos serão validados e pegar o InputFilter da Entidade para dizer como esses campos serão validados? Exato.Quando for feito um isValid() na instância do nosso formulário, o InputFilter será primeiramente procurado dentro da entidade relacionada, se não houver entidade ou se houver mas não há uma instância de InputFilter nela, será procurado no próprio Form, e se mesmo assim não houver, será instânciado e usado um novo InputFilter.
Bind()
class ExampleController extends AbstractActionController
{
public function indexAction() {
$form = new Form\MyForm
$request = $this->getRequest();
if ($request->isPost()) {
$form->setData($request->getPost());
if ($form->isValid()) {
//Entidade populada com os campos do formulário.
}
}
return array(‘form’ => $form);
}
}
|
Outro detalhe é que quando nossa entidade possui valores em seus atributos, esse valores são adotados nos respectivos campos do formulário quando fazemos bind. Suponha que uma instância da nossa entidade receba no atributo $name o valor “Tássio” e no atributo $surname o valor “Auad”. Agora imagine que vamos vincular essa entidade ao formulário, e a consequência disso é o form tendo o campo com nome “name” preenchido pelo valor “Tássio” e o campo com nome “surname” preenchido com o valor “Auad”.
Usando FieldSets em Forms
Existem algumas diferenças entre trabalhar simplesmente com forms e compor um form com fieldsets. Quando usamos fieldSet para conter os campos e ligamos ele ao formulários ao invés de ter tudo diretamente dentro do formulário, estamos definindo melhor o utilidade de cada um.
Além disso, o fieldSet estará ligada a uma entidade, nos permitindo assim trabalhar com entidade e uma melhor POO, e não simplesmente vetores com dados. Com o decorrer dos exemplos você verá algumas das grandes vantagens de se ter fieldSets
Criando um FieldSet
Um fieldSet é um container de campos de um formulário e que pode ou não estar ligado a uma entidade. Caso isso aconteça, cada campo contido no fieldSet poderá relacionado a um atributo da entidade, como por exemplo, o input com nome “name” do nosso fieldSet poderá ter um representante na entidade, que será um atributo $name.
Algo que deve ser ter em mente é que o Form é um FieldSet, ou seja, Form herda de FieldSet e por isso podemos inserir elementos diretamente nele. E também devemos entender que todo formulário tem um FieldSet base, mas poderá ter outros, apesar de não conseguir imaginar um caso para isso.
namespace Application\Form\Fieldset;
use Zend\Form\Fieldset;
class MyFieldSet extends Fieldset
{
function __construct() {
parent::__construct(‘my-fieldset’);
$this->add(
array(
‘name’ => ‘sex’,
‘type’ => ‘Zend\Form\Element\Select’,
‘options’ => array(
‘label’ => ‘Sex:’,
“value_options” => array(
“” => “- Choose your sex -“,
“0” => “Male”,
“1” => “Female”,
),
),
‘attributes’ => array(
‘style’ => ‘width: 350px’
)
)
);
$this->add(
array(
‘name’ => ‘name’,
‘options’ => array(
‘label’ => ‘Name:’
),
‘attributes’ => array(
‘class’ => ‘biggest’,
)
)
);
}
}
|
Repare que precisamos passar ao construtor da classe pai um nome para nosso FieldSet, que será usado para referenciar os seus campos no ValidationGroup do nosso formulário.
Vínculo com a Entidade
Conceitualmente, podemos dizer que a entidade é a representação de uma tabela do banco de dados em código PHP, por exemplo. Podemos também dizer que o FieldSet é a representação de uma entidade em campos para um formulário. E por fim, um formulário engloba o FieldSet que desejar.
E como relacionar nossa entidade ao nosso FieldSet? Simplesmente chamado o método setObject(Object) e passando como parâmetro uma instância da nossa entidade. Vamos supor que nossa entidade, posteriormente implementada, se chame MyEntity.
namespace Application\Form\Fieldset;
use Application\Entity;
use Zend\Form\Fieldset;
class MyFieldset extends Fieldset
{
function __construct() {
parent::__construct(‘my-fieldset’);
$this->setObject(new Entity\MyEntity);
$this->add(
array(
‘name’ => ‘sex’,
‘type’ => ‘Zend\Form\Element\Select’,
‘options’ => array(
‘label’ => ‘Sex:’,
“value_options” => array(
“” => “- Choose your sex -“,
“0” => “Male”,
“1” => “Female”,
),
),
‘attributes’ => array(
‘style’ => ‘width: 350px’
)
)
);
$this->add(
array(
‘name’ => ‘name’,
‘options’ => array(
‘label’ => ‘Name:’
),
‘attributes’ => array(
‘class’ => ‘biggest’,
)
)
);
}
}
|
A Entidade
Se você já estava usando uma entidade vinculada ao seu form, a única mudança será que ela passará a se vincular ao fieldSet e não mais ao form, como já visto acima. Agora se você não vinculou uma entidade diretamente em seu form, precisamos ter algumas mudanças esclarecidas.
A tal mudança será com a nossa classe entidade que passa a implementar a interface Zend\InputFilter\InputFilterAwareInterface e possuir um atributo que referenciará ao InputFilter, diferente do formulário que herda de Form, pois a classe pai já implementa e utiliza esse detalhes.
Como a entidade representa uma tabela do banco de dados, então é interessante que as restrições de cada coluna estejam representadas nela. Sendo assim, a nossa entidade de exemplo fica dessa maneira.
namespace Application\Entity;
use Zend\InputFilter\InputFilterAwareInterface;
public class MyEntity implements InputFilterAwareInterface{
private $name;
private $sex;
private $inputFilter;
//Getters and Setters..
/**
* Retrieve input filter
*
* @return InputFilterInterface
*/
public function getInputFilter()
{
$myValidator = new Validator\MyValidator();
if (null === $this->inputFilter) {
$this->inputFilter = new Factory()->createInputFilter(
array(
‘sex’ => array (
‘required’ => true,
‘validators’ => array(
$myValidator
)
),
‘name’ => array (
‘allow_empty’ => false,
‘validators’ => array(
array(‘name’ => ‘Application\Validator\Form\MyValidator’)
)
),
)
);
}
return $this->inputFilter;
}
}
|
Form
Com essas mudanças o form deixa de conter todos os campos, criamos um referencia ao nosso fieldSet, e o ValidationGroup da classe Form deixa de referenciar os campos a serem validados de modo direto, mas tendo o vetor do fieldSet indiretamente.
Lembrando que o botão de submissão ainda faz parte do contexto do formulário e não do fieldset, pois esse botão não é uma coluna do banco de dados e nem um atributo da entidade, e por isso ele permanecerá sendo adicionado pelo formulário. Abaixo segue como ficou o form.
namespace Application\Form;
use Zend\Form;
class MyForm extends Form {
function __contruct() {
$this->setAttribute(‘method’, ‘post’);
$this->setAttribute(‘id’, ‘my-form’);
$this->add(
array(
‘type’ => ‘Application\Form\Fieldset\MyFieldset’ ,
‘options’ => array(
‘use_as_base_fieldset’ => true
)
)
);
$this->add(
array(
‘name’ => ‘btnsubmit’,
‘options’ => array(
‘label’ => ‘Submit’
),
‘attributes’ => array(
‘class’ => ‘btn’,
‘type’ => ‘submit’
)
)
);
$this->add(
array(
‘type’ => ‘Zend\Form\Element\Csrf’,
‘name’ => ‘security’,
)
);
$this->setValidationGroup(
array(
‘security’,
‘my-fieldset’=> array (
‘sex’,
‘name’
)
)
);
}
}
|
Primeiro detalhe importante a se perceber é a indicação de qual fieldset será trabalhado nesse form através do método add(), antes usado apenas para inserir campos. Repare que no vetor de configuração estamos informando que o fieldset inserido será o fieldset base do formulário.
O segundo detalhe importante é no método setValidationGroup(), que recebe os campos do formulários, mas com intermédio do fieldset, ou seja, o nome dos campos estarão dentro do vetor do fieldset.
Bind
Depois de todas essas alterações e de definirmos consequentemente melhor o contexto de cada classe, fieldset com os campos, entity com as validações, form ligando todos em um único local e gerando grupos de validação, podemos realizar um trabalho mais limpo em um controller.
Depois dessa organização ainda é possível fazermos um vínculo entre uma entidade nossa e o formulário, mesmo que ela esteja vinculada ao fieldset base do form e não ao form diretamente. A diferença é que o formulário não encontrando um objeto vinculado a ele em suas configurações, ele irá procurar em seu base fieldset e usá-lo.
Então, com o uso de fieldsets ligados ao form, o método bind() espera a entidade relacionada ao fieldset base, não suportando vínculo com entidade dos possíveis demais fieldsets. E com esse vínculo, o form que fará a população dos atributos da entidade com os valores de seu campo caso o método isValid() retorne verdadeiro.
Segue abaixo um exemplo do procedimento de binding em um controller.
class ExampleController extends AbstractActionController
{
public function indexAction() {
$form = new Form\MyForm
$request = $this->getRequest();
if ($request->isPost()) {
$form->setData($request->getPost());
if ($form->isValid()) {
//Entidade populada com os campos do formulário.
}
}
return array(‘form’ => $form);
}
}
|
Outro detalhe é que quando nossa entidade possui valores em seus atributos, esse valores são adotados nos respectivos campos do formulário quando fazemos bind. Suponha que uma instância da nossa entidade receba no atributo $name o valor “Tássio” e no atributo $surname o valor “Auad”. Agora imagine que vamos vincular essa entidade ao formulário, e a consequência disso é o form tendo o campo com nome “name” preenchido pelo valor “Tássio” e o campo com nome “surname” preenchido com o valor “Auad”.
$entity = new Entity\MyEntity();
$entity->setName(“Tássio”);
$entity->setSurname(“Auad”);
$form = new Form\MyForm();
$form->bind($entity); //Formulário tendo os seus campos preenchidos
|
Atributos em camelCase
É importante lembra que o bind não considera que os atributos da entidade estejam em cameCase, mas sim com uso de “_” ou sem letras maiúsculas. O problema na verdade não está nos atributos, mas nos getters e setters que representam lidam com os atributos.
Um exemplo do que está sendo comentado é que um campo do formulário chamado fullName terá seu valor passado à entidade por meio do método getFullname(), desconsiderado a variação de maiúscula e minúscula do nome do campo.
setData()
O método para se popular um formulário com um vetor de valores é através do método setData(). Esse método relacionará as keys do vetor com nome dos campos e copiará os valores do vetor para os campos. Lembrando que o mesmo pode acontecer através do bind(), porém, não será com um vetor, mas com um objeto. Vamos ver um exemplo.
$form->setData(
array(
‘name’ => ‘Tássio’
‘surname’ => ‘Auad’
)
)
|
Quando exibirmos esse formulário em uma view, o campo name estará preenchido com o valor ‘Tássio’ e o campo surname, com o valor ‘Auad’.
isValid() e o InputFilter
Uma dúvida que pode ser gerada seria qual o InputFilter usado para a validação no método isValid() caso eu tenha um na entidade e outro no formulário? Qual tem a preferência? A resposta é a entidade.
Sempre que chamamos o método isValid() de um form, ele irá buscar na entidade em que foi feito o bind o seu InputFilter, caso não haja, ele irá procurar o InputFilter no Fieldset, e mesmo assim se não houver, ele irá instânciar um InputFilter.
Com o seu filter pronto, o método irá enviar a ele os valores dos campos e chamar o método isValid() do filter. Ou seja, esse método serve apenas para encapsular o processo de utilização ou criação de um InputFilter, passando a ele valores e verificando quais estão no ValidationGroup, e dos que estão, se todos são válidos.
Caso seja válido, a entidade que sofreu bind com o form será hidratada com os valores do formulário, e podemos assim, utilizá-la para inserir no banco de dados.