PHP XML DOM Document перемещает подузлы в пределах подузла

У меня есть 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
            }
        }
    }
}

**** ДОБАВЛЕНО ****
Я использую вложенные циклы для создания одного XML-кода для каждого продукта (каждый XML-файл содержит сведения о покупателе, товаре и продаже), а затем этот XML-файл передается в хранимую процедуру для создания записей о пользователе / ​​товаре / продаже: по нескольким причинам Я не могу сначала громко импортировать пользователей, затем товары, а затем продажи, но при создании xml продажи мне нужны некоторые детали из общего узла, и один из способов получить их - переместить общий узел поверх XML, но четко внутри узла заказа.
**** ДОБАВЛЕНО ****

Мне нужно получить доступ к некоторым подузлам Total перед обработкой продуктов. Единственное решение, которое я нашел, - это переместить узел Total в начале, но, несмотря на многочисленные попытки, я не смог добиться успеха: идея заключалась в клонировании totalNode и к appendbefore OrderID Узел

Проблема в том, что мне нужно работать над поддокументами и выбирать узел для клонирования из самого узла, в то время как все найденные примеры клонируют полный DocumentElement

возможно, более простое решение может быть достигнуто с помощью XSLT?

Можете предложить решение?

1 ответ

Решение

Я не совсем понимаю, что вы пытаетесь сказать о клонировании. Возможно, вы сможете отредактировать свой вопрос и уточнить, что вы имеете в виду.

Тем не менее, о доступе к 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
  */
}
Другие вопросы по тегам