Por trás do site IngressoPrático: O formulário de cadastro - parte 3

Last update on 25 de Julho de 2013.

Você está lendo a parte três de Por trás do site IngressoPrático, uma série em quatro posts que mostra como nós usamos o Symfony2 para desenvolver o site.

Na parte 1 dessa série, eu disse que um dos requisitos do projeto era de que haveria um período de venda de ingressos que limitaria a quantidade de ingressos que um usuário poderia comprar. Esse período de vendas com cota é definido arbitrariamente pelos administradores do site. Cada evento tem um elenco formado pelos alunos da escola. A cota é definida durante a definição do elenco para cada evento e para cada participante (aluno). Um participante é criado quando o pai (ou responsável legal) cadastra-se no site pelo formulário de cadastro:

signup-form-en.png

Os trechos de código e diagramas de classe foram simplificados com o objetivo de ilustrar como nós organizamos o código para customizar o formulário de cadastro. Eu adicionei referências onde você encontrará informações mais detalhadas sobre um determinado assunto.

FOSUserBundle

Para não reinventar a roda, nós usamos o bundle FOSUserBundle que provê um framework flexível para o gerenciamento de usuários. Basicamente nós sobrescrevemos a classe FOS\UserBundle\Controller\RegistrationController e empacotamos a classe FOS\UserBundle\Form\Type\RegistrationFormType na nossa classe RegistrationFormType customizada.

Por conveniência, nós iremos usar o FQCN para as classes do FOSUserBundle. Classes sem o FQCN são implicitamente do namespace IngressoPratico, exceto FormBuilder (Symfony\Component\Form\FormBuilder), FormFactory (Symfony\Component\Form\FormFactory) e FormType (sempre referindo como uma implementação de Symfony\Component\Form\FormTypeInterface).

As entidades e os formulários

Ao invés de adicionar os atributos na nossa classe User, nós escolhemos criar uma classe Profile que possui todos os atributos adicionais como endereço, nome completo e os nomes dos filhos (Participant):

class-diagram_1.png

As entidades para as classes acima:

Note que o método User::setUsername() lança uma exceção. Nós removemos o campo username do formulário de cadastro. O atributo username terá o mesmo valor que o atributo email já que queríamos que toda a autenticação fosse feita apenas pelo endereço de e-mail. No final, terminamos com as seguintes classes: ParticipantFormType, ProfileFormType e RegistrationFormType (todas subclasses de FormType). RegistrationFormType empacota FOS\UserBundle\Form\Type\RegistrationFormType e ProfileFormType empacota ParticipantFormType:

Para usar a nossa classe RegistrationFormType ao invés da classe do FOSUserBundle, definimos o formulário como um serviço (ao qual será consumido por RegistrationController encontrado no final desse post):

Dê uma olhada atenta na definição de serviço user.registration.form.type:

Nós definimos um serviço que usa o FormFactory para retornar um FormBuilder para a classe FOS\UserBundle\Form\Type\RegistrationFormType ao qual é o primeiro argumento do construtor para do nosso serviço user.registration.form.type (FOSUserBundle especificava um serviço semelhante até a revisão fccde65b7):

Note que esta é uma aproximação diferente da que está documentada na documentação do FOSUserBundle. A documentação do Symfony2 tem um cookbook descrevendo como usar um Factory para criar serviços.

Adicionando campos extras usando JavaScript

FormType collection tem uma forma conveniente que permite que você adicione campos adicionais dinamicamente com JavaScript. Em ProfileFormType nós definimos que é possível adicionar novos campos ou remover campos existentes para o campo children (ParticipantFormType):

Aqui nós divergimos um pouco da forma sugerida para adicionar/remover campos com JavaScript que está documentado na documentação do FormType collection. Ao invés de usar o atributo prototype, nosso JavaScript copia um container div inteiro que contém o campo:

Fallback para browsers com JavaScript desabilitado

O código acima funciona bem para browsers com JavaScript habilitado mas quebra em browsers que não suportam JavaScript (como o test client do Symfony2). Como nós usamos TDD, já tínhamos o formulário funcional para browsers não-JavaScript e apenas adicionamos o JavaScript após ter o formulário de cadastro totalmente testado com testes de unidade e testes funcionais. Essa prática se chama melhoria progressiva é não é algo difícil de seguir quando você desenvolve uma aplicação usando um framework como o Symfony2.

O resultado é que o nosso RegistrationFormHandler é preparado para a possibilidade de que campos children extras (Participant) podem ser adicionados ou removidos. Se o formulário não for válido ou se a requisição for para adicionar ou remover um child, o manuaseador irá retornar false, o controller não irá agir (p.e., não irá enviar uma mensagem para o modelo agir), irá apenas exibir o formulário populado e a mensagem de erro para o usuário.

Criar manuseadores de formulários dessa forma é uma boa prática para tornar os controllers magros. Finalmente, nós sobrescrevemos o action do controller de cadastro do FOSUserBundle:

A documentação do FOSUserBundle explica como sobrescrever um controller.

Próximo artigo

Artigo anterior

Artigos semelhantes


blog comments powered by Disqus

Pingbacks

Pingbacks estão abertas.