0

我正在开发一个应该处理 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 我有正确的值,这在我的情况下是一个巨大的进步:)

问题是表单助手仍然无法识别该值。如果我的编辑模板中有这个

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->sent 被 $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;
}

ps 如果您看到一些细微的不一致,请原谅我,我正在尝试删除所有不相关的代码以避免发布大量代码...

4

1 回答 1

2

SimpleXMLElement对象

实际上返回的对象不是空的,Cakesdebug()函数还不能像现在这样处理对象SimpleXMLElement,其中值没有显式存储在属性中。使用var_dump()或转换该值,您会看到它在那里:

var_dump($invoice->data->FT->Header->Transmission);
debug((string)$invoice->data->FT->Header->Transmission);

谜团已揭开。

表单输入中未显示的值

现在为什么表单输入中没有出现值?好吧,表单中的数据是通过上下文提供程序访问的,并且由于您正在传递一个实体,因此表单使用实体上下文提供程序。

实体提供者尝试从实体获取数据,以防它在请求对象中不可用,这是尚未提交的编辑表单的情况。这里的问题是提供者无法处理您的数据结构,它需要嵌套实体,而不是数组或标准/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;
    }
}

如需更多信息,请查看

于 2014-10-22T14:12:48.417 回答