Integracja WPH Pixel Domodi z WooCommerce w czystym PHP

Jakiś czas temu dostałem zapytanie od klienta czy pomogę im przeprowadzić integrację Pixela od Domodi z ich sklepem obuwniczym opartym na WooCommerce. Zazwyczaj pytam na początku, czy klient próbował wtyczek lub innych gotowych rozwiązań, w końcu jest to popularne zagadnienie, jednak jak się okazuje ani wtyczki płatne, ani te z katalogu WordPress nie były w stanie uruchomić śledzenia w prawidłowy sposób. Klient cały czas dostawał od Domodi feedback z informacją, że ich skrypt nie jest wywoływany właściwie i nie są w stanie gromadzić danych do analizy.

W manualu który dostarcza nam Domodi otrzymujemy w miarę prosty opis dla Shoplo i Shopera, dla pozostałych platform eCommerce jest tylko informacja jaki zestaw danych ma zostać wygenerowany a dalej radź sobie sam 🙂 Ani słowa o WooCommerce.

Integracja Domodi WPH Pixel – wybór metody.

W tym miejscu uznajemy, że mamy wklejony w <body> kod z definicją funkcji Pixela:

<script>
!function(d,m,e,v,n,t,s){d['DomodiTrackObject'] = n;
d[n] = window[n] || function() {(d[n].queue=d[n].queue||[]).push(arguments)},
d[n].l = 1 * new Date(), t=m.createElement(e), s=m.getElementsByTagName(e)[0],
t.async=1;t.src=v;s.parentNode.insertBefore(t,s)}(window,document,'script',
'https://pixel.wp.pl/w/tr.js','dmq');
dmq('init', 'DOMODI_SHOP_KEY');
</script>

a dalej interesuje nas już tylko odpowiednie wywołanie tej funkcji i przekazanie jej pożądanego zestawu danych.

Zastanawiałem się jak to ugryźć. Można by to zrobić za pomocą JavaScriptu, wszak zestaw potrzebnych danych dało by się wyłuskać z kodu HTML danej strony. Jednak jakoś wolałem spróbować zrobić to od backendu i za pomocą WordPressowych i WooCommercowych action_hooków, metod oraz czystego PHP zaimplementować odpowiedni fragment kodu do gotowego dokumentu HTML.

Zaletą tego rozwiązania jest też jego trwałość – wydobyte w ten sposób dane będą zawsze spójne i nic ich nie zmodyfikuje na frontendzie, metoda ta jest niewrażliwa na zmiany klas czy identyfikatorów w kodzie HTML tak więc mamy pewność, że nie zależnie od szablonu czy innych zmiennych, zawsze dostaniemy ten sam zestaw danych.

Jakiego zestawu danych potrzebuje Domodi WPH Pixel?

Według manuala opisującego integrację WPH Pixel z platformami innymi niż Shopper czy Shoplo (link: https://domodi.pl/docs/InstallationGuide_WPHPixel.pdf), potrzebujemy wydobyć dane dla następujących akcji:

  • Strona z podsumowaniem zamówienia
  • Dodanie produktu do koszyka
  • Widok szczegółowy produktu
  • Wyświetlanie listy produktów z danej kategorii

Do każdej z powyższych akcji są potrzebne dane takie jak: id produktu, cena, ilość, stan magazynowy, lista wariantów itp. Za chwilę przejdziemy do napisania i omówienia właściwego kodu, który pomoże te dane nam wydobyć.

Domodi WPH Pixel: tworzymy prostą wtyczkę z integracją

Stwierdziłem, że najłatwiej i najpraktyczniej będzie zapakować ten kawałek kodu w prostą wtyczkę. Co prawda można by spróbować umieścić kod w pliku functions.php albo próbować wkleić go w pliki szablonu, ale jest to mało praktyczne dla właściciela strony (w końcu kod może kiedyś przestać działać poprawnie, wtedy wystarczy wyłączyć wtyczkę). Również aktualizacja mogła by taki kod nadpisać (no chyba, że zrobilibyśmy tzw. Child Theme). Wtyczka to moim zdaniem najlepsza droga do wpakowania tego kawałka kodu do WordPressa 🙂

Kod zdarzenia na stronie podsumowania zamówienia

Jako pierwszą, napiszemy w PHP funkcję generującą wywołanie kodu dla akcji wyświetlania strony z podsumowaniem zamówienia. Kod jaki musimy wygenerować, prezentuje się tak:

dmq('track', 'Purchase', {
transaction_id: 'ZAMOWIENIE_ID1',
value: 90.00,
shipping_cost: 19.90,
discount_code: 'KOD15',
contents: [{
id: 'PRODUKT_ID1',
name: 'NAZWA_PRODUKTU1',
ean: 'PRODUKT_EAN_ID1',
price: 10.00,
quantity: 1,
in_stock: true
},
{
id: 'PRODUKT_ID2',
name: 'NAZWA_PRODUKTU2',
ean: 'PRODUKT_EAN_ID2',
price: 20.00,
quantity: 2,
in_stock: true
},
{
id: 'PRODUKT_ID3',
name: 'NAZWA_PRODUKTU3',
ean: 'PRODUKT_EAN_ID3',
price: 30.00,
quantity: 1,
in_stock: true
}]
});

Widzimy, że musimy tutaj przekazać pewien zestaw danych odnoście całego zamówienia (transaction_id, value, shipping_cost, discount_code) oraz listę (contents) w formacie JSON zawierającą odpowiednie dane dla każdego produktu (id, name, ean, price, quantity, in_stock).

Powyższe dane pomoże nam wydobyć poniższa funkcja PHP (w komentarzach do kodu omówione są poszczególne fragmenty i ich działanie):

//Dla ciekawskich: prefiks chms jest skrótem od nazwy firmy "Chcemiecstrone". Mocno rekomendowaną praktyką przy pisaniu własnego kodu w WordPress jest używanie unikalnych (w miarę możliwości) prefiksów aby uniknąć kolizji nazw.

function chms_get_checkout_summary($order_id)
{
    //Pobieramy dane o zamówieniu, $order_id jest przekazywane przy wywoływaniu akcji "woocommerce_thankyou" i umieszczamy je w tablicy $order_prev
    $order = wc_get_order($order_id);
    $order_prev['order_id'] = $order->get_id();
    $order_prev['order_netto'] = $order->get_subtotal();
    $order_prev['order_shipping_cost'] = $order->get_shipping_total();
    $order_prev['order_discount'] = (isset($order->get_coupon_codes()[0])) ? $order->get_coupon_codes()[0] : '';

    $products = $order->get_items(); //Pobieramy produkty wchodzące w skład zamówienia

    //W pętli budujemy tablicę zawierającą informacje na temat produktów z zamówienia, zmienna $i służy do czytelnego poindeksowania poszczególnych produktów
    $i = 0;
    foreach($products as $product)
    {
        $prod_details[$i]['prod_id'] = $product->get_product_id();
        $prod_details[$i]['prod_name'] = $product->get_name();
        $prod_details[$i]['prod_ean'] = wc_get_product($product->get_product_id())->get_sku();
        $prod_details[$i]['prod_price'] = $product->get_total();
        $prod_details[$i]['prod_quantity'] = $product->get_quantity();
        $prod_details[$i]['prod_in_stock'] = wc_get_product($product->get_product_id())->is_in_stock();
        $i++;
    }

    $prod_details_len = count($prod_details) - 1; //Pobieramy długość tablcy z produktami


    //Budujemy fragmenty wywołania funkcji z przekazaniem ogólnego podsumwania zamówienia
    $checkout_summary_json = "<script>";
    $checkout_summary_json .= "dmq('track', 'Purchase', {";
    $checkout_summary_json .= "transaction_id: '" .  $order_prev['order_id'] . "',";
    $checkout_summary_json .= "value: " . $order_prev['order_netto'] . ",";
    $checkout_summary_json .= "shipping_cost: " . $order_prev['order_shipping_cost'] . ",";
    $checkout_summary_json .= "discount_code: '" .  $order_prev['order_discount'] . "',";
    $checkout_summary_json .= "contents: [";

    //W pętli dodajemy kolejne produkty do listy
    for($i = 0; $i <= $prod_details_len; $i++)
    {
        $checkout_summary_json .= "{id: '" . $prod_details[$i]['prod_id'] . "',";
        $checkout_summary_json .= "name: '" . $prod_details[$i]['prod_name'] . "',";
        $checkout_summary_json .= "ean: '" . $prod_details[$i]['prod_ean'] . "',";
        $checkout_summary_json .= "price: " . $prod_details[$i]['prod_price'] . ",";
        $checkout_summary_json .= "quantity: " . $prod_details[$i]['prod_quantity'] . ",";
        $checkout_summary_json .=  "in_stock: ";
        $checkout_summary_json .= $prod_details[$i]['prod_in_stock'] ? 'true' : 'false' . ";";

        //Jeśli pętla dojdzie do ostatniego prodktu, usuwamy przecinek zza ostatniej klamry i przerywamy tworzenie listy produktów gdyż jest już gotowa
        if($prod_details_len == $i)
        {
            $checkout_summary_json .= "}";
            break;
        }
        $checkout_summary_json .= "},";
    }

    //Domykamy całe wywołanie
    $checkout_summary_json .= "]});";
    $checkout_summary_json .= "</script>";

    echo $checkout_summary_json; //Wyświetlamy je w kodzie
}
add_action('woocommerce_thankyou', 'chms_get_checkout_summary'); //Podpinamy się pod akcję "woocommerce_thankyou" która zawiera wszystkie informacje na temat zamówienia i jednocześnie wywołuje napisaną przez nas funckję.

Efektem działania napisanej powyżej funkcji jest wygenerowanie tego fragmentu kodu:

<script> dmq('track', 'Purchase', {
    transaction_id: '70',
    value: 100,
    shipping_cost: 17.95,
    discount_code: '',
    contents: [{
        id: '19',
        name: 'Mokasyny czarne',
        ean: 'mokaczarne',
        price: 45,
        quantity: 1,
        in_stock: true
    }, {
        id: '18',
        name: 'Trampki czarne',
        ean: 'tr123cz',
        price: 55,
        quantity: 1,
        in_stock: true
    }]
}); </script>

Kod zdarzenia pod akcję dodania produktu do koszyka

Kolejny zestaw danych jaki musimy wygenerować dla WPH Pixel Domodi to kod zbierający dane w momencie dodania produktu do koszyka. Wg. wytycznych kod prezentuje się tak:

dmq('track', 'AddToCart', {
id: 'PRODUKT_ID1',
name: 'NAZWA_PRODUKTU1',
content_category: 'NAZWA_KATEGORII',
price: 89.99,
sizes: ['s'],
quantity: 1
});

Czyli ponownie wymagany jest podstawowy zestaw danych na temat produktu (id, name, content_category, price, sizes, quantity). Obsługę akcji dodania do koszyka osiągniemy to za pomocą poniższego kawałka kodu PHP:

function chms_get_product_added_to_cart($id)
{
    $added_to_cart_arr = array(); // Inicjujemy tablicę do przechowywania danych

    $cart = WC()->cart->get_cart()[$id];

    //Pobierany dane na temat koszyka
    $single_product_cat = wc_get_product($cart['product_id'])->get_category_ids()[0]; //Pobieramy kategorię produktu
    $added_to_cart_arr['product_id'] = $cart['product_id'];
    $added_to_cart_arr['name'] = wc_get_product($cart['product_id'])->get_name();
    $added_to_cart_arr['cat_name'] = get_term_by('id', $single_product_cat, 'product_cat')->name;
    $added_to_cart_arr['price'] = wc_get_product($cart['product_id'])->get_price();
    $added_to_cart_arr['sizes'] = '';
    $added_to_cart_arr['quantity'] = $cart['quantity'];

    $cart_variations_count = count($cart['variation']) - 1; //Obliczamy długość tablicy z wariantami

    $i = 0;
    //W pętli budujemy listę wariantów
    foreach($cart['variation'] as $variation)
    {
        if($i == $cart_variations_count)
        {
            $added_to_cart_arr['sizes'] .= $variation;
            break;
        }
        $i++;
        $added_to_cart_arr['sizes'] .= $variation . ', ';
    }

    //Tworzymy kod wywołania
    $added_to_cart_json = "<script>";
    $added_to_cart_json .= "dmq('track', 'AddToCart',{";
    $added_to_cart_json .= "id: '" . $added_to_cart_arr['product_id'] . "', ";
    $added_to_cart_json .= "content_category: '" . $added_to_cart_arr['name'] . "', ";
    $added_to_cart_json .= "name: '" . $added_to_cart_arr['cat_name'] . "', ";
    $added_to_cart_json .= "price: '" . $added_to_cart_arr['price'] . "', ";
    $added_to_cart_json .= "sizes: '" . $added_to_cart_arr['sizes'] . "', ";
    $added_to_cart_json .= "quantity: '" . $added_to_cart_arr['quantity'] . "'";
    $added_to_cart_json .= "});";
    $added_to_cart_json .= "</script>";

    add_action('wp_footer', function() use ($added_to_cart_json){echo $added_to_cart_json;}); // Doklejamy kod w okolice stopki
}
add_action('woocommerce_add_to_cart', 'chms_get_product_added_to_cart'); // Podpinamy się z naszą funkcją pod akcję dodania produktu do koszyka

Po dodaniu do koszyka w źródle naszej strony powinien pojawić się taki kod:

<script > dmq('track', 'AddToCart', {
    id: '18',
    content_category: 'Trampki czarne',
    name: 'Trampki',
    price: '55',
    sizes: ['34'],
    quantity: '1'
}); 
</script>

Kod zdarzenia na widoku szczegółowym produktu

Tutaj zajmiemy się generowaniem kodu WPH Pixel Domodi dla widoku szczegółowego produktu. Innymi słowy – akcja wywoływana jest kiedy ktoś po prostu ogląda sobie dany towar na naszym sklepie. Kod jaki musimy wygenerować dla Pixela wygląda tak:

dmq('track', 'ViewContent', {
content_type: 'product',
id: 'PRODUKT_ID',
name: 'NAZWA_PRODUKTU',
content_category: 'NAZWA_KATEGORII',
price: 89.99,
sizes: ['s', 'm', 'l', 'xl'],
in_stock: true
});

Znowu pobieramy standardowy zestaw podstawowych informacji na temat danego produktu (id, name, content_category, price, sizes, in_stock) a osiągamy to za pomocą tego kawałka kodu PHP:

function chms_get_single_product_detail()
{
    if(is_product()) //Sprawdzamy czy przegladana treść jest produktem
    {
        $single_product = wc_get_product(get_the_ID()); //Inicjujemy obiekt pojedynczego produktu
        
        //Pobieramy potrzebne informacje na temat produktu
        $single_product_id = $single_product->get_id();
        $single_product_title = $single_product->get_title();
        $single_product_cat = $single_product->get_category_ids()[0];
        $single_product_cat_name = get_term_by('id', $single_product_cat, 'product_cat')->name;
        $single_product_price = $single_product->get_price();
        $single_product_in_stock = $single_product->is_in_stock();
        if($single_product->is_type('variable')) $single_product_variations = $single_product->get_available_variations();

        //Tworzymy string z wywołaniem funkcji Pixela
        $single_product_json = '<script>';
        $single_product_json .= "dmq('track', 'ViewContent', {content_type: 'product',";
        $single_product_json .= "id: '" . $single_product_id . "',";
        $single_product_json .= "name: '" . $single_product_title . "',";
        $single_product_json .= "content_category: '" . $single_product_cat_name . "',";
        $single_product_json .= "price: '" . $single_product_price . "',";
        $single_product_json .= "sizes: [";

        //Sprawdzamy czy produkt ma warianty
        if($single_product->is_type('variable'))
        {
            //Jeśli ma warianty to w pętli tworzymy ich listę, która jest doklejana do stringa
            $single_product_variations_len = count($single_product_variations) - 1;
            for($i = 0; $i <= $single_product_variations_len; $i++)
            {
                //Jeśli osiągneliśmy ostatni wariant z listy, to zamykamy ją i usuwamy przecinek z jej końca
                if($single_product_variations_len === $i)
                {
                    $single_product_json .= "'" . $single_product_variations[$i]['attributes']['attribute_rozmiar'] . "'"; //WAŻNE!!!!!!!!!! Tutaj musimy podać nasz indywidualny sufiks dla atrybutów
                    break;
                }
                $single_product_json .= "'" . $single_product_variations[$i]['attributes']['attribute_rozmiar'] . "',";
            }
        }

        //Domykamy string z wywołaniem
        $single_product_json .= "],";
        $single_product_json .= "in_stock: ";
        $single_product_json .= $single_product_in_stock ? 'true' : 'false';
        $single_product_json .= "});";
        $single_product_json .= '</script>';

        echo $single_product_json; //Echujemy string
    }
}
add_action('wp_footer', 'chms_get_single_product_detail'); //Podpinamy wywołanie funkcji pod stopkę WordPressa

Powyższy kod generuje takie oto wywołanie:

<script > dmq('track', 'ViewContent', {
    content_type: 'product',
    id: '18',
    name: 'Trampki czarne',
    content_category: 'Trampki',
    price: '55',
    sizes: ['34'],
    in_stock: true
}); 
</script>

Kod zdarzenia na stronie listy produktów

Ostatnim z wymaganych kodów śledzenia (jednocześnie wymagającym najwięcej roboty od strony PHP) WPH Pixel Domodi jest zdarzenie podglądu listy produktów, dokładniej mówiąc chodzi o podgląd kategorii produktów, czyli np. „Mokasyny”. Tutaj potrzebujemy wywołać funkcję i przekazać do niej listę aktualnie wyświetlonych produktów z danej kategorii:

dmq('track', 'ViewContent', {
content_type: 'category',
name: 'NAZWA_KATEGORII',
contents: [{
id: 'PRODUKT_ID1',
name: 'NAZWA_PRODUKTU1',
ean: 'PRODUKT_EAN_ID1',
price: 20.15,
in_stock: true
},
{
id: 'PRODUKT_ID2',
name: 'NAZWA_PRODUKTU2',
ean: 'PRODUKT_EAN_ID2',
price: 10.15,
in_stock: true
},
{
id: 'PRODUKT_ID3',
name: 'NAZWA_PRODUKTU3',
ean: 'PRODUKT_EAN_ID3',
price: 30.15,
in_stock: true
}]
});

Znowu musimy pobrać podstawowe dane na temat produktów, czyli: (id, name, ean, price, in_stock). Pomoże nam w tym ten kod PHP:

function chms_get_product_list_in_category()
{
    if(is_product_category())
    {
        $product_ids = array(); //Inicjujemy pustą tablicę do przechowywania numerów ID aktualnie wyświetlanych produktów

        //Za pomocą WordPressowej pętli, sprawdzamy czy są jakieś posty do wyświetlenia
        if(have_posts()) :
            while (have_posts()) : the_post();
                //Jeśli są, dodajemy ich ID do wcześniej utworzonej tablicy
                $product_ids[] = get_the_ID();
            endwhile;
        else :
            echo 'No posts!';
        endif;

        if(count($product_ids) > 0) //Jeśli nasza tablica zawiera chociaż jeden element, przechodzimy przez nią
        {
            $product_ids_len = count($product_ids) - 1;//Obliczamy długość tablicy z numerami ID

            $products_arr = array(); //Inicjujemy pustą tablicę do przechowywania danych na temat produktów

            //W pętli odczytujemy dane na temat każdego z produktów i informacje te umieszczamy w zainicjowanej powyżej tablicy $products_arr
            for($i = 0; $i <= $product_ids_len; $i++)
            {
                $product = wc_get_product($product_ids[$i]);
                $prod_cats = $product->get_category_ids()[0];
                $prod_cats = get_term_by('id', $prod_cats, 'product_cat')->name;
                $products_arr[$i]['prod_cat_name'] = $prod_cats;
                $products_arr[$i]['prod_id'] = $product->get_id();
                $products_arr[$i]['prod_name'] = $product->get_name();
                $products_arr[$i]['prod_sku'] = $product->get_sku();
                $products_arr[$i]['prod_price'] = $product->get_price();
                $products_arr[$i]['prod_in_stock'] = $product->is_in_stock();
                $products_arr[$i]['prod_permalink'] = get_permalink($product_ids[$i]);
            }

            $products_arr_len = count($products_arr) - 1; //Obliczamy długośc tablicy z informacjami na temat produktów

            //Budujemy string z wywołaniem funkcji Pixela
            $products_arr_json = "<script>";
            $products_arr_json .= "dmq('track', 'ViewContent', {content_type: 'category',";
            $products_arr_json .= "name: '" . $products_arr[0]['prod_cat_name'] . ", contents: [";
            //W pętli odczytujemy tablicę z informacjami na temat produktów i doklejamy informacje na temat każdego z nich, do naszego stringa
            for($i = 0; $i <= $products_arr_len; $i++)
            {
                $products_arr_json .= "{id: '" . $products_arr[$i]['prod_id'] . "', ";
                $products_arr_json .= "name: '" . $products_arr[$i]['prod_name'] . "', ";
                $products_arr_json .= "ean: '" . $products_arr[$i]['prod_sku'] . "', ";
                $products_arr_json .= "price: '" . $products_arr[$i]['prod_price'] . "', ";
                $products_arr_json .= "in_stock: '";
                $products_arr_json .= $products_arr[$i]['prod_in_stock'] ? 'true' : 'false';
                //Jeśli dotarliśmy do ostantiego elementu tablicy to usuwamy przecinek
                if($products_arr_len == $i)
                {
                    $products_arr_json .= "'}";
                    break;
                }
                $products_arr_json .= "'}, ";
            }

            //Domykamy nasz string
            $products_arr_json .= "]});";
            $products_arr_json .= "</script>";

            echo $products_arr_json; //Echujemy cały string z wywołaniem funkcji Pixela
        }
    }
}
add_action('wp_footer', 'chms_get_product_list_in_category'); //Podpinamy funkcję w okolice stopki

Powyższa funkcja powinna nam wygenerować kod podobny do tego:

<script >
  dmq('track', 'ViewContent', {
  content_type: 'category',
  name: 'Mokasyny, contents: [

  {id: '17 ', 
  name: 'Buty mokasyny ', 
  ean: '321 ', 
  price: '100 ', 
  in_stock: 'true '}, 

  {id: '19 ', 
  name: 'Mokasyny czarne ', 
  ean: 'mokaczarne ', 
  price: '14.85 ', 
  in_stock: 'true '}
  ]});
</script>

Jak tego użyć?

Jak wspomniałem na początku artykułu: powyższe kawałki kodu PHP można wkleić w plik functions.php używanego przez nas motywu i to będzie działać, jednak w dalszej perspektywie nie jest to dobre rozwiązanie.

Można stworzyć wtyczkę składającą się tylko z jednego pliku i wkleić do niego wszystkie 4 powyższe fragmenty kodu a potem sprawdzić w źródle strony czy nasze funkcje wywołują się odpowiednio dla każdej akcji.

UWAGA! Powyższa metoda została przetestowana na sklepie, który MA WYŁĄCZONY Ajax przy dodawaniu produktu do koszyka.

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany.