Сложный цикл через сложный SimpleXMLElement - PullRequest
0 голосов
/ 17 февраля 2019

Мне нужно сохранить некоторые значения из XML.

Первый шаг - я получаю структуру:

 $xml = $dom_xml->saveXML();
 $xml_ = new \SimpleXMLElement($xml);
 dd($xml_);

Здесь TextFrame имеет 8 массивов.Каждый из них имеет PathPointType, который имеет еще 4 массива с 3 атрибутами в каждом.И эти атрибуты мне нужны от каждого TextFrame.

enter image description here

Например, можно получить значение Anchor:

$res = $xml_
        ->Spread
        ->TextFrame
        ->Properties
        ->PathGeometry
        ->GeometryPathType
        ->PathPointArray
        ->PathPointType
        ->attributes();
    dd($res['Anchor']);

(Кстати: есть ещеБолее симпатичный способ получить его?)

Но вопрос в том, как можно пройти по всем массивам и сохранить значения отдельно для каждого массива?

Полагаю, здесь должен быть многомерный цикл foreach в сочетании с циклом for?

Или лучше добиться этого, используя DOMDocument?

Ответы [ 2 ]

0 голосов
/ 17 февраля 2019

В дополнение к существующему ответу Найджела Рена я подумал, что покажу, как выглядят те же самые циклы с SimpleXML.

Во-первых, обратите внимание, что вам не нужнопреобразовать XML в строку и обратно, если вы хотите по какой-либо причине переключаться между DOM и SimpleXML, вы можете использовать simplexml_import_dom , который просто меняет интерфейс:

 $sxml = simplexml_import_dom($dom_xml);

Далее нам нужноTextFrame элементов;мы могли бы либо пройти через структуру явно, как вы делали это раньше:

$textFrames = $sxml->Spread->TextFrame;

Или мы могли бы использовать XPath для поиска совпадающих имен тегов в нашем текущем узле (. - текущий элемент, а // означает «любой потомок»:

$textFrames = $sxml->xpath('.//TextFrame');

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

На этот раз мы определенно хотим, чтобы выражение XPath получало узлы PathPointType, чтобы избежать всех вложенных циклов через уровни, которые нам не интересны:

foreach ( $textFrames as $frame )   {
    $pathPointTypes = $frame->xpath('.//PathPointType');
    foreach ( $pathPointTypes as $type )    {
        echo $type['Anchor'] . PHP_EOL;
    }
}

Обратите внимание, что вы неВам не нужно вызывать $type->attributes(), если только вы не имеете дело с пространствами имен, все, что вам нужно для получения атрибута, это $node['AttributeName']. Но имейте в виду, что атрибуты в SimpleXML являются объектами, поэтому вам часто нужно заставить их быть строкамис (string)$node['AttributeName'].

Чтобы взять последний пример, у вас может быть что-то вроде этого:

$frames = [];
foreach ( $sxml->Spread->TextFrame as $frame )   {
    $anchors = [];
    $pathPointTypes = $frame->xpath('.//PathPointType');
    foreach ( $pathPointTypes as $type )    {
        $anchors[] = ['Anchor' => (string)$type['Anchor']];
    }
    $frames[] = $anchors;
}
0 голосов
/ 17 февраля 2019

Похоже, что вы начинаете с DOMDocument (так как вы используете $dom_xml->saveXML() для генерации XML), может быть проще продолжать его использовать, а также есть несколько простых функций для получения подробностей после.

Использование getElementsByTagName() позволяет получить список элементов с определенным именем тега из начальной точки, поэтому, начиная с $dom_xml, получите все элементы <TextFrame>.Затем foreach() над этим списком и используя этот элемент в качестве начальной точки, используйте getElementsByTagName("PathPointType"), чтобы получить вложенные <PathPointType> элементы.На этом этапе вы можете использовать getAttribute("Anchor") для каждого из необходимых вам атрибутов из <PathPointType> элементов ...

$textFrames = $dom_xml->getElementsByTagName("TextFrame");

foreach ( $textFrames as $frame )   {
    $pathPointTypes = $frame->getElementsByTagName("PathPointType");
    foreach ( $pathPointTypes as $type )    {
        echo $type->getAttribute("Anchor").PHP_EOL;
    }
}

Редактировать

Вы можетерасширить код для создания массива фреймов, а затем привязок к нему.Этот код также сохраняет привязку в ассоциативном массиве, так что, если вы добавите другие атрибуты, вы можете добавить их здесь (или удалить, если вам не нужен еще один уровень детализации) ...

$frames =[];
foreach ( $textFrames as $frame )   {
    $anchors = [];
    $pathPointTypes = $frame->getElementsByTagName("PathPointType");
    foreach ( $pathPointTypes as $type )    {
        $anchors[] = ['Anchor' => $type->getAttribute("Anchor")];
    }
    $frames[] = $anchors;
}

Также, если у вас есть какой-то способ идентификации фреймов, вы также можете создать ассоциативный массив на этом уровне ...

$frames[$frameID] = $anchors;
...