Documentation

Writing an EntryVault adapter

An EntryVault adapter teaches the plugin how to capture submissions from one form builder. Each adapter is a small class implementing a three-method contract, then registered via a filter. This guide walks through writing one.

You don’t need to modify EntryVault to add an adapter — register your class from your own plugin or your theme’s functions.php using the entryvault_adapters filter.

The contract

Every adapter extends EntryVault\Adapters\Adapter_Base and implements three abstract methods:

abstract public function slug(): string;         // stable id, e.g. "my-forms"
abstract public function is_active(): bool;       // is the host plugin installed?
abstract public function register_hooks(): void;  // hook the builder's submit event

The registry only calls register_hooks() on adapters whose is_active() returns true, so there’s zero overhead for builders that aren’t installed.

The normalised payload

Your hook callback’s job is to build one associative array and hand it to dispatch(). This is the shape EntryVault’s sync engine stores:

[
  'source_plugin'   => $this->slug(),      // who captured it
  'source_form_id'  => (string) $form_id,   // which form
  'source_entry_id' => (string) $entry_id,  // which submission (see dedupe note)
  'title'           => $title,              // card title
  'email'           => $email,              // card email (nullable)
  'fields'          => $fields,             // list of [key,label,type,value]
  'created_at'      => current_time( 'mysql' ),
]

Dedupe: the one rule that matters

EntryVault de-duplicates on the triple (source_plugin, source_form_id, source_entry_id). If the builder gives you a real, stable entry id, use it. If it stores nothing (as Contact Form 7 does), you must synthesise a unique id per submission — otherwise two unrelated submissions with the same id silently merge into one record.

Helpers the base class gives you

$this->dispatch( $normalized );        // ingest the payload (idempotent)
$this->pick_email( $fields );          // best-guess email from the fields
$this->pick_title( $fields, $email );  // best-guess card title (name field, else email)

A minimal adapter

A complete adapter for a hypothetical builder that fires myforms_after_submit with a form id and a label => value map:

namespace MyPlugin;

use EntryVault\Adapters\Adapter_Base;

class Adapter_My_Forms extends Adapter_Base {

    public function slug(): string {
        return 'my-forms';
    }

    public function is_active(): bool {
        return defined( 'MYFORMS_VERSION' );
    }

    public function register_hooks(): void {
        add_action( 'myforms_after_submit', [ $this, 'capture' ], 20, 2 );
    }

    public function capture( int $form_id, array $data ): void {
        $fields = [];
        foreach ( $data as $label => $value ) {
            $fields[] = [
                'key'   => sanitize_key( $label ),
                'label' => $label,
                'type'  => is_string( $value ) && is_email( $value ) ? 'email' : 'text',
                'value' => is_array( $value ) ? implode( ', ', $value ) : $value,
            ];
        }
        $email = $this->pick_email( $fields );
        $this->dispatch( [
            'source_form_id'  => (string) $form_id,
            'source_entry_id' => 'myforms_' . $form_id . '_' . uniqid( '', true ),
            'title'           => $this->pick_title( $fields, $email ),
            'email'           => $email,
            'fields'          => $fields,
        ] );
    }
}

Registering it

Add your class to the candidate list through the entryvault_adapters filter — no core edits:

add_filter( 'entryvault_adapters', function ( array $adapters ) {
    $adapters[] = \MyPlugin\Adapter_My_Forms::class;
    return $adapters;
} );

That’s the whole integration. On the next submission your adapter runs, the entry is normalised and stored, and it appears on the board alongside every other builder’s submissions.

Tips for metadata-poor builders

  • No labels? Humanise the field key (e.g. full_name → “Full Name”) and infer a type from the value.
  • No pre-send hook? Capture as early as the builder allows so a failed notification email doesn’t cost you the lead.
  • No entry id? Synthesise one from the form id plus microtime() and uniqid('', true) — see the dedupe rule above.