Skip to content

Implement Connection Configuration Fields

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

Prerequisites

  • You have an integration class that extends AbstractIntegration and is registered in IntegrationRegistry. If not, complete Implement a System Integration first.

1. Create the config class

Create a class in the same namespace as your integration (e.g. app/Integrations/Acme/) that extends AbstractConnectionConfig. The base class implements ConnectionConfigContract with the configuration every shop starts from — sync direction, data types and frequency — so a platform with no deviations needs an empty body, as WooCommerceConnectionConfig and PrestaShopConnectionConfig have today:

<?php

declare(strict_types=1);

namespace App\Integrations\Acme;

use App\ConnectionConfig\AbstractConnectionConfig;

/**
 * Acme's connection configuration — the shared shop defaults, unchanged so far.
 */
class AcmeConnectionConfig extends AbstractConnectionConfig {}

The base class provides four static methods; override only the ones where the platform diverges:

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::PULL_IN->value,
        'data_types' => array_map(static fn (SyncDataType $t) => $t->value, static::supportedDataTypes()),
        'frequency' => SyncFrequency::Daily->value,
    ];
}

Every method reads through static::, so an override takes effect in the inherited methods too: narrowing supportedDataTypes() changes both the options schema() offers and the defaults defaultConfiguration() writes.

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 a connection starts with before anyone edits it (see step 3 for when they are written).

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 can only be pulled from.
    return [SyncDirection::PULL_IN];
}

3. Implement HasConnectionConfiguration on the integration

Open your integration 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\AbstractIntegration;

class AcmeIntegration extends AbstractIntegration 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::saving() writes the default configuration into every SystemConnection of this driver that has none yet — on the first save that knows its system_id, which for a connection registered by a shop plugin is the update after /register, not the create. 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 integration's methods:

protected function fetchBulkExportBatches(SystemConnection $connection, ExportRequest $request, Closure $onBatch): void
{
    $direction = $connection->configuration['direction'] ?? SyncDirection::PULL_IN->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