HTTP Request
Laravel оборачивает входящий HTTP-запрос в объект Illuminate\Http\Request, который расширяет Symfony HttpFoundation\Request.
Получение экземпляра Request
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
final class UserController extends Controller
{
// Method 1: Type-hint in controller method (recommended)
public function store(Request $request): JsonResponse
{
$name = $request->input('name');
return response()->json(['name' => $name]);
}
// Method 2: Type-hint in route closure
// Route::post('/users', function (Request $request) { ... });
// Method 3: Global helper
// $request = request();
// $name = request('name');
// $name = request()->input('name');
}
Информация о запросе
<?php
declare(strict_types=1);
use Illuminate\Http\Request;
function inspectRequest(Request $request): array
{
return [
// URL information
'path' => $request->path(), // "users/1" (without domain)
'url' => $request->url(), // "http://example.com/users/1"
'fullUrl' => $request->fullUrl(), // "http://example.com/users/1?page=2"
'fullUrlWithQuery' => $request->fullUrlWithQuery(['sort' => 'name']),
// HTTP method
'method' => $request->method(), // "GET", "POST", etc.
'isGet' => $request->isMethod('get'), // true/false
'isPost' => $request->isMethod('post'), // true/false
// Request checks
'is_ajax' => $request->ajax(), // X-Requested-With: XMLHttpRequest
'is_json' => $request->isJson(), // Content-Type contains json
'expects_json' => $request->expectsJson(), // Accept header wants JSON
'is_secure' => $request->secure(), // HTTPS
'is_prefetch' => $request->prefetch(), // Prefetch request
// Client info
'ip' => $request->ip(), // Client IP
'ips' => $request->ips(), // All IPs (with proxies)
'userAgent' => $request->userAgent(), // Browser user agent
// Headers
'accept' => $request->header('Accept'),
'bearer' => $request->bearerToken(), // Authorization: Bearer <token>
'content_type' => $request->header('Content-Type'),
// Route info
'route_name' => $request->route()?->getName(),
'route_params' => $request->route()?->parameters(),
];
}
Получение входных данных
<?php
declare(strict_types=1);
use Illuminate\Http\Request;
function getInputData(Request $request): void
{
// === BASIC INPUT ===
// Get single value (from any source: query, post, json)
$name = $request->input('name');
$name = $request->input('name', 'default'); // With default
// Dot notation for nested data
$city = $request->input('address.city');
// Array input
$names = $request->input('users.*.name'); // From users array
// Get ALL input
$all = $request->all(); // All input including query string
// Get input from QUERY STRING only
$page = $request->query('page');
$page = $request->query('page', 1); // With default
$allQuery = $request->query(); // All query params
// === DYNAMIC PROPERTIES ===
// Access input as property (checks input, then route params)
$name = $request->name;
// === SELECTIVE RETRIEVAL ===
// Get only specified keys
$data = $request->only(['name', 'email']);
// Returns: ['name' => 'John', 'email' => '[email protected]']
// Get all EXCEPT specified keys
$data = $request->except(['password', '_token']);
// Returns all input except password and _token
// === PRESENCE CHECKS ===
// Check if input exists (even if empty string)
$exists = $request->has('name'); // true if key exists
$exists = $request->has(['name', 'email']); // true if ALL exist
// Check if ANY of the keys exist
$exists = $request->hasAny(['name', 'nickname', 'username']);
// Check if exists AND not empty
$filled = $request->filled('name'); // true if exists and not empty
// Check if missing or empty
$missing = $request->isNotFilled('name');
// Execute callback when present
$request->whenHas('coupon', function (string $coupon) {
// Apply coupon
});
// Execute callback when filled (not empty)
$request->whenFilled('coupon', function (string $coupon) {
// Apply coupon
}, function () {
// Coupon is empty or missing
});
// Check if key is missing entirely
$missing = $request->missing('key');
// === TYPE-SAFE RETRIEVAL ===
$request->string('name'); // Returns Stringable
$request->integer('page'); // Returns int
$request->float('amount'); // Returns float
$request->boolean('active'); // Returns bool (handles "1", "true", "on", "yes")
$request->date('birthday'); // Returns Carbon instance
$request->date('birthday', 'd.m.Y'); // With format
$request->enum('status', OrderStatus::class); // Returns enum or null
// Collection of input
$request->collect('items'); // Returns Collection
$request->collect(); // All input as Collection
}
Ловушка экзамена:
$request->has('key')возвращаетtrueдаже для пустой строки -- он проверяет только наличие ключа.$request->filled('key')возвращаетtrueтолько если ключ присутствует И значение не пустое.$request->boolean()корректно обрабатывает "1", "true", "on", "yes" какtrue.
Работа с JSON-данными
<?php
declare(strict_types=1);
use Illuminate\Http\Request;
function handleJsonInput(Request $request): void
{
// When Content-Type is application/json,
// input() automatically decodes JSON body
// POST body: {"user": {"name": "John", "roles": ["admin", "editor"]}}
$name = $request->input('user.name'); // "John"
$roles = $request->input('user.roles'); // ["admin", "editor"]
$firstRole = $request->input('user.roles.0'); // "admin"
// Get raw JSON body
$json = $request->json(); // ParameterBag instance
$name = $request->json('user.name'); // Dot notation
// Get raw content
$rawBody = $request->getContent(); // Raw request body string
}
Загрузка файлов
<?php
declare(strict_types=1);
use Illuminate\Http\Request;
use Illuminate\Http\UploadedFile;
function handleFileUpload(Request $request): void
{
// Check if file was uploaded
$hasFile = $request->hasFile('photo');
// Get uploaded file
/** @var UploadedFile|null $photo */
$photo = $request->file('photo');
if ($photo && $photo->isValid()) {
// File properties
$originalName = $photo->getClientOriginalName(); // "photo.jpg"
$extension = $photo->getClientOriginalExtension(); // "jpg"
$mimeType = $photo->getMimeType(); // "image/jpeg"
$size = $photo->getSize(); // bytes
$hashName = $photo->hashName(); // Random name: "aB3f...jpg"
// Store file — returns relative path
$path = $photo->store('photos');
// Saves to: storage/app/photos/randomname.jpg
// Returns: "photos/randomname.jpg"
// Store with custom name
$path = $photo->storeAs('photos', 'profile.jpg');
// Saves to: storage/app/photos/profile.jpg
// Store to specific disk
$path = $photo->store('photos', 's3');
// Store publicly
$path = $photo->storePublicly('photos', 's3');
// Store in public disk
$path = $photo->store('avatars', 'public');
// Accessible at: /storage/avatars/randomname.jpg
}
// Multiple files
if ($request->hasFile('documents')) {
/** @var UploadedFile[] $files */
$files = $request->file('documents');
foreach ($files as $file) {
$file->store('documents');
}
}
}
Form Requests (валидация в отдельном классе)
Form Request -- это отдельный класс для инкапсуляции логики валидации и авторизации.
# Create a Form Request
php artisan make:request StoreUserRequest
<?php
declare(strict_types=1);
namespace App\Http\Requests;
use App\Models\User;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
use Illuminate\Validation\Rules\Password;
final class StoreUserRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
// Return true to allow all users
// Return false to reject with 403
// Use Gate/Policy for complex authorization
return $this->user()?->can('create', User::class) ?? false;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array|string>
*/
public function rules(): array
{
return [
'name' => ['required', 'string', 'max:255'],
'email' => [
'required',
'email:rfc,dns',
Rule::unique('users', 'email'),
],
'password' => [
'required',
'confirmed',
Password::min(8)
->letters()
->mixedCase()
->numbers()
->symbols()
->uncompromised(),
],
'role' => ['required', Rule::in(['user', 'editor', 'admin'])],
'avatar' => ['nullable', 'image', 'max:2048', 'dimensions:min_width=100'],
'tags' => ['nullable', 'array', 'max:10'],
'tags.*' => ['string', 'max:50'],
];
}
/**
* Custom attribute names for error messages.
*/
public function attributes(): array
{
return [
'name' => 'имя пользователя',
'email' => 'электронная почта',
'password' => 'пароль',
];
}
/**
* Custom error messages.
*/
public function messages(): array
{
return [
'name.required' => 'Имя обязательно для заполнения.',
'email.unique' => 'Пользователь с таким email уже существует.',
'password.confirmed' => 'Пароли не совпадают.',
];
}
/**
* Prepare the data for validation.
* Called BEFORE validation.
*/
protected function prepareForValidation(): void
{
$this->merge([
'email' => strtolower($this->email),
'name' => trim($this->name),
]);
}
/**
* Get the validated data with additional processing.
* Called AFTER successful validation.
*/
public function validated($key = null, $default = null): array
{
$data = parent::validated($key, $default);
// Post-validation processing
if (isset($data['password'])) {
$data['password'] = bcrypt($data['password']);
}
return $data;
}
/**
* Handle a failed authorization attempt.
*/
protected function failedAuthorization(): void
{
throw new \Illuminate\Auth\Access\AuthorizationException(
'You are not authorized to create users.'
);
}
}
Использование Form Request в контроллере
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use App\Http\Requests\StoreUserRequest;
use App\Http\Requests\UpdateUserRequest;
use App\Models\User;
final class UserController extends Controller
{
// Form Request is automatically validated BEFORE method is called
// If validation fails:
// - Web: redirect back with errors
// - API: JSON 422 response with errors
public function store(StoreUserRequest $request): JsonResponse
{
// $request->validated() returns ONLY validated data
$user = User::create($request->validated());
return response()->json($user, 201);
}
public function update(UpdateUserRequest $request, User $user): JsonResponse
{
// safe() returns ValidatedInput object
$safe = $request->safe();
// Use only specific validated fields
$user->update($safe->only(['name', 'email']));
// Merge additional data with validated
$data = $safe->merge(['updated_by' => auth()->id()]);
return response()->json($user);
}
}
Для экзамена: Form Request выполняет валидацию АВТОМАТИЧЕСКИ до вызова метода контроллера. Если валидация не проходит: для web-запросов -- redirect back с ошибками, для API-запросов (Accept: application/json) -- JSON-ответ с кодом 422. Метод
authorize()проверяется ДО валидации.
Update Request с уникальным полем
<?php
declare(strict_types=1);
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
final class UpdateUserRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
public function rules(): array
{
// Get the user being updated from route parameter
$userId = $this->route('user')->id;
return [
'name' => ['sometimes', 'string', 'max:255'],
'email' => [
'sometimes',
'email:rfc,dns',
// Ignore current user when checking uniqueness
Rule::unique('users', 'email')->ignore($userId),
],
];
}
}
HTTP Responses
Типы ответов
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use App\Models\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Response;
use Illuminate\View\View;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
final class ResponseExamplesController extends Controller
{
// 1. String response
public function stringResponse(): string
{
return 'Hello, World!';
// Content-Type: text/html, Status: 200
}
// 2. Array response (auto-converted to JSON)
public function arrayResponse(): array
{
return ['name' => 'John', 'age' => 30];
// Content-Type: application/json, Status: 200
}
// 3. JSON response
public function jsonResponse(): JsonResponse
{
return response()->json([
'success' => true,
'data' => ['name' => 'John'],
], 200, [
'X-Custom-Header' => 'value',
]);
}
// 4. View response
public function viewResponse(): View
{
return view('users.index', [
'users' => User::paginate(15),
]);
}
// 5. Response with custom headers and cookies
public function customResponse(): Response
{
return response('Content here', 200)
->header('Content-Type', 'text/plain')
->header('X-Custom', 'value')
->withHeaders([
'X-Header-One' => 'value1',
'X-Header-Two' => 'value2',
])
->cookie('name', 'value', 60); // 60 minutes
}
// 6. No content response (204)
public function noContentResponse(): Response
{
return response()->noContent();
// Status: 204, No body
}
}
Redirect ответы
<?php
declare(strict_types=1);
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Route;
function redirectExamples(): RedirectResponse
{
// Redirect to URL
return redirect('/home');
// Redirect to named route
return redirect()->route('users.show', ['user' => 1]);
// Redirect to named route (shortcut)
return to_route('users.show', ['user' => 1]);
// Redirect back (to previous page)
return redirect()->back();
return back(); // Shortcut
// Redirect back with input (for form re-population)
return back()->withInput();
// Redirect back with only specific input
return back()->withInput($request->only('email'));
// Redirect with flash data
return redirect()
->route('users.index')
->with('success', 'User created!');
// Redirect with multiple flash messages
return redirect()
->route('users.index')
->with([
'success' => 'User created!',
'user_id' => 42,
]);
// Redirect with errors
return back()->withErrors([
'email' => 'This email is already taken.',
]);
// Redirect to controller action
return redirect()->action([UserController::class, 'index']);
// Redirect to external URL
return redirect()->away('https://google.com');
// Redirect with status code
return redirect('/new-location', 301);
// Intended redirect (after login)
return redirect()->intended('/dashboard');
// Redirects to the URL the user was trying to access before login
}
Download и File ответы
<?php
declare(strict_types=1);
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\StreamedResponse;
function fileResponses()
{
// Download a file
return response()->download(
storage_path('app/reports/report.pdf'),
'monthly-report.pdf', // Custom filename
['Content-Type' => 'application/pdf']
);
// Download and delete after sending
return response()->download($path)->deleteFileAfterSend();
// Display file inline (in browser, not download)
return response()->file(
storage_path('app/documents/contract.pdf'),
['Content-Type' => 'application/pdf']
);
// Stream download (generate on-the-fly)
return response()->streamDownload(function () {
echo "Name,Email,Role\n";
User::chunk(100, function ($users) {
foreach ($users as $user) {
echo "{$user->name},{$user->email},{$user->role}\n";
}
});
}, 'users.csv', [
'Content-Type' => 'text/csv',
]);
}
Streamed Response
<?php
declare(strict_types=1);
use Symfony\Component\HttpFoundation\StreamedResponse;
function streamedResponse(): StreamedResponse
{
// Stream response (useful for large data)
return response()->stream(function () {
$handle = fopen(storage_path('app/large-file.csv'), 'r');
while (! feof($handle)) {
echo fread($handle, 8192); // Read in 8KB chunks
flush(); // Send to client immediately
}
fclose($handle);
}, 200, [
'Content-Type' => 'text/csv',
'X-Accel-Buffering' => 'no', // Disable Nginx buffering
]);
}
Responsable Interface
<?php
declare(strict_types=1);
namespace App\Http\Responses;
use Illuminate\Contracts\Support\Responsable;
use Illuminate\Http\JsonResponse;
final readonly class ApiSuccessResponse implements Responsable
{
public function __construct(
private mixed $data,
private string $message = 'Success',
private int $status = 200,
private array $meta = [],
) {}
public function toResponse($request): JsonResponse
{
$response = [
'success' => true,
'message' => $this->message,
'data' => $this->data,
];
if ($this->meta !== []) {
$response['meta'] = $this->meta;
}
return response()->json($response, $this->status);
}
}
// Usage in controller:
// return new ApiSuccessResponse(
// data: UserResource::collection($users),
// message: 'Users fetched successfully',
// meta: ['total' => $users->total()],
// );
Для Senior: Интерфейс
Responsableпозволяет возвращать любой объект из контроллера, если он реализует методtoResponse(). Laravel автоматически вызывает его для формирования ответа. Это мощный паттерн для стандартизации API-ответов.
Inline валидация
<?php
declare(strict_types=1);
use Illuminate\Http\Request;
function inlineValidation(Request $request): void
{
// Method 1: validate() on Request
$validated = $request->validate([
'title' => 'required|string|max:255',
'body' => 'required|string',
'category_id' => 'required|exists:categories,id',
]);
// Returns only validated data
// Throws ValidationException on failure
// Method 2: validateWithBag() for named error bags
$validated = $request->validateWithBag('post', [
'title' => 'required|string|max:255',
]);
// Method 3: Validator facade (for custom error handling)
$validator = \Illuminate\Support\Facades\Validator::make($request->all(), [
'email' => 'required|email',
]);
if ($validator->fails()) {
// Custom error handling
return redirect()->back()
->withErrors($validator)
->withInput();
}
$validated = $validator->validated();
// Method 4: Validator with after hook
$validator = \Illuminate\Support\Facades\Validator::make($request->all(), [
'email' => 'required|email',
]);
$validator->after(function ($validator) {
if ($this->somethingElseIsInvalid()) {
$validator->errors()->add('field', 'Something is wrong with this field!');
}
});
}