Architectural notes

File structure (as of 1.1.4)

The former two god classes (LH_BP_Group_Taxonomies_Admin, LH_BP_Group_Taxonomies_Frontend) were split into eight focused, single-concern files. This wasn’t a mechanical file-per-method split — each cluster was kept together specifically where its methods share request-scoped instance state, and split apart everywhere else:

Groups directory filtering

Implemented via parse_select() / parse_total() in LH_BP_Group_Taxonomies_Frontend, hooked on BuddyPress’s own bp_groups_get_paged_groups_sql / bp_groups_get_total_groups_sql filters. parse_groups_query() (hooked on bp_before_has_groups_parse_args) collects every matching taxonomy present — either a directory URL segment or a $_GET param — into a WP_Tax_Query-shaped multi-clause array (AND relation across taxonomies). parse_select()/parse_total() then build a WP_Tax_Query from that array and inject its JOIN/WHERE SQL directly into BuddyPress’s group query (WP_Tax_Query::get_sql( 'g', 'id' )) — the same technique BuddyPress’s own native group_type filtering uses internally. Because $wpdb‘s term-table properties resolve to whichever site is current, this join correctly scopes to the current site’s own term tables even on a network-activated install where BuddyPress’s bp_groups table is centralised on the main site.

The first taxonomy/term matched is also kept on $this->taxonomy / $this->term for single-tag display contexts that only show one tag name — when multiple taxonomies are active at once, only the first is named in that display copy, though all are applied to the actual filtering.

An earlier PHP-side implementation (restrict_by_taxonomy(), using get_objects_in_term() + array_intersect() against $_GET) was removed in 1.0.1 in favour of the SQL-join approach above. Verified live on Princes Park Touch (site ID 2) that it correctly resolves to the site’s own term tables (e.g. lhero_2_term_relationships), not shared/network tables.

Reusable tax_query API

LH_BP_Group_Taxonomies_Taxonomy::get_group_ids_by_tax_query( array $tax_query ) returns matching group IDs for an arbitrary WP_Tax_Query-shaped argument — a single flat clause, or multiple clauses combined with a top-level relation key, exactly as WordPress core’s native tax_query argument works elsewhere (e.g. WP_Query). It runs standalone against BuddyPress’s own groups table via $wpdb, so it’s callable from any PHP code, not just the groups-directory-page context parse_select()/parse_total() are tied to.

// Multiple clauses, AND
$group_ids = LH_BP_Group_Taxonomies_Taxonomy::get_group_ids_by_tax_query( array(
    'relation' => 'AND',
    array( 'taxonomy' => 'lh_group_tags', 'terms' => 12, 'field' => 'term_id' ),
    array( 'taxonomy' => 'lh_comps_team_season', 'terms' => 'season-1', 'field' => 'slug' ),
) );

As of 1.1.0, lh-competitions‘s return_common_group_ids_by_term_id() and return_common_group_ids_by_term_slug() use this method internally instead of independently calling get_objects_in_term().

Forward-compatibility note: this array shape deliberately mirrors a generic tax_query argument BuddyPress core is adding to BP_Groups_Group::get() (landed on trunk June 2026, not yet in a stable release — confirmed absent from the BuddyPress 14.4.0 build installed on this network as of the 1.1.0 changelog entry). Once this network upgrades to a BuddyPress release that includes it, callers can switch to groups_get_groups( array( 'tax_query' => $tax_query ) ) directly, and this method (along with parse_select()/parse_total()) can likely be retired in favour of BuddyPress’s native support.

MCP abilities load order

The abilities file loads on plugins_loaded (priority 20), independent of the bp_loaded/bp_init-based bootstrap used for the BuddyPress-facing functionality (LH_BP_Group_Taxonomies_Taxonomy starts on bp_init, priority 11). Guarded on function_exists( 'wp_register_ability' ) as defense-in-depth, though the plugin’s declared minimum (WordPress 6.9+, see Requirements below) already guarantees the Abilities API is present.

Requirements