Генерация ссылки на корзину и восстановление корзины по ссылке.

Добрый день.

Давайте попрактикуемся с задачкой описанной в заголовке и за 10 минут напишем готовое бесплатное решение, без применения дополнительных компонентов.


Наш мини-компонент будет состоять из двух частей.

1. Сохранение собственной корзины и генерация ссылки на нее
2. Обработка ссылки при ее открытии и восстановление корзины.

Структурно компонент будет состоять из трех частей.
1. Класс компонента
2. Плагин
3. Js файл, который по сути нужен только для того чтобы отловить нажатие на кнопку «Получить ссылку» и показать готовую ссылку.

Сохраняем корзину



Начнем с того что, создадим в каталоге /core/components/ новый каталог с нашим компонентом. Назовем его например savecart

В каталоге компонента будет один единственный файл savecart.class.php
Обратите внимание обязательно только нижний регистр, и постфикс сlass. Такое правило написания требует MODX, для того, чтобы он мог найти файл при использовании метода $modx->getService(). А вот название класса уже можно писать как угодно.

Первоначальная структура файла такая:

<?php

class SaveCart
{
    public $modx;

    public function __construct(modX $modx, array $config = array())
    {
        $this->modx = &$modx;
    }

}

Метод $modx->getService() автоматически передает в конструктор класса экземпляр $modx. Мы сохраняем ссылку на него, и можем использовать во внутренних методах все возможности MODX.

Мы знаем что корзина хранится в сессии, а именно в $_SESSION['minishop2']['cart'] так что достаточно сохранить этот массив куда-либо, а потом его извлечь и подсунуть обратно в сессию. Вот и вся теория.

Сохранять по разному. Я предлагаю использовать для этого кэш MODX. Если вы плаваете в этом вопросе, то очень рекомендую познакомиться с исчерпывающей статьей о сохранении пользовательских данных в кэш
@Сергей Шлоков Низкий поклон за хороший материал. До сих пор пользуюсь.

На самом деле все просто. пишем новый служебный метод для сохранения корзины в кэш. Всего несколько строчек. Обратите внимание. Я поставил спецификатор доступа private, так как это служебный метод. Он будет недоступен снаружи. Чтобы сохранить данные этот метод кто-то должен вызвать внутри класса.

private function _saveUserCart()
    {
        //Получаем корзину из сессии
        $cart = $_SESSION['minishop2']['cart'];

        $savedCart = array(
            'cart' => $cart
        );
        
        //Указываем каталог saved_cart в папке cache
        $options = array(
            xPDO::OPT_CACHE_KEY => 'saved_cart',
        );
        
        //Получаем уникальный идентификатор пользователя.  
        // Я использую тот, что генерирует MODX.  Можно собрать свою рандомную строку
        $id = session_id();
        //Сохраняем, время хранения месяц.
        $this->modx->cacheManager->set($id, $savedCart, 60 * 60 * 24 * 30, $options);
        
        //Возвращаем уникальный идентификатор чтобы подсунуть его в ссылку
        return $id;
    }
Данные в кэш сохранены. Осталось сгенерировать и вернуть ссылку. Для этого пишу еще один метод. На этот раз публичный, доступный снаружи. Именно его мы будем вызвать в плагине. Он еще проще.

public function getCartLink($page_id)
    {
        //Вызываем служебный метод сохранения корзины
        $cart_id = $this->_saveUserCart();
        //Возвращаем в плагин готовую ссылку
        return $this->modx->makeUrl($page_id, 'web', ['cart_id' => $cart_id], 'full');
    }
Половину работы сделали. Вот полный код того, что получилось.

<?php

class SaveCart
{
    public $modx;

    public function __construct(modX $modx, array $config = array())
    {
        $this->modx = &$modx;
    }


    public function getCartLink($page_id)
    {
        $cart_id = $this->_saveUserCart();
        return $this->modx->makeUrl($page_id, 'web', ['cart_id' => $cart_id], 'full');
    }


    private function _saveUserCart()
    {
        $cart = $_SESSION['minishop2']['cart'];

        $savedCart = array(
            'cart' => $cart
        );

        $options = array(
            xPDO::OPT_CACHE_KEY => 'saved_cart',
        );


        $id = session_id();

        $this->modx->cacheManager->set($id, $savedCart, 60 * 60 * 24 * 30, $options);
        return $id;
    }

}

Теперь создадим плагин saveCart. Привяжем к нему событие OnHandleRequest

Плагин также очень простой

switch ($modx->event->name) {
    case 'OnHandleRequest':
        // Js скрипт будет обращаться к сайту передавая конкретный action  для идентификации
        if (!empty($_POST['action']) && $_POST['action'] === 'getCartLink') {
            //Загружаем свежесозданный компонент
            $SaveCart = $modx->getService('SaveCart', 'savecart', MODX_CORE_PATH . 'components/savecart/');
            //Дергаем генерацию ссылки
            // передаем id  страницы корзины
            $cart_link = $SaveCart->getCartLink(5);
        
            
            //Готовим json  для ответа в JS
            $output = [];
            $output['success'] = true;
            $output['link'] = $cart_link;

            echo json_encode($output);
            die();
        }
        break;
}

Вот и все. С первой частью мы разобрались.
Давайте кратко повторим, что у нас получилось.
1. Вы при помощи js отлавливаете нажатие нужной кнопки и посылаете POST запрос с определенным экшеном. Надеюсь здесь пример не нужен.
2. Плагин ловит нужный экшен, подключает компонент и запрашивает ссылку.
3. Компонент сохраняет корзину из сессии в кэш, в качестве уникального ключа использует идентификатор сессии пользователя. Генерирует ссылку типа ?cart_id={session_id}
4. Возвращаем готовую ссылку обратно в js скрипт, который показывает ее на экране. Тут думаю тоже все понятно. Если нет напишите — покажу пример.


Открываем ссылку и восстанавливаем корзину



На сцене все те же.

1. Плагин на событие onHandleRequest

Всего несколько строк
//Запускаем обработчик только если есть нужный GET параметр
 if(!empty($_GET['cart_id'])){
            //Фильтруем значение GET  параметра
            $cart_id = filter_input(INPUT_GET, 'cart_id', FILTER_SANITIZE_STRING);
            //Подключаем компонент
            $SaveCart = $modx->getService('SaveCart', 'savecart', MODX_CORE_PATH . 'components/savecart/');
            // Вызываем нужный метод компонента.  
            $SaveCart->getSavedCart($cart_id);
        }
Полный код плагина получился вот такой (23 строки с отступами)

switch ($modx->event->name) {
    case 'OnHandleRequest':
        if (!empty($_POST['action']) && $_POST['action'] === 'getCartLink') {
            $SaveCart = $modx->getService('SaveCart', 'savecart', MODX_CORE_PATH . 'components/savecart/');
            $cart_link = $SaveCart->getCartLink(5);

            $output = [];
            $output['success'] = true;
            $output['link'] = $cart_link;

            echo json_encode($output);
            die();
        }

        if(!empty($_GET['cart_id'])){
            $cart_id = filter_input(INPUT_GET, 'cart_id', FILTER_SANITIZE_STRING);
            $SaveCart = $modx->getService('SaveCart', 'savecart', MODX_CORE_PATH . 'components/savecart/');
            $SaveCart->getSavedCart($cart_id);
        }
        break;
}

2. Компонент и его метод восстановления корзины

Нам понадобится всего один метод в несколько строк

//На входе получаем идентификатор сохраненной корзины
public function getSavedCart($cart_id)
    {
        //Забираем из кэша  сохраненную корзину
        $cart = $this->modx->cacheManager->get($cart_id, array(xPDO::OPT_CACHE_KEY => 'saved_cart'));
        if (!empty($cart)) {
            // Подсовываем ее в сессию минишопа
            $_SESSION['minishop2']['cart'] = $cart['cart'];
        }
    }


Полный код класса компонента (46 строк со всеми отступами)

<?php

class SaveCart
{
    public $modx;

    public function __construct(modX $modx, array $config = array())
    {
        $this->modx = &$modx;
    }


    public function getCartLink()
    {
        $cart_id = $this->_saveUserCart();
        return $this->modx->makeUrl(129, 'web', ['cart_id' => $cart_id], 'full');
    }

    public function getSavedCart($cart_id)
    {
        $cart = $this->modx->cacheManager->get($cart_id, array(xPDO::OPT_CACHE_KEY => 'saved_cart'));
        if (!empty($cart)) {
            $_SESSION['minishop2']['cart'] = $cart['cart'];
        }
    }

    private function _saveUserCart()
    {
        $cart = $_SESSION['minishop2']['cart'];

        $savedCart = array(
            'cart' => $cart
        );

        $options = array(
            xPDO::OPT_CACHE_KEY => 'saved_cart',
        );


        $id = session_id();

        $this->modx->cacheManager->set($id, $savedCart, 60 * 60 * 24 * 30, $options);
        return $id;
    }

}
Николай Савин
03 августа 2020, 11:42
modx.pro
7
580
+14
Поблагодарить автора Отправить деньги

Комментарии: 6

Михаил
03 августа 2020, 12:40
0
Да уже давно же написано shareCart
    Николай Савин
    03 августа 2020, 12:43
    +3
    Может быть. Не пользовался.
    Моя задача была показать, что компонент для MODX это не обязательно долго-сложно.
      Николай Савин
      03 августа 2020, 12:49
      0
      Дык твой компонент в базу пишет. А мой в кэш. Это в разы быстрее и проще.
      Алексей Шумаев
      04 августа 2020, 00:36
      +1
      При восстановлении желательно цены обновлять — вдруг изменились)
        Сергей Шлоков
        04 августа 2020, 11:16
        +3
        Один маленький замечаний по коду
        $id = session_id();
        выкладывать идентификатор сессии в публичный доступ из вэри бэд. Это слив куки.
        Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
        6