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 RuntimeException; 13use Zend\Form\Element; 14use Zend\Form\ElementInterface; 15use Zend\Form\Element\Collection as CollectionElement; 16use Zend\Form\FieldsetInterface; 17use Zend\Form\LabelAwareInterface; 18use Zend\View\Helper\AbstractHelper as BaseAbstractHelper; 19 20class FormCollection extends AbstractHelper 21{ 22 /** 23 * If set to true, collections are automatically wrapped around a fieldset 24 * 25 * @var bool 26 */ 27 protected $shouldWrap = true; 28 29 /** 30 * This is the default wrapper that the collection is wrapped into 31 * 32 * @var string 33 */ 34 protected $wrapper = '<fieldset%4$s>%2$s%1$s%3$s</fieldset>'; 35 36 /** 37 * This is the default label-wrapper 38 * 39 * @var string 40 */ 41 protected $labelWrapper = '<legend>%s</legend>'; 42 43 /** 44 * Where shall the template-data be inserted into 45 * 46 * @var string 47 */ 48 protected $templateWrapper = '<span data-template="%s"></span>'; 49 50 /** 51 * The name of the default view helper that is used to render sub elements. 52 * 53 * @var string 54 */ 55 protected $defaultElementHelper = 'formrow'; 56 57 /** 58 * The view helper used to render sub elements. 59 * 60 * @var AbstractHelper 61 */ 62 protected $elementHelper; 63 64 /** 65 * The view helper used to render sub fieldsets. 66 * 67 * @var AbstractHelper 68 */ 69 protected $fieldsetHelper; 70 71 /** 72 * Invoke helper as function 73 * 74 * Proxies to {@link render()}. 75 * 76 * @param ElementInterface|null $element 77 * @param bool $wrap 78 * @return string|FormCollection 79 */ 80 public function __invoke(ElementInterface $element = null, $wrap = true) 81 { 82 if (!$element) { 83 return $this; 84 } 85 86 $this->setShouldWrap($wrap); 87 88 return $this->render($element); 89 } 90 91 /** 92 * Render a collection by iterating through all fieldsets and elements 93 * 94 * @param ElementInterface $element 95 * @return string 96 */ 97 public function render(ElementInterface $element) 98 { 99 $renderer = $this->getView(); 100 if (!method_exists($renderer, 'plugin')) { 101 // Bail early if renderer is not pluggable 102 return ''; 103 } 104 105 $markup = ''; 106 $templateMarkup = ''; 107 $elementHelper = $this->getElementHelper(); 108 $fieldsetHelper = $this->getFieldsetHelper(); 109 110 if ($element instanceof CollectionElement && $element->shouldCreateTemplate()) { 111 $templateMarkup = $this->renderTemplate($element); 112 } 113 114 foreach ($element->getIterator() as $elementOrFieldset) { 115 if ($elementOrFieldset instanceof FieldsetInterface) { 116 $markup .= $fieldsetHelper($elementOrFieldset, $this->shouldWrap()); 117 } elseif ($elementOrFieldset instanceof ElementInterface) { 118 $markup .= $elementHelper($elementOrFieldset); 119 } 120 } 121 122 // Every collection is wrapped by a fieldset if needed 123 if ($this->shouldWrap) { 124 $attributes = $element->getAttributes(); 125 unset($attributes['name']); 126 $attributesString = count($attributes) ? ' ' . $this->createAttributesString($attributes) : ''; 127 128 $label = $element->getLabel(); 129 $legend = ''; 130 131 if (!empty($label)) { 132 if (null !== ($translator = $this->getTranslator())) { 133 $label = $translator->translate( 134 $label, 135 $this->getTranslatorTextDomain() 136 ); 137 } 138 139 if (! $element instanceof LabelAwareInterface || ! $element->getLabelOption('disable_html_escape')) { 140 $escapeHtmlHelper = $this->getEscapeHtmlHelper(); 141 $label = $escapeHtmlHelper($label); 142 } 143 144 $legend = sprintf( 145 $this->labelWrapper, 146 $label 147 ); 148 } 149 150 $markup = sprintf( 151 $this->wrapper, 152 $markup, 153 $legend, 154 $templateMarkup, 155 $attributesString 156 ); 157 } else { 158 $markup .= $templateMarkup; 159 } 160 161 return $markup; 162 } 163 164 /** 165 * Only render a template 166 * 167 * @param CollectionElement $collection 168 * @return string 169 */ 170 public function renderTemplate(CollectionElement $collection) 171 { 172 $elementHelper = $this->getElementHelper(); 173 $escapeHtmlAttribHelper = $this->getEscapeHtmlAttrHelper(); 174 $fieldsetHelper = $this->getFieldsetHelper(); 175 176 $templateMarkup = ''; 177 178 $elementOrFieldset = $collection->getTemplateElement(); 179 180 if ($elementOrFieldset instanceof FieldsetInterface) { 181 $templateMarkup .= $fieldsetHelper($elementOrFieldset, $this->shouldWrap()); 182 } elseif ($elementOrFieldset instanceof ElementInterface) { 183 $templateMarkup .= $elementHelper($elementOrFieldset); 184 } 185 186 return sprintf( 187 $this->getTemplateWrapper(), 188 $escapeHtmlAttribHelper($templateMarkup) 189 ); 190 } 191 192 /** 193 * If set to true, collections are automatically wrapped around a fieldset 194 * 195 * @param bool $wrap 196 * @return FormCollection 197 */ 198 public function setShouldWrap($wrap) 199 { 200 $this->shouldWrap = (bool) $wrap; 201 return $this; 202 } 203 204 /** 205 * Get wrapped 206 * 207 * @return bool 208 */ 209 public function shouldWrap() 210 { 211 return $this->shouldWrap; 212 } 213 214 /** 215 * Sets the name of the view helper that should be used to render sub elements. 216 * 217 * @param string $defaultSubHelper The name of the view helper to set. 218 * @return FormCollection 219 */ 220 public function setDefaultElementHelper($defaultSubHelper) 221 { 222 $this->defaultElementHelper = $defaultSubHelper; 223 return $this; 224 } 225 226 /** 227 * Gets the name of the view helper that should be used to render sub elements. 228 * 229 * @return string 230 */ 231 public function getDefaultElementHelper() 232 { 233 return $this->defaultElementHelper; 234 } 235 236 /** 237 * Sets the element helper that should be used by this collection. 238 * 239 * @param AbstractHelper $elementHelper The element helper to use. 240 * @return FormCollection 241 */ 242 public function setElementHelper(AbstractHelper $elementHelper) 243 { 244 $this->elementHelper = $elementHelper; 245 return $this; 246 } 247 248 /** 249 * Retrieve the element helper. 250 * 251 * @return AbstractHelper 252 * @throws RuntimeException 253 */ 254 protected function getElementHelper() 255 { 256 if ($this->elementHelper) { 257 return $this->elementHelper; 258 } 259 260 if (method_exists($this->view, 'plugin')) { 261 $this->elementHelper = $this->view->plugin($this->getDefaultElementHelper()); 262 } 263 264 if (!$this->elementHelper instanceof BaseAbstractHelper) { 265 // @todo Ideally the helper should implement an interface. 266 throw new RuntimeException('Invalid element helper set in FormCollection. The helper must be an instance of AbstractHelper.'); 267 } 268 269 return $this->elementHelper; 270 } 271 272 /** 273 * Sets the fieldset helper that should be used by this collection. 274 * 275 * @param AbstractHelper $fieldsetHelper The fieldset helper to use. 276 * @return FormCollection 277 */ 278 public function setFieldsetHelper(AbstractHelper $fieldsetHelper) 279 { 280 $this->fieldsetHelper = $fieldsetHelper; 281 return $this; 282 } 283 284 /** 285 * Retrieve the fieldset helper. 286 * 287 * @return FormCollection 288 */ 289 protected function getFieldsetHelper() 290 { 291 if ($this->fieldsetHelper) { 292 return $this->fieldsetHelper; 293 } 294 295 return $this; 296 } 297 298 /** 299 * Get the wrapper for the collection 300 * 301 * @return string 302 */ 303 public function getWrapper() 304 { 305 return $this->wrapper; 306 } 307 308 /** 309 * Set the wrapper for this collection 310 * 311 * The string given will be passed through sprintf with the following three 312 * replacements: 313 * 314 * 1. The content of the collection 315 * 2. The label of the collection. If no label is given this will be an empty 316 * string 317 * 3. The template span-tag. This might also be an empty string 318 * 319 * The preset default is <pre><fieldset>%2$s%1$s%3$s</fieldset></pre> 320 * 321 * @param string $wrapper 322 * 323 * @return self 324 */ 325 public function setWrapper($wrapper) 326 { 327 $this->wrapper = $wrapper; 328 329 return $this; 330 } 331 332 /** 333 * Set the label-wrapper 334 * The string will be passed through sprintf with the label as single 335 * parameter 336 * This defaults to '<legend>%s</legend>' 337 * 338 * @param string $labelWrapper 339 * 340 * @return self 341 */ 342 public function setLabelWrapper($labelWrapper) 343 { 344 $this->labelWrapper = $labelWrapper; 345 346 return $this; 347 } 348 349 /** 350 * Get the wrapper for the label 351 * 352 * @return string 353 */ 354 public function getLabelWrapper() 355 { 356 return $this->labelWrapper; 357 } 358 359 /** 360 * Ge the wrapper for the template 361 * 362 * @return string 363 */ 364 public function getTemplateWrapper() 365 { 366 return $this->templateWrapper; 367 } 368 369 /** 370 * Set the string where the template will be inserted into 371 * 372 * This string will be passed through sprintf and has the template as single 373 * parameter 374 * 375 * THis defaults to '<span data-template="%s"></span>' 376 * 377 * @param string $templateWrapper 378 * 379 * @return self 380 */ 381 public function setTemplateWrapper($templateWrapper) 382 { 383 $this->templateWrapper = $templateWrapper; 384 385 return $this; 386 } 387} 388