System Connections resource (/admin/system-connections)¶
Manages App\Models\SystemConnection — the links between a Tenant and an external e-commerce system. Each system connection holds the credentials and configuration needed to communicate with a specific system account.
Model attributes¶
| Attribute | Type | Notes |
|---|---|---|
id |
UUID | Primary key via HasUuids |
tenant_id |
UUID (FK), nullable | Owning Tenant; FK uses restrictOnDelete |
system_id |
UUID (FK) | Target System |
external_user_id |
string | User identifier on the external system |
name |
string, nullable | Optional label; not exposed on the admin form |
base_url |
string | Root URL of the external system installation |
credentials |
array, nullable | Sensitive credentials; stored as encrypted:json |
status |
SystemConnectionStatus |
Enum: active, inactive, error, pending, disconnected, uninstalled |
meta |
array | Arbitrary metadata; cast to array |
configuration |
array, nullable | Connector-widget opt-in per data domain; cast to array |
last_connected_at |
datetime | Timestamp of the last successful connection |
disconnected_at |
datetime, nullable | Set for exactly as long as status is disconnected; the grace-period deadline is derived from it |
deleted_at |
timestamp | Soft deletes enabled |
Status values¶
| Value | Badge colour | Meaning |
|---|---|---|
active |
Green (success) |
System connection is healthy and in use |
pending |
Amber (warning) |
Awaiting first successful contact |
error |
Red (danger) |
Last connection attempt failed |
inactive |
Grey (gray) |
Manually disabled |
disconnected |
Blue (info) |
Ended by the shop; data kept for the grace period, reversible |
uninstalled |
Red (danger) |
Ended by removing the plugin; everything deleted at once |
List view columns¶
| Column | Source | Behaviour |
|---|---|---|
| ID | system_connections.id |
Searchable |
| Tenant | tenant.name |
Searchable |
| System | system.name |
Searchable |
| External User ID | system_connections.external_user_id |
Searchable |
| Base URL | system_connections.base_url |
Searchable |
| Status | system_connections.status |
Badge with colours above |
| Last Connected At | system_connections.last_connected_at |
Date-time, sortable |
| Disconnected At | system_connections.disconnected_at |
Date-time, sortable; hidden by default |
| Created At | system_connections.created_at |
Date-time, sortable; hidden by default |
| Updated At | system_connections.updated_at |
Date-time, sortable; hidden by default |
| Deleted At | system_connections.deleted_at |
Date-time, sortable; hidden by default |
Filters: Tenant (relationship), System (relationship), Status (enum), Trashed.
Bulk actions: Delete (soft), Force Delete, Restore.
View page — A disconnected connection additionally shows Grace period ends, the derived
deadline from SystemConnection::graceDeadline(), rendered as a relative window with the absolute
date as a tooltip. The entry is hidden for every other status.
Role-based visibility¶
| UI element | SuperAdmin | PlatformSupport | Viewer |
|---|---|---|---|
| Create button | yes | — | — |
| Edit / Delete actions | yes | — | — |
| Bulk Sync | yes | own connections only | — |
| Credentials field on create | yes | — | — |
| Credentials field on edit | yes | — | — |
| Tenant selector options | all tenants | reachable tenants | reachable tenants |
PlatformSupport holds system_connections.view and system_connections.resync, and nothing else on this resource. Bulk Sync is therefore its only write-shaped action here, and it applies only to connections that are fully accessible — those pointing at a system the user operates. Viewer holds view alone.
Credentials — The Credentials textarea is hidden whenever the operation is not create and the user is not a SuperAdmin. Because system_connections.create is SuperAdmin-only, in practice only a SuperAdmin ever sees the field at all; the hidden() condition is the second guard rather than the first. Once saved, credentials cannot be read back through the UI by anyone but a SuperAdmin.
Tenant selector scoping — The Tenant Select binds tenant_id to the tenant relationship and passes modifyQueryUsing a closure calling Tenant::accessibleBy($user), so the dropdown lists only tenants within the user's reach. Since the form is only reachable by a SuperAdmin, for whom that scope short-circuits to everything, this too is a safety net rather than observable behaviour.
Sibling connections — A connection that lies under a reachable tenant but does not point at a system the user operates still appears in the list, read-only: status, last contact, health. SystemConnectionPolicy reads view from SystemConnection::accessibleBy() and update, delete and resync from SystemConnection::fullyAccessibleBy(). See Own connections in full, siblings by status.
Form fields (create / edit)¶
The create form is intended for development and debugging. System connections are normally created via API.
| Field | Type | Validation | Notes |
|---|---|---|---|
| Tenant | Select (relationship) |
Required | Bound to tenant_id; scoped to Tenant::accessibleBy($user) — see Role-based visibility above |
| System | Select (relationship) |
Required | Bound to system_id via system relation |
| External User ID | TextInput |
Required | |
| Base URL | TextInput |
Required, URL | |
| Credentials | Textarea |
Required on create | JSON; pretty-printed; stored as encrypted:json; hidden on edit for non-SuperAdmin |
| Status | Select |
Required | Default: pending |
| Configuration | KeyValue |
Optional | Connector-widget opt-in per data domain; read by SystemConnection::pullInCapabilities() |
| Meta | KeyValue |
Optional | Free-form key/value pairs |
| Last Connected At | DateTimePicker |
— | Read-only; hidden on create |
Edit page header actions: DeleteAction (soft delete), ForceDeleteAction, RestoreAction.
Soft-deleted system connections remain accessible on the edit route — getRecordRouteBindingEloquentQuery() strips SoftDeletingScope so trashed records can still be restored.
Bulk Sync action¶
The Bulk Sync header action on the connection edit page lets an operator manually trigger a full data export for a connection.
Availability: the button is enabled only when the connection is healthy (status active) and at least one data scope is available to sync. It is disabled with an explanatory tooltip otherwise: "Only active connections can be synced" for any non-active status, "This connection has no system" when the linked System is missing or soft-deleted, and "No active sync rule covers this connection" when no active SyncRule names the connection as source.
Interaction: clicking the button opens a modal with a CheckboxList labelled "Data scopes". The list holds exactly the domains SystemConnection::pullInCapabilities() returns — the platform's capabilities, narrowed by the connector-widget opt-in and by active outgoing SyncRules (see Sync Rules). All of them are pre-selected. A bulk-toggle control allows selecting or deselecting all scopes at once. A submitted scope that no active SyncRule covers is rejected with a validation error on the field rather than being quietly dropped. Three field rules enforce that together, and a hand-crafted Livewire payload has to clear all three: required (at least one scope), an explicit array shape rule, and the per-item in rule Filament derives from the option list itself. The shape rule is not redundant — the derived in rule binds at capabilities.*, which Laravel only applies to an array, so without it a scalar payload would pass validation untouched and then be decoded into an array by OptionsArrayStateCast after the gate.
Behaviour: on confirmation, a single BulkSyncJob is dispatched carrying all selected capabilities. The job resolves the integration for the connection's SystemDriver via IntegrationRegistry and calls requestBulkExport() once per capability. The job runs on the rabbitmq queue asynchronously — the operator's browser is not blocked.
BulkSyncJob implements ShouldBeUnique keyed on the connection ID. If a sync for the same connection is already queued or running, a new dispatch is silently dropped — only one sync per connection can be active at a time.
| Aspect | Detail |
|---|---|
| Available for | Connections with status active that have at least one rule-backed data scope |
| Modal field | CheckboxList of the connection's rule-backed SystemCapability values |
| Jobs dispatched | One BulkSyncJob per trigger (carries all selected capabilities) |
| Uniqueness | One active sync per connection (ShouldBeUnique) |
| Queue driver | rabbitmq |
| Success notification | "Bulk sync triggered" |
Source files¶
| File | Purpose |
|---|---|
app/Filament/Resources/SystemConnections/SystemConnectionResource.php |
Resource definition, query customisation |
app/Filament/Resources/SystemConnections/Schemas/SystemConnectionForm.php |
Form schema |
app/Filament/Resources/SystemConnections/Tables/SystemConnectionsTable.php |
Table columns, filters, actions |
app/Filament/Resources/SystemConnections/RelationManagers/SyncBatchesRelationManager.php |
Sync batches tab |
app/Filament/Resources/SystemConnections/Pages/ListSystemConnections.php |
List page |
app/Filament/Resources/SystemConnections/Pages/CreateSystemConnection.php |
Create page |
app/Filament/Resources/SystemConnections/Pages/ViewSystemConnection.php |
Detail page |
app/Filament/Resources/SystemConnections/Pages/EditSystemConnection.php |
Edit page with header actions |
app/Filament/Resources/SystemConnections/Pages/ConfigureSystemConnectionWidget.php |
Connector-widget configuration page |
See also¶
- Admin Panel — panel overview, authentication, auto-discovery
- Policies —
SystemConnectionPolicy, itsresyncgate, and the full/status-only split - Roles & Permissions — the roles, the permission matrix, and why reach is derived