ここで問題を分割できます。DOMDocument
1 つのことは、HTML を解析することです。これは、 and DOMXpath
hereで最も簡単に実行できます。これは、別の xpath 式/クエリの結果のコンテキストでマッピングを実行しています。少し複雑に聞こえるかもしれませんが、そうではありません。より単純化されたバリアントでは、 xpath とすべての子要素を介して親要素を取得する への以前の回答で概説されているこれを見つけることができます。
あなたの場合、これはもう少し複雑な疑似コードです。デモンストレーションの目的で物事をより見やすくするため、ラベルを追加しました。
foreach //li ::
ID := string(./@id)
ParentID := string(./ancestor::li[1]/@id)
Label := normalize-space(./text()[1])
これが示すように、これは生のデータのみを返します。Order と Children もあります。通常、Children のリストは必要ありません (とにかくここに置いておきます)。Order 値と Children 値の類似点は、コンテキストから取得されることです。
たとえば、ドキュメントの順序でノードリストをトラバースしているとき//li
に、各 ParentID ごとにカウンターが保持されている場合、各子の順序に番号を付けることができます。
Children と同様に、カウンターのように、リストを繰り返し処理しながら値を構築する必要があります。各 listitem の正しい値は、最後にのみ使用できます。
したがって、これら 2 つの値はコンテキスト内にあるため、ParentID: をキーとする配列の形式でそのコンテキストを作成します$parents
。ID ごとに 2 つのエントリが含まれます。0 には Order のカウンタが含まれ、1 には子 (存在する場合) の ID を保持するための配列が含まれます。
注:技術的には、これは完全に正しいわけではありません。Order と Children は、純粋な xpath でも表現できるはずです。この例では、独自の非 xpath コンテキストも追加する方法を示すためにそれを行いませんでした。たとえば、別の順序付けや子の処理が必要な場合などです。
理論で十分です。標準設定を考慮すると、次のようになります。
$doc = new DOMDocument();
$doc->loadHTML($html);
$xp = new DOMXPath($doc);
上記のマッピングは含まれています。そのコンテキストは無名関数として記述できます。
$parents = [];
$map = function (DOMElement $li) use ($xp, &$parents) {
$id = (int)$xp->evaluate('string(./@id)', $li);
$parentId = (int)$xp->evaluate('string(./ancestor::li[1]/@id)', $li);
$label = $xp->evaluate('normalize-space(./text()[1])', $li);
isset($parents[$parentId][0]) ? $parents[$parentId][0]++ : ($parents[$parentId][0] = 1);
$order = $parents[$parentId][0];
$parents[$parentId][1][] = $id;
isset($parents[$id][1]) || $parents[$id][1] = [];
return array($id, $label, $order, $parentId, &$parents[$id][1]);
};
ご覧のとおり、最初に疑似コードのような値の取得が含まれ、2 番目の部分でコンテキスト値の処理が含まれます。ID / ParentID がまだ存在しない場合は、そのコンテキストを初期化するだけです。
このマッピングを適用する必要があります。
$result = [];
foreach ($xp->query('//li') as $li) {
list($id) = $array = $map($li);
$result[$id] = $array;
}
これには$result
、アイテムのリストと$parents
コンテキスト データが含まれます。参照が使用されているため、ここで Children 値を内破する必要があります。その後、参照を削除できます。
foreach ($parents as &$parent) {
$parent[1] = implode(',', $parent[1]);
}
unset($parent, $parents);
これにより$result
、出力可能な最終結果が得られます。
echo '+----+----------------+-------+--------+----------+
| ID | LABEL | ORDER | PARENT | CHILDREN |
+----+----------------+-------+--------+----------+
';
foreach ($result as $line) {
vprintf("| %' 2d | %' -14s | %' 2d | %' 2d | %-8s |\n", $line);
}
echo '+----+----------------+-------+--------+----------+
';
次に、次のようになります。
+----+----------------+-------+--------+----------+
| ID | LABEL | ORDER | PARENT | CHILDREN |
+----+----------------+-------+--------+----------+
| 1 | Page 1 | 1 | 0 | |
| 2 | Page 2 | 2 | 0 | 3,4,5 |
| 3 | Sub Page A | 1 | 2 | |
| 4 | Sub Page B | 2 | 2 | |
| 5 | Sub Page C | 3 | 2 | 6 |
| 6 | Sub Sub Page I | 1 | 5 | |
| 7 | Page 3 | 3 | 0 | 8 |
| 8 | Sub Page D | 1 | 7 | |
| 9 | Page 4 | 4 | 0 | |
+----+----------------+-------+--------+----------+
デモはオンラインでここにあります。