PHP XML DOM 文件在子节点内移动子子节点

PHP XML DOM Document move sub-sub-nodes within sub-node

我有一个 xml 这样的:

<?xml version="1.0" encoding="UTF-8"?>
<OrderListResponse>
  <OrderListResponseContainer>
    <DateFrom>2018-07-01T00:00:00+00:00</DateFrom>
    <DateTo>2018-07-19T00:00:00+00:00</DateTo>
    <Page>1</Page>
    <TotalNumberOfPages>4</TotalNumberOfPages>
    <Orders>
      <Order>
        <OrderID>158772</OrderID>
        <Customer>
          <Name><![CDATA[John Smith]]></Name>
          <StreetAddress><![CDATA[33, Sunset Boulevrd]]></StreetAddress>
        </Customer>
        <Delivery>
          <Name><![CDATA[John Smith]]></Name>
          <StreetAddress><![CDATA[47, Rodeo Drive]]></StreetAddress>
        </Delivery>
        <Billing>
          <Name><![CDATA[John Smith]]></Name>
          <StreetAddress><![CDATA[33, Sunset Boulevrd]]></StreetAddress>
        </Billing>
        <Payment>
          <Module>paypal</Module>
          <TransactionID/>
        </Payment>
        <DatePurchased>2018-07-01 16:30:42</DatePurchased>
        <DateLastModified>2018-07-02 21:08:28</DateLastModified>
        <CheckoutMessage><![CDATA[]]></CheckoutMessage>
        <Status>cancelled</Status>
        <Currency>EUR</Currency>
        <Products>
          <Product>
            <MxpID>44237</MxpID>
            <SKU>IRF 8707TR</SKU>
            <Quantity>3</Quantity>
            <Price>2.46</Price>
          </Product>
        </Products>
        <Total>
          <SubTotal>7.38</SubTotal>
          <Shipping>2.7</Shipping>
          <Cod>0</Cod>
          <Insurance>0</Insurance>
          <Tax>1.62</Tax>
          <Total>11.7</Total>
        </Total>
      </Order>
      <Order>...</Order>
     </Orders>
  </OrderListResponseContainer>
</OrderListResponse>

虽然肯定有更好的方法, 为了解析所有订单,我构建了一个这样的例程:

$xmlDoc = new DOMDocument();
$xmlDoc->preserveWhiteSpace = false;
$xmlDoc->loadXML($response);
$xpath = new DOMXPath($xmlDoc);
$rootNode = $xpath->query('//OrderListResponseContainer/Orders')->item(0);
foreach($rootNode->childNodes as $node)
{
    foreach($node->childNodes as $subnode)
    {
        Process User
        foreach($subnode->childNodes as $subsubnode)
        {
            foreach($subsubnode->childNodes as $subsubsubnode)
            {
                Process Products and Sales
            }
        }
    }
}

**** ADDED ****
I use the nested loops to create one xml for each product (each xml contains details about the buyer, the item and the sale) and then this xml is passed to a Stored Procedure to generate the user/item/sale records: For several reason I cannot bulky import Users first, then Items and then Sales but while building the sale xml I need some details from the Total Node and one way to get them is to move Total Node on top of the XML, but clearly within the Order Node
**** ADDED ****

我需要在处理 Products 之前访问一些 Total 子节点 我找到的唯一解决方案是在开始时移动 Total 节点,但尽管尝试了很多次,但我一直未能成功: 这个想法是克隆 totalNodeappendbefore OrderID 节点

问题是我需要处理子文档和 select 从节点本身克隆的节点,而我发现的所有示例都克隆了完整的 DocumentElement

也许使用 XSLT 可以实现更简单的解决方案?

可以提出解决方案吗?

我不完全理解你想说的关于克隆部分的内容。也许您可以编辑您的问题并阐明您的意思。

但是,关于访问 Total 节点...您也可以简单地使用 XPath。

$xmlDoc = new DOMDocument();
$xmlDoc->preserveWhiteSpace = false;
$xmlDoc->loadXML($response);
$xpath = new DOMXPath($xmlDoc);

// first, let's fetch all <Order> elements
$orders = $xpath->query('//OrderListResponseContainer/Orders/Order');

// loop through all <Order> elements
foreach( $orders as $order ) {
  /*
    There's all sorts of ways you could convert <Total> to something useful
  */

  // Example 1.
  // fetch <Total> that is a direct child (./) of our context node (second argument) $order
  $total = $xpath->query( './Total', $order )->item( 0 );

  // then do something like
  $subTotal = $total->getElementsByTagName( 'SubTotal' )->item( 0 );
  $shipping = $total->getElementsByTagName( 'Shipping' )->item( 0 );
  // ... etc. for each child node of <Total>

  // or perhaps simply convert it to a SimpleXMLElement
  $total = simplexml_import_dom( $total );
  var_dump( $total );
  // and then access the values like this:
  $total->SubTotal;
  $total->Shipping;
  // ... etc.


  // Example 2.1
  // fetch all children of <Total> into an array
  $total = [];
  foreach( $xpath->query( './Total/*', $order ) as $totalNode ) {
    $total[ $totalNode->nodeName ] = $totalNode->textContent;
  }
  var_dump( $total );

  // Example 2.2
  // fetch all children of <Total> into a stdClass object
  $total = new \stdClass;
  foreach( $xpath->query( './Total/*', $order ) as $totalNode ) {
    $total->{ $totalNode->nodeName } = $totalNode->textContent;
  }
  var_dump( $total );

  /*
    Now, after this you can create and process the Customer and Products data
    in a similar fashion as I've shown how to process the Total data above
  */
}