'running', 'page' => 1, 'done' => 0 ], false ); Stats::flush(); self::ensure(); } /** * One page. * * @internal Hooked to the fill job. */ public function run(): void { if ( ! function_exists( 'wc_get_orders' ) ) { return; } $fill = get_option( Stats::FILL_OPTION, [] ); $fill = is_array( $fill ) && isset( $fill['state'] ) ? $fill : [ 'state' => 'running', 'page' => 1, 'done' => 0 ]; $page = max( 1, (int) ( $fill['page'] ?? 1 ) ); if ( 'done' === $fill['state'] ) { return; } // The end alone was logged before, so how long a store's first fill took // could not be told afterwards. if ( 1 === $page ) { Logger::info( 'customers', 'Customer stats fill started.' ); } $ids = $this->page( $page ); $customers = []; foreach ( $ids as $id ) { $order = wc_get_order( $id ); if ( $order instanceof WC_Order && 'trash' !== $order->get_status() ) { foreach ( Stats::record( $order ) as $hash ) { $customers[ $hash ] = true; } } } foreach ( array_keys( $customers ) as $hash ) { Stats::rebuild( $hash ); } $fill['page'] = $page + 1; $fill['done'] = (int) ( $fill['done'] ?? 0 ) + count( $ids ); if ( count( $ids ) < self::PAGE ) { $fill['state'] = 'done'; Logger::info( 'customers', 'Customer stats filled.', [ 'orders' => $fill['done'] ] ); } update_option( Stats::FILL_OPTION, $fill, false ); if ( 'done' !== $fill['state'] ) { Queue::push( Hooks::CUSTOMER_STATS_FILL, [], 0, 20, 'customer_stats:fill' ); } } /** * One page of order ids, oldest first, through WooCommerce's own query so HPOS * and the old posts tables both work. The connector's backfill pages the same * way. * * @param int $page Page number, from 1. * * @return list */ private function page( int $page ): array { $ids = wc_get_orders( [ 'type' => 'shop_order', 'status' => array_keys( wc_get_order_statuses() ), 'limit' => self::PAGE, 'paged' => $page, 'orderby' => 'ID', 'order' => 'ASC', 'return' => 'ids', ] ); return is_array( $ids ) ? Backfill::toIds( $ids ) : []; } }