goncin@wordpress.com:~$ _

Linux, programação e toda sorte de nerdices

Gerador de formulários HTML em PHP orientado a objetos (parte 1)

Os frameworks para desenvolvimento Web nunca estiveram tão em voga quanto atualmente. De fato, utilizá-los é bem melhor do que reinventar a roda, apesar do esforço despendido no apendizado no aprendizado da ferramenta.

O código fonte de seu framework favorito é uma fonte abundante de técnicas de programação. Não hesite em vasculhá-lo. Foi olhando um desses códigos (no caso, o do Yii Framework), que cogitei em escrever um gerador de formulários HTML, como prova de conceito.

Mas, para quê reimplementar, se já está no framework? No meu caso, pelo próprio aprendizado, para dominar técnicas novas. E, diga-se de passagem, não sou o único a fazer esse tipo de coisa. Obviamente, essas experimentações jamais entrarão em produção. A não ser que, é claro, fiquem tão boas e estáveis que se tornem um novo framework. 😛

A ideia por detrás do gerador de formulários é bastante simples: a partir de uma conjunto de objetos, cada qual representando um controle de formulário e com propriedades corretamente configuradas, obter código o HTML válido correspondente. Não sei quando a vocês, mas acho digitar HTML um porre (abre tag, põe id, configura atributos, etc., etc. e, no fim, acaba esquecendo de fechar a tag). Por que não, então, escrever esses formulários em puro PHP, e deixá-lo gerar o chato do HTML?

De acordo? Vamos lá.

Comecei codificando uma classe genérica e abstrata, para servir de base a todos os controles, chamada GFormControl. Segue o código abaixo:

<?php
  abstract class GFormControl {

    private $_id;                         // ID do campo de formulário
    private $_label;                      // Texto descritivo do campo
    private $_attributes;                 // Outros atributos do campo
    private $_labelSeparator = '
';  // Separador entre o "label" e o campo

    public function  __construct($id, $label = '', $attributes = array()) {
      $this->setId($id);
      $this->setLabel($label);
      $this->setAttributes($attributes);
    }

    /********** SETTERS **********/

    protected function setId($value) {
      $this->_id = $value;
      return $this;
    }

    public function setLabel($value) {
      $this->_label = $value;
      return $this;
    }

    public function setLabelSeparator($value) {
      $this->_labelSeparator = $value;
      return $this;
    }

    public function setAttributes($value) {
      $this->_attributes = $value;
      return $this;
    }

    /********** GETTERS **********/

    public function getId() {
      return $this->_id;
    }

    public function getLabel() {
      return $this->_label;
    }

    abstract protected function getOpenTag();

    abstract protected function getCloseTag();

    public function getLabelSeparator() {
      return $this->_labelSeparator;
    }

    public function getAttributes() {
      return $this->_attributes;
    }

    /***** MÉTODOS PROTEGIDOS *****/

    // Rendereiza a abertura da DIV
    protected function renderOpenDiv() {
      return "<div>\n\t";
    }

    // Renderiza o LABEL que contém a descrição do campo
    protected function renderLabel($labelSeparator = null) {

      if (! is_null($labelSeparator))
        $this->setLabelSeparator($labelSeparator);

      return "<label for="{$this->getId()}">{$this->getLabel()}</label>{$this->getLabelSeparator()}\n\t";

    }

    // Renderiza o campo de formulário propriamente dito
    protected function renderField() {

      $html = $this->getOpenTag() . " id='{$this->getId()}' ";
      $html .= $this->renderFieldAttributes();
      $html .= $this->getCloseTag();

      return $html;

    }

    // Renderiza os atributos do campo de formulário
    protected function renderFieldAttributes() {
      $html = '';

      if ($attrs = $this->getAttributes())
        foreach($attrs as $attr => $value)
          $html .= "{$attr}='{$value}' ";

      return $html;
    }

    // Renderiza o fechamento da DIV
    protected function renderCloseDiv() {
      return "\n</div>\n";
    }

    /***** MÉTODOS PÚBLICOS */

    /*
     * Renderiza o campo de formulário, de acordo com os parâmetros configurados.
     * Para tanto, efetua chamadas aos métodos protegidos responsáveis pela
     * renderização das diferentes partes.
     * Cada campo será renderizado da seguinte forma:
     *
     *
<div>
     *   <label for="nome_campo">Descrição do campo</label>
     *
     *</div>
     */

    public function render($labelSeparator = null) {

      $html = $this->renderOpenDiv();
      $html .= $this->renderLabel($labelSeparator);
      $html .= $this->renderField();
      $html .= $this->renderCloseDiv();

      return $html;

    }

  }

Por que uma classa abstrata? Simples: não desejo criar instâncias dessa classe. Sua razão de ser é servir como ancestral para as classes de cada controle de formulário. Além da própria classe, os métodos protegidos getOpenTag() e getCloseTag() estão também marcados como abstract. Isso significa que qualquer classe que estenda GFormControl fica obrigada a implementar esses dois métodos.

O único método público da classe (desconsiderando-se os getters) é o render(), responsável por gerar o código HTML correspondente ao campo de formulário. Esse método faz chamadas a outros métodos, cada qual responsável por renderizar uma parte diferente do campo. Os métodos renderXXX() foram definidos como protegidos – isso significa que são visíveis somente pelas classes descendentes, pois não foram projetadas para serem chamados pelo usuário final.

Em suma, esta classe define muita coisa, mas não faz nada. E agora? Veja como ficou o código de quatro controles:

<?php
  // Campo de texto
  class GTextBox extends GFormControl {

    protected function getOpenTag() {
      return "<input type="text" />';
    }

  }

  /***********************************************************/

  // Campo de senha
  class GPassword extends GFormControl {

    protected function getOpenTag() {
      return "<input type="password" />';
    }

  }

  /***********************************************************/

  // Caixa de verificação
  class GCheckBox extends GFormControl {

    protected function getOpenTag() {
      return "<input type="checkbox" />';
    }

  }

  /***********************************************************/

  // Botão de enviar
  class GSubmit extends GFormControl {

    protected function getOpenTag() {
      return "<input type="submit" />';
    }

    /* No caso do botão de enviar, foi necessária uma sobrecarga (override)
     * dos métodos renderField() e render(), porque o controle não tem um
     * LABEL associado a ele, e o texto que iria no LABEL vai no atributo
     * VALUE do próprio controle.
     */

    protected function renderField() {
      $html = $this->getOpenTag() . " id='{$this->getId()}' value='{$this->getLabel()}' ";
      $html .= $this->renderFieldAttributes();
      $html .= $this->getCloseTag();

      return $html;
    }

    public function render() {
      $html = $this->renderOpenDiv();
      $html .= $this->renderField();
      $html .= $this->renderCloseDiv();

      return $html;

    }
  }

?>

Nos casos dos controles de caixa de texto, caixa de senha e caixa de verificação, basta implementar os métodos getOpenTag() e getCloseTag(), para que tudo o que foi definido no ancestral funcione apropriadamente. No caso do botão de enviar, foram necessárias algumas modificações nos métodos definidos na classe base, pois seu comportamento foge um pouco ao padrão (vide comentários no código).

Isso feito, podemos escrever um formulário simples de login da seguinte forma:

<?php   $usuario = new GTextBox('usuario', 'Usuário:', array('maxlength'=-->50, 'size'=>15));
  $senha = new GPassword('senha', 'Senha:', array('maxlength'=>50, 'size'=>15));
  $guardarInfo = new GCheckBox('guardar_info', 'Guardar informações?');
  $enviar = new GSubmit('enviar', 'Enviar');

  echo $usuario->render();
  echo $senha->render();
  echo $guardarInfo->render(' ');
  echo $enviar->render();

?>

O código HTML gerado, por seu turno, é o seguinte:

<div>
	<label for="usuario">Usuário:</label>

	<input id="usuario" maxlength="50" size="15" type="text" /></div>
<div>
	<label for="senha">Senha:</label>

	<input id="senha" maxlength="50" size="15" type="password" /></div>
<div>
	<label for="guardar_info">Guardar informações?</label>

	<input id="guardar_info" type="checkbox" /></div>
<div>
	<input id="enviar" type="submit" value="Enviar" /></div>
Aparência do formulário gerado

Aparência do formulário gerado

Acho que devo parabenizá-lo por ter chegado até aqui na leitura deste post. E, se entendeu o que eu quis fazer, parabéns duplos!!

Todavia, o trabalho está bem longe de terminar. Falta escrever os demais controles de formulário e, se você notou bem, o código sequer gera a tag <form>. Isso é tarefa para as próximas partes. 😉

Anúncios

17 Respostas para “Gerador de formulários HTML em PHP orientado a objetos (parte 1)

  1. André Luis 05/07/2010 às 11\1114

    parabens pela iniciativa, pois é assim que se aprende e se consegue crescer na vida, estudando o código de outros e adaptando-os a nossas necessidades, implementando melhorias nos mesmos.

  2. Pingback: Tweets that mention Gerador de formulários HTML em PHP orientado a objetos (parte 1) « goncin@wordpress.com:~$ _ -- Topsy.com

  3. Marcos Borges 05/07/2010 às 14\0233

    Não seria legal ao invés de concatenar html pra gerar seus componentes html utilizar o um objeto DOM ?

    • goncin 05/07/2010 às 14\0243

      Olá, Marcos!

      Temo não ter entendido corretamente seu questionamento. Você quis dizer um objeto DOM que repressentasse todo o documento, e nele serem inseridos os controles? É isso mesmo?

      Um abraço, e obrigado pelo comentário.

  4. André D. Molin 06/07/2010 às 18\0645

    Muito bom goncin. Concordo plenamente com o que disse. Os frameworks são legais, quebram o maior galho, na maioria das vezes aumentam a produtividade, mas escrever o proprio framework mesmo que inicialmente não focado para produção é um grande poço de aprendizado.

    Apoio totalmente!

    Abraços!

  5. Phillip 16/02/2011 às 18\0615

    goncin, boa tarde, espero que vc ainda esteja vendo esses post. olha eu gostei muito da sua proposta, estou querendo algo parecido, porém estou com algumas dúvidas.
    Em uma formulário grande onde teria várias inputs e outros elementos com propriedades diferentes repetidos, isse código funcionaria??
    Desde já agradeço.

  6. Leandro 17/02/2011 às 08\0839

    Há algum tempo atrás descobri o Smarty. Ótimo, mas ele trabalha apenas com os templates.
    Recentemente descobri o Lumine, uma classe de abstração para banco de dados e isso já ajudou
    bastante na velocidade do desenvolvimento das aplicações aqui na agência onde eu trabalho.
    A idéia de uma classe para gerar formulários é ótima. Eu sempre esqueço tags abertas e/ou desorganizadas, que acabam “ferrando” com o visual do site.
    Quando terminei de ler esse post, pensei imediatamente em utilizar essa classe em conjunto com as tecnologias citadas acima. O que acham?

    • goncin 17/02/2011 às 08\0847

      Leandro,

      Fico satisfeito ao ver alguém disposto a usar seriamente algo que eu escrevi de forma experimental. Vá em frente, mas talvez você terá que completar as minhas classes para que elas se tornem realmente utilizáveis.

      Considere também aprender e utilizar um framework, que traz muitas ferramentas prontas, incluindo abstração de bancos de dados e geração de formulários. Recomendo o Yii Framework.

      Obrigado pela visita e pelo comentário! 🙂

  7. Anilton Brandãp 29/02/2012 às 06\0654

    Bom dia.

    Muito interressante.

    Mais gostaria ver isso no pacote a funcionar , porque não consegui fazer funcionar completamente. gerou o form , ficou a mostrar as funções getLabel e campos de form.

    • goncin 29/02/2012 às 09\0953

      Anilton,

      O propósito do artigo é didático. Mesmo assim, foi testado e funcionou. Verifique se copiou o código corretamente.

      • Anilton Brandãp 29/02/2012 às 09\0958

        Em primeiro lugar muito obrigado pela disponibilidade.
        este é código gerado
        e mostra no ecra…..
        Utilizador: id=’p_utilizador’ ‘maxlength’=’100’ ‘size’=’100′
        Senha: id=’p_senha’ ‘maxlength’=’50’ ‘size’=’150′
        Tipo de Acesso:
        Guardar informações? id=’p_guardar_info’
        id=’p_enviar’ value=’Enviar’

  8. Um pretenso POOta programador 30/05/2012 às 16\0409

    Parabéns, amigo!!!
    É com iniciativas como essa que o desenvolvimento web vai ficando mais padronizado.
    Você me ajudou muito, pois estava com dificuldades em demonstrar no meu tcc, em UML, a minha GUI.
    E quanto ao seu código se tornar um framework, pode ter certeza, pelo meno comigo pode considerá-lo como tal.
    Não desista e lembre-se: “Quase todos os padrões um dia foram gabiarra”.
    Vlw

    • goncin 30/05/2012 às 16\0418

      Opa, obrigado. Mas esse exemplo não tem pretensões de se tornar um framework, mesmo porque há muitos ótimos por aí, como o Yii Framework. Mas, como acredito que de nada adianta usar um framework sem ter um mínimo de ideia de seu funcionamento interno, escrevi este post com a finalidade de ajudar nesse entendimento. Se foi útil para você, já me sinto satisfeito 🙂

  9. michael villander 14/02/2013 às 12\1249

    Gonci, tudo bom?
    Deixe te perguntar nessa classe de controles dos campos, ele da erro na hora de extender a classe, pois diz que é abstrata, e precisa completar com o método Opendiv?

    Como voce disse que tava funcionando, quero tirar essa duvida.

    Abraço

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s

%d blogueiros gostam disto: