Appearance
Xử lý Request trong Laravel
1. Giới thiệu
Trong Laravel, để xử lý các yêu cầu HTTP, chúng ta thường sử dụng class Illuminate\Http\Request như sau:
php
use Illuminate\Http\Request;Đây là class cơ sở cung cấp đầy đủ các tính năng cần thiết. Khi cần xử lý validation hoặc authorization phức tạp hơn, việc tạo các class Form Request riêng (kế thừa từ Illuminate\Foundation\Http\FormRequest) là cách tiếp cận được khuyến khích.
2. Tránh ->input(), ->get() và ->all()
$request->input('field')/$request->get('field'): Trả vềmixed, dẫn đến mất an toàn về kiểu dữ liệu khi sử dụng.$request->all(): Trả về mảng chứa tất cả dữ liệu đầu vào chưa qua cast hoặc filter, dẫn đến rủi ro mass assignment, lộ trường nhạy cảm, hoặc dễ bị tấn công bằng cách thay đổi dữ liệu.
Dưới đây là các phương thức thay thế được khuyến khích:
| Phương thức | Kiểu trả về |
|---|---|
$request->string('name')->value() | string (đã trim và cast tự động) |
$request->integer('age', $defaultValue) | int |
$request->float('price', $defaultValue) | float |
$request->boolean('flag', $defaultValue) | bool |
$request->array('items') | array |
$request->enum(MyEnum::class, 'status') | MyEnum (hoặc null nếu nullable) |
$request->file('avatar') | UploadedFile |
Việc sử dụng các phương thức này đảm bảo an toàn kiểu dữ liệu và giúp code rõ ràng hơn.
3. Validation rules
- Luôn viết rules dưới dạng mảng, không dùng chuỗi ngăn cách bởi dấu pipe (
|).
php
use Illuminate\Validation\Rule;
use App\Enums\UserStatus;
use App\Rules\IsValidDomain;
public function rules(): array
{
return [
'name' => ['required', 'string', 'max:255'],
'email' => [
'required',
'email',
Rule::unique('users', 'email')->ignore($this->user?->id),
],
'password' => ['required', 'string', 'min:8', 'confirmed'],
'status' => ['required', Rule::enum(UserStatus::class)],
'website' => ['nullable', 'url', new IsValidDomain()],
'category_id' => [
'required',
'integer',
Rule::exists('categories', 'id')->where(function ($query) {
return $query->where('is_active', true);
}),
],
];
}4. Tách logic lớn với Custom Rule
Khi logic kiểm tra dữ liệu trở nên phức tạp, hãy tách logic đó khỏi phương thức rules() của Form Request bằng Custom Validation Rule. Cách này giúp tách biệt trách nhiệm, tái sử dụng logic, giữ Form Request dễ đọc và giúp viết unit test cho Rule độc lập.
Cách tạo và sử dụng Custom Rule
Tạo class bằng Artisan:
bash
php artisan make:rule IsValidPromotionCodeClass Rule triển khai Illuminate\Contracts\Validation\Rule và định nghĩa passes($attribute, $value) cùng message():
php
<?php
namespace App\Rules;
use App\Models\Promotion;
use Illuminate\Contracts\Validation\Rule;
final class IsValidPromotionCode implements Rule
{
public function passes($attribute, $value): bool
{
$promotion = Promotion::where('code', $value)
->where('expires_at', '>', now())
->where('is_active', true)
->first();
return ! is_null($promotion);
}
public function message(): string
{
return 'Mã khuyến mãi không hợp lệ hoặc đã hết hạn.';
}
}Sử dụng Rule trong Form Request:
php
<?php
namespace App\Http\Requests;
use App\Rules\IsValidPromotionCode;
use Illuminate\Foundation\Http\FormRequest;
final class ApplyPromotionRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
/** @return array<string, mixed> */
public function rules(): array
{
return [
'product_id' => ['required', 'exists:products,id'],
'quantity' => ['required', 'integer', 'min:1'],
'promo_code' => ['nullable', 'string', 'max:50', new IsValidPromotionCode()],
];
}
}5. Type hint và sử dụng validated data
- Inject
Requesthoặc customFormRequestvào controller/action. - Dùng
$request->validated()để chỉ lấy dữ liệu đã được định nghĩa và kiểm tra trongrules(), giúp giảm nguy cơ lấy nhầm trường chưa được phép và bảo vệ chống mass assignment.
php
public function store(MyFormRequest $request): JsonResponse
{
$data = $request->validated();
return $this->ok(
app(CreateUserAction::class)->execute($data),
'Tạo người dùng thành công'
);
}6. API Response
- Luôn sử dụng trait
App\Concerns\HasApiResponsetrong các Action để trả về phản hồi JSON một cách nhất quán. - Sử dụng
self::ok($data, $message)cho các phản hồi thành công. - Sử dụng
self::exception($e)bên trong khốicatchđể trả về lỗi chi tiết khi có ngoại lệ xảy ra.
Ví dụ trong IndexProductAction:
php
use App\Concerns\HasApiResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Lorisleiva\Actions\Concerns\AsAction;
use Throwable;
final class IndexProductAction
{
use HasApiResponse, AsAction;
public function asController(Request $request): JsonResponse
{
try {
$query->standardQuery($request);
$result = $this->buildResponseData($request, $query);
return self::ok($result);
} catch (Throwable $e) {
return self::exception($e);
}
}
}7. Tóm tắt Best Practices
- Import:
use Illuminate\Http\Request; - Không dùng:
->input(),->get(),->all() - Dùng:
->string(),->integer(),->array(),->enum()… - Validation: rules dạng mảng, Custom Rule
- Logic: tách ra Service/Action, không viết trong Request
- Type safety: PHPStan-friendly, dễ test và maintain