Courses

How to Create Laravel Package: Step-by-Step Example

Starting Point: Package Functionality

We start from the situation where we have this Permission Editor as a functionality inside of our Laravel application.

Laravel package roles list

Laravel package roles create

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.

avatar

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

👍 1
avatar

I second this message :)

avatar

Thanks, very much appreciate for this