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\ElementInterface; 13use Zend\Form\Element\MultiCheckbox as MultiCheckboxElement; 14use Zend\Form\Exception; 15use Zend\Form\LabelAwareInterface; 16 17class FormMultiCheckbox extends FormInput 18{ 19 const LABEL_APPEND = 'append'; 20 const LABEL_PREPEND = 'prepend'; 21 22 /** 23 * The attributes applied to option label 24 * 25 * @var array 26 */ 27 protected $labelAttributes; 28 29 /** 30 * Where will be label rendered? 31 * 32 * @var string 33 */ 34 protected $labelPosition = self::LABEL_APPEND; 35 36 /** 37 * Separator for checkbox elements 38 * 39 * @var string 40 */ 41 protected $separator = ''; 42 43 /** 44 * Prefixing the element with a hidden element for the unset value? 45 * 46 * @var bool 47 */ 48 protected $useHiddenElement = false; 49 50 /** 51 * The unchecked value used when "UseHiddenElement" is turned on 52 * 53 * @var string 54 */ 55 protected $uncheckedValue = ''; 56 57 /** 58 * Form input helper instance 59 * 60 * @var FormInput 61 */ 62 protected $inputHelper; 63 64 /** 65 * Form label helper instance 66 * 67 * @var FormLabel 68 */ 69 protected $labelHelper; 70 71 /** 72 * Invoke helper as functor 73 * 74 * Proxies to {@link render()}. 75 * 76 * @param ElementInterface|null $element 77 * @param null|string $labelPosition 78 * @return string|FormMultiCheckbox 79 */ 80 public function __invoke(ElementInterface $element = null, $labelPosition = null) 81 { 82 if (!$element) { 83 return $this; 84 } 85 86 if ($labelPosition !== null) { 87 $this->setLabelPosition($labelPosition); 88 } 89 90 return $this->render($element); 91 } 92 93 /** 94 * Render a form <input> element from the provided $element 95 * 96 * @param ElementInterface $element 97 * @throws Exception\InvalidArgumentException 98 * @return string 99 */ 100 public function render(ElementInterface $element) 101 { 102 if (!$element instanceof MultiCheckboxElement) { 103 throw new Exception\InvalidArgumentException(sprintf( 104 '%s requires that the element is of type Zend\Form\Element\MultiCheckbox', 105 __METHOD__ 106 )); 107 } 108 109 $name = static::getName($element); 110 111 $options = $element->getValueOptions(); 112 113 $attributes = $element->getAttributes(); 114 $attributes['name'] = $name; 115 $attributes['type'] = $this->getInputType(); 116 $selectedOptions = (array) $element->getValue(); 117 118 $rendered = $this->renderOptions($element, $options, $selectedOptions, $attributes); 119 120 // Render hidden element 121 $useHiddenElement = method_exists($element, 'useHiddenElement') && $element->useHiddenElement() 122 ? $element->useHiddenElement() 123 : $this->useHiddenElement; 124 125 if ($useHiddenElement) { 126 $rendered = $this->renderHiddenElement($element, $attributes) . $rendered; 127 } 128 129 return $rendered; 130 } 131 132 /** 133 * Render options 134 * 135 * @param MultiCheckboxElement $element 136 * @param array $options 137 * @param array $selectedOptions 138 * @param array $attributes 139 * @return string 140 */ 141 protected function renderOptions(MultiCheckboxElement $element, array $options, array $selectedOptions, array $attributes) 142 { 143 $escapeHtmlHelper = $this->getEscapeHtmlHelper(); 144 $labelHelper = $this->getLabelHelper(); 145 $labelClose = $labelHelper->closeTag(); 146 $labelPosition = $this->getLabelPosition(); 147 $globalLabelAttributes = array(); 148 $closingBracket = $this->getInlineClosingBracket(); 149 150 if ($element instanceof LabelAwareInterface) { 151 $globalLabelAttributes = $element->getLabelAttributes(); 152 } 153 154 if (empty($globalLabelAttributes)) { 155 $globalLabelAttributes = $this->labelAttributes; 156 } 157 158 $combinedMarkup = array(); 159 $count = 0; 160 161 foreach ($options as $key => $optionSpec) { 162 $count++; 163 if ($count > 1 && array_key_exists('id', $attributes)) { 164 unset($attributes['id']); 165 } 166 167 $value = ''; 168 $label = ''; 169 $inputAttributes = $attributes; 170 $labelAttributes = $globalLabelAttributes; 171 $selected = (isset($inputAttributes['selected']) && $inputAttributes['type'] != 'radio' && $inputAttributes['selected']); 172 $disabled = (isset($inputAttributes['disabled']) && $inputAttributes['disabled']); 173 174 if (is_scalar($optionSpec)) { 175 $optionSpec = array( 176 'label' => $optionSpec, 177 'value' => $key 178 ); 179 } 180 181 if (isset($optionSpec['value'])) { 182 $value = $optionSpec['value']; 183 } 184 if (isset($optionSpec['label'])) { 185 $label = $optionSpec['label']; 186 } 187 if (isset($optionSpec['selected'])) { 188 $selected = $optionSpec['selected']; 189 } 190 if (isset($optionSpec['disabled'])) { 191 $disabled = $optionSpec['disabled']; 192 } 193 if (isset($optionSpec['label_attributes'])) { 194 $labelAttributes = (isset($labelAttributes)) 195 ? array_merge($labelAttributes, $optionSpec['label_attributes']) 196 : $optionSpec['label_attributes']; 197 } 198 if (isset($optionSpec['attributes'])) { 199 $inputAttributes = array_merge($inputAttributes, $optionSpec['attributes']); 200 } 201 202 if (in_array($value, $selectedOptions)) { 203 $selected = true; 204 } 205 206 $inputAttributes['value'] = $value; 207 $inputAttributes['checked'] = $selected; 208 $inputAttributes['disabled'] = $disabled; 209 210 $input = sprintf( 211 '<input %s%s', 212 $this->createAttributesString($inputAttributes), 213 $closingBracket 214 ); 215 216 if (null !== ($translator = $this->getTranslator())) { 217 $label = $translator->translate( 218 $label, 219 $this->getTranslatorTextDomain() 220 ); 221 } 222 223 if (! $element instanceof LabelAwareInterface || ! $element->getLabelOption('disable_html_escape')) { 224 $label = $escapeHtmlHelper($label); 225 } 226 227 $labelOpen = $labelHelper->openTag($labelAttributes); 228 $template = $labelOpen . '%s%s' . $labelClose; 229 switch ($labelPosition) { 230 case self::LABEL_PREPEND: 231 $markup = sprintf($template, $label, $input); 232 break; 233 case self::LABEL_APPEND: 234 default: 235 $markup = sprintf($template, $input, $label); 236 break; 237 } 238 239 $combinedMarkup[] = $markup; 240 } 241 242 return implode($this->getSeparator(), $combinedMarkup); 243 } 244 245 /** 246 * Render a hidden element for empty/unchecked value 247 * 248 * @param MultiCheckboxElement $element 249 * @param array $attributes 250 * @return string 251 */ 252 protected function renderHiddenElement(MultiCheckboxElement $element, array $attributes) 253 { 254 $closingBracket = $this->getInlineClosingBracket(); 255 256 $uncheckedValue = $element->getUncheckedValue() 257 ? $element->getUncheckedValue() 258 : $this->uncheckedValue; 259 260 $hiddenAttributes = array( 261 'name' => $element->getName(), 262 'value' => $uncheckedValue, 263 ); 264 265 return sprintf( 266 '<input type="hidden" %s%s', 267 $this->createAttributesString($hiddenAttributes), 268 $closingBracket 269 ); 270 } 271 272 /** 273 * Sets the attributes applied to option label. 274 * 275 * @param array|null $attributes 276 * @return FormMultiCheckbox 277 */ 278 public function setLabelAttributes($attributes) 279 { 280 $this->labelAttributes = $attributes; 281 return $this; 282 } 283 284 /** 285 * Returns the attributes applied to each option label. 286 * 287 * @return array|null 288 */ 289 public function getLabelAttributes() 290 { 291 return $this->labelAttributes; 292 } 293 294 /** 295 * Set value for labelPosition 296 * 297 * @param mixed $labelPosition 298 * @throws Exception\InvalidArgumentException 299 * @return FormMultiCheckbox 300 */ 301 public function setLabelPosition($labelPosition) 302 { 303 $labelPosition = strtolower($labelPosition); 304 if (!in_array($labelPosition, array(self::LABEL_APPEND, self::LABEL_PREPEND))) { 305 throw new Exception\InvalidArgumentException(sprintf( 306 '%s expects either %s::LABEL_APPEND or %s::LABEL_PREPEND; received "%s"', 307 __METHOD__, 308 __CLASS__, 309 __CLASS__, 310 (string) $labelPosition 311 )); 312 } 313 $this->labelPosition = $labelPosition; 314 315 return $this; 316 } 317 318 /** 319 * Get position of label 320 * 321 * @return string 322 */ 323 public function getLabelPosition() 324 { 325 return $this->labelPosition; 326 } 327 328 /** 329 * Set separator string for checkbox elements 330 * 331 * @param string $separator 332 * @return FormMultiCheckbox 333 */ 334 public function setSeparator($separator) 335 { 336 $this->separator = (string) $separator; 337 return $this; 338 } 339 340 /** 341 * Get separator for checkbox elements 342 * 343 * @return string 344 */ 345 public function getSeparator() 346 { 347 return $this->separator; 348 } 349 350 /** 351 * Sets the option for prefixing the element with a hidden element 352 * for the unset value. 353 * 354 * @param bool $useHiddenElement 355 * @return FormMultiCheckbox 356 */ 357 public function setUseHiddenElement($useHiddenElement) 358 { 359 $this->useHiddenElement = (bool) $useHiddenElement; 360 return $this; 361 } 362 363 /** 364 * Returns the option for prefixing the element with a hidden element 365 * for the unset value. 366 * 367 * @return bool 368 */ 369 public function getUseHiddenElement() 370 { 371 return $this->useHiddenElement; 372 } 373 374 /** 375 * Sets the unchecked value used when "UseHiddenElement" is turned on. 376 * 377 * @param bool $value 378 * @return FormMultiCheckbox 379 */ 380 public function setUncheckedValue($value) 381 { 382 $this->uncheckedValue = $value; 383 return $this; 384 } 385 386 /** 387 * Returns the unchecked value used when "UseHiddenElement" is turned on. 388 * 389 * @return string 390 */ 391 public function getUncheckedValue() 392 { 393 return $this->uncheckedValue; 394 } 395 396 /** 397 * Return input type 398 * 399 * @return string 400 */ 401 protected function getInputType() 402 { 403 return 'checkbox'; 404 } 405 406 /** 407 * Get element name 408 * 409 * @param ElementInterface $element 410 * @throws Exception\DomainException 411 * @return string 412 */ 413 protected static function getName(ElementInterface $element) 414 { 415 $name = $element->getName(); 416 if ($name === null || $name === '') { 417 throw new Exception\DomainException(sprintf( 418 '%s requires that the element has an assigned name; none discovered', 419 __METHOD__ 420 )); 421 } 422 return $name . '[]'; 423 } 424 425 /** 426 * Retrieve the FormInput helper 427 * 428 * @return FormInput 429 */ 430 protected function getInputHelper() 431 { 432 if ($this->inputHelper) { 433 return $this->inputHelper; 434 } 435 436 if (method_exists($this->view, 'plugin')) { 437 $this->inputHelper = $this->view->plugin('form_input'); 438 } 439 440 if (!$this->inputHelper instanceof FormInput) { 441 $this->inputHelper = new FormInput(); 442 } 443 444 return $this->inputHelper; 445 } 446 447 /** 448 * Retrieve the FormLabel helper 449 * 450 * @return FormLabel 451 */ 452 protected function getLabelHelper() 453 { 454 if ($this->labelHelper) { 455 return $this->labelHelper; 456 } 457 458 if (method_exists($this->view, 'plugin')) { 459 $this->labelHelper = $this->view->plugin('form_label'); 460 } 461 462 if (!$this->labelHelper instanceof FormLabel) { 463 $this->labelHelper = new FormLabel(); 464 } 465 466 return $this->labelHelper; 467 } 468} 469