Zf2窗体和对象绑定,不清除非传递值


zf2 forms and object binding, without clearing non-passed values

我已经阅读了Zend-Framework 2中Form-Component的教程/参考资料,也许我错过了它,所以我在这里问。

我有一个名为Node的对象,并将其绑定到一个表单。我用的是Zend'Stdlib'Hydrator'ArraySerializable -标准水合器。所以我的Node对象有exchangeArray()getArrayCopy()两种方法:

class Node
{
    public function exchangeArray($data)
    {
        // Standard-Felder
        $this->node_id        = (isset($data['node_id']))   ? $data['node_id']   : null;
        $this->node_name      = (isset($data['node_name'])) ? $data['node_name'] : null;
        $this->node_body      = (isset($data['node_body'])) ? $data['node_body'] : null;
        $this->node_date      = (isset($data['node_date'])) ? $data['node_date'] : null;
        $this->node_image     = (isset($data['node_image'])) ? $data['node_image'] : null;
        $this->node_public    = (isset($data['node_public'])) ? $data['node_public'] : null;
        $this->node_type      = (isset($data['node_type'])) ? $data['node_type']:null;
        $this->node_route      = (isset($data['node_route'])) ? $data['node_route']:null;
    }
    public function getArrayCopy()
    {
        return get_object_vars($this);
    }
}

在我的控制器我有一个editAction()。在这里,我想修改这个Node对象的值。所以我使用我的形式的bind -方法。我的表单只有字段来修改node_namenode_body -属性。在验证表单并在提交表单后转储Node -对象之后,node_namenode_body -属性现在包含来自提交表单的值。但是,现在所有其他字段都是空的,即使它们之前包含初始值。

class AdminController extends AbstractActionController
{
    public function editAction()
    {
        // ... more stuff here (getting Node, etc)             
        // Get Form
        $form = $this->_getForm(); // return a 'Zend'Form instance
        $form->bind($node); // This is the Node-Object; It contains values for every property
        if(true === $this->request->isPost())
        {
            $data = $this->request->getPost();
            $form->setData($data);
            // Check if form is valid
            if(true === $form->isValid())
            {
                // Dumping here....
                // Here the Node-object only contains values for node_name and node_body all other properties are empty
                echo'<pre>';print_r($node);echo'</pre>';exit;
            }
        }
        // View
        return array(
            'form' => $form,
            'node' => $node,
            'nodetype' => $nodetype
        );
    }
}

我只想覆盖来自形式(node_namenode_body)的值,而不是其他的。

我认为一个可能的解决方案是给其他属性作为隐藏字段的形式,但我不想这样做。

是否有可能不覆盖表单中不存在的值?

我重新检查了'Zend'Form的代码,我必须诚实,我只是猜测如何解决我的问题。

我唯一改变的是水合器。看来Zend'Stdlib'Hydrator'ArraySerializable不是为我的情况准备的。因为我的Node -Object是一个对象而不是一个数组,我检查了其他可用的水合器。我找到了Zend'Stdlib'Hydrator'ObjectProperty水合剂。效果很好。只有表单中可用的字段才会在绑定对象中填充。这正是我所需要的。看起来ArraySerializable -hydrator重置了对象属性,因为它调用了绑定对象(Node)的exchangeArray -方法。在这种方法中,我将非给定字段设置为null(参见我的问题中的代码)。另一种可能的方法是更改exchangeArray -方法,以便它只设置尚未可用的值。

所以代码中的解决方案很简单:

$form = $this->_getForm();
$form->setHydrator(new 'Zend'Stdlib'Hydrator'ObjectProperty()); // Change default hydrator

在form.php类中有一个bug,过滤器没有在bindvalues方法中初始化,只需添加$filter->setData($this->data);

在包含 行之后,它应该是这样的
public function bindValues(array $values = array())
{
    if (!is_object($this->object)) {
        return;
    }
    if (!$this->hasValidated() && !empty($values)) {
        $this->setData($values);
        if (!$this->isValid()) {
            return;
        }
    } elseif (!$this->isValid) {
        return;
    }
    $filter = $this->getInputFilter();
    $filter->setData($this->data);          //added to fix binding empty data
    switch ($this->bindAs) {
        case FormInterface::VALUES_RAW:
            $data = $filter->getRawValues();
            break;
        case FormInterface::VALUES_NORMALIZED:
        default:
            $data = $filter->getValues();
            break;
    }
    $data = $this->prepareBindData($data, $this->data);
    // If there is a base fieldset, only hydrate beginning from the base fieldset
    if ($this->baseFieldset !== null) {
        $data = $data[$this->baseFieldset->getName()];
        $this->object = $this->baseFieldset->bindValues($data);
    } else {
        $this->object = parent::bindValues($data);
    }
}

是宝贵的,它是第282行在我的zf2.0.6库

这将解决您的问题,这只发生在绑定对象的情况下

我遇到了同样的问题,但Raj的解决方案不是正确的方式。这不是一个bug,因为今天的代码仍然类似,没有Raj的"修复",添加了一行:

$filter->setData($this->data);

这里的主要问题是,当您将对象绑定到表单时,inputfilter没有存储在表单对象中。但是每次都从绑定对象调用。

public function getInputFilter()
    ...
    $this->object->getInputFilter();
    ...
}

我的问题是,每次调用函数getInputFilter时,我都创建了一个新的InputFilter对象。所以我将其修改为如下所示:

protected $filter;
...
public function getInputFilter {
    if (!isset($this->filter)) {
        $this->filter = new InputFilter();
        ...
    }
    return $this->filter;
}

我今天遇到了同样的问题,但修复Raj建议没有工作。我使用的是ZF2的最新版本(写这篇文章的时候),所以它不能工作我并不完全惊讶。

改变到另一个Hydrator是不可能的,因为我的属性保存在一个数组中。ObjectProperty和ClassMethods水合器都依赖于你实际声明的属性(ObjectProperty使用object_get_vars, ClassMethods使用property_exists)。我不想创建我自己的Hydrator(懒惰!)。

相反,我坚持使用ArraySerializable水化器,并稍微改变了我的exchangeArray()方法。

原来我有:

public function exchangeArray(array $data)
{
    $newData = [];
    foreach($data as $property=>$value)
    {
        if($this->has($property))
        {
            $newData[$property] = $value;
        }
    }
    $this->data = $newData;
}

这在大多数情况下都可以正常工作,但正如您所看到的,它会清除$ This ->data中所有现有的数据。

我做了如下调整:

public function exchangeArray(array $data)
{
    $newData = [];
    foreach($data as $property=>$value)
    {
        if($this->has($property))
        {
            $newData[$property] = $value;
        }
    }
    //$this->data = $newData; I changed this line...
    //to...
    $this->data = array_merge($this->data, $newData);
}

如果在传入的新数据中没有键,则保留$ This ->data中的任何现有键。这种方法的唯一缺点是我不能再使用exchangeArray()来覆盖保存在$this->data中的所有内容。在我的项目中,这种方法是一次性的,所以它不是一个大问题。此外,新的replaceAllData()overwrite()方法在任何情况下都可能是首选方法,如果不是因为它的作用很明显的话。