Instead of defining all of your request handling logic in route files, you may wish to organize this behavior using Controller classes. Controllers can group related request handling logic into a class.
Controllers are stored in the app/Http/Controllers
directory of your application.
Here is an example of a basic controller class. By default, controller classes extend the default Controller
class coming with the framework which provides a few helper methods like the form()
method which may be used to attach a new form:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class PageController extends Controller
{
/**
* Show the home page.
*/
public function index()
{
return view('home');
}
}
Now based on our code example above, we can use this controller and its method/action to define a route like so:
Route::get('/', 'PageController@index');
You can also write the same route in an alternate syntax:
Route::get('/', [PageController::class, 'index']);
Now, when a request matches the specified route URI (/
), the index
method on the PageController
class will be executed. Of course, the route parameters will also be passed to the method.
The Themosis framework does not provide all controller base class utility methods you can expect to find from the Laravel framework. Methods currently available are:
validate()
andmiddleware()
.
Sometimes routes need more parameters. For example when you define a route for a specific page. To use a controller, add the uses
key to the route callback array and set its value to the controller like so:
Route::get('page', ['about-us', 'uses' => 'About@index']);
// or...
Route::get('page', ['about-us', 'uses' => [About::class, 'index']]);
When defining a controller route, it is very important to note that we did not need to specify the full controller namespace. Since the RouteServiceProvider
loads your route files within a route group that contains the namespace, we only specified the portion of the class name that comes after the App\Http\Controllers
portion of the namespace.
If you choose to nest your controllers deeper into the App\Http\Controllers
directory, use the specific class name relative to the App\Http\Controllers
root namespace. So, if your full controller class is App\Http\Controllers\Posts\ResourceController
, you should register routes to the controller like so:
Route::get('uri', 'Posts\ResourceController@method');
// or...
<?php
use App\Http\Controllers\Posts\ResourceController;
Route::get('uri', [ResourceController::class, 'method']);
If you would like to define a controller that only handles a single action, you may place a single __invoke
method on the controller:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class ShowProfile extends Controller
{
/**
* Handle the incoming request.
*
* @param int $id
* @param \Illuminate\Http\Request $request
*
* @return \Illuminate\Http\Response
*/
public function __invoke($id, Request $request)
{
return 'User profile...';
}
}
When registering routes for single action controllers, you do not need to specify a method:
Route::get('user/{id}', 'ShowProfile');
// or...
Route::get('user/{id}', ShowProfile::class);
You can create an invokable controller by using the --invokable
option of the make:controller
console command:
php artisan make:controller ShowProfile --invokable
Middleware may be assigned to the controller's routes in your route files:
Route::get('profile', 'UserController@show')->middleware('wp.cap');
However, it is more convenient to specify middleware within your controller's constructor. Using the middleware()
method from your controller's constructor, you may easily assign middleware to the controller's action. You may even restrict the middleware to only certain methods on the controller class:
class UserController extends Controller
{
/**
* Instantiate a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('wp.can');
$this->middleware('log')->only('index');
$this->middleware('subscribed')->except('store');
}
}
Controllers also allow you to register middleware using a Closure. This provides a convenient way to define a middleware for a single controller without defining an entire middleware class:
$this->middleware(function ($request, $next) {
// ...
return $next($request);
});
The framework resource routing assigns the typical "CRUD" routes to a controller with a single line of code. For example, you may wish to create a controller that handles all HTTP requests for "posts" stored by your application. Using the make:controller
console command, we can quickly create such a controller:
php artisan make:controller PostController --resource
This command will generate a controller at app/Http/Controllers/PostController.php
. The controller will contain a method for each of the available resource operations.
Next, you may register a resourceful route to the controller:
Route::resource('posts', 'PostController');
// or...
Route::resource('posts', PostController::class);
This single route declaration creates multiple routes to handle a variety of actions on the resource. The generated controller will already have methods stubbed for each of these actions, including notes informing you of the HTTP verbs and URIs they handle.
You may register many resource controllers at once by passing an array to the resources()
method:
Route::resources([
'posts' => 'PostController',
'tags' => 'TagController'
]);
Verb | URI | Action | Route Name |
---|---|---|---|
GET | /posts |
index | posts.index |
GET | /posts/create |
create | posts.create |
POST | /posts |
store | posts.store |
GET | /posts/{photo} |
show | posts.show |
GET | /posts/{photo}/edit |
edit | posts.edit |
PUT/PATCH | /posts/{photo} |
update | posts.update |
DELETE | /posts/{photo} |
destroy | posts.destroy |
If you are using route model binding and would like the resource controller's methods to type-hint a model instance, you may use the--model
option when generating the controller:
php artisan make:controller PostController --resource --model=Post
Since HTML forms can't make PUT
, PATCH
, or DELETE
requests, you will need to add a hidden _method
field to spoof these HTTP verbs. The @method
Blade directive can create this field for you:
<form action="/foo/bar" method="POST">
@method('PUT')
</form>
When declaring a resource route, you may specify a subset of actions the controller should handle instead of the full set of default actions:
Route::resource('posts', 'PostController')->only([
'index', 'show'
]);
Route::resource('posts', 'PostController')->except([
'create', 'store', 'update', 'destroy'
]);
When declaring resource routes that will be consumed by APIs, you will commonly want to exclude routes that present HTML templates such as create
and edit
. For convenience, you may use the apiResource()
method to automatically exclude these two routes:
Route::apiResource('posts', 'PostController');
You may register many API resource controllers at once by passing an array to the apiResources()
method:
Route::apiResources([
'posts' => 'PostController',
'tags' => 'TagController'
]);
To quickly generate an API resource controller that does not include the create
or edit
methods, use the --api
option when executing the make:controller
command:
php artisan make:controller Api/PostController --api
By default, all resource controller actions have a route name; however, you can override these names by passing a names
array with your options to the names()
method:
Route::resource('posts', 'PostController')->names([
'create' => 'posts.new'
]);
By default, Route::resource
will create the route parameters for your resource routes based on the "singular" version of the resource name. You can easily override this on a per-resource basis by using the parameters()
method. The array passed into the parameters()
method should be an associative array of resource names and parameter names:
Route::resource('posts', 'PostController')->parameters([
'posts' => 'article'
]);
The example above generates the following URIs for the resource's show
route:
/posts/{article}
By default, Route::resource()
will create resource URIs using English verbs. If you need to localize the create
and edit
action verbs, you may use the Route::resourceVerbs()
method. This may be done in the boot()
method of your AppServiceProvider
:
use Themosis\Support\Facades\Route;
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
Route::resourceVerbs([
'create' => 'creer',
'edit' => 'editer',
]);
}
Once the verbs have been customized, a resource route registration such as Route::resource('posts', 'PostController')
will produce the following URIs:
/posts/creer
/posts/{post}/editer
If you need to add additional routes to a resource controller beyond the default set of resource routes, you should define those routes before your call to Route::resource
; otherwise, the routes defined by the resource()
method may unintentionally take precedence over your supplemental routes:
Route::get('posts/popular', 'PostController@method');
Route::resource('posts', 'PostController');
The Themosis framework implements the Illuminate\Container in order to resolve controllers. As a result, you can now type-hint any dependencies your controller may need in its constructor or public methods.
Here is an example of a dependency injected in a controller constructor:
<?php
namespace App\Http\Controllers;
use App\Books;
class HomeController extends Controller
{
/*
* Auto-instantiate a App\Books class.
*/
public function __construct(private Books $books)
{}
}
Of course, you may also type-hint any contract from Laravel. If the container can resolve it, you can type-hint it. Depending on your application, injecting your dependencies into your controller may provide better testability.
The same principle can be used to controller methods. You can type-hint your dependency just like in the constructor. By default, the Illuminate\Http\Request
class instance is use when you create a controller with the make:controller
console command:
<?php
namespace App\Http\Controllers;
use App\Books;
use Illuminate\Http\Request;
class HomeController extends Controller
{
/*
* Auto-instantiate a App\Books class.
*/
public function show(Request $request, Books $books)
{
//
}
}
If your controller method is also expecting input from route parameters, simply append those parameters after your dependencies. Two possible scenarios for this case:
Route::put('books/{id}', 'BookController@update');
You may still type-hint the Illuminate\Http\Request
and access your id
parameter by defining your controller method as follows:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class BookController extends Controller
{
/**
* Update the given book.
*
* @param Request $request
* @param string $id
* @return Response
*/
public function update(Request $request, $id)
{
//
}
}
If your application is exclusively using controller based routes, you should take advantage of route cache. Using the route cache will drastically decrease the amount of time it takes to register all of your application's routes. In some cases, your route registration may even be up to 100x faster. To generate a route cache, just execute the route:cache
console command:
php artisan route:cache
After running this command, your cached routes file will be loaded on every request. Remember, if you add any new routes you will need to generate a fresh route cache. Because of this, you should only run the route:cache
command during your project's deployment.
You may use the route:clear
command to clear the route cache:
php artisan route:clear
Read the models guide
You can find below a list of links to the official documentation of the Laravel framework that may give you more details about the available APIs also implemented into the Themosis framework:
Made in Belgium