Skip to content

Adapter Pattern

Varun Kumar Dubey edited this page Mar 27, 2026 · 1 revision

Adapter Pattern

Jetonomy uses an interface-based adapter pattern to provide swappable backends for external services. This keeps the core decoupled from any specific provider and allows site owners (or Pro extensions) to substitute implementations without modifying core code.

Overview

There are four adapter types, all registered through the central Adapter_Registry:

Adapter Default Implementation Purpose
Email WP_Mail_Adapter Transactional email delivery
Search MySQL FULLTEXT Content indexing and search
Membership WP_Roles_Adapter User level/plan resolution
Realtime Polling_Adapter Live updates to connected clients

Each adapter type has a corresponding PHP interface that defines the contract. Default implementations ship with Jetonomy core. Pro or third-party plugins can register replacements via the Adapter_Registry.


Email Adapter

Interface: interface-email-adapter.php

Method Signature Description
is_active (): bool Whether this adapter is configured and usable
send ($to, $subject, $html, $plain, $headers): bool Send a single email
send_batch ($messages): array Send multiple emails, returns per-message results
register_hooks (): void Register any WordPress hooks the adapter needs

Default: WP_Mail_Adapter wraps the standard wp_mail() function. Works out of the box with any SMTP plugin or hosting mail configuration.


Search Adapter

Interface: interface-search-adapter.php

Method Signature Description
is_active (): bool Whether the search backend is available
index ($type, $id, $data): void Index a document (post, reply, etc.)
search ($query, $type, $space_id, $limit, $offset): array Execute a search query
delete ($type, $id): void Remove a document from the index

Default: MySQL FULLTEXT search against Jetonomy's content tables. No external dependencies.

Ready for: Meilisearch, Elasticsearch, or any external search engine. Implement the interface, register via Adapter_Registry, and all search queries route through the new backend automatically.


Membership Adapter

Interface: interface-membership-adapter.php

Method Signature Description
is_active (): bool Whether the membership backend is available
get_user_levels ($user_id): array Get all membership levels for a user
user_has_level ($user_id, $level): bool Check if a user has a specific level
get_all_levels (): array List all defined membership levels
get_level_label ($level): string Human-readable label for a level
register_hooks (): void Register any WordPress hooks the adapter needs

Implementations:

Class Description
WP_Roles_Adapter Default. Maps WordPress roles to membership levels.
Member_Press_Adapter Reads levels from MemberPress subscriptions.
PMPro_Adapter Reads levels from Paid Memberships Pro.

Realtime Adapter

Interface: interface-realtime-adapter.php

Method Signature Description
is_active (): bool Whether the realtime backend is available
publish ($channel, $event, $data): void Push an event to connected clients
get_client_config (): array Config the JS client needs to connect

Default: Polling_Adapter uses REST-based long-polling. No external services required. Suitable for small to medium communities.

Ready for: WebSocket servers, Pusher, Ably, or any pub/sub service. Implement the interface and the JS client automatically switches transport based on get_client_config().


How to Create a Custom Adapter

The following walkthrough creates a SendGrid email adapter as an example.

Step 1: Create the adapter class

<?php
namespace My_Plugin\Adapters;

use Jetonomy\Adapters\Interfaces\Email_Adapter;

class SendGrid_Adapter implements Email_Adapter {

    private string $api_key;

    public function __construct() {
        $this->api_key = get_option( 'my_sendgrid_api_key', '' );
    }

    public function is_active(): bool {
        return ! empty( $this->api_key );
    }

    public function send( $to, $subject, $html, $plain, $headers = [] ): bool {
        $response = wp_remote_post( 'https://api.sendgrid.com/v3/mail/send', [
            'headers' => [
                'Authorization' => 'Bearer ' . $this->api_key,
                'Content-Type'  => 'application/json',
            ],
            'body' => wp_json_encode( [
                'personalizations' => [ [ 'to' => [ [ 'email' => $to ] ] ] ],
                'from'             => [ 'email' => get_option( 'admin_email' ) ],
                'subject'          => $subject,
                'content'          => [
                    [ 'type' => 'text/plain', 'value' => $plain ],
                    [ 'type' => 'text/html',  'value' => $html ],
                ],
            ] ),
        ] );

        return ! is_wp_error( $response )
            && wp_remote_retrieve_response_code( $response ) === 202;
    }

    public function send_batch( array $messages ): array {
        $results = [];
        foreach ( $messages as $key => $msg ) {
            $results[ $key ] = $this->send(
                $msg['to'],
                $msg['subject'],
                $msg['html'],
                $msg['plain'],
                $msg['headers'] ?? []
            );
        }
        return $results;
    }

    public function register_hooks(): void {
        // No hooks needed for SendGrid.
    }
}

Step 2: Register the adapter

Hook into jetonomy_register_adapters (or plugins_loaded after Jetonomy loads) and register your adapter with the registry:

add_action( 'jetonomy_register_adapters', function ( $registry ) {
    $adapter = new \My_Plugin\Adapters\SendGrid_Adapter();

    if ( $adapter->is_active() ) {
        $registry->register( 'email', $adapter );
    }
} );

Step 3: Verify

Once registered, all Jetonomy email (notifications, digests, invitations) routes through your adapter automatically. No core modifications required.

The same pattern applies to Search, Membership, and Realtime adapters. Implement the interface, register it, and the core routes through your implementation.

Clone this wiki locally