Dynamic addition of groups of elements in the forms Zend Framework using ZendX_JQuery

it's No secret that it is often necessary to add to the form elements or groups of elements, the number of which may be uncertain or large enough to specify them explicitly in the configuration form.

Also, it is no secret that there is a common approach to solving this problem — adding groups of elements using sub-shapes a more. The logic of this approach is simple in the template via JavaScript to the form are added to the required groups of elements, in the form handler counts the number of received groups of elements and, according to their number, add sub-shapes a more, then the entire form with subforma validinputs.

For me, the downside of this approach is that it is almost impossible to make a form configuration in a separate place (in a separate configuration file), and it has "deconfiguring" in the form handler.

I propose to solve this problem by creating a separate form element that implements this functionality.

image

Let's move on to the practical implementation of it.



Create a new ZF project

% zf create project www.multielement.lo

Initialize the View object with support for JQuery in the configuration application file.ini, set the path to our helpers, version and path to the javascript libraries

resources.view[] = ""
resources.view.helperPath.ZendX_JQuery_View_Helper = "ZendX/JQuery/View/Helper"
resources.view.helperPath.My_JQuery_View_Helper = "My/JQuery/View/Helper"
resources.jquery.version = "1.7"


Create a FormController in /application/controllers/

the
<?php
class FormController extends Zend_Controller_Action
{
public function indexAction()
{
$opts = array(
'elements' => array(
'firstname' => array(
'type' => 'Text',
'options' => array(
'label' => 'Name'
)
),
'lastname' => array(
'type' => 'Text',
'options' => array(
'label' => 'Name'
)
),
'items' => array(
'type' => 'MultiElement',
'options' => array(
'label' => 'Products',
'required' => true,
'elements' => array(
'name' => array(
'type' => 'Text',
'options' => array(
'label' => 'Name',
'required' => true
)
),
'type' => array(
'type' => 'Select',
'options' => array(
'label' => 'Color',
'required' => true,
'multioptions' => array(
'green' => 'Green',
'red' => 'Red',
'blue' => 'Blue',
)
)
),
'price' => array(
'type' => 'Text',
'options' => array(
'label' => 'Cost,$.',
'required' => true
)
),
)
)
),
'logons' => array(
'type' => 'MultiElement',
'options' => array(
'label' => 'Turnout and passwords',
'required' => true,
'elements' => array(
'login' => array(
'type' => 'Text',
'options' => array(
'label' => 'Login',
'required' => true
)
),
'passw' => array(
'type' => 'Text',
'options' => array(
'label' => 'Password',
'required' => true
)
),
'type' => array(
'type' => 'Select',
'options' => array(
'label' => 'the Social network',
'required' => true,
'multioptions' => array(
'vk' => 'OpenID',
'fc' => 'FaceBook',
'tw' => 'Twitter',
)
)
),
)
)
),
'submit' => array(
'type' => 'Submit',
'options' => array(
'label' => 'Send'
)
),
),
);
$form = new Zend_Form();
$form- > addPrefixPath('My_JQuery_Form','My/JQuery/Form');
$form->setOptions($opts);
if($this->getRequest()->isPost()) {
if($form->isValid($this->getRequest()->getPost())) {
$values = $form->getValues();
$this->view->assign('MyFormValues',$values);
}
}
$this->view->assign('MyForm',$form->render());
}
}


Create a view script in /views/scripts/form/index.phtml

the
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Example form with multielemental</title>
<?php print $this->JQuery(); ?>
</head>
<body class="ui-widget">
<h1>Example form with multielemental</h1>
<?php print $this- > MyForm; ?>
<?php if($this->MyFormValues) { ?>
<pre>
<?php print_r($this->MyFormValues); ?>
</pre>
<?php } ?>
</body>
</html>


image

Apparently, the MultiElement element contains the section options, "elements", which is compatible with Zend_Form_Element.
The principle of operation of the element MultiElement is as follows:

the
    the
  • For each group of items creates a form Zend_Form without Form and DtDdWrapper decorators
  • the
  • to Each item in the group by setElementsBelongTo is set to the name of the form element[][elementgroup]
  • the
  • Form being rendered and put in the options element of the MultiElement for later use in the view helper
  • the form Object is placed in a private property to further use his cloned copies in the methods isValid and setValue the

  • In the view helper is rendered form of the group is placed in a js variable and is added to the View object via addJavascript
  • the
  • In the view helper creates a button "Add", and js to handle clicks on it
  • the
  • When you click insert a rendered form of the js-variable with replacement element names to the element[count][elementgroup]
  • the
  • In the view helper is also rendered prishedshie values of groups of elements and groups


the
<?php
require_once "Zend/Form/Element/Xhtml.php";
class My_JQuery_Form_Element_MultiElement extends Zend_Form_Element_Xhtml {
public $helper = "multiElement";
/**
* An array of form objects for each group of additional elements
* @var array
*/
protected $forms = array();
/**
* The form object for more items
* @var Zend_Form
*/
protected $form;
/**
* Rendered form additional elements
* @var string
*/
protected $renderform = ";
/**
* Define the options array and pull the parent's constructor
*
* @param mixed $spec
* @param array $options
*/
public function __construct($spec, $options = null) {
/**
* Allocated additional elements in a separate form, and will render it
*/
if(isset($options['elements']) && is_array($options['elements'])) {
$form = new Zend_Form(array('elements'=>$options['elements']));
$form - > removeDecorator('Form');
$form - > removeDecorator('DtDdWrapper');
$form - > setElementsBelongTo($spec.'[]');
$this->renderform = $form->render();
unset($options['elements']);
$this->form = $form;
}
/**
* Initialize the parent constructor
*/
parent::__construct($spec, $options);
}

/**
* Validation of the element
*
* @param mixed $value
* @return boolean
*/
public function isValid($value) {
$this->_messages = array();
$this->_errors = array();
$this->setValue($value);
$value = $this->getValue();
if(!is_array($value) && $this->isRequired()) {
$this->_messages = array('Value is required and cannot be empty');
return false;
}
$result = true;
if(is_array($value)) {
foreach ($value as $key=>$mini_form) { 
if(key_exists($key,$this- > forms)) {
$form = $this- > forms[$key];
if(!$form->isValid($mini_form)) $result = false;
}
}
}
return $result;
}

/**
* Set element value
*
* @param array $value
* @return Zend_Form_Element
*/
public function setValue($value) {
if(!is_array($value)) return $this;
$this->_value = $value;
foreach ($value as $mini_form) { 
$form = clone $this->form;
$this- > forms[] = $form->setDefaults($mini_form);
}
return $this;
}
}


As you can see, supported by the required option. If necessary, the element MultiElement can be extended to processing of additional options, filters, and validators

The code of the view helper

the
<?php
require_once "ZendX/JQuery/View/Helper/UiWidget.php";
class My_JQuery_View_Helper_MultiElement extends ZendX_JQuery_View_Helper_UiWidget {
/**
* Draw the element
*
* @param string $id Id of the HTML element
* @param string $value the element Value
* @param array $params an Array of configuration options section
* @return string
*/
public function multiElement($id, $value = null, array $params = array()) {
/**
* The designated form with elements of group
* Add a form in JS
*/
$js_var = $id . '_subform';
if(isset($params['renderform'])) {
$this->jquery->addJavascript('var' . $js_var . the ' =' 
. ZendX_JQuery::encodeJson($params['renderform']) . ';');
}
/**
* Define the delete button groups
*/
$icon_delete = $this->view->formButton($id . '_delete', 'Remove');;
/**
* Generated button add a new group and hang on her JS
*/
$button_id = $id . '_add';
$button = $this->view->formButton($button_id, 'Add');
$jquery_handler = ZendX_JQuery_View_Helper_JQuery::getJQueryHandler();
$js = array();
$js[] = sprintf('%s("#%s").next("ul").find (">li").prepend ('%s (%s).click(function(){
if(confirm("%s")) %s(this).parent("li").remove();
return false;
}));',
$jquery_handler, $button_id, $jquery_handler, addslashes($icon_delete), 'Delete?', $jquery_handler);
$js[] = sprintf('%s("#%s").click(function(){
var itr = %s(this).next("ul").find (">li").length-1;
var Tmpl = %s.replace(/name=\"%s\[\]\[/g,"name=\"%s["+itr+"][");
var li = %s(this).next("ul").find("li:last").clone(true).insertBefore ('%s(this)
.next("ul").find("li:last")).append(Tmpl).show();
});',
$jquery_handler, $button_id, $jquery_handler, $js_var, $id, $id, $jquery_handler, $jquery_handler);
$this->jquery->addOnLoad(join(PHP_EOL,$js));
/**
* Draw a transferred pattern and

$xhtml = array();
$xhtml[] = '<ul>';
$attribs = array();
foreach ($params as $k= > $v) if(in_array($k,array('class','style'))) $attribs[$k] = $v;
/**
* Set the come of value
*/
foreach ($params['forms'] as $key=>$form) {
$form - > setElementsBelongTo($id . '['.$key.']');
$xhtml[] = '<li' . $this->_htmlAttribs($attribs) . '>' . $form->render() . '</li>';
}
/**
* Draw a "tail" of the list
*/
if(isset($attribs['style'])) $attribs['style'] .= ';display:none'; else $attribs['style'] = 'display:none'; 
$xhtml[] = '<li' . $this->_htmlAttribs($attribs) . '></li>';
$xhtml[] = '</ul>';
return $button . join(PHP_EOL,$xhtml);
}
}


Form values are obtained in a neat hierarchical view

the
[firstname] => Bob
[lastname] = > Ivanov
[items] => Array
(
[2] => Array
(
[name] => Butterfly
[type] => red
[price] => 1000
)
[3] => Array
(
[name] => Logs
[type] => blue
[price] => 2000
)
)
[logons] => Array
(
[0] => Array
(
[login] => username
[passw] => qwerty
[type] => vk
)
)


Unfortunately, the group of elements you cannot add a File element, since it does not support BelongTo. I tried to add her support to the designer File, but faced problems in Zend_File_Transfer_Adapter, associated with lack of processing multidimensional arrays in the variable $_FILES.

You can try to generate the prefix in setBelongTo without [] and process the array of files separately from the group of elements to which they belong, or use some Ajax File Uploader instead of the File item.

Download a fully working example can here
Article based on information from habrahabr.ru

Комментарии

Популярные сообщения из этого блога

Car navigation in detail

PostgreSQL: Analytics for DBA

Google has launched an online training course advanced search