Ensure all item menu has parent.
@param  array $assigned
@param  array $menus
@return array
434131
private static function requiredParent($assigned, &$menus)
    {
        $l = count($assigned);
        for ($i = 0; $i < $l; $i++) {
            $id = $assigned[$i];
            $parent_id = $menus[$id]['parent'];
            if ($parent_id !== null && !in_array($parent_id, $assigned)) {
                $assigned[$l++] = $parent_id;
            }
        }

        return $assigned;
    }
891784
public function getParentsList()
    {
        /** @var static $root */
        $root  = static::root();
        $items = [
            ['id' => '1', 'title' => 'Item Root'],
        ];
        $nodes = $root->descendants();
        if ($this->exists) {
            $nodes->where('navigation_id', $this->navigation_id)->where('id', '<>', $this->id);
        }

        foreach ($nodes->get() as $node) {
            foreach ($node->getDescendantsAndSelf()->toHierarchy() as $menu) {
                $this->appendMenu($menu, $items);
            }
        }

        return array_pluck($items, 'title', 'id');
    }
827261
public function showOnlyCurrentChildren(ItemInterface $menu)
    {
        $itemIterator = new RecursiveItemIterator($menu->getIterator());

        $iterator = new \RecursiveIteratorIterator($itemIterator, \RecursiveIteratorIterator::SELF_FIRST);
        foreach ($iterator as $menuItem) {
            /** @var \Knp\Menu\MenuItem $menuItem */
            if (!$this->matcher->isCurrent($menuItem) && !$this->matcher->isAncestor($menuItem)) {
                $menuItem->setDisplayChildren(false);
            }
        }
    }
609724
public function getChildren(MenuItem $parent = null)
    {
        if ($parent == null) {
            return $this->getTopChildren();
        }
        $result = [];
        foreach ($this->adaptors as $menuAdaptor) {
            $menuAdaptor->adaptChildren($this, $result, $parent, $this->getRequest());
        }

        return $result;
    }
1171588
protected function _getCurrentMenuElement(array $options = [], $level = 1)
    {
        if ($level > 1) {
            $levelElement = $this->_getLevelElement($options, $level);
            if ($this->_View->elementExists($levelElement)) {
                return $levelElement;
            }

            return $options['childMenuAttr']['element'];
        }

        return $options['menuAttr']['element'];
    }
155644
public function parents(array $parents): self
    {
        if ($parents === []) {
            return $this;
        }

        $data = [];

        foreach ($parents as $parent) {
            if (!isset($this->preparedGroups[$parent])) {
                throw new \LogicException(sprintf(
                    'Unable to find parent group: %s for route group: %s',
                    $parent,
                    $this->group
                ));
            }

            $data[] = $this->preparedGroups[$parent];
        }

        $old = $this->options->dump();
        $data = Arr::mergeRecursive(...$data);

        $this->options->bind($data);
        $this->options->bind($old);

        return $this;
    }
749340
public function genealogyMenuContent(array $menus): string
    {
        return implode('', array_map(static function (Menu $menu): string {
            return $menu->bootstrap4();
        }, $menus));
    }
1207893
protected function getMenu(): Menu
	{
		$parent = $this->parent;
		if ($parent instanceof Menu) {
			return $parent;
		} elseif ($parent instanceof Item) {
			return $parent->getMenu();
		}
		throw new InvalidStateException();
	}
1288653
public function getMenuItems(): array
    {
        usort($this->menuItems, function ($a, $b) {
            return $a->getOrder() > $b->getOrder();
        });

        return $this->menuItems;
    }
486952
protected function saveMenuItems(Menu $menu, $items)
    {
        $itemStorage  = $this->treeManager->getItemStorage();
        $currentTree  = $this->treeManager->buildTree($menu->getId(), false);
        $menuId       = $menu->getId();

        // First, get all elements so that we can delete those that are removed
        // @todo pri: sorry this is inneficient, but I need it
        $old = [];
        foreach ($currentTree->getAll() as $item) {
            $old[$item->getId()] = $item;
        }

        // FIXME, this is coming from javascript, we should really check access on nodes

        // Keep a list of processed elements
        $processed = [];
        $deleteItems = [];

        // Keep in mind that items ordered
        if (!empty($items)) {

            // Because they are ordered and a parent will be saved before a child,
            // thus modifying a child: we have to use a classic loop
            $itemsCount = count($items);

            // Keep track of latest root parent found, in order to reorder top
            // level item relative to each other.
            $latestRootParentId = null;

            for ($i = 0; $i < $itemsCount; $i++) {

                $item     = $items[$i];
                $nodeId   = $item['item_id'];
                $isNew    = substr($item['id'], 0, 4) == 'new_' || empty($item['id']);
                $title    = trim($item['title']);
                $itemId   = $isNew ? null : $item['id'];
                $parentId = empty($item['parent_id']) ? null : $item['parent_id'];

                if ($isNew) {
                    if ($parentId) {
                        $itemId = $itemStorage->insertAsChild($parentId, $nodeId, $title);
                    } else {
                        $itemId = $itemStorage->insert($menuId, $nodeId, $title);
                        // Move the new item, in root tree, right after the
                        // lastest processed root item
                        if ($latestRootParentId) {
                            $itemStorage->moveAfter($itemId, $latestRootParentId);
                        }
                    }
                    // New potential parent item inserted, replace potential children parent_id
                    foreach ($items as $index => $potentialChild) {
                        if ($potentialChild['parent_id'] === $item['id']) {
                            $items[$index]['parent_id'] = $itemId;
                        }
                    }
                } else {
                    if ($parentId) {
                        $itemStorage->moveAsChild($itemId, $parentId);
                    } else {
                        if ($latestRootParentId) {
                            $itemStorage->moveAfter($itemId, $latestRootParentId);
                        } else {
                            $itemStorage->moveToRoot($itemId);
                        }
                    }

                    // When inserting or moving an item in the root of the tree
                    // we must keep track of it for being able to properly place
                    // the item relatively to its previous sibling.
                    if (!$parentId) {
                        $latestRootParentId = $itemId;
                    }

                    // Update title if revelant
                    if ($title !== $currentTree->getItemById($itemId)->getTitle()) {
                        $itemStorage->update($itemId, null, $title);
                    }
                }

                $processed[$itemId] = true;
            }
        }

        $newTree = $this->treeManager->buildTree($menuId, false, false, true);

        // Remove elements not in the original array
        foreach (array_diff_key($old, $processed) as $itemId => $deleted) {
            $itemStorage->delete($itemId);
            $deleteItems[$itemId] = $deleted;
        }

        $this->dispatcher->dispatch(
            MenuEvent::EVENT_TREE,
            new MenuEvent(
                $menu->getName(),
                $newTree,
                $deleteItems,
                $menu->hasSiteId() ? $this->siteManager->getStorage()->findOne($menu->getSiteId()) : null
            )
        );
    }
1233243
private function parseMenusSettings(\DOMNode $menus) {

		foreach ((new \DOMXPath($menus->ownerDocument))->query('menu', $menus) as $menu) {

			$id = $menu->getAttribute('id') ?: Menu::DEFAULT_ID;

			if(isset($this->menus[$id])) {
				$this->appendMenuEntries($menu->childNodes, $this->menus[$id]);
			}
			else {
				$this->menus[$id] = $this->parseMenu($menu);
			}

		}

	}
1148801
public function get_sub_menu_items( $menu_items, $parent_id ) {
		$items = [];

		foreach ( $menu_items as $menu_item ) {
			if ( intval( $parent_id ) === intval( $menu_item->menu_item_parent ) ) {
				$items[] = [
					'title' => $menu_item->title,
					'link' => str_replace( site_url(), '', $menu_item->url ),
					'items' => self::get_sub_menu_items( $menu_items, $menu_item->ID ),
				];
			}
		}

		return $items;
	}
802721
public static function getAccessTreeByParentId($isSuper, $menuIdList, $parentId, $condition = '', $params = [], $order = 'display_order asc')
    {
        $data = static::find()
            ->active()
            ->findByParentId($parentId)
            ->andWhere($condition, $params)
            ->orderBy($order)
            ->all();
        $tree = [];
        if ($data) {
            foreach ($data as $item) {
                if ($isSuper || in_array($item['menu_id'], $menuIdList)) {
                    $node = [
                        'menu_id' => $item['menu_id'],
                        'parent_id' => $item['parent_id'],
                        'tab_id' => 'tab_' . $item['menu_id'],
                        'text' => $item['text'],
                        'origin_text' => $item['text'],
                        'title' => $item['title'],
                        'url' => $item['url'],
                        'view_package' => $item['view_package'],
                        'expanded' => (bool) $item['expanded'],
                        'is_expand' => (bool) $item['expanded'],
                        'closable' => (bool) $item['closable'],
                        'is_open_url' => (bool) $item['is_open_url'],
                        'is_open_target' => (bool) $item['is_open_target'],
                        'is_every_open' => (bool) $item['is_every_open'],
                        'is_hide' => (bool) $item['is_hide'],
                        'display_order' => $item['display_order'],
                        'params' => $item['params'],
                        'note' => $item['note'],
                        'status' => intval($item['status']),
                    ];
                    $children = static::getAccessTreeByParentId($isSuper, $menuIdList, $item['menu_id'], $condition, $params, $order);
                    if ($children) {
                        $node['leaf'] = false;
                        $node['children'] = $children;
                    } else {
                        $node['leaf'] = true;
                    }
                    $tree[] = $node;
                }
            }
        }

        return $tree;
    }
526952
public function registerMenuSubPage($slug, $parent_slug, $title, $permissions = null, $link = null, $icon = null)
    {
        $slug = trim(Str::slug($slug));
        $title = trim($title);
        if ( ! is_array($permissions)) {
            $permissions = (array)$permissions;
        }
        $link = trim($link);

        if ( ! $this->menuPageExists($parent_slug)) {
            return false;
        }

        if (empty($permissions)) {
            $permissions = $this->getMenuPage($parent_slug)[ 'permissions' ];
        }

        $this->menuLinks[ $parent_slug ][ 'subMenu' ][ $slug ] = [
            'title'       => $title,
            'permissions' => $permissions,
            'link'        => $link,
            'icon'        => $icon,
        ];

        return $slug;
    }
351997
public function setMenus($menus)
    {
        $this->_menus = array_merge($this->_menus, $menus);
        $this->_normalizeMenus = null;
    }
908317
public function addMainMenuItems($owner, array $definitions)
    {
        foreach ($definitions as $code => $definition) {
            $this->addMainMenuItem($owner, $code, $definition);
        }
    }
919606
public function setIndividuationMenu($menus, $matchrule)
    {
        if ($menus instanceof Closure) {
            $menus = $menus($this);
        }

        if (!is_array($menus)) {
            $this->setError('子菜单必须是数组或者匿名函数返回数组');

            return false;
        }
        $menus = $this->extractMenus($menus);

        $data = [
            'button'    => $menus,
            'matchrule' => $matchrule,
        ];

        $res = $this->_post('addconditional', $data);

        return $res;
    }