Расширение Woocommerce Analytics
Woocommerce Analytics — это инструмент отчетности и анализа данных, который отображает статистику в виде таблиц и графиков и однажды появилась задача расширить функционал данного инструмента. К сожалению в интернете очень мало информации на текущий момент по возможности взаимодействия с данным функционалом, что побудило меня написать данную статью.
Оглавление
Формат
Чтобы начать работать с Woocommerce Analytics нужно создать расширение для Woocommerce, тут есть инструкция, но мы все же пройдемся по основным моментам чтобы было понятнее.
Располагается расширение точно так же в папке /wp-content/plugins/
и имеет такую структуру:
Самое любопытное то что создается расширение при помощи репозитория Woocommerce admin для этого клонируем данный репозиторий в папку /wp-content/plugins/
командой
git clone git@github.com:woocommerce/woocommerce-admin.git
После чего переходим в папку с только что склонированным проектом cd woocommerce-admin
, устанавливаем node зависимости и собираем проект
npm install
npm run build
После сборки для создания расширения используем команду
npm run create-wc-extension
После чего можете зайти в папку со своим созданным расширением запустите установку node зависимостей и сборку
npm install
npm start
и увидите следующие файлы:
Как вы можете видеть пакет Woocommerce admin создает все что нужно для создания нового расширения под woocommerce что крайне удобно.
Пример 1
Далее действуем по обычной схеме для плагинов — в основном php файле нашего расширения используя хук admin_enqueue_scripts
подключаем файлы скриптов и стилей которые будет собирать наш сборщик:
function add_extension_register_script() {
if ( ! class_exists( 'Automattic\WooCommerce\Admin\Loader' ) || ! \Automattic\WooCommerce\Admin\Loader::is_admin_or_embed_page() ) {
return;
}
$script_path = '/build/index.js';
$script_asset_path = dirname( __FILE__ ) . '/build/index.asset.php';
$script_asset = file_exists( $script_asset_path )
? require( $script_asset_path )
: array( 'dependencies' => array(), 'version' => filemtime( $script_path ) );
$script_url = plugins_url( $script_path, __FILE__ );
wp_register_script(
'woocommerce-analytics-data',
$script_url,
$script_asset['dependencies'],
$script_asset['version'],
true
);
wp_register_style(
'woocommerce-analytics-data',
plugins_url( '/build/index.css', __FILE__ ),
array(), // Add any dependencies styles may have, such as wp-components.
filemtime( dirname( __FILE__ ) . '/build/index.css' )
);
wp_enqueue_script( 'woocommerce-analytics-data' );
wp_enqueue_style( 'woocommerce-analytics-data' );
}
add_action( 'admin_enqueue_scripts', 'add_extension_register_script' );
Далее покажем простейший пример как добавить новый столбец в статистику по пользователям:
import {addFilter} from '@wordpress/hooks';
addFilter(
'woocommerce_admin_report_table',
'woocommerce',
(reportTableData) => {
if (reportTableData.endpoint !== 'customers') {
return reportTableData;
}
reportTableData.headers = [
...reportTableData.headers,
{
label: 'Customer handle',
key: 'customer_id',
},
];
if (
!reportTableData.items ||
!reportTableData.items.data ||
!reportTableData.items.data.length
) {
return reportTableData;
}
reportTableData.rows = reportTableData.rows.map((row, index) => {
const customer = reportTableData.items.data[index];
const link = React.createElement("a", {
href: "https://app.reepay.com/#/rp/customers/customers/customer/customer-" + customer.user_id,
target: "_blank"
},
'customer-' + customer.user_id
);
return [
...row,
{
display: link,
value: 'customer-' + customer.user_id,
},
];
});
return reportTableData;
}
);
Давайте разберем данный пример:
- Так же как и через PHP добавляем функцию на хук
woocommerce_admin_report_table
- Проверяем является ли текущая страница аналитики таблицей с пользователями используя переменную
reportTableData.endpoint
- Добавляем новый столбец в таблицу с аналитикой по пользователям добавляя значение в массив
reportTableData.headers
- Делаем проверку есть ли в объекте
reportTableData
в принципе значения для вывода - Создаем ссылку которая будет выведена в виде данных для каждого пользователя в статистике используются инструменты React
- Добавляем данные в новый столбец добавляя в массив
row
значенияdisplay
— созданная нами ранее ссылка иvalue
— собственно само значение для данного столбца
Таким образом мы добавили в таблицу аналитики новый столбец Customer handle в который вывели новый идентификатор пользователя при нажатии на который откроется страница на стороннем сервисе с информацией по этому пользователю
Пример 2
Сейчас попробуем рассмотреть более сложный пример с использованием бэкенда и дополнительных джоинов
Перед нами стоит задача добавить новые данные в таблицу с аналитикой по заказам, но самое не просто в том что нужно добавлять данные которые находятся в других таблицах и по умолчанию их никак не получить в таблице с заказами, для этого нам придется зацепится за хук MySql запроса к базе данных заказов и сделать JOIN других таблиц для вывода.
Для этого используем 3 фильтра:
woocommerce_analytics_clauses_join_orders_subquery
woocommerce_analytics_clauses_join_orders_stats_total
woocommerce_analytics_clauses_join_orders_stats_interval
которые будут ссылаться на одну и ту же функцию. К примеру запрос для получения Customer ID выглядит так:
$clauses[] = "JOIN {$wpdb->prefix}wc_customer_lookup user_customer ON {$wpdb->prefix}wc_order_stats.customer_id = user_customer.customer_id";
А вот так выглядит функция в целом:
/**
* Add a JOIN clause.
*
* @param array $clauses an array of JOIN query strings.
*
* @return array augmented clauses.
*/
function add_join_subquery( $clauses ) {
global $wpdb;
$clauses[] = "JOIN {$wpdb->prefix}wc_customer_lookup user_customer ON {$wpdb->prefix}wc_order_stats.customer_id = user_customer.customer_id";
$clauses[] = "LEFT JOIN {$wpdb->usermeta} user_meta ON user_meta.user_id = user_customer.user_id AND user_meta.meta_key = 'wp_capabilities'";
$clauses[] = "LEFT JOIN {$wpdb->usermeta} user_meta_company ON user_meta_company.user_id = user_customer.user_id AND user_meta_company.meta_key = 'billing_company'";
$clauses[] = "LEFT JOIN {$wpdb->postmeta} post_meta_billing ON post_meta_billing.post_id = {$wpdb->prefix}wc_order_stats.order_id AND post_meta_billing.meta_key = '_payment_method_title'";
$clauses[] = "LEFT JOIN {$wpdb->postmeta} post_meta_shipping ON post_meta_shipping.post_id = {$wpdb->prefix}wc_order_stats.order_id AND post_meta_shipping.meta_key = '_order_shipping'";
$clauses[] = "LEFT JOIN {$wpdb->postmeta} post_meta_discount ON post_meta_discount.post_id = {$wpdb->prefix}wc_order_stats.order_id AND post_meta_discount.meta_key = '_cart_discount'";
$clauses[] = "LEFT JOIN {$wpdb->prefix}wc_order_coupon_lookup coupons ON {$wpdb->prefix}wc_order_stats.order_id = coupons.order_id";
$clauses[] = "LEFT JOIN {$wpdb->posts} post_coupon ON post_coupon.ID = coupons.coupon_id";
return $clauses;
}
add_filter( 'woocommerce_analytics_clauses_join_orders_subquery', 'add_join_subquery' );
add_filter( 'woocommerce_analytics_clauses_join_orders_stats_total', 'add_join_subquery' );
add_filter( 'woocommerce_analytics_clauses_join_orders_stats_interval', 'add_join_subquery' );
Далее сделав JOIN всех нужных нам таблиц нужно обозначить нужные нам значения уникальными названиями чтобы потом их можно было использовать в самой таблице аналитики. Для этого используем так же 3 хука:
woocommerce_analytics_clauses_select_orders_subquery
woocommerce_analytics_clauses_select_orders_stats_total
woocommerce_analytics_clauses_select_orders_stats_interval
которые так же будут ссылаться на одну функцию которая выглядит следующим образом:
/**
* Add a SELECT clause.
*
* @param array $clauses an array of WHERE query strings.
* @return array augmented clauses.
*/
function add_select_subquery( $clauses ) {
$clauses[] = ', user_meta.meta_value AS customer_role';
$clauses[] = ', user_meta_company.meta_value AS billing_company';
$clauses[] = ', post_meta_billing.meta_value AS payment_method';
$clauses[] = ', post_meta_shipping.meta_value AS order_shipping';
$clauses[] = ', post_meta_discount.meta_value AS order_discount';
$clauses[] = ', post_coupon.post_title AS coupon';
return $clauses;
}
add_filter( 'woocommerce_analytics_clauses_select_orders_subquery', 'add_select_subquery' );
add_filter( 'woocommerce_analytics_clauses_select_orders_stats_total', 'add_select_subquery' );
add_filter( 'woocommerce_analytics_clauses_select_orders_stats_interval', 'add_select_subquery' );
Теперь перейдем в наш файл со скриптом и посмотрим что изменилось:
import './index.scss';
/**
* External dependencies
*/
import {addFilter} from '@wordpress/hooks';
addFilter(
'woocommerce_admin_report_table',
'woocommerce',
(reportTableData) => {
if (reportTableData.endpoint !== 'orders') {
return reportTableData;
}
reportTableData.headers = [
...reportTableData.headers,
{
label: 'Customer ID',
key: 'customer_id',
},
{
label: 'Customer Role',
key: 'customer_role',
},
{
label: 'Company',
key: 'billing_company',
},
{
label: 'Payment method',
key: 'payment_method',
},
{
label: 'Shipping',
key: 'order_shipping',
},
{
label: 'Discount',
key: 'order_discount',
},
{
label: 'Coupon',
key: 'coupon',
},
{
label: 'Subtotal',
key: 'subtotal',
},
];
if (
!reportTableData.items ||
!reportTableData.items.data ||
!reportTableData.items.data.length
) {
return reportTableData;
}
reportTableData.rows = reportTableData.rows.map((row, index) => {
const order = reportTableData.items.data[index];
var data_role = order.customer_role;
var role = '';
Object.keys(roles_list).forEach(function (item, i, arr) {
if (data_role.indexOf(item) !== -1) {
role = item;
}
});
var subtotal = order.total_sales - order.order_discount - order.order_shipping;
return [
...row,
{
display: order.extended_info.customer.user_id,
value: order.extended_info.customer.user_id,
},
{
display: role,
value: role,
},
{
display: order.billing_company,
value: order.billing_company,
},
{
display: order.payment_method,
value: order.payment_method,
},
{
display: order.order_shipping,
value: order.order_shipping,
},
{
display: order.order_discount,
value: order.order_discount,
},
{
display: order.coupon,
value: order.coupon,
},
{
display: subtotal,
value: subtotal,
},
];
});
return reportTableData;
}
);
В принципе суть не изменилась, изменилось количество данных для вывода и то что эти данные получаются все из разных таблиц и все переменные которым мы присваивали уникальные названия в MySql выборке теперь доступны в объекте order.
Также в данном примере передается массив данных напрямую в скрипт, но тут мы опять же используем все так же как при работе с плагинами wp_localize_script('woocommerce-analytics-data', 'roles_list', $roles);
Для того чтобы посмотреть внесенные изменения нужно собрать проект используя npm run build
и очистить кэш аналитики (без этого можно долго искать что же вы сделали не так) перейдя в панели администратора WooCommerce->Status->Tools(таб) вы увидите опцию Clear analytics cache:
Заключение
По моему мнению сам процесс создания расширения довольно удобный, хоть и не очень простой (особенно для backend разработчика). Работа с Woocommerce Analytics сильно отличается в принципе от остальных элементов панели администратора WP и WooCommrece разве что кроме Gutenberg и лично для меня разработка на первый взгляд такой простой фичи стало целым испытанием, во многом потому что в интернете очень мало информации и примеров данной кастомизации. Но если разобраться глубже и иметь хотя бы небольшой опыт работы с React и сборщиками то это не составит для вас особого труда и надеюсь данная статья этому поспособствует.
Комментирование этой и других статей доступно в нашем Телеграм канале