Writing Controllers
Basic Controllers
- Thông thường Controller bạn tạo ra sẽ extend từ base Controller
App\Http\Controllers\Controller
<?php namespace App\Http\Controllers; use App\Models\User; class UserController extends Controller
{ /** * Show the profile for a given user. * * @param int $id * @return \Illuminate\View\View */ public function show($id) { return view('user.profile', [ 'user' => User::findOrFail($id) ]); }
}
- Bạn có thể định nghĩa route cho method
show
như sau:
use App\Http\Controllers\UserController; Route::get('/user/{id}', [UserController::class, 'show']);
- Extend từ base controller là không bắt buộc, nhưng khi đó controller bạn tạo ra sẽ không có quyền truy cập vào các tính năng sẵn có của laravel như
middleware
default hoặcauthorize
method
Single Action Controllers
- Trong trường hợp một action trong controller của bạn đặc biệt phức tạp, sẽ thuận tiện khi bạn tạo ra 1 controller và xử lý duy nhất action đó trong controller, khi đó bạn cần định nghĩa 1 method
__invoke
<?php namespace App\Http\Controllers; use App\Models\User; class ProvisionServer extends Controller
{ /** * Provision a new web server. * * @return \Illuminate\Http\Response */ public function __invoke() { // ... }
}
- Khi đăng ký route cho signle action controller trên bạn sẽ không cần chỉ định method, thay vào đó bạn chỉ cần truyền class của controller:
use App\Http\Controllers\ProvisionServer; Route::post('/server', ProvisionServer::class);
- Bạn cũng có thể tạo 1 single action controller thông qua artisan command:
php artisan make:controller ProvisionServer --invokable
Controller Middleware
- Bạn có thể chỉ định middleware cho route của controller trong file route của bạn:
Route::get('profile', [UserController::class, 'show'])->middleware('auth');
- Hoặc bạn có thể chỉ định middleware trong method
__construct()
của controller, khi đó bạn cũng có thể dễ dàng tùy chỉnh middleware cho action nào hoặc loại bỏ action nào:
class UserController extends Controller
{ /** * Instantiate a new controller instance. * * @return void */ public function __construct() { $this->middleware('auth'); //Chỉ định middleware cho action $this->middleware('log')->only('index'); //Loại bỏ middleware cho action $this->middleware('subscribed')->except('store'); }
}
- Ngoài ra controller cũng cho phép bạn đăng ký middleware bằng cách sử dụng một closure, cung cấp cú pháp thuận tiện cho việc đăng ký một middleware đơn cho controller mà không cần định nghĩa 1 class middleware và đăng ký sử dụng nó:
$this->middleware(function ($request, $next) { return $next($request);
});
Resource Controllers
- Bạn có thể hiểu resource controller là một khuôn mẫu controller gồm 7 method sẵn có để xử lý CRUD (create, read, update, and delete)
- 7 method gồm:
index()
create()
store()
show()
edit()
update()
destroy()
- Để tạo resource controller bạn có thể dùng artisan command:
php artisan make:controller PhotoController --resource
- Khi đó, để khai báo route resource:
use App\Http\Controllers\PhotoController; Route::resource('photos', PhotoController::class);
- Hoặc khai báo route cho nhiều resource controller:
Route::resources([ 'photos' => PhotoController::class, 'posts' => PostController::class,
]);
- Actions Handled By Resource Controller:
Customizing Missing Model Behavior
- Thông thường, phản hồi HTTP 404 sẽ được tạo nếu không tìm thấy mô hình tài nguyên bị ràng buộc ngầm nhưng bạn có thể customize bằng cách call method
missing
khi định nghĩa resource route. Methodmissing
là một closure được gọi khi không tìm thấy một mô hình rằng buộc ngầm:
use App\Http\Controllers\PhotoController;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Redirect; Route::resource('photos', PhotoController::class) ->missing(function (Request $request) { return Redirect::route('photos.index'); });
Soft Deleted Models
- Thông thường model sử dụng
soft deleted
thì các truy vấn sẽ mặc định không truy xuất data đã bị xóa mềm, trong trường hợp bạn muốn bỏ qua rằng buộc ngầm đó thì có thể dùng methodwithTrashed
, để bỏ qua đối với tất cả resource route:
use App\Http\Controllers\PhotoController; Route::resource('photos', PhotoController::class)->withTrashed();
- Hoặc cũng có thể bỏ qua với medthod chỉ định trong controller bằng cách truyền tham số vào method
withTrashed
Route::resource('photos', PhotoController::class)->withTrashed(['show']);
- Bạn cũng có thể bỏ qua bằng cách dùng method
withTrashed
trong eloquent
Specifying The Resource Model
- Nếu bạn muốn tạo model cùng controller bạn có thể thêm optinal
--model
php artisan make:controller PhotoController --model=Photo --resource
Generating Form Requests
- Nếu bạn muốn tạo form request tương ứng khi tạo controller
php artisan make:controller PhotoController --model=Photo --resource --requests
API Resource Routes
- Khi định nghĩa route API cho resource controller, bạn có thể muốn loại bỏ 1 số route render HTML như:
create
,edit
, để loại bỏ 2 route đó bạn có thể sử dụng methodapiResource
của facecadeRoute
use App\Http\Controllers\PhotoController; Route::apiResource('photos', PhotoController::class);
- Bạn cũng có thể loại bỏ route
create
vàedit
cho 1 nhóm resorce controller
use App\Http\Controllers\PhotoController;
use App\Http\Controllers\PostController; Route::apiResources([ 'photos' => PhotoController::class, 'posts' => PostController::class,
]);
- Bạn cũng có thể loại bỏ luôn route
create
vàedit
cũng như method của nó trong controller bằng cách thêm optinal--api
khi tạo resource controller
php artisan make:controller PhotoController --api
- Khi đó trong resource controller chỉ tạo ra các method:
index()
,store()
,update()
,show()
,destroy()
Nested Resources
- Nếu bạn muốn khai báo route lồng nhau:
use App\Http\Controllers\PhotoCommentController; Route::resource('photos.comments', PhotoCommentController::class);
- Khi đó URIs sẽ có dạng:
/photos/{photo}/comments/{comment}
- Chi tiết tất cả url
Shallow Nesting
- Thường thì trong route không nhất thiết phải chứa cả id cha và id con, ví dụ như trong trường route
/photos/{photo}/comments/{comment}
thì không cần thiết chứa cả id của phôt và id của comment vì chỉ cần id của comment đã là mã định danh duy nhất rồi, nên trong các trường hợp như vậy bạn có thể sử dụng lông nhau nôngshallow nesting
:
Route::resource('photos.comments', \App\Http\Controllers\PhotoCommentController::class)->shallow();
- Khi đó route sẽ như sau:
Naming Resource Routes
- Theo mặc định thì tất cả các route của resource controller đều đã có một route name, tuy nhiên bạn có thể override tên của nó bằng cách truyền mảng names khi đăng ký route:
use App\Http\Controllers\PhotoController; Route::resource('photos', PhotoController::class)->names([ 'create' => 'photos.build'
]);
Naming Resource Route Parameters
- Mặc định
Route::resource
sẽ tạo parameter cho route dựa trên dạng số ít, bạn có thể override điều này bằng phương thứcparameters
, mảng truyền trong phương thứcparameters
phải là mảng kết hợp với key làresource
và value là tên tham số
use App\Http\Controllers\AdminUserController; Route::resource('users', AdminUserController::class)->parameters([ 'users' => 'admin_user'
]);
- Ví dụ trên tạo URI sau cho route
show
:
/users/{admin_user}
Scoping Resource Routes
Localizing Resource URIs
- Mặc định thì
Route:resource
sẽ tạo ra URIs sử dụng động từ tiếng anh, ví dụ như:photos/{photo}/comments/create
,photos/{photo}/comments/{comment}/edit
nếu bạn không muốn sử dụng các từ khóa nhưcreate
,edit
thì có thể override lại bằng cách sử dụng phương thứcRoute::resourceVerbs
trong actionboot()
ởRouteServiceProvider.php
public function boot() { $this->configureRateLimiting(); $this->routes(function () { Route::middleware('api') ->prefix('api') ->group(base_path('routes/api.php')); Route::middleware('web') ->group(base_path('routes/web.php')); }); Route::resourceVerbs([ 'create' => 'crear', 'edit' => 'editar', ]); }
- Khi đó URIs route sẽ như sau:
Supplementing Resource Controllers
- Để bổ sung route cho resource controller, bạn chỉ cần định nghĩa route như bình thường
- Trong trường hợp URIs của route trùng với resource controller thì route nào được định nghĩa sau sẽ override route trước đó
Route::get('photos/{photo}/comments', [\App\Http\Controllers\PhotoCommentController::class, 'index'])->name('test');
Route::resource('photos.comments', \App\Http\Controllers\PhotoCommentController::class);
- Trong trường hợp trên route có name là
test
đã bị route resource override do route khai báo trùng URI làphotos/{photo}/comments
Singleton Resource Controllers
- Đôi khi bạn muốn có những tài nguyên chỉ có thể 1 instance duy nhất, VD
profile
của user có thể đượcupdated
những một user chỉ có duy nhất 1profile
, những tài nguyên như vậy được gọi làsingleton resources
có nghĩa là trong 1 thời điểm chỉ có duy nhất 1 phiên bản của tài nguyên được tồn tại, trong trường hợp như vậy bạn có thể đăng ký 1singleton
resource controller
use App\Http\Controllers\ProfileController;
use Illuminate\Support\Facades\Route; Route::singleton('profile', ProfileController::class);
- Route trên sẽ sinh ra các URIs như sau:
- Bạn thấy route
create
,store
,destroy
sẽ không được sinh ra và các routeedit
,show
,update
sẽ không chấp nhận tham số Creatable Singleton Resources - Nhưng đôi khi bạn lại muốn định nghĩa route
create
,store
,delete
chosingleton resource
. Khi đó bạn có thể sử dụng methodcreatable()
trong khi định nghĩasingleton resource route
:
Route::singleton('photos.thumbnail', ThumbnailController::class)->creatable();
- Các URIs tương ứng cho route trên như sau:
- Nếu bạn chỉ muốn định nghĩa thêm route
DELETE
chosingleton resource
mà không muốn định nghĩa routecreate
vàstore
thì bạn có thể dùng methoddestroyable
Route::singleton(...)->destroyable();
API Singleton Resources
apiSingleton
method sử dụng để đăng ký mộtsingleton resource
cho API, khi đó sẽ loại bỏ các route không cần thiết khi sử dụng API như:create
vàedit
Route::apiSingleton('profile', ProfileController::class);
- Tương tự trường hợp đặc biệt bạn cũng có thể tạo ra các route như:
create
,edit
bằng methodcreatable()
Route::apiSingleton('photos.thumbnail', ProfileController::class)->creatable();
Dependency Injection & Controllers
Constructor Injection
<?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 \App\Repositories\UserRepository $users * @return void */ public function __construct(UserRepository $users) { $this->users = $users; }
}
Method Injection
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; class UserController extends Controller
{ /** * Store a new user. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\Response */ public function store(Request $request) { $name = $request->name; // }
}
- Nếu bạn muốn truyền tham số vào method trong controller, giải sử bạn muốn truyền tham số cho route tương ứng như:
use App\Http\Controllers\UserController; Route::put('/user/{id}', [UserController::class, 'update']);
- Khi đó bạn chỉ cần khai báo tham số sau biến
$request
như sau:
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; class UserController extends Controller
{ /** * Update the given user. * * @param \Illuminate\Http\Request $request * @param string $id * @return \Illuminate\Http\Response */ public function update(Request $request, $id) { // }
}