trackOrderById( (int) $order ); return; } if ( ! $order instanceof \WC_Order ) { return; } if ( $this->hasTrackedOrder( (int) $order->get_id() ) ) { return; } $this->record( 'order_completed', [ 'order_id' => $order->get_id(), 'revenue' => (float) $order->get_total(), ] ); } public function trackOrderProcessed( int $order_id ): void { $this->trackOrderById( $order_id ); } public function trackOrderById( int $order_id ): void { if ( $order_id <= 0 || $this->hasTrackedOrder( $order_id ) ) { return; } $order = wc_get_order( $order_id ); if ( ! $order instanceof \WC_Order ) { return; } $this->trackOrder( $order ); } /** * Record a product being added to the cart. * * @param string $cart_item_key Cart item key WooCommerce generated. Unused. * @param int $product_id Product added to the cart. * @param int $quantity Quantity added. */ public function trackAddToCart( string $cart_item_key, int $product_id, int $quantity ): void { $this->record( 'add_to_cart', [ 'product_id' => $product_id, 'quantity' => $quantity, ] ); } public function trackCheckoutView(): void { $this->record( 'checkout_view' ); } public function record( string $event_type, array $meta = [] ): void { global $wpdb; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery $inserted = $wpdb->insert( $wpdb->prefix . 'arrayfunnel_analytics', [ 'event_type' => $event_type, 'order_id' => $meta['order_id'] ?? null, 'user_id' => get_current_user_id() ?: null, 'session_id' => $this->getSessionId(), 'revenue' => $meta['revenue'] ?? 0.00, 'meta' => ! empty( $meta ) ? wp_json_encode( $meta ) : null, 'created_at' => current_time( 'mysql', true ), ], [ '%s', '%d', '%d', '%s', '%f', '%s', '%s' ] ); if ( false !== $inserted ) { $this->invalidateAnalyticsCache(); } } private function hasTrackedOrder( int $order_id ): bool { if ( $order_id <= 0 ) { return false; } global $wpdb; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching $exists = $wpdb->get_var( $wpdb->prepare( "SELECT id FROM {$wpdb->prefix}arrayfunnel_analytics WHERE event_type = %s AND order_id = %d LIMIT 1", 'order_completed', $order_id ) ); return ! empty( $exists ); } private function invalidateAnalyticsCache(): void { global $wpdb; $patterns = [ '_transient_arrayfunnel_overview_%', '_transient_timeout_arrayfunnel_overview_%', '_transient_arrayfunnel_revenue_chart_%', '_transient_timeout_arrayfunnel_revenue_chart_%', '_transient_arrayfunnel_events_log_%', '_transient_timeout_arrayfunnel_events_log_%', ]; foreach ( $patterns as $pattern ) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s", $pattern ) ); } } private function getSessionId(): string { $session_id = WC()->session ? (string) WC()->session->get_customer_id() : ''; if ( '' !== $session_id ) { return hash( 'sha256', 'arrayfunnel_analytics|' . $session_id ); } return wp_generate_uuid4(); } }