Nova 操作允许您对一个或多个 Eloquent 模型执行自定义任务。例如,您可以编写一个操作,向用户发送包含他们请求的帐户数据的电子邮件。或者,您可以编写一个操作将一组记录转移到另一个用户。
将操作附加到资源定义后,您可以从资源的索引或详细信息页面启动它。
如果为资源的表格行启用了操作显示,您也可以从资源的索引页面通过资源的操作下拉菜单启动操作。这些操作被称为“内联操作”。
Nova 操作可以使用 nova:action
Artisan 命令生成。默认情况下,所有操作都放置在 app/Nova/Actions
目录中。
php artisan nova:action EmailAccountProfile
您可以通过传递 --destructive
选项来生成 破坏性操作
php artisan nova:action DeleteUserData --destructive
要了解如何定义 Nova 操作,让我们看一个例子。在这个例子中,我们将定义一个操作,向用户或用户组发送电子邮件。
<?php
namespace App\Nova\Actions;
use App\Models\AccountData;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Support\Collection;
use Laravel\Nova\Actions\Action;
use Laravel\Nova\Http\Requests\NovaRequest;
use Laravel\Nova\Fields\ActionFields;
class EmailAccountProfile extends Action
{
use InteractsWithQueue, Queueable;
/**
* Perform the action on the given models.
*
* @param \Laravel\Nova\Fields\ActionFields $fields
* @param \Illuminate\Support\Collection $models
* @return mixed
*/
public function handle(ActionFields $fields, Collection $models)
{
foreach ($models as $model) {
(new AccountData($model))->send();
}
}
/**
* Get the fields available on the action.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function fields(NovaRequest $request)
{
return [];
}
}
操作最重要的方法是 handle
方法。handle
方法接收附加到操作的任何字段的值,以及选定模型的集合。handle
方法**始终**接收一个 Collection
模型,即使操作仅针对单个模型执行。
在 handle
方法中,您可以执行完成操作所需的任何任务。您可以自由更新数据库记录、发送电子邮件、调用其他服务等。一切皆有可能!
通常,Nova 使用操作的类名来确定应在操作选择菜单中显示的操作的显示名称。如果您想更改操作的显示名称,可以在操作类上定义一个 name
属性。
/**
* The displayable name of the action.
*
* @var string
*/
public $name = 'Action Title';
您可以通过定义扩展 Laravel\Nova\Actions\DestructiveAction
的操作类来将操作指定为破坏性或危险操作。这将更改操作确认按钮的颜色为红色。
破坏性操作和策略
当将破坏性操作添加到具有关联授权策略的资源时,策略的 delete
方法必须返回 true
,才能使操作运行。
排队操作回调
如果您的操作已排队,则不应使用 Action::then
方法。要使用排队操作实现类似的功能,您应该利用 Nova 的 操作批处理回调。
当对多个资源运行操作时,您可能希望在操作完成对所有资源的执行后执行一些代码。例如,您可能希望生成一份报告,详细说明所有选定资源的更改。要实现这一点,您可以在 注册操作 时调用 then
方法。
then
方法接受一个闭包,该闭包将在操作完成对所有选定资源的执行后被调用。闭包将接收一个扁平化的 Laravel 集合,其中包含操作返回的值。
例如,请注意以下操作的 handle
方法返回它接收的 $models
public function handle(ActionFields $fields, Collection $models)
{
foreach ($models as $model) {
(new AccountData($model))->send();
}
return $models;
}
在资源上注册此操作时,我们可以使用 then
回调来访问返回的模型并在操作完成执行后与它们进行交互。
public function actions(NovaRequest $request)
{
return [
(new Actions\EmailAccountProfile)->then(function ($models) {
$models->each(function ($model) {
//
});
}),
];
}
有时您可能希望在调度操作之前从用户那里收集更多信息。为此,Nova 允许您将大多数 Nova 支持的 字段 直接附加到操作。当操作启动时,Nova 会提示用户为这些字段提供输入。
要向操作添加字段,请将字段添加到操作的 fields
方法返回的字段数组中。
use Laravel\Nova\Fields\Text;
/**
* Get the fields available on the action.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function fields(NovaRequest $request)
{
return [
Text::make('Subject'),
];
}
最后,在操作的 handle
方法中,您可以使用提供的 ActionFields
实例上的动态访问器来访问您的字段。
/**
* Perform the action on the given models.
*
* @param \Laravel\Nova\Fields\ActionFields $fields
* @param \Illuminate\Support\Collection $models
* @return mixed
*/
public function handle(ActionFields $fields, Collection $models)
{
foreach ($models as $model) {
(new AccountData($model))->send($fields->subject);
}
}
您可以使用 default
方法为操作字段设置默认值。
Text::make('Subject')->default(function ($request) {
return 'Test: Subject';
}),
通常,当执行操作时,Nova UI 会显示通用的“成功”消息。但是,您可以使用 ActionResponse
类提供的多种方法自定义此响应。要显示自定义的“成功”消息,您可以在 handle
方法中调用 ActionResponse::message
方法。
use Laravel\Nova\Actions\ActionResponse;
/**
* Perform the action on the given models.
*
* @param \Laravel\Nova\Fields\ActionFields $fields
* @param \Illuminate\Support\Collection $models
* @return mixed
*/
public function handle(ActionFields $fields, Collection $models)
{
// ...
return ActionResponse::message('It worked!');
}
要返回红色“危险”消息,您可以调用 ActionResponse::danger
方法。
use Laravel\Nova\Actions\ActionResponse;
return ActionResponse::danger('Something went wrong!');
要将用户重定向到操作执行后的全新位置,您可以使用 ActionResponse::redirect
方法。
use Laravel\Nova\Actions\ActionResponse;
return ActionResponse::redirect('https://example.com');
要将用户重定向到 Nova 中的另一个位置,您可以使用 ActionResponse::visit
方法。
use Laravel\Nova\Actions\ActionResponse;
return ActionResponse::visit('/resources/posts/new', [
'viaResource' => 'users',
'viaResourceId' => 1,
'viaRelationship' => 'posts'
]);
要将用户重定向到新浏览器标签页中的新位置,您可以使用 ActionResponse::openInNewTab
方法。
use Laravel\Nova\Actions\ActionResponse;
return ActionResponse::openInNewTab('https://example.com');
要操作执行后启动文件下载,您可以使用 ActionResponse::download
方法。download
方法接受所需的文件名作为第一个参数,并接受要下载的文件的 URL 作为第二个参数。
use Laravel\Nova\Actions\ActionResponse;
return ActionResponse::download('Invoice.pdf', 'https://example.com/invoice.pdf');
除了操作执行之前和期间提供的自定义选项外,Nova 还支持向用户呈现自定义模态响应。这使您可以根据您的用例向用户提供额外的上下文或后续操作。
例如,假设您定义了一个名为 GenerateApiToken
的操作,该操作创建用于 REST API 的唯一令牌。使用自定义操作响应模态,您可以向运行操作的用户显示一个模态,允许他们将新生成的 API 令牌复制到剪贴板。
使用 nova:asset
Artisan 命令,您可以 生成自定义资产 并将自定义模态注册到 Nova 的 Vue 实例中。
import ApiTokenCopier from "./components/ApiTokenCopier";
Nova.booting(app => {
app.component("api-token-copier", ApiTokenCopier);
});
接下来,您可以在操作的 handle
方法中使用 modal
方法,该方法将指示 Nova 在运行操作后显示模态,并将 Vue 组件的名称和您指定的所有其他数据传递给组件。这些数据将作为道具提供给自定义模态的 Vue 组件。
public function handle(ActionFields $fields, Collection $models)
{
if ($models->count() > 1) {
return Action::danger('Please run this on only one user resource.');
}
$models->first()->update(['api_token' => $token = Str::random(32)]);
return Action::modal('api-token-copier', [
'message' => 'The API token was generated!',
'token' => $token,
]);
}
有时,您可能有一些需要一段时间才能完成运行的操作。为此,Nova 使您能够轻松地 排队 您的操作。要指示 Nova 将操作排队而不是同步运行它,请使用 ShouldQueue
接口标记操作。
<?php
namespace App\Nova\Actions;
use App\Models\AccountData;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Support\Collection;
use Laravel\Nova\Actions\Action;
use Laravel\Nova\Contacts\BatchableAction;
use Laravel\Nova\Fields\ActionFields;
class EmailAccountProfile extends Action implements ShouldQueue
{
use InteractsWithQueue, Queueable;
// ...
}
您可以通过在执行 nova:action
Artisan 命令时提供 --queued
选项来快速创建排队的 Nova 操作。
php artisan nova:action EmailAccountProfile --queued
使用排队操作时,请不要忘记为您的应用程序配置和启动 队列工作者。否则,您的操作将不会被处理。
排队操作文件
目前,Nova 不支持将 File
字段附加到排队操作。如果您需要将 File
字段附加到操作,则操作必须同步运行。
您可以在操作的构造函数中设置 $connection
和 $queue
属性,从而自定义操作排队的队列连接和队列名称。
/**
* Create a new action instance.
*
* @return void
*/
public function __construct()
{
$this->connection = 'redis';
$this->queue = 'emails';
}
您还可以通过使用 Laravel\Nova\Contracts\BatchableAction
接口标记操作,来指示 Nova 将操作排队为 批处理。此外,操作应使用 Illuminate\Bus\Batchable
特性。
当操作可批处理时,您应该定义一个 withBatch
方法,该方法将负责配置操作的 批处理回调。这允许您定义在针对多个选定资源执行完一整批操作后应运行的代码。实际上,您甚至可以访问在执行批处理操作时选定的所有资源的模型 ID。
<?php
namespace App\Nova\Actions;
use App\Models\AccountData;
use Illuminate\Bus\Batch;
use Illuminate\Bus\Batchable;
use Illuminate\Bus\PendingBatch;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Support\Collection;
use Laravel\Nova\Actions\Action;
use Laravel\Nova\Contacts\BatchableAction;
use Laravel\Nova\Fields\ActionFields;
use Throwable;
class EmailAccountProfile extends Action implements BatchableAction, ShouldQueue
{
use Batchable, InteractsWithQueue, Queueable;
/**
* Prepare the given batch for execution.
*
* @param \Laravel\Nova\Fields\ActionFields $fields
* @param \Illuminate\Bus\PendingBatch $batch
* @return void
*/
public function withBatch(ActionFields $fields, PendingBatch $batch)
{
$batch->then(function (Batch $batch) {
// All jobs completed successfully...
$selectedModels = $batch->resourceIds;
})->catch(function (Batch $batch, Throwable $e) {
// First batch job failure detected...
})->finally(function (Batch $batch) {
// The batch has finished executing...
});
}
}
查看针对特定资源运行的操作日志通常很有用。此外,在排队操作时,了解排队操作何时实际完成执行通常很重要。值得庆幸的是,Nova 通过将 Laravel\Nova\Actions\Actionable
特性附加到资源的相应 Eloquent 模型,使添加操作日志变得轻而易举。
例如,我们可以将 Laravel\Nova\Actions\Actionable
特性附加到 User
Eloquent 模型。
<?php
namespace App;
use Laravel\Nova\Actions\Actionable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
use Actionable, Notifiable;
// ...
}
将特性附加到模型后,Nova 将自动开始在资源的详细信息页面底部显示操作日志。
如果您不想在操作日志中记录操作,可以在操作类中添加 withoutActionEvents
属性来禁用此行为。
/**
* Disables action log events for this action.
*
* @var bool
*/
public $withoutActionEvents = true;
或者,使用 withoutActionEvents
方法,您可以在将操作附加到资源时禁用操作日志。当操作经常针对数千个资源执行时,禁用操作日志通常特别有用,因为它允许您避免数千个缓慢的、顺序的操作日志数据库插入。
/**
* Get the actions available for the resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function actions(NovaRequest $request)
{
return [
(new SomeAction)->withoutActionEvents()
];
}
在排队操作运行时,您可以通过其模型集合更新操作针对任何传递给操作的模型的“状态”。例如,您可以使用操作的 markAsFinished
方法来指示操作已完成处理特定模型。
/**
* Perform the action on the given models.
*
* @param \Laravel\Nova\Fields\ActionFields $fields
* @param \Illuminate\Support\Collection $models
* @return mixed
*/
public function handle(ActionFields $fields, Collection $models)
{
foreach ($models as $model) {
(new AccountData($model))->send($fields->subject);
$this->markAsFinished($model);
}
}
或者,如果您想指示操作针对给定模型“失败”,可以使用 markAsFailed
方法。
/**
* Perform the action on the given models.
*
* @param \Laravel\Nova\Fields\ActionFields $fields
* @param \Illuminate\Support\Collection $models
* @return mixed
*/
public function handle(ActionFields $fields, Collection $models)
{
foreach ($models as $model) {
try {
(new AccountData($model))->send($fields->subject);
} catch (Exception $e) {
$this->markAsFailed($model, $e);
}
}
}
默认情况下,操作在运行之前会询问用户确认。您可以自定义确认消息、确认按钮和取消按钮,以便在运行操作之前为用户提供更多上下文。这可以通过在定义操作时调用 `confirmText`、`confirmButtonText` 和 `cancelButtonText` 方法来实现。
/**
* Get the actions available for the resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function actions(NovaRequest $request)
{
return [
(new Actions\ActivateUser)
->confirmText('Are you sure you want to activate this user?')
->confirmButtonText('Activate')
->cancelButtonText("Don't activate"),
];
}