fix: keep menu items in parent cycles, tighten menu write tests
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
This commit is contained in:
@@ -21,20 +21,55 @@ final class MenuTree
|
||||
public static function build(array $items): array
|
||||
{
|
||||
$nodes = [];
|
||||
$parentOf = [];
|
||||
foreach ($items as $item) {
|
||||
$nodes[(int) $item->ID] = self::node($item);
|
||||
$id = (int) $item->ID;
|
||||
$nodes[$id] = self::node($item);
|
||||
$parentOf[$id] = (int) $item->menu_item_parent;
|
||||
}
|
||||
|
||||
$children = [];
|
||||
foreach ($items as $item) {
|
||||
$id = (int) $item->ID;
|
||||
$parent = (int) $item->menu_item_parent;
|
||||
$children[$parent !== $id && isset($nodes[$parent]) ? $parent : 0][] = $id;
|
||||
foreach (array_keys($nodes) as $id) {
|
||||
$children[self::topLevelParent($id, $parentOf, $nodes)][] = $id;
|
||||
}
|
||||
|
||||
return self::attach(0, $nodes, $children);
|
||||
}
|
||||
|
||||
/**
|
||||
* The parent to attach $id under: its own menu_item_parent, unless that would
|
||||
* make $id unreachable from the root – a self-parent, a parent missing from
|
||||
* this menu, or a chain of parents that cycles back on itself instead of
|
||||
* reaching the root (0) within count($nodes) steps (e.g. A's parent is B and
|
||||
* B's parent is A). Any of these fall back to the top level (0): a broken or
|
||||
* cyclic parent link must never make an item disappear from the tree.
|
||||
*
|
||||
* @param array<int, int> $parentOf
|
||||
* @param array<int, array<string, mixed>> $nodes
|
||||
*/
|
||||
private static function topLevelParent(int $id, array $parentOf, array $nodes): int
|
||||
{
|
||||
$parent = $parentOf[$id];
|
||||
if ($parent === $id || ! isset($nodes[$parent])) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$current = $parent;
|
||||
for ($steps = 0; $steps < count($nodes); $steps++) {
|
||||
if ($current === 0) {
|
||||
return $parent;
|
||||
}
|
||||
|
||||
if (! isset($nodes[$current])) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$current = $parentOf[$current];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @return array{id: int, title: string, type: string, object: string, object_id: int, url: string} */
|
||||
public static function node(object $item): array
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user