Как обновить количество товара в корзине

AmdY

Пью пиво
Команда форума
По ссылке шаг за шагом объясняют что делать и для чего. И по размеру статьи видно, почему на такие вещи не стоит давать ответы на форуме. Слишком много нюансов.
 

Valick

Новичок
Посмотрел код по ссылке внимательее, единственное могу сказать, что там "много текста"... и ни одной функции для корзины, за 35 баксов такое продавать это грех
PHP:
<?php
/**
 * Model
 */

// регистратор ошибок
function addError($text): void
{
    $_SESSION['error'][] = $text;
}

function getError(): array
{
    return $_SESSION['error'] ?? [];
}

function clearError(): void
{
    unset($_SESSION['error']);
}

/**
 * Функции корзины
 */

// инициализация корзины
function basketInit($product): array
{
    if (!empty($_COOKIE['basket'])) {
        $basket = json_decode(base64_decode($_COOKIE['basket']), true);
        if (!empty($basket['item'])) {
            $moveUpItem = [];
            // проверка доступности товара
            foreach ($basket['item'] as $itemCode => &$item) {
                $item['available'] = $item['quantity'];
                $inStockProduct = getStockProduct($product, $itemCode);
                if (empty($inStockProduct)) {
                    $item['available'] = 0;
                    $moveUpItem[$itemCode] = $item;
                    unset($basket['item'][$itemCode]);
                } else if ($item['quantity'] > $inStockProduct) {
                    $item['available'] = $inStockProduct;
                }
            }
            unset($item);
            $basket['item'] = $moveUpItem + $basket['item'];

            return $basket;
        }
    }

    return [
        'basket_id' => uniqid('', true),
        'date_create' => date('Y-m-d H:i:s'),
        'currency' => 'руб',
        'item' => [],
    ];
}

// добавление товара в корзину
function addItem($basket, $itemCode, $quantity = 1): array
{
    if (isset($basket['item'][$itemCode])) {
        $basket['item'][$itemCode]['quantity'] += $quantity;
        $basket['item'][$itemCode]['update_timestamp'] = time();
    } else {
        $basket['item'][$itemCode]['quantity'] = $quantity;
        $basket['item'][$itemCode]['add_timestamp'] = $basket['item'][$itemCode]['update_timestamp'] = time();
    }

    return $basket;
}

// удаление товара из корзины
function removeItem($basket, $itemCode): array
{
    if (isset($basket['item'][$itemCode])) {
        unset($basket['item'][$itemCode]);
    }

    return $basket;
}

function getStockProduct($product, $itemCode): int
{
    return $product[$itemCode]['stock'];
}

// количество товаров в корзине
function countItem($basket): int
{
    return count($basket['item']) ?? 0;
}

// общее количество товарных единиц в корзине
function countAllQuantityItem($basket): int
{
    return array_sum(array_column($basket['item'], 'available'));
}

// общая стоимость корзины
function getBasketPrice($basket, $product): float
{
    $allProductPrice = [];
    $productBasket = array_intersect_key($product, $basket['item']);
    foreach ($productBasket as $productCode => $productItem) {
        $allProductPrice[] = $productItem['price'] * $basket['item'][$productCode]['available'];
    }
    return array_sum($allProductPrice);
}

// идентификатор корзины
function getBasketId($basket): string
{
    return $basket['basket_id'];
}

// валюта корзины
function getBasketCurrency($basket): string
{
    return $basket['currency'];
}

// сохранение корзины
function saveBasket($basket): void
{
    $basket = base64_encode(json_encode($basket));
    setcookie('basket', $basket, time() + 360000, '/');
}

// удаление корзины
function deleteBasket($basket): void
{
    saveBasket($basket);
}

function getProduct(): array
{
    return [
        1000 => [
            'code' => 1000,
            'stock' => 10,
            'name' => 'Калькулятор',
            'price' => 699.99,
            'currency' => 'руб',
        ],
        2000 => [
            'code' => 2000,
            'stock' => 5,
            'name' => 'Ручка шариковая',
            'price' => 12.05,
            'currency' => 'руб',
        ],
        3000 => [
            'code' => 3000,
            'stock' => 20,
            'name' => 'Набор карандашей',
            'price' => 56.99,
            'currency' => 'руб',
        ],
        4000 => [
            'code' => 4000,
            'stock' => 132,
            'name' => 'Тетрадь общая',
            'price' => 32.50,
            'currency' => 'руб',
        ],
    ];
}

function addItemAction($basket, $product): void
{
    $itemCode = (int)$_POST['add_item'];
    if (!isset($product[$itemCode])) {
        addError('Попытка добавления в корзину несуществующего товара');
        return;
    }
    $quantity = (int)($_POST['quantity'][$itemCode] ?? 1);
    if ($quantity < 1) {
        addError('Количество товара добавляемого в корзину должно быть положительным числом');
    } elseif ($product[$itemCode]['stock'] === 0) {
        addError('[' . $product[$itemCode]['code'] . '] "' . $product[$itemCode]['name'] . '"  недоступен к покупке.');
        addError('Товар не может быть добавлен в корзину (<a href="">подобрать замену</a>)');
    } elseif ($product[$itemCode]['stock'] >= $quantity + ($basket['item'][$itemCode]['available'] ?? 0)) {
        $basket = addItem($basket, $itemCode, $quantity);
    } else {
        $quantityNew = $product[$itemCode]['stock'] - ($basket['item'][$itemCode]['available'] ?? 0);
        if ($quantityNew > 0) {
            $basket = addItem($basket, $itemCode, $quantityNew);
            addError(
                '[' . $product[$itemCode]['code'] . '] "' . $product[$itemCode]['name'] . '"  в наличии '
                . $product[$itemCode]['stock'] . ' шт. В корзине '
                . ($basket['item'][$itemCode]['available'] ?? 0) . ' шт.');
            addError('В корзину добавлено максимальное количество товара доступное к покупке.');
        } else {
            addError(
                '[' . $product[$itemCode]['code'] . '] "' . $product[$itemCode]['name'] . '"  в наличии '
                . $product[$itemCode]['stock'] . ' шт. В корзине '
                . ($basket['item'][$itemCode]['available'] ?? 0) . ' шт.');
        }
    }

    useAction($basket);
}

function changeQuantityItemAction($basket, $product): void
{
    $itemCode = (int)$_POST['change_quantity'];
    if (!isset($basket['item'][$itemCode])) {
        addError('Попытка изменения количества несуществующего товара');
        return;
    }
    $quantity = (int)($_POST['basket_quantity'][$itemCode] ?? 0);
    if ($quantity > 0) {
        if ($product[$itemCode]['stock'] >= $quantity + ($basket['item'][$itemCode]['available'] ?? 0)) {
            $basket = addItem($basket, $itemCode, $quantity);
        } else {
            $quantityNew = $product[$itemCode]['stock'] - ($basket['item'][$itemCode]['available'] ?? 0);
            if ($quantityNew > 0) {
                $basket = addItem($basket, $itemCode, $quantityNew);
                addError(
                    '[' . $product[$itemCode]['code'] . '] "' . $product[$itemCode]['name'] . '"  в наличии '
                    . $product[$itemCode]['stock'] . ' шт. В корзине '
                    . ($basket['item'][$itemCode]['available'] ?? 0) . ' шт.');
                addError('Установлено максимальное количество товара доступное к покупке.');
            } else {
                addError(
                    '[' . $product[$itemCode]['code'] . '] "' . $product[$itemCode]['name'] . '"  в наличии '
                    . $product[$itemCode]['stock'] . ' шт. В корзине '
                    . ($basket['item'][$itemCode]['available'] ?? 0) . ' шт.');
            }
        }
    } elseif ($quantity < 0) {
        if ($basket['item'][$itemCode]['quantity'] > abs($quantity)) {
            $basket = addItem($basket, $itemCode, $quantity);
        } else {
            $quantityNew = $quantity + ($basket['item'][$itemCode]['available'] ?? 0);
            if ($basket['item'][$itemCode]['quantity'] > abs($quantity) && $quantityNew !== 0) {
                $basket = addItem($basket, $itemCode, $quantityNew);
            } else {
                $quantityNew = -($basket['item'][$itemCode]['quantity'] ?? 0) + 1;
                $basket = addItem($basket, $itemCode, $quantityNew);
                addError(
                    '[' . $product[$itemCode]['code'] . '] "' . $product[$itemCode]['name'] . '" в корзине '
                    . ($basket['item'][$itemCode]['available'] ?? 0) . ' шт.');
                addError(
                    'Установлено минимальное количество товара для заказа.');
            }
        }
    }
    useAction($basket);
}

function removeItemAction($basket): void
{
    $itemCode = (int)$_POST['remove_item'];
    $basket = removeItem($basket, $itemCode);
    useAction($basket);
}

function deleteBasketAction($basket): void
{
    $basket['item'] = [];
    deleteBasket($basket);
    useAction($basket);
}

function useAction($basket): void
{
    if (empty(countItem($basket))) {
        deleteBasket($basket);
    } else {
        saveBasket($basket);
    }
    header('Location:/basket.php');
    exit();
}

/**
 * Controller
 */

session_start();
// товары
$product = getProduct();
// инициализация корзины
$basket = basketInit($product);
// если нажата ссылка "в корзину"
if (!empty($_POST['add_item'])) {
    addItemAction($basket, $product);
}
// если нажата кнопка "изменить"
if (!empty($_POST['change_quantity'])) {
    changeQuantityItemAction($basket, $product);
}
// если нажата ссылка "удалить"
if (!empty($_POST['remove_item'])) {
    removeItemAction($basket);
}
// если нажата ссылка "очистить корзину"
if (!empty($_POST['delete_basket'])) {
    deleteBasketAction($basket);
}
// переменные используемые в html
$basketId = getBasketId($basket);
$currency = getBasketCurrency($basket);
$totalPrice = getBasketPrice($basket, $product);
$countItem = countItem($basket);
$allQuantity = countAllQuantityItem($basket);

// добавим несуществующий товар для проверки
$code = 5000;
$product[$code] = [
    'code' => $code,
    'stock' => 0,
    'name' => 'Несуществующий товар',
    'price' => 00.00,
    'currency' => 'руб',
];

/**
 * View
 */
?>
<html lang="ru">
<head>
    <meta charset="utf-8">
    <title>Корзина</title>
    <!-- CSS only -->
    <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
          rel="stylesheet"
          integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3"
          crossorigin="anonymous">
    <style>
        .icon-red {
            fill: red;
        }
        .icon-gray {
            fill: gray;
        }
    </style>
</head>
<body>
<div class="container">
    <figure class="text-end">
        <blockquote class="blockquote">
            <p>Простой скрипт корзины (хранение корзины на стороне клиента)</p>
        </blockquote>
        <figcaption class="blockquote-footer">
            с использованием cookie<br />
            Обсуждение скрипта на форуме
            <a href="https://phpclub.ru/talk/threads/Как-обновить-количество-товара-в-корзине.87820/page-3#post-780660" target="_blank">
                https://phpclub.ru/talk/threads/Как-обновить-количество-товара-в-корзине.87820/
            </a>
        </figcaption>
    </figure>
    <h2>Товары</h2>
    <div class="col text-end">
        <svg class="<?= !empty($totalPrice) ? ' icon-red' : ' icon-gray' ?>" width="30" height="30" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
            <path fill-rule="evenodd" clip-rule="evenodd" d="M2 4c0-.55228.44772-1 1-1h2c.48884 0 .90603.35341.98639.8356L6.18046 5H17c.5523 0 1 .44772 1 1v3.85094c0 1.26646-.9125 2.34866-2.1607 2.56266l-8.87438 1.5216a1.0211 1.0211 0 01-.1507.0143 1.0052 1.0052 0 01-.14982.0369c-.54477.0908-1.06-.2772-1.15079-.822L4.15287 5H3c-.55228 0-1-.44772-1-1zm5.32127 7.8449L6.51379 7H16v2.85094c0 .29226-.2106.54196-.4986.59136l-8.18013 1.4026zM9 16.5c0 .8284-.67157 1.5-1.5 1.5S6 17.3284 6 16.5 6.67157 15 7.5 15s1.5.6716 1.5 1.5zm7 0c0 .8284-.6716 1.5-1.5 1.5s-1.5-.6716-1.5-1.5.6716-1.5 1.5-1.5 1.5.6716 1.5 1.5z"></path>
        </svg>
        <?php if (!empty($totalPrice)) { ?>
            <a href=""><span class="info-text">Корзина</span></a>
            <span class="goods-count"><b><?= $countItem ?></b>(<?= $allQuantity ?>) шт</span>
            <span class="all-price"><b><?= $totalPrice ?></b> <?= $currency ?></span>
        <?php } ?>
    </div>
    <form id="product" action="/basket.php" method="post">
        <table class="table">
            <tr>
                <th class="col col-1 text-end">Код</th>
                <th class="col">Наименование товара</th>
                <th class="col col-1 text-end">Цена</th>
                <th class="col col-1 text-end">Доступно</th>
                <th class="col col-1">Количество</th>
                <th class="col col-1"></th>
                <th class="col col-1">в корзине</th>
            </tr>
            <?php foreach ($product as $item) { ?>
                <tr>
                    <td class="text-end"><?= $item['code'] ?></td>
                    <td><?= $item['name'] ?></td>
                    <td class="text-end"><?= $item['price'] ?> <?= $item['currency'] ?></td>
                    <td class="text-end"><?= $item['stock'] ?> шт</td>
                    <td>
                        <input class="form-control" size="3" type="number" name="quantity[<?= $item['code'] ?>]" value="1"/>
                    </td>
                    <td>
                        <button class="btn btn-info btn-sm" name="add_item" value="<?= $item['code'] ?>">
                            в корзину
                        </button>
                    </td>
                    <td><?= !empty($basket['item'][$item['code']]) ? $basket['item'][$item['code']]['available'] . ' шт'
                            : '' ?></td>
                </tr>
            <?php } ?>
        </table>
    </form>
    <?php if (!empty(getError())) { ?>
        <div class="alert alert-danger" role="alert">
            <ul>
                <li><?= implode('</li><li>', getError()) ?></li>
            </ul>
        </div>
    <?php }
    clearError(); ?>
    <?php if (!empty($basket['item'])) { ?>
        <h2>Корзина</h2>
        <figure class="text-end">
            <figcaption class="blockquote-footer">
                идентификатор корзины <?= $basketId ?>
            </figcaption>
        </figure>
        <table class="table">
            <tr>
                <th class="col col-1 text-end">Код</th>
                <th>Наименование товара</th>
                <th class="col col-1 text-end">Цена за шт</th>
                <th class="col col-1 text-end">Количество</th>
                <th class="col col-1 text-end">Сумма</th>
                <th class="col col-1"></th>
                <th class="col col-1" colspan="2">
                    <form action="/basket.php" method="post">
                        <button class="btn btn-sm btn-link" name="delete_basket" value="Y">
                            очистить корзину
                        </button>
                    </form>
                </th>
            </tr>
            <form id="basket" action="/basket.php" method="post">
                <?php foreach ($basket['item'] as $itemCode => $item) { ?>
                    <tr>
                        <?php if (empty($item['available'])) { ?>
                            <td class="text-end text-danger"><?= $itemCode ?></td>
                            <td class="text-danger"><?= $product[$itemCode]['name'] ?></td>
                            <td class="text-end text-danger" colspan="3">товар недоступен</td>
                            <td class="text-end" colspan="2"><a href="">подобрать замену</a></td>
                        <?php } else { ?>
                            <td class="text-end"><?= $itemCode ?></td>
                            <td><?= $product[$itemCode]['name'] ?></td>
                            <td class="text-end"><?= $product[$itemCode]['price'] ?> <?= $product[$itemCode]['currency'] ?></td>
                            <td class="text-end"><?= $item['quantity'] === $item['available']
                                    ? '<span class="text-success">' . $item['quantity'] . '</span>'
                                    : '<span class="text-danger">' . $item['quantity']
                                      . '</span> | <span class="text-success">' . $item['available'] . '</span>' ?> шт
                            </td>
                            <td class="col col-1 text-end"><?= $product[$itemCode]['price']
                                                               * $item['available'] ?> <?= $product[$itemCode]['currency'] ?></td>
                            <td class="col col-1">
                                <input class="form-control" size="3" type="number" name="basket_quantity[<?= $itemCode ?>]" value=""/>
                            </td>
                            <td class="col col-1">
                                <button class="btn btn-info btn-sm" name="change_quantity" value="<?= $itemCode ?>">
                                    изменить
                                </button>
                            </td>
                        <?php } ?>
                        <td class="col col-1">
                            <button class="btn btn-close btn-sm" aria-label="Удалить товар" name="remove_item" value="<?= $itemCode ?>"></button>
                        </td>
                    </tr>
                <?php } ?>
        </table>
        <div class="text-end">
            <p>Количество товаров: <b><?= $countItem ?></b>(<?= $allQuantity ?>) шт</p>
            <p>Сумма: <b><?= $totalPrice ?></b> <?= $currency ?></p>
        </div>
        <hr/>
        <div class="text-end">
            <button class="btn btn-success" name="add_order" value="<?= $basket['basket_id'] ?>">Оформить заказ
            </button>
        </div>
        </form>
        <h2>Задания</h2>
        <ul>
            <li>Вынести функции модели работы с корзиной в отдельный файл <b>basket_model_function.php</b>.</li>
            <li>Разделить товары и корзину по отдельным страницам сайта (<b>product.php</b> и <b>basket.php</b>).</li>
        </ul>
    <?php } ?>
</div>
</body>
</html>
 
Последнее редактирование:

arhat78

Новичок

WMix

герр M:)ller
Партнер клуба
1. нажал полсотни раз неожиданных результатов не увидел
 

Valick

Новичок
Я помоему ДМБ так ниразу целиком и не смотрел.
WMix, прежде чем, что-то исправить, я всё-таки пытаюсь это воспроизвести. Тем более, что вариант "к неожиданным результатам" меня сильно не устраивает. Хотелось бы конкретный "зловещий кейс".
а вот changeQuantityItem() - до сих пор не понял, как это осуществить
По аналогии с добавлением addItem()
Посмотри что для этого есть в форме, посмотри как принимается переменная из браузера, и что с ней происходит далее
 

WMix

герр M:)ller
Партнер клуба
@Valick просто как нить между чтением и записью разделять хочется: запись (изменение, удаление) - POST
 

Valick

Новичок
WMix, если только так, то да. Я даже изменил на пост, но потом вернул гет (надо же было хоть что-то оставить от кода ТС).
Ну и в конце концов можно все замечания по скрипту (написанному на коленке за два дня по 15 минут в день), можно оформить как задания для самостоятельного дальнейшего исправления. Чем больше замечаний тем лучше. Там и на отдельные файлы разделить надо и ajax не помешает добавить, вобщем много места для творчества, хоть сутки себе удлиняй до 36 часов :)
 
Последнее редактирование:

arhat78

Новичок
Чем больше замечаний тем лучше. Там и на отдельные файлы разделить надо и ajax не помешает добавить, вобщем много места для творчества, хоть сутки себе удлиняй до 36 часов :)
У меня на такую констркции function : void, :array выдаёт ошибку уже в текстовом редакторе. И раньше я не встречался с такими.... Но как я поняли из описания, не обязательно же так объявлять тип?
 
Последнее редактирование:

Valick

Новичок
arhat78, значит у тебя версия РНР старая. Если хочешь отстрели типизацию, тем более, что в аргументах её нет.
 

arhat78

Новичок
arhat78, значит у тебя версия РНР старая. Если хочешь отстрели типизацию, тем более, что в аргументах её нет.
Сегодня с БД уже не успеваю поработать. Код изучаю, много неизвестных мне ранее функций,смотрю их действия. Благодарю!
Хм... даже больше скажу - ещё ошибки на оператор ?? во всех вариантах использования (count, например). Может другой редактор использовать (phpdesigner пользуюсь)?
Установил Notepad++, ошибок не выдаёт. Надеюсь окажется неплохим и удобным.
 
Последнее редактирование:

флоппик

promotor fidei
Команда форума
Партнер клуба
Сейчас есть только один полноценный редактор под php. В наших мордорах он больше не продаётся, но есть ознакомительная версия https://www.jetbrains.com/ru-ru/phpstorm/nextversion/
Ну, в ваших мордорах вроде продлили все лицензии до октября, не? Ну и если надо помочь с оплатой лицензии (и если это имеет смысл для тебя), то ты вполне в числе людей, которым я готов с этим помочь
 
  • Like
Реакции: AmdY

arhat78

Новичок
Немного подправил скрипт $_GET -> $_POST
Я в курсе, что нужен $_POST, с этим то и сам справлюсь :)

Сейчас по вашему коду вывел catalog (Товары), создал таблицу в БД basket, теперь займусь добавлением в БД basket и выводом уже на странице корзины. Вопросы уже есть, по позже сформулирую и задам, если не против :)

Первый, это будет про basketInit и 'basket_id' ...............
 
Последнее редактирование:

Valick

Новичок
Это лишнее.
Лучше сессиями.
Чем корзина в сессии лучше корзины в кукисах?

Более того, я бы предложил не отказываться от реализации корзины на печеньках в пользу хранения в БД. Лучше реализовать оба варианта и для авторизованных пользователей хранить корзину в БД, а для неавторизованных в кукисах. Тут бы ООП сильно помогло, но процедурка так процедурка...
 
Последнее редактирование:

AmdY

Пью пиво
Команда форума
Что такое куки и сессия? Это сдача экзаменов к кулинарном колледже? Корзина должна быть стороджнезависимой, это просто набор объектов, которые можно сложить куда угодно. В современном вебе со СПА и мобилками не обязательно быть кукам и сессиям.
 
Сверху