-
Notifications
You must be signed in to change notification settings - Fork 0
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.
There are four adapter types, all registered through the central Adapter_Registry:
| Adapter | Default Implementation | Purpose |
|---|---|---|
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.
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.
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.
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. |
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().
The following walkthrough creates a SendGrid email adapter as an example.
<?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.
}
}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 );
}
} );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.