1<?php 2/** 3 * Zend Framework (http://framework.zend.com/) 4 * 5 * @link http://github.com/zendframework/zf2 for the canonical source repository 6 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 7 * @license http://framework.zend.com/license/new-bsd New BSD License 8 */ 9 10namespace Zend\Form\View\Helper; 11 12use Zend\Form\Element\Button; 13use Zend\Form\Element\MonthSelect; 14use Zend\Form\Element\Captcha; 15use Zend\Form\ElementInterface; 16use Zend\Form\Exception; 17use Zend\Form\LabelAwareInterface; 18 19class FormRow extends AbstractHelper 20{ 21 const LABEL_APPEND = 'append'; 22 const LABEL_PREPEND = 'prepend'; 23 24 /** 25 * The class that is added to element that have errors 26 * 27 * @var string 28 */ 29 protected $inputErrorClass = 'input-error'; 30 31 /** 32 * The attributes for the row label 33 * 34 * @var array 35 */ 36 protected $labelAttributes; 37 38 /** 39 * Where will be label rendered? 40 * 41 * @var string 42 */ 43 protected $labelPosition = self::LABEL_PREPEND; 44 45 /** 46 * Are the errors are rendered by this helper? 47 * 48 * @var bool 49 */ 50 protected $renderErrors = true; 51 52 /** 53 * Form label helper instance 54 * 55 * @var FormLabel 56 */ 57 protected $labelHelper; 58 59 /** 60 * Form element helper instance 61 * 62 * @var FormElement 63 */ 64 protected $elementHelper; 65 66 /** 67 * Form element errors helper instance 68 * 69 * @var FormElementErrors 70 */ 71 protected $elementErrorsHelper; 72 73 /** 74 * @var string 75 */ 76 protected $partial; 77 78 /** 79 * Invoke helper as functor 80 * 81 * Proxies to {@link render()}. 82 * 83 * @param null|ElementInterface $element 84 * @param null|string $labelPosition 85 * @param bool $renderErrors 86 * @param string|null $partial 87 * @return string|FormRow 88 */ 89 public function __invoke(ElementInterface $element = null, $labelPosition = null, $renderErrors = null, $partial = null) 90 { 91 if (!$element) { 92 return $this; 93 } 94 95 if (is_null($labelPosition)) { 96 $labelPosition = $this->getLabelPosition(); 97 } 98 99 if ($renderErrors !== null) { 100 $this->setRenderErrors($renderErrors); 101 } 102 103 if ($partial !== null) { 104 $this->setPartial($partial); 105 } 106 107 return $this->render($element, $labelPosition); 108 } 109 110 /** 111 * Utility form helper that renders a label (if it exists), an element and errors 112 * 113 * @param ElementInterface $element 114 * @param null|string $labelPosition 115 * @throws \Zend\Form\Exception\DomainException 116 * @return string 117 */ 118 public function render(ElementInterface $element, $labelPosition = null) 119 { 120 $escapeHtmlHelper = $this->getEscapeHtmlHelper(); 121 $labelHelper = $this->getLabelHelper(); 122 $elementHelper = $this->getElementHelper(); 123 $elementErrorsHelper = $this->getElementErrorsHelper(); 124 125 $label = $element->getLabel(); 126 $inputErrorClass = $this->getInputErrorClass(); 127 128 if (is_null($labelPosition)) { 129 $labelPosition = $this->labelPosition; 130 } 131 132 if (isset($label) && '' !== $label) { 133 // Translate the label 134 if (null !== ($translator = $this->getTranslator())) { 135 $label = $translator->translate($label, $this->getTranslatorTextDomain()); 136 } 137 } 138 139 // Does this element have errors ? 140 if (count($element->getMessages()) > 0 && !empty($inputErrorClass)) { 141 $classAttributes = ($element->hasAttribute('class') ? $element->getAttribute('class') . ' ' : ''); 142 $classAttributes = $classAttributes . $inputErrorClass; 143 144 $element->setAttribute('class', $classAttributes); 145 } 146 147 if ($this->partial) { 148 $vars = array( 149 'element' => $element, 150 'label' => $label, 151 'labelAttributes' => $this->labelAttributes, 152 'labelPosition' => $labelPosition, 153 'renderErrors' => $this->renderErrors, 154 ); 155 156 return $this->view->render($this->partial, $vars); 157 } 158 159 if ($this->renderErrors) { 160 $elementErrors = $elementErrorsHelper->render($element); 161 } 162 163 $elementString = $elementHelper->render($element); 164 165 // hidden elements do not need a <label> -https://github.com/zendframework/zf2/issues/5607 166 $type = $element->getAttribute('type'); 167 if (isset($label) && '' !== $label && $type !== 'hidden') { 168 $labelAttributes = array(); 169 170 if ($element instanceof LabelAwareInterface) { 171 $labelAttributes = $element->getLabelAttributes(); 172 } 173 174 if (! $element instanceof LabelAwareInterface || ! $element->getLabelOption('disable_html_escape')) { 175 $label = $escapeHtmlHelper($label); 176 } 177 178 if (empty($labelAttributes)) { 179 $labelAttributes = $this->labelAttributes; 180 } 181 182 // Multicheckbox elements have to be handled differently as the HTML standard does not allow nested 183 // labels. The semantic way is to group them inside a fieldset 184 if ($type === 'multi_checkbox' 185 || $type === 'radio' 186 || $element instanceof MonthSelect 187 || $element instanceof Captcha 188 ) { 189 $markup = sprintf( 190 '<fieldset><legend>%s</legend>%s</fieldset>', 191 $label, 192 $elementString 193 ); 194 } else { 195 // Ensure element and label will be separated if element has an `id`-attribute. 196 // If element has label option `always_wrap` it will be nested in any case. 197 if ($element->hasAttribute('id') 198 && ($element instanceof LabelAwareInterface && !$element->getLabelOption('always_wrap')) 199 ) { 200 $labelOpen = ''; 201 $labelClose = ''; 202 $label = $labelHelper($element); 203 } else { 204 $labelOpen = $labelHelper->openTag($labelAttributes); 205 $labelClose = $labelHelper->closeTag(); 206 } 207 208 if ($label !== '' && (!$element->hasAttribute('id')) 209 || ($element instanceof LabelAwareInterface && $element->getLabelOption('always_wrap')) 210 ) { 211 $label = '<span>' . $label . '</span>'; 212 } 213 214 // Button element is a special case, because label is always rendered inside it 215 if ($element instanceof Button) { 216 $labelOpen = $labelClose = $label = ''; 217 } 218 219 if ($element instanceof LabelAwareInterface && $element->getLabelOption('label_position')) { 220 $labelPosition = $element->getLabelOption('label_position'); 221 } 222 223 switch ($labelPosition) { 224 case self::LABEL_PREPEND: 225 $markup = $labelOpen . $label . $elementString . $labelClose; 226 break; 227 case self::LABEL_APPEND: 228 default: 229 $markup = $labelOpen . $elementString . $label . $labelClose; 230 break; 231 } 232 } 233 234 if ($this->renderErrors) { 235 $markup .= $elementErrors; 236 } 237 } else { 238 if ($this->renderErrors) { 239 $markup = $elementString . $elementErrors; 240 } else { 241 $markup = $elementString; 242 } 243 } 244 245 return $markup; 246 } 247 248 /** 249 * Set the class that is added to element that have errors 250 * 251 * @param string $inputErrorClass 252 * @return FormRow 253 */ 254 public function setInputErrorClass($inputErrorClass) 255 { 256 $this->inputErrorClass = $inputErrorClass; 257 return $this; 258 } 259 260 /** 261 * Get the class that is added to element that have errors 262 * 263 * @return string 264 */ 265 public function getInputErrorClass() 266 { 267 return $this->inputErrorClass; 268 } 269 270 /** 271 * Set the attributes for the row label 272 * 273 * @param array $labelAttributes 274 * @return FormRow 275 */ 276 public function setLabelAttributes($labelAttributes) 277 { 278 $this->labelAttributes = $labelAttributes; 279 return $this; 280 } 281 282 /** 283 * Get the attributes for the row label 284 * 285 * @return array 286 */ 287 public function getLabelAttributes() 288 { 289 return $this->labelAttributes; 290 } 291 292 /** 293 * Set the label position 294 * 295 * @param string $labelPosition 296 * @throws \Zend\Form\Exception\InvalidArgumentException 297 * @return FormRow 298 */ 299 public function setLabelPosition($labelPosition) 300 { 301 $labelPosition = strtolower($labelPosition); 302 if (!in_array($labelPosition, array(self::LABEL_APPEND, self::LABEL_PREPEND))) { 303 throw new Exception\InvalidArgumentException(sprintf( 304 '%s expects either %s::LABEL_APPEND or %s::LABEL_PREPEND; received "%s"', 305 __METHOD__, 306 __CLASS__, 307 __CLASS__, 308 (string) $labelPosition 309 )); 310 } 311 $this->labelPosition = $labelPosition; 312 313 return $this; 314 } 315 316 /** 317 * Get the label position 318 * 319 * @return string 320 */ 321 public function getLabelPosition() 322 { 323 return $this->labelPosition; 324 } 325 326 /** 327 * Set if the errors are rendered by this helper 328 * 329 * @param bool $renderErrors 330 * @return FormRow 331 */ 332 public function setRenderErrors($renderErrors) 333 { 334 $this->renderErrors = (bool) $renderErrors; 335 return $this; 336 } 337 338 /** 339 * Retrieve if the errors are rendered by this helper 340 * 341 * @return bool 342 */ 343 public function getRenderErrors() 344 { 345 return $this->renderErrors; 346 } 347 348 /** 349 * Set a partial view script to use for rendering the row 350 * 351 * @param null|string $partial 352 * @return FormRow 353 */ 354 public function setPartial($partial) 355 { 356 $this->partial = $partial; 357 return $this; 358 } 359 360 /** 361 * Retrieve current partial 362 * 363 * @return null|string 364 */ 365 public function getPartial() 366 { 367 return $this->partial; 368 } 369 370 /** 371 * Retrieve the FormLabel helper 372 * 373 * @return FormLabel 374 */ 375 protected function getLabelHelper() 376 { 377 if ($this->labelHelper) { 378 return $this->labelHelper; 379 } 380 381 if (method_exists($this->view, 'plugin')) { 382 $this->labelHelper = $this->view->plugin('form_label'); 383 } 384 385 if (!$this->labelHelper instanceof FormLabel) { 386 $this->labelHelper = new FormLabel(); 387 } 388 389 if ($this->hasTranslator()) { 390 $this->labelHelper->setTranslator( 391 $this->getTranslator(), 392 $this->getTranslatorTextDomain() 393 ); 394 } 395 396 return $this->labelHelper; 397 } 398 399 /** 400 * Retrieve the FormElement helper 401 * 402 * @return FormElement 403 */ 404 protected function getElementHelper() 405 { 406 if ($this->elementHelper) { 407 return $this->elementHelper; 408 } 409 410 if (method_exists($this->view, 'plugin')) { 411 $this->elementHelper = $this->view->plugin('form_element'); 412 } 413 414 if (!$this->elementHelper instanceof FormElement) { 415 $this->elementHelper = new FormElement(); 416 } 417 418 return $this->elementHelper; 419 } 420 421 /** 422 * Retrieve the FormElementErrors helper 423 * 424 * @return FormElementErrors 425 */ 426 protected function getElementErrorsHelper() 427 { 428 if ($this->elementErrorsHelper) { 429 return $this->elementErrorsHelper; 430 } 431 432 if (method_exists($this->view, 'plugin')) { 433 $this->elementErrorsHelper = $this->view->plugin('form_element_errors'); 434 } 435 436 if (!$this->elementErrorsHelper instanceof FormElementErrors) { 437 $this->elementErrorsHelper = new FormElementErrors(); 438 } 439 440 return $this->elementErrorsHelper; 441 } 442} 443