# Menu Builder

The menu builder aims to make building dynamic menus easier. It allows you to

  • Create a menu, add items to it and return the menu as an array
  • Divide the menu into sections
  • Add multiple levels of submenus
  • Hide specific menu items from users who don't have permission to access them (hence the 'dynamic' part)

# Initalizing the menu builder

To start constructing a new menu, you need to call MenuBuilder::new(). This will return an instance of the MenuBuilder class which you can then start adding sections and items to.

You are also able to pass an entire menu array to MenuBuilder::new(). This can be helpful in cases where you have an existing menu already and want to add onto it in some other location.

Here's the constructor for the MenuItem data type:

    /**
     * @param string $label The label of the menu item
     * @param string $route The name of the Laravel route which the menu item links to
     * @param string|null $icon The name of the icon to be shown next to the label
     * @param string $viewBox The viewbox of the icon component
     * @param MenuItem[]|null $subMenu
     */
    public function __construct(
        public string $label,
        public string $route,
        public string|null $icon = null,
        public string $viewBox = "0 0 32 32",
        public array|null $subMenu = null
    ) {
    }

You can create a new menu item either by using new MenuItem() like this:

    $item = new MenuItem(
        "Users",
        "admin.users",
        "IconUser",
    );

or in an array format:

    $item = MenuItem::from([
        "label" => "Users",
        "route" => "admin.users",
        "icon" => "IconUser",
    ]);

This is done using the spatie/laravel-data (opens new window) package so you can view those docs for more info.

# Adding items to the menu

public function addItem(MenuItem $item, string $sectionName = null, string $permission = null): static

To add an item to the menu, you can use the addItem() method. You need to pass a MenuItem data object as the first argument.

    use Salt\Core\MenuBuilder;

    $userMenuItem = new MenuItem(
        "Manage users",
        "admin.users",
        "IconUser",
    );

    $menu = MenuBuilder::new()
        ->addItem($userMenuItem)
        ->build();

//  $menu => [
//     [
//        "label" => "Manage users",
//        "route" => "admin.users",
//        "icon" => "IconUsers",
//     ]
//  ]

If you just add the menu item, it will be added directly to the menu. However if you add a section name as the second argument, it will place the item in a sub array which has the given name as the index.


$menu = MenuBuilder::new()
    ->addItem($adminMenuItem, "Admin")
    ->build();

// $menu => [
//    "Admin" => [
//         [
//            "label" => "Manage users",
//            "route" => "admin.users",
//            "icon" => "IconUsers",
//         ]
//     ]
//  ]

Finally, you can add the name of a permission as the third argument. If the current user does not have that permission, then the menu item will not show up for them.

    $menu = MenuBuilder::new()
        ->addItem($adminMenuItem, 'Admin', 'view-assessments')
        ->build();

    // If the current user has the 'view-assessments' permission,
    // the menu item will be visible for them

# Adding submenus

A MenuItem can have a submenu, which itself takes in an array of MenuItems. Each of those could then also have their own submenus, so you can technically have any number of nested submenus if you wanted to.

You can add the submenu as a property:

    $menuItem = new MenuItem(
        "Manage users",
        "admin.users",
        "IconUser",
        [
            new MenuItem("Archived users", "admin.users.archived"),
            new MenuItem("Add new user", "admin.users.create"),
            new MenuItem("Import new users", "admin.users.import"),
        ]

    )

Or use the addSubMenu() method:

    $item = new MenuItem(
        "Users",
        "admin.users",
        "IconUser",
    );

    $subMenu = [
        new MenuItem("Archived users", "admin.users.archived"),
        new MenuItem("Add new user", "admin.users.create"),
        new MenuItem("Import new users", "admin.users.import"),
    ];

    $item->addSubMenu(
        ...$subMenu
    );

If you want to group menu items together, you can add a section and then place the items in that instead of adding them directly to the root of the menu.

# Adding sections to the menu

public function addSection(string $name, MenuSectionItem ...$items): static

You can add a new section to the menu with addSection(). This method requires a name to be passed:

    $menu = MenuBuilder::new()
        ->addSection("Admin")  // ['Admin' => []]

An empty section is not very useful though. You can pass any number of MenuSectionitems as to the section and they will be added to it as well:

     $menu = MenuBuilder::new()
        ->addSection(
            "Admin",
            new MenuSectionItem($userMenuItem),
            new MenuSectionItem($settingsMenuItem)
        )
        ->build()

The MenuSectionItem data type takes two arguments, $data and $permission:

/**
     * @param MenuItem $data The menu item to be added to the section
     * @param string|null $permission The permission that the user needs in order to view the menu item
     */
    public function __construct(
        public MenuItem $data,
        public ?string $permission = null
    ) {
    }

$data must be a MenuItem. $permission is optional - if added, then it will determine whether or not that item can be shown to the current user based on their permissions

     $menu = MenuBuilder::new()
        ->addSection(
            "section",
            new MenuSectionItem($userMenuItem, 'manage-users'),
            new MenuSectionItem($settingsMenuItem, 'manage-settings')
        )
        ->build()

# Add items to the menu conditionally

You don't need to build the menu all at once, you can break it up as needed in your application:


$menu = MenuBuilder::new();

if ($user->hasCourses()) {
    $menu->addItem($courseMenuItem, "Learn")
};

if($library->isEnabled()) {
    $menu->addItem($libraryMenuItem, "Learn")
}

if($user->isTeacher()) {
    $menu->addSection('Insights',
        new MenuSectionItem($campusProgressMenu,'view-campus-progress'),
        new MenuSectionItem($courseInsightMenu,'view-course-progress')
    );
}

$menu->build();