Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

phayes0289's avatar

Using Spatie's Laravel-Permissions To Show nested Menu Items Based on Permissions

I am using Spatie’s Laravel-permission package and am working in incorporating the “hasanyrole” directive display the various elements of the menu.

I have a vertical menu system that is configured using a JSON string. Here is a shortened version of the entire JSON string that makes up my menu:

<?php
$menuJson =
    '{
    "menuList": [
      {
        "title": "Home",
        "link": "/home",
        "icon": "fal fa-graduation-cap",
        "filterTag": "Home",
        "i18n": "nav.home",
        "permissions": ["Administrator", "User"]
      },
      {
        "title": "Home Tools",
        "icon": "fal fa-graduation-cap",
        "filterTag": "Home",
        "i18n": "nav.home",
        "permissions": ["Administrator"],
        "child": [
          {
            "link": "/home/activity",
            "title": "Activity Wall",
            "filterTag": "activity wall",
            "i18n": "nav.activity",
            "permissions": ["Administrator"]
          },
          {
            "link": "/home/active_users",
            "title": "Active Users",
            "filterTag": "active Users",
            "i18n": "nav.active_users",
            "permissions": ["Test"]
          }
          ]
      },
      {
        "title": "Messaging",
        "filterTag": "messaging",
        "icon": "fal fa-info-circle",
        "i18n": "nav.messaging",
        "permissions": ["Administrator2", "User"],
        "child": [
          {
            "link": "/messages/inbox",
            "title": "Inbox",
            "filterTag": "inbox",
            "i18n": "nav.messaging.inbox",
            "permissions": ["Test"]
          }
          ]
        }
    ]
  }';
?>

The "permissions" item in the above JSON defines which user roles are allowed to see the menu item.

This is my foreach loop that generates the menu based on the JSON string that gets passed in.

 @foreach ($decodedMenu->{'menuList'} as $key => $value)
                {{-- Check if the menu item has permissions set and if the user has any of the required roles --}}
                @if (!isset($value->permissions) || auth()->user()->hasAnyRole($value->permissions))
                    @if (isset($value->onlyLi) && $value->onlyLi === 'true')
                        <li class="nav-title @if (isset($value->liClass)) {{ $value->liClass }} @endif">
                            {{ $value->title }}</li>
                    @elseif(!isset($value->child))
                        <li class="@if (isset($value->liClass)) {{ $value->liClass }} @endif">
                            <a href="{{ $value->link }}" title="{{ $value->title }}"
                               data-filter-tags="{{ $value->filterTag }}">
                                <i class="{{ $value->icon }}"></i>
                                <span class="nav-link-text" data-i18n="{{ $value->i18n }}">
                                {{ $value->filterTag }}
                            </span>
                            </a>
                        </li>
                    @else
                        {{-- Check permissions for parent menu item --}}
                        @if (!isset($value->permissions) || auth()->user()->hasAnyRole($value->permissions))

                            <li class="{{ isset($value->liClass) ?? $value->liClass }}">
                                <a href="javascript:void(0);" title="{{ $value->title }}"
                                   data-filter-tags="{{ $value->filterTag }}">
                                    <i class="{{ $value->icon }}"></i>
                                    <span class="nav-link-text" data-i18n="{{ $value->i18n }}">{{ $value->title }}
                            </span>
                                </a>
                                <ul id="{{ $value->i18n }}">
                                    {{-- Begin For Loop --}}
                                    @for ($i = 0; $i < sizeof($value->child); $i++)

                                        <li class="@if (isset($value->child[$i]->liClass)) {{ $value->child[$i]->liClass }} @endif">
                                                <?php
                                                $i18Var = '';
                                                $i18Var = $value->child[$i]->{'i18n'}; ?>
                                            <a href="{{ $value->child[$i]->{'link'} }}"
                                               title="{{ $value->child[$i]->{'title'} }}"
                                               data-filter-tags="{{ $value->child[$i]->{'filterTag'} }}">
                                        <span class="nav-link-text" data-i18n="{{ $value->child[$i]->{'i18n'} }}">
                                            {{ $value->child[$i]->{'title'} }}
                                        </span>
                                                @if (isset($value->child[$i]->{'spanText'}))
                                                    <span
                                                            class="{{ isset($value->child[$i]->{'spanClass'}) ? $value->child[$i]->{'spanClass'} : '' }}">{{ $value->child[$i]->{'spanText'} }}</span>
                                                @endif
                                            </a>
                                            @if (isset($value->child[$i]->{'subChild'}))
                                                <ul id="{{ $i18Var }}">
                                                    {{-- Begin For Loop 2 --}}
                                                    @for ($k = 0; $k < sizeof($value->child[$i]->{'subChild'}); $k++)
                                                        <li
                                                                class="@if (isset($value->child[$i]->{'subChild'}[$k]->liClass)) {{ $value->child[$i]->{'subChild'}[$k]->liClass }} @endif">
                                                            <a href="{{ $value->child[$i]->{'subChild'}[$k]->link }}"
                                                               title="{{ $value->child[$i]->{'subChild'}[$k]->title }}"
                                                               data-filter-tags="{{ $value->child[$i]->{'subChild'}[$k]->filterTag }}">
                                                        <span class="nav-link-text"
                                                              data-i18n="{{ $value->child[$i]->{'subChild'}[$k]->i18n }}">{{ $value->child[$i]->{'subChild'}[$k]->title }}</span>
                                                            </a>
                                                        </li>
                                                    @endfor
                                                    {{-- End For Loop 2 --}}
                                                </ul>
                                            @endif
                                        </li>
                                    @endfor
                                    {{-- End For Loop --}}
                                </ul>
                            </li>
                        @endif
                    @endif
                @endif
            @endforeach

I currently have the top level menu items checking a users permissions and hiding the menu item if the user does not have the proper permissions. There are two more “@for” loops that are nested inside that allow for up to two more levels of menu items. This is where I am lost.

I need help / advice rewriting the two nested code blocks for the @for loops to check for user permissions, just as I did for the top level menu item, and hide them if the user does not have the proper permissions.

0 likes
1 reply
LaryAI's avatar
Level 58

To check for user permissions on nested menu items, you can apply the same logic you used for the top-level menu items. You need to add a conditional check within your @for loops to determine if the user has the required permissions for each nested menu item. Here's how you can modify your nested @for loops:

@foreach ($decodedMenu->menuList as $key => $value)
    {{-- Check if the menu item has permissions set and if the user has any of the required roles --}}
    @if (!isset($value->permissions) || auth()->user()->hasAnyRole($value->permissions))
        {{-- ... existing code for top-level menu items ... --}}

        @if (isset($value->child))
            {{-- Parent menu item code here --}}
            <ul>
                {{-- Begin For Loop for child items --}}
                @foreach ($value->child as $child)
                    {{-- Check permissions for child menu item --}}
                    @if (!isset($child->permissions) || auth()->user()->hasAnyRole($child->permissions))
                        <li>
                            {{-- Child menu item code here --}}
                            <a href="{{ $child->link }}">
                                {{-- ... --}}
                            </a>
                            {{-- Check for subChild items --}}
                            @if (isset($child->subChild))
                                <ul>
                                    {{-- Begin For Loop for subChild items --}}
                                    @foreach ($child->subChild as $subChild)
                                        {{-- Check permissions for subChild menu item --}}
                                        @if (!isset($subChild->permissions) || auth()->user()->hasAnyRole($subChild->permissions))
                                            <li>
                                                {{-- SubChild menu item code here --}}
                                                <a href="{{ $subChild->link }}">
                                                    {{-- ... --}}
                                                </a>
                                            </li>
                                        @endif
                                    @endforeach
                                    {{-- End For Loop for subChild items --}}
                                </ul>
                            @endif
                        </li>
                    @endif
                @endforeach
                {{-- End For Loop for child items --}}
            </ul>
        @endif
    @endif
@endforeach

In this solution, I replaced the @for loops with @foreach loops for better readability and to avoid manual index management. Each nested loop checks if the permissions property is set for the child or subChild item and if the user has any of the required roles using auth()->user()->hasAnyRole($child->permissions) or auth()->user()->hasAnyRole($subChild->permissions) respectively.

This way, you ensure that each menu item, whether it's a top-level, child, or subChild item, is only displayed if the user has the appropriate permissions.

Please or to participate in this conversation.