Appearance
Handling Requests in Laravel
1. Introduction
In Laravel, HTTP requests are typically handled with the Illuminate\Http\Request class:
php
use Illuminate\Http\Request;This base class provides the features you need for most cases. When validation or authorization becomes more complex, prefer dedicated Form Request classes that extend Illuminate\Foundation\Http\FormRequest.
2. Avoid ->input(), ->get(), and ->all()
$request->input('field')/$request->get('field'): Returnmixed, which weakens type safety.$request->all(): Returns an array of all raw input without casting or filtering, which increases mass assignment risk, can expose sensitive fields, and makes data-tampering attacks easier.
Prefer these typed alternatives:
| Method | Return type |
|---|---|
$request->string('name')->value() | string (trimmed and cast) |
$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 (or null if nullable) |
$request->file('avatar') | UploadedFile |
These methods keep types safe and make the code clearer.
3. Validation rules
- Always write rules as an array, not a pipe-separated string (
|).
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. Extract complex logic into a Custom Rule
When validation logic becomes complex, move it out of the Form Request rules() method into a Custom Validation Rule. This separates concerns, improves reuse, keeps Form Requests readable, and makes the rule easy to unit test on its own.
Creating and using a Custom Rule
Create the class with Artisan:
bash
php artisan make:rule IsValidPromotionCodeThe Rule class implements Illuminate\Contracts\Validation\Rule and defines passes($attribute, $value) and 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 'The promotion code is invalid or has expired.';
}
}Use the rule in a 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 hints and validated data
- Inject
Requestor a customFormRequestinto the controller/action. - Use
$request->validated()to read only fields defined and checked inrules(), which reduces accidental access to disallowed fields and helps prevent mass assignment.
php
public function store(MyFormRequest $request): JsonResponse
{
$data = $request->validated();
return $this->ok(
app(CreateUserAction::class)->execute($data),
'User created successfully'
);
}6. API response
- Always use the
App\Concerns\HasApiResponsetrait in Actions for consistent JSON responses. - Use
self::ok($data, $message)for successful responses. - Use
self::exception($e)inside acatchblock to return detailed errors when an exception occurs.
Example in 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. Best practices summary
- Import:
use Illuminate\Http\Request; - Do not use:
->input(),->get(),->all() - Use:
->string(),->integer(),->array(),->enum()… - Validation: array-form rules, Custom Rule
- Logic: extract into Service/Action; do not put it in the Request
- Type safety: PHPStan-friendly, easier to test and maintain