Магазин на Yii2, часть 19. Корзина покупателя, часть третья
20.07.2019
Теги: AJAX • Web-разработка • Yii2 • ИнтернетМагазин • КаталогТоваров • Корзина • Практика • Форум • Фреймворк
Наша корзина сейчас довольно примитивная — не хватает возможности удалить товар и обновить содержимое. Давайте это исправим и начнем с обновления view-шаблонов страницы корзины и содержимого модального окна. Добавим ссылки для удаления товаров из корзины и форму — чтобы можно было изменить количество.
Итак, вносим изменения в view-шаблон страницы корзины:
<?php /* * Страница корзины покупателя, файл views/basket/index.php */ use app\components\TreeWidget; use app\components\BrandsWidget; use yii\helpers\Html; use yii\helpers\Url; ?> <section> <div class="container"> <div class="row"> <div class="col-sm-3"> <h2>Каталог</h2> <div class="category-products"> <?= TreeWidget::widget(); ?> </div> <h2>Бренды</h2> <div class="brand-products"> <?= BrandsWidget::widget(); ?> </div> </div> <div class="col-sm-9"> <h1>Корзина</h1> <div id="basket-content"> <?php if (!empty($basket)): ?> <p class="text-right"> <a href="<?= Url::to(['basket/clear']); ?>" class="text-danger"> Очистить корзину </a> </p> <div class="table-responsive"> <form action="<?= Url::to(['basket/update']); ?>" method="post"> <?= Html::hiddenInput( Yii::$app->request->csrfParam, Yii::$app->request->csrfToken ); ?> <table class="table table-bordered"> <tr> <th>Наименование</th> <th>Кол-во, шт.</th> <th>Цена, руб.</th> <th>Сумма, руб.</th> <th></th> </tr> <?php foreach ($basket['products'] as $id => $item): ?> <tr> <td> <a href="<?= Url::to(['catalog/product', 'id' => $id]); ?>"> <?= Html::encode($item['name']); ?> </a> </td> <td class="text-right"> <?= Html::input( 'text', 'count['.$id.']', $item['count'], ['style' => 'width: 100%; text-align: right;'] ); ?> </td> <td class="text-right"><?= $item['price']; ?></td> <td class="text-right"><?= $item['price'] * $item['count']; ?></td> <td> <a href="<?= Url::to(['basket/remove', 'id' => $id]); ?>" class="text-danger"> <i class="fa fa-times" aria-hidden="true"></i> </a> </td> </tr> <?php endforeach; ?> <tr> <td> <button type="submit" class="btn btn-primary"> <i class="fa fa-refresh" aria-hidden="true"></i> Пересчитать </button> </td> <td colspan="2" class="text-right">Итого</td> <td class="text-right"><?= $basket['amount']; ?></td> <td></td> </tr> </table> </form> </div> <?php else: ?> <p>Ваша корзина пуста</p> <?php endif; ?> </div> <?php if (!empty($basket)): ?> <a href="<?= Url::to(['order/checkout']); ?>" class="btn btn-warning pull-right"> Оформить заказ </a> <?php endif; ?> </div> </div> </div> </section>
Изменяем view-шаблон корзины в модальном окне:
<?php /* * Корзина покупателя в модальном окне, файл views/basket/modal.php */ use yii\helpers\Html; use yii\helpers\Url; ?> <?php if (!empty($basket)): ?> <p class="text-right clear"> <a href="<?= Url::to(['basket/clear']); ?>" class="text-danger"> Очистить корзину </a> </p> <div class="table-responsive"> <form action="<?= Url::to(['basket/update']); ?>" method="post"> <?= Html::hiddenInput( Yii::$app->request->csrfParam, Yii::$app->request->csrfToken ); ?> <table class="table table-bordered"> <tr> <th>Наименование</th> <th>Кол-во, шт.</th> <th>Цена, руб.</th> <th>Сумма, руб.</th> <th></th> </tr> <?php foreach ($basket['products'] as $id => $item): ?> <tr> <td> <a href="<?= Url::to(['catalog/product', 'id' => $id]); ?>"> <?= Html::encode($item['name']); ?> </a> </td> <td class="text-right"> <?= Html::input( 'text', 'count['.$id.']', $item['count'], ['style' => 'width: 100%; text-align: right;'] ); ?> </td> <td class="text-right"><?= $item['price']; ?></td> <td class="text-right"><?= $item['price'] * $item['count']; ?></td> <td> <a href="<?= Url::to(['basket/remove', 'id' => $id]); ?>" class="text-danger"> <i class="fa fa-times" aria-hidden="true"></i> </a> </td> </tr> <?php endforeach; ?> <tr> <td> <button type="submit" class="btn btn-primary"> <i class="fa fa-refresh" aria-hidden="true"></i> Пересчитать </button> </td> <td colspan="2" class="text-right">Итого</td> <td class="text-right"><?= $basket['amount']; ?></td> <td></td> </tr> </table> </form> </div> <?php else: ?> <p>Ваша корзина пуста</p> <?php endif; ?>
Удаление товара из корзины
Добавляем новый метод в контроллер BasketController
:
<?php namespace app\controllers; use app\models\Basket; use Yii; class BasketController extends AppController { public function actionIndex() { /* ... */ } public function actionAdd() { /* ... */ } public function actionRemove($id) { $basket = new Basket(); $basket->removeFromBasket($id); return $this->redirect(['basket/index']); } }
Теперь удаление уже работает, но не слишком красиво. При удалении товара из корзины в модальном окне присходит переход на страницу корзины. Давайте это исправим, будем перехватывать событие клика по ссылке и отправлять GET-запрос с использованием AJAX:
jQuery(document).ready(function($) { /* * Добавление товара в корзину с использованием AJAX */ $('.add-to-basket').on('submit', function (event) { /* ... */ }); /* * Удаление товара из корзины в модальном окне */ $('#basket-modal .modal-body').on('click', 'table a.text-danger', function (event) { var href = $(this).attr('href'); $('#basket-modal .modal-body').load(href, function () { // если корзина пуста, скрываем кнопку «Оформить заказ» if ( ! $('#basket-modal .modal-body table').length) { $('#basket-modal .modal-footer .btn-warning').hide(); } }); event.preventDefault(); }); /* * Удаление товара из корзины на странице корзины */ $('#basket-content').on('click', 'table a.text-danger', function (event) { var href = $(this).attr('href'); $('#basket-content').load(href, function () { // если корзина пуста, скрываем кнопку «Оформить заказ» if ( ! $(this).find('table').length) { $('#basket-content').next('.btn-warning').hide(); } }); event.preventDefault(); }); });
Соответственно, изменим метод контроллера actionRemove()
, чтобы он умел обрабатывать AJAX-запросы:
<?php namespace app\controllers; use app\models\Basket; use Yii; class BasketController extends AppController { public function actionIndex() { /* ... */ } public function actionAdd() { /* ... */ } public function actionRemove($id) { $basket = new Basket(); $basket->removeFromBasket($id); /* * Тут возможны две ситуации: пришел просто GET-запрос * или GET-запрос с использованием XmlHttpRequest */ if (Yii::$app->request->isAjax) { // с использованием AJAX // layout-шаблон нам не нужен, только view-шаблон $this->layout = false; $content = $basket->getBasket(); return $this->render('modal', ['basket' => $content]); } else { // без использования AJAX return $this->redirect(['basket/index']); } } }
Удаление всех товаров из корзины
Добавляем новый метод в контроллер BasketController
:
<?php namespace app\controllers; use app\models\Basket; use Yii; class BasketController extends AppController { public function actionIndex() { /* ... */ } public function actionAdd() { /* ... */ } public function actionRemove($id) { /* ... */ } public function actionClear() { $basket = new Basket(); $basket->clearBasket(); return $this->redirect(['basket/index']); } }
По аналогии с удалением товара из корзины, будем отлавливать событие клика по ссылке «Очистить корзину» и отправлять GET-запрос с использованием AJAX:
jQuery(document).ready(function($) { /* * Добавление товара в корзину с использованием AJAX */ $('.add-to-basket').on('submit', function (event) { /* ... */ }); /* * Удаление товара из корзины в модальном окне */ $('#basket-modal .modal-body').on('click', 'table a.text-danger', function (event) { /* ... */ }); /* * Удаление товара из корзины на странице корзины */ $('#basket-content').on('click', 'table a.text-danger', function (event) { /* ... */ }); /* * Удаление всех товаров из корзины в модальном окне */ $('#basket-modal .modal-body').on('click', 'p a.text-danger', function (event) { var href = $(this).attr('href'); $('#basket-modal .modal-body').load(href); // корзина пуста, скрываем кнопку «Оформить заказ» $('#basket-modal .modal-footer .btn-warning').hide(); event.preventDefault(); }); /* * Удаление всех товаров из корзины на странице корзины */ $('#basket-content').on('click', 'p a.text-danger', function (event) { var href = $(this).attr('href'); $('#basket-content').load(href); // корзина пуста, скрываем кнопку «Оформить заказ» $('#basket-content').next('.btn-warning').hide(); event.preventDefault(); }); });
Вносим изменения в метод контроллера actionClear()
, чтобы он умел обрабатывать AJAX-запросы:
<?php namespace app\controllers; use app\models\Basket; use Yii; class BasketController extends AppController { public function actionIndex() { /* ... */ } public function actionAdd() { /* ... */ } public function actionRemove($id) { /* ... */ } public function actionClear() { $basket = new Basket(); $basket->clearBasket(); /* * Тут возможны две ситуации: пришел просто GET-запрос * или GET-запрос с использованием XmlHttpRequest */ if (Yii::$app->request->isAjax) { // с использованием AJAX // layout-шаблон нам не нужен, только view-шаблон $this->layout = false; $content = $basket->getBasket(); return $this->render('modal', ['basket' => $content]); } else { // без использования AJAX return $this->redirect(['basket/index']); } } }
Изменение количества товаров
При нажатии на кнопку «Пересчитать» на сервер будет отправлен POST-запрос, содержащий идентификаторы всех товаров в корзине и их количество:
Array ( [_csrf] => ..... [count] => Array ( [123] => 1 [456] => 2 ) )
Мы принимаем данные в методе actionUpdate()
контроллера и вызываем метод updateBasket()
модели:
<?php namespace app\controllers; use app\models\Basket; use Yii; class BasketController extends AppController { public function actionIndex() { /* ... */ } public function actionAdd() { /* ... */ } public function actionRemove($id) { /* ... */ } public function actionClear() { /*...*/ } public function actionUpdate() { $basket = new Basket(); /* * Данные должны приходить методом POST; если это не * так — просто показываем корзину */ if (!Yii::$app->request->isPost) { return $this->redirect(['basket/index']); } $data = Yii::$app->request->post(); if (!isset($data['count'])) { return $this->redirect(['basket/index']); } if (!is_array($data['count'])) { return $this->redirect(['basket/index']); } $basket->updateBasket($data); return $this->redirect(['basket/index']); } }
<?php namespace app\models; use yii\base\Model; use Yii; class Basket extends Model { /** * Метод добавляет товар в корзину */ public function addToBasket($id, $count = 1) { /* ... */ } /** * Метод удаляет товар из корзины */ public function removeFromBasket($id) { /* ... */ } /** * Метод возвращает содержимое корзины */ public function getBasket() { /* ... */ } /** * Метод удаляет все товары из корзины */ public function clearBasket() { /* ... */ } /** * Метод обновляет содержимое корзины */ public function updateBasket($data) { $this->clearBasket(); foreach ($data['count'] as $id => $count) { $this->addToBasket($id, $count); } } }
Обновление теперь работает, но не слишком красиво. Здесь та же проблема, как и при удалении товаров из корзины. После отправки формы из модального окна происходит переход на страницу корзину. Но мы уже знаем, что с этим делать — надо отправлять данные формы с использованием AJAX:
jQuery(document).ready(function($) { /* * Добавление товара в корзину с использованием AJAX */ $('.add-to-basket').on('submit', function (event) { /* ... */ }); /* * Удаление товара из корзины в модальном окне */ $('#basket-modal .modal-body').on('click', 'table a.text-danger', function (event) { /* ... */ }); /* * Удаление товара из корзины на странице корзины */ $('#basket-content').on('click', 'table a.text-danger', function (event) { /* ... */ }); /* * Удаление всех товаров из корзины в модальном окне */ $('#basket-modal .modal-body').on('click', 'p a.text-danger', function (event) { /* ... */ }); /* * Удаление всех товаров из корзины на странице корзины */ $('#basket-content').on('click', 'p a.text-danger', function (event) { /* ... */ }); /* * Обновление содержимого корзины в модальном окне */ $('#basket-modal').on('submit', 'form', function (event) { var action = $(this).attr('action'); var method = $(this).attr('method'); if (method == undefined) { method = 'get'; } var data = $(this).serialize(); $.ajax({ url: action, type: method, data: data, dataType: 'html', success: function (response) { $('#basket-modal .modal-body').html(response); // если корзина пуста, скрываем кнопку «Оформить заказ» if ( ! $('#basket-modal .modal-body table').length) { $('#basket-modal .modal-footer .btn-warning').hide(); } }, error: function () { alert('Произошла ошибка при обновлении корзины'); } }); event.preventDefault(); }) /* * Обновление содержимого корзины на странице корзины */ $('#basket-content').on('submit', 'form', function (event) { var action = $(this).attr('action'); var method = $(this).attr('method'); if (method == undefined) { method = 'get'; } var data = $(this).serialize(); $.ajax({ url: action, type: method, data: data, dataType: 'html', success: function (response) { $('#basket-content').html(response); // если корзина пуста, скрываем кнопку «Оформить заказ» if ( ! $('#basket-content table').length) { $('#basket-content').next('.btn-warning').hide(); } }, error: function () { alert('Произошла ошибка при обновлении корзины'); } }); event.preventDefault(); }); });
И вносим изменения в метод контроллера actionUpdate()
, чтобы он умел обрабатывать AJAX-запросы:
<?php namespace app\controllers; use app\models\Basket; use Yii; class BasketController extends AppController { public function actionIndex() { /* ... */ } public function actionAdd() { /* ... */ } public function actionRemove($id) { /* ... */ } public function actionClear() { /*...*/ } public function actionUpdate() { $basket = new Basket(); /* * Данные должны приходить методом POST; если это не * так — просто показываем корзину */ if (!Yii::$app->request->isPost) { return $this->redirect(['basket/index']); } $data = Yii::$app->request->post(); if (!isset($data['count'])) { return $this->redirect(['basket/index']); } if (!is_array($data['count'])) { return $this->redirect(['basket/index']); } $basket->updateBasket($data); /* * Тут возможны две ситуации: пришли просто данные POST или * пришли данные POST с использованием XmlHttpRequest */ if (Yii::$app->request->isAjax) { // с использованием AJAX // layout-шаблон нам не нужен, только view-шаблон $this->layout = false; $content = $basket->getBasket(); return $this->render('modal', ['basket' => $content]); } else { // без использования AJAX return $this->redirect(['basket/index']); } } }
- Магазин на Yii2, часть 18. Корзина покупателя, часть вторая
- Магазин на Yii2, часть 22. Оформление заказа, часть третья
- Магазин на Yii2, часть 21. Оформление заказа, часть вторая
- Магазин на Yii2, часть 20. Оформление заказа, часть первая
- Магазин на Yii2, часть 17. Корзина покупателя, часть первая
- Магазин на Laravel 7, часть 19. Панель управления, добавляем редактор для страниц сайта
- Магазин на Laravel 7, часть 10. Форма оформления, сохранение заказа в базу данных
Поиск: AJAX • Web-разработка • Yii2 • Интернет магазин • Каталог товаров • Корзина • Форум • Фреймворк