Goodbye controllers, hello request handlers!
This package adds a handy way of creating the request handlers.
Basically, the request handler is a single action controller which leads to a more clear request to the response flow.
You should try to use request handlers instead of with controllers and you probably won't feel the need to go back to controllers anytime soon.
- single responsibility (seems like controllers with many actions break this principle);
- testability (you won't need to resolve dependencies that are not related to the action you are testing, since there is only single action in each request handler);
- registering of routes (
Route::get('/posts/{post}', ShowPost::class)
is much more convenient and pretty thanRoute::get('/posts/{post}', 'PostController@show')
).
You can install this package via composer using this command:
composer require hivokas/laravel-handlers
The package will automatically register itself.
You can publish the config file with:
php artisan vendor:publish --provider="Hivokas\LaravelHandlers\Providers\HandlersServiceProvider" --tag="config"
This is the contents of the published config file:
return [
/*
|--------------------------------------------------------------------------
| Base Handler Class
|--------------------------------------------------------------------------
|
| Here you may specify which class will be used as the base class
| for all generated handlers.
|
*/
'base' => Hivokas\LaravelHandlers\Handler::class,
];
- Create a handler
php artisan make:handler ShowPost
ShowPost
handler will be created
- Create handlers for all resource actions (
index
,show
,create
,store
,edit
,update
,destroy
)
php artisan make:handler Post --resource
IndexPost
,ShowPost
,CreatePost
,StorePost
,EditPost
,UpdatePost
,DestroyPost
handlers will be created
- Exclude unnecessary for an API actions (
create
,edit
)
php artisan make:handler Post --resource --api
IndexPost
,ShowPost
,StorePost
,UpdatePost
,DestroyPost
handlers will be created
- Create handlers by the specified actions
php artisan make:handler Post --actions=show,destroy,approve
ShowPost
,DestroyPost
,ApprovePost
handlers will be created
- Exclude specified actions
php artisan make:handler Post --resource --except=index,show,edit
CreatePost
,StorePost
,UpdatePost
,DestroyPost
handlers will be created
- Specify namespace for handlers creating (relative path)
php artisan make:handler Post --resource --namespace=Post
IndexPost
,ShowPost
,CreatePost
,StorePost
,EditPost
,UpdatePost
,DestroyPost
handlers will be created underApp\Http\Handlers\Post
namespace inapp/Http/Handlers/Post
directory
- Specify namespace for handlers creating (absolute path)
php artisan make:handler ActivateUser --namespace=\\App\\Foo\\Bar
ActivateUser
handler will be created underApp\Foo\Bar
namespace inapp/Foo/Bar
directory
- Force create
php artisan make:handler EditPost --force
If
EditPost
handler already exists, it will be overwritten by the new one
Request handlers are invokable classes that use PHP's __invoke
magic function, turning them into a Callable, which allows them to be called as a function. So you need to return response in __invoke
method.
Eventually your handlers will look something like this:
<?php
namespace App\Http\Handlers\Foo;
use App\Models\Foo;
use App\Services\FooService;
use Hivokas\LaravelHandlers\Handler;
use App\Http\Requests\Foo\UpdateFooRequest;
class UpdateFoo extends Handler
{
protected $service;
public function __construct(FooService $service)
{
$this->service = $service;
}
public function __invoke(UpdateFooRequest $request, Foo $foo)
{
$this->bar($foo, $request->validated());
return redirect('foo.show', $foo);
}
protected function bar(Foo $foo, array $validated)
{
return $this->service->baz($foo, $validated);
}
}
Here are several ways to register routes where request handlers are used as an actions.
- Create
routes/handlers.php
file (you can choose any name, it's just an example) - Define the "handlers" route group in
app/Providers/RouteServiceProvider.php
// app/Providers/RouteServiceProvider.php
protected function mapHandlersRoutes()
{
Route::middleware('web')
->namespace('App\Http\Handlers')
->group(base_path('routes/handlers.php'));
}
// app/Providers/RouteServiceProvider.php
public function map()
{
$this->mapApiRoutes();
$this->mapWebRoutes();
$this->mapHandlersRoutes();
//
}
// routes/handlers.php
Route::get('/post/{post}', 'ShowPost');
// app/Providers/RouteServiceProvider.php
protected function mapHandlersRoutes()
{
Route::middleware('web')
->group(base_path('routes/handlers.php'));
}
// app/Providers/RouteServiceProvider.php
public function map()
{
$this->mapApiRoutes();
$this->mapWebRoutes();
$this->mapHandlersRoutes();
//
}
// routes/handlers.php
use App\Handlers\ShowPost;
Route::get('/post/{post}', ShowPost::class); // pretty sweet, isn't it? 😍
- Change the namespace for "web" group in
RouteServiceProvider.php
// app/Providers/RouteServiceProvider.php
protected function mapWebRoutes()
{
Route::middleware('web')
->namespace('App\Http') // pay attention here
->group(base_path('routes/web.php'));
}
- Put request handlers and controllers in different route groups in
routes/web.php
file and prepend an appropriate namespace for each of them
// routes/web.php
Route::group(['namespace' => 'Handlers'], function () {
Route::get('/posts/{post}', 'ShowPost');
Route::delete('/posts/{post}', 'DestroyPost');
});
Route::group(['namespace' => 'Controllers'], function () {
Route::get('/users', 'UserController@index');
Route::get('/users/{user}', 'UserController@show');
});
You can run the tests with:
composer test
Please see CHANGELOG for more information what has changed recently.
Please see CONTRIBUTING for details.
The MIT License (MIT). Please see License File for more information.