Appearance
Nova Action
Class Structure
- Actions should be final by default and extend either
ActionorDestructiveAction:
php
final class YourAction extends Action
{
}
// For destructive operations
final class DeleteSomethingAction extends DestructiveAction
{
}- Actions are organized in an
Nova/Actionsdirectory within their respective module:
text
app/YourModule/Nova/Actions/YourAction.phpCommon Patterns
Base Classes
- Regular actions extend
\App\Nova\Actions\Action - Destructive actions extend
\App\Nova\Actions\DestructiveAction
Action Types
- Simple Actions
php
final class SendPasswordResetEmailAction extends Action
{
public function handle(ActionFields $fields, Collection $members): void
{
// Implementation
}
}- Destructive Actions
php
final class CancelUnpaidInstallmentAction extends DestructiveAction
{
public function handle(ActionFields $fields, Collection $installments): void
{
// Implementation
}
}- Actions with Fields
php
final class ExtendMembershipTimeAction extends Action
{
public function fields(NovaRequest $request): array
{
return [
DateTime::make('Extend Until'),
Text::make('Reason'),
];
}
}Best Practices
Naming Conventions
- Use verb-noun format:
SendEmailAction,CancelSubscriptionAction - End with "Action" suffix
- Name should clearly indicate the action's purpose
- Use verb-noun format:
Type Safety
php
public function handle(ActionFields $fields, Collection $models): mixed
{
/** @var \App\Modules\YourModule\Models\YourModel $model */
foreach ($models as $model) {
// Implementation
}
}- Validation
php
public function fields(NovaRequest $request): array
{
return [
Text::make('Reason')
->rules('required', 'min:10'),
];
}- Error Handling
php
public function handle(ActionFields $fields, Collection $models): mixed
{
try {
// Implementation
} catch (Exception $e) {
return Action::danger('Action failed: ' . $e->getMessage());
}
}Examples
Simple Action
php
namespace App\Member\Nova\Actions;
use App\Nova\Actions\Action;use Illuminate\Support\Collection;use Laravel\Nova\Actions\ActionFields;
final class ResendWelcomeEmailAction extends Action
{
public $name = 'Resend Welcome Email';
public function handle(ActionFields $fields, Collection $models): mixed
{
foreach ($models as $member) {
// Implementation
}
return Action::message('Welcome emails sent successfully');
}
}Destructive Action with Confirmation
php
namespace App\Payment\Nova\Actions;
use App\Nova\Actions\DestructiveAction;use Illuminate\Support\Collection;use Laravel\Nova\Actions\ActionFields;
final class CancelUnpaidInstallmentAction extends DestructiveAction
{
public $name = 'Cancel Unpaid Installment';
public function handle(ActionFields $fields, Collection $models): mixed
{
return $this->transaction(function () use ($models) {
foreach ($models as $installment) {
// Implementation
}
return Action::message('Installments cancelled successfully');
});
}
public function confirmButtonText(): string
{
return 'Yes, Cancel These Installments';
}
}Action with Fields and Validation
php
namespace App\Member\Nova\Actions;
use App\Nova\Actions\Action;use Illuminate\Support\Collection;use Laravel\Nova\Actions\ActionFields;use Laravel\Nova\Fields\DateTime;use Laravel\Nova\Fields\Text;use Laravel\Nova\Http\Requests\NovaRequest;
final class ExtendMembershipTimeAction extends Action
{
public $name = 'Extend Membership';
public function fields(NovaRequest $request): array
{
return [
DateTime::make('Extend Until')
->rules('required', 'after:today'),
Text::make('Reason')
->rules('required', 'min:10')
->help('Explain why this membership is being extended'),
];
}
public function handle(ActionFields $fields, Collection $models): mixed
{
return $this->transaction(function () use ($fields, $models) {
foreach ($models as $member) {
// Implementation
}
return Action::message('Memberships extended successfully');
});
}
}