代码之家  ›  专栏  ›  技术社区  ›  Stephen Melrose

如何基于symfony中父窗体中的值验证/保存嵌入窗体?

  •  2
  • Stephen Melrose  · 技术社区  · 14 年前

    我有一个symfony1.4表单和两个嵌入式表单。

    父窗体有一个下拉列表,用于确定要填充的嵌入窗体(使用JavaScript在前端隐藏/显示)。

    问题是,当我提交表单时,验证和保存在两个嵌入表单上运行,这显然是我不想要的。

    谢谢。

    2 回复  |  直到 14 年前
        1
  •  1
  •   Stephen Melrose    14 年前

    注:请看杰里米的答案,因为我的答案是基于他的。

    谢谢你的回答,杰里米。你的代码有一些问题,所以我想我应该发布我实现的解决方案来解释我的不同之处。

    doBind()的重写有一个问题,如果父值没有从验证器返回clean,则会抛出未捕获的sfValidatorError。我把它包在一个try/catch里来抑制它。

    protected $selectedTemplate;
    
    public function getTemplateToEmbeddedFormKeyMap()
    {
        // An array of template values to embedded forms
        return array(
            'template1' => 'templateform1',
            'template2' => 'templateform2',
            'template3' => 'templateform3',
            'templateN' => 'templateformN'
        );
    }
    
    protected function doBind(array $values)
    {
        // Clean the "template" value
        try
        {
            $this->selectedTemplate = $this->validatorSchema['template']->clean(array_key_exists('template', $values) ? $values['template'] : NULL);
        }
        catch(sfValidatorError $e) {}
    
        // For each template embedded form
        foreach($this->getTemplateToEmbeddedFormKeyMap() as $template => $form_key)
        {
            // If there is no selected template or the embedded form is not for the selected template
            if ($this->selectedTemplate == NULL || $this->selectedTemplate != $template)
            {
                // Don't validate it
                $this->validatorSchema[$form_key] = new sfValidatorPass();
            }
        }
    
        // Parent
        parent::doBind($values);
    }
    

    2 新步骤 重写updateObjectEmbeddedForms()

    updateObjectEmbeddedForms() 删除与未验证的嵌入窗体相关的任何数据。

    public function updateObjectEmbeddedForms($values, $forms = null)
    {
        // For each template embedded form
        foreach($this->getTemplateToEmbeddedFormKeyMap() as $template => $form_key)
        {
            // If there is no selected template or the embedded form is not for the selected template
            if ($this->selectedTemplate == NULL || $this->selectedTemplate != $template)
            {
                // Remove the data
                unset($values[$form_key]);
            }
        }
    
        // Parent
        parent::updateObjectEmbeddedForms($values, $forms);
    }
    

    最后,我不喜欢我必须复制和粘贴整个基地 saveEmbeddedForms()

    public function saveEmbeddedForms($con = null, $forms = null)
    {
        // Get the embedded forms
        if ($forms === NULL)
        {
            $forms = $this->getEmbeddedForms();
        }
    
        // For each template embedded form
        foreach($this->getTemplateToEmbeddedFormKeyMap() as $template => $form_key)
        {
            // If there is no selected template or the embedded form is not for the selected template
            if ($this->selectedTemplate == NULL || $this->selectedTemplate != $template)
            {
                // Remove the form so it isn't saved
                unset($forms[$form_key]);
            }
        }
    
        // Parent
        parent::saveEmbeddedForms($con, $forms);
    }
    

        2
  •  0
  •   Jeremy Kauffman    14 年前

    下面是这样做的一般方法。所有这些方法都可以添加到 BaseFormDoctrine 除了 doBind

    1添加方法以跳过窗体。

    /**
    * @param string $name Adds $name to an array of form names to ignore when saving/updating.
    */
    protected function skipSavingForm($name)
    {
      $this->skipSavingForms[$name] = $name;
      $this->validatorSchema[$name] = new sfValidatorPass();
    }
    

    2重写doBind以便不验证您不保存的窗体

    这样,即使未保存的表单提交时出现错误,表单仍然会验证。这是安全的,因为这些值不会被保存。或者,可以使用preValidator清除这些值。但是,我更喜欢这种解决方案,这样如果用户提交的表单有错误,那么两个表单的值仍然存在。

    /**
    * Override doBind to skip validation on the form not being saved
    * @param array $values
    * @see sfForm::doBind
    */
    protected function doBind(array $values)
    {
      try
      {
        $formDecidingValue = $this->validatorSchema[$values['form_deciding_field']]->clean();
      } catch (sfValidatorError $e) {
         //either create an sfValidatorErrorSchema and throw it or call through to parent here and let parent::doBind throw the error
         return; //either way, we want to stop processing
      }
    
      $this->skipSavingForm($formDecidingValue ? 'Form1' : 'Form2');
      return parent::doBind($values);
    }
    

    /**
    *@return array An array of forms to be saved
    */
    public function getFormsToSave()
    {
      return array_diff_key($this->getEmbeddedForms(), $this->skipSavingForms);
    }
    

    4重写saveEmbeddedForms和updateObjectEmbeddedForms

    public function saveEmbeddedForms($con = null, $forms = null)
    {
      if (null === $con)
      {
        $con = $this->getConnection();
      }
    
      if (null === $forms)
      {
        $forms = $this->getFormsToSave();
      }
    
      foreach ($forms as $form)
      {
        if ($form instanceof sfFormObject)
        {
          $form->saveEmbeddedForms($con);
          $form->getObject()->save($con);
        }
        else
        {
          $this->saveEmbeddedForms($con, $form->getFormsToSave());
        }
      }
    }
    
    public function updateObjectEmbeddedForms($values, $forms = null)
    {
      if (null === $forms)
      {
        $forms = $this->getFormsToSave();
      }
    
      foreach ($forms as $name => $form)
      {
        if (!isset($values[$name]) || !is_array($values[$name]))
        {
          continue;
        }
    
        if ($form instanceof sfFormObject)
        {
          $form->updateObject($values[$name]);
        }
        else
        {
          $this->updateObjectEmbeddedForms($values[$name], $form->getFormsToSave());
        }
      }
    }