'GET', 'callback' => [ $this, 'overview' ], 'permission_callback' => $auth, ] ); register_rest_route( self::NAMESPACE, '/analytics/chart', [ 'methods' => 'GET', 'callback' => [ $this, 'chart' ], 'permission_callback' => $auth, 'args' => [ 'days' => [ 'type' => 'integer', 'default' => 30, 'minimum' => 1, 'maximum' => 365 ], ], ] ); register_rest_route( self::NAMESPACE, '/analytics/top-posts', [ 'methods' => 'GET', 'callback' => [ $this, 'top_posts' ], 'permission_callback' => $auth, 'args' => [ 'limit' => [ 'type' => 'integer', 'default' => 10, 'minimum' => 1, 'maximum' => 100 ], 'post_type' => [ 'type' => 'string', 'default' => '' ], 'period' => [ 'type' => 'string', 'default' => 'total', 'enum' => [ 'total', 'today', 'week', 'month' ] ], ], ] ); register_rest_route( self::NAMESPACE, '/analytics/post/(?P\d+)', [ 'methods' => 'GET', 'callback' => [ $this, 'single_post' ], 'permission_callback' => $auth, 'args' => [ 'id' => [ 'type' => 'integer', 'minimum' => 1 ], ], ] ); } public function require_manage_options(): bool { return current_user_can( 'manage_options' ); } public function overview(): WP_REST_Response { $repo = new ViewsRepository(); $totals = $repo->get_site_totals(); return new WP_REST_Response( [ 'total_views' => $totals['total'], 'today_views' => $totals['today'], 'week_views' => $totals['week'], 'month_views' => $totals['month'], 'trending_count' => $totals['trending_count'], ] ); } public function chart( WP_REST_Request $request ): WP_REST_Response { $repo = new ViewsRepository(); $days = (int) $request->get_param( 'days' ); $post_id = (int) $request->get_param( 'post_id' ); if ( ! $post_id ) { global $wpdb; $agg_table = $wpdb->prefix . 'pulseviews_aggregates'; $start = gmdate( 'Y-m-d', strtotime( "-{$days} days", current_time( 'timestamp' ) ) ); $rows = $wpdb->get_results( $wpdb->prepare( "SELECT period_date AS date, SUM(view_count) AS views FROM %i WHERE period_type = 'day' AND period_date >= %s GROUP BY period_date ORDER BY period_date ASC", $agg_table, $start ), ARRAY_A ); $indexed = array_column( (array) $rows, 'views', 'date' ); $series = []; for ( $i = $days - 1; $i >= 0; $i-- ) { $d = gmdate( 'Y-m-d', strtotime( "-{$i} days", current_time( 'timestamp' ) ) ); $series[] = [ 'date' => $d, 'views' => (int) ( $indexed[ $d ] ?? 0 ) ]; } return new WP_REST_Response( [ 'series' => $series, 'days' => $days ] ); } return new WP_REST_Response( [ 'series' => $repo->get_daily_views( $post_id, $days ), 'post_id' => $post_id, 'days' => $days, ] ); } public function top_posts( WP_REST_Request $request ): WP_REST_Response { $repo = new ViewsRepository(); $items = $repo->get_top_posts( (int) $request->get_param( 'limit' ), (string) $request->get_param( 'post_type' ), (string) $request->get_param( 'period' ) ); $enriched = array_map( static function ( array $item ): array { $post = get_post( $item['post_id'] ); return [ 'post_id' => $item['post_id'], 'views' => $item['views'], 'title' => $post ? get_the_title( $post ) : '', 'permalink' => $post ? get_permalink( $post ) : '', 'post_type' => $post ? $post->post_type : '', 'edit_link' => $post ? get_edit_post_link( $post, 'raw' ) : '', 'thumb' => get_the_post_thumbnail_url( $item['post_id'], 'thumbnail' ) ?: '', ]; }, $items ); return new WP_REST_Response( [ 'posts' => $enriched ] ); } public function single_post( WP_REST_Request $request ): WP_REST_Response { $repo = new ViewsRepository(); $post_id = (int) $request->get_param( 'id' ); return new WP_REST_Response( [ 'post_id' => $post_id, 'total' => $repo->get_post_views( $post_id, 'total' ), 'today' => $repo->get_post_views( $post_id, 'today' ), 'week' => $repo->get_post_views( $post_id, 'week' ), 'month' => $repo->get_post_views( $post_id, 'month' ), 'daily_30' => $repo->get_daily_views( $post_id, 30 ), ] ); } }