Skip to content

Actions Reference

An action is a custom button that appears on one or more operations (list, view, etc.) and triggers a dedicated service method. Use actions for domain-specific operations like "Approve", "Send Notification", "Generate PDF", or "Mark as Paid".


Array Shape

Corrected 2026-08-02

actions is a map keyed by action key, not a flat JSON array. This page previously showed "actions": [{...}] — copying that literally breaks ModuleScaffolder's foreach ($config['actions'] as $actionKey => $action) loop (a JSON array decodes to integer keys 0, 1, ... in PHP, not the action's own name). Confirmed against the real scaffolding code and every real test fixture that builds this config; see generator-engine's own actions-suite example for a config that was actually generated and executed end-to-end.

actions is an object at the top level of the module config, keyed by a unique action key (conventionally the same as name).

json
"actions": {
  "approve": {
    "name":        "approve",
    "label":       "Approve",
    "hasUI":       false,
    "uiType":      null,
    "urlParams":   ["uuid"],
    "methodName":  "approve",
    "serviceName": "ApproveService",
    "operations": {
      "list":   { "enabled": false, "endpoint": { "method": "POST", "path": "/products/{uuid}/approve", "permission": "Products.approve" } },
      "view":   { "enabled": true,  "endpoint": { "method": "POST", "path": "/products/{uuid}/approve", "permission": "Products.approve" } },
      "create": { "enabled": false },
      "edit":   { "enabled": false },
      "delete": { "enabled": false }
    }
  }
}

Action Object Keys

KeyTypeDefaultDescription
namestringrequiredcamelCase or snake_case action name. Derives class and method names.
labelstringsame as nameButton label shown in the UI.
hasUIbooleanfalseSet true if the action renders a modal/page for user input before executing.
uiTypestring|nullnull"modal" or "page" when hasUI is true.
urlParamsstring[][]URL path parameter names injected into the service method signature (e.g. ["uuid"]string $uuid).
methodNamestring""Override for the generated PHP method name. Defaults to StudlyCase of name.
serviceNamestring""Override for the generated service class name (without module prefix and without "Service" suffix).
placementstring"more"Where the button renders on the view page/modal. "main" places it in the primary button row next to Edit; anything else (including omitting the key) puts it in the "More actions" dropdown menu. Defaults to "more" so adding an action never silently promotes it into the primary row.
iconstring""lucide-vue-next icon component name (e.g. "CheckIcon") rendered next to the button/menu-item label. Falls back to "ZapIcon" when empty.
destructivebooleanfalseWhen true and placement is "more", the dropdown menu item is styled with destructive (red) text classes — use for actions like "Revoke" or "Deactivate". No effect on "main"-placement buttons.
operationsobjectall disabledWhich module operations show the action button.

operations Object

json
"operations": {
  "list":   { "enabled": true,  "endpoint": { "method": "POST", "path": "/products/{uuid}/action", "permission": "Products.action" } },
  "view":   { "enabled": true,  "endpoint": { "method": "POST", "path": "/products/{uuid}/action", "permission": "Products.action" } },
  "create": { "enabled": false },
  "edit":   { "enabled": false },
  "delete": { "enabled": false }
}

Each operation entry:

KeyTypeDescription
enabledbooleanWhether the action button appears on this operation's page.
endpointobject{ method, path, permission } — the API route for this action.

Generated Files

For each action, the generator creates:

FileClass name pattern
Services/{Module}{ActionName}Service.phpProductsApproveService

The controller gets a new method wired to the action endpoint. If hasUI is true, a modal or page component is also generated.


urlParams Explained

When urlParams: ["uuid"], the generated service receives the URL parameter as a typed PHP argument:

php
public function process(array $data, string $uuid): array
{
    $record = ProductsModel::where('uuid', $uuid)->firstOrFail();
    // ...
}

Multiple params: urlParams: ["uuid", "year"]process(array $data, string $uuid, string $year).


Examples

Simple no-UI action (list + view)

json
"actions": {
  "deactivate": {
    "name":      "deactivate",
    "label":     "Deactivate",
    "hasUI":     false,
    "urlParams": ["uuid"],
    "operations": {
      "list":   { "enabled": true,  "endpoint": { "method": "POST", "path": "/products/{uuid}/deactivate", "permission": "Products.deactivate" } },
      "view":   { "enabled": true,  "endpoint": { "method": "POST", "path": "/products/{uuid}/deactivate", "permission": "Products.deactivate" } },
      "create": { "enabled": false },
      "edit":   { "enabled": false },
      "delete": { "enabled": false }
    }
  }
}
json
"actions": {
  "sendNotification": {
    "name":    "sendNotification",
    "label":   "Send Notification",
    "hasUI":   true,
    "uiType":  "modal",
    "urlParams": ["uuid"],
    "operations": {
      "view":   { "enabled": true, "endpoint": { "method": "POST", "path": "/users/{uuid}/send-notification", "permission": "Users.sendNotification" } },
      "list":   { "enabled": false },
      "create": { "enabled": false },
      "edit":   { "enabled": false },
      "delete": { "enabled": false }
    }
  }
}

Released under the Apache-2.0 License.