Skip to content

Actions ​

Les actions sont ce qu'on peut faire avec un enregistrement (ou avec le Resource entier). Elles se déclarent dans actions() :

php
public function actions(): array
{
    return [
        ViewAction::make(),
        EditAction::make(),
        DeleteAction::make()->bulk(),
        RestoreAction::make(),
        ForceDeleteAction::make(),

        Action::make('publish')
            ->icon('check')
            ->requiresConfirmation()
            ->bulk()
            ->handle(function (array $data, Post $post): array {
                $post->update(['is_published' => true]);

                return $post->only(['id', 'title', 'is_published']);
            }),

        Action::make('purge_drafts')
            ->label('Purge drafts')
            ->icon('trash')
            ->standalone()
            ->requiresConfirmation()
            ->handle(function (array $data): array {
                return ['deleted' => Post::where('is_published', false)->delete()];
            }),
    ];
}

L'API d'Action ​

MéthodeDescription
make(string $name)Nom interne de l'action (il apparaît dans l'URL de l'endpoint).
label(string $label)Libellé du bouton. Par défaut, Str::headline($name).
icon(string $icon)Nom d'icône (voir « Icônes » plus bas).
standalone(bool = true)Action sur le Resource entier, pas sur un enregistrement précis (par exemple, « Purge drafts »).
bulk(bool = true)Peut s'appliquer à une sélection de plusieurs enregistrements à la fois ; réutilise le même endpoint par enregistrement, il n'y a pas d'API bulk séparée.
requiresConfirmation(bool = true)Le frontend demande une confirmation avant de l'exécuter.
withTrashed(bool = true)L'action opère sur des enregistrements en soft delete (nécessaire pour restore/forceDelete).
authorize(Closure $callback)Décide si l'action est visible et exécutable. Reçoit les mêmes arguments que handle(). Voir « Autorisation ».
schema(array<Field> $fields)Champs d'un formulaire propre à l'action. Voir « Actions avec formulaire ».
url(Closure $callback, bool $openInNewTab = false)Transforme l'action en lien. Voir « Actions qui sont un lien ».
handle(Closure $callback)La logique de l'action. Reçoit (array $data, Model $record) pour les actions sur un enregistrement, ou seulement (array $data) pour standalone().

Autorisation ​

  • Avec authorize(), l'action utilise cette closure et rien d'autre.
  • Sans authorize(), l'action exige canEdit() sur l'enregistrement, ou canCreate() si elle est standalone(). Une action qui doit rester ouverte à des utilisateurs qui ne peuvent pas modifier doit déclarer authorize() explicitement.
  • Les actions intégrées suivent leur habilité : ViewAction avec canView(), EditAction et RestoreAction avec canEdit(), DeleteAction et ForceDeleteAction avec canDelete(). Si vous déclarez authorize() sur l'une d'elles, les deux conditions doivent être remplies.
  • Un Resource que l'utilisateur ne peut pas lister (canViewAny() faux) ferme toutes ses actions.

Actions avec formulaire ​

schema() demande des données à l'utilisateur avant d'exécuter l'action. Une action standalone() avec schema() ouvre le formulaire sans aucun enregistrement :

php
Action::make('invite')
    ->label('Invite people')
    ->icon('paper-airplane')
    ->standalone()
    ->schema([
        Tags::make('emails')
            ->itemRules('email')
            ->rules('required', 'array'),
        Select::make('role')
            ->options(['editor' => 'Editor', 'viewer' => 'Viewer'])
            ->rules('required'),
    ])
    ->handle(function (array $data): array {
        Invitations::send($data['emails'], $data['role']);

        return ['message' => count($data['emails']).' invitations sent'];
    })

handle() reçoit les données déjà validées par rapport aux règles des champs. Les erreurs de validation s'affichent avec le libellé du champ, pas avec son nom interne. Les champs Relation d'une action ne servent leurs options qu'aux utilisateurs qui peuvent l'exécuter, et leur query() s'applique aussi à l'envoi du formulaire.

Messages de l'action ​

Si le handler retourne un tableau avec une clé message de type texte, le panneau l'affiche à l'utilisateur une fois l'action terminée. Les autres clés sont retournées telles quelles au frontend. Un handler qui ne retourne rien répond { "success": true }.

Actions qui sont un lien ​

Avec url(), une action n'exécute aucun handler : elle navigue. La closure reçoit l'enregistrement et retourne l'URL, ou null pour ne pas afficher l'action sur cette ligne.

php
Action::make('open_public')
    ->label('Open public page')
    ->icon('eye')
    ->url(fn (Post $post): ?string => $post->is_published ? route('posts.show', $post) : null, openInNewTab: true)

Actions intégrées ​

ViewAction, EditAction, DeleteAction, RestoreAction et ForceDeleteAction sont des Action avec une icône (et, pour les deux dernières, withTrashed()/requiresConfirmation()) prédéfinies :

php
ViewAction::make();        // icône 'eye'
EditAction::make();        // icône 'pencil'
DeleteAction::make();      // icône 'trash'
RestoreAction::make();     // icône 'arrow-uturn-left', withTrashed()
ForceDeleteAction::make(); // icône 'trash', withTrashed(), requiresConfirmation()

DeleteAction, RestoreAction et ForceDeleteAction reçoivent un handler par défaut si vous n'en déclarez aucun : supprimer/restaurer/forcer la suppression de l'enregistrement via Eloquent (en respectant SoftDeletes quand le modèle l'utilise). Il n'est nécessaire d'appeler handle() explicitement que si vous voulez remplacer ce comportement.

Bloquer une suppression depuis le modèle : si un listener de l'événement deleting de votre modèle lève une RuntimeException (par exemple, « il a encore des dépendants »), l'endpoint de suppression l'attrape et répond 422 avec le message de l'exception, au lieu d'un 500 sans corps que le frontend ne pourrait pas afficher. C'est le même schéma que celui de Filament avec DeleteAction::configureUsing().

php
protected static function booted(): void
{
    static::deleting(function (AcademicYear $year): void {
        if ($year->courses()->exists()) {
            throw new RuntimeException('Ce cours a encore des matières.');
        }
    });
}

AttachAction et DetachAction sont propres aux Relation managers ; elles y sont expliquées en détail.

Icônes ​

icon() prend un nom d'un sous-ensemble choisi de Heroicons qu'Arrel embarque déjà. Ce n'est pas tout Heroicons : seulement les icônes qu'Arrel utilise dans ses propres actions, widgets et navigation, pour ne pas payer de taille de bundle pour des icônes qu'aucun panneau n'utilise.

eye, trash, pencil, pencil-square, check, plus, plus-circle, x-mark, inbox, paper-airplane, key, envelope-open, arrow-uturn-left, document-text, home, arrow-right-on-rectangle, academic-cap, book-open, shield-check, squares-2x2, user-group, users, clipboard-document-check, bars-3, cog-6-tooth, chart-bar, calendar-days, calendar, chat-bubble-left-right, document-duplicate, information-circle, clock, user, exclamation-triangle, arrow-down-tray et bell.

Un nom hors de cette liste ne s'affiche pas. Les icônes se trouvent dans resources/js/support/icons.ts du paquet.

Actions standard vs. actions sur mesure ​

ViewAction/EditAction/DeleteAction n'ont rien de magique : ce sont des Action avec des valeurs par défaut pratiques. Toute action sur mesure se définit exactement de la même façon, avec son propre schema() et handle() :

php
Action::make('rename')
    ->icon('pencil-square')
    ->schema([
        Text::make('title')->rules('required', 'string', 'max:255'),
    ])
    ->handle(function (array $data, Post $post): array {
        $post->update(['title' => $data['title']]);

        return $post->only(['id', 'title']);
    })