Книга рецептов по расширению HPOS

Что такое High-Performance Order Storage (HPOS)?

До недавнего времени WooCommerce хранил данные о заказах в таблицах post и postmeta как пользовательский тип записи WordPress. Это позволяло использовать стандартные API WordPress для работы с заказами.

Однако в начале 2022 года было объявлено о переходе на отдельные таблицы для заказов. Это позволяет магазинам лучше масштабироваться, упрощает структуру данных и повышает надежность.

В целом WooCommerce старается сохранить обратную совместимость, но разработчикам расширений всё же нужно внести изменения, чтобы использовать HPOS. Это связано с тем, что структура хранения данных изменилась.

Теперь вместо API WordPress для работы с заказами нужно использовать API WooCommerce. Эти API были введены еще в WooCommerce 3.0, чтобы упростить переход.

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

Подробнее о включении/отключении HPOS и синхронизации данных можно узнать в документации HPOS.

Обратная совместимость

Чтобы упростить переход для владельцев магазинов и разработчиков, WooCommerce постарался сохранить максимальную обратную совместимость.

Основная проблема заключается в том, что раньше данные заказов хранились в таблицах wp_posts и wp_postmeta. Поэтому можно было напрямую работать с ними через API WordPress, обходя классы WooCommerce.

Теперь это изменилось. Прямое чтение из этих таблиц может возвращать устаревшие данные, а запись — не использоваться системой. Поэтому на переходный период добавлены некоторые механизмы совместимости.

Переключение источника данных

При необходимости можно вручную переключиться обратно на старый способ хранения заказов. Для этого перейдите в WooCommerce → Настройки → Дополнительно → Функции и измените настройку «Хранилище данных заказов».

Поддержка HPOS в вашем расширении

Несмотря на обратную совместимость, разработчикам расширений необходимо поддерживать оба варианта — и старое хранение (через записи), и HPOS.

Ниже приведены рекомендации, которые помогут адаптировать ваш код.

Рекомендуется использовать dev-версию WooCommerce при разработке, чтобы иметь доступ ко всем последним изменениям и API для HPOS.

Определение, используется ли HPOS

В большинстве случаев достаточно использовать CRUD API WooCommerce. Но если вы пишете собственные SQL-запросы, может понадобиться проверка, включён ли HPOS:

use Automattic\WooCommerce\Utilities\OrderUtil;

if ( OrderUtil::custom_orders_table_usage_is_enabled() ) {
    // Используется HPOS
} else {
    // Используется старое хранение заказов (через записи)
}

Проверка кода на прямой доступ к базе данных

Первый шаг — проверить код на использование прямых запросов к базе данных и функций WordPress, которые больше не подходят для работы с заказами.

Можно использовать следующее регулярное выражение для поиска:

wpdb|get_post|get_post_field|get_post_status|get_post_type|get_post_type_object|get_posts|metadata_exists|get_post_meta|get_metadata|get_metadata_raw|get_metadata_default|get_metadata_by_mid|wp_insert_post|add_metadata|add_post_meta|wp_update_post|update_post_meta|update_metadata|update_metadata_by_mid|delete_metadata|delete_post_meta|delete_metadata_by_mid|delete_post_meta_by_key|wp_delete_post|wp_trash_post|wp_untrash_post|wp_transition_post_status|clean_post_cache|update_post_caches|update_postmeta_cache|post_exists|wp_count_post|shop_order

После поиска:

  1. Проверьте каждое совпадение — связано ли оно с заказами.
  2. Если да — перепишите код с использованием API WooCommerce.

API для работы с заказами и метаданными

Если вы получаете заказ через функции WordPress, замените это на вызов WooCommerce:

// Вместо
$post = get_post( $post_id ); // Возвращает WP_Post

// Используйте
$order = wc_get_order( $post_id ); // Возвращает WC_Order

Для работы с метаданными используйте методы объекта заказа:

// Вместо функций WordPress:
update_post_meta( $post_id, $meta_key_1, $meta_value_1 );
add_post_meta( $post_id, $meta_key_2, $meta_value_2 );
delete_post_meta( $post_id, $meta_key_3, $meta_value_3 );

// Используйте:
$order = wc_get_order( $post_id );

$order->update_meta_data( $meta_key_1, $meta_value_1 );
$order->add_meta_data( $meta_key_2, $meta_value_2 );
$order->delete_meta_data( $meta_key_3, $meta_value_3 );

$order->save();

WooCommerce сам определит, какие таблицы используются, и сохранит данные в нужное место.

( $meta_key_3, $meta_value_3 );
$order->save();

Примечание: вызов метода save() — это достаточно ресурсоёмкая операция. Старайтесь не вызывать его без необходимости (например, если он будет вызван позже в этом же процессе, можно избежать лишних вызовов при работе с тем же объектом).

Чтобы получить точный тип заказа или проверить, является ли указанный ID заказом, можно использовать методы класса OrderUtil.

// Шаблон для проверки, является ли ID заказом
'shop_order' === get_post_type( $post_id ); // или
in_array( get_post_type( $post_type ), wc_get_order_types() );

// заменить на:
use Automattic\WooCommerce\Utilities\OrderUtil;
'shop_order' === OrderUtil::get_order_type( $post_id ); // или
OrderUtil::is_order( $post_id, wc_get_order_types() );

Проверка функций экрана администрирования заказов

Так как WooCommerce не может использовать стандартные экраны списка записей и редактирования записей WordPress, были добавлены новые экраны для управления заказами. Они очень похожи на текущие экраны в админке WooCommerce (за исключением того, что используют таблицы HPOS). Для проверки можно использовать следующее регулярное выражение:

post_updated_messages|do_meta_boxes|enter_title_here|edit_form_before_permalink|edit_form_after_title|edit_form_after_editor|submitpage_box|submitpost_box|edit_form_advanced|dbx_post_sidebar|manage_shop_order_posts_columns|manage_shop_order_posts_custom_column

Здесь также будет много ложных срабатываний. Но если вы обнаружите использование этих методов для экрана заказов, для перехода на HPOS нужно внести следующие изменения:

Вместо объекта $post класса WP_Post необходимо использовать объект $order класса WC_Order. Если это фильтр или действие, в новом экране WooCommerce будет реализован аналогичный хук, который вместо объекта записи принимает объект WC_Order.

Пример ниже показывает, как добавить метабоксы в старый экран редактирования заказов (если используются устаревшие заказы) и в новый экран на базе HPOS:

use Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController;

add_action( 'add_meta_boxes', 'add_xyz_metabox' );

function add_xyz_metabox() {
    $screen = class_exists( '\Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController' ) && wc_get_container()->get( CustomOrdersTableController::class )->custom_orders_table_usage_is_enabled()
        ? wc_get_page_screen_id( 'shop-order' )
        : 'shop_order';

    add_meta_box(
        'xyz',
        'Пользовательский метабокс',
        'render_xyz_metabox',
        $screen,
        'side',
        'high'
    );
}

Также изменится параметр, передаваемый в метабокс — теперь это будет объект заказа. Поэтому в обработчиках метабоксов нужно учитывать, что может быть передан как объект записи, так и объект заказа. Рекомендуется получать объект заказа и работать уже с ним, а не с исходным параметром.

function render_xyz_metabox( $post_or_order_object ) {
    $order = ( $post_or_order_object instanceof WP_Post )
        ? wc_get_order( $post_or_order_object->ID )
        : $post_or_order_object;

    // ... остальной код. Ниже не используйте напрямую $post_or_order_object.
}

Объявление совместимости (или несовместимости) расширения

После анализа кода расширения вы можете указать, поддерживает ли оно HPOS. Для этого есть специальный API.

Чтобы объявить расширение совместимым, добавьте следующий код в основной файл плагина:

add_action( 'before_woocommerce_init', function() {
    if ( class_exists( \Automattic\WooCommerce\Utilities\FeaturesUtil::class ) ) {
        \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'custom_order_tables', __FILE__, true );
    }
} );

Если ваш код не поддерживает HPOS, объявите несовместимость. Добавьте следующий код в основной файл плагина:

add_action( 'before_woocommerce_init', function() {
    if ( class_exists( \Automattic\WooCommerce\Utilities\FeaturesUtil::class ) ) {
        \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'custom_order_tables', __FILE__, false );
    }
} );

Если вы хотите разместить объявление совместимости не в основном файле плагина, укажите вместо __FILE__ путь вида 'my-plugin-slug/my-plugin.php'.

WooCommerce предупредит пользователей, если они попытаются включить HPOS при активных несовместимых плагинах. Также в разделе «Плагины» будет показано уведомление о несовместимости.

Поскольку многие плагины WordPress не связаны с WooCommerce, информация будет отображаться только для тех расширений, у которых в заголовке основного файла указано WC tested up to.

Новые API для запросов заказов

HPOS через WC_Order_Query добавляет новые типы запросов, позволяющие выполнять более сложные выборки заказов по датам, метаданным и полям заказа. Подробнее и с примерами смотрите в статье: HPOS: новые API для запросов заказов.

Поделиться с друзьями
Документация WooCommerce