* @package tao */ abstract class tao_helpers_form_Form { /** * the form name * * @access protected * @var string */ protected $name = ''; /** * the list of element composing the form * * @access protected * @var tao_helpers_form_FormElement[] */ protected $elements = []; /** * the actions of the form by context * * @access protected * @var tao_helpers_form_FormElement[][] */ protected $actions = []; /** * if the form is valid or not * * @access public * @var bool */ public $valid = false; /** * if the form has been submited or not * * @access protected * @var bool */ protected $submited = false; /** * It represents the logicall groups * * @access protected * @var array */ protected $groups = []; /** * The list of Decorator linked to the form * * @access protected * @var array */ protected $decorators = []; /** * The form's options * * @access protected * @var array */ protected $options = []; /** * Global form error message * * @access public * @var string */ public $error = ''; /** * List of fields names that are system only and which values doesn't need to be returned by `getValues()` call * * @access protected * @var array */ protected $systemElements = []; /** * the form constructor * * @access public * @author Cédric Alfonsi, * @param string $name * @param array $options */ public function __construct($name = '', array $options = []) { $this->name = $name; $this->options = $options; } /** * set the form name * * @access public * @author Cédric Alfonsi, * @param string $name */ public function setName($name) { $this->name = $name; } /** * Get the form name * * @access public * @author Cédric Alfonsi, * @return string */ public function getName() { return (string) $this->name; } /** * set the form options * * @access public * @author Cédric Alfonsi, * @param array $options */ public function setOptions(array $options) { $this->options = $options; } /** * Has an element of the form identified by it's name * * @param string $name * * @return bool */ public function hasElement($name) { foreach ($this->elements as $element) { if ($element->getName() === $name) { return true; } } return false; } /** * get an element of the form identified by it's name * * @access public * @author Cédric Alfonsi, * @param string $name * @return tao_helpers_form_FormElement */ public function getElement($name) { $returnValue = null; foreach ($this->elements as $element) { if ($element->getName() === $name) { $returnValue = $element; break; } } if ($returnValue === null) { common_Logger::w('Element with name "' . $name . '" not found'); } return $returnValue; } /** * get all the form elements * * @access public * @author Cédric Alfonsi, * @return array */ public function getElements() { return $this->elements; } /** * Define the list of form elements * * @access public * @author Cédric Alfonsi, * @param array $elements */ public function setElements(array $elements) { $this->elements = $elements; } /** * Remove an element identified by it's name. * * @access public * @author Cédric Alfonsi, * @param string $name * @return bool */ public function removeElement($name) { $returnValue = false; foreach ($this->elements as $index => $element) { if ($element->getName() === $name) { unset($this->elements[$index]); $groupName = $this->getElementGroup($name); if (!empty($groupName) && isset($this->groups[$groupName]['elements'][$name])) { unset($this->groups[$groupName]['elements'][$name]); } $returnValue = true; } } return $returnValue; } /** * Add an element to the form * * @access public * @author Cédric Alfonsi, * @param tao_helpers_form_FormElement $element * @param bool|false $isSystem */ public function addElement(tao_helpers_form_FormElement $element, $isSystem = false) { $elementPosition = -1; foreach ($this->elements as $i => $elt) { if ($elt->getName() === $element->getName()) { $elementPosition = $i; break; } } if ($elementPosition >= 0) { $this->elements[$elementPosition] = $element; } else { $this->elements[] = $element; } if ($isSystem) { $this->systemElements[] = $element->getName(); } } /** * Define the form actions for a context. * The different contexts are top and bottom. * * @access public * @author Cédric Alfonsi, * * @param array $actions * @param string $context * * @throws Exception */ public function setActions($actions, $context = 'bottom') { $this->actions[$context] = []; foreach ($actions as $action) { if (!$action instanceof tao_helpers_form_FormElement) { throw new Exception('The actions parameter must only contains instances of tao_helpers_form_FormElement'); } $this->actions[$context][] = $action; } } /** * Get the defined actions for a context * * @access public * @author Cédric Alfonsi, * @param string $context * @return array */ public function getActions($context = 'bottom') { $returnValue = []; if (isset($this->actions[$context])) { $returnValue = $this->actions[$context]; } return (array) $returnValue; } /** * Get specific action element from context * @param $name * @param string $context * * @return mixed */ public function getAction($name, $context = 'bottom') { $returnValue = null; foreach ($this->getActions($context) as $element) { if ($element->getName() === $name) { $returnValue = $element; break; } } if ($returnValue === null) { common_Logger::w('Action with name \'' . $name . '\' not found'); } return $returnValue; } /** * Set the decorator of the type defined in parameter. * The different types are element, error, group. * By default it uses the element decorator. * * @access public * @author Cédric Alfonsi, * * @param tao_helpers_form_Decorator $decorator * @param string $type type */ public function setDecorator(tao_helpers_form_Decorator $decorator, $type = 'element') { $this->decorators[$type] = $decorator; } /** * Set the form decorators * * @access public * @author Cédric Alfonsi, * @param array decorators */ public function setDecorators($decorators) { foreach ($decorators as $type => $decorator) { $this->setDecorator($decorator, $type); } } /** * Get the decorator of the type defined in parameter. * The different types are element, error, group. * By default it uses the element decorator. * * @access public * @author Cédric Alfonsi, * @param string $type * @return tao_helpers_form_Decorator */ public function getDecorator($type = 'element') { $returnValue = null; if (array_key_exists($type, $this->decorators)) { $returnValue = $this->decorators[$type]; } return $returnValue; } /** * render all the form elements * * @access public * @author Cédric Alfonsi, * @return string */ public function renderElements() { $returnValue = ''; foreach ($this->elements as $element) { if ($this->getElementGroup($element->getName()) !== '') { continue; } if ($this->getDecorator() !== null && !($element instanceof tao_helpers_form_elements_Hidden)) { $returnValue .= $this->getDecorator()->preRender(); } if (!$this->isValid() && $element->getError()) { $element->addClass('error'); } //render element $returnValue .= $element->render(); //render element help $help = trim($element->getHelp()); if (!empty($help)) { if ($this->getDecorator('help') !== null) { $returnValue .= $this->getDecorator('help')->preRender(); } $returnValue .= $help; if ($this->getDecorator('help') !== null) { $returnValue .= $this->getDecorator('help')->postRender(); } } //render error message if (!$this->isValid() && $element->getError()) { if ($this->getDecorator('error') !== null) { $returnValue .= $this->getDecorator('error')->preRender(); } $returnValue .= nl2br($element->getError()); if ($this->getDecorator('error') !== null) { $returnValue .= $this->getDecorator('error')->postRender(); } } if (!$element instanceof tao_helpers_form_elements_Hidden && $this->getDecorator() !== null) { $returnValue .= $this->getDecorator()->postRender(); } } $subGroupDecorator = null; if ($this->getDecorator('group') instanceof tao_helpers_form_Decorator) { $decoratorClass = get_class($this->getDecorator('group')); $subGroupDecorator = new $decoratorClass(); } //render group foreach ($this->groups as $groupName => $group) { if ($this->getDecorator('group') !== null) { $this->getDecorator('group')->setOption('id', $groupName); if (isset($group['options']['class'])) { $cssClasses = explode(' ', $this->getDecorator('group')->getOption('cssClass')); $currentClasses = array_map('trim', $cssClasses); if (!in_array($group['options']['class'], $currentClasses, true)) { $currentClasses[] = $group['options']['class']; $this->getDecorator('group')->setOption( 'cssClass', implode(' ', array_unique($currentClasses)) ); } } $returnValue .= $this->getDecorator('group')->preRender(); } $returnValue .= $group['title']; if ($subGroupDecorator instanceof tao_helpers_form_Decorator) { $returnValue .= $subGroupDecorator->preRender(); } foreach ($group['elements'] as $elementName) { if ($this->getElementGroup($elementName) === $groupName && $element = $this->getElement($elementName)) { if ($this->getDecorator() !== null) { $returnValue .= $this->getDecorator()->preRender(); } //render element if (! $this->isValid() && $element->getError()) { $element->addClass('error'); } $returnValue .= $element->render(); //render element help $help = trim($element->getHelp()); if (!empty($help)) { if ($this->getDecorator('help') !== null) { $returnValue .= $this->getDecorator('help')->preRender(); } $returnValue .= $help; if ($this->getDecorator('help') !== null) { $returnValue .= $this->getDecorator('help')->postRender(); } } //render error message if (!$this->isValid() && $element->getError()) { if ($this->getDecorator('error') !== null) { $returnValue .= $this->getDecorator('error')->preRender(); } $returnValue .= nl2br($element->getError()); if ($this->getDecorator('error') !== null) { $returnValue .= $this->getDecorator('error')->postRender(); } } if ($this->getDecorator() !== null) { $returnValue .= $this->getDecorator()->postRender(); } } } if ($subGroupDecorator instanceof tao_helpers_form_Decorator) { $returnValue .= $subGroupDecorator->postRender(); } if ($this->getDecorator('group') !== null) { $returnValue .= $this->getDecorator('group')->postRender(); $this->getDecorator('group')->setOption('id', ''); } } return $returnValue; } /** * render the form actions by context * * @access public * @author Cédric Alfonsi, * @param string $context * @return string */ public function renderActions($context = 'bottom') { $returnValue = ''; if (isset($this->actions[$context])) { $decorator = null; if ($this->getDecorator('actions-' . $context) !== null) { $decorator = $this->getDecorator('actions-' . $context); } elseif ($this->getDecorator('actions') !== null) { $decorator = $this->getDecorator('actions'); } if ($decorator !== null) { $returnValue .= $decorator->preRender(); } foreach ($this->actions[$context] as $action) { $returnValue .= $action->render(); } if ($decorator !== null) { $returnValue .= $decorator->postRender(); } } return $returnValue; } /** * Initialize the elements set */ protected function initElements() { } /** * Check if the form contains a file upload element * * @access public * @author Cédric Alfonsi, * @return bool */ public function hasFileUpload() { $returnValue = false; foreach ($this->elements as $element) { if ($element instanceof tao_helpers_form_elements_File) { $returnValue = true; break; } } return $returnValue; } /** * Enables you to know if the form is valid * * @access public * @author Cédric Alfonsi, * @return bool */ public function isValid() { return $this->valid; } /** * Enables you to know if the form has been submited * * @access public * @author Cédric Alfonsi, * @return bool */ public function isSubmited() { return $this->submited; } /** * Update manually the values of the form * * @access public * @author Cédric Alfonsi, * @param array $values */ public function setValues($values) { foreach ($values as $key => $value) { foreach ($this->elements as $element) { if ($element->getName() === $key) { if ( $element instanceof tao_helpers_form_elements_Checkbox || (method_exists($element, 'setValues') && is_array($value)) ) { $element->setValues($value); } else { $element->setValue($value); } break; } } } } /** * Disables the whole form */ public function disable() { foreach ($this->elements as $element) { $element->disable(); } foreach ($this->actions as $context) { foreach ($context as $action) { $action->disable(); } } } /** * Get the current values of the form * * @access public * @author Cédric Alfonsi, * @param string $groupName * @return array */ abstract public function getValues($groupName = ''); /** * get the current value of the element identified by the name in parameter * * @access public * @author Cédric Alfonsi, * @param string $name * @return boolean */ public function getValue($name) { foreach ($this->elements as $element) { if ($element->getName() === $name) { return $element->getEvaluatedValue(); } } return false; } /** * Short description of method getGroups * * @access public * @author Cédric Alfonsi, * @return array */ public function getGroups() { return $this->groups; } /** * Short description of method setGroups * * @access public * @author Cédric Alfonsi, * @param array $groups */ public function setGroups($groups) { $this->groups = $groups; } /** * Create a logical group of elements * * @access public * @author Cédric Alfonsi, * @param string $groupName * @param string $groupTitle * @param array $elements array of form elements or their identifiers * @param array $options * * @throws common_Exception */ public function createGroup($groupName, $groupTitle = '', array $elements = [], array $options = []) { $identifier = []; foreach ($elements as $element) { if ($element instanceof tao_helpers_form_FormElement) { $identifier[] = $element->getName(); } elseif (is_string($element)) { $identifier[] = $element; } else { throw new common_Exception('Unknown element of type ' . gettype($element) . ' in ' . __FUNCTION__); } } $this->groups[$groupName] = [ 'title' => empty($groupTitle) ? $groupName : $groupTitle, 'elements' => $identifier, 'options' => $options ]; } /** * add an element to a group * * @access public * @author Cédric Alfonsi, * @param string $groupName * @param string $elementName */ public function addToGroup($groupName, $elementName = '') { if ( isset($this->groups[$groupName]['elements']) && !in_array( $elementName, $this->groups[$groupName]['elements'], true ) ) { $this->groups[$groupName]['elements'][] = $elementName; } } /** * get the group where is an element * * @access protected * @author Cédric Alfonsi, * @param string $elementName * @return string */ protected function getElementGroup($elementName) { $returnValue = ''; foreach ($this->groups as $groupName => $group) { if (in_array($elementName, $group['elements'], true)) { $returnValue = $groupName; break; } } return $returnValue; } /** * remove the group identified by the name in parameter * * @access public * @author Cédric Alfonsi, * @param string $groupName */ public function removeGroup($groupName) { if (isset($this->groups[$groupName])) { foreach ($this->groups[$groupName]['elements'] as $element) { $this->removeElement($element); } unset($this->groups[$groupName]); } } public function evaluateInputValues(): void { foreach ($this->elements as $id => $element) { $this->elements[$id]->feedInputValue(); } } /** * evaluate the form inside the current context. Must be overridden, for * rendering mode: for example, it's used to populate and validate the data * the http request for an xhtml context * * @abstract * @access public * @author Cédric Alfonsi, * @return mixed */ abstract public function evaluate(); /** * Render the form. Must be overridden for each rendering mode. * * @abstract * @access public * @author Cédric Alfonsi, * @return string */ abstract public function render(); }