203 lines
6.5 KiB
PHP
203 lines
6.5 KiB
PHP
<?php
|
|
|
|
declare( strict_types=1 );
|
|
|
|
namespace Automattic\WooCommerce\Caches;
|
|
|
|
use WC_Order;
|
|
use Automattic\WooCommerce\Enums\OrderStatus;
|
|
use Automattic\WooCommerce\Utilities\OrderUtil;
|
|
|
|
/**
|
|
* A service class to help with updates to the aggregate orders cache.
|
|
*
|
|
* @internal
|
|
*/
|
|
class OrderCountCacheService {
|
|
|
|
const BACKGROUND_EVENT_HOOK = 'woocommerce_refresh_order_count_cache';
|
|
|
|
/**
|
|
* OrderCountCache instance.
|
|
*
|
|
* @var OrderCountCache
|
|
*/
|
|
private $order_count_cache;
|
|
|
|
/**
|
|
* Array of order ids with their last transitioned status as key value pairs.
|
|
*
|
|
* @var array
|
|
*/
|
|
private $order_statuses = array();
|
|
|
|
/**
|
|
* Array of order ids with their initial status as key value pairs.
|
|
*
|
|
* @var array
|
|
*/
|
|
private $initial_order_statuses = array();
|
|
|
|
/**
|
|
* Class initialization, invoked by the DI container.
|
|
*
|
|
* @internal
|
|
*/
|
|
final public function init() {
|
|
$this->order_count_cache = new OrderCountCache();
|
|
add_action( 'woocommerce_new_order', array( $this, 'update_on_new_order' ), 10, 2 );
|
|
add_action( 'woocommerce_order_status_changed', array( $this, 'update_on_order_status_changed' ), 10, 4 );
|
|
add_action( 'woocommerce_before_trash_order', array( $this, 'update_on_order_trashed' ), 10, 2 );
|
|
add_action( 'woocommerce_before_delete_order', array( $this, 'update_on_order_deleted' ), 10, 2 );
|
|
add_action( self::BACKGROUND_EVENT_HOOK, array( $this, 'refresh_cache' ) );
|
|
add_action( 'action_scheduler_ensure_recurring_actions', array( $this, 'schedule_background_actions' ) );
|
|
|
|
if ( defined( 'WC_PLUGIN_BASENAME' ) ) {
|
|
add_action( 'deactivate_' . WC_PLUGIN_BASENAME, array( $this, 'unschedule_background_actions' ) );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Refresh the cache for a given order type.
|
|
*
|
|
* @param string $order_type The order type.
|
|
* @return void
|
|
*/
|
|
public function refresh_cache( $order_type ) {
|
|
$this->order_count_cache->flush( $order_type );
|
|
OrderUtil::get_count_for_type( $order_type );
|
|
}
|
|
|
|
/**
|
|
* Register background caching for each order type.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function schedule_background_actions() {
|
|
$order_types = wc_get_order_types( 'order-count' );
|
|
$frequency = HOUR_IN_SECONDS * 12;
|
|
foreach ( $order_types as $order_type ) {
|
|
as_schedule_recurring_action( time() + $frequency, $frequency, self::BACKGROUND_EVENT_HOOK, array( $order_type ), 'count', true );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Unschedules background actions.
|
|
*
|
|
* @since 10.0.0
|
|
* @internal
|
|
*/
|
|
public function unschedule_background_actions() {
|
|
WC()->queue()->cancel_all( self::BACKGROUND_EVENT_HOOK );
|
|
}
|
|
|
|
/**
|
|
* Update the cache when a new order is made.
|
|
*
|
|
* @param int $order_id Order id.
|
|
* @param WC_Order $order The order.
|
|
*/
|
|
public function update_on_new_order( $order_id, $order ) {
|
|
if ( ! $this->order_count_cache->is_cached( $order->get_type(), $this->get_prefixed_status( $order->get_status() ) ) ) {
|
|
return;
|
|
}
|
|
|
|
// If the order status was updated, we need to increment the order count cache for the
|
|
// initial status that was errantly decremented on order status change.
|
|
if ( isset( $this->initial_order_statuses[ $order_id ] ) ) {
|
|
$this->order_count_cache->increment( $order->get_type(), $this->get_prefixed_status( $this->initial_order_statuses[ $order_id ] ) );
|
|
}
|
|
|
|
// If the order status count has already been incremented, we can skip incrementing it again.
|
|
if ( isset( $this->order_statuses[ $order->get_id() ] ) && $this->order_statuses[ $order->get_id() ] === $order->get_status() ) {
|
|
return;
|
|
}
|
|
|
|
$this->order_statuses[ $order_id ] = $order->get_status();
|
|
$this->order_count_cache->increment( $order->get_type(), $this->get_prefixed_status( $order->get_status() ) );
|
|
}
|
|
|
|
/**
|
|
* Update the cache when an order is trashed.
|
|
*
|
|
* @param int $order_id Order id.
|
|
* @param WC_Order $order The order.
|
|
*/
|
|
public function update_on_order_trashed( $order_id, $order ) {
|
|
if (
|
|
! $this->order_count_cache->is_cached( $order->get_type(), $this->get_prefixed_status( $order->get_status() ) ) ||
|
|
! $this->order_count_cache->is_cached( $order->get_type(), OrderStatus::TRASH ) ) {
|
|
return;
|
|
}
|
|
|
|
$this->order_count_cache->decrement( $order->get_type(), $this->get_prefixed_status( $order->get_status() ) );
|
|
$this->order_count_cache->increment( $order->get_type(), OrderStatus::TRASH );
|
|
}
|
|
|
|
/**
|
|
* Update the cache when an order is deleted.
|
|
*
|
|
* @param int $order_id Order id.
|
|
* @param WC_Order $order The order.
|
|
*/
|
|
public function update_on_order_deleted( $order_id, $order ) {
|
|
if ( ! $this->order_count_cache->is_cached( $order->get_type(), $this->get_prefixed_status( $order->get_status() ) ) ) {
|
|
return;
|
|
}
|
|
|
|
$this->order_count_cache->decrement( $order->get_type(), $this->get_prefixed_status( $order->get_status() ) );
|
|
}
|
|
|
|
/**
|
|
* Update the cache whenver an order status changes.
|
|
*
|
|
* @param int $order_id Order id.
|
|
* @param string $previous_status the old WooCommerce order status.
|
|
* @param string $next_status the new WooCommerce order status.
|
|
* @param WC_Order $order The order.
|
|
*/
|
|
public function update_on_order_status_changed( $order_id, $previous_status, $next_status, $order ) {
|
|
if (
|
|
! $this->order_count_cache->is_cached( $order->get_type(), $this->get_prefixed_status( $next_status ) ) ||
|
|
! $this->order_count_cache->is_cached( $order->get_type(), $this->get_prefixed_status( $previous_status ) )
|
|
) {
|
|
return;
|
|
}
|
|
|
|
// If the order status count has already been incremented, we can skip incrementing it again.
|
|
if ( isset( $this->order_statuses[ $order_id ] ) && $this->order_statuses[ $order_id ] === $next_status ) {
|
|
return;
|
|
}
|
|
|
|
$this->order_statuses[ $order_id ] = $next_status;
|
|
$was_decremented = $this->order_count_cache->decrement( $order->get_type(), $this->get_prefixed_status( $previous_status ) );
|
|
$this->order_count_cache->increment( $order->get_type(), $this->get_prefixed_status( $next_status ) );
|
|
|
|
// Set the initial order status in case this is a new order and the previous status should not be decremented.
|
|
if ( ! isset( $this->initial_order_statuses[ $order_id ] ) && $was_decremented ) {
|
|
$this->initial_order_statuses[ $order_id ] = $previous_status;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the prefixed status.
|
|
*
|
|
* @param string $status The status.
|
|
* @return string
|
|
*/
|
|
private function get_prefixed_status( $status ) {
|
|
$status = 'wc-' . $status;
|
|
|
|
$special_statuses = array(
|
|
'wc-' . OrderStatus::AUTO_DRAFT => OrderStatus::AUTO_DRAFT,
|
|
'wc-' . OrderStatus::TRASH => OrderStatus::TRASH,
|
|
);
|
|
|
|
if ( isset( $special_statuses[ $status ] ) ) {
|
|
return $special_statuses[ $status ];
|
|
}
|
|
|
|
return $status;
|
|
}
|
|
}
|