@@ -254,6 +254,39 @@ public function register_routes() {
254254 'callback ' => array ( $ this , 'get_forms_config ' ),
255255 )
256256 );
257+
258+ register_rest_route (
259+ $ this ->namespace ,
260+ $ this ->rest_base . '/counts ' ,
261+ array (
262+ 'methods ' => \WP_REST_Server::READABLE ,
263+ 'permission_callback ' => array ( $ this , 'get_items_permissions_check ' ),
264+ 'callback ' => array ( $ this , 'get_status_counts ' ),
265+ 'args ' => array (
266+ 'search ' => array (
267+ 'type ' => 'string ' ,
268+ 'sanitize_callback ' => 'sanitize_text_field ' ,
269+ ),
270+ 'parent ' => array (
271+ 'type ' => 'array ' ,
272+ 'items ' => array (
273+ 'type ' => 'integer ' ,
274+ ),
275+ 'sanitize_callback ' => function ( $ value ) {
276+ return array_map ( 'absint ' , (array ) $ value );
277+ },
278+ ),
279+ 'before ' => array (
280+ 'type ' => 'string ' ,
281+ 'sanitize_callback ' => 'sanitize_text_field ' ,
282+ ),
283+ 'after ' => array (
284+ 'type ' => 'string ' ,
285+ 'sanitize_callback ' => 'sanitize_text_field ' ,
286+ ),
287+ ),
288+ )
289+ );
257290 }
258291
259292 /**
@@ -302,6 +335,75 @@ static function ( $post_id ) {
302335 );
303336 }
304337
338+ /**
339+ * Retrieves status counts for inbox, spam, and trash in a single optimized query.
340+ *
341+ * @param WP_REST_Request $request Full data about the request.
342+ * @return WP_REST_Response Response object on success.
343+ */
344+ public function get_status_counts ( $ request ) {
345+ global $ wpdb ;
346+
347+ $ search = $ request ->get_param ( 'search ' );
348+ $ parent = $ request ->get_param ( 'parent ' );
349+ $ before = $ request ->get_param ( 'before ' );
350+ $ after = $ request ->get_param ( 'after ' );
351+
352+ $ cache_key = 'jetpack_forms_status_counts_ ' . md5 ( wp_json_encode ( compact ( 'search ' , 'parent ' , 'before ' , 'after ' ) ) );
353+ $ cached_result = get_transient ( $ cache_key );
354+ if ( false !== $ cached_result ) {
355+ return rest_ensure_response ( $ cached_result );
356+ }
357+
358+ $ where_conditions = array ( $ wpdb ->prepare ( 'post_type = %s ' , 'feedback ' ) );
359+ $ join_clauses = '' ;
360+
361+ if ( ! empty ( $ search ) ) {
362+ $ search_like = '% ' . $ wpdb ->esc_like ( $ search ) . '% ' ;
363+ $ where_conditions [] = $ wpdb ->prepare ( '(post_title LIKE %s OR post_content LIKE %s) ' , $ search_like , $ search_like );
364+ }
365+
366+ if ( ! empty ( $ parent ) && is_array ( $ parent ) ) {
367+ $ parent_ids = array_map ( 'absint ' , $ parent );
368+ $ parent_ids_string = implode ( ', ' , $ parent_ids );
369+ $ where_conditions [] = "post_parent IN ( $ parent_ids_string) " ;
370+ }
371+
372+ if ( ! empty ( $ before ) || ! empty ( $ after ) ) {
373+ if ( ! empty ( $ before ) ) {
374+ $ where_conditions [] = $ wpdb ->prepare ( 'post_date <= %s ' , $ before );
375+ }
376+ if ( ! empty ( $ after ) ) {
377+ $ where_conditions [] = $ wpdb ->prepare ( 'post_date >= %s ' , $ after );
378+ }
379+ }
380+
381+ $ where_clause = implode ( ' AND ' , $ where_conditions );
382+
383+ // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared
384+ $ counts = $ wpdb ->get_row (
385+ "SELECT
386+ SUM(CASE WHEN post_status IN ('publish', 'draft') THEN 1 ELSE 0 END) as inbox,
387+ SUM(CASE WHEN post_status = 'spam' THEN 1 ELSE 0 END) as spam,
388+ SUM(CASE WHEN post_status = 'trash' THEN 1 ELSE 0 END) as trash
389+ FROM $ wpdb ->posts
390+ $ join_clauses
391+ WHERE $ where_clause " ,
392+ ARRAY_A
393+ );
394+ // phpcs:enable
395+
396+ $ result = array (
397+ 'inbox ' => (int ) ( $ counts ['inbox ' ] ?? 0 ),
398+ 'spam ' => (int ) ( $ counts ['spam ' ] ?? 0 ),
399+ 'trash ' => (int ) ( $ counts ['trash ' ] ?? 0 ),
400+ );
401+
402+ set_transient ( $ cache_key , $ result , 30 );
403+
404+ return rest_ensure_response ( $ result );
405+ }
406+
305407 /**
306408 * Adds the additional fields to the item's schema.
307409 *
0 commit comments