2011-03-16 75 views
2

我在Zend框架中构建了一个抽象窗体。这是通过从数据库创建动态表单元素(包含键/值的表)来完成的。 除了表单的setDefault/populate函数外,它大部分工作正常。让我解释。在zend框架中填充子窗体的问题

我有一个主表单,附有3个子表单(向导样式表单)。表单的第3步有5个或更多的动态元素。 (例如:服务器机架的属性)。
步骤3中的表单可以使用ajax进行克隆。 (所以你可以一次添加4个服务器机架)。提交表单时,preValidation()函数将检查所有新的字段并将它们添加到子表单中。 太好了。现在问题开始了。

当添加字段,我用创建表单元素的工厂方法子窗体=>

$subForm->addElement($presentation_type,'fixedField'. $key.'_'.$formStep, 
array('label' => $label.':', 
'required'  => $is_mandatory, 
'filter'  => $filters, 
'value'  => $value, 
'description' => $unit .' '. $description, 
'validators' => $validators)); 

开始一个新的未提交表单时也能正常工作,但没有设定值参数表格提交时,他没有填充值参数(同一个函数中的其他参数工作得很好)。 我升级了zend框架到最新版本,试图找到我在谷歌和论坛上的问题,但没有成功。

如果你解决的话,我会给你寄一份比利时啤酒:) 已经找了3天了。

还尝试使用setDefault函数和填充函数。奇怪的是当我做“echo $ subForm-> getElement('xxxxxx') - > getValue();”我得到正确的输出。所以似乎zend只是不会呈现元素内的值。

控制器代码:

<?php 
class TestController extends Zend_Controller_Action { 
    protected $_form; 
    protected $_namespace = 'TestController'; 
    protected $_session; 

    /** 
    * Gets the add/edit form for the current object 
    * 
    * @access public 
    * @return object|void 
    * @param boolean $search_form Set to true if you want the search form object to be returned 
    */ 
    public function getForm() 
    { 
     if (null === $this->_form) { 
      $action = $this->getRequest()->getActionName(); 
      $this->_form = new Application_Form_Test(); 

     } 
     return $this->_form; 
    } 

    /** 
    * Action for the new page 
    * 
    * @access public 
    * @return void 
    */ 
    public function newAction(){ 


     //Store the parent object in a session, this way we can use the var in the 3th form step 
     $this->getSessionNamespace(); 

     // Either re-display the current page, or grab the "next" 
     // (first) sub form 
     if (!$form = $this->getCurrentSubForm()) { 
      $form = $this->getNextSubForm(); 
     } 

     $this->view->form = $this->getForm()->prepareSubForm($form); 

    } 

    /** 
    * Action to process the multi step form 
    * 
    * @access public 
    * @return mixed 
    */ 
    public function processAction(){ 

     //No form is set 
     if (!$form = $this->getCurrentSubForm()) { 
      return $this->_forward('new'); 
     } 

     if (!$this->subFormIsValid($form, $this->getRequest()->getPost())) { 
      $this->view->form = $this->getForm()->prepareSubForm($form); 
      return $this->render('new'); 
     } 

     if (!$this->formIsValid()) { 
      $form = $this->getNextSubForm(); 
      $this->view->form = $this->getForm()->prepareSubForm($form); 
      return $this->render('new'); 
     } 

     // Valid form! 
     // Let's save everything 
     //...... 

     // All done, clear the sessions 
     Zend_Session::namespaceUnset($this->_namespace); 
     //$this->render('index'); 
     $this->_forward('index'); 
    } 

    /** 
    * Ajax action that returns the dynamic form field for step3 in the form 
    */ 
    public function newajaxformAction() { 

     if(!$this->getRequest()->isXmlHttpRequest()) throw new Zend_Controller_Action_Exception("This isn't a Ajax request !", 404); 

     $ajaxContext = $this->_helper->getHelper('AjaxContext'); 
     $ajaxContext->addActionContext('newfield', 'html')->initContext(); 

     //Disable view 
     $this->_helper->viewRenderer->setNoRender(); 
     $this->_helper->layout()->disableLayout(); 

     $id = $this->_getParam('id', null); 
     $amount = $this->_getParam('amount', null); 

     $fieldsKeys = $_POST['key']; 
     $fieldsValues = $_POST['value']; 

     //This one adds multiple objects on one page 
     $po = new Test_Partial($id,$amount,$fieldsKeys,$fieldsValues); 

     echo $po->__toString(); 
    } 

    /** 
    * Get the session namespace we're using 
    * 
    * @access public 
    * @return Zend_Session_Namespace 
    */ 
    public function getSessionNamespace() 
    { 
     if (null === $this->_session) { 
      $this->_session = new Zend_Session_Namespace($this->_namespace); 
     } 

     return $this->_session; 
    } 

    /** 
    * Get a list of forms already stored in the session 
    * 
    * @access public 
    * @return array 
    */ 
    public function getStoredForms() 
    { 
     $stored = array(); 
     foreach ($this->getSessionNamespace() as $key => $value) { 
      $stored[] = $key; 
     } 

     return $stored; 
    } 

    /** 
    * Get list of all subforms available 
    * 
    * @access public 
    * @return array 
    */ 
    public function getPotentialForms() 
    { 
     return array_keys($this->getForm()->getSubForms()); 
    } 

    /** 
    * What sub form was submitted? 
    * 
    * @access public 
    * @return false|Zend_Form_SubForm 
    */ 
    public function getCurrentSubForm() 
    { 
     $request = $this->getRequest(); 
     if (!$request->isPost()) { 
      return false; 
     } 

     foreach ($this->getPotentialForms() as $name) { 
      if ($data = $request->getPost($name, false)) { 
       if (is_array($data)) { 
        return $this->getForm()->getSubForm($name); 
        break; 
       } 
      } 
     } 

     return false; 
    } 

    /** 
    * Get the next sub form to display 
    * 
    * @return Zend_Form_SubForm|false 
    * @access public 
    */ 
    public function getNextSubForm() 
    { 
     $storedForms = $this->getStoredForms(); 
     $potentialForms = $this->getPotentialForms(); 

     foreach ($potentialForms as $name) { 
      if (!in_array($name, $storedForms)) { 
       return $this->getForm()->getSubForm($name); 
      } 
     } 

     return false; 
    } 

    /** 
    * Is the sub form valid? 
    * 
    * @param Zend_Form_SubForm $subForm 
    * @param array $data 
    * @return bool 
    */ 
    public function subFormIsValid(Zend_Form_SubForm $subForm,array $data) 
    { 
     $name = $subForm->getName(); 

     echo '<br />Submitted data(Send from Controller) = <pre>'; 
      print_r($data); 
     echo '</pre>'; 

     if ($subForm->isValid($data)) { 
      $this->getSessionNamespace()->$name = $subForm->getValues(); 
      return true; 
     } 

     return false; 
    } 

    /** 
    * Is the full form valid? 
    * 
    * @return bool 
    * @access public 
    */ 
    public function formIsValid() 
    { 
     $data = array(); 
     foreach ($this->getSessionNamespace() as $key => $info) { 
      $data[$key] = $info[$key]; 
     } 

     return (count($this->getStoredForms()) < count($this->getPotentialForms()))? false : $this->getForm()->isValid($data); 
    } 
} 
?> 


Form Code: 

<?php 
class Application_Form_Test extends Zend_Form { 

    public function init() { 

     //Set some filters for are fields 
     $this->setElementFilters(array('StringTrim')); 

     //Lets make some subforms = > each subform will be on a different page 
     //Step 1 
     $step1 = new Zend_Form_SubForm(); 

     $step1->addElement('select', 'test', array( 'label' => 'Save in:', 
                'multiOptions' => array('choose'=>'Choose one ...','a'=>'a','b'=>'b'), 
                'required'  => false, 
                'ignore' => true, 
                'value'  => array('choose'), 
                'validators' => array(array('InArray',false,array(array_keys(array('choose'=>'Choose one ...','a'=>'a','b'=>'b'))))))); 



     // Step 3 
     $step2 = new Zend_Form_SubForm(); 

     // Add a remove and add button for the dynamic forms 
     $step2->addElement('text', 'addFormAmount', array('label' => '', 
                 'required' => false, 
                 'ignore'=> true, 
                 'value'  => 1, 
                 'description' => 'objects.', 
                 'order' => 99992 
     )); 

     $step2->addElement('button', 'addForm', array('label' => 'Add', 
                 'order' => 99991 
     )); 

     $step2->getElement('addForm')->setAttrib('onClick', 'ajaxAddForm();'); 

     // Add a hidden id field, this way we can use the id in javascript to count the numner of fields 
     $step2->addElement('hidden', 'id', array('value' => 1)); 

      $this->addAbstractField($step2,'',1,'test value'); 


     //test, let's put our prevalidation at the end of the form object 
     $this->preValidation($step2,$_POST); 

     // Finally attach sub forms to main form 
     $this->addSubForms(array(
      'step1' => $step1, 
      'step2' => $step2 
     )); 
    } 


    /** 
    * Create a sluggable string for forms or any other uri related string 
    * 
    * @return mixed 
    * @access public 
    * @param mixed $array 
    */ 
    protected function getSlug($string){ 
     $slug = trim($string); // trim the string 
     $slug= preg_replace('/[^a-zA-Z0-9 -]/','',$slug); // only take alphanumerical characters, but keep the spaces and dashes too… 
     $slug= str_replace(' ','-', $slug); // replace spaces by dashes 
     $slug= strtolower($slug); // make it lowercase 
     return $slug; 
    } 

    /** 
    * Prepare a sub form for display 
    * 
    * @param string|Zend_Form_SubForm $spec 
    * @return Zend_Form_SubForm 
    */ 
    public function prepareSubForm($spec) 
    { 
     if (is_string($spec)) { 
      $subForm = $this->{$spec}; 
     } elseif ($spec instanceof Zend_Form_SubForm) { 
      $subForm = $spec; 
     } else { 
      throw new Exception('Invalid argument passed to ' . __FUNCTION__ . '()'); 
     } 
     $this->setSubFormDefaultDecorators($subForm) 
      ->addSubmitButton($subForm) 
      ->addSubFormActions($subForm); 

     return $subForm; 
    } 

    /** 
    * Add form decorators to an individual sub form 
    * 
    * @param Zend_Form_SubForm $subForm 
    * @return My_Form_Registration 
    */ 
    public function setSubFormDefaultDecorators(Zend_Form_SubForm $subForm) 
    { 
     $subForm->setDecorators(array(
      'FormElements', 
      array('HtmlTag', array('tag' => 'dl','class' => 'zend_form')),'Form',)); 
     return $this; 
    } 

    /** 
    * Add a submit button to an individual sub form 
    * 
    * @param Zend_Form_SubForm $subForm 
    * @return My_Form_Registration 
    */ 
    public function addSubmitButton(Zend_Form_SubForm $subForm) 
    { 
     $subForm->addElement(new Zend_Form_Element_Submit(
      'save', 
      array(
       'label' => 'Save and continue', 
       'required' => false, 
       'ignore' => true, 
       'order' => 99999 
      ))); 

     $subForm->getElement('save')->setAttrib('onClick', 'ajaxController(); $("#processing_alert").css("display", "block");'); 

     return $this; 
    } 

    /** 
    * Add action and method to sub form 
    * 
    * @param Zend_Form_SubForm $subForm 
    * @return My_Form_Registration 
    */ 
    public function addSubFormActions(Zend_Form_SubForm $subForm) 
    { 
     $subForm->setAction('/test/process') 
       ->setMethod('post') 
       ->setEnctype(Zend_Form::ENCTYPE_MULTIPART); 
     return $this; 
    } 

/** 
* After post, pre validation hook 
* 
* Finds all fields where name includes 'newField' and uses addNewField to add 
* them to the form object 
* 
* @param array $data $_GET or $_POST 
*/ 
public function preValidation(Zend_Form_SubForm $subForm,array $data) { 
    // array_filter callback 
    function findFields($field) { 
    // return field names that include 'newField' 
    if (strpos($field, 'newField') !== false) { 
     return $field; 
    } 
    } 

    // Search $data for dynamically added fields using findFields callback 
    $newFields = array_filter(array_keys($data), 'findFields'); 

    foreach ($newFields as $fieldName) { 
    // strip the id number off of the field name and use it to set new order 
    $ex1 = explode('newField', $fieldName); 
    $ex2 = explode('_',$ex1[1]); 
    $key = $ex2[0]; 
    $order = $ex2[1]; 

    $this->addAbstractField($subForm,$key, $order,$data[$fieldName]); 
    //echo 'order :'.$order." and key is " .$key."<br />"; test ok 
    } 
} 

/** 
* Adds new fields to form dynamically 
* 
* @param string $name 
* @param string $value 
* @param int $order 
* @param object $subForm 
* 
*/ 
public function addAbstractField(Zend_Form_SubForm $subForm, $key, $formStep=null,$value){ 

       $subForm->addElement('text','fixedField'. $key.'_'.$formStep, array('label' => 'Test label:', 
                          'required'  => 'true', 
                          'value'  => $value, 
                          'description' => 'test description')); 

      echo '<br />Added element to subform (Send from Form method) key = "fixedField'. $key.'_'.$formStep .'" and value "'.$value.'"<br />'; 
      return $this; 
    } 
} 
?> 


Form Partial code: 

<?php 
class Test_Partial { 

    protected $id; 
    public function __construct($id,$amount=1,$fieldsKeys=array(),$fieldsValues=array()) 
    { 
     $this->id = $id; 
     $this->amount = is_int((int)$amount) ? $amount: 1 ; 
     $this->fields = array(); 

     //Lets combine both arrays into one 
     foreach ($fieldsKeys as $key => $value){ 
      $ex = explode('fixedField',$value); 
      $ex2 = explode('_',$ex[1]); 
      $this->fields[$ex2[0]] = $fieldsValues[$key]; 
     } 
    } 

    public function get() { 
     $result_array = array(); 
     $amount_counter = 1; 

     while ($amount_counter <= $this->amount) { 

      $result_array[] = new Zend_Form_Element_Text('newField'. $keyvalue['id'].'_'.($this->id+$amount_counter), array( 'label' => 'test:', 
                               'required'  => true, 
                               'value'  => 'this data will be lost')); 


     $tikk = new Zend_Form_Element_Button('removeForm'.($this->id+$amount_counter), array('label' => 'Remove')); 
      $tikk->setAttrib('onClick', 'ajaxRemoveForm('.($this->id+$amount_counter).')'); 

      $result_array[] = $tikk; 
      ++ $amount_counter; 
     } 

     return $result_array; 
    } 

    public function __toString() 
    { 
     return implode('', $this->get()); 
    } 

    /** 
    * Create a sluggable string for forms or any other uri related string 
    * 
    * @return mixed 
    * @access public 
    * @param mixed $array 
    */ 
    protected function getSlug($string){ 
     $slug = trim($string); // trim the string 
     $slug= preg_replace('/[^a-zA-Z0-9 -]/','',$slug); // only take alphanumerical characters, but keep the spaces and dashes too… 
     $slug= str_replace(' ','-', $slug); // replace spaces by dashes 
     $slug= strtolower($slug); // make it lowercase 
     return $slug; 
    } 
} 
?> 

View: 

<script type="text/javascript"> 
function getLastSubId(){ 
    var maxc = 0; 
    // Remove all elements with a certain sub id 
    $('*').each(function() { 
     num = parseInt(this.id.split("_")[1],10); 
     if(num > maxc) 
     { 
      maxc = num; 

     } 
    }); 
    return maxc; 
} 

// Retrieve new element's html from action controller 
function ajaxAddForm(amount) { 
    // Diplay processing msg 
    $("#processing_alert").css("display", "block"); 

    // Get value of id - integer appended to dynamic form field names and ids 
    var id = $("#step2-id").val(); 

    if(typeof amount == 'undefined'){ 
     var amount = $("#step2-addFormAmount").val(); 
    } 

    var fields = ''; 

    // Collect all field keys and values and include them in the ajax request. 
    $('*[id*=step2-fixedField]:visible').each(function() { 
      var key = $(this).attr('id'); 
      var value = $(this).attr('value'); 
      fields += '&key[]='+key+'&value[]='+value; 
    }); 

    $.ajax(
    { 
     type: "POST", 
     url: "<?php echo $this->url(array('action' => 'newAjaxForm')); ?>", 
     data: "id=" + id + "&amount=" + amount + fields, 
     async: false, 
     success: function(newForm) { 

     // Insert new element before the Add button 
     var counter = parseInt(id) + parseInt(amount); 

     $("#addForm-label").before(newForm); 
     $("#step2-id").val(counter); 
     } 
    } 
); 
    // Disable processing msg 
    $("#processing_alert").css("display", "none"); 
} 

function ajaxRemoveForm(id) { 
    // Diplay processing msg 
    $("#processing_alert").css("display", "block"); 

    // Remove the "remove" button that we just pressed + label 
    $("#removeForm"+id+"-label").remove(); 
    $("#removeForm"+id+"-element").remove(); 

    // Remove all elements with a certain sub id 
    $('*[id*=_'+id+'-]:visible').each(function() { 
     $(this).remove(); 
    }); 

    // Disable processing msg 
    $("#processing_alert").css("display", "none"); 
} 
</script> 
<?php echo $this->form; ?> 

回答

0

我工作在同一个问题在毛里求斯:)

我猜你基地的AJAX产生的杰里米·肯德尔的教程额外的字段:

http://www.jeremykendall.net/2009/01/19/dynamically-adding-elements-to-zend-form/

我花了很长时间才得到这个工作。

我已经修改了他的教程是这样的:

在我的主要形式定义,我得到了功能preValidation和addNewExperience(我在简历在线工作)。

public function preValidation(array $data) { 

     // array_filter callback 
     function findSubForms($subForms) { 
      // return field names that include 'newName' 
      if (strpos($subForms, 'experience') !== false) { 
       return $subForms; 
      } 
     } 

     // Search $data for dynamically added fields using findFields callback 
     $newSubForms = array_filter(array_keys($data), 'findSubForms'); 

     foreach ($newSubForms as $subForm) { 
      // strip the id number off of the field name and use it to set new order 

      $order = ltrim($subForm, 'experience') + 3; 
      $this->addNewExperience($subForm, $data[$subForm], $order); 

     } 
    } 



public function addNewExperience($name, $value, $order) { 

     $mysubForm = new Application_Form_Experience(); 
     $this->addSubForm($mysubForm, $name, $order); 
    } 

所以,

无论是形式通过验证在这种情况下,它会在数据库模型和插入,或验证失败和形式必须与价值观类型化显示。

在这种情况下,preValidation启动后,新添加的子表单将重新显示新添加的值。

它完全适合我。而且我认为我甚至不需要addNewExperience中的$ value(它在函数中没有使用)。当你的数组被填充时,Zend只能找到值的属性。

我正在努力将表格格式化为表格:/结构有点脆弱。如果我稍微触摸装饰器,表单将停止正确填充。

不太清楚发生了什么事情,正如你发现的那样,我们并不是很多人在网上做这类东西。