Actions

Actions are a mechanism for performing tasks on one or more models – for example, deleting comments, or locking a post.

Each item's context menu is really just a list of actions, and extensions can add their own into the mix.

The post controls menu is made up of a list of Actions.

Defining an Action

To define a new action, extend the Waterhole\Actions\Action class. Give your action a label, an icon, and a run method to execute your task on a collection of $items:

use Illuminate\Support\Collection;
use Waterhole\Actions\Action;

class Delete extends Action
{
    public function label(Collection $items): string
    {
        return 'Delete';
    }

    public function icon(Collection $items): ?string
    {
        return 'tabler-trash';
    }

    public function run(Collection $items)
    {
        $items->each->delete();

        session()->flash('success', 'Items deleted.');
    }
}

Filtering

Most actions only apply to certain types of items. For example, the Mark as Read action can only be applied to unread posts. To enforce this, implement the appliesTo method in your action:

public function appliesTo($item): bool
{
    return $item instanceof Post && $item->isUnread();
}

Authorization

Some actions can only be performed by users with special privileges. To enforce this, implement the authorize method in your action:

public function authorize(?User $user, $item): bool
{
    return $user && $user->can('waterhole.moderate', $item);
}

See the Authorization page for more information about how Waterhole's permission system works.

Confirmation & Input

Some actions may need confirmation or additional input from the user before they are executed. To show a confirmation dialog, return a confirmation message from the confirm method. You can customize the "confirm" button text with the confirmButton method:

public function confirm(Collection $items)
{
    return 'Are you sure you want to do this?';
}

public function confirmButton(Collection $items): string
{
    return 'Yes';
}

You can also return a view from confirm, which is useful if you need to collect information to use in the run method:

public function confirm(Collection $items)
{
    return view('my-action'); // contains <input name="foo">
}

public function run(Collection $items)
{
    $name = request()->input('foo');

    // ...
}

Destructive Actions

If your action is potentially destructive, set the $destructive property to true to give the action a special appearance:

public bool $destructive = true;

Responses & Streams

In your action's run method, you can optionally return a response, such as a redirect or a file download:

public function run(Collection $items)
{
    return redirect($items[0]->url);
}

If you don't return anything, Waterhole will keep the user on the current page. You can stream partial updates to the page by implementing the stream method, returning an array of Turbo Streams:

public function stream($item)
{
    return [
        TurboStream::replace(new Components\PostListItem($item)),
    ];
}

See the Frontend page for more information about how Turbo Streams work.

Links

Some actions don't actually perform an action at all – they just redirect to another location. A good example is the "edit post" action, which just sends the user to the post's edit route.

For cases like this, do not return a redirect response from the run method. Rather, extend the Waterhole\Actions\Link class and implement the url method:

use Waterhole\Actions\Link;

class EditPost extends Link
{
    // ...

    public function url($item): string
    {
        return $item->edit_url;
    }
}

This will render the action as an <a> element rather than a <button>.

Conditional Rendering

If you only want your action to render in menus in a certain context (e.g. only in the Control Panel), or you don't want it to render in menus at all (e.g. you want to render it manually in a view), you can implement the shouldRender method:

public function shouldRender(Collection $models): bool
{
    return Waterhole::isCpRoute();
}

Registering an Action

Register your action using the Actions extender in your service provider:

Extend\Actions::add(Delete::class);

Actionables

Actions can only be applied to models that have been registered as actionable. To register a model as actionable, use the Actionables extender:

Extend\Actionables::add('dogs', App\Models\Dog::class);

Rendering Action Buttons

Waterhole provides various UI components to render action buttons and menus:

Action Button

Render a single button that will execute the given action on the given item(s).

<x-waterhole::action-button
    :action="Acme\Example\Actions\MyAction::class"
    :for="$item"
/>

Action Buttons

Render all of the actions for the given item(s). If you specify a limit, remaining actions will be displayed in an overflow menu.

<x-waterhole::action-buttons
    :for="$item"
    :button-attributes="['class' => 'btn btn--icon btn--transparent']"
    :limit="3"
    tooltips
/>

Action Menu

Render a menu button containing all of the actions that apply to the given item(s).

<x-waterhole::action-menu
    :for="$item"
    placement="bottom-start"
/>