Skip to content

Implement Connection Configuration Fields

Use this guide when adding user-facing configuration fields to a new plugin. After completing these steps, merchants will see a form rendered by the connection widget when they configure their sync connection.

Prerequisites

  • You have a plugin class that extends AbstractPlugin and is registered in PluginRegistry. If not, complete Implement a System Plugin first.

1. Create the config class

Create a class in the same namespace as your plugin (e.g. app/Integrations/Acme/) that implements ConnectionConfigContract:

<?php

declare(strict_types=1);

namespace App\Integrations\Acme;

use App\ConnectionConfig\ConnectionConfigSchema;
use App\ConnectionConfig\Fields\MultiSelectField;
use App\ConnectionConfig\Fields\RadioField;
use App\ConnectionConfig\Fields\SelectField;
use App\Contracts\ConnectionConfigContract;
use App\Enums\SyncDataType;
use App\Enums\SyncDirection;
use App\Enums\SyncFrequency;

class AcmeConnectionConfig implements ConnectionConfigContract
{
    public static function schema(): ConnectionConfigSchema
    {
        return ConnectionConfigSchema::make([
            SelectField::make('direction')
                ->label('Sync direction')
                ->options(static::supportedDirections())
                ->required(),

            MultiSelectField::make('data_types')
                ->label('Data to sync')
                ->options(static::supportedDataTypes())
                ->required(),

            RadioField::make('frequency')
                ->label('Sync frequency')
                ->options(SyncFrequency::class)
                ->required(),
        ]);
    }

    /** @return list<SyncDataType> */
    public static function supportedDataTypes(): array
    {
        return SyncDataType::cases();
    }

    /** @return list<SyncDirection> */
    public static function supportedDirections(): array
    {
        return SyncDirection::cases();
    }

    /** @return array<string, mixed> */
    public static function defaultConfiguration(): array
    {
        return [
            'direction'  => SyncDirection::Inbound->value,
            'data_types' => array_map(static fn (SyncDataType $t) => $t->value, static::supportedDataTypes()),
            'frequency'  => SyncFrequency::Daily->value,
        ];
    }
}

schema() returns a ConnectionConfigSchema containing an ordered list of field objects. Each field serializes to a JSON shape that the widget reads to render the form.

supportedDataTypes() and supportedDirections() return the subset of enum cases this integration actually supports. Passing these to ->options() ensures the widget only presents valid choices for this platform — not all values of the global enum.

defaultConfiguration() returns the values that will be pre-populated when a connection is first created.

2. Choosing and limiting field types

Pick the right field type for each setting. The full reference is in Connection Config Fields.

Single-choice settings — use SelectField for a dropdown or RadioField for inline radio buttons:

use App\ConnectionConfig\Fields\SelectField;
use App\ConnectionConfig\Fields\RadioField;

SelectField::make('direction')
    ->label('Sync direction')
    ->options(static::supportedDirections())  // list<BackedEnum>
    ->required(),

RadioField::make('frequency')
    ->label('Sync frequency')
    ->options(SyncFrequency::class)           // class-string<BackedEnum> — all cases
    ->required(),

Multi-choice settings — use MultiSelectField when the merchant can pick multiple values:

use App\ConnectionConfig\Fields\MultiSelectField;

MultiSelectField::make('data_types')
    ->label('Data to sync')
    ->options(static::supportedDataTypes())
    ->required(),

Boolean settings — use ToggleField for on/off flags:

use App\ConnectionConfig\Fields\ToggleField;

ToggleField::make('include_tax')
    ->label('Include tax in order totals')
    ->required(),

To restrict a SelectField, MultiSelectField, or RadioField to a subset of an enum, return only the relevant cases from supportedDataTypes()/supportedDirections() and pass the result to ->options():

public static function supportedDirections(): array
{
    // This platform only supports inbound sync.
    return [SyncDirection::Inbound];
}

3. Implement HasConnectionConfiguration on the plugin

Open your plugin class and add the HasConnectionConfiguration interface. Delegate both methods to the config class:

<?php

declare(strict_types=1);

namespace App\Integrations\Acme;

use App\ConnectionConfig\ConnectionConfigSchema;
use App\Contracts\HasConnectionConfiguration;
use App\Services\AbstractPlugin;

class AcmePlugin extends AbstractPlugin implements HasConnectionConfiguration
{
    // ... existing methods ...

    public function connectionConfigurationSchema(): ConnectionConfigSchema
    {
        return AcmeConnectionConfig::schema();
    }

    /** @return array<string, mixed> */
    public function defaultConnectionConfiguration(): array
    {
        return AcmeConnectionConfig::defaultConfiguration();
    }
}

Once this interface is in place, SystemConnectionObserver::creating() will automatically write the default configuration into any new SystemConnection for this driver — no manual call is needed.

4. Reading configuration values at runtime

Saved configuration lives in system_connections.configuration (a JSON column, cast to array). Access it through the SystemConnection passed into your plugin methods:

protected function fetchBulkExportBatches(SystemConnection $connection, ExportRequest $request, Closure $onBatch): void
{
    $direction = $connection->configuration['direction'] ?? SyncDirection::Inbound->value;
    $dataTypes = $connection->configuration['data_types'] ?? [];

    // Use $direction and $dataTypes to parameterise the API call.
}

Validate before use

Configuration is merchant-supplied and may be stale if the schema changes after a connection is saved. Guard against missing or unexpected values with ?? fallbacks or explicit validation before using them to drive API calls.

See also