Back to Course |
How to Create Laravel Package: Step-by-Step Example

Middleware: Web and Spatie Check

Now, if you try to click Add Role, you will get an error Undefined variable $errors

That variable comes from the default Validation mechanism, exactly as the Laravel documentation says.

packages/laraveldaily/laravel-permission-editor/resources/views/roles/create.blade.php:

// ...
 
<div class="sm:max-w-md px-6 py-4">
 
@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('permission-editor.roles.store') }}" method="POST">
@csrf
<div>
// ...

So if Laravel docs say that validation error should come automatically, why is it not defined as a variable?

Because that automation comes from the Laravel middleware called "web".

It is automatically enabled in the Laravel project app/Providers/RouteServiceProvider.php when registering routes/web.php, but in our package, we haven't specifically enabled it.

Let's fix this by adding it to the Route::group().

packages/laraveldaily/laravel-permission-editor/src/PermissionEditorServiceProvider.php:

class PermissionEditorServiceProvider extends ServiceProvider
{
public function boot()
{
Route::prefix('permission-editor')
->as('permission-editor.')
->middleware('web') // <- THIS
->group(function () {
$this->loadRoutesFrom(__DIR__ . '/../routes/web.php');
});
}
}

Now if we reload the browser for the Create Role form...

Laravel package create role

It works!

Now, while we're discussing the subject of Middleware, it may be beneficial to know not only how to assign the existing Laravel middleware, but also to create your custom one.

For example, let's add a check that Spatie Permission DB tables are created, if we don't find them in the database, it means the package is not configured and we need to throw an exception with the error message.

Like for some other classes, I prefer to run the Artisan command to generate the class outside the package, and then copy the generated file into the package, changing the namespaces.

php artisan make:middleware SpatiePermissionMiddleware

Then, after copying that file and changing namespaces, we have this:

packages/laraveldaily/laravel-permission-editor/src/Http/Middleware/SpatiePermissionMiddleware.php:

namespace Laraveldaily\LaravelPermissionEditor\Http\Middleware;
 
use Closure;
use Illuminate\Support\Facades\Schema;
 
class SpatiePermissionMiddleware
{
public function handle($request, Closure $next) {
if (!Schema::hasTable('roles') || !Schema::hasTable('permissions')) {
throw new \Exception('Spatie Laravel Permission package is not configured: missing roles/permissions DB tables');
}
 
return $next($request);
}
}

Notice that we place that file into src/Http/Middleware, exactly as we would do in app/Http/Middleware outside the package.

Now, we need to assign this middleware to our Routes:

packages/laraveldaily/laravel-permission-editor/src/PermissionEditorServiceProvider.php:

use Illuminate\Routing\Router;
use Laraveldaily\LaravelPermissionEditor\Http\Middleware\SpatiePermissionMiddleware;
 
class PermissionEditorServiceProvider extends ServiceProvider
{
public function boot()
{
Route::prefix('permission-editor')
->as('permission-editor.')
->middleware(['web', 'spatie-permission'])
->group(function () {
$this->loadRoutesFrom(__DIR__ . '/../routes/web.php');
});
 
$router = $this->app->make(Router::class);
$router->aliasMiddleware('spatie-permission', SpatiePermissionMiddleware::class);
}
}

As you can see, the syntax is a bit different than registering the Middleware in a typical app/Http/Kernel.php outside the package, but it's doing the same thing: assigning the name of spatie-permission to our Middleware class.

Now, if we don't have the roles DB table for some reason, we will see this:

Laravel package Middleware

Great, we've protected our package from being misconfigured.

Of course, you may add more Middleware classes to secure your routes, like auth and others.

Speaking of which... Let's make the Middlewares configurable?