Skip to main content

Starting Point: Package Functionality

Lesson 01/12 4 min read
Autoplay

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.

Comments & Discussion

FA
Faizan Amin ✓ Link copied!

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

GK
Gavin Kimpson ✓ Link copied!

I second this message :)

AM
Andi Muhammad ✓ Link copied!

Thanks, very much appreciate for this

AM
adam mrizeq ✓ Link copied!

GJW9

We'd Love Your Feedback

Tell us what you like or what we can improve

Feel free to share anything you like or dislike about this page or the platform in general.