7 * This source file is subject to the new BSD license that is bundled
8 * with this package in the file LICENSE.txt.
9 * It is also available through the world-wide-web at this URL:
10 * http://framework.zend.com/license/new-bsd
11 * If you did not receive a copy of the license and are unable to
12 * obtain it through the world-wide-web, please send an email
13 * to license@zend.com so we can send you a copy immediately.
17 * @copyright Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
18 * @license http://framework.zend.com/license/new-bsd New BSD License
22 * Zend_Form_DisplayGroup
26 * @copyright Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
27 * @license http://framework.zend.com/license/new-bsd New BSD License
28 * @version $Id: DisplayGroup.php 16218 2009-06-21 19:44:04Z thomas $
30 class Zend_Form_DisplayGroup
implements Iterator
,Countable
36 protected $_attribs = array();
39 * Display group decorators
42 protected $_decorators = array();
48 protected $_description;
51 * Should we disable loading the default decorators?
54 protected $_disableLoadDefaultDecorators = false;
60 protected $_elementOrder = array();
66 protected $_elements = array();
69 * Whether or not a new element has been added to the group
72 protected $_groupUpdated = false;
75 * Plugin loader for decorators
76 * @var Zend_Loader_PluginLoader
95 protected $_translator;
98 * Is translation disabled?
101 protected $_translatorDisabled = false;
104 * @var Zend_View_Interface
111 * @param string $name
112 * @param Zend_Loader_PluginLoader $loader
113 * @param array|Zend_Config $options
116 public function __construct($name, Zend_Loader_PluginLoader
$loader, $options = null)
118 $this->setName($name);
120 $this->setPluginLoader($loader);
122 if (is_array($options)) {
123 $this->setOptions($options);
124 } elseif ($options instanceof Zend_Config
) {
125 $this->setConfig($options);
131 $this->loadDefaultDecorators();
135 * Initialize object; used by extending classes
139 public function init()
146 * @param array $options
147 * @return Zend_Form_DisplayGroup
149 public function setOptions(array $options)
152 'Options', 'Config', 'PluginLoader', 'View',
153 'Translator', 'Attrib'
155 foreach ($options as $key => $value) {
156 $normalized = ucfirst($key);
158 if (in_array($normalized, $forbidden)) {
162 $method = 'set' . $normalized;
163 if (method_exists($this, $method)) {
164 $this->$method($value);
166 $this->setAttrib($key, $value);
173 * Set options from config object
175 * @param Zend_Config $config
176 * @return Zend_Form_DisplayGroup
178 public function setConfig(Zend_Config
$config)
180 return $this->setOptions($config->toArray());
184 * Set group attribute
187 * @param mixed $value
188 * @return Zend_Form_DisplayGroup
190 public function setAttrib($key, $value)
192 $key = (string) $key;
193 $this->_attribs
[$key] = $value;
198 * Add multiple form attributes at once
200 * @param array $attribs
201 * @return Zend_Form_DisplayGroup
203 public function addAttribs(array $attribs)
205 foreach ($attribs as $key => $value) {
206 $this->setAttrib($key, $value);
212 * Set multiple form attributes at once
214 * Overwrites any previously set attributes.
216 * @param array $attribs
217 * @return Zend_Form_DisplayGroup
219 public function setAttribs(array $attribs)
221 $this->clearAttribs();
222 return $this->addAttribs($attribs);
226 * Retrieve a single form attribute
231 public function getAttrib($key)
233 $key = (string) $key;
234 if (!isset($this->_attribs
[$key])) {
238 return $this->_attribs
[$key];
242 * Retrieve all form attributes/metadata
246 public function getAttribs()
248 return $this->_attribs
;
257 public function removeAttrib($key)
259 if (array_key_exists($key, $this->_attribs
)) {
260 unset($this->_attribs
[$key]);
268 * Clear all form attributes
272 public function clearAttribs()
274 $this->_attribs
= array();
279 * Filter a name to only allow valid variable characters
281 * @param string $value
284 public function filterName($value)
286 return preg_replace('/[^a-zA-Z0-9_\x7f-\xff]/', '', (string) $value);
292 * @param string $name
293 * @return Zend_Form_DisplayGroup
295 public function setName($name)
297 $name = $this->filtername($name);
298 if (('0' !== $name) && empty($name)) {
299 require_once 'Zend/Form/Exception.php';
300 throw new Zend_Form_Exception('Invalid name provided; must contain only valid variable characters and be non-empty');
303 $this->_name
= $name;
308 * Retrieve group name
312 public function getName()
318 * Get fully qualified name
320 * Places name as subitem of array and/or appends brackets.
324 public function getFullyQualifiedName()
326 return $this->getName();
334 public function getId()
336 if (isset($this->id
)) {
340 $id = $this->getFullyQualifiedName();
342 // Bail early if no array notation detected
343 if (!strstr($id, '[')) {
347 // Strip array notation
348 if ('[]' == substr($id, -2)) {
349 $id = substr($id, 0, strlen($id) - 2);
351 $id = str_replace('][', '-', $id);
352 $id = str_replace(array(']', '['), '-', $id);
353 $id = trim($id, '-');
361 * @param string $legend
362 * @return Zend_Form_DisplayGroup
364 public function setLegend($legend)
366 return $this->setAttrib('legend', (string) $legend);
370 * Retrieve group legend
374 public function getLegend()
376 return $this->getAttrib('legend');
382 * @param string $value
383 * @return Zend_Form_DisplayGroup
385 public function setDescription($value)
387 $this->_description
= (string) $value;
396 public function getDescription()
398 return $this->_description
;
405 * @return Zend_Form_Element
407 public function setOrder($order)
409 $this->_order
= (int) $order;
414 * Retrieve group order
418 public function getOrder()
420 return $this->_order
;
426 * Add element to stack
428 * @param Zend_Form_Element $element
429 * @return Zend_Form_DisplayGroup
431 public function addElement(Zend_Form_Element
$element)
433 $this->_elements
[$element->getName()] = $element;
434 $this->_groupUpdated
= true;
439 * Add multiple elements at once
441 * @param array $elements
442 * @return Zend_Form_DisplayGroup
443 * @throws Zend_Form_Exception if any element is not a Zend_Form_Element
445 public function addElements(array $elements)
447 foreach ($elements as $element) {
448 if (!$element instanceof Zend_Form_Element
) {
449 require_once 'Zend/Form/Exception.php';
450 throw new Zend_Form_Exception('elements passed via array to addElements() must be Zend_Form_Elements only');
452 $this->addElement($element);
458 * Set multiple elements at once (overwrites)
460 * @param array $elements
461 * @return Zend_Form_DisplayGroup
463 public function setElements(array $elements)
465 $this->clearElements();
466 return $this->addElements($elements);
472 * @param string $name
473 * @return Zend_Form_Element|null
475 public function getElement($name)
477 $name = (string) $name;
478 if (isset($this->_elements
[$name])) {
479 return $this->_elements
[$name];
489 public function getElements()
491 return $this->_elements
;
495 * Remove a single element
497 * @param string $name
500 public function removeElement($name)
502 $name = (string) $name;
503 if (array_key_exists($name, $this->_elements
)) {
504 unset($this->_elements
[$name]);
505 $this->_groupUpdated
= true;
513 * Remove all elements
515 * @return Zend_Form_DisplayGroup
517 public function clearElements()
519 $this->_elements
= array();
520 $this->_groupUpdated
= true;
524 // Plugin loader (for decorators)
529 * @param Zend_Loader_PluginLoader $loader
530 * @return Zend_Form_DisplayGroup
532 public function setPluginLoader(Zend_Loader_PluginLoader
$loader)
534 $this->_loader
= $loader;
539 * Retrieve plugin loader
541 * @return Zend_Loader_PluginLoader
543 public function getPluginLoader()
545 return $this->_loader
;
549 * Add a prefix path for the plugin loader
551 * @param string $prefix
552 * @param string $path
553 * @return Zend_Form_DisplayGroup
555 public function addPrefixPath($prefix, $path)
557 $this->getPluginLoader()->addPrefixPath($prefix, $path);
562 * Add several prefix paths at once
565 * @return Zend_Form_DisplayGroup
567 public function addPrefixPaths(array $spec)
569 if (isset($spec['prefix']) && isset($spec['path'])) {
570 return $this->addPrefixPath($spec['prefix'], $spec['path']);
572 foreach ($spec as $prefix => $paths) {
573 if (is_numeric($prefix) && is_array($paths)) {
575 if (isset($paths['prefix']) && isset($paths['path'])) {
576 $this->addPrefixPath($paths['prefix'], $paths['path']);
578 } elseif (!is_numeric($prefix)) {
579 if (is_string($paths)) {
580 $this->addPrefixPath($prefix, $paths);
581 } elseif (is_array($paths)) {
582 foreach ($paths as $path) {
583 $this->addPrefixPath($prefix, $path);
594 * Set flag to disable loading default decorators
597 * @return Zend_Form_Element
599 public function setDisableLoadDefaultDecorators($flag)
601 $this->_disableLoadDefaultDecorators
= (bool) $flag;
606 * Should we load the default decorators?
610 public function loadDefaultDecoratorsIsDisabled()
612 return $this->_disableLoadDefaultDecorators
;
616 * Load default decorators
620 public function loadDefaultDecorators()
622 if ($this->loadDefaultDecoratorsIsDisabled()) {
626 $decorators = $this->getDecorators();
627 if (empty($decorators)) {
628 $this->addDecorator('FormElements')
629 ->addDecorator('HtmlTag', array('tag' => 'dl'))
630 ->addDecorator('Fieldset')
631 ->addDecorator('DtDdWrapper');
636 * Instantiate a decorator based on class name or class name fragment
638 * @param string $name
639 * @param null|array $options
640 * @return Zend_Form_Decorator_Interface
642 protected function _getDecorator($name, $options = null)
644 $class = $this->getPluginLoader()->load($name);
645 if (null === $options) {
646 $decorator = new $class;
648 $decorator = new $class($options);
655 * Add a decorator for rendering the group
657 * @param string|Zend_Form_Decorator_Interface $decorator
658 * @param array|Zend_Config $options Options with which to initialize decorator
659 * @return Zend_Form_DisplayGroup
661 public function addDecorator($decorator, $options = null)
663 if ($decorator instanceof Zend_Form_Decorator_Interface
) {
664 $name = get_class($decorator);
665 } elseif (is_string($decorator)) {
668 'decorator' => $name,
669 'options' => $options,
671 } elseif (is_array($decorator)) {
672 foreach ($decorator as $name => $spec) {
675 if (is_numeric($name)) {
676 require_once 'Zend/Form/Exception.php';
677 throw new Zend_Form_Exception('Invalid alias provided to addDecorator; must be alphanumeric string');
679 if (is_string($spec)) {
681 'decorator' => $spec,
682 'options' => $options,
684 } elseif ($spec instanceof Zend_Form_Decorator_Interface
) {
688 require_once 'Zend/Form/Exception.php';
689 throw new Zend_Form_Exception('Invalid decorator provided to addDecorator; must be string or Zend_Form_Decorator_Interface');
692 $this->_decorators
[$name] = $decorator;
698 * Add many decorators at once
700 * @param array $decorators
701 * @return Zend_Form_DisplayGroup
703 public function addDecorators(array $decorators)
705 foreach ($decorators as $decoratorInfo) {
706 if (is_string($decoratorInfo)) {
707 $this->addDecorator($decoratorInfo);
708 } elseif ($decoratorInfo instanceof Zend_Form_Decorator_Interface
) {
709 $this->addDecorator($decoratorInfo);
710 } elseif (is_array($decoratorInfo)) {
711 $argc = count($decoratorInfo);
713 if (isset($decoratorInfo['decorator'])) {
714 $decorator = $decoratorInfo['decorator'];
715 if (isset($decoratorInfo['options'])) {
716 $options = $decoratorInfo['options'];
718 $this->addDecorator($decorator, $options);
724 $decorator = array_shift($decoratorInfo);
726 $options = array_shift($decoratorInfo);
728 $this->addDecorator($decorator, $options);
733 require_once 'Zend/Form/Exception.php';
734 throw new Zend_Form_Exception('Invalid decorator passed to addDecorators()');
742 * Overwrite all decorators
744 * @param array $decorators
745 * @return Zend_Form_DisplayGroup
747 public function setDecorators(array $decorators)
749 $this->clearDecorators();
750 return $this->addDecorators($decorators);
754 * Retrieve a registered decorator
756 * @param string $name
757 * @return false|Zend_Form_Decorator_Abstract
759 public function getDecorator($name)
761 if (!isset($this->_decorators
[$name])) {
762 $len = strlen($name);
763 foreach ($this->_decorators
as $localName => $decorator) {
764 if ($len > strlen($localName)) {
768 if (0 === substr_compare($localName, $name, -$len, $len, true)) {
769 if (is_array($decorator)) {
770 return $this->_loadDecorator($decorator, $localName);
778 if (is_array($this->_decorators
[$name])) {
779 return $this->_loadDecorator($this->_decorators
[$name], $name);
782 return $this->_decorators
[$name];
786 * Retrieve all decorators
790 public function getDecorators()
792 foreach ($this->_decorators
as $key => $value) {
793 if (is_array($value)) {
794 $this->_loadDecorator($value, $key);
797 return $this->_decorators
;
801 * Remove a single decorator
803 * @param string $name
806 public function removeDecorator($name)
808 $decorator = $this->getDecorator($name);
810 if (array_key_exists($name, $this->_decorators
)) {
811 unset($this->_decorators
[$name]);
813 $class = get_class($decorator);
814 unset($this->_decorators
[$class]);
823 * Clear all decorators
825 * @return Zend_Form_DisplayGroup
827 public function clearDecorators()
829 $this->_decorators
= array();
836 * @param Zend_View_Interface $view
837 * @return Zend_Form_DisplayGroup
839 public function setView(Zend_View_Interface
$view = null)
841 $this->_view
= $view;
848 * @return Zend_View_Interface
850 public function getView()
852 if (null === $this->_view
) {
853 require_once 'Zend/Controller/Action/HelperBroker.php';
854 $viewRenderer = Zend_Controller_Action_HelperBroker
::getStaticHelper('viewRenderer');
855 $this->setView($viewRenderer->view
);
862 * Render display group
866 public function render(Zend_View_Interface
$view = null)
868 if (null !== $view) {
869 $this->setView($view);
872 foreach ($this->getDecorators() as $decorator) {
873 $decorator->setElement($this);
874 $content = $decorator->render($content);
880 * String representation of group
884 public function __toString()
887 $return = $this->render();
889 } catch (Exception
$e) {
890 trigger_error($e->getMessage(), E_USER_WARNING
);
896 * Set translator object
898 * @param Zend_Translate|Zend_Translate_Adapter|null $translator
899 * @return Zend_Form_DisplayGroup
901 public function setTranslator($translator = null)
903 if ((null === $translator) ||
($translator instanceof Zend_Translate_Adapter
)) {
904 $this->_translator
= $translator;
905 } elseif ($translator instanceof Zend_Translate
) {
906 $this->_translator
= $translator->getAdapter();
908 require_once 'Zend/Form/Exception.php';
909 throw new Zend_Form_Exception('Invalid translator specified');
915 * Retrieve translator object
917 * @return Zend_Translate_Adapter|null
919 public function getTranslator()
921 if ($this->translatorIsDisabled()) {
925 if (null === $this->_translator
) {
926 require_once 'Zend/Form.php';
927 return Zend_Form
::getDefaultTranslator();
930 return $this->_translator
;
934 * Indicate whether or not translation should be disabled
937 * @return Zend_Form_DisplayGroup
939 public function setDisableTranslator($flag)
941 $this->_translatorDisabled
= (bool) $flag;
946 * Is translation disabled?
950 public function translatorIsDisabled()
952 return $this->_translatorDisabled
;
956 * Overloading: allow rendering specific decorators
958 * Call renderDecoratorName() to render a specific decorator.
960 * @param string $method
963 * @throws Zend_Form_Exception for invalid decorator or invalid method call
965 public function __call($method, $args)
967 if ('render' == substr($method, 0, 6)) {
968 $decoratorName = substr($method, 6);
969 if (false !== ($decorator = $this->getDecorator($decoratorName))) {
970 $decorator->setElement($this);
972 if (0 < count($args)) {
973 $seed = array_shift($args);
975 return $decorator->render($seed);
978 require_once 'Zend/Form/Exception.php';
979 throw new Zend_Form_Exception(sprintf('Decorator by name %s does not exist', $decoratorName));
982 require_once 'Zend/Form/Exception.php';
983 throw new Zend_Form_Exception(sprintf('Method %s does not exist', $method));
986 // Interfaces: Iterator, Countable
991 * @return Zend_Form_Element
993 public function current()
996 current($this->_elementOrder
);
997 $key = key($this->_elementOrder
);
998 return $this->getElement($key);
1006 public function key()
1009 return key($this->_elementOrder
);
1013 * Move pointer to next element
1017 public function next()
1020 next($this->_elementOrder
);
1024 * Move pointer to beginning of element loop
1028 public function rewind()
1031 reset($this->_elementOrder
);
1035 * Determine if current element/subform/display group is valid
1039 public function valid()
1042 return (current($this->_elementOrder
) !== false);
1046 * Count of elements/subforms that are iterable
1050 public function count()
1052 return count($this->_elements
);
1056 * Sort items according to their order
1060 protected function _sort()
1062 if ($this->_groupUpdated ||
!is_array($this->_elementOrder
)) {
1063 $elementOrder = array();
1064 foreach ($this->getElements() as $key => $element) {
1065 $elementOrder[$key] = $element->getOrder();
1070 foreach ($elementOrder as $key => $order) {
1071 if (null === $order) {
1072 while (array_search($index, $elementOrder, true)) {
1075 $items[$index] = $key;
1078 $items[$order] = $key;
1082 $items = array_flip($items);
1084 $this->_elementOrder
= $items;
1085 $this->_groupUpdated
= false;
1090 * Lazy-load a decorator
1092 * @param array $decorator Decorator type and options
1093 * @param mixed $name Decorator name or alias
1094 * @return Zend_Form_Decorator_Interface
1096 protected function _loadDecorator(array $decorator, $name)
1099 if ($name == $decorator['decorator']) {
1103 $instance = $this->_getDecorator($decorator['decorator'], $decorator['options']);
1105 $newName = get_class($instance);
1106 $decoratorNames = array_keys($this->_decorators
);
1107 $order = array_flip($decoratorNames);
1108 $order[$newName] = $order[$name];
1109 $decoratorsExchange = array();
1110 unset($order[$name]);
1112 foreach ($order as $key => $index) {
1113 if ($key == $newName) {
1114 $decoratorsExchange[$key] = $instance;
1117 $decoratorsExchange[$key] = $this->_decorators
[$key];
1119 $this->_decorators
= $decoratorsExchange;
1121 $this->_decorators
[$name] = $instance;