_dfRelations = [ * [ * 'element' => 'moreInfoType', * 'event' => 'click', // 'change' - default * 'rules' => [ * [ * 'value' => 'none', * 'elements' => [ * ], * [ * 'value' => 'url', * 'elements' => ['moreInfoUrl'] * ], * [ * 'value' => 'page', * 'elements' => ['moreInfoPage'] * ], * ] * ], * ]; * @var array Node relations that will be converted to format compatible with jQuery.dynamicForm2() */ protected $_dfRelations = []; /** * @var array List on elements names that will be preserved in method $this->_dfClearUnusedValues() */ protected $_dfPreserveFields = []; protected $_dfHiddenFields = []; protected $_dfData = []; protected function isElementRequired($name) { return Qs_Request::isGet() || $this->isVisible($name); } protected function isVisible($name) { $data = $this->_getData(); if (($belongsTo = $this->getElementsBelongTo()) && array_key_exists($belongsTo, $data)) { $data = Qs_Array::get($data, $belongsTo); } $this->_dfInitData($data); $hiddenFields = $this->getHiddenFields(); return !in_array($name, $hiddenFields); } /** * Clears values of hidden nodes (values of elements listed in $this->_dfPreserveFields will be preserved) * @param array $values * @return $this * @throws \Exception */ public function dfClearUnusedValues(array &$values) { $this->_dfInitData($values); $hiddenFields = $this->getHiddenFields(); $hiddenFields = array_diff($hiddenFields, $this->_dfPreserveFields); $values = array_merge($values, array_fill_keys($hiddenFields, null)); return $this; } protected function _dfInitData($data) { if ($data == $this->_dfData) { return $this; } $this->_dfData = $data; $this->_dfHiddenFields = $this->_findHiddenElements($data, $this->_dfRelations); return $this; } protected function _findHiddenElements(array $data, array $relations) { $hidden = $this->_findElements($relations); $visible = $this->_findVisibleElements($data, $relations); $hiddenFields = array_diff($hidden, $visible); return $hiddenFields; } protected function _findVisibleElements(array $data, array $relations) { $elements = []; foreach ($relations as $relation) { if (!($element = Qs_Array::get($relation, 'element'))) { trigger_error('DynamicForm: undefined relation element'); continue; } $value = Qs_Array::get($data, $element); foreach ($relation['rules'] as $rule) { $ruleValue = $this->getRuleValue($rule); if ($this->match($ruleValue, $value)) { $elements = array_merge($elements, $rule['elements']); if (!empty($rule['subRelations'])) { $elements = array_merge($elements, $this->_findVisibleElements($data, $rule['subRelations'])); } } } } return $elements; } protected function _findElements(array $relations) { $elements = []; foreach ($relations as $relation) { if (!($element = Qs_Array::get($relation, 'element'))) { trigger_error('DynamicForm: undefined relation element'); continue; } foreach ($relation['rules'] as $rule) { $elements = array_merge($elements, $rule['elements']); if (!empty($rule['subRelations'])) { $elements = array_merge($elements, $this->_findElements($rule['subRelations'])); } } } return $elements; } protected function getRuleValue(array $rule) { return array_key_exists('value', $rule) ? $rule['value'] : null; } protected function getHiddenFields() { return $this->_dfHiddenFields; } protected function walkRelations($callback, $relations = null) { if (null === $relations) { $relations = $this->_dfRelations; } foreach ($relations as $relation) { if (false === call_user_func($callback, $relation)) { return false; } foreach ($relation['rules'] as $rule) { if (empty($rule['subRelations'])) { continue; } if (false === $this->walkRelations($callback, $rule['subRelations'])) { return false; } } } return true; } public function dfGetElements() { $triggerElements = []; $targetElements = []; $this->walkRelations(function ($relation) use (&$triggerElements, &$targetElements) { $triggerElements[] = $relation['element']; foreach ($relation['rules'] as $rule) { $targetElements = array_merge($targetElements, $rule['elements']); } }); $result = []; foreach (array_unique(array_merge($triggerElements, $targetElements)) as $name) { $result[] = [ 'id' => $this->_dfGetElementId($name), 'name' => $name, 'selector' => $this->_dfGetElementSelector($name), 'type' => $this->_dfGetElementType($name), 'trigger' => in_array($name, $triggerElements), 'target' => in_array($name, $targetElements), ]; } return $result; } protected function _dfGetElementType($name) { if (($item = $this->getItem($name))) { $type = get_class($item); $type = str_replace(['Zend_Form_Element_', 'Qs_Form_Element_'], '', $type); $type = lcfirst($type); return $type; } return null; } protected function _dfGetElementSelector($name) { if (!($element = $this->getElement($name))) { return null; } if ($element instanceof Zend_Form_Element_Multi && !$element instanceof Zend_Form_Element_Select) { return '[name="' . $element->getFullyQualifiedName() . '"]'; } return '#' . $element->getId(); } /** * @param $name * @return null|\Qs_Form_SubForm|\Zend_Form_DisplayGroup|\Zend_Form_Element */ public function getItem($name) { if (!($item = $this->getElement($name))) { if (!($item = $this->getDisplayGroup($name))) { if (!($item = $this->getSubForm($name))) { return null; } } } return $item; } protected function _dfGetElementId($name) { if (($item = $this->getItem($name))) { return $item->getId(); } return null; } protected function _addResources() { $this->getDoc()->addScript('js/jquery.dynamicForm2.js'); $id = $this->getId(); if ($this instanceof Qs_Form_SubForm) { $id .= '-element'; } $this->getDoc()->addInitFunction( '$("#' . $id . '").dynamicForm2', [[ 'relations' => $this->_dfRelations, 'elements' => $this->dfGetElements(), ]] ); return parent::_addResources(); } protected function match($ruleValue, $targetValue) { if ($ruleValue instanceof \Closure) { return $ruleValue($targetValue); } if (is_array($ruleValue)) { return in_array($targetValue, $ruleValue); } return $ruleValue == $targetValue; } public function getValues($suppressArrayNotation = false) { $values = parent::getValues($suppressArrayNotation); $this->dfClearUnusedValues($values); return $values; } }