nova-import icon indicating copy to clipboard operation
nova-import copied to clipboard

Nova v3.10.0+ Import "standalone" action

Open anaseqal opened this issue 3 years ago • 12 comments

Hello,

Nova v3.10.0 introduce a new standalone action for running actions without selecting resources. which make a good use case to import data to your resource without using this package.

Result:

Screenshot

Step by step:

  1. create new action: php artisan nova:action ImportUsers

  2. enable stand alone option by adding:

    public $standalone = true;
  1. create an import class for your resource using Laravel Excel. composer require maatwebsite/excel php artisan make:import UsersImport --model=User then in action file:
   public function handle(ActionFields $fields)
    {
        \Maatwebsite\Excel\Facades\Excel::import(new \App\Imports\UsersImport, $fields->file);
        return Action::message('It worked!');
    }

    public function fields()
    {
        return [
            \Laravel\Nova\Fields\File::make('File')
                ->rules('required'),
        ];
    }
  1. register the action into your resource.
public function actions(Request $request)
{
    return [new Actions\ImportUsers];
}

Happy coding!

anaseqal avatar Dec 07 '20 19:12 anaseqal

Good to see this feature!

phoenixg avatar Dec 09 '20 10:12 phoenixg

I'm getting the following error when running the action after following this tutorial:

Call to undefined method App\Nova\Actions\ImportUsers::validateFields()

It seems like Nova Action's have this method but Anaseqal\NovaImport\Actions\Action does not. My ImportUsers extends NovaImport, not Nova.

(Nova v3.21.0)

Solved

It would appear you should extend the Nova Action, not NovaImport.

Can the docs be updated to reflect this?

MCKLtech avatar Feb 09 '21 21:02 MCKLtech

Hello i getting this error, even if i think i setup all things correctly... Call to undefined method App\Nova\Actions\ImportProducts::shownOnIndex()

urbandario avatar Feb 26 '22 11:02 urbandario

I'm getting the following error when running the action after following this tutorial:

Call to undefined method App\Nova\Actions\ImportUsers::validateFields()

It seems like Nova Action's have this method but Anaseqal\NovaImport\Actions\Action does not. My ImportUsers extends NovaImport, not Nova.

(Nova v3.21.0)

Solved

It would appear you should extend the Nova Action, not NovaImport.

Can the docs be updated to reflect this?

When i change on from NovaImport to Nova i get Class "Anaseqal\Nova\Actions\Action" not found

urbandario avatar Feb 26 '22 11:02 urbandario

Hi there, I am getting the same error after applying all the mentioned setup steps:

Call to undefined method App\Nova\Actions\ImportSupervisors::shownOnIndex()

The import process itself is working however I cannot view the resource detail view (data not being displayed). The index view lists the records, however the error keeps showing up over and over. Please advise how to fix this.

Excel-Import-Error

LaravelLover069 avatar Apr 02 '22 09:04 LaravelLover069

Hi all, I found the solution to this issue

Hello i getting this error, even if i think i setup all things correctly... Call to undefined method App\Nova\Actions\ImportProducts::shownOnIndex()

@Urbanizacija69 I faced the same issue and found the solution for this issue. I have created an issue record and provided the solution for this. https://github.com/anaseqal/nova-import/issues/33 Cheers

LaravelLover069 avatar Apr 03 '22 01:04 LaravelLover069

I am doing the following steps composer require anaseqal/nova-import I registered the tool in my NovaServiceProvider.php app/Providers/NovaServiceProvider.php I created a nova action file and added my importer to the handle() method I registered my action in my resource then had an error saying Class Anaseqal\NovaImport\NovaImport contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (Laravel\Nova\Tool::menu) Any ideas on how to fix this please

StevenBokesa avatar May 16 '22 14:05 StevenBokesa

no support laravel nova 4, @anaseqal is correct?

CarlosHidalgo89 avatar Jun 18 '22 22:06 CarlosHidalgo89

I registered the tool in my NovaServiceProvider.php app/Providers/NovaServiceProvider.php

is laravel nova 4 ?

CarlosHidalgo89 avatar Jun 18 '22 22:06 CarlosHidalgo89

i have the same problem did you get any solution?

Class Anaseqal\NovaImport\NovaImport contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (Laravel\Nova\Tool::menu)

Wilquey avatar Nov 23 '22 01:11 Wilquey

Hi! Wilquey.

LavareI Nova V4. corrected it by replacing two files. Anaseqal\NovaImport\NovaImport Anaseqal\NovaImport\Actions\Action

El mar, 22 nov 2022 a la(s) 20:29, Wilquey Caetano da Cruz ( @.***) escribió:

i have the same problem did you get any solution?

Class Anaseqal\NovaImport\NovaImport contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (Laravel\Nova\Tool::menu)

— Reply to this email directly, view it on GitHub https://github.com/anaseqal/nova-import/issues/26#issuecomment-1324436591, or unsubscribe https://github.com/notifications/unsubscribe-auth/AVFA4NWRDX57ZPC366J5VUTWJVXPHANCNFSM4UQ5KYNQ . You are receiving this because you commented.Message ID: @.***>

-- Saludos,

Ing. Carlos Hidalgo Reyes Software Developer p: +593 9 822 71342 a: Guayaquil: Av. Juan Tanca Marengo Km 0.5 Edif. Francisco Sánchez Piso 2 Of. 10 w: www.siberian.com.ec e: @.*** @.***>

Este correo y sus documentos adjuntos son confidenciales. Cualquier divulgación, distribución y/o copia de dicha información se encuentra prohibida y sancionada por la ley. Si usted ha recibido este mensaje por error, por favor notifíquenos de inmediato.

This e-mail and its attachments contains confidential information. Any disclosure, distribution and/or copying of the information is prohibited and sanctioned by law. If you received this email in error, please notify the sender immediately and delete this message.

runCallback ? call_user_func($this->runCallback, $request, $model) : true; } /** * Return a message response from the action. * * @param string $message * @return array */ public static function message($message) { return ['message' => $message]; } /** * Return a dangerous message response from the action. * * @param string $message * @return array */ public static function danger($message) { return ['danger' => $message]; } /** * Return a delete response from the action. * * @return array */ public static function deleted() { return ['deleted' => true]; } /** * Return a redirect response from the action. * * @param string $url * @return array */ public static function redirect($url) { return ['redirect' => $url]; } /** * Return a Inertia visit from the action. * * @deprecated * * @param string $path * @param array $options * @return array> */ public static function push($path, $query = []) { return [ 'push' => [ 'path' => $path, 'query' => $query, ], ]; } /** * Return a Inertia visit from the action. * * @param string $path * @param array $options * @return array> */ public static function visit($path, $options = []) { return [ 'visit' => [ 'path' => '/'.ltrim($path, '/'), 'options' => $options, ], ]; } /** * Return an open new tab response from the action. * * @param string $url * @return array */ public static function openInNewTab($url) { return ['openInNewTab' => $url]; } /** * Return a download response from the action. * * @param string $url * @param string $name * @return array */ public static function download($url, $name) { return ['download' => $url, 'name' => $name]; } /** * Return an action modal response from the action. * * @param string $modal * @param array $data * @return array */ public static function modal($modal, $data) { return array_merge(['modal' => $modal], $data); } /** * Execute the action for the given request. * * @param \Laravel\Nova\Http\Requests\ActionRequest $request * @return mixed * @throws MissingActionHandlerException */ public function handleRequest(ActionRequest $request) { $method = ActionMethod::determine($this, $request->targetModel()); if (! method_exists($this, $method)) { throw MissingActionHandlerException::make($this, $method); } $fields = $request->resolveFields(); return DispatchAction::forModels( $request, $this, $method, collect([]), $fields ); return $this->handleResult($fields, $results); } /** * Handle chunk results. * * @param \Laravel\Nova\Fields\ActionFields $fields * @param array $results * @return mixed */ public function handleResult(ActionFields $fields, $results) { return count($results) ? end($results) : null; } /** * Handle any post-validation processing. * * @param \Laravel\Nova\Http\Requests\NovaRequest $request * @param \Illuminate\Contracts\Validation\Validator $validator * @return void */ protected function afterValidation(NovaRequest $request, $validator) { // } /** * Mark the action event record for the model as finished. * * @param \Illuminate\Database\Eloquent\Model $model * @return int */ protected function markAsFinished($model) { return $this->batchId ? ActionEvent::markAsFinished($this->batchId, $model) : 0; } /** * Mark the action event record for the model as failed. * * @param \Illuminate\Database\Eloquent\Model $model * @param \Throwable|string $e * @return int */ protected function markAsFailed($model, $e = null) { return $this->batchId ? ActionEvent::markAsFailed($this->batchId, $model, $e) : 0; } /** * Indicate that this action can be run for the entire resource at once. * * @param bool $value * @return $this */ public function availableForEntireResource($value = true) { $this->availableForEntireResource = $value; return $this; } /** * Get the fields available on the action. * * @param \Laravel\Nova\Http\Requests\NovaRequest $request * @return array */ public function fields(NovaRequest $request) { return []; } /** * Validate the given request. * * @param \Laravel\Nova\Http\Requests\ActionRequest $request * @return array * * @throws \Illuminate\Validation\ValidationException */ public function validateFields(ActionRequest $request) { $fields = collect($this->fields($request)); return Validator::make( $request->all(), $fields->mapWithKeys(function ($field) use ($request) { return $field->getCreationRules($request); })->all(), [], $fields->reject(function ($field) { return empty($field->name); })->mapWithKeys(function ($field) { return [$field->attribute => $field->name]; })->all() )->after(function ($validator) use ($request) { $this->afterValidation($request, $validator); })->validate(); } /** * Indicate that this action is only available on the resource index view. * * @param bool $value * @return $this */ public function onlyOnIndex($value = true) { $this->onlyOnIndex = $value; $this->showOnIndex = $value; $this->showOnDetail = ! $value; $this->showInline = ! $value; return $this; } /** * Indicate that this action is available except on the resource index view. * * @return $this */ public function exceptOnIndex() { $this->showOnDetail = true; $this->showInline = true; $this->showOnIndex = false; return $this; } /** * Indicate that this action is only available on the resource detail view. * * @param bool $value * @return $this */ public function onlyOnDetail($value = true) { $this->onlyOnDetail = $value; $this->showOnDetail = $value; $this->showOnIndex = ! $value; $this->showInline = ! $value; return $this; } /** * Indicate that this action is available except on the resource detail view. * * @return $this */ public function exceptOnDetail() { $this->showOnIndex = true; $this->showOnDetail = false; $this->showInline = true; return $this; } /** * Indicate that this action is only available on the resource's table row. * * @param bool $value * @return $this */ public function onlyOnTableRow($value = true) { return $this->onlyInline($value); } /** * Indicate that this action is only available on the resource's table row. * * @param bool $value * @return $this */ public function onlyInline($value = true) { $this->showInline = $value; $this->showOnIndex = ! $value; $this->showOnDetail = ! $value; return $this; } /** * Indicate that this action is available except on the resource's table row. * * @return $this */ public function exceptOnTableRow() { return $this->exceptInline(); } /** * Indicate that this action is available except on the resource's table row. * * @return $this */ public function exceptInline() { $this->showInline = false; $this->showOnIndex = true; $this->showOnDetail = true; return $this; } /** * Show the action on the index view. * * @return $this */ public function showOnIndex() { $this->showOnIndex = true; return $this; } /** * Show the action on the detail view. * * @return $this */ public function showOnDetail() { $this->showOnDetail = true; return $this; } /** * Show the action on the table row. * * @deprecated * * @return $this */ public function showOnTableRow() { return $this->showInline(); } /** * Show the action on the table row. * * @return $this */ public function showInline() { $this->showInline = true; return $this; } /** * Register a callback that should be invoked after the action is finished executing. * * @param callable(\Illuminate\Support\Collection):mixed $callback * @return $this */ public function then($callback) { $this->thenCallback = $callback; return $this; } /** * Set the current batch ID being handled by the action. * * @param string $actionBatchId * @return $this */ public function withActionBatchId(string $batchId) { $this->batchId = $batchId; return $this; } /** * Register `then`, `catch`, and `finally` callbacks on the pending batch. * * @param \Laravel\Nova\Fields\ActionFields $fields * @param \Illuminate\Bus\PendingBatch $batch * @return void */ public function withBatch(ActionFields $fields, PendingBatch $batch) { // } /** * Set the current batch ID being handled by the action. * * @param string $batchId * @return $this */ public function withBatchId($batchId) { $this->batchId = $batchId; return $this; } /** * Set the callback to be run to authorize running the action. * * @param \Closure(\Laravel\Nova\Http\Requests\NovaRequest, mixed):bool $callback * @return $this */ public function canRun(Closure $callback) { $this->runCallback = $callback; return $this; } /** * Get the component name for the action. * * @return string */ public function component() { return $this->component; } /** * Get the displayable name of the action. * * @return string */ public function name() { return $this->name ?: Nova::humanize($this); } /** * Get the URI key for the action. * * @return string */ public function uriKey() { return Str::slug($this->name(), '-', null); } /** * Set the action to execute instantly. * * @return $this */ public function withoutConfirmation() { $this->withoutConfirmation = true; return $this; } /** * Set the action to skip action events for models. * * @return $this */ public function withoutActionEvents() { $this->withoutActionEvents = true; return $this; } /** * Return the CSS classes for the Action. * * @return string */ public function actionClass() { return $this instanceof DestructiveAction ? 'btn-danger' : 'btn-primary'; } /** * Determine if the action is to be shown on the index view. * * @return bool */ public function shownOnIndex() { if ($this->onlyOnIndex == true) { return true; } if ($this->onlyOnDetail) { return false; } return $this->showOnIndex; } /** * Determine if the action is to be shown on the detail view. * * @return bool */ public function shownOnDetail() { if ($this->onlyOnDetail) { return true; } if ($this->onlyOnIndex) { return false; } return $this->showOnDetail; } /** * Determine if the action is to be shown inline on the table row. * * @return bool */ public function shownOnTableRow() { return $this->showInline; } /** * Set the text for the action's confirmation button. * * @param string $text * @return $this */ public function confirmButtonText($text) { $this->confirmButtonText = $text; return $this; } /** * Set the text for the action's cancel button. * * @param string $text * @return $this */ public function cancelButtonText($text) { $this->cancelButtonText = $text; return $this; } /** * Set the text for the action's confirmation message. * * @param string $text * @return $this */ public function confirmText($text) { $this->confirmText = $text; return $this; } /** * Mark the action as a standalone action. * * @return $this */ public function standalone() { $this->standalone = true; return $this; } /** * Determine if the action is a standalone action. * * @return bool */ public function isStandalone() { return $this->standalone; } /** * Prepare the action for JSON serialization. * * @return array */ public function jsonSerialize(): array { $request = app(NovaRequest::class); return array_merge([ 'component' => $this->component(), 'destructive' => $this instanceof DestructiveAction, 'name' => $this->name(), 'uriKey' => $this->uriKey(), 'fields' => collect($this->fields($request))->filter->authorizedToSee($request)->each->resolveForAction($request)->all(), 'availableForEntireResource' => $this->availableForEntireResource, 'onlyOnDetail' => $this->onlyOnDetail, 'onlyOnIndex' => $this->onlyOnIndex, 'withoutConfirmation' => $this->withoutConfirmation, 'cancelButtonText' => __($this->cancelButtonText), 'confirmButtonText' => __($this->confirmButtonText), 'confirmText' => __($this->confirmText), 'class' => $this->actionClass(), 'showOnDetail' => $this->shownOnDetail(), 'showOnIndex' => $this->shownOnIndex(), 'showOnTableRow' => $this->shownOnTableRow(), 'standalone' => $this->isStandalone(), 'responseType' => $this->responseType, ], $this->meta()); } /** * Prepare the instance for serialization. * * @return array */ public function __sleep() { $properties = (new ReflectionClass($this))->getProperties(); return array_values(array_filter(array_map(function ($p) { return ($p->isStatic() || in_array($name = $p->getName(), ['runCallback', 'seeCallback', 'thenCallback'])) ? null : $name; }, $properties))); } }

CarlosHidalgo89 avatar Nov 23 '22 14:11 CarlosHidalgo89

Thank you CarlosHidalgo89 for the instructions, the replacements were carried out. However, another error appeared that I could not identify.

Declaration of App\Nova\Actions\ImportUsers::fields() should be compatible with Anaseqal\NovaImport\Actions\Action::fields(Laravel\Nova\Http\Requests\NovaRequest $request)

did you get any solution?

Wilquey avatar Nov 23 '22 21:11 Wilquey