我正在开发一个应该处理XML数据的Web应用程序,当我尝试编辑之前保存的数据时,我遇到了一些问题。
这就是我从一开始就要做的事情:用户必须填写一个包含多个输入的表单,我在控制器中获取数据,将其转换为带有Xml :: fromArray的XML字符串并将其存储在一个数据库字段。这完美地工作,生成的XML字符串正是我需要的。这是表格的一部分。
echo $this->Form->input('data.FT.Header.Transmission.Number');
echo $this->Form->input('data.FT.Header.Transmission.Format');
echo $this->Form->input('data.FT.Header.Transmission.Destination');
现在我正在尝试为该数据添加编辑功能。从我的控制器中获取数据,将其转换回具有Xml :: build的对象......这就是问题开始的地方。
如果我这样做
debug($invoice->data->FT->Header->Transmission);
一切看起来都很好,因为它告诉我我有一个包含它的对象:
object(SimpleXMLElement) {
Number => 'Npslu'
Format => 'Rgytz'
Destination => 'Uheac'
}
但是,如果我试图查看$ invoice-> data-> FT-> Header-> Transmission-> Number包含,而不是字符串,我得到一个空结果。这就是为什么(我认为)所有表单输入都是空的。
我查看了官方文档,尝试将其转换为数组(这应该是cakephp处理数据的旧方式)以及我想到的所有内容......仍然没有结果。
我做错了什么?
更新:我设法以这种(脏)的方式创建一个工作对象:
$array = Xml::toArray( Xml::build( $xmlString ) );
$object = json_decode(json_encode($array), FALSE);
现在,如果我调试$ invoice-> data-> FT-> Header-> Transmission我有正确的值,这在我的情况下向前迈出了一大步:)
问题是Form帮助程序仍然无法识别该值。如果我在我的编辑模板中有这个
echo $this->Form->input('data.FT.Header.Transmission.Number');
然后该字段为空。如果我以这种方式指定其值
echo $this->Form->input('data.FT.Header.Transmission.Number', array('value' => $invoice->data->FT->Header->Transmission->Number);
它可以正常工作。我知道我可以指定每个值,但我想自动拥有它,就像我在该页面中的其他字段(非XML相关)一样,例如$ invoice->发送,这被$ this-正确识别>形状配合>输入( '发送')...
更新2:这是XML生成的代码:
<?xml version="1.0" encoding="UTF-8"?>
<FT>
<Header>
<Transmission>
<Number>1234</Number>
<Format>qwer</Format>
<Destination>asdf</Destination>
</Transmission>
</Header>
</FT>
这些是我用来将输入字段的值转换为XML并返回的函数:
// Values to XML, called from the add function this way:
// $this->request->data['data'] = $this->_arrayToXml($this->request->data['data']);
protected function _arrayToXml($array) {
$xmlObject = Xml::fromArray($array);
$xmlString = $xmlObject->asXML();
return $xmlString;
}
// XML to Values, called from the edit function this way:
// $invoice->data = $this->_xmlToObject($invoice->data);
protected function _xmlToObject($xmlString) {
$array = Xml::toArray( Xml::build( $xmlString ) );
$object = json_decode(json_encode($array), FALSE);
return $object;
}
P.S。请原谅我,如果你看到一些小的不一致,我试图剥离所有不相关的代码,以避免发布大量的代码......
答案 0 :(得分:2)
SimpleXMLElement
个对象返回的对象实际上不是空的,Cakes debug()
函数只能处理SimpleXMLElement
之类的对象,其中值未明确存储在属性中。使用var_dump()
或投放价值,您就会看到它在那里:
var_dump($invoice->data->FT->Header->Transmission);
debug((string)$invoice->data->FT->Header->Transmission);
神秘解决了。
现在为什么表格输入中出现的值不是?好吧,表单中的数据是通过context providers访问的,因为您传递的是实体,所以表单使用实体上下文提供程序。
如果实体提供程序在请求对象中不可用,则该实体提供程序会尝试从该实体获取数据,这是尚未提交的编辑表单的情况。这里的问题是提供者无法处理您的数据结构,它需要嵌套实体,而不是数组或标准/ SimpleXML对象。
实现您要做的事情的最简单方法是始终设置请求数据,以便实体上下文提供程序不会尝试从实体获取数据,但始终从实体获取数据请求数据(参见EntityContext::val()
)。类似的东西:
<强>控制器强>
public function edit($id = null) {
// ...
if ($this->request->is(['post', 'put'])) {
// convert array input data to XML string
$this->request->data['data'] =
Xml::fromArray($this->request->data['data'])->asXML();
$invoice = $this->Invoices->patchEntity($invoice, $this->request->data);
if ($this->Invoices->save($invoice)) {
// ...
}
}
// convert XML string to array
$this->request->data['data'] = XML::toArray(Xml::build($invoice->data));
// ...
$this->set(compact('invoice'));
}
这样,在获取输入数据时不会触及实体,一切都应该正常工作。
可能更简洁但更复杂的方法可能是使用转换数据的自定义数据类型,以及可以处理对XML对象的点标记路径访问的自定义上下文提供程序。这样就不需要在控制器中进行任何特殊的转换等。
然而,这并不是问题/问题的真正部分,所以我只是通过提供一些示例(未经过彻底测试)来提及这一主题。
XML数据类型
这会将从数据库中检索到的字符串转换为实体将要公开的XML对象,并将数组请求输入数据编组为XML字符串
namespace App\Database\Type;
use Cake\Database\Driver;
use Cake\Database\Type;
use Cake\Utility\Xml;
class XmlType extends Type {
public function toPHP($value, Driver $driver)
{
if($value === null)
{
return null;
}
return Xml::build($value);
}
public function marshal($value) {
return Xml::fromArray($value)->asXML();
}
}
XML实体背景
如果需要,它将从XML对象中提取数据。外部部分是从原始Entity::val()
方法复制的。
namespace App\View\Form;
use Cake\ORM\Entity;
use Cake\View\Form\EntityContext;
class XmlEntityContext extends EntityContext
{
public function val($field) {
$val = $this->_request->data($field);
if ($val !== null) {
return $val;
}
if (empty($this->_context['entity'])) {
return null;
}
$parts = explode('.', $field);
$entity = $this->_getEntity($parts);
// begin: special XML element treatment
if($entity instanceof \SimpleXMLElement)
{
array_shift($parts);
return current($entity->xpath('/' . implode('/', $parts)));
}
// end: special XML element treatment
if (end($parts) === '_ids' && !empty($entity)) {
return $this->_extractMultiple($entity, $parts);
}
if ($entity instanceof Entity) {
return $entity->get(array_pop($parts));
}
return null;
}
}
查看强>
有关详细信息,请查看