Search code examples
phpxmlforeachsimplexml

Php inner loop problem with xml and continue


Having this xml

    $xml_text = "<file>
  <record>
    <name>A</name>
    <total_parts>2</total_parts>
    <value>25</value>
    <part>2</part>
  </record>
  <record>
    <name>D</name>
    <total_parts>1</total_parts>
    <value>30</value>
    <part>1</part>
  </record>
  <record>
    <name>B</name>
    <total_parts>1</total_parts>
    <value>75</value>
    <part>1</part>
  </record>
  <record>
    <name>A</name>
    <total_parts>2</total_parts>
    <value>80</value>
    <part>1</part>
  </record>
  <record>
    <name>T</name>
    <total_parts>1</total_parts>
    <value>1</value>
    <part>1</part>
  </record>
  <record>
    <name>Z</name>
    <total_parts>1</total_parts>
    <value>3</value>
    <part>1</part>
  </record>
</file>";

Then $xml:

$xml =  simplexml_load_string($xml_text);

If I want to extract for each <name> the list of <value> respecting <part> order (exept T):

foreach ($xml as $record) {
    $print = '';
    if ($record->total_parts == 1) {
        $print .= $record->name . ": " . $record->value;
    } else {
        $print .= $record->name . ": ";
        for ($i=1;$i<=$record->total_parts;$i++) {
            foreach ($xml as $record_inner) {
                if (($record_inner->name == $record->name) and ($record_inner->part==$i)) {
                    $print .= $record_inner->value . ", ";
                    break;
                }      
            }
            
        }        
    }
    if ($record->name=="T") {
        continue;
    } 
    echo $print;  
    echo "\n"; 
}

This is what I want:

A: 80, 25, 
D: 30
B: 75
A: 80, 25, 
Z: 3

This is the php result:

A: 25, 
D: 30
B: 75
A: 80, 

https://onlinephp.io/c/e5df5

Why in the first inner loop condition name = A and part = 1 doesn't catch value 80 and in the second inner loop name = A and part = 2 doesn't catch value 25? Why after continue to skip T, Z is not considered?


Solution

  • You can get all the results into a new array.

    $xml =  simplexml_load_string($xml_text);
    $records = [];
    foreach($xml->record as $record) {
        $name = (string)$record->name;
        if('T' === $name) {
            continue;
        }
        $records[$name][(int)$record->part - 1] = (int)$record->value;
    }
    

    Which is then

    Array
    (
        [A] => Array
            (
                [1] => 25
                [0] => 80
            )
    
        [D] => Array
            (
                [0] => 30
            )
    
        [B] => Array
            (
                [0] => 75
            )
    
        [Z] => Array
            (
                [0] => 3
            )
    
    )
    

    and can easily brought to your wanted output

    foreach($records as $key => $values) {
        ksort($values, SORT_NUMERIC);
        echo "$key: ", implode(', ', $values), "\n";
    }
    

    printing as

    A: 80, 25
    D: 30
    B: 75
    Z: 3
    

    I think your expected result is that printed above and not the repeated letters?