prefix . 'statbird_changes'; } /** * Saves group moves and fires an action for each. * * A first group (from '' on a new customer) is a new customer, not a move, and is skipped. * * @param array[] $changes Each: customer key, from group, to group. * @param string $when MySQL datetime (GMT). */ public static function record( array $changes, $when ) { global $wpdb; $changes = array_values( array_filter( $changes, static function ( $c ) { return '' !== $c[1] && $c[1] !== $c[2]; } ) ); if ( ! $changes ) { return; } $table = self::table(); foreach ( array_chunk( $changes, 500 ) as $chunk ) { $values = array(); foreach ( $chunk as $c ) { $values[] = $wpdb->prepare( '(%s, %s, %s, %s)', $c[0], $c[1], $c[2], $when ); } // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- values prepared above. $wpdb->query( "INSERT INTO $table (customer_key, from_segment, to_segment, changed_at) VALUES " . implode( ',', $values ) ); } foreach ( $changes as $c ) { /** * Fires when a customer moves to another group. * * @param string $customer_key MD5 key. * @param string $from Previous group key. * @param string $to New group key. */ do_action( 'statbird_segment_changed', $c[0], $c[1], $c[2] ); } } /** * Recent moves with customer details, newest first. * * @param int $days Look-back window. * @param int $limit Maximum rows. * @return array[] */ public static function recent( $days = 30, $limit = 8 ) { global $wpdb; $c = self::table(); $s = Store::table(); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- table names only. return (array) $wpdb->get_results( $wpdb->prepare( "SELECT ch.*, cu.name, cu.email, cu.user_id, cu.spent FROM $c ch LEFT JOIN $s cu ON cu.customer_key = ch.customer_key WHERE ch.changed_at >= %s ORDER BY ch.changed_at DESC, cu.spent DESC LIMIT %d", gmdate( 'Y-m-d H:i:s', time() - $days * DAY_IN_SECONDS ), $limit ), ARRAY_A ); } /** * Count of moves between each pair of groups in a window. * * @param int $days Look-back window. * @return array[] Rows of from_segment, to_segment, n. */ public static function flows( $days = 30 ) { global $wpdb; $c = self::table(); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- table name only. return (array) $wpdb->get_results( $wpdb->prepare( "SELECT from_segment, to_segment, COUNT(*) AS n FROM $c WHERE changed_at >= %s GROUP BY from_segment, to_segment ORDER BY n DESC", gmdate( 'Y-m-d H:i:s', time() - $days * DAY_IN_SECONDS ) ), ARRAY_A ); } }