We start from the situation where we have this Permission Editor as a functionality inside of our Laravel application.
We have a typical MVC structure: routes, controllers, views, and validation.
routes/web.php:
Route::resource('roles', \App\Http\Controllers\RoleController::class);Route::resource('permissions', \App\Http\Controllers\PermissionController::class);
app/Http/Controllers/RoleController.php:
namespace App\Http\Controllers; use Illuminate\Http\Request;use Spatie\Permission\Models\Role;use Spatie\Permission\Models\Permission; class RoleController extends Controller{ public function index() { $roles = Role::withCount('permissions')->get(); return view('roles.index', compact('roles')); } public function create() { $permissions = Permission::pluck('name', 'id'); return view('roles.create', compact('permissions')); } public function store(Request $request) { $request->validate([ 'name' => ['required', 'string', 'unique:roles'], 'permissions' => ['array'], ]); $role = Role::create(['name' => $request->input('name')]); $role->givePermissionTo($request->input('permissions')); return redirect()->route('roles.index'); } public function edit(Role $role) { $permissions = Permission::pluck('name', 'id'); return view('roles.edit', compact('role', 'permissions')); } public function update(Request $request, Role $role) { $request->validate([ 'name' => ['required', 'string', 'unique:roles,name,' . $role->id], 'permissions' => ['array'], ]); $role->update(['name' => $request->input('name')]); $role->syncPermissions($request->input('permissions')); return redirect()->route('roles.index'); } public function destroy(Role $role) { $role->delete(); return redirect()->route('roles.index'); }}
resources/views/roles/index.blade.php:
@extends('layouts.app') @section('content') <h1 class="text-xl font-semibold text-gray-900">Roles</h1> <a href="{{ route('roles.create') }}">Add Role</a> <table class="min-w-full divide-y divide-gray-300"> <thead class="bg-gray-50"> <tr> <th>Name</th> <th>Permissions</th> <th scope="col"></th> </tr> </thead> <tbody class="divide-y divide-gray-200 bg-white"> @forelse ($roles as $role) <tr> <td>{{ $role->name }}</td> <td>{{ $role->permissions_count }}</td> <td> <a href="{{ route('roles.edit', $role) }}">Edit</a> <form action="{{ route('roles.destroy', $role) }}" method="POST" onsubmit="return confirm('Are you sure?')" class="inline-block"> @csrf @method('DELETE') <button type="submit">Delete</button> </form> </td> </tr> @empty <tr> <td colspan="3">No roles found.</td> </tr> @endforelse </tbody> </table>@endsection
In the Blade snippet above, I intentionally skipped some <div>
parts and CSS classes, so you would focus on the structure and not how it looks visually.
The form to create the role:
resources/views/roles/create.blade.php:
@extends('layouts.app') @section('content') <h1 class="text-xl font-semibold text-gray-900">Create Role</h1> @if ($errors->any()) <div class="text-red-500 text-sm mb-4"> <ul> @foreach ($errors->all() as $error) <li>{{ $error }}</li> @endforeach </ul> </div> @endif <form action="{{ route('roles.store') }}" method="POST"> @csrf <div> <label for="name">Name</label> <input type="text" name="name" id="name" value="{{ old('name') }}" required autofocus> </div> @if ($permissions->count()) <div class="mt-4"> <label for="permissions">Permissions</label> @foreach ($permissions as $id => $name) <input type="checkbox" name="permissions[]" id="permission-{{ $id }}" value="{{ $id }}" @checked(in_array($id, old('permissions', [])))> <label for="permission-{{ $id }}">{{ $name }}</label> <br /> @endforeach </div> @endif <div class="mt-4"> <button type="submit">Save</button> </div> </form>@endsection
Again, some sections are "stripped down" for simplicity.
So, our goal is to turn these two Route Resources into a package, so developers would be able to run composer require our/package
and then navigate to, for example, /permission-editor
to see the same functionality.
Our package will have a prerequisite of spatie/laravel-permission
installed and configured, but we will get to that later in the tutorial. Now, let's start actually creating the package.
Povilas sir thank you so much for this tutorial I was trying to dive into package development but didn't know where to start. Hope this tutorial will make this journey easy
I second this message :)
Thanks, very much appreciate for this