Skip to content

Instantly share code, notes, and snippets.

@mralaminahamed
Created March 11, 2026 06:54
Show Gist options
  • Select an option

  • Save mralaminahamed/ec5576c2eefcaea9894345cee8871d60 to your computer and use it in GitHub Desktop.

Select an option

Save mralaminahamed/ec5576c2eefcaea9894345cee8871d60 to your computer and use it in GitHub Desktop.
class-affiliate-role-manager.php
<?php
/**
* Affiliate Role Manager
*
* Registers a custom 'affiliate' WordPress role, assigns it to existing users
* based on the '_wc_affiliate_status' user meta, and automatically maintains
* role assignment whenever that meta value is created or updated.
* Also provides a utility query to retrieve all non-affiliate users.
*
* @package YourPlugin
* @subpackage Affiliate
* @author Al Amin Ahamed <me@alaminahamed.com>
* @copyright 2025 Al Amin Ahamed
* @license GPL-2.0-or-later
* @since 1.0.0
*
* @wordpress-plugin
* Plugin Name: Affiliate Role Manager
* Plugin URI: https://example.com/affiliate-role-manager
* Description: Registers a custom affiliate role and manages its assignment
* based on WooCommerce affiliate status user meta.
* Version: 1.0.0
* Author: Al Amin Ahamed
* Author URI: https://example.com
* License: GPL-2.0-or-later
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
* Text Domain: affiliate-role-manager
* Domain Path: /languages
* Requires at least: 5.8
* Requires PHP: 7.4
* WC requires at least: 6.0
* WC tested up to: 8.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Prevent direct file access.
}
// -------------------------------------------------------------------------
// Step 1: Register the custom 'affiliate' role.
// -------------------------------------------------------------------------
/**
* Registers the 'affiliate' user role with minimal capabilities.
*
* Hooked to 'init' so it runs on every request; the inner guard prevents
* redundant calls to add_role() once the role already exists in the database.
*
* @since 1.0.0
* @return void
*/
function register_affiliate_role() {
if ( ! get_role( 'affiliate' ) ) {
add_role(
'affiliate',
__( 'Affiliate', 'affiliate-role-manager' ),
array(
'read' => true,
'edit_posts' => false,
'delete_posts' => false,
)
);
}
}
add_action( 'init', 'register_affiliate_role' );
// -------------------------------------------------------------------------
// Step 2: Bulk-assign the affiliate role to pre-existing users.
// -------------------------------------------------------------------------
/**
* Iterates over all users that carry the '_wc_affiliate_status' meta key and
* assigns the 'affiliate' role to those whose status equals 'active'.
*
* A long-lived transient is used as a one-time execution guard so that this
* bulk operation does not repeat on every page load after the initial run.
*
* @since 1.0.0
* @return void
*/
function assign_affiliate_role_to_existing_users() {
// Bail early if the bulk assignment has already been performed.
if ( get_transient( 'affiliate_role_assigned' ) ) {
return;
}
$users = get_users(
array(
'meta_key' => '_wc_affiliate_status', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
'meta_compare' => 'EXISTS',
'fields' => 'all',
'number' => -1,
)
);
if ( empty( $users ) ) {
return;
}
foreach ( $users as $user ) {
$affiliate_status = get_user_meta( $user->ID, '_wc_affiliate_status', true );
// Only assign the role when the affiliate status is explicitly 'active'.
if ( ! empty( $affiliate_status ) && 'active' === $affiliate_status ) {
$user_obj = new WP_User( $user->ID );
$user_obj->add_role( 'affiliate' );
}
}
// Mark bulk assignment as complete for one year.
set_transient( 'affiliate_role_assigned', true, DAY_IN_SECONDS * 365 );
}
add_action( 'init', 'assign_affiliate_role_to_existing_users' );
// -------------------------------------------------------------------------
// Step 3: React to meta create / update events for new users.
// -------------------------------------------------------------------------
/**
* Adds or removes the 'affiliate' role whenever '_wc_affiliate_status' meta
* is created or updated for a user.
*
* Hooked to both 'added_user_meta' and 'updated_user_meta' so that the role
* state remains in sync regardless of whether the meta record is new or
* pre-existing.
*
* @since 1.0.0
*
* @param int $meta_id ID of the user meta row that was affected.
* @param int $user_id ID of the user whose meta was changed.
* @param string $meta_key Meta key that was added or updated.
* @param mixed $meta_value New value of the meta key.
* @return void
*/
function assign_affiliate_role_on_meta_update( $meta_id, $user_id, $meta_key, $meta_value ) {
if ( '_wc_affiliate_status' !== $meta_key ) {
return;
}
$user = new WP_User( $user_id );
if ( 'active' === $meta_value ) {
$user->add_role( 'affiliate' );
} else {
// Remove the role when the status is set to anything other than 'active'.
$user->remove_role( 'affiliate' );
}
}
add_action( 'updated_user_meta', 'assign_affiliate_role_on_meta_update', 10, 4 );
add_action( 'added_user_meta', 'assign_affiliate_role_on_meta_update', 10, 4 );
// -------------------------------------------------------------------------
// Step 4: Utility — query non-affiliate users only.
// -------------------------------------------------------------------------
/**
* Retrieves all users who are neither assigned the 'affiliate' role nor carry
* the '_wc_affiliate_status' meta key.
*
* The dual exclusion strategy ensures edge cases are handled: users who have
* the meta key present but whose role was never applied (e.g. due to a failed
* earlier migration) are also excluded from the result set.
*
* @since 1.0.0
*
* @param array $extra_args Optional additional arguments to merge into the
* get_users() call (e.g. 'number', 'offset', 'orderby').
* @return WP_User[] Array of WP_User objects representing non-affiliate users.
*/
function get_non_affiliate_users( array $extra_args = array() ) {
$default_args = array(
'role__not_in' => array( 'affiliate' ),
'meta_query' => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
array(
'key' => '_wc_affiliate_status',
'compare' => 'NOT EXISTS',
),
),
'fields' => 'all',
'number' => -1,
);
$args = wp_parse_args( $extra_args, $default_args );
return get_users( $args );
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment