Магазин на Laravel 7, часть 5. Создаем корзину покупателя, добавление товара в корзину
03.10.2020
Теги: Laravel • MySQL • PHP • Web-разработка • ИнтернетМагазин • КаталогТоваров • Корзина • Практика • Фреймворк • ШаблонСайта
Добавим еще один контроллер BasketController
, который будет отвечать за корзину покупателя. Корзины будем хранить в таблице baskets
базы данных. И нам еще потребуется таблица для связи многие-ко-многим — для товаров и корзин. В одной корзине может быть несколько товаров, один товар может быть в нескольких корзинах.
Контроллер BasketController
Создаем контроллер BasketController
:
> php artisan make:controller BasketController
namespace App\Http\Controllers; use Illuminate\Http\Request; class BasketController extends Controller { public function index() { return view('basket.index'); } public function checkout() { return view('basket.checkout'); } }
Сразу создадим два шаблона:
@extends('layout.site') @section('content') <h1>Ваша корзина</h1> <p>Здесь будет содержимое корзины</p> @endsection
@extends('layout.site') @section('content') <h1>Оформление заказа</h1> <p>Здесь будет форма оформления</p> @endsection
И добавим два маршртута:
Route::get('/basket/index', 'BasketController@index')->name('basket.index'); Route::get('/basket/checkout', 'BasketController@checkout')->name('basket.checkout');
Модель Basket
Создаем модель Basket
вместе с миграцией:
> php artisan make:model -m Basket
use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class CreateBasketsTable extends Migration { public function up() { Schema::create('baskets', function (Blueprint $table) { $table->id(); $table->timestamps(); }); } public function down() { Schema::dropIfExists('baskets'); } }
Создаем таблицу для связи baskets
и products
:
> php artisan make:migration create_basket_product_table
use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class CreateBasketProductTable extends Migration { public function up() { Schema::create('basket_product', function (Blueprint $table) { $table->id(); $table->unsignedBigInteger('basket_id'); $table->unsignedBigInteger('product_id'); $table->unsignedTinyInteger('quantity'); $table->foreign('basket_id') ->references('id') ->on('baskets') ->cascadeOnDelete(); $table->foreign('product_id') ->references('id') ->on('products') ->cascadeOnDelete(); }); } public function down() { Schema::dropIfExists('basket_product'); } }
Задаем связь между таблицами:
namespace App; use Illuminate\Database\Eloquent\Model; class Basket extends Model { /** * Связь «многие ко многим» таблицы `baskets` с таблицей `products` */ public function products() { return $this->belongsToMany(Product::class)->withPivot('quantity'); } }
namespace App; use Illuminate\Database\Eloquent\Model; class Product extends Model { /** * Связь «многие ко многим» таблицы `products` с таблицей `baskets` * * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany */ public function baskets() { return $this->belongsToMany(Basket::class)->withPivot('quantity'); } }
Применяем наши миграции:
> php artisan migrate
Добавление в корзину
Во-первых, нам нужен новый маршрут в файле web.php
:
Route::post('/basket/add/{id}', 'BasketController@add') ->where('id', '[0-9]+') ->name('basket.add');
Во-вторых, добавляем форму на страницу товара:
<div class="col-md-6"> <p>Цена: {{ number_format($product->price, 2, '.', '') }}</p> <!-- Форма для добавления товара в корзину --> <form action="{{ route('basket.add', ['id' => $product->id]) }}" method="post" class="form-inline"> @csrf <label for="input-quantity">Количество</label> <input type="text" name="quantity" id="input-quantity" value="1" class="form-control mx-2 w-25"> <button type="submit" class="btn btn-success">Добавить в корзину</button> </form> </div>
После этого добавляем новый метод add()
в контроллер:
namespace App\Http\Controllers; use Illuminate\Http\Request; class BasketController extends Controller { public function index() { return view('basket.index'); } public function checkout() { return view('basket.checkout'); } /** * Добавляет товар с идентификатором $id в корзину */ public function add(Request $request, $id) { $basket_id = $request->cookie('basket_id'); $quantity = $request->input('quantity') ?? 1; if (empty($basket_id)) { // если корзина еще не существует — создаем объект $basket = Basket::create(); // получаем идентификатор, чтобы записать в cookie $basket_id = $basket->id; } else { // корзина уже существует, получаем объект корзины $basket = Basket::findOrFail($basket_id); // обновляем поле `updated_at` таблицы `baskets` $basket->touch(); } if ($basket->products->contains($id)) { // если такой товар есть в корзине — изменяем кол-во $pivotRow = $basket->products()->where('product_id', $id)->first()->pivot; $quantity = $pivotRow->quantity + $quantity; $pivotRow->update(['quantity' => $quantity]); } else { // если такого товара нет в корзине — добавляем его $basket->products()->attach($id, ['quantity' => $quantity]); } // выполняем редирект обратно на страницу, где была нажата кнопка «В корзину» return back()->withCookie(cookie('basket_id', $basket_id, 525600)); } }
Обновить значение pivot-поля quantity
в промежуточной таблице basket_product
можно с помощью метода
$basket->products()->updateExistingPivot( $product_id, ['quantity', $quantity] );
Для удобства временно отключим шифрование cookie, чтобы иметь возможность видеть значение, которые мы сохраняем в basket_id
:
namespace App\Http\Middleware; use Illuminate\Cookie\Middleware\EncryptCookies as Middleware; class EncryptCookies extends Middleware { /** * The names of the cookies that should not be encrypted. * * @var array */ protected $except = [ 'basket_id' ]; }
Но не забываем вернуть все обратно, после того, как закончим работать над корзиной покупателя.
Содержимое корзины
Надо доработать метод index()
контроллера и внести изменения в шаблон, который отвечает за показ корзины:
namespace App\Http\Controllers; use App\Basket; use Illuminate\Http\Request; class BasketController extends Controller { /** * Показывает корзину покупателя */ public function index(Request $request) { $basket_id = $request->cookie('basket_id'); if (!empty($basket_id)) { $products = Basket::findOrFail($basket_id)->products; return view('basket.index', compact('products')); } else { abort(404); } } /* ... */ }
@extends('layout.site') @section('content') <h1>Ваша корзина</h1> @if (count($products)) @php $basketCost = 0; @endphp <table class="table table-bordered"> <tr> <th>№</th> <th>Наименование</th> <th>Цена</th> <th>Кол-во</th> <th>Стоимость</th> </tr> @foreach($products as $product) @php $itemPrice = $product->price; $itemQuantity = $product->pivot->quantity; $itemCost = $itemPrice * $itemQuantity; $basketCost = $basketCost + $itemCost; @endphp <tr> <td>{{ $loop->iteration }}</td> <td> <a href="{{ route('catalog.product', [$product->slug]) }}">{{ $product->name }}</a> </td> <td>{{ number_format($itemPrice, 2, '.', '') }}</td> <td> <i class="fas fa-minus-square"></i> <span class="mx-1">{{ $itemQuantity }}</span> <i class="fas fa-plus-square"></i> </td> <td>{{ number_format($itemCost, 2, '.', '') }}</td> </tr> @endforeach <tr> <th colspan="4" class="text-right">Итого</th> <th>{{ number_format($basketCost, 2, '.', '') }}</th> </tr> </table> @else <p>Ваша корзина пуста</p> @endif @endsection
- Магазин на Laravel 7, часть 10. Форма оформления, сохранение заказа в базу данных
- Магазин на Laravel 7, часть 6. Изменение количества товара, удаление товара из корзины
- Магазин на Laravel 7, часть 24. Фильтр товаров категории по цене, новинкам и лидерам продаж
- Магазин на Laravel 7, часть 23. Главная страница сайта, новинки, лидеры продаж и распродажа
- Магазин на Laravel 7, часть 22. Рефакторинг кода, работа над каталогом товаров и корзиной
- Магазин на Laravel 7, часть 21. Добавляем профили и используем их при оформлении заказа
- Магазин на Laravel 7, часть 20. Показ отдельной страницы и верхнее меню всех страниц
Поиск: Laravel • MySQL • PHP • Web-разработка • Интернет магазин • Каталог товаров • Корзина • Практика • Фреймворк • Шаблон сайта