[buddypress-trac] [BuddyPress Trac] #9328: Feature Request: Add query filter hooks to `BP_Messages_Thread::get_current_threads_for_user()`

buddypress-trac noreply at wordpress.org
Fri Feb 6 00:01:24 UTC 2026


#9328: Feature Request: Add query filter hooks to
`BP_Messages_Thread::get_current_threads_for_user()`
-------------------------+-----------------------------
 Reporter:  indigetal    |      Owner:  (none)
     Type:  enhancement  |     Status:  new
 Priority:  normal       |  Milestone:  Awaiting Review
Component:  Core         |    Version:  14.4.0
 Severity:  normal       |   Keywords:
-------------------------+-----------------------------
 #### The Gap

 BuddyPress's activity component has excellent query-level extensibility:

 ```php
 // From BP_Activity_Activity::get() — lines 666, 682
 $where_conditions = apply_filters( 'bp_activity_get_where_conditions',
 $where_conditions, $r, $select_sql, $from_sql, $join_sql );
 $join_sql = apply_filters( 'bp_activity_get_join_sql', $join_sql, $r,
 $select_sql, $from_sql, $where_sql );
 ```

 These filters allow any addon to inject WHERE clauses and JOINs into the
 main activity query without modifying core. BuddyBoss's moderation system,
 for example, hooks into `bp_activity_get_where_conditions` to hide
 suspended content — all from addon-level code.

 The messages component has **no equivalent**.
 `BP_Messages_Thread::get_current_threads_for_user()` builds its SQL in a
 `$sql` array (lines 790–794) and executes it directly:

 ```php
 $sql['select'] = 'SELECT m.thread_id, MAX(m.date_sent) AS date_sent';
 $sql['from']   = "FROM {$bp->messages->table_name_recipients} r INNER JOIN
 {$bp->messages->table_name_messages} m ON m.thread_id = r.thread_id
 {$meta_query_sql['join']}";
 $sql['where']  = "WHERE {$deleted_sql} {$user_id_sql} {$sender_sql}
 {$type_sql} {$search_sql} {$meta_query_sql['where']}";
 $sql['misc']   = "GROUP BY m.thread_id ORDER BY date_sent DESC
 {$pag_sql}";

 $thread_ids = $wpdb->get_results( implode( ' ', $sql ) );
 ```

 The only filter (`bp_messages_thread_current_threads`) fires AFTER the
 query on the fully-constructed result set (line 841) — meaning addons must
 filter in PHP, not SQL. This is:

 1. **A performance problem** — filtering in PHP means fetching rows from
 the database that will be discarded, and constructing full
 `BP_Messages_Thread` objects for threads that are then thrown away. This
 gets worse as thread count grows.
 2. **An extensibility gap** — addons that need custom thread filtering
 (archiving, priority inbox, moderation, read/unread management) have no
 clean way to modify the query
 3. **Inconsistent with the activity API** — activity has comprehensive
 pre-query filters; messages doesn't

 #### Proposed Change

 Add filter hooks to `get_current_threads_for_user()` before query
 execution, following the pattern established by
 `BP_Activity_Activity::get()`:

 ```php
 // After building $sql array (around line 794):

 /**
  * Filters the WHERE SQL for the current threads query.
  *
  * @since {next_version}
  *
  * @param string $where_sql  Current WHERE clause.
  * @param array  $r          Parsed query arguments.
  * @param string $select_sql Current SELECT clause.
  * @param string $from_sql   Current FROM clause (includes JOINs).
  */
 $sql['where'] = apply_filters( 'bp_messages_thread_get_where_conditions',
 $sql['where'], $r, $sql['select'], $sql['from'] );

 /**
  * Filters the FROM/JOIN SQL for the current threads query.
  *
  * @since {next_version}
  *
  * @param string $from_sql   Current FROM clause (includes JOINs).
  * @param array  $r          Parsed query arguments.
  * @param string $select_sql Current SELECT clause.
  * @param string $where_sql  Current WHERE clause.
  */
 $sql['from'] = apply_filters( 'bp_messages_thread_get_join_sql',
 $sql['from'], $r, $sql['select'], $sql['where'] );
 ```

 **Note on parameter style:** In `BP_Activity_Activity::get()`, the WHERE
 conditions are passed as an array (joined to a string after the filter),
 whereas in the messages method they are already a string. This proposal
 preserves the existing messages code structure and filters the string
 directly, which is the minimal-change approach. If BP maintainers prefer,
 the messages SQL could also be refactored to use an array of conditions
 for parity with activity — but filtering the string is sufficient for all
 the use cases described below.

 #### Scope

 - ~12 lines added to one file (`class-bp-messages-thread.php`)
 - Zero behavioral change — the filters pass through existing values by
 default
 - Follows the pattern established by the activity component's existing
 filters
 - The existing `bp_messages_thread_current_threads` post-query filter
 continues to work as-is

 #### What This Enables for Addons

 With these hooks, addon plugins can implement:

 - **Message archiving** — Add `is_hidden` column to
 `bp_messages_recipients`, filter `WHERE` to exclude `r.is_hidden = 1`
 - **Message soft-delete** — Add `is_deleted` column to
 `bp_messages_messages`, filter `FROM`/`JOIN` to exclude deleted messages
 from thread listing
 - **Content moderation** — Inject suspend/block conditions into thread
 queries
 - **Priority inbox** — Filter by custom meta or thread properties
 - **Group messaging** — Filter threads by group membership context

 None of these features require core changes — they just need the ability
 to modify the query, which activity already provides but messages doesn't.

 #### Prior Art

 BuddyBoss Platform needed exactly these extension points. Because the
 hooks don't exist in BuddyPress core, BuddyBoss had to:

 1. **Restructure `get_current_threads_for_user()`** entirely — they
 replaced it with a delegating call to a new `get_threads_for_user()`
 method containing ~400 lines of rewritten SQL logic
 2. **Add `is_hidden` and `is_deleted` columns** directly into the
 restructured query conditions
 3. **Add their own filter hooks** —
 `bp_messages_recipient_get_where_conditions` and
 `bp_messages_recipient_get_join_sql` — with signatures functionally
 identical to what this proposal suggests

 The fact that BuddyBoss independently arrived at the same solution (pre-
 query filter hooks on the messages SQL) validates the need. If these hooks
 had existed in BuddyPress core, BuddyBoss could have achieved the same
 result from addon-level code without forking the class.

 I'm happy to submit a PR for this change.

-- 
Ticket URL: <https://buddypress.trac.wordpress.org/ticket/9328>
BuddyPress Trac <http://buddypress.org/>
BuddyPress Trac


More information about the buddypress-trac mailing list