Namísto definování veškeré logiky v route souborech můžete tuto logiku přesměrovat do samostatných Controller tříd. Controllery mohou seskupovat požadované requesty do jediné třídy. Controllery jsou uloženy v adresáři app/Http/Controllers
.
Controller je třída jako každá jiná. Ve většině případů využijete rozšíření od rodičovského controlleru, který obsahuje samotný Laravel framework. Tento rodič poskytuje některé velmi užitečné metody jako jsou middleware
, validate
nebo dispatch
. Toto rozšíření samozřejmě nemusíte používat, pokud k tomu máte nějaký důvod. Controller pak vypadá takto:
<?php
namespace App\Http\Controllers;
use App\User;
use App\Http\Controllers\Controller;
class UserController extends Controller
{
/**
* Show the profile for the given user.
*
* @param int $id
* @return View
*/
public function show($id)
{
return view('user.profile', ['user' => User::findOrFail($id)]);
}
}
Routu s tímto controllerem pak můžeme definovat takto:
Route::get('user/{id}', 'UserController@show');
Jakmile se tedy HTTP požadavek shoduje se specifikovanou URI adresou, zavolá se metoda show
ve třídě UserController
. Parametry se samozřejmě také přesunou.
Je důležité si všimnout, že jsme v routě nedefinovali žádný namespace. To proto, že RouteServiceProvider
automaticky načítá všechny route soubory a přidává k nim specifikovaný namespace, kdy výchozí namespace je App\Http\Controllers
. Název controlleru pak tedy zadáváme až po tomto namespace.
Pokud budete potřebovat vytvořit hlubší strukturu v adresáři App\Http\Controllers
, pak tento namespace berte jako kořenový a pokračujte v jeho pojmenování. Například pokud bychom měli třídu AdminController
v adresáři app/Http/Controllers/Photos
, pak namespace ve třídě bude App\Http\Controllers\Photos\AdminController
a controller v routě definujeme takhle:
Route::get('foo', 'Photos\AdminController@method');
Middleware můžeme ke controlleru přiřadit v routě:
Route::get('profile', 'UserController@show')->middleware('auth');
Nicméně je mnohem pohodlnější middleware specifikovat přímo ve třídě, nejlépe hned v konstruktoru. Díky tomu můžeme hned na začátku určit, jaký middleware se má použít a také můžeme definovat přímo metodu, pro kterou se má či nemá použít:
class UserController extends Controller
{
/**
* @return void
*/
public function __construct()
{
$this->middleware('auth');
$this->middleware('log')->only('index');
$this->middleware('subscribed')->except('store');
}
}
V controllerech nejčastěji řešíme CRUD operace (create, read, update, delete). Klasicky bychom museli pro každou operaci zaregistrovat novou routu a poté ve controlleru vytvořit pro každou opeaci samostatnou metodu, která by požadavek zpracovávala. Sice banalita, avšak psát to pokaždé je celkem neefektivní. Navíc seznam rout se nám rapidně zvýší.
Laravel nám umožňuje velmi jednoduše definovat controller, který tyto operace bude zpracovávat a routa bude mít pouze jeden řádek. Mějme příklad, kdy potřebujeme vytvořit controller, který bude zpracovávat všechny HTTP požadavky pro fotky. S použitím Artisan příkazu make:controller
můžeme velmi rychle takovýto controller vytvořit:
php artisan make:controller PhotoController --resource
Tento příkaz nám vygeneruje nový controller app/Http/Controllers/PhotoController.php
. A právě možnost --resource
vygeneruje ve třídě všechny metody, které potřebujeme pro zpracování CRUD operací.
A teď k routě. Abychom nemuseli psát pro každou operaci samostatnou routu, využijeme metodu resource
, která automaticky registruje HTTP požadavky typu GET, POST, PUT/PATCH a DELETE:
Route::resource('photos', 'PhotoController');
Naráz můžeme definovat i více controllerů, pro které chceme využít metody resource
:
Route::resources([
'photos' => 'PhotoController',
'posts' => 'PostController'
]);
Když bychom se podívali do třídy PhotoController
, kterou jsme si vytvořili, našli bychom sedm různých metod. Každá metoda odpovídá samostatně na různou routu. Zde je seznam na základě jaké routy se volá jaká metoda v controlleru:
Typ | URI | Akce | Název routy |
---|---|---|---|
GET | /photos |
index | photos.index |
GET | /photos/create |
create | photos.create |
POST | /photos |
store | photos.store |
GET | /photos/{photo} |
show | photos.show |
GET | /photos/{photo}/edit |
edit | photos.edit |
PUT/PATCH | /photos/{photo} |
update | photos.update |
DELETE | /photos/{photo} |
destroy | photos.destroy |
Pokud používáte route model binding a chcete, aby se vygenerovali v controlleru metody s type-hintem instance modelu, můžeme v Artisan příkazu při generování nového controlleru použít možnost --model
:
php artisan make:controller PhotoController --resource --model=Photo
HTML formuláře nedokážou odesílat požadavky typu PUT, PATCH nebo DELETE a proto je potřeba přidat skryté pole, podle kterého Laravel rozpozná, o jaký typ se jedná. Blade direktiva @method
tento input za vás rovnou vygeneruje:
<form action="/foo/bar" method="POST">
@method('PUT')
</form>
Když definujeme resource routu, můžeme specifikovat pouze akce, na které má controller reagovat:
Route::resource('photos', 'PhotoController')->only([
'index', 'show'
]);
Route::resource('photos', 'PhotoController')->except([
'create', 'store', 'update', 'destroy'
]);
Pokud deklarujeme routy, které mají zpracováva API požadavky, většinou chceme vyloučit routy, které obsahují HTML šablony, například create
a edit
. Laravel umožňuje takto rovnou specifikovat routy bez těchto dvou pomocí metody apiResource
:
Route::apiResource('photos', 'PhotoController');
Stejně jako u resource
i zde můžeme naráz definovat více controllerů:
Route::apiResources([
'photos' => 'PhotoController',
'posts' => 'PostController'
]);
Pro rychle vygenerování controlleru bez metod create
a edit
můžeme v Artisan příkazu použít možnost --api
:
php artisan make:controller API/PhotoController --api
Pokud použijeme v routě resource, pak všechny akce mají automaticky i vlastní název, jak můžeme vidět v předešlé tabulce. Pokud bychom tyto názvy chtěli přepsat, pak můžeme použít metodu names
:
Route::resource('photos', 'PhotoController')->names([
'create' => 'photos.build'
]);
Ve výchozím nastavení Route::resource
vytvoří parametry rout z názvu resource v jednotném čísle:
// web.php
Route::resource('users', 'AdminUserController');
// AdminUserController
public function show(User $user) {}
Pokud bychom chtěli názvy těchto parametrů změnit, pak můžeme využít metody parameters
a názvy přepsat:
Route::resource('users', 'AdminUserController')->parameters([
'users' => 'admin_user'
]);
To nám vygeneruje následující URI pro metodu show
:
/users/{admin_user}
Pokud potřebujete přidat routy do resource, které nejsou v ní definované, pak je potřeba definovat tyto routy ještě dříve, než zavoláte Route::resource
. Jinak se může stát, že routy definované v resource
získají nechtěně přednost před vlastními routami:
Route::get('photos/popular', 'PhotoController@method');
Route::resource('photos', 'PhotoController');
Pro zavolání všech Laravel controllerů framework využívá service containeru. Výsledkem je to, že díky type-hintu můžete definovat jakékoliv závislosti, které váš controller může potřebovat v jeho konstruktoru. Deklarované závislosti se automaticky načtou a injektnou do instance controlleru:
<?php
namespace App\Http\Controllers;
use App\Repositories\UserRepository;
class UserController extends Controller
{
/**
* The user repository instance.
*/
protected $users;
/**
* Create a new controller instance.
*
* @param UserRepository $users
* @return void
*/
public function __construct(UserRepository $users)
{
$this->users = $users;
}
}
Kromě constructor injection můžeme také využít type-hintu závislostí v metodách controlleru. Nejběžnější případem method injection je vkládání instance Illuminate\Http\Request
do metody controlleru:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class UserController extends Controller
{
/**
* Store a new user.
*
* @param Request $request
* @return Response
*/
public function store(Request $request)
{
$name = $request->name;
//
}
}
Pokud vaše metoda controlleru také očekává parametr z routy, vkládejte tyto parametry až po ostatních specifikovaných závislostech. Například pokud je vaše routa definována takto:
Route::put('user/{id}', 'UserController@update');
Pak stále můžete využít type-hintu Illuminate\Http\Request
a mít zároveň přístup k parametru id
následným definováním metody:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class UserController extends Controller
{
/**
* Update the given user.
*
* @param Request $request
* @param string $id
* @return Response
*/
public function update(Request $request, $id)
{
//
}
}
Hned na úvod je potřeba upozornit, že routy obsahující closure nemohou být zakešované. Abyste mohli routy zakešovat, musíte všechny closure převést na třídy controllerů.
Použití kešování rout můžete drasticky snížit zatížení aplikace, kdy jinak musíte prvně zaregistrovat všechny routy aplikace a až poté vykonat požadavek. V některých případech může tento proces být až 100x rychlejší. Pro zakešování rout stačí zavolat tento příkaz:
php artisan route:cache
Poté co se vykoná tento příkaz, zakešované routy se budou načítat při každém requestu. Nezapomeňte ale, že pokud přidáte novou routu nebo nějakou upravíte, musíte znovu přegenerovat zakešované routy, jinak se změna nezaregistruje.
Pro vymazání kešky rout můžete využít příkaz route:clear
:
php artisan route:clear