Back-End

24 nov, 2010

Classe genérica para formulários – Parte 03 Final

Publicidade

Neste último artigo sobre a construção de classes para geração de formulários através de código orientado ao objeto, iremos abordar as classes dos elementos option, optgroup, select e fieldset.

Vamos realizar também algumas melhorias em nosso código e adicionar a classe abstrata elementComposite (elemento composto).

Confira os artigos anteriores e acompanhe o conteúdo desta terceira e última parte:

Melhorias no código

Antes de continuarmos, vamos fazer algumas pequenas melhorias em nosso código. Primeiro no método element. Vamos retirar a função trim() e alterar a posição do espaço em branco, da direita para esquerda, na seguinte linha:

De:
$elementAttributes .= "{$attribute->attribute}=\"{$attribute->value}\" ";
Para:
$elementAttributes .= " {$attribute->attribute}=\"{$attribute->value}\"";

Nos métodos input, textarea e label, vamos retirar o espaço a frente da abertura da tag. Pronto! Segue o código com as alterações.

abstract class commons {
 function __set( $property,$val ) {
  $this->$property = $val;
 }
 
 function __get( $property ) {
  return $this->$property;
 }
}
 
final class attribute extends commons {
 protected $attribute;
 protected $value;
 
 function __construct( $attribute,$value ) {
  $this->attribute = $attribute;
  $this->value = $value;
 }
}
 
abstract class element extends commons {
 protected $attributes;
 
 final public function addAttribute( attribute $attribute ) {
  $this->attributes[] = $attribute;
 }
 
 final public function hasAttributes() {
  return count( $this->attributes ) > 0;
 }
 
 final public function getAttributes() {  
  $elementAttributes = '';   
  foreach( $this->attributes as $attribute ) {
   $elementAttributes .= " {$attribute->attribute}=\"{$attribute->value}\"";
  }
  return $elementAttributes;
 }
 
 abstract function draw();  
}
 
class input extends element {
 
 public function draw() {
 
  if( parent::hasAttributes() ) {
   $input = '<input ';
   $input .= parent::getAttributes();
   $input .= ' />' . PHP_EOL;
   return $input;
  }
 }
}
 
class textarea extends element {
 
 protected $value;
 
 function __construct( $value ) {
  $this->value = $value;
 }
 
 public function draw() {  
 
  $textarea = '<textarea ';
  if( parent::hasAttributes() ) { $textarea .= parent::getAttributes(); }
  $textarea .= '>';   
  if( !empty( $this->value ) ) { $textarea .= $this->value; }
  $textarea .= '</textarea>' . PHP_EOL;
  return $textarea;  
 }
}
 
class label extends element {
 protected $value;
 
 function __construct( $value ) {
  $this->value = $value;
 }
 
 public function draw() {  
 
  $label = '<label ';
  if( parent::hasAttributes() ) { $label .= parent::getAttributes(); }
  $label .= '>';   
  if( !empty( $this->value ) ) { $label .= $this->value; }
  $label .= '</label>' . PHP_EOL;
  return $label;  
 }
}

Note que o método form não está incluso, isso porque no último artigo criamos o mesmo apenas para realizar os testes das classes. Neste artigo, reescreveremos esse método com várias alterações.

Class elementComposite

Como explicado no final do artigo passado, alguns elementos possuem uma estrutura que agrega elementos adicionais, como o caso de form, fieldset, optgroup e select.

Todos esses elementos deverão agregar uma ou mais instâncias de outros objetos. Para isso, devemos criar uma classe abstrata para auxiliar esses elementos nessa tarefa.

A classe elementComposite é bem parecida com a element, mas em vez de agregar instâncias de attribute, agregará instâncias de qualquer objeto que seja herdeiro de element.

A lógica é a mesma: addElement, hasElements e getElements têm as funcionalidades de adicionar, verificar existência e resgatar instâncias. Abaixo o código da classe:

abstract class elementComposite extends element {
 protected $elements;
 
 final public function hasElements() {
  return count( $this->elements ) > 0;
 }
 
 final public function addElement( element $element ) {
  $this->elements[] = $element;
 }
 
 final public function getElements() {
  $out = '';
  foreach( $this->elements as $element ) {
   $out .= $element->draw();
  }
  return $out;
 }
}

Class form

Agora com a classe elementComposite criada fica fácil reescrever a classe form. Veja o resultado:

class form extends elementComposite {  
 function draw() {  
  if( parent::hasElements() ) {
   $form = '<form ';
   $form .= parent::getAttributes();
   $form .= '>' . PHP_EOL;
   $form .= parent::getElements();   
   $form .= '</form>';
   return $form;
  } 
 }
}

Class select e optgroup

A estrutura aplicada na classe form se repete nas classes select e optgroup:

class optgroup extends elementComposite {
 public function draw() {
  if( parent::hasElements() ) {
   $optgroup = '<optgroup ';
   if( parent::hasAttributes() ) { $optgroup .= parent::getAttributes(); }
   $optgroup .= '>' . PHP_EOL;
   $optgroup .= parent::getElements();
   $optgroup .= '</optgroup>' . PHP_EOL;
   return $optgroup;
  }
 }
}
 
class select extends elementComposite {  
 public function draw() {
  if( parent::hasElements() ) {
   $select = '<select ';
   if( parent::hasAttributes() ) { $select .= parent::getAttributes(); }
   $select .= '>' . PHP_EOL;
   $select .= parent::getElements();
   $select .= '</select>' . PHP_EOL;
   return $select;
  }
 }
}

Class fieldset

Vamos acrescentar à classe fieldset dois métodos auxiliares além do método draw() aplicado nas demais, construtor __construct() e setLegend. Ambos serão responsáveis por adicionar uma instância do objeto legend na variável legend da classe:

class fieldset extends elementComposite {
 protected $legend;
 
 function __construct( $legend ) {
  if( isset( $legend ) and !empty( $legend ) ) {
   $this->setLegend( $legend );
  }
 }
 
 final public function setLegend( legend $legend ) {
  $this->legend = $legend;
 }
 
 public function draw() {
  if( parent::hasElements() ) {   
   $fieldset = '<fieldset ';
   $fieldset .= parent::getAttributes();
   $fieldset .= '>' . PHP_EOL;
   if( is_object( $this->legend ) ) { $fieldset .= $this->legend->draw(); }
   $fieldset .= parent::getElements();
   $fieldset .= '</fieldset>' . PHP_EOL;
   return $fieldset;
  }
 }
}

Class option e legend

Para finalizar, criaremos a classe option e legend, ambas estendidas de element. O padrão é o mesmo seguido nas classes anteriores:

class option extends element {
 
 protected $label;
 
 function __construct( $label ) {
  $this->label = $label;
 }
 
 public function draw() {
  if( !empty( $this->label ) ) {
   $option = '<option ';
   if( parent::hasAttributes() ) { $option .= parent::getAttributes(); }
   $option .= '>';
   $option .= $this->label;
   $option .= '</option>' . PHP_EOL;   
   return $option;
  }
 }
}
 
class legend extends element {
 
 protected $value;
 
 function __construct( $value ) {
  $this->value = $value;
 }
 
 public function draw() {
  if( !empty( $this->value ) ) {
   $legend = '<legend ';
   if( parent::hasAttributes() ) { $legend .= parent::getAttributes(); }
   $legend .= '>';
   $legend .= $this->value;
   $legend .= '</legend>' . PHP_EOL;   
   return $legend;
  }
 }
}

Segue o código completo:

abstract class commons {
 function __set( $property,$val ) {
  $this->$property = $val;
 }
 
 function __get( $property ) {
  return $this->$property;
 }
}
 
final class attribute extends commons {
 protected $attribute;
 protected $value;
 
 function __construct( $attribute,$value ) {
  $this->attribute = $attribute;
  $this->value = $value;
 }
}
 
abstract class element extends commons {
 protected $attributes;
 
 final public function addAttribute( attribute $attribute ) {
  $this->attributes[] = $attribute;
 }
 
 final public function hasAttributes() {
  return count( $this->attributes ) > 0;
 }
 
 final public function getAttributes() {  
  $elementAttributes = '';   
  foreach( $this->attributes as $attribute ) {
   $elementAttributes .= " {$attribute->attribute}=\"{$attribute->value}\"";
  }
  return $elementAttributes;
 }
 
 abstract function draw();  
}
 
abstract class elementComposite extends element {
 protected $elements;
 
 final public function hasElements() {
  return count( $this->elements ) > 0;
 }
 
 final public function addElement( element $element ) {
  $this->elements[] = $element;
 }
 
 final public function getElements() {
  $out = '';
  foreach( $this->elements as $element ) {
   $out .= $element->draw();
  }
  return $out;
 }
}
 
class input extends element {
 
 public function draw() {
 
  if( parent::hasAttributes() ) {
   $input = '<input ';
   $input .= parent::getAttributes();
   $input .= ' />' . PHP_EOL;
   return $input;
  }
 }
}
 
class textarea extends element {
 
 protected $value;
 
 function __construct( $value ) {
  $this->value = $value;
 }
 
 public function draw() {  
 
  $textarea = '<textarea ';
  if( parent::hasAttributes() ) { $textarea .= parent::getAttributes(); }
  $textarea .= '>';   
  if( !empty( $this->value ) ) { $textarea .= $this->value; }
  $textarea .= '</textarea>' . PHP_EOL;
  return $textarea;  
 }
}
 
class option extends element {
 
 protected $label;
 
 function __construct( $label ) {
  $this->label = $label;
 }
 
 public function draw() {
  if( !empty( $this->label ) ) {
   $option = '<option ';
   if( parent::hasAttributes() ) { $option .= parent::getAttributes(); }
   $option .= '>';
   $option .= $this->label;
   $option .= '</option>' . PHP_EOL;   
   return $option;
  }
 }
}
 
class label extends element {
 protected $value;
 
 function __construct( $value ) {
  $this->value = $value;
 }
 
 public function draw() {  
 
  $label = '<label ';
  if( parent::hasAttributes() ) { $label .= parent::getAttributes(); }
  $label .= '>';   
  if( !empty( $this->value ) ) { $label .= $this->value; }
  $label .= '</label>' . PHP_EOL;
  return $label;  
 }
}
 
class legend extends element {
 
 protected $value;
 
 function __construct( $value ) {
  $this->value = $value;
 }
 
 public function draw() {
  if( !empty( $this->value ) ) {
   $legend = '<legend ';
   if( parent::hasAttributes() ) { $legend .= parent::getAttributes(); }
   $legend .= '>';
   $legend .= $this->value;
   $legend .= '</legend>' . PHP_EOL;   
   return $legend;
  }
 }
}
 
class optgroup extends elementComposite {
 public function draw() {
  if( parent::hasElements() ) {
   $optgroup = '<optgroup ';
   if( parent::hasAttributes() ) { $optgroup .= parent::getAttributes(); }
   $optgroup .= '>' . PHP_EOL;
   $optgroup .= parent::getElements();
   $optgroup .= '</optgroup>' . PHP_EOL;
   return $optgroup;
  }
 }
}
 
class select extends elementComposite {  
 public function draw() {
  if( parent::hasElements() ) {
   $select = '<select ';
   if( parent::hasAttributes() ) { $select .= parent::getAttributes(); }
   $select .= '>' . PHP_EOL;
   $select .= parent::getElements();
   $select .= '</select>' . PHP_EOL;
   return $select;
  }
 }
}
 
class fieldset extends elementComposite {
 protected $legend;
 
 function __construct( $legend ) {
  if( isset( $legend ) and !empty( $legend ) ) {
   $this->setLegend( $legend );
  }
 }
 
 final public function setLegend( legend $legend ) {
  $this->legend = $legend;
 }
 
 public function draw() {
  if( parent::hasElements() ) {   
   $fieldset = '<fieldset ';
   $fieldset .= parent::getAttributes();
   $fieldset .= '>' . PHP_EOL;
   if( is_object( $this->legend ) ) { $fieldset .= $this->legend->draw(); }
   $fieldset .= parent::getElements();
   $fieldset .= '</fieldset>' . PHP_EOL;
   return $fieldset;
  }
 }
}
 
class form extends elementComposite {  
 function draw() {  
  if( parent::hasElements() ) {
   $form = '<form ';
   $form .= parent::getAttributes();
   $form .= '>' . PHP_EOL;
   $form .= parent::getElements();   
   $form .= '</form>';
   return $form;
  } 
 }
}

Demonstração:
Para finalizar o artigo, vamos simular alguns casos para observar a utilização das classes criadas:

01. Formulário simples:
Carlos será nosso personagem, ele quer exibir um formulário simples em seu site. Esse formulário deve conter:

Campo input text e label do mesmo
Campo textarea e label do mesmo
Botão de submissão

Segue o código:

$labelInput = new label( 'label input' );
$labelInput->addAttribute( new attribute( 'for','meuinput' ) );
 
$input = new input;
$input->addAttribute( new attribute( 'type','text' ) );
$input->addAttribute( new attribute( 'name','meuinput' ) );
$input->addAttribute( new attribute( 'id','meuinput' ) );
 
$labelTextarea = new label( 'label textarea' );
$labelTextarea->addAttribute( new attribute( 'for','minhatextarea' ) );
 
$textarea = new textarea;
$textarea->addAttribute( new attribute( 'name','minhatextarea' ) );
$textarea->addAttribute( new attribute( 'id','minhatextarea' ) );
 
$submit = new input;
$submit->addAttribute( new attribute( 'type','submit' ) );
$submit->addAttribute( new attribute( 'value','enviar' ) );
 
$form = new form;
$form->addElement( $labelInput );
$form->addElement( $input );
$form->addElement( $labelTextarea );
$form->addElement( $textarea );
$form->addElement( $submit );
print $form->draw();

02. Select dinâmico
Agora Carlos deseja popular um select com dados vindos do banco de dados. Segue o código:

#array simulando retorno de dados do db
$arrQuery = array(
 array( 'arroz',1 ),
 array( 'feijao',2 ),
 array( 'bife',3 )
);
 
 
$select = new select;
$select->addAttribute( new attribute( 'name','myselect' ) );
 
foreach( $arrQuery as $val ) {
 $option = new option( $val[0] );
 $option->addAttribute( new attribute( 'value',$val[1] ) );
 $select->addElement( $option ); 
}
 
$form = new form;
$form->addElement( $select );
print $form->draw();

03. Utilizando todas classes
Carlos dessa vez deseja utilizar todas as classes criadas. Segue o código:

$labelInput = new label( 'label input' );
$labelInput->addAttribute( new attribute( 'for','meuinput' ) );
 
$input = new input;
$input->addAttribute( new attribute( 'type','text' ) );
$input->addAttribute( new attribute( 'name','meuinput' ) );
$input->addAttribute( new attribute( 'id','meuinput' ) );
 
$labelTextarea = new label( 'label textarea' );
$labelTextarea->addAttribute( new attribute( 'for','minhatextarea' ) );
 
$textarea = new textarea;
$textarea->addAttribute( new attribute( 'name','minhatextarea' ) );
$textarea->addAttribute( new attribute( 'id','minhatextarea' ) );
 
$labelSelect = new label( 'label select' );
$labelSelect->addAttribute( new attribute( 'for','meuselect' ) );
 
$select = new select;
$select->addAttribute( new attribute( 'name','meuselect' ) );
$select->addAttribute( new attribute( 'id','meuselect' ) );
 
$option1 = new option( 'option1' );
$option2 = new option( 'option2' );
$option3 = new option( 'option3' );
 
$option1->addAttribute( new attribute( 'value',1 ) );
$option2->addAttribute( new attribute( 'value',2 ) );
$option2->addAttribute( new attribute( 'selected','selected' ) );
$option3->addAttribute( new attribute( 'value',3 ) );
 
$optgroup = new optgroup;
$optgroup->addAttribute( new attribute( 'label','option 1 e 2' ) );
$optgroup->addElement( $option1 );
$optgroup->addElement( $option2 );
 
$select->addElement( $optgroup );
$select->addElement( $option3 );
 
$submit = new input;
$submit->addAttribute( new attribute( 'type','submit' ) );
$submit->addAttribute( new attribute( 'value','enviar' ) );
 
$legend = new legend( 'meu formulário' );
 
$fieldset = new fieldset;
$fieldset->setLegend( $legend );
$fieldset->addElement( $labelInput );
$fieldset->addElement( $input );
$fieldset->addElement( $labelTextarea );
$fieldset->addElement( $textarea );
$fieldset->addElement( $labelSelect );
$fieldset->addElement( $select );
$fieldset->addElement( $submit );
 
$form = new form;
$form->addAttribute( new attribute( 'name','meuform' ) );
$form->addElement( $fieldset );
print $form->draw();

Chegamos ao fim desta série. Espero ter colaborado e/ou auxiliado vocês!

Vou deixar o código da classe implementada para suportar multiplas adições de atributos e de elementos:

abstract class commons {
 function __set( $property,$val ) {
  $this->$property = $val;
 }
 
 function __get( $property ) {
  return $this->$property;
 }
}
 
final class attribute extends commons {
 protected $attribute;
 protected $value;
 
 function __construct( $attribute,$value ) {
  $this->attribute = $attribute;
  $this->value = $value;
 }
}
 
abstract class element extends commons {
 protected $attributes;
 
 final public function addAttribute( $attribute ) {
 
  if( is_object( $attribute ) and $attribute instanceof attribute ) {
   $this->attributes[] = $attribute;
  } elseif( is_array( $attribute ) ) {   
   foreach( $attribute as $obj ) {    
    if( is_object( $obj ) and $obj instanceof attribute ) {    
     $this->attributes[] = $obj;
    }
   }
  }
 }
 
 final public function hasAttributes() {
  return count( $this->attributes ) > 0;
 }
 
 final public function getAttributes() {
  if( $this->hasAttributes() ) {  
   $elementAttributes = '';   
   foreach( $this->attributes as $attribute ) {
    $elementAttributes .= " {$attribute->attribute}=\"{$attribute->value}\"";
   }
   return $elementAttributes;
  }
 }
 
 abstract function draw();  
}
 
abstract class elementComposite extends element {
 protected $elements;
 
 final public function hasElements() {
  return count( $this->elements ) > 0;
 }
 
 final public function addElement( $element ) {
 
  if( is_object( $element ) and is_subclass_of( $element,'element' ) ) {
   $this->elements[] = $element;
  } elseif( is_array( $element ) ) {   
   foreach( $element as $obj ) {    
    if( is_object( $obj ) and is_subclass_of( $obj,'element' ) ) {     
     $this->elements[] = $obj;
    }
   }
  }
 }
 
 final public function getElements() {  
  if( $this->hasElements() ) {  
   $out = '';
   foreach( $this->elements as $element ) {
    $out .= $element->draw();
   }
   return $out;
  }
 }
}
 
class input extends element {
 
 public function draw() {
 
  if( parent::hasAttributes() ) {
   $input = '<input ';
   $input .= parent::getAttributes();
   $input .= ' />' . PHP_EOL;
   return $input;
  }
 }
}
 
class textarea extends element {
 
 protected $value;
 
 function __construct( $value ) {
  $this->value = $value;
 }
 
 public function draw() {  
 
  $textarea = '<textarea ';
  if( parent::hasAttributes() ) { $textarea .= parent::getAttributes(); }
  $textarea .= '>';   
  if( !empty( $this->value ) ) { $textarea .= $this->value; }
  $textarea .= '</textarea>' . PHP_EOL;
  return $textarea;  
 }
}
 
class option extends element {
 
 protected $label;
 
 function __construct( $label ) {
  $this->label = $label;
 }
 
 public function draw() {
  if( !empty( $this->label ) ) {
   $option = '<option ';
   if( parent::hasAttributes() ) { $option .= parent::getAttributes(); }
   $option .= '>';
   $option .= $this->label;
   $option .= '</option>' . PHP_EOL;   
   return $option;
  }
 }
}
 
class label extends element {
 protected $value;
 
 function __construct( $value ) {
  $this->value = $value;
 }
 
 public function draw() {  
 
  $label = '<label ';
  if( parent::hasAttributes() ) { $label .= parent::getAttributes(); }
  $label .= '>';   
  if( !empty( $this->value ) ) { $label .= $this->value; }
  $label .= '</label>' . PHP_EOL;
  return $label;  
 }
}
 
class legend extends element {
 
 protected $value;
 
 function __construct( $value ) {
  $this->value = $value;
 }
 
 public function draw() {
  if( !empty( $this->value ) ) {
   $legend = '<legend ';
   if( parent::hasAttributes() ) { $legend .= parent::getAttributes(); }
   $legend .= '>';
   $legend .= $this->value;
   $legend .= '</legend>' . PHP_EOL;   
   return $legend;
  }
 }
}
 
class optgroup extends elementComposite {
 public function draw() {
  if( parent::hasElements() ) {
   $optgroup = '<optgroup ';
   if( parent::hasAttributes() ) { $optgroup .= parent::getAttributes(); }
   $optgroup .= '>' . PHP_EOL;
   $optgroup .= parent::getElements();
   $optgroup .= '</optgroup>' . PHP_EOL;
   return $optgroup;
  }
 }
}
 
class select extends elementComposite {  
 public function draw() {
  if( parent::hasElements() ) {
   $select = '<select ';
   if( parent::hasAttributes() ) { $select .= parent::getAttributes(); }
   $select .= '>' . PHP_EOL;
   $select .= parent::getElements();
   $select .= '</select>' . PHP_EOL;
   return $select;
  }
 }
}
 
class fieldset extends elementComposite {
 protected $legend;
 
 function __construct( $legend ) {
  if( isset( $legend ) and !empty( $legend ) ) {
   $this->setLegend( $legend );
  }
 }
 
 final public function setLegend( legend $legend ) {
  $this->legend = $legend;
 }
 
 public function draw() {
  if( parent::hasElements() ) {   
   $fieldset = '<fieldset ';
   $fieldset .= parent::getAttributes();
   $fieldset .= '>' . PHP_EOL;
   if( is_object( $this->legend ) ) { $fieldset .= $this->legend->draw(); }
   $fieldset .= parent::getElements();
   $fieldset .= '</fieldset>' . PHP_EOL;
   return $fieldset;
  }
 }
}
 
class form extends elementComposite {  
 function draw() {  
  if( parent::hasElements() ) {
   $form = '<form ';
   $form .= parent::getAttributes();
   $form .= '>' . PHP_EOL;
   $form .= parent::getElements();   
   $form .= '</form>';
   return $form;
  } 
 }
}

Utilização:

$labelInput = new label( 'label input' );
$labelInput->addAttribute( new attribute( 'for','input' ) );
 
$labelSelect = new label( 'label select' );
$labelSelect->addAttribute( new attribute( 'for','select' ) );
 
$labelTextarea = new label( 'label textarea' );
$labelTextarea->addAttribute( new attribute( 'for','textarea' ) );
 
$legend = new legend( 'meu fieldset' );
 
$input = new input;
$input->addAttribute( array( new attribute( 'type','text' ), new attribute( 'name','input' ), new attribute( 'id','input' ) ) );
 
$select = new select;
$select->addAttribute( new attribute( 'name','select' ) );
$select->addAttribute( new attribute( 'id','select' ) );
 
$arroz = new option( 'arroz' );
$arroz->addAttribute( new attribute( 'value',1 ) );
 
$feijao = new option( 'feijao' );
$feijao->addAttribute( new attribute( 'value',2 ) );
 
$bife = new option;
$bife->label = 'bife';
$bife->addAttribute( array ( new attribute( 'value',3 ), new attribute( 'selected','selected' ) ) );
 
$graos = new optgroup;
$graos->addAttribute( new attribute( 'label','graos' ) );
$graos->addElement( array( $arroz, $feijao ) );
 
$select->addElement( array( $graos, $bife ) );
 
$textarea = new textarea;
$textarea->addAttribute( array( new attribute( 'name','textarea' ), new attribute( 'id','textarea' ) ) );
 
$submit = new input;
$submit->addAttribute( array( new attribute( 'type','submit' ), new attribute( 'value','enviar' ) ) );
 
$fieldset = new fieldset;
$fieldset->setLegend( $legend );
$fieldset->addElement( array( $labelInput,$input,$labelSelect,$select,$labelTextarea,$textarea,$submit ) );
 
$form = new form;
$form->addAttribute( array( new attribute( 'name','form' ), new attribute( 'method','post' ) ) );
$form->addElement( $fieldset );
print $form->draw();

Até a próxima, pessoal! Compartilhem ideias sobre essa série de artigos e deixem seus comentários!