ZendX_JQueryを使用してZend Frameworkフォームに要素グループを動的に追加する

フォームに要素または要素のグループを追加する必要があることがよくありますが、その数は、フォームの構成で明示的に示すのに十分な数であるか、不明確である場合があります。



また、サブフォームを介して要素のグループを追加するという、この問題を解決するための一般的なアプローチがあることも秘密ではありません。 このアプローチのロジックは単純です-テンプレートでは、javascriptを使用して、要素の必要なグループがフォームに追加され、フォームハンドラーでは、受信する要素のグループの数が計算され、その数に応じてサブフォームが追加され、サブフォームを持つフォーム全体が検証されます



私にとってこのアプローチのマイナス点は、フォームの構成を別の場所(別の構成ファイル)に取り出すことはほとんど不可能であり、フォームハンドラーで "事前構成"する必要があることです。



この機能を実装する別のフォーム要素を作成して、この問題を解決することを提案します。



画像



これの実用的な実装に移りましょう。







新しいZFプロジェクトを作成する



% zf create project www.multielement.lo







application.ini構成ファイルでjQueryサポートを使用してViewオブジェクトを初期化し、ヘルパーへのパスを設定し、javascriptライブラリへのバージョンとパスを設定します



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"









/ application / controllers /にFormControllerを作成します



 <?php class FormController extends Zend_Controller_Action { public function indexAction() { $opts = array( 'elements' => array( 'firstname' => array( 'type' => 'Text', 'options' => array( 'label' => '' ) ), 'lastname' => array( 'type' => 'Text', 'options' => array( 'label' => '' ) ), 'items' => array( 'type' => 'MultiElement', 'options' => array( 'label' => '', 'required' => true, 'elements' => array( 'name' => array( 'type' => 'Text', 'options' => array( 'label' => '', 'required' => true ) ), 'type' => array( 'type' => 'Select', 'options' => array( 'label' => '', 'required' => true, 'multioptions' => array( 'green' => '', 'red' => '', 'blue' => '', ) ) ), 'price' => array( 'type' => 'Text', 'options' => array( 'label' => ', .', 'required' => true ) ), ) ) ), 'logons' => array( 'type' => 'MultiElement', 'options' => array( 'label' => '  ', 'required' => true, 'elements' => array( 'login' => array( 'type' => 'Text', 'options' => array( 'label' => '', 'required' => true ) ), 'passw' => array( 'type' => 'Text', 'options' => array( 'label' => '', 'required' => true ) ), 'type' => array( 'type' => 'Select', 'options' => array( 'label' => ' ', 'required' => true, 'multioptions' => array( 'vk' => '', 'fc' => 'FaceBook', 'tw' => 'Twitter', ) ) ), ) ) ), 'submit' => array( 'type' => 'Submit', 'options' => array( 'label' => '' ) ), ), ); $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()); } }
      
      







/views/scripts/form/index.phtmlにビュースクリプトを作成します



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







画像



ご覧のとおり、MultiElement要素には「要素」オプションセクションが含まれており、Zend_Form_Elementと互換性があります。



MultiElement要素の動作原理は次のとおりです。







 <?php require_once "Zend/Form/Element/Xhtml.php"; class My_JQuery_Form_Element_MultiElement extends Zend_Form_Element_Xhtml { public $helper = "multiElement"; /** *         * @var array */ protected $forms = array(); /** *     * @var Zend_Form */ protected $form; /** *     * @var string */ protected $renderform = ''; /** *        * * @param mixed $spec * @param array $options */ public function __construct($spec, $options = null) { /** *          */ 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; } /** *    */ parent::__construct($spec, $options); } /** *   * * @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('        '); 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; } }
      
      







ご覧のとおり、必要なオプションがサポートされています。 必要に応じて、追加のオプション、フィルター、バリデーターの処理を追加することにより、MultiElement要素を拡張できます



ヘルパーコードを表示



 <?php require_once "ZendX/JQuery/View/Helper/UiWidget.php"; class My_JQuery_View_Helper_MultiElement extends ZendX_JQuery_View_Helper_UiWidget { /** *   * * @param string $id Id HTML- * @param string $value   * @param array $params     options * @return string */ public function multiElement($id, $value = null, array $params = array()) { /** *      *    JS */ $js_var = $id . '_subform'; if(isset($params['renderform'])) { $this->jquery->addJavascript('var ' . $js_var . ' = ' . ZendX_JQuery::encodeJson($params['renderform']) . ';'); } /** *     */ $icon_delete = $this->view->formButton($id . '_delete', '');; /** *          JS */ $button_id = $id . '_add'; $button = $this->view->formButton($button_id, ''); $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), '?', $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)); /** *      */ $xhtml = array(); $xhtml[] = '<ul>'; $attribs = array(); foreach ($params as $k=>$v) if(in_array($k,array('class','style'))) $attribs[$k] = $v; /** *    */ foreach ($params['forms'] as $key=>$form) { $form -> setElementsBelongTo($id . '['.$key.']'); $xhtml[] = '<li' . $this->_htmlAttribs($attribs) . '>' . $form->render() . '</li>'; } /** *  ""  */ 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); } }
      
      







フォーム値はきちんとした階層形式で取得されます



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







残念ながら、BelongToをサポートしていないため、File要素を要素グループに追加できません。 ファイルデコレータにサポートを追加しようとしましたが、変数$ _FILESの多次元配列の処理が不足しているため、Zend_File_Transfer_Adapterで問題が発生しました。



setBelongToで[]なしで接頭辞を形成し、それらが属する要素のグループとは別にファイル配列を処理したり、File要素の代わりに何らかのAjax File Uploaderを使用したりできます。



ここから完全に機能する例をダウンロードできます



All Articles