Ở bài trước các closure trong routes/web.php của TaskFlow đã bắt đầu dài ra. Tiếp tục với series, bài này mình sẽ giới thiệu với mọi người về controller trong Laravel 13 và chuyển phần xử lý của các route project vào ProjectController.
1. Controller là gì?
Controller là các class chứa phần xử lý request thay cho closure trong file route. Mỗi controller gom các xử lý có chung một nghiệp vụ, VD ProjectController lo việc hiển thị, tạo, sửa, xóa project, còn file route chỉ khai báo URI nào gọi method nào.
Mặc định các controller nằm trong thư mục app/Http/Controllers, trong đó có sẵn abstract class rỗng Controller. Controller không bắt buộc phải kế thừa nó, nhưng nên kế thừa để sau này đặt vào đó các method dùng chung.
2. Tạo controller và gắn vào route.
Để tạo controller, các bạn sử dụng lệnh artisan với cú pháp như sau:
php artisan make:controller ControllerName
Trong đó: ControllerName là tên controller, có thể kèm thư mục con (VD Admin/ReportController).
VD: Mình tạo ProjectController cho TaskFlow, rồi chuyển code trong closure của hai route projects.index và projects.show vào hai method index và show.
php artisan make:controller ProjectController
- app/Http/Controllers/ProjectController.php
<?php
namespace App\Http\Controllers;
class ProjectController extends Controller
{
public function index(): array
{
return [
['id' => 1, 'name' => 'Website toidicode.com'],
['id' => 2, 'name' => 'TaskFlow API'],
];
}
public function show(string $id): string
{
return 'Chi tiết project #'.$id;
}
}
Ở route, thay cho closure là mảng gồm tên class controller và tên method. Tham số {id} được truyền vào method như với closure.
- routes/web.php
use App\Http\Controllers\ProjectController;
Route::prefix('projects')
->name('projects.')
->whereNumber('id')
->middleware('throttle:projects')
->group(function () {
Route::get('/', [ProjectController::class, 'index'])->name('index');
Route::get('/{id}', [ProjectController::class, 'show'])->name('show');
// route tasks.index tạm giữ nguyên closure
});
// /projects => [{"id":1,"name":"Website toidicode.com"},{"id":2,"name":"TaskFlow API"}]
// /projects/1 => Chi tiết project #1
Nếu nhiều route cùng dùng một controller, các bạn có thể khai báo controller một lần bằng controller(), mỗi route chỉ cần ghi tên method:
Route::controller(ProjectController::class)->group(function () {
Route::get('/projects', 'index');
Route::get('/projects/{id}', 'show');
});
3. Resource controller.
Project cần đủ các thao tác CRUD: xem danh sách, xem chi tiết, tạo, sửa, xóa. Resource controller có sẵn các method cho những thao tác đó, và chỉ cần một dòng Route::resource() để khai báo toàn bộ route.
Để tạo resource controller, các bạn thêm option --resource. Vì ProjectController đã tồn tại nên mình thêm --force để ghi đè:
php artisan make:controller ProjectController --resource --force
- app/Http/Controllers/ProjectController.php
// file được tạo (rút gọn, bỏ comment)
class ProjectController extends Controller
{
public function index() {} // danh sách
public function create() {} // form tạo mới
public function store(Request $request) {} // lưu project mới
public function show(string $id) {} // chi tiết
public function edit(string $id) {} // form sửa
public function update(Request $request, string $id) {} // lưu thay đổi
public function destroy(string $id) {} // xóa
}
Mình chép lại nội dung của index và show ở phần 2, các method còn lại tạm để trống, chúng sẽ được viết dần khi có form (Bài 8) và model (Bài 15).
Tiếp theo, thay các route project bằng Route::resource():
- routes/web.php
Route::middleware('throttle:projects')
->whereNumber('id')
->group(function () {
Route::get('/projects/{id}/tasks', function (string $id) {
return 'Danh sách task của project #'.$id;
})->name('projects.tasks.index');
Route::resource('projects', ProjectController::class)
->parameters(['projects' => 'id']);
});
Trong đó:
'projects'là tên resource, dùng làm prefix cho URI và tên route, nên nhóm route không cầnprefix(),name()như bài trước nữa.- Tham số mặc định là dạng số ít của tên resource (
{project}),parameters()giữ lại tên{id}cho khớp vớiwhereNumber('id').
php artisan route:list --path=projects
# GET|HEAD projects .......... projects.index › ProjectController@index
# POST projects .......... projects.store › ProjectController@store
# GET|HEAD projects/create . projects.create › ProjectController@create
# GET|HEAD projects/{id} ....... projects.show › ProjectController@show
# PUT|PATCH projects/{id} ... projects.update › ProjectController@update
# DELETE projects/{id} . projects.destroy › ProjectController@destroy
# GET|HEAD projects/{id}/edit .. projects.edit › ProjectController@edit
# GET|HEAD projects/{id}/tasks projects.tasks.index › routes/web.php:13
Một dòng Route::resource() đã tạo ra 7 route ứng với 7 method của controller, tên route vẫn như cũ nên các chỗ gọi route('projects.show', ['id' => 1]) không phải sửa. Các route POST, PUT, DELETE lúc này sẽ trả về lỗi 419 nếu gửi không kèm CSRF token như mình đã nói ở Bài 3.
Nếu không cần đủ 7 route, các bạn dùng only() để chọn hoặc except() để bỏ bớt:
Route::resource('projects', ProjectController::class)->only(['index', 'show']);
// chỉ tạo projects.index, projects.show
Route::resource('projects', ProjectController::class)->except(['create', 'edit']);
// tạo 5 route, bỏ projects.create, projects.edit
API thường không cần hai route hiển thị form create, edit, khi đó dùng Route::apiResource() (Bài 23).
4. Dependency injection trong controller.
Laravel dùng service container (Bài 25) để khởi tạo controller, nên các bạn chỉ cần khai báo kiểu của đối tượng cần dùng ở constructor hoặc tham số method, Laravel sẽ tự tạo và truyền vào. Cách này gọi là dependency injection.
Inject qua constructor.
VD: Mình chuyển mảng project cố định ra một class riêng là ProjectRepository, rồi inject nó vào ProjectController.
- app/Repositories/ProjectRepository.php
<?php
namespace App\Repositories;
class ProjectRepository
{
public function all(): array
{
return [
['id' => 1, 'name' => 'Website toidicode.com'],
['id' => 2, 'name' => 'TaskFlow API'],
];
}
}
- app/Http/Controllers/ProjectController.php
use App\Repositories\ProjectRepository;
class ProjectController extends Controller
{
public function __construct(
protected ProjectRepository $projects,
) {}
public function index(): array
{
return $this->projects->all();
}
}
Chúng ta không hề gọi new ProjectRepository(), Laravel tự làm việc đó khi tạo controller.
Inject qua method.
Các bạn cũng có thể inject ở tham số của từng method, hay gặp nhất là Illuminate\Http\Request chứa thông tin của request hiện tại. Tham số route phải đặt sau các đối tượng được inject.
use Illuminate\Http\Request;
public function show(Request $request, string $id): string
{
$tab = $request->query('tab', 'overview');
return 'Chi tiết project #'.$id.', tab: '.$tab;
}
// /projects/1 => Chi tiết project #1, tab: overview
// /projects/1?tab=tasks => Chi tiết project #1, tab: tasks
$request->query() lấy giá trị trên query string, tham số thứ hai là giá trị mặc định. Request sẽ được nói chi tiết ở Bài 8.
5. Single action controller.
Controller chỉ làm một việc có thể viết dưới dạng single action controller, chỉ có một method __invoke. Để tạo nó, các bạn thêm option --invokable.
VD: Chuyển closure của route projects.tasks.index vào controller ListProjectTasks.
php artisan make:controller ListProjectTasks --invokable
- app/Http/Controllers/ListProjectTasks.php
<?php
namespace App\Http\Controllers;
class ListProjectTasks extends Controller
{
public function __invoke(string $id): string
{
return 'Danh sách task của project #'.$id;
}
}
Ở route, các bạn chỉ cần truyền tên class, không cần tên method:
- routes/web.php
use App\Http\Controllers\ListProjectTasks;
Route::get('/projects/{id}/tasks', ListProjectTasks::class)
->name('projects.tasks.index');
// /projects/1/tasks => Danh sách task của project #1
Đến đây routes/web.php của TaskFlow không còn closure nào cho project nữa. Việc gắn middleware ngay trong controller bằng attribute #[Middleware] mình sẽ nói ở Bài 9.
6. Lời kết.
Như vậy chúng ta đã tìm hiểu xong về controller trong Laravel 13: tạo controller, resource controller, dependency injection và single action controller. Các bạn có thể xem thêm tại tài liệu chính thức. Controller của TaskFlow mới trả về mảng và chuỗi, bài sau mình sẽ giới thiệu về Blade template để hiển thị danh sách project bằng HTML.
Đăng ký nhận tin.
Chúng tôi chỉ gửi tối đa 2 lần trên 1 tháng. Tuyên bố không spam mail!





0 Comments