← все проекты
Разработчик2023

Zoo-Land

Интернет-магазин зоотоваров — новая версия

Скриншот: Zoo-Land

Что это за проект

Второй магазин на том же самописном стеке студии: свой тип записи для товаров, корзина на AJAX, оформление заказа, личный кабинет, админ-модуль заказов на 1339 строк. Каталог — 2771 товар и 198 категорий — перенесён со старой версии сайта своим скриптом: там цены лежали в ACF-репитере, здесь в собственном поле, поэтому основная работа миграции это преобразование данных, а не копирование строк.

Моя роль

Логин и регистрация, добавление заказов, футер и контакты, merge-работа между ветками трёх разработчиков.

20 из 271 коммита

Стек

  • PHP
  • WordPress
  • MySQL
  • Redis
  • SCSS
  • AJAX
  • Swiper

Что внутри стоит посмотреть

  • Каталог из 2771 товара и 198 категорий, перенесён со старого движка своим скриптом миграции
  • Корзина и избранное на AJAX с плавающей панелью и счётчиками
  • Админ-модуль заказов на 1339 строк: обработанные, удалённые, контент-страница
  • Фильтр по цене считает диапазон по данным категории, Redis object cache

Технические решения

Выдержки из настоящих файлов проекта — путь к каждому указан над кодом.

AJAX-корзина с вариациями: два уровня ключей

У товара есть вариации — фасовка, вкус, объём — и в корзине они должны лежать раздельно, но под одним товаром. Отсюда двухуровневый ключ postId → variantKey. Количество приходит от фронтенда то числом, то строкой, поэтому перед сложением приводится явно: иначе "2" + 1 давало бы "21".

functions/plugins/cart/cartajax.phpphp
function add_to_cart()
{
    session_start();

    $data = json_decode(file_get_contents('php://input'), true);

    if (!isset($_SESSION['cart'])) {
        $_SESSION['cart'] = [];
    }

    $cart = $_SESSION['cart'];

    if (isset($cart[$data['postId']][$data['variantKey']])) {
        // количество приходит и числом, и строкой — приводим явно
        if (is_string($data['quantity'])) {
            $cart[$data['postId']][$data['variantKey']] = strval(
                intval($data['quantity']) + intval($cart[$data['postId']][$data['variantKey']])
            );
        } else {
            $cart[$data['postId']][$data['variantKey']] += intval($data['quantity']);
        }
    } else {
        $cart[$data['postId']][$data['variantKey']] = $data['quantity'];
    }

    $_SESSION['cart'] = $cart;

    session_write_close();
    wp_die();
}

add_action('wp_ajax_add_to_cart', 'add_to_cart');
add_action('wp_ajax_nopriv_add_to_cart', 'add_to_cart');

Заказ описан списком полей, а не кодом

Обработчик заказа не пишет SQL и не собирает письмо: он объявляет, какие поля принимает, какие из них попадут в письмо менеджеру и в какую таблицу это сохранить. Всё остальное делает общий get_request(). Двадцать одно поле — доставка, самовывоз, промокод, сдача с суммы, — и ни одной строки повторяющейся обвязки.

functions/plugins/cart/order.phpphp
function add_order()
{
    $fields = array(
        'user_id', 'user_name', 'phone', 'address', 'home', 'flat', 'floor',
        'porch', 'comment', 'method', 'change_val', 'delivery', 'delivery_price',
        'pickup_point', 'payment', 'promo', 'products', 'total_price',
        'quantity', 'date', 'status',
    );

    // что уходит менеджеру в письме — подмножество тех же полей
    $message = array('Заявка на сайте', array(
        'user_name', 'phone', 'address', 'home', 'flat', 'floor', 'porch',
        'method', 'change_val', 'delivery', 'delivery_price',
        'pickup_point', 'payment', 'quantity', 'total_price', 'date',
    ));

    get_request($fields, 'init_orders', 0, $_POST['data'], $message);

    die();
}

add_action('wp_ajax_add_order', 'add_order');
add_action('wp_ajax_nopriv_add_order', 'add_order');

Корзина в сессии, а не в базе

Гость собирает корзину без регистрации, поэтому состав лежит в сессии и уезжает во фронтенд одной переменной. Обращение к ключу надо защищать: на первом визите корзины ещё нет, и без проверки в php-fpm.log на каждый запрос падало предупреждение — это один из код-фиксов, которые я сделал при подъёме проекта.

themes/init/footer.phpphp
<script>
    var cartData = <?php echo json_encode(
        isset($_SESSION["cart"]) ? $_SESSION["cart"] : array()
    ); ?>;
</script>

Что не доделано

Пишу сам, до того как заметит собеседующий.

  • Перенесены обложки товаров, но не галереи: на карточке товара один кадр вместо нескольких
  • Личный кабинет пуст — страницы «Мои заказы», «Сохранённое», «Отзывы» есть, демо-данных нет