Adminix Documentation Help

Actions

Adminix action metadata is the shared contract for existing action-like modules. It does not replace current execution flows; links, modal togglers, and bulk actions still run through their existing routes, Blade views, and handlers.

AdminixActionInterface exposes:

use AlexKudrya\Adminix\Actions\AdminixActionInterface; use AlexKudrya\Adminix\Actions\ActionMetadataDto; /** @var AdminixActionInterface $action */ $metadata = $action->actionMetadata();

ActionMetadataDto contains:

  • name - stable action key;

  • title - visible label;

  • icon - Bootstrap icon class;

  • tooltip - optional hover text;

  • color - optional ColorsEnum value or CSS color string;

  • destructive - boolean marker for dangerous actions;

  • confirm - optional confirmation text;

  • criteria - optional visibility criteria.

The contract is currently implemented by:

  • LinkModule;

  • AdminixLinkModule;

  • ModalTogglerModule;

  • BulkAction.

use AlexKudrya\Adminix\Enums\ColorsEnum; use AlexKudrya\Adminix\Modules\Link\LinkModule; use AlexKudrya\Adminix\Modules\List\BulkAction; use AlexKudrya\Adminix\Modules\Modal\ModalTogglerModule; LinkModule::name('ban-user') ->title('Ban user') ->icon('bi bi-person-fill-slash') ->tooltip('Ban this user') ->confirm('Ban this user?') ->criteria([['status', '!=', 'banned']]) ->color(ColorsEnum::CRIMSON) ->destructive(); ModalTogglerModule::name('edit-user') ->title('Edit') ->icon('bi bi-pencil') ->modalName('edit-user-modal') ->tooltip('Open edit modal') ->color(ColorsEnum::TEAL); BulkAction::make('archive') ->title('Archive selected') ->icon('bi bi-archive') ->tooltip('Archive selected records') ->confirm('Archive selected records?') ->color('var(--adminix-danger)') ->destructive() ->handler(ArchiveSelectedRecords::class);

Execution boundaries

Action metadata is descriptive. Do not use it as authorization, persistence, or routing input by itself.

State-changing actions must still be resolved from server-side Adminix configuration:

  • row actions are configured through ListModule::addAction()/addActions();

  • bulk actions are configured through ListModule::addBulkAction()/addBulkActions();

  • modal togglers open configured modal modules;

  • selected IDs, page params, relation context, and list scope remain server-verified.

Future action features can build on ActionMetadataDto without making browser-submitted action metadata authoritative.

Security checklist

Action handlers run inside privileged admin routes. Keep every write tied to server-side Adminix configuration:

  • use $request->query() for bulk writes instead of rebuilding a query from request input;

  • read only declared action fields through $request->field()/$request->fields();

  • treat selected IDs as already filtered by Adminix, and never re-add raw browser IDs to a fresh datasource query;

  • do not read datasource names, primary keys, writable columns, tenant IDs, parent IDs, or param:* values from browser metadata;

  • keep authorization, tenant/user ownership, and destructive-action checks in server-side handlers or policies;

  • build redirect URLs, modal bodies, downloads, and new-tab targets from trusted server-side state.

Generating handlers

Use the action generator for reusable bulk action handlers:

php artisan make:adminix_action ArchiveSelectedRecordsAction --tests

The generated class lives in app/Adminix/Actions, implements BulkActionHandlerInterface, and receives a BulkActionRequest. The optional test stub lives in tests/Feature/Adminix/Actions.

make:adminix_action currently supports --type=bulk. The generated handler is intentionally not registered automatically; attach it to a server-defined action after reviewing the code:

BulkAction::make('archive') ->title('Archive selected') ->handler(ArchiveSelectedRecordsAction::class);

Action fields

BulkAction can declare input fields that are shown before the action is submitted. Adminix renders these fields in the bulk action form only when the matching action is selected. During execution, BulkActionExecutor reads only fields declared on the selected server-side BulkAction; forged extra keys are ignored.

use AlexKudrya\Adminix\Actions\ActionField; use AlexKudrya\Adminix\Actions\ActionFieldTypeEnum; use AlexKudrya\Adminix\Modules\List\BulkAction; use AlexKudrya\Adminix\Modules\SelectRecord; BulkAction::make('change_status') ->title('Change status') ->addFields( ActionField::make('status', 'Status') ->type(ActionFieldTypeEnum::SELECT) ->required() ->addSelectRecords( SelectRecord::name('Review')->value('review'), SelectRecord::name('Archived')->value('archived') ), ActionField::make('reason', 'Reason') ->type(ActionFieldTypeEnum::TEXTAREA) ->placeholder('Reason for the change'), ActionField::make('notify', 'Notify users') ->type(ActionFieldTypeEnum::BOOLEAN) ) ->handler(ChangeStatusAction::class);

Handlers read normalized values from BulkActionRequest:

use AlexKudrya\Adminix\Modules\List\BulkActionHandlerInterface; use AlexKudrya\Adminix\Modules\List\BulkActionRequest; use AlexKudrya\Adminix\Modules\List\BulkActionResult; final class ChangeStatusAction implements BulkActionHandlerInterface { public function handle(BulkActionRequest $request): BulkActionResult { $status = $request->field('status'); $reason = $request->field('reason'); $notify = $request->field('notify', false); $request->query()->update([ 'status' => $status, ]); return BulkActionResult::success('Status changed.'); } }

Supported field types:

  • STRING;

  • TEXTAREA;

  • INTEGER;

  • FLOAT;

  • BOOLEAN;

  • DATE;

  • DATETIME;

  • SELECT.

Server-side normalization:

  • unknown field keys are ignored;

  • required fields are checked before the handler runs;

  • integer and float fields must contain numeric scalar values;

  • boolean fields are normalized to true/false;

  • select fields must match one of the configured SelectRecord values when options are configured.

Action fields are handler input only. They do not change list scope, selected IDs, authorization, datasource, or writable columns.

Queued bulk actions

Use Laravel queues for slow bulk handlers:

use AlexKudrya\Adminix\Modules\List\BulkAction; BulkAction::make('archive') ->title('Archive selected') ->queued('Archive job queued.') ->progressTrigger('archive-progress') ->onConnection('redis') ->onQueue('adminix') ->handler(ArchiveSelectedRecordsAction::class);

Queued actions use the same form and endpoint as normal bulk actions. Before dispatching the job, Adminix verifies the signed page params, optional relation context, selected IDs, current list scope, declared action fields, batch limit, and action authorization. The HTTP request then returns the queued() message as the normal success toast. Adminix records a bulk.queued audit event after successful dispatch.

When the job runs, Adminix re-resolves the page, module, relation context, and action from server-side configuration. It also reapplies the saved query parameters for filters, search, lenses, and sorting, then filters selected IDs against the current server-side list scope before calling the handler. After the handler finishes, Adminix records the normal bulk.executed audit event with the handler result.

Queued action options:

  • queued('Message') enables queued execution and sets the immediate toast message.

  • progressTrigger('archive-progress') starts a matching ProgressBarModule trigger in the browser.

  • onConnection('redis') optionally chooses the Laravel queue connection.

  • onQueue('adminix') optionally chooses the queue name.

Queued handlers receive the same BulkActionRequest contract. They should return success, error, or validation results for logs and future notification/progress integrations. Browser response types such as redirects, downloads, and new-tab handoffs are useful for synchronous handlers only because queued jobs run after the original HTTP request has ended.

Action responses

BulkActionResult supports the normal message outcomes and typed response outcomes:

use AlexKudrya\Adminix\Modules\List\BulkActionResult; return BulkActionResult::success('Records updated.'); return BulkActionResult::error('Unable to update records.'); return BulkActionResult::validation(['adminix_bulk_ids' => ['Select records first.']]); return BulkActionResult::redirect('/adminix/orders?status=archived', 'Redirecting.'); return BulkActionResult::openInNewTab('/adminix/orders/export.csv', 'Opening export.'); return BulkActionResult::modal( title: 'Export queued', body: 'The export job has started.', message: 'Export queued.', severity: 'success' ); return BulkActionResult::download(response('csv...', 200, [ 'Content-Type' => 'text/csv', 'Content-Disposition' => 'attachment; filename="orders.csv"', ]));

Response behavior:

  • success, error, and validation redirect back with the normal Adminix toast and validation errors;

  • redirect redirects to the server-provided URL with the normal session toast;

  • openInNewTab returns a small safe handoff page that attempts to open the server-provided URL and includes fallback links;

  • modal redirects back, renders a session-backed Bootstrap modal, and shows the normal toast;

  • download returns the provided Symfony/Laravel response directly.

The response target is resolved by the server-side handler. Do not derive redirect URLs, modal content, download responses, or new-tab targets from browser-owned datasource, primary key, writable fields, or tenant context.

Last modified: 21 June 2026