As I've been going through building my app for the Practical Learning series, one thing that has been bugging me is the way we had to prepend/append the individual divs for each form row item in our forms. It seemed to be a great way to introduce errors and inconsistency in our user interfaces. When we decided we would stick with Zend's default of using in-line errors (instead of grouping them at the top as we used to), I also hit on the need to be able to pass some kind of styling, at the minimum, to those errors.
When Googling about the latter, I quickly discovered the best (only real) way to do it was by extending the formElementErrors class. I had, in fact, tried to extend another Zend class before with no success, but I decided to try it again. By this point, I've come to realize that having the Zend source code at GitHub is extremely helpful. As the online docs can be lacking in details, just going to the appropriate file at looking at the code can help fill in the gaps.
The stock formElementErrors is already pretty basic and to clarify, it does have a way of overriding the defaults – but it is done in the views and manually for EVERY elements. Again, it would be inefficient and highly increase the probability for inconsistencies in the site.
So in my src for MovieShelves, I created a Helper folder for my new class to live. It could probably go in Application just as easily, and is probably where we will default such things in the future unless they are specific to a single module. Anyway, I made a file called FormElementErrors.php. In this case, all I wanted was to override the default formatting, so I only needed a small bit of code in my version.
<?php
	namespace MovieShelves\Helper;
	use Zend\Form\View\Helper\FormElementErrors as OriginalFormElementErrors; // alias the original
	
	class FormElementErrors extends OriginalFormElementErrors { // extend the original so we don't have to rewrite the whole thing
		protected $messageCloseString     = '</span></div>';  // change our defaults to a div with spans instead of the UL with lis; also set our class rather than making users pass it in.
		protected $messageOpenFormat      = '<div class="inlineError"><span>';
		protected $messageSeparatorString = '</span><span>';
	}
?>
Then we head over to our Module.php in MovieShelves and add our new view helper.
...
public function getViewHelperConfig() {
	return array(
		'invokables' => array(
			'formelementerrors' => 'MovieShelves\Helper\FormElementErrors',
		),
	);
}
...
And that's it. Since we kept the same name, our new class will automatically be used instead of the Zend one. Yay!!!

With that working, I decided it was time to try (again) to do something similar with formRow, so we didn't have to have stuff like this in our add/edit files:
<?php
    $title = 'Add New Series';
    $this->headTitle($title);
    $this->layout()->pageTitle = $title;
     
    $form->setAttribute('action', $this->url('series', array('action' => 'add')));
    $form->prepare();
     
    echo $this->form()->openTag($form) . "\n";
    echo $this->formHidden($form->get('seriesid')) . "\n";
    echo '<div class="pure-control-group">' . $this->formRow($form->get('seriesname')) . "</div>\n\n";
    echo '<div class="pure-control-group">' . $this->formRow($form->get('numberoftitles')) . "</div>\n\n";
    echo '<div class="pure-control-group radiosRequired">' . $this->formRow($form->get('isongoing')) . "</div>\n\n";
    echo '<div class="buttonWrapper">' . $this->formButton($form->get('saveButton')) . "</div>\n";
    echo $this->form()->closeTag() . "\n";
?>
In the same Helper folder, I made a new file called FormRow.php. The stock Zend version is a bit more complex, and in this case we are going to take over an entire function rather than the variables, so we have to include the use statements as well.
<?php
	namespace MovieShelves\Helper;
	use Zend\Form\Element\Button;
	use Zend\Form\Element\MonthSelect;
	use Zend\Form\ElementInterface;
	use Zend\Form\Exception;
	use Zend\Form\LabelAwareInterface;
	use Zend\Form\View\Helper\FormRow as OriginalFormRow;
	
	class FormRow extends OriginalFormRow {
		public function render(ElementInterface $element) {
			$escapeHtmlHelper = $this->getEscapeHtmlHelper();
			$labelHelper = $this->getLabelHelper();
			$elementHelper = $this->getElementHelper();
			$elementErrorsHelper = $this->getElementErrorsHelper();
		
			$label = $element->getLabel();
			$inputErrorClass = $this->getInputErrorClass();
		
			if (isset($label) && '' !== $label) {
				// Translate the label
				if (null !== ($translator = $this->getTranslator())) {
					$label = $translator->translate(
						$label, $this->getTranslatorTextDomain()
					);
				}
			}
		
			// Does this element have errors ?
			if (count($element->getMessages()) > 0 && !empty($inputErrorClass)) {
				$classAttributes = ($element->hasAttribute('class') ? $element->getAttribute('class') . ' ' : '');
				$classAttributes = $classAttributes . $inputErrorClass;
		
				$element->setAttribute('class', $classAttributes);
			}
		
			if ($this->partial) {
				$vars = array(
					'element' => $element,
					'label' => $label,
					'labelAttributes' => $this->labelAttributes,
					'labelPosition' => $this->labelPosition,
					'renderErrors' => $this->renderErrors,
				);
		
				return $this->view->render($this->partial, $vars);
			}
		
			if ($this->renderErrors) {
				$elementErrors = $elementErrorsHelper->render($element);
			}
		
			$elementString = $elementHelper->render($element);
		
			// hidden elements do not need a <label> -https://github.com/zendframework/zf2/issues/5607
			$type = $element->getAttribute('type');
			
			if (isset($label) && '' !== $label && $type !== 'hidden') {
				$labelAttributes = array();
		
				if ($element instanceof LabelAwareInterface) {
					$labelAttributes = $element->getLabelAttributes();
				}
		
				if (! $element instanceof LabelAwareInterface || ! $element->getLabelOption('disable_html_escape')) {
					$label = $escapeHtmlHelper($label);
				}
		
				if (empty($labelAttributes)) {
					$labelAttributes = $this->labelAttributes;
				}
		
				// Multicheckbox elements have to be handled differently as the HTML standard does not allow nested
				// labels. The semantic way is to group them inside a fieldset
				if ($type === 'multi_checkbox' || $type === 'radio' || $element instanceof MonthSelect ) {
					$labelAttributes = $element->getLabelAttributes(); // were any label classes passed in?
					
					if (isset($labelAttributes['class'])) {
						$labelClasses = $labelAttributes['class'];
					}
					else {
						$labelClasses = "";
					}
					
					// instead of a fieldset/legend, put our label in a div and then the real label/element as normal; this could probably be shortened up even more 
					// since the code is the same other than the class in the div
					if (strpos($labelClasses, 'required') !== false) {  //if this is a required checkbox/radio, add the required class to our div
						$markup = sprintf(
							'<div class="required pseudoLabel">%s</div>%s',
							$label,
							$elementString);
					}
					else {
						$markup = sprintf(
							'<div class="pseudoLabel">%s</div>%s',
							$label,
							$elementString);
					}
					
					// then wrap the whole bit in our usual row div
					$markup = '<div class="pure-control-group radioGroup">' . $markup . '</div>'; 
					
					
				} else {
					// Ensure element and label will be separated if element has an `id`-attribute.
					// If element has label option `always_wrap` it will be nested in any case.
					if ($element->hasAttribute('id') && ($element instanceof LabelAwareInterface && !$element->getLabelOption('always_wrap')) ) {
						$labelOpen = '';
						$labelClose = '';
						$label = $labelHelper($element);
					} else {
						$labelOpen = $labelHelper->openTag($labelAttributes);
						$labelClose = $labelHelper->closeTag();
					}
		
					if ($label !== '' && (!$element->hasAttribute('id')) || ($element instanceof LabelAwareInterface && $element->getLabelOption('always_wrap')) ) {
						$label = '<span>' . $label . '</span>';
					}
		
					// Button element is a special case, because label is always rendered inside it
					if ($element instanceof Button) {
						$labelOpen = $labelClose = $label = '';
					}
		
					switch ($this->labelPosition) {
						case self::LABEL_PREPEND:
							$markup = $labelOpen . $label . $elementString . $labelClose;
							break;
						case self::LABEL_APPEND:
						default:
							$markup = $labelOpen . $elementString . $label . $labelClose;
							break;
					}
					
					if ($element instanceof Button) {
						$markup = '<div class="buttonWrapper">' . $markup . '</div>'; //always wrap our button in its wrapper class
					}
					else {
						$markup = '<div class="pure-control-group">' . $markup . '</div>'; //always wrap our regular form fields in our div
					}
				}
		
				if ($this->renderErrors) {
					$markup .= $elementErrors;
				}
			} else {
				if ($this->renderErrors) {
					$markup = $elementString . $elementErrors;
				} else {
					$markup = $elementString;
				}
			}
		
			$markup . "\n\n";
		
			return $markup;
		}
	}
?>
Basically I modified a few places of the render function to conform more to our design norms. The first being with the way multiple check boxes and radios are handled. No more damn forced fieldsets!!! Instead, the parent "label" will go in a div, then the whole set of check boxes, radios, etc will go in a containing div. Combined with the CSS, this allowed me to style everything nicely while still retaining the ability to send custom classes and the like to the individual elements if needed.
...
if ($type === 'multi_checkbox' || $type === 'radio' || $element instanceof MonthSelect ) {
	$labelAttributes = $element->getLabelAttributes(); // were any label classes passed in?
	
	if (isset($labelAttributes['class'])) {
		$labelClasses = $labelAttributes['class'];
	}
	else {
		$labelClasses = "";
	}
	
	// instead of a fieldset/legend, put our label in a div and then the real label/element as normal; this could probably be shortened up even more 
	// since the code is the same other than the class in the div
	if (strpos($labelClasses, 'required') !== false) {  //if this is a required checkbox/radio, add the required class to our div
		$markup = sprintf(
			'<div class="required pseudoLabel">%s</div>%s',
			$label,
			$elementString);
	}
	else {
		$markup = sprintf(
			'<div class="pseudoLabel">%s</div>%s',
			$label,
			$elementString);
	}
	
	// then wrap the whole bit in our usual row div
	$markup = '<div class="pure-control-group radioGroup">' . $markup . '</div>'; 
}
...
In the else part of the code, which handles all other types, I just added a few lines to wrap the resulting label/element combo in our containing group. I used an if statement to give buttons a different wrapper.
...
	if ($element instanceof Button) {
		$markup = '<div class="buttonWrapper">' . $markup . '</div>'; //always wrap our button in its wrapper class
	}
	else {
		$markup = '<div class="pure-control-group">' . $markup . '</div>'; //always wrap our regular form fields in our div
	}
...
And finally at the end I concated line breaks on to try to pretty up the source code. Now, the odd thing on this last bit is that it only works if you use formRow itself. For my movies add/edit, I wanted to avoid having to do the individual form rows, so I instead used the form option, which greatly reduced my source code for the add.html to just this:
<?php
	$title = 'Add New Movie';
	$this->headTitle($title);
	$this->layout()->pageTitle = $title;
	$this->layout()->needsDataTables = true;
	
	$form = $this->form;
	
	$form->setAttribute('action', $this->url('movies', array('action' => 'add')));
	$form->prepare();
	
	echo $this->form($form);
?>

Using the Form function just iterates the formRow view helper (or the formCollection, if appropriate), but for some reason it strips out my line breaks. One work around would be, of course, to extend it as well and concat in the line breaks there. And because I am oddly anal about that sort of thing, that's just what I did.
<?php
	namespace MovieShelves\Helper;
	
	use Zend\Form\FieldsetInterface;
	use Zend\Form\FormInterface;
	use Zend\Form\View\Helper\Form as OriginalForm;
	
	class Form extends OriginalForm {
	    public function render(FormInterface $form) {
		   if (method_exists($form, 'prepare')) {
			  $form->prepare();
		   }
	
		   $formContent = '';
	
		   foreach ($form as $element) {
			  if ($element instanceof FieldsetInterface) {
				 $formContent.= $this->getView()->formCollection($element) . "\n\n";
			  } else {
				 $formContent.= $this->getView()->formRow($element) . "\n\n";
			  }
		   }
	
		   return $this->openTag($form) . "\n\n" . $formContent . $this->closeTag() . "\n\n";
	    }
	}
?>
And now we have nice line breaks between our elements to make the code at least somewhat more readable. I'll concede on not being able to indent too 😉 Oh, and don't forget to register our second two custom helpers in Module.php; works the same way as the first one.
...
public function getViewHelperConfig() {
	return array(
		'invokables' => array(
			'formelementerrors' => 'MovieShelves\Helper\FormElementErrors',
			'formrow' => 'MovieShelves\Helper\FormRow',
			'form' => 'MovieShelves\Helper\Form',
		),
	);
}
...
Now whether doing all of this is a "best practice" or not, I can't really say. I do know that, in general, we try to maintain a very consistent look/feel across our forms in each app for better usability. Using these custom view helpers insures that our standards will be applied without requiring us to remember to append every little bit to the individual rows. It also will keep us from having to do as many mass search/replaces if we need to change a class or layout. I did, however, decide not to put our normal classes in the form tag itself (which could also be done by modifying the form view helper) because we do often use different classes to quickly restyle a form without a lot of fuss. For a smaller app where we just had a single style form, though, I could see us going ahead and adding that too.
