Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature][3.4][WIP] Clone operation #28

Closed
tabacitu opened this issue Jun 8, 2016 · 11 comments
Closed

[Feature][3.4][WIP] Clone operation #28

tabacitu opened this issue Jun 8, 2016 · 11 comments

Comments

@tabacitu
Copy link
Member

tabacitu commented Jun 8, 2016

Alongside the regular buttons, there should be a Clone button, in this order: Edit | Clone | Delete

Behaviour:

  • clicking the clone button will open an "Add item" view, with the previous item's information already populated in the fields;
  • this way, by showing everything in the "Add item" view:
    • the user can quickly modify what needs to be changed (most used case);
    • any validations will show up nicely and can be easily resolved (ex: slug is unique);

Details:

  • this will require a new "access" variable, "clone", which by default is missing; only by giving $this->crud->allowAccess('clone') will the button show up and functionality work;
@axyr
Copy link

axyr commented Jun 15, 2016

What about moving the buttons to a seperate class.
Now you mention clone, but I would like to have a 'send password reset' button for the user table?

And that we can add FormActions() to ListActions() and EditActions() so the detail forms can have custom buttons as well (SendSomeEmail, ExportForNewsletter, MoveToSomewhere, etc, etc)

@tabacitu
Copy link
Member Author

Completely agree about custom buttons. Here are my thoughts about that.

@tabacitu tabacitu added this to the 2.0 milestone Jun 15, 2016
@tabacitu tabacitu modified the milestones: 2.1, 2.0 Jul 4, 2016
@tabacitu tabacitu modified the milestones: 3.0, 2.1 Jul 23, 2016
@tabacitu
Copy link
Member Author

Started working on it in 84f8f10, but hit a brick wall: n-n relationships are more difficult to clone than expected.

We either need to

  1. specify which n-n relationships need to be duplicated;

(or)

  1. go through the create/update fields and duplicate all n-n relationships there;

The problem with (2) is that

  • there could be custom relationship fields, and those don't get duplicated;
  • there could be relationships that are added automatically, but are not displayed as fields to the user (ex: auto-assign the groups a user belongs to to a tag);
  • what would we use, the create or update fields? because they might be different;

The problem with (1) is that:

  • it's uncomfortable to write on the model a $duplicateRelations variable;
  • I'm not quite sure we CAN get the relationship table, just from the relationship method name;

Right not option (1) seems the way to go, if it's possible. Will investigate and follow-up.

@tabacitu tabacitu modified the milestones: 3.2 - maturity, 3.0 Jul 28, 2016
@OwenMelbz
Copy link
Contributor

@tabacitu
Copy link
Member Author

tabacitu commented Oct 3, 2016

Didn't know about this. I think so, yes. And it's very elegant. It still doesn't clone relationships but we could do that manually, like in the last comment here.

@cristianuibar
Copy link

Hello guys,

Any news on this? Using replicate seams the way to go, yet when pressing duplicate it should prompt you with an edit panel right away even before adding the new entry in DB or am I wrong?

Any thoughts on how I can start doing this myself?

@tabacitu
Copy link
Member Author

tabacitu commented Aug 6, 2018

Hi @cristianuibar ,

No development on this so far, no. But I do support bringing this feature to CRUD 4.0, so let's reopen this and appropriately tag it.

In theory it shouldn't be to difficult to do for your project. You'd have the added benefit of choosing the solution for the dilemma above and just stick with it (whether to clone related entities, or link to them).

In your project, you'd need:

  • a trait that attaches this operation on the EntityCrudController... say... Clone; that trait would have a clone() method that does the actual cloning operation, and it could return the operation status (true or false string), similar to how delete() works;
  • a route for that operation (probably /{$id}/clone;
  • a button type in resources/views/vendor/backpack/crud/buttons/clone.blade.php very similar to delete.blade.php, but that would call the route above;

Then, for each entity you'd like to be clonable, you would:

  • add the route
  • add the trait

Hope it makes sense.

@tabacitu tabacitu reopened this Aug 6, 2018
@tabacitu tabacitu changed the title Clone button [Feature req][4.0] Clone operation Aug 6, 2018
@tswonke
Copy link
Contributor

tswonke commented Aug 6, 2018

I use cloning in a current project by adding a route/button like @tabacitu suggests and this small piece of code using route-model-binding:

public function clone(Task $task)
    {
        $clonedTask = $task->replicate();
        $clonedTask->push();

        return redirect()->route('crud.task.edit', [$clonedTask->id]);
    }

I don't need any n-n relations to be cloned. 1-n works with this automatically.

@tabacitu
Copy link
Member Author

I've used something myself, which might be useful to others, or we might even include it one day. Same caveat as @tswonke above - only 1-n relations are cloned, n-n are NOT.

I have two buttons:

  • one to be included per line (like Delete)
  • one to be included in the bottom stack (like bulk delete)

Both work the same way: they don't refresh the page when successful, but refresh the ajax data tables. So no redirect. Just like delete and bulk delete.

Route example:

    CRUD::resource('monster', 'MonsterCrudController')->with(function() {
    	Route::post('monster/{id}/clone', 'MonsterCrudController@clone');
    });

That leads to a new method inside the CrudController:

    public function clone($id)
    {
        $this->crud->hasAccessOrFail('create');

        $clonedEntry = $this->crud->model->findOrFail($id)->replicate();

        return (string) $clonedEntry->push();
    }

And in the same controller, in setup(), one needs to add the buttons:

        $this->crud->addButtonFromView('line', 'clone', 'clone', 'beginning');
        $this->crud->addButtonFromView('bottom', 'bulk_clone', 'bulk_clone', 'end');

And here are the actual buttons:

  • clone.blade.php
@if ($crud->hasAccess('create'))
	<a href="javascript:void(0)" onclick="cloneEntry(this)" data-route="{{ url($crud->route.'/'.$entry->getKey().'/clone') }}" class="btn btn-xs btn-default" data-button-type="clone"><i class="fa fa-clone"></i> Clone</a>
@endif

<script>
	if (typeof cloneEntry != 'function') {
	  $("[data-button-type=clone]").unbind('click');

	  function cloneEntry(button) {
	      // ask for confirmation before deleting an item
	      // e.preventDefault();
	      var button = $(button);
	      var route = button.attr('data-route');

          $.ajax({
              url: route,
              type: 'POST',
              success: function(result) {
                  // Show an alert with the result
                  new PNotify({
                      title: "Entry cloned",
                      text: "A new entry has been added, with the same information as this one.",
                      type: "success"
                  });

                  // Hide the modal, if any
                  $('.modal').modal('hide');

                  crud.table.ajax.reload();
              },
              error: function(result) {
                  // Show an alert with the result
                  new PNotify({
                      title: "Cloning failed",
                      text: "The new entry could not be created. Please try again.",
                      type: "warning"
                  });
              }
          });
      }
	}

	// make it so that the function above is run after each DataTable draw event
	// crud.addFunctionToDataTablesDrawEventQueue('cloneEntry');
</script>
  • bulk_clone.blade.php
@if ($crud->hasAccess('create'))
	<a href="javascript:void(0)" onclick="bulkCloneEntries(this)" class="btn btn-default"><i class="fa fa-clone"></i> Clone</a>
@endif

@push('after_scripts')
<script>
	if (typeof bulkCloneEntries != 'function') {
	  function bulkCloneEntries(button) {

	      if (typeof crud.checkedItems === 'undefined' || crud.checkedItems.length == 0)
	      {
	      	new PNotify({
	              title: "{{ trans('backpack::crud.bulk_no_entries_selected_title') }}",
	              text: "{{ trans('backpack::crud.bulk_no_entries_selected_message') }}",
	              type: "warning"
	          });

	      	return;
	      }

	      var message = "Are you sure you want to clone these :number entries?";
	      message = message.replace(":number", crud.checkedItems.length);

	      // show confirm message
	      if (confirm(message) == true) {
	      		var ajax_calls = [];

		        // for each crud.checkedItems
		        crud.checkedItems.forEach(function(item) {
	      		  var clone_route = "{{ url($crud->route) }}/"+item+"/clone";

		      	  // submit an AJAX delete call
	      		  ajax_calls.push($.ajax({
		              url: clone_route,
		              type: 'POST',
		              success: function(result) {
		                  // Show an alert with the result
		                  new PNotify({
		                      title: "Entry cloned",
		                      text: "A new entry has been added, with the same information as this one.",
		                      type: "success"
		                  });
		              },
		              error: function(result) {
		                  // Show an alert with the result
		                  new PNotify({
		                      title: "Cloning failed",
		                      text: "The new entry could not be created. Please try again.",
		                      type: "warning"
		                  });
		              }
		          }));

		      });

		      $.when.apply(this, ajax_calls).then(function ( ajax_calls ) {
		      		crud.checkedItems = [];
		      		crud.table.ajax.reload();
				});
	      }
      }
	}
</script>
@endpush

Hope they help someone.

@tabacitu tabacitu changed the title [Feature req][4.0] Clone operation [Feature][3.4][WIP] Clone operation Aug 27, 2018
@cristianuibar
Copy link

Thank you @tabacitu ! I'll give them a try tonight.

@tabacitu
Copy link
Member Author

BOOM! PR for it here: #1708

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants