Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Duplicate Resource Action for Laravel Nova
<?php
namespace App\Nova\Actions;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Collection;
use Laravel\Nova\Actions\Action;
use Laravel\Nova\Fields\ActionFields;
abstract class DuplicateResource extends Action {
use SerializesModels;
public $showOnDetail = true;
public $showOnIndex = false;
public $showOnTableRow = true;
public $confirmButtonText = 'Duplicate Resource';
public $cancelButtonText = 'Cancel';
public $confirmText = 'Are you sure you want to duplicate this resource?';
public $withoutActionEvents = true;
protected $keepRelations = [];
protected $duplicateRelations = [];
/**
* Perform the action on the given models.
*
* @param ActionFields $fields
* @param Collection $models
*
* @return mixed
*/
public function handle(ActionFields $fields, Collection $models)
{
if ($models->count() !== 1) {
return Action::danger("Cannot duplicate multiple models simultaneously.");
}
$model = $models->first();
$newModel = $model->replicate();
// Override values from fields
foreach ($fields->getAttributes() as $key => $value) {
if(isset($value)){
$newModel->$key = $value;
}
}
$newModel->push();
if (!empty($this->duplicateRelations)) {
// load the relations
$model->load($this->duplicateRelations);
foreach ($model->getRelations() as $relation => $items) {
// works for hasMany
foreach ($items as $item) {
// clean up our models, remove the id and remove the appends
unset($item->id);
$item->setAppends([]);
// create a relation on the new model with the data.
$newModel->{$relation}()->create($item->toArray());
}
}
}
if (!empty($this->keepRelations)) {
// load the fresh model with relations to maintain
unset($model->relations);
$model->load($this->keepRelations);
foreach ($model->getRelations() as $relation => $items) {
// works for hasMany
foreach ($items as $item) {
$newModel->{$relation}()->attach($item);
}
}
}
$newModel->save();
return Action::message($this->getSuccessMessage($model, $newModel, $fields));
}
protected function getSuccessMessage(Model $originalModel, Model $newModel, ActionFields $fields) : String {
return "Resource has been duplicated.";
}
/**
* Get the fields available on the action.
*
* @return array
*/
public function fields()
{
return [];
}
}
@ansezz

This comment has been minimized.

Copy link

@ansezz ansezz commented Apr 5, 2020

Thank You, It's work 👍

@ns139426

This comment has been minimized.

Copy link
Owner Author

@ns139426 ns139426 commented Apr 6, 2020

Thank You, It's work 👍

Glad you found it useful.

@philipbaginski

This comment has been minimized.

Copy link

@philipbaginski philipbaginski commented Apr 27, 2020

Thank you, it works great! But I need to copy more than one row. Any idea how to do it?

@ns139426

This comment has been minimized.

Copy link
Owner Author

@ns139426 ns139426 commented Apr 27, 2020

Thank you, it works great! But I need to copy more than one row. Any idea how to do it?

Off the top of my head, I'd remove the lines

if ($models->count() !== 1) {
    return Action::danger("Cannot duplicate multiple models simultaneously.");
}

then wrap the body of the code in foreach($models as $model) before finally returning an appropriate error message.

I can't recall why I intentionally avoided duplicating multiple resources. Hopefully this works for you.

@philipbaginski

This comment has been minimized.

Copy link

@philipbaginski philipbaginski commented Apr 27, 2020

But, in my code I don't have this lines :-(

`<?php
namespace App\Nova\Actions;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Collection;
use Laravel\Nova\Actions\Action;
use Laravel\Nova\Fields\ActionFields;

class DuplicateResource extends Action {
use SerializesModels;
public $showOnDetail = true;
public $showOnIndex = true;
public $showOnTableRow = false;
public $confirmButtonText = '...';
public $cancelButtonText = '...';
public $confirmText = '...';
public $withoutActionEvents = true;
protected $keepRelations = [];
protected $duplicateRelations = [];
/**
* Perform the action on the given models.
*
* @param ActionFields $fields
* @param Collection $models
*
* @return mixed
*/
public function handle(ActionFields $fields, Collection $models)
{

    $model = $models->first();
    $newModel = $model->replicate();
    // Override values from fields
    foreach ($fields->getAttributes() as $key => $value) {
        if(isset($value)){
            $newModel->$key = $value;
        }
    }
    $newModel->push();
    if (!empty($this->duplicateRelations)) {
        // load the relations
        $model->load($this->duplicateRelations);
        foreach ($model->getRelations() as $relation => $items) {
            // works for hasMany
            foreach ($items as $item) {
                // clean up our models, remove the id and remove the appends
                unset($item->id);
                $item->setAppends([]);
                // create a relation on the new model with the data.
                $newModel->{$relation}()->create($item->toArray());
            }
        }
    }
    if (!empty($this->keepRelations)) {
        // load the fresh model with relations to maintain
        unset($model->relations);
        $model->load($this->keepRelations);
        foreach ($model->getRelations() as $relation => $items) {
            // works for hasMany
            foreach ($items as $item) {
                $newModel->{$relation}()->attach($item);
            }
        }
    }
    $newModel->save();
    return Action::message($this->getSuccessMessage($model, $newModel, $fields));
}
protected function getSuccessMessage(Model $originalModel, Model $newModel, ActionFields $fields) : String {
    return "...";
}
/**
 * Get the fields available on the action.
 *
 * @return array
 */
public function fields()
{
    return [];
}

}`

@philipbaginski

This comment has been minimized.

Copy link

@philipbaginski philipbaginski commented Apr 27, 2020

And also when I have: abstract class DuplicateResource extends Action it shows error. So, I removed abstract and it works for one record.

@ns139426

This comment has been minimized.

Copy link
Owner Author

@ns139426 ns139426 commented Apr 27, 2020

And also when I have: abstract class DuplicateResource extends Action it shows error. So, I removed abstract and it works for one record.

It's written as an abstract class so that you extend it for your own needs rather than just using it directly. If you just want to hack it in place, probably remove $model = $models->first(); and put in a foreach on the $models parameter.

The PHP docs can be pretty tough to read but try https://www.php.net/manual/en/language.oop5.abstract.php. There are probably lots of other good resources on OOP that will help you better understand abstract classes too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.