otifsolutions/aclmenu

An ACL Menu Package that includes User Roles, Permissions, Db based Menu Items, rights and permission based menu items

1.0.10 2021-07-29 12:20 UTC

This package is auto-updated.

Last update: 2024-04-29 04:00:22 UTC


README

Requirements

PHP 5 > 5.3.0
Laravel > 5.0

How to install the library

Install via Composer

Using Composer (Recommended)

Either run the following command in the root directory of your project:

 composer require otifsolutions/aclmenu

Usage

  1. Create User Role ( via Seeder )

    UserRole::updateOrCreate(
        ['id' => 1],[
           'name' => 'ADMIN'
        ]);
  2. Create Menu Items for Created UserRole.

    $id = UserRole::Where(['name' => 'ADMIN'])->get('id');
    MenuItem::updateOrCreate(
     ['id' => 1], [
         'order_number'=> 1,
         'parent_id' => null,
         'icon' => 'feather icon-home',
         'name' => 'dashboard',
         'route' => '/dashboard',
         'generate_permission' => 'ALL'
      ])
         ->user_roles()
         ->sync($id);

    $id is id of user role that is admin.

    Option type Description
    order_number INT Number to show the item in sequence.
    parent_id INT Id of any item as a parent menu item.
    icon Varchar Icon of created menu item.
    name Varchar Show the name of created menu item.
    route Varchar Route access the intended page.
    • generate_permission is ENUM type of granted permission to the User Role, that are 'ALL', 'MANAGE_ONLY', 'READ_ONLY'.
    Option Description
    All Allow user role to create, read, update, delete.
    MANAGE_ONLY Allow user role manage.
    READ_ONLY show that User can only read.
    • User role and permissions are created.
    • Sync the permissions against menu items, so that user can have permissions to access the menu items.
    {
    $userRole = UserRole::where(['name' => 'ADMIN'])->first();
    $permissions = Permission::whereIn('menu_item_id',$userRole->menu_items()->pluck('id'))->pluck('id');
    $userRole->permissions()->sync($permissions);
    }
  3. Register the artisan command in database seeder in App/Database/Seeder/DatabaseSeeder.php;

    Artisan::call('aclmenu:refresh');
    

    Seeder run in this sequence.

    $this->call(UserRolesTableSeeder::class);
    $this->call(MenuItemsTableSeeder::class);
    $this->call(UserTableSeeder::class);
    $this->call(TeamsTableSeeder::class);
    Artisan::call('aclmenu:refresh');
    $this->call(DefaultUserPermissionsSync::class);
    
  4. Run the seeder to implement the changes

    php artisan db:seed
    

aclmenu:refresh

  • This command seeds data in Permission model after checking the permission in MeuItem model.
  • Possible permissions are 'All', "READ" and "MANAGE_ONLY".
  • Default permission is READ.

ACLUserTrait

  • Use OTIFSolutions\ACLMenu\Traits\ACLUserTrait in User model

  • Following methods are used in this trait

    Method relation Description
    user_role one-to-many (Inverse) This method returns user role from UserRole model to which user belongs.
    group one-to-many (Inverse) This method returns group from UserRoleGroup model to which user belongs.
    parent_team one-to-many (Inverse) This method returns parent_team from Team model to which it belongs.
    child_team one-to-one This method returns user who created the team.
  • team

    This method returns the user who created the team or parent_team.

  • hasPermission

    This method checks if the user has permission or not to access the page.

       if (!Auth::user()->hasPermission('READ', '/dashboard'))
           return 'error';
       return view('dashboard');
    • Returns True if condition is true otherwise return false.
    • Two Attributes are passed when calling the method.
    • One is permissionTypeString, possible values are READ, CREATE, UPDATE, or DELETE.
    • If no permissionTypeString is passed, READ is considered default.
    • Another attribute is permission, which is the route of page.
    • If no permission is passed, current permission is stored in session.
  • hasPermissionMenuItem

    • This method checks if the user has permission or not to access the menu item.
    • Id of menu item is passed menu_item_id.
    • Boolean value is returned. Possible values ar true or false.
  • getTeamOwnerAttribute

    This method returns team owner from the team. return $this['team']['owner']

Config

  • Returns redirect_url if user is unauthorized e.g. /

     if ($request->user() == null)
     return redirect(config('laravelacl.redirect_url'));
  • It has a path of laravel config.php which returns the user from model.

     'models' => [
         'user'   => config('auth.providers.users.model')
     ]
  • Use this code to use config. config('laravelacl.models.user')

Middleware

  • Middleware handles the incoming request.

  • Middleware is set on route. such as

       Route::get('/dashboard', [DashboardController::class, 'dashboard'])->middleware('role:dashboard'); 
  • If route has permission, intended page will be returned otherwise user is redirected.

How Middleware Works

  • If Auth::User not found, homepage is returned. e.g. /

  • If permission is null

    • Get the current path info for the request.

      $permission = $request->path();

    • Current permission is stored in the session using current path.

      \Session::put('current_permission', $permission);
      

MODELS

  • MenuItem

    Methode relation Description
    children one-to-many This method returns submenu items from MenuItem model. One menuitem can have one or more child items.
    permissions one-to-many This method returns list of permissions from Permission model. One menuitem can have one or more permissions.
    user_roles many-To-many This method returns list of user_roles that belong to MenuItem.
  • Permission

    Method relation Description
    menu_item one-to-many (Inverse) This method return menuitem from MenuItem model that belongs to permission.
    type one-to-many (Inverse) This method return permission type which belongs to permission.
  • PermissionType

    • permission types has been created through seeder.
    • These types are "READ", "CREATE", "UPDATE", "DELETE" and "MANAGE".
  • Team

    Method relation Description
    owner one-to-many(Inverse) This method returns user who creates the team. A team can have only one owner.
    permissions belongToMany This method returns list of permissions from Permission model that belongs to Team.
    members one-to-many This method returns list of members. A team can have one or more members.
    user_roles one-to-many This method returns list of user_roles from UserRole model. Team can have one or more user roles.
  • User Role

    Method relation Description
    permissions belongsToMany This method returns list of permissions from Permission model that belong to user_role.
    menu_items belongsToMany This method returns list of menu_items from MenuItem belong with user_role.
    team one-to-many (Inverse) This method returns team which belongs to user role.
    users one-to-many This method returns list of users from User model. A user role can one or more users.
    groups belongsToMany This method returns groups that belong to UserRoleGroup.
  • UserRoleGroup

    Method relation Description
    users one-to-many This method returns list of users object. One user role group can have one or more users.
    user_roles belongsToMany This method returns list of user_roles from UserRole that belong with UserRoleGroup model.

    Teams

Step. 1

  • Team is created with a user_id.

    Team::updateOrCreate(['user_id' => 1]);

Step. 2

  • Team owner creates user role.

Step. 3

  • Owner assigns the permission to the created user role.

  • Owner can assign the permission which are accessible by him.

  • Permission is fetched from Permission model to assign permission,

  • When owner assigns the permissions, these permissions will sync using following code.

    $userRole->permissions()->sync($request['permissions']);

Step. 4

  • Team members can be added by using the created user role.

Sidebar Creation

  • Use the following class for side bar.
<aside class="sidenav bg-white navbar navbar-vertical navbar-expand-xs border-0 border-radius-xl my-3 fixed-start ms-4 " id="sidenav-main">
  <div class="sidenav-header">
    <div class="main-menu menu-fixed menu-dark menu-accordion menu-shadow" data-scroll-to-active="true">
      <div class="navbar-header bg-white">
        <ul class="nav navbar-nav flex-row">
          <li class="nav-item mr-auto">
            <a class="navbar-brand" href="{{ url('/dashboard') }}">
              <div class="brand-logo"></div>
              <h2 class="brand-text mb-0">Your Project Name</h2>
            </a>
          </li>
          <li class="nav-item nav-toggle">
            <a class="nav-link modern-nav-toggle pr-0" data-toggle="collapse" id="sidebar_collapse"><i class="feather icon-x d-block d-xl-none font-medium-4 black toggle-icon"></i><i class="toggle-icon feather icon-disc font-medium-4 d-none d-xl-block collapse-toggle-icon black" data-ticon="icon-disc"></i></a>
          </li>
        </ul>
      </div>
        <div class="main-menu-content mt-2">

          Content of sidebar
          
      </div>
    </div>
  </div>
</aside>

Content of sidebar

  • Sidebar is created using the permissions which are assigned.

  • If the user_role is authenticated.

    • Loop begins and checks if user has permission to access the menuitem.
    • Name of menu item appears on the sidebar.
  • Request::is checks if the incoming request matches to the menuitem route then item becomes active. Request::is(strtolower(str_replace('/','',$menuItem['route'])))

  • If any menu item has submenu item, it will be opened after matching the request.

  • If user has permission to access menuitem, the loop starts and matching submenu item becomes active.

  <ul class="navigation navigation-main" id="main-menu-navigation" data-menu="menu-navigation">
     @if(Auth::user()['user_role'])
      @foreach(Auth::user()['user_role']->menu_items()->orderBy('order_number', 'ASC')->get() as $menuItem)
        @if (Auth::user()->hasPermissionMenuItem($menuItem['id']))
          @if ($menuItem['show_on_sidebar'])
               @if($menuItem['heading'])
                  <li class="navigation-header">
                         {{$menuItem['heading']}}
                  </li>
               @endif
               @if (count($menuItem['children']) == 0)
                  @if($menuItem['parent_id'] === 0)
                   <li class="nav-item {{ Request::is(strtolower(str_replace('/','',$menuItem['route']))) || Request::is(strtolower(str_replace('/','',$menuItem['route'])).'/*')?'active':'' }}">
                      <a href="{{ url($menuItem['route']) }}">
                          <i class="{{ $menuItem['icon'] }}"></i>
                          <span class="menu-title" data-i18n="{{ $menuItem['name'] }}">{{ $menuItem['name'] }}</span>
                      </a>
                   </li>
                  @endif
                  @else
                    <li class="nav-item has-sub {{ Request::is(strtolower(str_replace(' ','_',str_replace('/','',$menuItem['route']))).'/*') && (Auth::user()->sidebar_collapse == 0)?  'open' :'' }}">
                       <a href="#"><i class="{{ $menuItem['icon'] }}"></i>
                        <span class="menu-title" data-i18n="{{ $menuItem['name'] }}">{{ $menuItem['name'] }}</span>
                      </a>
                      <ul class="menu-content">
                         @foreach($menuItem['children'] as $child)
                           @if (Auth::user()->hasPermissionMenuItem($child['id']))
                             <li class="nav-item {{ Request::is(strtolower(str_replace(' ','_',$child['name']))) || Request::is('*/'.strtolower(str_replace(' ','_',$child['name'])))?'active':'' }}">
                                <a href="{{ url($child['route']) }}"><i class="{{ $child['icon'] }}"></i><span class="menu-item" data-i18n="{{ $child['name'] }}">{{ $child['name'] }}</span></a>
                             </li>
                           @endif
                         @endforeach
                      </ul>
                    </li>
                  @endif
            @endif
          @endif
        @endforeach
       @endif
    </ul>