<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Models\User;
use Illuminate\Support\Facades\Hash;
use Spatie\Permission\Models\Role;
class UserController extends Controller
{
public function __construct()
{
$this->middleware('can:user list', ['only' => ['index', 'show']]);
$this->middleware('can:user create', ['only' => ['create', 'store']]);
$this->middleware('can:user edit', ['only' => ['show', 'edit']]);
$this->middleware('can:user delete', ['only' => ['destroy']]);
}
/**
* Display a listing of the resource.
*/
public function index()
{
$users = (new User)->newQuery();
if (request()->has('search')) {
$users->where('name', 'Like', '%' . request()->input('search') . '%');
}
$users->latest();
$users = $users->paginate(5);
return view('admin.user.index', compact('users'))->with('i', (request()->input('page', 1) - 1) * 5);
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
$roles = Role::all();
return view('admin.user.create', compact('roles'));
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
{
$request->validate([
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
'password' => ['required', 'confirmed'],
]);
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => Hash::make($request->password),
]);
if (!empty($request->roles)) {
$user->assignRole($request->roles);
}
return redirect()->route('admin.user.index')->with('message', 'User created successfully.');
}
/**
* Display the specified resource.
*/
public function show(User $user)
{
$roles = Role::all();
$userHasRoles = array_column(json_decode($user->roles, true), 'id');
return view('admin.user.show', compact('user', 'roles', 'userHasRoles'));
}
/**
* Show the form for editing the specified resource.
*/
public function edit(User $user)
{
$roles = Role::all();
$userHasRoles = array_column(json_decode($user->roles, true), 'id');
return view('admin.user.edit', compact('user', 'roles', 'userHasRoles'));
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, User $user)
{
$request->validate([
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:users,email,' . $user->id],
'password' => ['nullable', 'confirmed'],
]);
$user->update([
'name' => $request->name,
'email' => $request->email,
]);
if ($request->password) {
$user->update([
'password' => Hash::make($request->password),
]);
}
$roles = $request->roles ?? [];
$user->syncRoles($roles);
return redirect()->route('admin.user.index')->with('message', 'User updated successfully.');
}
/**
* Remove the specified resource from storage.
*/
public function destroy(User $user)
{
$user->delete();
return redirect()->route('admin.user.index')->with('message', 'User deleted successfully');
}
}
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;
class RoleController extends Controller
{
public function __construct()
{
$this->middleware('can:role list', ['only' => ['index', 'show']]);
$this->middleware('can:role create', ['only' => ['create', 'store']]);
$this->middleware('can:role edit', ['only' => ['update', 'edit']]);
$this->middleware('can:role delete', ['only' => ['delete']]);
}
/**
* Display a listing of the resource.
*/
public function index()
{
$roles = (new Role())->newQuery();
if (request()->has('search')) {
$roles->where('name', 'Like', '%' . request()->input('search') . '%');
}
$roles->latest();
$roles = $roles->paginate(5);
return view('admin.role.index', compact('roles'))->with('i', (request()->input('page', 1) - 1) * 5);
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
$permissions = Permission::all();
return view('admin.role.create', compact('permissions'));
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
{
$request->validate(['name' => 'required|string|max:255|unique:' . config('permission.table_names.roles', 'roles') . ',name']);
$role = Role::create(['name' => $request->name, 'guard_name' => 'web']);
if (!empty($request->permissions)) {
$role->givePermissionTo($request->permissions);
}
return redirect()->route('admin.role.index')->with('message', 'Role created successfully.');
}
/**
* Display the specified resource.
*/
public function show(Role $role)
{
$permissions = Permission::all();
$roleHasPermissions = array_column(json_decode($role->permissions, true), 'id');
return view('admin.role.show', compact('role', 'permissions', 'roleHasPermissions'));
}
/**
* Show the form for editing the specified resource.
*/
public function edit(Role $role)
{
$permissions = Permission::all();
$roleHasPermissions = array_column(json_decode($role->permissions, true), 'id');
return view('admin.role.edit', compact('role', 'permissions', 'roleHasPermissions'));
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, Role $role)
{
$request->validate(['name' => 'required|string|max:255|unique:' . config('permission.table_names.roles', 'roles') . ',name,' . $role->id]);
$role->update(['name' => $request->name, 'guard_name' => 'web']);
$permissions = $request->permissions ?? [];
$role->syncPermissions($permissions);
return redirect()->route('admin.role.index')->with('message', 'Role updated successfully.');
}
/**
* Remove the specified resource from storage.
*/
public function destroy(Role $role)
{
$role->delete();
return redirect()->route('admin.role.index')->with('message', 'Role deleted successfully');
}
}
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Spatie\Permission\Models\Permission;
class PermissionController extends Controller
{
public function __construct()
{
$this->middleware('can:permission list', ['only' => ['index', 'show']]);
$this->middleware('can:permission create', ['only' => ['create', 'store']]);
$this->middleware('can:permission edit', ['only' => ['edit', 'update']]);
$this->middleware('can:permission delete', ['only' => ['destroy']]);
}
/**
* Display a listing of the resource.
*/
public function index()
{
$permissions = (new Permission())->newQuery();
if (request()->has('search')) {
$permissions->where('name', 'Like', '%' . request()->input('search') . '%');
}
$permissions->latest();
$permissions = $permissions->paginate(5);
return view('admin.permission.index', compact('permissions'))->with('i', (request()->input('page', 1) - 1) * 5);
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
return view('admin.permission.create');
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
{
$request->validate(['name' => 'required|string|max:255|unique:' . config('permission.table_names.permissions', 'permissions') . ',name']);
Permission::create(['name' => $request->name, 'guard_name' => 'web']);
return redirect()->route('admin.permission.index')->with('message', 'Permission created successfully.');
}
/**
* Display the specified resource.
*/
public function show(Permission $permission)
{
return view('admin.permission.show', compact('permission'));
}
/**
* Show the form for editing the specified resource.
*/
public function edit(Permission $permission)
{
return view('admin.permission.edit', compact('permission'));
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, Permission $permission)
{
$request->validate(['name' => 'required|string|max:255|unique:' . config('permission.table_names.permissions', 'permissions') . ',name,' . $permission->id,]);
$permission->update(['name' => $request->name, 'guard_name' => 'web']);
return redirect()->route('admin.permission.index')->with('message', 'Permission updated successfully.');
}
/**
* Remove the specified resource from storage.
*/
public function destroy(Permission $permission)
{
$permission->delete();
return redirect()->route('admin.permission.index')->with('message', 'Permission deleted successfully');
}
}
<?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;
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable, HasRoles;
/**
* 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 = [
'email_verified_at' => 'datetime',
];
}
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Pagination\Paginator;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
//
}
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Paginator::useBootstrap();
}
}
<?php
namespace App\View\Components;
use Closure;
use Illuminate\Contracts\View\View;
use Illuminate\View\Component;
class Layout extends Component
{
/**
* Create a new component instance.
*/
public function __construct()
{
//
}
/**
* Get the view / contents that represent the component.
*/
public function render(): View|Closure|string
{
return view('components.layout');
}
}
/*
* Dev Customer Service Providers...
*/
Spatie\Permission\PermissionServiceProvider::class,
<?php
return [
'models' => [
/*
* When using the "HasPermissions" trait from this package, we need to know which
* Eloquent model should be used to retrieve your permissions. Of course, it
* is often just the "Permission" model but you may use whatever you like.
*
* The model you want to use as a Permission model needs to implement the
* `Spatie\Permission\Contracts\Permission` contract.
*/
'permission' => Spatie\Permission\Models\Permission::class,
/*
* When using the "HasRoles" trait from this package, we need to know which
* Eloquent model should be used to retrieve your roles. Of course, it
* is often just the "Role" model but you may use whatever you like.
*
* The model you want to use as a Role model needs to implement the
* `Spatie\Permission\Contracts\Role` contract.
*/
'role' => Spatie\Permission\Models\Role::class,
],
'table_names' => [
/*
* When using the "HasRoles" trait from this package, we need to know which
* table should be used to retrieve your roles. We have chosen a basic
* default value but you may easily change it to any table you like.
*/
'roles' => 'roles',
/*
* When using the "HasPermissions" trait from this package, we need to know which
* table should be used to retrieve your permissions. We have chosen a basic
* default value but you may easily change it to any table you like.
*/
'permissions' => 'permissions',
/*
* When using the "HasPermissions" trait from this package, we need to know which
* table should be used to retrieve your models permissions. We have chosen a
* basic default value but you may easily change it to any table you like.
*/
'model_has_permissions' => 'model_has_permissions',
/*
* When using the "HasRoles" trait from this package, we need to know which
* table should be used to retrieve your models roles. We have chosen a
* basic default value but you may easily change it to any table you like.
*/
'model_has_roles' => 'model_has_roles',
/*
* When using the "HasRoles" trait from this package, we need to know which
* table should be used to retrieve your roles permissions. We have chosen a
* basic default value but you may easily change it to any table you like.
*/
'role_has_permissions' => 'role_has_permissions',
],
'column_names' => [
/*
* Change this if you want to name the related pivots other than defaults
*/
'role_pivot_key' => null, //default 'role_id',
'permission_pivot_key' => null, //default 'permission_id',
/*
* Change this if you want to name the related model primary key other than
* `model_id`.
*
* For example, this would be nice if your primary keys are all UUIDs. In
* that case, name this `model_uuid`.
*/
'model_morph_key' => 'model_id',
/*
* Change this if you want to use the teams feature and your related model's
* foreign key is other than `team_id`.
*/
'team_foreign_key' => 'team_id',
],
/*
* When set to true, the method for checking permissions will be registered on the gate.
* Set this to false, if you want to implement custom logic for checking permissions.
*/
'register_permission_check_method' => true,
/*
* When set to true the package implements teams using the 'team_foreign_key'. If you want
* the migrations to register the 'team_foreign_key', you must set this to true
* before doing the migration. If you already did the migration then you must make a new
* migration to also add 'team_foreign_key' to 'roles', 'model_has_roles', and
* 'model_has_permissions'(view the latest version of package's migration file)
*/
'teams' => false,
/*
* When set to true, the required permission names are added to the exception
* message. This could be considered an information leak in some contexts, so
* the default setting is false here for optimum safety.
*/
'display_permission_in_exception' => false,
/*
* When set to true, the required role names are added to the exception
* message. This could be considered an information leak in some contexts, so
* the default setting is false here for optimum safety.
*/
'display_role_in_exception' => false,
/*
* By default wildcard permission lookups are disabled.
*/
'enable_wildcard_permission' => false,
'cache' => [
/*
* By default all permissions are cached for 24 hours to speed up performance.
* When permissions or roles are updated the cache is flushed automatically.
*/
'expiration_time' => \DateInterval::createFromDateString('24 hours'),
/*
* The cache key used to store all permissions.
*/
'key' => 'spatie.permission.cache',
/*
* You may optionally indicate a specific cache driver to use for permission and
* role caching using any of the `store` drivers listed in the cache.php config
* file. Using 'default' here means to use the `default` set in cache.php.
*/
'store' => 'default',
],
];
<?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('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('users');
}
};
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
use Spatie\Permission\PermissionRegistrar;
class CreatePermissionTables extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
$tableNames = config('permission.table_names');
$columnNames = config('permission.column_names');
$teams = config('permission.teams');
if (empty($tableNames)) {
throw new \Exception('Error: config/permission.php not loaded. Run [php artisan config:clear] and try again.');
}
if ($teams && empty($columnNames['team_foreign_key'] ?? null)) {
throw new \Exception('Error: team_foreign_key on config/permission.php not loaded. Run [php artisan config:clear] and try again.');
}
Schema::create($tableNames['permissions'], function (Blueprint $table) {
$table->bigIncrements('id'); // permission id
$table->string('name'); // For MySQL 8.0 use string('name', 125);
$table->string('guard_name'); // For MySQL 8.0 use string('guard_name', 125);
$table->timestamps();
$table->unique(['name', 'guard_name']);
});
Schema::create($tableNames['roles'], function (Blueprint $table) use ($teams, $columnNames) {
$table->bigIncrements('id'); // role id
if ($teams || config('permission.testing')) { // permission.testing is a fix for sqlite testing
$table->unsignedBigInteger($columnNames['team_foreign_key'])->nullable();
$table->index($columnNames['team_foreign_key'], 'roles_team_foreign_key_index');
}
$table->string('name'); // For MySQL 8.0 use string('name', 125);
$table->string('guard_name'); // For MySQL 8.0 use string('guard_name', 125);
$table->timestamps();
if ($teams || config('permission.testing')) {
$table->unique([$columnNames['team_foreign_key'], 'name', 'guard_name']);
} else {
$table->unique(['name', 'guard_name']);
}
});
Schema::create($tableNames['model_has_permissions'], function (Blueprint $table) use ($tableNames, $columnNames, $teams) {
$table->unsignedBigInteger(PermissionRegistrar::$pivotPermission);
$table->string('model_type');
$table->unsignedBigInteger($columnNames['model_morph_key']);
$table->index([$columnNames['model_morph_key'], 'model_type'], 'model_has_permissions_model_id_model_type_index');
$table->foreign(PermissionRegistrar::$pivotPermission)
->references('id') // permission id
->on($tableNames['permissions'])
->onDelete('cascade');
if ($teams) {
$table->unsignedBigInteger($columnNames['team_foreign_key']);
$table->index($columnNames['team_foreign_key'], 'model_has_permissions_team_foreign_key_index');
$table->primary(
[$columnNames['team_foreign_key'], PermissionRegistrar::$pivotPermission, $columnNames['model_morph_key'], 'model_type'],
'model_has_permissions_permission_model_type_primary'
);
} else {
$table->primary(
[PermissionRegistrar::$pivotPermission, $columnNames['model_morph_key'], 'model_type'],
'model_has_permissions_permission_model_type_primary'
);
}
});
Schema::create($tableNames['model_has_roles'], function (Blueprint $table) use ($tableNames, $columnNames, $teams) {
$table->unsignedBigInteger(PermissionRegistrar::$pivotRole);
$table->string('model_type');
$table->unsignedBigInteger($columnNames['model_morph_key']);
$table->index([$columnNames['model_morph_key'], 'model_type'], 'model_has_roles_model_id_model_type_index');
$table->foreign(PermissionRegistrar::$pivotRole)
->references('id') // role id
->on($tableNames['roles'])
->onDelete('cascade');
if ($teams) {
$table->unsignedBigInteger($columnNames['team_foreign_key']);
$table->index($columnNames['team_foreign_key'], 'model_has_roles_team_foreign_key_index');
$table->primary(
[$columnNames['team_foreign_key'], PermissionRegistrar::$pivotRole, $columnNames['model_morph_key'], 'model_type'],
'model_has_roles_role_model_type_primary'
);
} else {
$table->primary(
[PermissionRegistrar::$pivotRole, $columnNames['model_morph_key'], 'model_type'],
'model_has_roles_role_model_type_primary'
);
}
});
Schema::create($tableNames['role_has_permissions'], function (Blueprint $table) use ($tableNames) {
$table->unsignedBigInteger(PermissionRegistrar::$pivotPermission);
$table->unsignedBigInteger(PermissionRegistrar::$pivotRole);
$table->foreign(PermissionRegistrar::$pivotPermission)
->references('id') // permission id
->on($tableNames['permissions'])
->onDelete('cascade');
$table->foreign(PermissionRegistrar::$pivotRole)
->references('id') // role id
->on($tableNames['roles'])
->onDelete('cascade');
$table->primary([PermissionRegistrar::$pivotPermission, PermissionRegistrar::$pivotRole], 'role_has_permissions_permission_id_role_id_primary');
});
app('cache')
->store(config('permission.cache.store') != 'default' ? config('permission.cache.store') : null)
->forget(config('permission.cache.key'));
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
$tableNames = config('permission.table_names');
if (empty($tableNames)) {
throw new \Exception('Error: config/permission.php not found and defaults could not be merged. Please publish the package configuration before proceeding, or drop the tables manually.');
}
Schema::drop($tableNames['role_has_permissions']);
Schema::drop($tableNames['model_has_roles']);
Schema::drop($tableNames['model_has_permissions']);
Schema::drop($tableNames['roles']);
Schema::drop($tableNames['permissions']);
}
}
<?php
namespace Database\Seeders;
// use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Seed the application's database.
*/
public function run(): void
{
$this->call([
BasicPermissionSeeder::class
]);
}
}
<?php
use App\Http\Controllers\Admin\RoleController;
use App\Http\Controllers\Admin\UserController;
use App\Http\Controllers\Admin\PermissionController;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\HomeController;
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider and all of them will
| be assigned to the "web" middleware group. Make something great!
|
*/
Route::get('/', function () {
return view('welcome');
});
Auth::routes();
Route::get('/home', [HomeController::class, 'index'])->name('home');
Route::prefix('admin')->namespace('App\Http\Controllers\Admin')->middleware(['auth'])->group(function () {
Route::name('admin.')->group(function () {
Route::resource('user', UserController::class);
Route::resource('role', RoleController::class);
Route::resource('permission', PermissionController::class);
});
});
<?php
return [
/*
|--------------------------------------------------------------------------
| Authentication Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are used during authentication for various
| messages that we need to display to the user. You are free to modify
| these language lines according to your application's requirements.
|
*/
'user' => [
'index' => 'User List',
'create' => 'User Create',
],
'permission' => [
'index' => 'Permission List',
'create' => 'Permission Create',
],
'role' => [
'index' => 'Role List',
'create' => 'Role Create',
],
];