ecommerce.js - Электронная коммерция (Яндекс.Метрика и Google Analytics) для сайтов на базе MODX + miniShop2

Привет!

Хочу поделиться js скриптом, который позволяет передавать данные электронной коммерции в системы аналитики Яндекс.Метрика и Google Analytics.

Из особенностей отмечу то, что мое решение выполнено не в виде дополнения для MODX, а в виде одного js файла и не имеет в своем составе php кода (сниппетов и т.п.). Также я применил, пожалуй, не самый стандартный подход в том, как будут получаться данные о товарах на страницах сайта.

Введение



Если вам не совсем знакома тема, рекомендую сначала почитать (или аналогичные материалы от гугл):
yandex.ru/support/metrica/data/e-commerce.html
yandex.ru/support/metrica/ecommerce/data.html#data

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

В принципе, чтобы сбор данных заработал, нужно добавить код счетчика на ваш сайт, включив в настройках электронную коммерцию, и далее в JavaScript-массив window.dataLayer (контейнер данных) в глобальном пространстве имен (window) добавлять данные методом push.

Чем плох самый простой путь передачи данных?

В целом мы можем просто в нужных местах сайта добавлять такие вставки кода (это пример от Яндекса):
<script type="text/javascript">
    // Используем метод push для добавления Ecommerce-объекта
    window.dataLayer.push(
        {
            "ecommerce": {
                "currencyCode": "RUB",
                "<actionType>": {
                    "actionField": <actionField>,
                    "products" : [<productFieldObject>, <productFieldObject>, ...]
                }
            }
        }
    );
</script>
и оно будет работать. Но управлять этим, вспоминать о том, где и какой код размещен, искать его через админку MODX то еще удовольствие. Да и получим десятки inline скриптов на странице, что вряд ли понравится оптимизатору.

По какому пути решил пойти?

Что ж, соберем весь js код в один файл, а в html нужно оставить только какую-то разметку с данными. Тут мне и вспомнилась семантическая разметка Schema.org. Ведь там уже описаны такие вещи, как товар Product и заказ Order, а именно их и нужно передавать в системы аналитики.
Если в html код добавить семантическую разметку, то при помощи javascript можно получить данные непосредственно из html разметки.

Возможности


Предлагаемый скрипт позволяет отправлять в системы аналитики такие данные, как:
1. Просмотр списка товаров (т.е. мы будем видеть, какие листинги товаров пользователи просматривают, какие в них товары присутствуют, популярные товарные категории)
2. Клики по ссылкам на товар (увидим из каких списков кликают, на товары по каким позициям и т.п.)
3. Просмотры карточек товаров
4. Добавление и удаление товаров в корзину
5. Оформление заказа (номер заказа, товары в нем)

Из всей этой кучи данных таже Яндекс.Метрика строит большое количество интересных отчетов!

Как настроить


Опишу по шагам, сначала подключим скрипт, а далее распишу по порядку по всем типам действий из документации Яндекс.Метрики:

1. Подключите скрипт
Создайте файл ecommerce.js с этим кодом и подключите его на страницы сайта после инициализации счетчиков Яндекс.Метрики и Google Analitycs, после подключения скриптов miniShop и mSearch (если используете). Сейчас скрипт еще не отправляет никаких данных, т.к. он не знает, какую информацию отправлять. Давайте его познакомим с тем, как на вашем сайте выглядят товары.

2. Действие «impression» — Просмотр списка товаров
Данные должны отправляться в момент открытия списка товаров.

Ваши товары отображаются в неких списках, это может быть список товаров в разделе или в блоке «популярные». Покажем скрипту эти блоки, для чего добавим пару атрибутов с префиксом data-ecommerce слою обертке (классы не нужны, просто указываю их для упрощения восприятия):
<div class="product-list" data-ecommerce="impressions" data-ecommerce-list="Популярные">
  ... товары
</div>


А теперь для товаров внутри этих блоков добавим микроразметку, пример на минималках:
<div class="ms2_product" itemscope itemtype="http://schema.org/Product">
    <meta itemprop="identifier" content="{$id}" />
    <meta itemprop="description" content="{$longtitle}" />
    <meta itemprop="category" content="{$parent | resource : 'pagetitle'}" />
    <meta itemprop="price" content="{$price | replace : ' ' : ''}" />
    <meta itemprop="priceCurrency" content="RUB" />
    <a href="{$id|url}"  itemprop="name" data-ecommerce="click">{$pagetitle}</a>
    .. прочий код
</div>
В этом примере: 1) указываем identifier, name, description, category, price и priceCurrency товара. Помимо этих полей Яндекс поддерживает еще brand, variant, я их не использовал. 2) list (список) мы указали на предыдущем шаге 3) важно заметить, что названия полей в schema.org не совпадает с modx и системами аналитики, скрипт это учитывает (identifier => id).

Всё, «impression» должны передаваться.

3. Действие «click» — Клик по товару из списка
Данные должны передаваться в момент, когда пользователь совершил клик по ссылке товара.

А мы это уже решили, обратите внимание на атрибут data-ecommerce=«click» у ссылки (тег ) из предыдущего блока с кодом. Этого достаточно.

4. Действие «detail» — Просмотр товара
Данные должны отправляться в момент открытия страницы с карточкой товара.

В шаблоне карточки товара нужно добавить один data- атрибут и разметку schema.org для товара, краткий пример:
<div data-ecommerce="detail" itemscope itemtype="http://schema.org/Product">
    <h1 itemprop="name">{$_modx->resource.pagetitle}</h1>
    <div itemprop="offers" itemscope itemtype="http://schema.org/Offer">
        <meta itemprop="price" content="{$price | replace : ' ' : ''}" />
        <meta itemprop="priceCurrency" content="RUB" />
    </div>
    <meta itemprop="identifier" content="{$_modx->resource.id}" />
    <meta itemprop="category" content="{$_modx->resource['parent'] | resource : 'pagetitle'}" />
    <meta itemprop="description" content="{$_modx->resource.description}" />
</div>

5. Действие «add» — Добавление товара в корзину
Данные должны отправляться в момент добавления заказа в корзину. Например, при нажатии кнопки «Добавить в корзину».

Если вы сделали предыдущие шаги — скрипт должен отработать корректно без дополнительной настройки.
Он отслеживает событие минишоп Cart.add.response.success и обрабатывает его. Чтобы получить данные о товаре — ищет на странице блок с микроразметкой товара с соответствующим id и берет данные из этого блока.

6. Действия «add» и «remove» при нахождении в корзине.
Данные должны отправляться в момент добавления и удаления заказа из корзины.

Вот тут пока вопрос сложный, дело в том, что в событиях Cart.remove.response.success, Cart.change.response.success мы получаем только id товара и его цену, в случае с remove еще и кол-во товара. А вот название товара, сколько его было добавлено (или изменено в случае change) — не знаем. Поэтому данные передаются частично… вопрос не до конца проработан.

6. Действие «purchase» — Покупка
Данные должны отправляться в момент подтверждения заказа.

Я реализовал его через микроразметку на странице оформления заказа.
В том чанке, который изначально называется «tpl.msOrder.success» нужна примерно такая разметка:
<div itemscope itemtype="http://schema.org/Order" data-ecommerce="purchase">
    <meta itemprop="orderNumber" content="{$order.num}">
    <h2>Ваш заказ принят, спасибо!</h2>
    {foreach $products as $p}
        <div itemprop="orderedItem" itemscope itemtype="http://schema.org/OrderItem">
            <meta itemprop="orderQuantity" content="{$p.count}">
            <div itemprop="orderedItem" itemscope itemtype="https://schema.org/Product">
                <meta itemprop="identifier" content="{$p.id}" />
                <meta itemprop="name" content="{$p.pagetitle}">
                <meta itemprop="description" content="{$p.description}">
                <meta itemprop="category" content="{$p.parent | resource : 'pagetitle'}" />
                <div itemprop="offers" itemscope itemtype="http://schema.org/Offer">
                    <meta itemprop="price" content="{$p.price | replace : ' ' : ''}">
                    <meta itemprop="priceCurrency" content="RUB">
                </div>
                {foreach $p.options as $option => $value}
                    <div itemprop="additionalProperty" itemscope itemtype="http://schema.org/PropertyValue">
                        <meta itemprop="name" content="{$option}">
                        <meta itemprop="value" content="{$value | join : ', '}">
                    </div>
                {/foreach}
            </div>
        </div>
    {/foreach}
</div>

скрипт найдет по атрибуту data-ecommerce=«purchase» и обработает корректно.
Из недостатков приёма — если страницу обновить — данные о заказе продублируются.

Поддержка mFilter2


Она есть, скрипт обрабатывает событие mse2_load чтобы передать данные о новом просмотре списка товаров (impressions) и клике по товару.

Заключение


Мы реализовали почти всё возможности электронной коммерции, кроме действий promoView и promoClick. Я ими не пользуюсь :)

Есть ложки дегтя:
— не до конца обрабатываются действия в корзине, а именно изменение кол-ва товара в ней;
— не везде передаются такие данные о товаре, как position или list.

Плюсики:
— сами того не желая мы добавили на страницу разметку schema.org, которую должны оценить поисковые системы.

В общем будет интересно услышать мнение сообщества, может кому-то пригодится)

p.s. еще раз ссылка на файл:
gist.github.com/createit-ru/2575f7a672060f839747b65383223421

Если вам понравился мой подход и пригодился скрипт — можно угостить чашкой кофе:



Ваши благодарности ускорят внедрение новых функций, увеличат мою мотивацию, заставят внимательнее прислушиваться к пожеланиям!
Наумов Алексей
20 декабря 2023, 12:03
modx.pro
6
712
+12

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

Андрей Шевяков
20 декабря 2023, 17:14
0
На одном сайте при подключении пошли по такому пути:
В целом мы можем просто в нужных местах сайта добавлять такие вставки кода (это пример от Яндекса):

Скрипт для действия покупки располагается на странице успешного заказа в чанке tpl.msGetOrder. И мы столкнулись с проблемой, что если клиент совершает заказ без оплаты (у нас есть такой вариант, после подтверждения от менеджера), то все ок, но если клиент уходит на оплату, то некоторые, после оплаты просто не возвращаются на сайт и поэтому не видят страницу успешной оплаты и отсюда минус, что эти данные в коммерцию метрики не уходят.

Пока еще не исправляли этот момент, но в теории данные должны отправляться в момент успешного оформления заказа, до перехода клиента на оплату. С вашим скриптом в таком случае видимо тоже не отработает скрипт. Вот если это как то сможете настроить, было бы супер!
    Наумов Алексей
    20 декабря 2023, 20:18
    0
    Верно всё, я про эту особенность написал в тексте.
    Пока у меня нет других идей, добавлять серверную часть я не хотел.
    В общем это первая версия скрипта, может кто применит, появится больше опыта и наработок, скрипт станет лучше)

    Кстати, в магазине modstore есть 2 дополнения, которые работают с google и отправляют данные средствами php.
      Андрей Шевяков
      20 декабря 2023, 20:22
      0
      Кстати, в магазине modstore есть 2 дополнения, которые работают с google и отправляют данные средствами php.
      А каких 2? Только это находил msEcommerce
Dan
Dan
24 декабря 2023, 15:03
+1
Отличное решение!
Кстати по поводу purchase я вешал отправку на колбэк
miniShop2.Callbacks.Order.submit.response.success = function (response) {
//Тут сбор данных и отправка
}
Но минус в том, что под каждую разметку приходится немного править код, но зато нет дублей.

А для событий change и remove я расширил msCartHandler, чтобы он возвращал id товара, добавленное/удаленное количество, его категорию и название.
    Наумов Алексей
    25 декабря 2023, 11:26
    +1
    Спасибо!
    В принципе да, с miniShop2.Callbacks.Order.submit.response.success можно попробовать переписать скрипт, по идее должно сработать.

    Про прием с расширением msCartHandler знаю, ведь хотелось обойтись без любого php кода, но такого способа в текущей реализации miniShop2 нет.
      Dan
      Dan
      25 декабря 2023, 11:33
      0
      С версии 4.1.4 кстати можно обойтись и без PHP, но тогда часть скрипта придется внедрять в скрипт от ms2. События remove и change с этой версии возвращают id товара и его кол-во в корзине. По сути нужно только category и name. Можно внедрить код в метод send в скрипте ms2 и в случае success получать объект формы, с которой была отправка и цеплять эти данные с дочерних метатегов.
        Наумов Алексей
        25 декабря 2023, 11:42
        +1
        Речь про объект cart в response? Там да, есть данные вроде товар id=3, кол-во 5. Вот только в метрику (как я понял) нужно передавать другое: не текущее кол-во товаров, а сколько именно его добавили в корзину или удалили. Ну типа было 5 товаров, мы 2 убрали нажатием на "-", осталось 3. Вот минишоп возвращает 3, а где взять 2 — вопрос.
          Dan
          Dan
          25 декабря 2023, 11:44
          0
          А ну кстати да, тогда видимо без пыхи никак)
    Дмитрий Суворов
    03 апреля 2024, 22:48
    0
    Очень полезное решение, спасибо!
    Но есть проблема с событием добавления в корзину. Ошибка следующая:
    Uncaught TypeError: Cannot read properties of undefined (reading 'id')

    В строке 75:
    if (identifierElement && identifierElement.content === row.id) {

    Не подскажите, в чем может быть проблема?
      Наумов Алексей
      03 апреля 2024, 23:39
      0
      Нужно отлаживать… добавьте debugger перед этим if, и нужно посмотреть что приходит в responseData? Есть там row? Есть в этом row id? Может различия в версиях miniShop2…
      Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
      11