request = $request; $this->action = $action; $this->fields = $fields; if ($action instanceof BatchableAction) { $this->configureBatchJob($action, $fields); } } /** * Configure the batch job for the action. * * @param \Laravel\Nova\Actions\Action $action * @param \Laravel\Nova\Fields\ActionFields $fields * @return void */ protected function configureBatchJob(Action $action, ActionFields $fields) { $this->batchJob = tap(Bus::batch([]), function (PendingBatch $batch) use ($action, $fields) { $batch->name($action->name()); if (! is_null($connection = $this->connection())) { $batch->onConnection($connection); } if (! is_null($queue = $this->queue())) { $batch->onQueue($queue); } $action->withBatch($fields, $batch); }); } /** * Dispatch the action. * * @return \Laravel\Nova\Actions\Response * * @throws \Throwable */ public function dispatch() { if ($this->action instanceof ShouldQueue) { return tap(new Response(), function ($response) { with($response, $this->dispatchableCallback); if (! is_null($this->batchJob)) { $this->batchJob->dispatch(); } return $response->successful(); }); } return with(new Response(), $this->dispatchableCallback); } /** * Dispatch the given action. * * @param string $method * @return $this * * @throws \Throwable */ public function handleStandalone($method) { $this->dispatchableCallback = function (Response $response) use ($method) { if ($this->action instanceof ShouldQueue) { $this->addQueuedActionJob($method, collect()); return; } return $response->successful([ $this->dispatchSynchronouslyForCollection($method, collect()), ]); }; return $this; } /** * Dispatch the given action. * * @param \Laravel\Nova\Http\Requests\ActionRequest $request * @param string $method * @param int $chunkCount * @return $this * * @throws \Throwable */ public function handleRequest(ActionRequest $request, $method, $chunkCount) { $this->dispatchableCallback = function (Response $response) use ($request, $method, $chunkCount) { if ($this->action instanceof ShouldQueue) { $request->chunks($chunkCount, function ($models) use ($request, $method) { $models = $models->filterForExecution($request); return $this->forModels($method, $models); }); return; } $wasExecuted = false; $results = $request->chunks( $chunkCount, function ($models) use ($request, $method, &$wasExecuted) { $models = $models->filterForExecution($request); if (count($models) > 0) { $wasExecuted = true; } return $this->forModels($method, $models); } ); return $wasExecuted ? $response->successful($results) : $response->failed(); }; return $this; } /** * Dispatch the given action using custom handler. * * @param \Laravel\Nova\Http\Requests\ActionRequest $request * @param \Closure(\Laravel\Nova\Http\Requests\ActionRequest, \Laravel\Nova\Actions\Response, \Laravel\Nova\Fields\ActionFields):\Laravel\Nova\Actions\Response $callback * @return $this */ public function handleUsing(ActionRequest $request, $callback) { $this->dispatchableCallback = function (Response $response) use ($request, $callback) { return $callback($request, $response, $this->fields); }; return $this; } /** * Dispatch the given action. * * @param string $method * @param \Illuminate\Support\Collection $models * @return mixed|void * * @throws \Throwable */ public function forModels($method, Collection $models) { if ($this->action->isStandalone() || $models->isEmpty()) { return; } if ($this->action instanceof ShouldQueue) { $this->addQueuedActionJob($method, $models); return; } return $this->dispatchSynchronouslyForCollection($method, $models); } /** * Dispatch the given action synchronously for a model collection. * * @param string $method * @param \Illuminate\Support\Collection $models * @return mixed * * @throws \Throwable */ protected function dispatchSynchronouslyForCollection($method, Collection $models) { return Transaction::run(function ($batchId) use ($method, $models) { Nova::usingActionEvent(function ($actionEvent) use ($batchId, $models) { if (! $this->action->withoutActionEvents) { $actionEvent->createForModels( $this->request, $this->action, $batchId, $models ); } }); return $this->action->withActionBatchId($batchId)->{$method}($this->fields, $models); }, function ($batchId) { Nova::usingActionEvent(function ($actionEvent) use ($batchId) { $actionEvent->markBatchAsFinished($batchId); }); }); } /** * Dispatch the given action to the queue for a model collection. * * @param string $method * @param \Illuminate\Support\Collection $models * @return mixed * * @throws \Throwable */ protected function addQueuedActionJob($method, Collection $models) { return Transaction::run(function ($batchId) use ($method, $models) { Nova::usingActionEvent(function ($actionEvent) use ($batchId, $models) { if (! $this->action->withoutActionEvents) { $actionEvent->createForModels( $this->request, $this->action, $batchId, $models, 'waiting' ); } }); $job = new CallQueuedAction( $this->action, $method, $this->request->resolveFields(), $models, $batchId ); if ($this->action instanceof BatchableAction) { $this->batchJob->add([$job]); $this->batchJob->options['resourceIds'] = array_values(array_unique(array_merge( $this->batchJob->options['resourceIds'] ?? [], $models->map(function ($model) { return $model->getKey(); })->all() ))); } else { Queue::connection($this->connection())->pushOn( $this->queue(), $job ); } }); } /** * Extract the queue connection for the action. * * @return string|null */ protected function connection() { return property_exists($this->action, 'connection') ? $this->action->connection : null; } /** * Extract the queue name for the action. * * @return string|null */ protected function queue() { return property_exists($this->action, 'queue') ? $this->action->queue : null; } }