С выходом WooCommerce 7.x был представлен новый механизм хранения заказов — High-Performance Order Storage (HPOS). Это значительно изменило процесс работы с заказами, так как теперь они могут храниться не в таблице wp_posts, а в специально созданной для этого таблице wc_orders. Это улучшает производительность, особенно на крупных магазинах.
Однако разработчики, создающие свои собственные решения для работы с заказами, могут столкнуться с проблемой: при использовании кастомных классов для работы с заказами нужно правильно обрабатывать сценарии с включенным или выключенным HPOS, чтобы гарантировать, что данные будут сохраняться в правильные места.
В этой статье мы рассмотрим, как реализовать динамическое переключение между различными системами хранения заказов (стандартное через wp_posts и HPOS) с учетом принципов ООП.
Проблема
Когда вы создаете собственный класс заказа, например, расширяя класс WC_Order, вы можете столкнуться с рядом проблем при попытке сохранить заказ или кастомные данные (например, posting_number, tracking_number). Основная проблема возникает в том, что ваш заказ может не отображаться в списке заказов WooCommerce в админке, особенно если вы используете High-Performance Order Storage (HPOS). Либо, если он отображается, кастомные поля могут не сохраняться должным образом.
Рассмотрим пример кастомного класса Order, который наследуется от WC_Order:
class Order extends \WC_Order {
protected $data_store_name = 'my-custom-order-data-store';
protected $extra_data = [
'posting_number' => null,
'tracking_number' => null
];
// Здесь ваши методы работы с заказом
}
Проблема 1: Заказ не отображается в списке заказов
Когда мы создаем экземпляр этого класса:
$order = new Order();
$order->save();
Заказ сохраняется, но не отображается в списке заказов WooCommerce. Это происходит потому, что при включенной опции HPOS (High-Performance Order Storage) данные сохраняются только в таблицу wp_posts, а не в новую таблицу wc_orders, которая отвечает за хранение заказов в HPOS.
Проблема 2: Кастомные данные не сохраняются
Если вы измените data_store_name на 'order', заказ будет отображаться в списке WooCommerce:
class Order extends \WC_Order {
protected $data_store_name = 'order'; // Используем стандартный data store
}
Однако в этом случае другая проблема заключается в том, что ваши кастомные поля, такие как posting_number, tracking_number, и другие данные, определенные в extra_data, не будут сохраняться, так как они не будут обработаны стандартным data store WooCommerce.
Эти проблемы возникают из-за того, что разные системы хранения данных WooCommerce — классическое хранение через wp_posts и новое через HPOS — требуют разного подхода к сохранению и отображению заказов. Включение HPOS вводит новую структуру хранения, а кастомный data store должен быть совместим как с новой, так и со старой системой.
Решение: Динамическое переключение между системами хранения
Для решения этих проблем мы предложим гибкое решение с использованием кастомного data store, который сможет динамически переключаться между двумя системами хранения заказов, чтобы:
- Заказы всегда отображались в WooCommerce, независимо от того, включен HPOS или нет.
- Кастомные данные, такие как posting_number, tracking_number, сохранялись корректно.
Пример кода для кастомного data store
Создадим кастомный класс WC_My_Custom_Order_Data_Store_CPT, который наследуется от WC_Order_Data_Store_CPT для работы с системой хранения через wp_posts:
class WC_My_Custom_Order_Data_Store_CPT extends WC_Order_Data_Store_CPT {
protected $meta_keys = [
'_posting_number' => 'posting_number',
'_tracking_number' => 'tracking_number',
// Добавляем кастомные мета-ключи
];
// Методы сохранения и загрузки данных кастомных полей
}
Также создаем класс для работы с HPOS, который наследуется от OrdersTableDataStore:
class WC_My_Custom_Order_Data_Store_COT extends OrdersTableDataStore {
// Здесь вы можете добавить логику для работы с кастомными полями при HPOS
}
Этот класс может содержать любую дополнительную логику для работы с HPOS, если это необходимо в вашем проекте. Основная задача — использовать методы родительского класса OrdersTableDataStore, чтобы гарантировать корректную работу с новой таблицей заказов wc_orders.
Используя фильтры WooCommerce, мы можем динамически переключаться между этими двумя системами хранения:
add_filter( 'woocommerce_data_stores', function( $stores ) {
$stores['my-custom-order-data-store'] = 'WC_My_Custom_Order_Data_Store_CPT';
return $stores;
});
Здесь мы добавляем новый тип data store под названием my-custom-order-data-store, который ссылается на наш кастомный класс WC_My_Custom_Order_Data_Store_CPT. Этот класс должен поддерживать хранение заказов через wp_posts и, следовательно, наследуется от WC_Order_Data_Store_CPT.
add_filter( 'woocommerce_my-custom-order-data-store_data_store', function( $default_data_store ) {
return wc_get_container()->get( Automattic\WooCommerce\Internal\DataStores\Orders\OrdersTableController::class )->custom_orders_table_usage_is_enabled()
? get_my_custom_data_store_instance()
: $default_data_store;
});
Здесь используется встроенный контейнер зависимостей WooCommerce (wc_get_container()), чтобы получить текущее состояние HPOS. Если HPOS включен, мы используем наш кастомный data store для HPOS, а если отключен — возвращаем стандартный default_data_store, который работает через wp_posts.
И, наконец, создаем функцию для получения правильного экземпляра data store:
function get_my_custom_data_store_instance() {
$data_store = new WC_My_Custom_Order_Data_Store_COT();
$data_store->init(
wc_get_container()->get( \Automattic\WooCommerce\Internal\DataStores\Orders\OrdersTableDataStoreMeta::class ),
wc_get_container()->get( \Automattic\WooCommerce\Internal\Utilities\DatabaseUtil::class ),
wc_get_container()->get( \Automattic\WooCommerce\Proxies\LegacyProxy::class )
);
return $data_store;
}
В этой функции мы создаем экземпляр нашего кастомного класса WC_My_Custom_Order_Data_Store_COT, который наследуется от OrdersTableDataStore. Мы также используем контейнер зависимостей WooCommerce для получения нужных классов (OrdersTableDataStoreMeta, DatabaseUtil, LegacyProxy) и их передачи в наш data store через метод init().
Преимущества данного подхода
- Динамическое переключение: Решение автоматически определяет, включен ли HPOS, и выбирает правильный data store в зависимости от этого.
- Четкое разделение обязанностей: Кастомные классы для работы с заказами полностью изолированы. Это облегчает поддержку и расширение.
- Использование лучших практик ООП: Мы используем наследование и внедрение зависимостей через контейнер, что улучшает тестируемость и гибкость решения.
- Поддержка обеих систем хранения: Наше решение сохраняет совместимость как с классической системой хранения заказов через wp_posts, так и с новой системой HPOS.
Заключение
Предложенное решение помогает разработчикам эффективно работать с заказами в WooCommerce, независимо от того, какую систему хранения заказов использует магазин. Динамическое переключение между системами хранения, использование контейнеров зависимостей и принципы ООП делают решение гибким, расширяемым и легко поддерживаемым.
Если вы разрабатываете плагины или кастомные решения для WooCommerce и хотите, чтобы ваше решение поддерживало HPOS, этот подход поможет вам справиться с переключением между системами хранения заказов без проблем.