Skip to content

Remote API Admin UI ​

Remote API integrates with Modularous forms (link picker) and data tables (sync toolbar).

Form Input: remote-api ​

Module Config ​

php
'inputs' => [
    'remote_id' => [
        'type' => 'remote-api',
        'label' => 'Remote record',
        'name' => 'remote_id',
        // Optional named catalog from connector catalogs.*
        // 'catalog' => 'regions',
    ],
],

Hydrate → Vue Pipeline ​

StageOutput
Config type: remote-apiRemoteApiHydrate
Hydrated schematype: input-remote-api
Vue registryVInputRemoteApi (RemoteApi.vue)

Hydrate adds:

  • itemValue — Resolved from mapping (default remote JSON id key).
  • itemTitle — From catalog_title_key config (default name).
  • items — Prefetched catalog when not running in console.
  • catalogTotal — Row count for incomplete-hydrate detection.
  • catalogEndpoint — URL to listRemoteCatalog for async fetch.

Frontend Behavior (RemoteApi.vue) ​

  • Renders a v-select bound to virtual remote_id.
  • On mount, if hydrated items are empty or catalogTotal exceeds loaded rows, fetches catalogEndpoint via GET.
  • Validates completeness: warns in console if response rows < expected total.
  • Prepends a “Please Select” row when items exist.

Catalog API ​

GET {panel}/{module}/{route}/list-remote-catalog?catalog={key}

Response:

json
{
  "data": [ { "id": 1, "name": "Example" } ],
  "meta": { "total": 1 }
}

Requires repository RemoteApiSourceTrait and enabled connector.

Table Toolbar Actions ​

When the repository uses RemoteApiSourceTrait and connector enabled is true, ManageRemoteApiSync::setTableActionsManageRemoteApiSync() appends actions from getRemoteApiActionSchema().

Edit Form Actions ​

When the same trait and connector are active, RemoteApiSourceTrait::getFormActionsRemoteApiSourceTrait() registers record-level actions on the edit form (not create):

Action keySchema nameScopeHTTP
sync_recordsyncRemoteform (edit)PUT …/sync-remote/{id}
previewpreviewRemoteform (edit)PUT …/preview-remote/{id}

Form actions are merged in FormActions::preloadFormActions() via repository->getFormActions(). Endpoints use route names with :id placeholders resolved by FormActions::getFormActions().

  • Shown only when editing (creatable: false) and the record has a linked remote_id.
  • syncRemote reloads the form on success (reloadOnSuccess: true).
  • previewRemote opens a modal with structured remote payload fields via responseModalAttributes, responseDisplay, responseFields, and useItemActions.

Table-scoped actions (sync_all, clear_cache) remain toolbar-only (scope: table).

Configured via connector actions array:

Action keySchema nameScopeHTTP
sync_recordsyncRemoteRow / form (edit)PUT …/sync-remote/{id}
sync_allsyncRemoteAlltablePOST …/sync-remote-all
clear_cacheclearRemoteCachetablePOST …/clear-remote-cache
previewpreviewRemoteRow / form (edit)PUT …/preview-remote/{id}

Table-scoped actions include confirmation modals (translated via messages.remote-api.* lang keys).

Example generated action shape:

php
[
    'name' => 'syncRemoteAll',
    'label' => 'Sync all from remote',
    'icon' => 'mdi-sync',
    'color' => 'primary',
    'scope' => 'table',
    'type' => 'request',
    'method' => 'post',
    'endpoint' => '{panel}.{module}.{route}.syncRemoteAll',
    'hasConfirmation' => true,
    'confirmationModalAttributes' => [ /* … */ ],
]

useItemActions handles confirmation via useDynamicModal when hasConfirmation is true.

Preview / Payload Display ​

previewRemote returns the full remote JSON in data, plus optional structured display metadata:

json
{
  "data": { "id": 12, "name": "Europe", "description": "Regional package", "packages": [ … ] },
  "display_mode": "fields",
  "display": [
    { "key": "name", "label": "Name", "value": "Europe" },
    { "key": "description", "label": "Description", "value": "Regional package" }
  ]
}

The edit-form action includes:

Action keyPurpose
responseDisplayfields (default) or raw (full JSON)
responseFieldsField map copied from connector preview config

Configure per connector:

php
'preview' => [
    'display' => 'fields', // or 'raw'
    'fields' => [
        ['key' => 'name', 'label' => 'Name'],
        ['key' => 'description', 'label' => 'Description'],
        ['key' => 'packageable.name', 'label' => 'Region'],
    ],
],

AbstractRemoteApiConnector::customizePreviewResponseDisplay() can reshape server-side display rows after values are extracted from the payload. The frontend prefers response.data.display when present, otherwise resolves values from responseFields + data.

syncRemoteAll Response ​

Success message summarizes created/updated/skipped counts. JSON includes:

json
{
  "message": "Synced 12 remote records (2 created, 10 updated).",
  "data": { "created": 2, "updated": 10, "skipped": 0, "total": 12, … },
  "meta": {
    "http_requests": { "total": 1, "by_url": { … } },
    "skipped_records": []
  }
}

clearRemoteCache Response ​

json
{ "message": "Remote API cache cleared." }

Flushes the full connector cache tag/version — use after remote bulk updates.

Controller Requirements ​

BaseController already uses ManageRemoteApiSync. Module controllers extend BaseController (or PanelController); no extra trait import needed per module.

Routes are registered automatically in RouteServiceProvider when the controller implements the action methods (always true via BaseController).

Disabling UI Surfaces ​

GoalHow
Hide all Remote API UI'enabled' => false in connector config
Hide sync buttons onlyRemove sync_all / clear_cache from actions
Keep link picker, no sync'actions' => [] but leave enabled true and form input

Misconfigured connectors (missing base URL) fail closed — no table actions, hydrate skips connector schema.

Row-Level Sync ​

Record-level syncRemote and previewRemote actions appear automatically on the edit form when sync_record / preview are listed in connector actions. They require a linked remote_id and are hidden on create.

To expose per-row sync on the index table instead, add a custom row action pointing to syncRemote:

php
// Example manual row action in module table config
[
    'type' => 'request',
    'method' => 'put',
    'endpoint' => '/panel/module-name/entity-name/sync-remote/:id',
    'label' => 'Sync from remote',
]