non-input Zend Form Element

Simple case : let’s say you make a form for user to register . You have an email field , right ? Ok , now when the user is logged you have a page where he can modify password and other settings . You should normally want to display the email too , but you don’t want to be editable ( because emails should unique etc. ) .You could show the email separated from the form , but you don’t want that , you want it to be in the same area with the other fields .
One simple approach is to create a “dummy” element , one that can be populated by the populate() function , but actually don’t have an <input> . The way is to create an element that extends Zend_Form_Element or Zend_Form_Element_Xhtml which uses formNote helper . Normally this helper is used by hidden elements ( it’s a dead simple helper , you can check the source in Zend/View/Helper) , but you can use it in this case too :

class App_Form_Element_Xhtml extends Zend_Form_Element_Xhtml
{
public $helper = 'formNote';
}

Now ,you will proubably extend your form when editing ( I use a Base form with the fields and for Edit/Register I extend it with new ones ) . To get the email displayed , you need first to remove the email element ( text type ) and replace it with this one :

$this->removeElement('email');
$fakeEmail = new App_Form_Element_Xhtml('email',array(
                 'required' => false,
                'ignore'=> true,
		'label' => 'Email : ',
		));
$this->addElement($fakeEmail);

You need to add ignore to be true , otherwise $form->getValues() will return you a “” value form the email – this can be fixed by declaring getValue and setValue functions for the element , but setting ignore works too .
The last step is to re-order . If the email is the first you can use $fakeEmail->setOrder(-1) before adding .

Eclipse , xdebug , remote system

This morning I had nothing better to do ( actually I had , but … ) so I thought “let’s try xdebug” . I’m working remote on a EC2 instance using Eclipse PDT . Installing xdebug is pretty easy , apt-get install php5-xdebug or something like that ( on debians) , you need to edit a .ini file then you create a debug profile in eclipse ( bla bla bla there’re a lot of tutorials about it ) .

Hit debug buton annnndd ….. “launching: waiting for xdebug session” . Wtf … doesn’t work .

Gogled a bit more , found that you should have the xdebug option xdebug.idekey to be the same with XDEBUG_SESSION_START used by the IDE . IN this case it’s ECLIPSE_DBGP . Netbeans eg. have netbeans-xdebug I think . Well, you can set it to whatever .

Hit button annndd … “launching: waiting for xdebug session” . Wtf … doesn’t work .

I remember I used xdebug some time ago , but I was running locally . So what could it be ? Well xdebug to help you need a remote address . By default is localhost . If you work remote , you should have instead of localhost your IP . Well , if you have a public IP , lucky you , but I bet you don’t have .

What to do ? SSH tunnel . You can do it . OR you could use the built-in option PDT have 🙂

Zend Forms

Zend Forms are a nice tool in ZF , but it’s a bit weird and looks bloated for new people . One thing is that it follows the decorator pattern and another is that you end up writing some code for a damn html form of several lines . Yet , it offers a validation system and if you have many forms in your project , the decorations might turn in a not bad thing .
After reading the tutorial , first thing you will want to do is to get rid of the “damn” dt/dd . The dt/dd combination was chosen to be default for a number of reasons ( some good ones ) . Yet designers might look weird at you ( even if dt/dd can be handled pretty well ) or you just want div tags or a damn table there .
How to do that ? well ,there are 2 ( no , 3) sollutions : one is to modify the decorators , second is to create your own elements ( maybe with their decorators too) … and last one is to use the ViewScript decorator ( basicly you template the form ) . The second , even if looks nice – to create your own element , sucks because you need to re-create all the input elements . And most of all sucks because you re-invent the wheel . Writing a decorator that pretty much does same thing as the default ones , sucks too.
ViewScript is a nice sollution , the problem I see here is that if you want to change your forms layout , you need to edit all those template files ( or maybe I’m wrong ). It’s a good sollution , but I want to use the normal path , if those zf people made the effort to build it .
So … after googling and googling , I have to say the docs are pretty bad . Even tutorials . They don’t explain exactly how to “reset” the damn decorating .
Let’s take it easly and explain how decorators apply ( and how forms works ) . It’s easy : you give an array which is processed … in the order you gave it . First decorator will be always the ViewHelper . This one renders your <input> element and nothing else ( along with the attributes you set for it ). The next decorator you declare will “embrace” the content you already have . If you have a label , it will be concated with the <input> element . If you have a HtmlTag , it will include your input or whatever is the current content. If you have another Html, it will embrace the new content , -that’s your previous HtmlTag which has the input side .  And so on . It’s like having boxes of different sizes in the order of first is the smallest , last is the biggest .  Pretty much same thing is when you add things in a form class , either they are elements or groups or decorations . There is one thing : you can define order here ( I’ll show later an example).

So , let’s say we want to have table instead of dt/dd .

$this->addElement('text','name',array(
			'filters' => array('StringTrim'),
			'validators' => array(array('StringLength',true,array(3,128))),
			'required' => true,
			'label' => 'Name :',
			'decorators' => array('ViewHelper',
						array(array('linebr' => 'HtmlTag'),'options'=> array('tag' => 'br', 'placement' => 'append','openOnly'=>true)),
						array('Errors'),
						array(array('data'=>'HtmlTag'),'options'=>array('tag'=>'td')),
						array('Label','options'=>array('tag'=>'td')),
						array(array('row'=>'HtmlTag'),'options'=>array('tag'=>'tr')))
			));	

This will render something like this :

<tr>
    <td>
        <label>Name:</label>
    </td>
    <td>
        <input type="text" name="name" id="name" value="">
       <br/>
       <ul><li>Error 1</li></ul>
    </td>
</tr>

Let’s recap : first we have the input element , rendered by ViewHelper , then we have a br line ( use openOnly to not create 2 br’s) – also note we use placement ( if we use prepend , the br will be pasted before the input ) , then we have Errors . Next comes a td tag which will embrace everything we have until now . Now it’s the Label time , instead of dt we have a td . By default it’s prepended . The last one is the tr tag that completes our row . Note that for multiple HtmlTag decorators you need to give them a key(or name , whatever).This is to create a new HtmlTag instance , otherwise the same decorator is applied – you can try removing the keys to see what will happen .
This looks ugly … if you do it for all your elements . But we’re in OO world so we can improve it. But first , let’s say you add several more elements .At the end you will add this :

$this->setDecorators(array('FormElements',array('HtmlTag', array('tag' => 'table')),'Form'));

This will render the table tags . Remember this must be added after you inserted all your elements , more likely after the submit button .
Ok , how can you make this spagetti be more nicer ? Your could use overwriting the default decoratos for elements , but I’m using another alternative : simply create a form with no elements , you create a method that returns the whole decorator array for the elements and you can have something like this :

$this->addElement('text','name',array(
	'filters' => array('StringTrim'),
	'validators' => array(array('StringLength',true,array(3,128))),
	'required' => true,
	'label' => 'Name :',
	'decorators' => $this->decorators()
));	

Looks a bit nicer now . Even more you can have a method :

public function loadDefaultDecorators()
{
      $this->setDecorators(array('FormElements',array('HtmlTag', array('tag' => 'table')),'Form'));
}

This is be the default decorator for the form , so you don’t need to add it in init() . Your form will extends this one instead of default Zend_Form .
So now you can create forms that extends this one and you will get html table output .
It’s very likely that you’ll have several types of decorators , one is for sure the submit button – because you don’t have a label there .

public function submitdecorator()
{
	return array('ViewHelper',
			array(array('data'=>'HtmlTag'),'options'=>array('tag'=>'td')),
			array(array('data2'=>'HtmlTag'),'options'=>array('tag'=>'td','placement'=>"prepend")),
			array(array('row'=>'HtmlTag'),'options'=>array('tag'=>'tr')));
}

Here you go . For submit button you do ‘decorators’ => $this->submitdecorator() . You can have a label too , but it will be ignored , because we didn’t defined the decorator for it . Note that for the second HtmlTag I used prepends , otherwise the cell with the submit button will be in left and we want it in right (to be aligned with the inputs , not the labels , but as you wish ) .

Ok, what’s next : using DisplayGroup . Let’s say you have you form table with several input elements , but you also have 2 checkboxes that you want to be displayed in the same cell .
First , you can’t use the decorator above , because it will render one element per row . Instead you want the 2 checkboxes to be rendered inside the cell and every checkbox to be inside a div for futher styling .

'decorators' => array(
			'ViewHelper',
			array('Label','options'=>array('placement'=>'append')),
			array('HtmlTag','options'=>array('tag'=>'div'))

This will be your decorator for each checkbox . Note that I put the label after the checkbox .
Next is to group them :

$this->addDisplayGroup(array('firstcheckbox','secondcheckbox'), 'nameofgroup',
		array(
			'description' => 'Bla bla bla :',
			'decorators'=>array('FormElements',
					  array(array('data'=>'HtmlTag'),'options'=>array('tag'=>'td')),
					  array('Description','options'=>array('tag'=>'td','placement'=>"prepend")),
				          array(array('row'=>'HtmlTag'),'options'=>array('tag'=>'tr'))
		)));		

‘firstcheckbox’ and ‘secondcheckbox’ are the names of your checkboxes .You always need to add there the elements names , otherwise they are not included in the group . I used Description for filling the left row .

Last thing : I said something about order of elements . How is useful . Let’s say you made a form … user data or something . Somewhere you have an admin are where as admin you can edit this data . Most likely you wil have several additions fields ( like if user is banned or something ) . You can extend the form you have . The problem is when you add new fields , they will be added AFTER the submit button . Why ? because elements are rendered in the order they are added , except if you set an order . By default , there is no order , it’s the normal iteration of the array with elements . But you can set it . The most simple way is to set a high value to the submit button – it will be rendered the last one . You can do it even in your admin form :

$this->getElement('mysubmit')->setOrder(100);

This way you don’t need to touch the initial form .