Панель администратора: управление заказами
Студент добавит интерфейс для просмотра списка заказов пользователей и изменения их статусов, решая проблему N+1 запроса.
Отображение заказов и проблема N+1
Управление заказами — сердце любого интернет-магазина. Администратор должен видеть список всех заказов, информацию о покупателе, состав заказа (товары) и иметь возможность менять статус (например, с 'Новый' на 'Отправлен'). При выводе списка заказов разработчики часто сталкиваются с классической проблемой производительности, известной как «Проблема N+1 запроса» (Direct Instruction).
Представьте, что вы выводите на страницу 50 заказов. Для каждого заказа вам нужно показать имя пользователя, который его сделал. Если вы напишете цикл и внутри вызовете `$order->user->name`, Laravel будет делать отдельный SQL-запрос к таблице `users` для каждого из 50 заказов. Итого: 1 запрос для получения заказов + 50 запросов для получения пользователей = 51 запрос. Это сильно замедляет работу приложения. Решение — использовать Eager Loading (Жадная загрузка) с помощью метода `with()`.
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Order;
use Illuminate\Http\Request;
class AdminOrderController extends Controller {
public function index() {
// Использование Eager Loading (Жадной загрузки) для решения проблемы N+1
// Загружаем заказы вместе с пользователями и позициями заказа
$orders = Order::with(['user', 'items.product'])
->orderBy('created_at', 'desc')
->paginate(20);
return view('admin.orders.index', compact('orders'));
}
public function show(Order $order) {
// Для одного заказа подгружаем связи
$order->load(['user', 'items.product']);
return view('admin.orders.show', compact('order'));
}
}
Изменение статуса заказа с использованием PHP 8
Статус заказа определяет, на каком этапе находится покупка. Обычно это набор фиксированных значений: `pending` (ожидает оплаты), `paid` (оплачен), `shipped` (отправлен), `completed` (завершен), `cancelled` (отменен). Для изменения статуса мы создадим отдельный метод `updateStatus` в нашем контроллере.
Для вывода человекопонятного названия статуса в интерфейсе, мы можем использовать новую возможность PHP 8 — выражение `match` (взято из архивных материалов). Конструкция `match` работает аналогично `switch`, но возвращает значение, имеет более строгий синтаксис (сравнивает по типу `===`) и не требует использования `break`. Это делает код более коротким, безопасным и легко читаемым.
<?php
// Пример использования выражения match (PHP 8) в модели Order или Хелпере
function getStatusName(string $statusCode): string {
return match($statusCode) {
'pending' => 'Ожидает оплаты',
'paid' => 'Оплачен',
'shipped' => 'Отправлен',
'completed' => 'Доставлен',
'cancelled' => 'Отменен',
default => throw new InvalidArgumentException('Неизвестный статус: ' . $statusCode),
};
}
// Метод в AdminOrderController для обновления статуса
public function updateStatus(Request $request, Order $order) {
$request->validate([
'status' => 'required|in:pending,paid,shipped,completed,cancelled'
]);
$order->status = $request->status;
$order->save();
return back()->with('success', 'Статус заказа обновлен на: ' . getStatusName($order->status));
}
Детализация заказа и связи Eloquent
Давайте подробнее рассмотрим конструкцию `with(['user', 'items.product'])`. Здесь мы видим силу Eloquent ORM. Мы не просто загружаем связанные позиции заказа (`items`), мы используем «точечную нотацию» (`items.product`), чтобы сказать Laravel: «Загрузи позиции заказа, а для каждой позиции сразу загрузи связанный с ней товар». Это позволяет нам в Blade-шаблоне свободно выводить название товара и его картинку: `{{ $item->product->name }}`, не опасаясь дополнительных SQL-запросов.
В шаблоне `admin.orders.show` мы выведем таблицу с составом заказа. Важно отображать цену товара на момент покупки (которая сохранена в таблице `order_items`), а не текущую цену из таблицы `products`. Цена товара может измениться, но история заказа должна оставаться неизменной (Mastery Learning).