W jednym z poprzednich wpisów poświęconych Zend_Form opisałem sposób dekorowania formularzy w Zend Framework. Niestety poważną wadą tej metody było powtarzanie kodu, co w przypadku rozbudowanej aplikacji nie jest najlepszym pomysłem. Dobrym wyjściem z tej sytuacji będzie zastosowanie klasy bazowej dla naszych formularzy, dziedziczącej po Zend_Form. Klasa ta będzie odpowiedzialna za ustawienie dekoratorów oraz wyrenderowanie formularza.

Przykładowy formularz wygląda następująco

class Application_Form_Example extends Batman_Form
{
	protected function _renderForm()
	{
		$this->setName('form-example');

		$name = new Zend_Form_Element_Text('name');
		$name->setLabel('Nazwa');
		
		$submit = new Zend_Form_Element_Submit('btn_save');
		$submit->setLabel('Zapisz');
		
		$this->addElement($name);
		$this->addElement($submit);
	}
}

Jak widać jedyną różnicą w stosunku do standardowego podejścia jest zmiana nazwy metody, w której znajduje się kod odpowiedzialny za formularz. Ponadto metoda ta nie zawiera kodu odpowiedzialnego za dekoratory.

Cała magia dzieje się w klasie Batman_Form

abstract class Batman_Form extends Zend_Form
{
	abstract protected function _renderForm();
	
	public function init()
	{
		$this->_renderForm();
		$this->_resetDecorators();
	}
	
	protected function _resetDecorators()
	{
		$this->clearDecorators();
		$this->addDecorator('FormElements')
			 ->addDecorator('HtmlTag', array('tag' => 'div'))
			 ->addDecorator('Form');

		$this->setElementDecorators(array(
			array('ViewHelper'),
			array('Errors'),
			array('Label'),
			array('HtmlTag', array('tag' => 'div', 'class' => 'element-group'))
		));
        
		// dodatkowe operacje na konkretnych elementach formularza
		// np zmiana nazwy klasy css dla przycisków
		foreach($this->getElements() as $element) {
			if($element instanceof Zend_Form_Element_Submit) {
				$element->removeDecorator('Label');
				$element->addDecorator('HtmlTag', array('tag' => 'div', 'class' => 'submit-group'));
			}
		}
	}
}

Klasa przesłania metodę init, która jest wywoływana z konstruktora Zend_Form. W metodzie tej wywoływana jest z kolei metoda klasy potomka o nazwie _renderForm oraz metoda _resetDecorators. Konstrukcja ta posiada dwie zalety. Po pierwsze klasa formularza odpowiada jedynie za utworzenie elementów. Po drugie nie musimy powielać kodu odpowiedzialnego za dekoratory.

Dla zachowania porządku oraz dla zapewnienia poprawności kodu, klasa Batman_Form jest klasą abstrakcyjną, zawierającą abstrakcyjną metodę _renderForm.

A co jeśli w jednym formularzu mają być użyte inne dekoratory? Nic prostszego. Wystarczy w klasie formularza przesłonić metodę _resetDecorators i napisać własny kod odpowiedzialny za dekoratory.