Show AI Search to subscribers at or above a required tier
Renders the AI Search widget for subscribers whose active tier meets or exceeds a configurable tier slug, and falls back to the Benecaster’s own search for everyone else.
Add [my_tiered_search show_id="123" required_tier="growth"] to any page. Subscribers at the growth tier or above (by position in the show’s tier list) see [benecaster_ai_search]; lower-tier subscribers, logged-out users, and non-subscribers see [benecaster_search].
The AI Search add-on must be active for the AI branch to render. When the add-on is inactive, [benecaster_ai_search] is unregistered and the recipe falls through to the Benecaster’s own search automatically — no conditional needed in your template.
Why check shortcode_exists() first. [benecaster_ai_search] is registered by the AI Search add-on; if the operator disables the add-on (license lapse, temporary deactivation), the tag becomes unregistered and do_shortcode() returns the literal text [benecaster_ai_search show_id="123"] to the browser. The shortcode_exists() guard makes the recipe degrade cleanly to the built-in search on any request where the add-on isn’t loaded.
Preview as Tier. benecaster_get_user_tier_for_show() fires the benecaster_user_tier_for_show filter, which is what the Preview-as-Tier feature uses to make an admin appear as a subscriber at a chosen tier. That means an admin previewing “Growth” on their site will see the AI Search branch exactly as a real Growth subscriber would — no extra work needed for testing.
Tier ordering — position, not the number. MembershipTierRepository::ranked_slugs() returns the show’s tiers in the order the Memberships list and the pricing table show them (stored order, then creation order), lowest first. Comparing positions in that list is what [benecaster_tier_gate] itself does. On a built-in show that order is the one the podcaster sets by dragging tiers on Memberships → Tiers; new tiers are added last. Do not compare raw tier_order values instead — tiers created in the Memberships admin all stored tier_order = 0 before tier ordering was made explicit, so a raw-value comparison passes every tier, including a free tier against your top one.
No API call on page load. benecaster_get_user_tier_for_show() reads from a repository that queries the benecaster_tokens table (indexed on user_id + show_id), and MembershipTierRepository::ranked_slugs() is one indexed SELECT on benecaster_membership_tiers. Two indexed lookups per request — no HTTP call, no cache lookup outside the DB. Safe to place on high-traffic pages without a fragment-cache wrapper.
Simpler route, no code: [benecaster_tier_gate min_tier="growth"][benecaster_ai_search][benecaster_else][benecaster_search][/benecaster_tier_gate] does the same “at or above tier X” check in Benecaster itself, and never runs the hidden branch. Keep this recipe for conditions more complex than one tier threshold. The one thing this recipe does that the gate does not is its shortcode_exists() fallback: if the AI Search add-on is inactive, a Growth subscriber inside a [benecaster_tier_gate] sees the literal [benecaster_ai_search] text rather than falling back to the built-in search.
Code
<?php
add_shortcode( 'my_tiered_search', function ( $atts ): string {
$atts = shortcode_atts(
[
'show_id' => 0,
'required_tier' => '',
],
$atts,
'my_tiered_search'
);
$show_id = (int) $atts['show_id'];
$required_slug = sanitize_key( (string) $atts['required_tier'] );
if ( $show_id <= 0 || '' === $required_slug ) {
return '';
}
$ai_shortcode = sprintf( '[benecaster_ai_search show_id="%d"]', $show_id );
$fallback_shortcode = sprintf( '[benecaster_search show_id="%d"]', $show_id );
if ( ! shortcode_exists( 'benecaster_ai_search' ) ) {
return do_shortcode( $fallback_shortcode );
}
$user_tier_slug = benecaster_get_user_tier_for_show( $show_id );
if ( '' === $user_tier_slug ) {
return do_shortcode( $fallback_shortcode );
}
// Rank by POSITION in the show's tier list (lowest first), never by
// the raw tier_order value: tiers created in the Memberships admin
// all stored tier_order = 0, so comparing values would admit every
// tier, a free one included.
$ranked = ( new \Benecaster\Membership\MembershipTierRepository() )->ranked_slugs( $show_id );
$user_at = array_search( $user_tier_slug, $ranked, true );
$need_at = array_search( $required_slug, $ranked, true );
// If either tier can't be resolved (e.g. tier was deleted), be
// conservative and fall back to the built-in search rather than
// accidentally exposing AI search to someone who shouldn't have it.
if ( false === $user_at || false === $need_at ) {
return do_shortcode( $fallback_shortcode );
}
if ( $user_at >= $need_at ) {
return do_shortcode( $ai_shortcode );
}
return do_shortcode( $fallback_shortcode );
} );
Need this built rather than just documented? See our services →