😆Use package lavary/laravel-menu create menu have role & permission (ok)

https://github.com/lavary/laravel-menu

C:\xampp82\htdocs\lva2\app\Http\Middleware\GenerateMenus.php

<?php
namespace App\Http\Middleware;
use Closure;
class GenerateMenus
{
  /**
   * Handle an incoming request.
   *
   * @param  \Illuminate\Http\Request  $request
   * @return mixed
   */
  public function handle($request, Closure $next)
  {
    \Menu::make('admin_sidebar', function ($menu) {
      // Dashboard
      $menu->add('<i class="nav-icon fa-solid fa-cubes"></i> ' . __('Dashboard'), [
        'route' => 'home',
        'class' => 'nav-item',
      ])
        ->data([
          'order' => 1,
          'activematches' => 'admin/dashboard*',
        ])
        ->link->attr([
          'class' => 'nav-link',
        ]);
      // Notifications
      $menu->add('<i class="nav-icon fas fa-bell"></i> Notifications', [
        'route' => 'home',
        'class' => 'nav-item',
      ])
        ->data([
          'order' => 99,
          'activematches' => 'admin/notifications*',
          'permission' => [],
        ])
        ->link->attr([
          'class' => 'nav-link',
        ]);
      // Separator: Access Management
      $menu->add('Management', [
        'class' => 'nav-title',
      ])
        ->data([
          'order' => 101,
          'permission' => ['edit_settings', 'view_backups', 'view_users', 'view_roles', 'view_logs'],
        ]);
      // Settings
      $menu->add('<i class="nav-icon fas fa-cogs"></i> Settings', [
        'route' => 'home',
        'class' => 'nav-item',
      ])
        ->data([
          'order' => 102,
          'activematches' => 'admin/settings*',
          'permission' => ['edit_settings'],
        ])
        ->link->attr([
          'class' => 'nav-link',
        ]);
      // Backup
      $menu->add('<i class="nav-icon fas fa-archive"></i> Backups', [
        'route' => 'home',
        'class' => 'nav-item',
      ])
        ->data([
          'order' => 103,
          'activematches' => 'admin/backups*',
          'permission' => ['view_backups'],
        ])
        ->link->attr([
          'class' => 'nav-link',
        ]);
      // Access Control Dropdown
      $accessControl = $menu->add('<i class="nav-icon fa-solid fa-user-gear"></i> Access Control', [
        'class' => 'nav-group',
      ])
        ->data([
          'order' => 104,
          'activematches' => [
            'admin/users*',
            'admin/roles*',
          ],
          'permission' => ['view_users', 'view_roles'],
        ]);
      $accessControl->link->attr([
        'class' => 'nav-link nav-group-toggle',
        'href' => '#',
      ]);
      // Submenu: Users
      $accessControl->add('<i class="nav-icon fa-solid fa-user-group"></i> Users', [
        'route' => 'home',
        'class' => 'nav-item',
      ])
        ->data([
          'order' => 105,
          'activematches' => 'admin/users*',
          'permission' => ['view_users'],
        ])
        ->link->attr([
          'class' => 'nav-link',
        ]);
      // Submenu: Roles
      $accessControl->add('<i class="nav-icon fa-solid fa-user-shield"></i> Roles', [
        'route' => 'home',
        'class' => 'nav-item',
      ])
        ->data([
          'order' => 106,
          'activematches' => 'admin/roles*',
          'permission' => ['view_roles'],
        ])
        ->link->attr([
          'class' => 'nav-link',
        ]);
      // Log Viewer
      // Log Viewer Dropdown
      $accessControl = $menu->add('<i class="nav-icon fa-solid fa-list-check"></i> Log Viewer', [
        'class' => 'nav-group',
      ])
        ->data([
          'order' => 107,
          'activematches' => [
            'log-viewer*',
          ],
          'permission' => ['view_logs'],
        ]);
      $accessControl->link->attr([
        'class' => 'nav-link nav-group-toggle',
        'href' => '#',
      ]);
      // Submenu: Log Viewer Dashboard
      $accessControl->add('<i class="nav-icon fa-solid fa-list"></i> Dashboard', [
        'route' => 'home',
        'class' => 'nav-item',
      ])
        ->data([
          'order' => 108,
          'activematches' => 'admin/log-viewer',
        ])
        ->link->attr([
          'class' => 'nav-link',
        ]);
      // Submenu: Log Viewer Logs by Days
      $accessControl->add('<i class="nav-icon fa-solid fa-list-ol"></i> Logs by Days', [
        'route' => 'home',
        'class' => 'nav-item',
      ])
        ->data([
          'order' => 109,
          'activematches' => 'admin/log-viewer/logs*',
        ])
        ->link->attr([
          'class' => 'nav-link',
        ]);
      // Access Permission Check
      $menu->filter(function ($item) {
        if ($item->data('permission')) {
          if (auth()->check()) {
            if (auth()->user()->hasRole('super admin')) {
              return true;
            } elseif (auth()->user()->hasAnyPermission($item->data('permission'))) {
              return true;
            }
          }
          return false;
        } else {
          return true;
        }
      });
      // Set Active Menu
      $menu->filter(function ($item) {
        if ($item->activematches) {
          $activematches = (is_string($item->activematches)) ? [$item->activematches] : $item->activematches;
          foreach ($activematches as $pattern) {
            if (request()->is($pattern)) {
              $item->active();
              $item->link->active();
              if ($item->hasParent()) {
                $item->parent()->active();
              }
            }
          }
        }
        return true;
      });
    })->sortBy('order');
    return $next($request);
  }
}

C:\xampp82\htdocs\lva2\resources\views\welcome.blade.php

{!! $admin_sidebar->asUl( ['class' => 'sidebar-nav', 'data-coreui'=>'navigation', 'data-simplebar'], ['class' => 'nav-group-items'] ) !!}

C:\xampp82\htdocs\lva2\app\Http\Kernel.php

\App\Http\Middleware\GenerateMenus::class,

C:\xampp82\htdocs\lva2\config\app.php

Lavary\Menu\ServiceProvider::class,
'Menu' => Lavary\Menu\Facade::class,

C:\xampp82\htdocs\lva2\app\Console\Commands\AuthPermissionCommand.php

<?php
namespace App\Console\Commands;
use App\Models\Permission;
use Illuminate\Console\Command;
use Illuminate\Support\Str;
class AuthPermissionCommand extends Command
{
  /**
   * The name and signature of the console command.
   *
   * @var string
   */
  protected $signature = 'auth:permission {name} {--R|remove}';
  /**
   * The console command description.
   *
   * @var string
   */
  protected $description = 'Create Permissions for default mothods. The Names shoule be plural.';
  /**
   * Create a new command instance.
   *
   * @return void
   */
  public function __construct()
  {
    parent::__construct();
  }
  /**
   * Execute the console command.
   *
   * @return mixed
   */
  public function handle()
  {
    $permissions = $this->generatePermissions();
    // check if its remove
    if ($this->option('remove')) {
      // remove permission
      if (Permission::where('name', 'LIKE', '%' . $this->getNameArgument())->delete()) {
        $this->warn('Permissions ' . implode(', ', $permissions) . ' deleted.');
      } else {
        $this->warn('No permissions for ' . $this->getNameArgument() . ' found!');
      }
    } else {
      // create permissions
      foreach ($permissions as $permission) {
        Permission::firstOrCreate(['name' => $permission]);
      }
      $this->info('Permissions ' . implode(', ', $permissions) . ' created.');
    }
  }
  private function generatePermissions()
  {
    $abilities = ['view', 'add', 'edit', 'delete', 'restore'];
    $name = $this->getNameArgument();
    return array_map(function ($val) use ($name) {
      return $val . '_' . $name;
    }, $abilities);
  }
  private function getNameArgument()
  {
    return strtolower(Str::plural($this->argument('name')));
  }
}

C:\xampp82\htdocs\lva2\app\Events\Backend\UserCreated.php

<?php
namespace App\Events\Backend;
use App\Models\User;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class UserCreated
{
  use Dispatchable;
  use InteractsWithSockets;
  use SerializesModels;
  public $user;
  /**
   * Create a new event instance.
   *
   * @return void
   */
  public function __construct(User $user)
  {
    $this->user = $user;
  }
  /**
   * Get the channels the event should broadcast on.
   *
   * @return \Illuminate\Broadcasting\Channel|array
   */
  public function broadcastOn()
  {
    return new PrivateChannel('channel-name');
  }
}

C:\xampp82\htdocs\lva2\app\Models\Permission.php

<?php
namespace App\Models;
class Permission extends \Spatie\Permission\Models\Permission
{
  /**
   * Default Permissions of the Application.
   */
  public static function defaultPermissions()
  {
    return [
      'view_users',
      'add_users',
      'edit_users',
      'delete_users',
      'restore_users',
      'block_users',
      'view_roles',
      'add_roles',
      'edit_roles',
      'delete_roles',
      'restore_roles',
      'view_backups',
      'add_backups',
      'create_backups',
      'download_backups',
      'delete_backups',
    ];
  }
  /**
   * Name should be lowercase.
   *
   * @param  string  $value  Name value
   */
  public function setNameAttribute($value)
  {
    $this->attributes['name'] = strtolower($value);
  }
}

C:\xampp82\htdocs\lva2\app\Models\Role.php

<?php
namespace App\Models;
class Role extends \Spatie\Permission\Models\Role
{
  /**
   * Name should be lowercase.
   *
   * @param  string  $value  Name value
   */
  public function setNameAttribute($value)
  {
    $this->attributes['name'] = strtolower($value);
  }
}

C:\xampp82\htdocs\lva2\app\Models\User.php

<?php
namespace App\Models;
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
use Spatie\Permission\Traits\HasRoles;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\Auth;
class User extends Authenticatable
{
  use HasApiTokens, HasFactory, Notifiable, HasRoles, SoftDeletes;
  protected $guarded = [
    'id',
    'updated_at',
    '_token',
    '_method',
    'password_confirmation',
  ];
  /**
   * The attributes that are mass assignable.
   *
   * @var array<int, string>
   */
  protected $fillable = [
    'name',
    'email',
    'password',
  ];
  /**
   * The attributes that should be hidden for serialization.
   *
   * @var array<int, string>
   */
  protected $hidden = [
    'password',
    'remember_token',
  ];
  /**
   * The attributes that should be cast.
   *
   * @var array<string, string>
   */
  protected $casts = [
    'deleted_at' => 'datetime',
    'date_of_birth' => 'datetime',
    'email_verified_at' => 'datetime',
  ];
  protected static function boot()
  {
    parent::boot();
    // create a event to happen on creating
    static::creating(function ($table) {
      $table->created_by = Auth::id();
    });
    // create a event to happen on updating
    static::updating(function ($table) {
      $table->updated_by = Auth::id();
    });
    // create a event to happen on saving
    static::saving(function ($table) {
      $table->updated_by = Auth::id();
    });
    // create a event to happen on deleting
    static::deleting(function ($table) {
      $table->deleted_by = Auth::id();
      $table->save();
    });
  }
}

C:\xampp82\htdocs\lva2\database\factories\UserFactory.php

<?php
namespace Database\Factories;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;
/**
 * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\User>
 */
class UserFactory extends Factory
{
  /**
   * The name of the factory's corresponding model.
   *
   * @var string
   */
  protected $model = User::class;
  /**
   * Define the model's default state.
   *
   * @return array<string, mixed>
   */
  public function definition(): array
  {
    $first_name = $this->faker->firstName;
    $last_name = $this->faker->lastName;
    $name = $first_name . ' ' . $last_name;
    return [
      'first_name' => $first_name,
      'last_name' => $last_name,
      'name' => $name,
      'email' => $this->faker->unique()->safeEmail(),
      'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
      'remember_token' => Str::random(10),
      'mobile' => $this->faker->phoneNumber,
      'date_of_birth' => $this->faker->date,
      'avatar' => 'img/default-avatar.jpg',
      'gender' => $this->faker->randomElement(['Male', 'Female', 'Other']),
      'email_verified_at' => now(),
      'created_at' => now(),
      'updated_at' => now(),
    ];
  }
}

C:\xampp82\htdocs\lva2\database\migrations\2014_10_12_000000_create_users_table.php

<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
  /**
   * Run the migrations.
   */
  public function up(): void
  {
    Schema::create('users', function (Blueprint $table) {
      $table->id();
      $table->string('name');
      $table->string('first_name');
      $table->string('last_name');
      $table->string('username')->nullable();
      $table->string('email')->unique();
      $table->string('mobile')->nullable();
      $table->string('gender')->nullable();
      $table->date('date_of_birth')->nullable();
      $table->timestamp('email_verified_at')->nullable();
      $table->string('password')->nullable();
      $table->string('avatar')->nullable()->default('img/default-avatar.jpg');
      $table->tinyInteger('status')->default(1)->unsigned();
      $table->rememberToken();
      $table->integer('created_by')->unsigned()->nullable();
      $table->integer('updated_by')->unsigned()->nullable();
      $table->integer('deleted_by')->unsigned()->nullable();
      $table->timestamps();
      $table->softDeletes();
    });
  }
  /**
   * Reverse the migrations.
   */
  public function down(): void
  {
    Schema::dropIfExists('users');
  }
};

C:\xampp82\htdocs\lva2\database\seeders\Auth\PermissionRoleTableSeeder.php

<?php
namespace Database\Seeders\Auth;
use App\Models\Permission;
use App\Models\Role;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Schema;
/**
 * Class PermissionRoleTableSeeder.
 */
class PermissionRoleTableSeeder extends Seeder
{
  /**
   * Run the database seed.
   *
   * @return void
   */
  public function run()
  {
    Schema::disableForeignKeyConstraints();
    // Create Roles
    $super_admin = Role::create(['name' => 'super admin']);
    $admin = Role::create(['name' => 'administrator']);
    $manager = Role::create(['name' => 'manager']);
    $executive = Role::create(['name' => 'executive']);
    $user = Role::create(['name' => 'user']);
    // Create Permissions
    Permission::firstOrCreate(['name' => 'view_backend']);
    Permission::firstOrCreate(['name' => 'edit_settings']);
    Permission::firstOrCreate(['name' => 'view_logs']);
    $permissions = Permission::defaultPermissions();
    foreach ($permissions as $perms) {
      Permission::firstOrCreate(['name' => $perms]);
    }
    Artisan::call('auth:permission', [
      'name' => 'posts',
    ]);
    echo "\n _Posts_ Permissions Created.";
    Artisan::call('auth:permission', [
      'name' => 'categories',
    ]);
    echo "\n _Categories_ Permissions Created.";
    Artisan::call('auth:permission', [
      'name' => 'tags',
    ]);
    echo "\n _Tags_ Permissions Created.";
    Artisan::call('auth:permission', [
      'name' => 'comments',
    ]);
    echo "\n _Comments_ Permissions Created.";
    echo "\n\n";
    // Assign Permissions to Roles
    $admin->givePermissionTo(Permission::all());
    $manager->givePermissionTo('view_backend');
    $executive->givePermissionTo('view_backend');
    Schema::enableForeignKeyConstraints();
  }
}

C:\xampp82\htdocs\lva2\database\seeders\Auth\UserRoleTableSeeder.php

<?php
namespace Database\Seeders\Auth;
use App\Models\User;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\Schema;
/**
 * Class UserRoleTableSeeder.
 */
class UserRoleTableSeeder extends Seeder
{
  /**
   * Run the database seed.
   *
   * @return void
   */
  public function run()
  {
    Schema::disableForeignKeyConstraints();
    User::findOrFail(1)->assignRole('super admin');
    User::findOrFail(2)->assignRole('administrator');
    User::findOrFail(3)->assignRole('manager');
    User::findOrFail(4)->assignRole('executive');
    User::findOrFail(5)->assignRole('user');
    Schema::enableForeignKeyConstraints();
  }
}

C:\xampp82\htdocs\lva2\database\seeders\Auth\UserTableSeeder.php

<?php
namespace Database\Seeders\Auth;
use App\Events\Backend\UserCreated;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Schema;
/**
 * Class UserTableSeeder.
 */
class UserTableSeeder extends Seeder
{
  /**
   * Run the database seed.
   *
   * @return void
   */
  public function run()
  {
    Schema::disableForeignKeyConstraints();
    $faker = \Faker\Factory::create();
    // Add the master administrator, user id of 1
    $users = [
      [
        'first_name' => 'Super',
        'last_name' => 'Admin',
        'name' => 'Super Admin',
        'email' => 'super@admin.com',
        'password' => Hash::make('secret'),
        'username' => '100001',
        'mobile' => $faker->phoneNumber,
        'date_of_birth' => $faker->date,
        'avatar' => 'img/default-avatar.jpg',
        'gender' => $faker->randomElement(['Male', 'Female', 'Other']),
        'email_verified_at' => Carbon::now(),
        'created_at' => Carbon::now(),
        'updated_at' => Carbon::now(),
      ],
      [
        'first_name' => 'Admin',
        'last_name' => 'Istrator',
        'name' => 'Admin Istrator',
        'email' => 'admin@admin.com',
        'password' => Hash::make('secret'),
        'username' => '100002',
        'mobile' => $faker->phoneNumber,
        'date_of_birth' => $faker->date,
        'avatar' => 'img/default-avatar.jpg',
        'gender' => $faker->randomElement(['Male', 'Female', 'Other']),
        'email_verified_at' => Carbon::now(),
        'created_at' => Carbon::now(),
        'updated_at' => Carbon::now(),
      ],
      [
        'first_name' => 'Manager',
        'last_name' => 'User User',
        'name' => 'Manager',
        'email' => 'manager@manager.com',
        'password' => Hash::make('secret'),
        'username' => '100003',
        'mobile' => $faker->phoneNumber,
        'date_of_birth' => $faker->date,
        'avatar' => 'img/default-avatar.jpg',
        'gender' => $faker->randomElement(['Male', 'Female', 'Other']),
        'email_verified_at' => Carbon::now(),
        'created_at' => Carbon::now(),
        'updated_at' => Carbon::now(),
      ],
      [
        'first_name' => 'Executive',
        'last_name' => 'User',
        'name' => 'Executive User',
        'email' => 'executive@executive.com',
        'password' => Hash::make('secret'),
        'username' => '100004',
        'mobile' => $faker->phoneNumber,
        'date_of_birth' => $faker->date,
        'avatar' => 'img/default-avatar.jpg',
        'gender' => $faker->randomElement(['Male', 'Female', 'Other']),
        'email_verified_at' => Carbon::now(),
        'created_at' => Carbon::now(),
        'updated_at' => Carbon::now(),
      ],
      [
        'first_name' => 'General',
        'last_name' => 'User',
        'name' => 'General User',
        'email' => 'user@user.com',
        'password' => Hash::make('secret'),
        'username' => '100005',
        'mobile' => $faker->phoneNumber,
        'date_of_birth' => $faker->date,
        'avatar' => 'img/default-avatar.jpg',
        'gender' => $faker->randomElement(['Male', 'Female', 'Other']),
        'email_verified_at' => Carbon::now(),
        'created_at' => Carbon::now(),
        'updated_at' => Carbon::now(),
      ],
    ];
    foreach ($users as $user_data) {
      $user = User::create($user_data);
      event(new UserCreated($user));
    }
    Schema::enableForeignKeyConstraints();
  }
}

C:\xampp82\htdocs\lva2\database\seeders\AuthTableSeeder.php

<?php
namespace Database\Seeders;
use Database\Seeders\Auth\PermissionRoleTableSeeder;
use Database\Seeders\Auth\UserRoleTableSeeder;
use Database\Seeders\Auth\UserTableSeeder;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\Schema;
/**
 * Class AuthTableSeeder.
 */
class AuthTableSeeder extends Seeder
{
  /**
   * Run the database seeds.
   *
   * @return void
   */
  public function run()
  {
    Schema::disableForeignKeyConstraints();
    // Reset cached roles and permissions
    app()['cache']->forget('spatie.permission.cache');
    $this->call(UserTableSeeder::class);
    $this->call(PermissionRoleTableSeeder::class);
    $this->call(UserRoleTableSeeder::class);
    Schema::enableForeignKeyConstraints();
  }
}

Và kết quả thật tuyệt vời

Last updated