我已经阅读了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_name
和node_body
-属性。在验证表单并在提交表单后转储Node
-对象之后,node_name
和node_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_name
和node_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()方法在任何情况下都可能是首选方法,如果不是因为它的作用很明显的话。