[buddypress-trac] [BuddyPress Trac] #9328: Enhancement Request: Add query filter hooks to `BP_Messages_Thread::get_current_threads_for_user()`
buddypress-trac
noreply at wordpress.org
Fri Feb 6 01:34:08 UTC 2026
#9328: Enhancement 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: Messages | Version:
Severity: normal | Resolution:
Keywords: |
-------------------------+------------------------------
Old description:
> == The Gap
>
> !BuddyPress's activity component has excellent query-level extensibility:
>
> 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:
>
> {{{
> $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()`:
>
> 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.
New description:
== The Gap
!BuddyPress's activity component has excellent query-level extensibility:
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:
{{{
$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()`:
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
[[br]]
== 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.
--
Comment (by emaralive):
[[span(style=color: #FF0000, Edited the description for readability.
Slightly modified the Summary.)]]
For reference, this started as a support forum topic, see
[https://buddypress.org/support/topic/add-query-filter-hooks-to-
bp_messages_threadget_current_threads_for_user/ Add query filter hooks to
`BP_Messages_Thread::get_current_threads_for_user()`]
--
Ticket URL: <https://buddypress.trac.wordpress.org/ticket/9328#comment:2>
BuddyPress Trac <http://buddypress.org/>
BuddyPress Trac
More information about the buddypress-trac
mailing list