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
AbstractIntegrationand is registered inIntegrationRegistry. 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¶
- Connection Config Fields — all field types, serialized shapes, and
options()input formats - Connection Widget — how the schema is served and how the widget renders it
- Embed the Widget — how to place the widget on an external site
- WooCommerce Integration —
WooCommerceIntegrationandWooCommerceConnectionConfigimplement every step above