Skip to main content

Register a custom Member Thanks query type

Premium Advanced

The [benecaster_member_thanks] shortcode features a list of subscribers on any page or episode — recent joiners, top-tier members, or other built-in selections. The query type controls which subscribers are fetched and in what order.

This recipe registers a custom query type that appears alongside the built-in options. The example surfaces top donors: whoever has contributed the most in the past 30 days. Place [benecaster_member_thanks query="top_donors"] on any episode or page to feature them.

Use this pattern any time the built-in query types don’t match what you want to show — subscribers from an external CRM, members who left a review, first-time donors, or any other list your data supports. The custom type integrates with the shortcode’s existing count and tier_slug arguments and renders using the standard Member Thanks template with no additional template work required.

Your query chooses who; core describes them

A query type is a plain array — a label, a description and a results callable. There is no interface to implement and no class name to know.

The result rows want user_id, display_name, tier_slug and joined_at. Only the first is yours to know — the other three are Benecaster’s, and filling them by hand is a five-way join: the tokens table for the tier, the users table plus three meta rows for the display-name resolution chain, and a grouped subquery over subscriber events for the join date.

benecaster_get_member_thanks_rows( $show_id, $user_ids ) fills them exactly as core’s own query types do, so a custom type describes its members identically to a built-in one. Select IDs; hand them over.

Three things about the result

Your ordering survives. Rows come back in the order you passed the IDs, not in database order. If you ranked members by donation total, that ranking is the one thing core cannot recompute for you.

The result can be shorter than the list you passed. IDs with no active token for the show are dropped — a former member is not a member, and a blank-tier row is worse than no row. Do not index into the result positionally against your own list, and if you need exactly count rows, ask your own query for more than you need.

$args['tier_slug'] is yours to honour. The helper describes whoever you give it; it does not filter. If the shortcode passed a tier and your query type should respect it, narrow your own ID list before calling.

Read your own data, not Benecaster’s

The example reads the add-on’s own mirror of donation activity, kept current from benecaster_listener_support_donation_logged.

Mirror what you need rather than querying Benecaster’s tables — the storage layout is internal and changes between releases without notice. The division is the point of this recipe: your tables answer who, and a documented public function answers how they are described. Neither half needs you to know Benecaster’s schema.

Code

<?php
// In your add-on's boot class:
add_filter( 'benecaster_member_thanks_query_types', function ( array $types ): array {
    $types['top_donors'] = [
        'label'       => __( 'Top Donors', 'my-addon' ),
        'description' => __( 'Members who have donated the most in the past 30 days.', 'my-addon' ),

        // Show ID first. The sibling related-episodes filter takes episode
        // first - both are ints, so PHP will not catch a transposition.
        'results'     => function ( int $show_id, int $episode_id, array $args ): array {
            global $wpdb;

            $count = (int) ( $args['count'] ?? 5 );

            // Your query answers the part core cannot: WHO. Select IDs only.
            //
            // my_addon_donations is the add-on's own mirror of donation activity,
            // written from benecaster_listener_support_donation_logged. Mirror what
            // you need rather than reading Benecaster's tables directly.
            $user_ids = $wpdb->get_col( $wpdb->prepare(
                "SELECT d.user_id
                 FROM {$wpdb->prefix}my_addon_donations d
                 WHERE d.show_id = %d
                   AND d.donated_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)
                 GROUP BY d.user_id
                 ORDER BY SUM(d.amount) DESC
                 LIMIT %d",
                $show_id,
                $count > 0 ? $count : PHP_INT_MAX
            ) );

            // Core answers the rest: display name, tier, join date - filled the
            // same way the built-in query types fill them.
            return benecaster_get_member_thanks_rows( $show_id, array_map( 'intval', $user_ids ) );
        },
    ];

    return $types;
} );

View on GitHub →

Hooks Used

Need this built rather than just documented? See our services →