Skip to main content

Actions

Resource actions are backend-defined operations that can run for one model from the detail page or for selected models from the index table.

Use actions for workflows such as approving, sending, exporting, archiving or impersonating. Keep direct create/edit/delete behaviour in the resource pages unless the workflow needs its own form or confirmation.

Create An Action

namespace App\Actions;

use App\Models\Product;
use Illuminate\Database\Eloquent\Model;
use Qore\Next\System\Action\ResourceAction;
use Qore\Next\System\Field\Field;
use Qore\Next\System\Fields\TextAreaField;

class ArchiveProductAction extends ResourceAction
{
public function label(): string
{
return __('common.archive');
}

public function icon(): string
{
return 'archive';
}

/**
* @return list<Field>
*/
protected function fields(): array
{
return [
TextAreaField::make('reason', __('common.reason'))
->setRules(fn () => ['required', 'string', 'max:1000']),
];
}

/**
* @param Product $model
* @param array<string, mixed> $validated
*/
public function handle(Model $model, array $validated): void
{
$model->forceFill([
'archived_at' => now(),
'archive_reason' => $validated['reason'],
])->save();
}
}

The action form is rendered as a FormNode. The validated payload is passed to handle() for each authorized selected model.

Register Actions On The Resource

use App\Actions\ArchiveProductAction;
use Qore\Next\System\Action\ResourceAction;

/**
* @return ResourceAction[]
*/
public function actions(): array
{
return [
new ArchiveProductAction,
];
}

QoreResource::getActions() sets the resource on each action and keys actions by their generated name.

Visibility

Actions use the same conditional visibility traits as fields:

new ArchiveProductAction()
->setIsShownOnIndex(fn () => true)
->setIsShownOnDetail(fn (Product $product) => ! $product->archived_at);

Index actions appear as table bulk actions. Detail actions appear in the actions dropdown for a single model.

Authorization

By default, an action authorizes each model with:

user()->can('update', $model);

Override authorizeForModel() when the action has a more specific rule:

public function authorizeForModel(Model $model): bool
{
return user()->can('archive', $model);
}

The action skips unauthorized models when running a bulk action.

Action Forms

An action with fields renders a modal form. An action without fields still renders a submit form, which makes destructive or auditable actions explicit.

Useful methods:

  • description(): ?string adds explanatory text above the form.
  • isDanger(): bool marks the dropdown item and submit button as dangerous.
  • getShouldReloadParentOnFormSuccess(): bool controls whether the parent node reloads after submit.
  • getShouldReloadGlobalsOnFormSuccess(): bool requests a globals refresh.
  • getNavigateUrlOnFormSuccess(): ?string navigates after success.

Logging

After handle() runs, Qore logs the action name and validated payload for each handled model:

qore()->logger()
->setEntity($model)
->setDescription($this->getName())
->setPayload($validated)
->save();

That log appears in the resource logbook for the model.