Уроки курса
1 Основы синтаксиса PHP 8
60 мин
2 Функции и управляющие конструкции
60 мин
3 Объектно-ориентированное программирование на PHP
60 мин
4 Наследование, интерфейсы и полиморфизм
60 мин
5 Основы баз данных MySQL и работа через PDO
60 мин
6 Архитектурный паттерн MVC
60 мин
7 Введение в Laravel и настройка окружения
60 мин
8 Маршрутизация и контроллеры в Laravel
60 мин
9 Шаблонизатор Blade и создание интерфейса
60 мин
10 Миграции баз данных и Eloquent ORM
60 мин
11 Связи моделей в Eloquent — Настройка связей 'один …
60 мин
12 Аутентификация и защита маршрутов
60 мин
13 Разработка каталога товаров и категорий
60 мин
14 Реализация корзины покупок через сессии
60 мин
15 Оформление заказа и транзакции базы данных
60 мин
16 Интеграция платежной системы Stripe
60 мин
17 Интеграция платежной системы Vipps
60 мин
18 Разработка панели администратора
60 мин
19 Оптимизация и подготовка к production
60 мин
20 Деплой Laravel-приложения на сервер
60 мин
21 Основы PHP 8: Синтаксис, переменные и управляющие конструкции
60 мин
22 Функции и массивы в PHP 8
60 мин
23 Объектно-ориентированное программирование: Классы и объекты
60 мин
24 Продвинутое ООП: Наследование, интерфейсы и полиморфизм
60 мин
25 Основы баз данных: Проектирование БД и работа с …
60 мин
26 Взаимодействие PHP с MySQL через PDO
60 мин
27 Архитектура MVC и введение в фреймворк Laravel
60 мин
28 Маршрутизация и контроллеры в Laravel
60 мин
29 Шаблонизатор Blade: Создание пользовательского интерфейса
60 мин
30 Миграции и Eloquent ORM: Создание таблиц товаров
60 мин
31 Связи в Eloquent ORM: Отношения между моделями
60 мин
32 Аутентификация и авторизация пользователей
60 мин
33 Разработка каталога товаров: Вывод, фильтрация и пагинация
60 мин
34 Реализация корзины покупок с использованием сессий
60 мин
35 Оформление заказа: Работа с формами и валидация
60 мин
36 Транзакции базы данных: Сохранение заказа
60 мин
37 Интеграция платежной системы Stripe
60 мин
38 Интеграция платежной системы Vipps
60 мин
39 Создание панели администратора
60 мин
40 Подготовка к production и деплой приложения
60 мин
41 Основы PHP 8 и синтаксис
60 мин
42 Объектно-ориентированное программирование на PHP
60 мин
43 Продвинутое ООП и интерфейсы
60 мин
44 Основы баз данных и MySQL
60 мин
45 Архитектурный паттерн MVC
60 мин
46 Введение в Laravel и маршрутизация
60 мин
47 Шаблонизатор Blade и верстка интерфейса
60 мин
48 Миграции и структура базы данных
60 мин
49 Eloquent ORM и модели
60 мин
50 Отношения в Eloquent ORM
60 мин
51 Авторизация и защита маршрутов
60 мин
52 Создание каталога товаров
60 мин
53 Реализация корзины покупок
60 мин
54 Оформление заказа и транзакции БД
60 мин
55 Интеграция платежной системы Stripe
60 мин
56 Интеграция платежной системы Vipps
60 мин
57 Панель администратора: управление товарами
60 мин
58 Панель администратора: управление заказами
60 мин
59 Подготовка к production и деплой
60 мин
60 Запуск и тестирование готового интернет-магазина
60 мин

Оформление заказа и транзакции базы данных

Сохранение данных заказа и связанных товаров в БД с обеспечением атомарности операций через DB::transaction. Финализация основного функционала магазина.

Прогресс урока: 0%

Мы подошли к кульминации разработки интернет-магазина — процессу оформления заказа (Checkout). В этот момент временные данные из сессионной корзины должны превратиться в постоянные записи в базе данных. Процесс состоит из нескольких шагов: создание записи в таблице orders, создание нескольких записей в таблице order_items (по одной на каждый товар в корзине) и очистка корзины. Здесь кроется огромная опасность. Представьте ситуацию: заказ создан, первый товар добавлен в order_items, а при добавлении второго товара сервер падает (например, сбой соединения с БД). В результате в базе остается 'сломанный' заказ без части товаров. Чтобы избежать подобных катастроф, используются Транзакции базы данных. Транзакция гарантирует свойство Атомарности (Atomicity) — 'всё или ничего'. Либо все SQL-запросы внутри транзакции выполняются успешно, и данные фиксируются (Commit), либо при малейшей ошибке происходит откат (Rollback) к исходному состоянию, словно никаких запросов и не было.

php
namespace App\Http\Controllers;

use Illuminate\Support\Facades\DB;
use App\Models\Order;
use App\Models\OrderItem;
use Illuminate\Http\Request;

class CheckoutController extends Controller
{
    public function process(Request $request)
    {
        $cart = session('cart', []);
        if (empty($cart)) {
            return redirect()->route('cart.index')->with('error', 'Корзина пуста');
        }

        // Запуск транзакции базы данных
        DB::transaction(function () use ($cart) {
            // 1. Считаем общую сумму
            $total = array_sum(array_map(fn($item) => $item['price'] * $item['quantity'], $cart));
            
            // 2. Создаем заказ
            $order = Order::create([
                'user_id' => auth()->id(), // Пользователь должен быть авторизован (middleware 'auth')
                'total_price' => $total,
                'status' => 'pending'
            ]);

            // 3. Создаем позиции заказа
            foreach ($cart as $id => $item) {
                OrderItem::create([
                    'order_id' => $order->id,
                    'product_id' => $id,
                    'quantity' => $item['quantity'],
                    'price' => $item['price'] // Фиксируем цену на момент покупки
                ]);
            }
        });

        // Если код дошел сюда, транзакция успешна. Очищаем корзину.
        session()->forget('cart');

        return redirect()->route('orders.success')->with('success', 'Заказ успешно оформлен!');
    }
}

Разберем код контроллера, опираясь на материалы наших упражнений. Метод DB::transaction принимает анонимную функцию (Closure). Важно: поскольку анонимная функция в PHP имеет свою область видимости, мы должны передать внутрь нее переменную $cart с помощью конструкции use ($cart). Внутри замыкания мы сначала вычисляем общую стоимость заказа (total). Затем создаем запись в таблице orders. Мы используем auth()->id(), потому что маршрут защищен middleware 'auth' (об этом мы говорили в 12 уроке). Получив объект $order, мы запускаем цикл foreach по корзине. Для каждого товара создается запись OrderItem, куда мы передаем $order->id в качестве внешнего ключа. Если на любом этапе (например, при сохранении OrderItem) возникнет исключение (Exception), Laravel автоматически перехватит его и выполнит SQL-команду ROLLBACK. База данных останется в чистоте. Если цикл завершается без ошибок, Laravel выполняет COMMIT.

Действие при Checkout Таблица Результат
Создание заказа orders Новая строка: user_id, total_price, статус 'pending'
Копирование товаров order_items Новые строки: order_id, product_id, цена, количество
Очистка сессия Удаление ключа 'cart'

Особое внимание обратите на сохранение цены в таблице order_items. Мы берем цену из сессии (которая была актуальна на момент добавления в корзину) и жестко записываем ее в БД. Никогда не делайте связь в заказе, которая динамически тянет цену из таблицы products. Почему? Потому что через год администратор магазина повысит цену на товар. Если вы будете динамически брать текущую цену, у пользователя в истории заказов за прошлый год цена тоже изменится, и общая сумма заказа 'поплывет'. Заказ — это исторический документ, он должен фиксировать состояние (snapshot) на момент сделки. Транзакции и историческая фиксация данных — это стандарты индустрии, которые отличают любительский код от профессионального production-решения.

Почему при создании позиций заказа (OrderItem) необходимо сохранять цену товара в эту таблицу, а не просто ссылаться на таблицу Products?