Acciones
Las acciones son lo que se puede hacer con un registro (o con el Resource entero). Se declaran dentro de actions():
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()];
}),
];
}API de Action
| Método | Descripción |
|---|---|
make(string $name) | Nombre interno de la acción (aparece en la URL del endpoint). |
label(string $label) | Etiqueta del botón. Por defecto, Str::headline($name). |
icon(string $icon) | Nombre de icono (véase «Iconos» más abajo). |
standalone(bool = true) | Acción sobre el Resource entero, no sobre un registro concreto (por ejemplo, «Purge drafts»). |
bulk(bool = true) | Se puede aplicar a una selección de varios registros a la vez; reutiliza el mismo endpoint por registro, no hay una API bulk separada. |
requiresConfirmation(bool = true) | El frontend pide confirmación antes de ejecutarla. |
withTrashed(bool = true) | La acción opera sobre registros con soft delete (necesario para restore/forceDelete). |
authorize(Closure $callback) | Decide si la acción es visible y ejecutable. Recibe los mismos argumentos que handle(). Véase «Autorización». |
schema(array<Field> $fields) | Campos de un formulario propio de la acción. Véase «Acciones con formulario». |
url(Closure $callback, bool $openInNewTab = false) | Convierte la acción en un enlace. Véase «Acciones que son un enlace». |
handle(Closure $callback) | La lógica de la acción. Recibe (array $data, Model $record) para acciones sobre un registro, o solo (array $data) para standalone(). |
Autorización
- Con
authorize(), la acción usa esa closure y nada más. - Sin
authorize(), la acción exigecanEdit()sobre el registro, ocanCreate()si esstandalone(). Una acción que debe estar abierta a usuarios que no pueden editar debe declararauthorize()explícitamente. - Las acciones incluidas siguen su habilidad:
ViewActionconcanView(),EditActionyRestoreActionconcanEdit(),DeleteActionyForceDeleteActionconcanDelete(). Si declarasauthorize()en una de ellas, deben cumplirse las dos condiciones. - Un Resource que el usuario no puede listar (
canViewAny()falso) cierra todas sus acciones.
Acciones con formulario
schema() pide datos al usuario antes de ejecutar la acción. Un standalone() con schema() abre el formulario sin ningún registro:
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() recibe los datos ya validados contra las reglas de los campos. Los errores de validación salen con la etiqueta del campo, no con su nombre interno. Los campos Relation de una acción sirven sus opciones solo a los usuarios que pueden ejecutarla, y su query() se aplica también al enviar el formulario.
Mensajes de la acción
Si el handler devuelve un array con una clave message de tipo texto, el panel se lo muestra al usuario una vez hecha la acción. El resto de claves se devuelven al frontend tal cual. Un handler que no devuelve nada responde { "success": true }.
Acciones que son un enlace
Con url(), una acción no ejecuta ningún handler: navega. La closure recibe el registro y devuelve la URL, o null para no mostrar la acción en esa fila.
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)Acciones incluidas
ViewAction, EditAction, DeleteAction, RestoreAction y ForceDeleteAction son Action con un icono (y, para las dos últimas, withTrashed()/requiresConfirmation()) predefinidos:
ViewAction::make(); // icono 'eye'
EditAction::make(); // icono 'pencil'
DeleteAction::make(); // icono 'trash'
RestoreAction::make(); // icono 'arrow-uturn-left', withTrashed()
ForceDeleteAction::make(); // icono 'trash', withTrashed(), requiresConfirmation()DeleteAction, RestoreAction y ForceDeleteAction reciben un handler por defecto si no declaras ninguno: borrar/restaurar/forzar el borrado del registro vía Eloquent (respetando SoftDeletes cuando el modelo lo usa). Solo hace falta llamar a handle() explícitamente cuando quieras sustituir este comportamiento.
Bloquear un borrado desde el modelo: si un listener del evento deleting de tu modelo lanza una RuntimeException (por ejemplo, «todavía tiene dependientes»), el endpoint de borrado la captura y responde 422 con el mensaje de la excepción, en lugar de un 500 sin cuerpo que el frontend no pueda mostrar. Es el mismo patrón que usa Filament con DeleteAction::configureUsing().
protected static function booted(): void
{
static::deleting(function (AcademicYear $year): void {
if ($year->courses()->exists()) {
throw new RuntimeException('Este curso todavía tiene asignaturas.');
}
});
}AttachAction y DetachAction son específicas de los Relation managers; se explican allí en detalle.
Iconos
icon() toma un nombre de un subconjunto curado de Heroicons que Arrel ya lleva incorporado. No es todo Heroicons: solo los iconos que Arrel usa en sus propias acciones, widgets y navegación, para no pagar tamaño de bundle por iconos que ningún panel usa.
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 y bell.
Un nombre fuera de esta lista no se renderiza. Los iconos están en resources/js/support/icons.ts del paquete.
Acciones estándar vs. acciones a medida
ViewAction/EditAction/DeleteAction no hacen nada mágico: son Action con valores por defecto convenientes. Cualquier acción a medida se define exactamente igual, con su propio schema() y handle():
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']);
})