To add an object duplication feature to a CRUD list, you
need to modify the list by adding the button, configure a route,
execute object duplication code, and finally, reference the
controller that will host our function in our interface, via its
service.
Adding the button:
In your controller, add the
button by referencing the button template.
Here we set the path
'Admin/list__action_clone.html.twig'. The system will look for the
file at: /templates/Admin/list__action_clone.html.twig
protected function configureListFields(ListMapper $listMapper)
{
$listMapper->addIdentifier('id', null, ['label' => 'id']);
$listMapper->addIdentifier('label', null, ['label' => 'type']);
$listMapper->addIdentifier('description', null, ['label' => 'Label']);
$listMapper->add('actif', null, ['editable' => true]);
$listMapper->add('_action', null, [
'actions' => [
'clone' => [
'template' => 'Admin/list__action_clone.html.twig'
]
]
]);
}
The content of our twig file is very simple. It's just the display of an icon.
<a class="btn btn-sm btn-default"
href="{{ admin.generateObjectUrl('clone', object) }}"
title="{{ 'Clone'|trans({}, 'default') }}"
alt="{{ 'Clone'|trans({}, 'default') }}">
<i class="fa fa-clone"></i>
</a>
The next step is to create the route in our controller:
protected function configureRoutes(RouteCollection $collection)
{
$collection->add('clone', $this->getRouterIdParameter().'/clone');
}
And the last step, clone our object along with all of its
configurations. To do this, we will create a controller
specifically dedicated to managing our additional functions and
add it to our service so it becomes available.
In App/Admin
we're going to create a file called CustomAction.php which will
contain our function cloneAction().
This must be extended by
CRUDController and not AbstractAdmin as with our interfaces. This
is why we put our function in a new file. But it also allows us to
reuse this function if needed.
In src/Admin/CustomAction.php:
<?php
namespace App\Admin;
use Sonata\AdminBundle\Controller\CRUDController;
use Symfony\Component\HttpFoundation\RedirectResponse;
class CustomAction extends CRUDController
{
/**
* @param $id
*/
public function cloneAction($id)
{
$object = $this->admin->getObject($id);
$objectConf=$object->getWtypeWconf()->toArray();
if (!$object) {
throw new NotFoundHttpException(sprintf('unable to find the object with id: %s', $id));
}
$clonedObject = clone $object;
$clonedObject->unsetId();
$clonedObject->setLabel($object->getLabel().' clone('.uniqid().')');
$this->admin->create($clonedObject);
if(!empty($objectConf)){
foreach($objectConf as $c){
$clonedConf=clone $c;
$clonedConf->setWconfTypcont($clonedObject);
$this->admin->create($clonedConf);
}
}
$this->addFlash('sonata_flash_success', 'L\'élément a correctement été dupliqué.');
return new RedirectResponse($this->admin->generateUrl('list'));
// if you have a filtered list and want to keep your filters after the redirect
// return new RedirectResponse($this->admin->generateUrl('list', ['filter' => $this->admin->getFilterParameters()]));
}
}
As you can see, our clone function is responsible for cloning our
object as well as all of its OneToMany relationships.
The line
$clonedConf->setWconfTypcont($clonedObject); is used to put the id
of the parent relationship in the field of the child relationship.
Otherwise, all objects would keep their relationships with the
initial object.
Last step, we need to reference our new
controller in the service of our interface.
Before:
admin.wtype:
class: App\Admin\WtypeAdmin
arguments: [~, App\Entity\Wtype, ~]
calls:
- [addChild, ["@admin.wconf"]]
tags:
- { name: sonata.admin, manager_type: orm, label: "Types de contrats" }
public: true
After:
admin.wtype:
class: App\Admin\WtypeAdmin
arguments: [~, App\Entity\Wtype, App\Admin\CustomAction]
calls:
- [addChild, ["@admin.wconf"]]
tags:
- { name: sonata.admin, manager_type: orm, label: "Types de contrats" }
public: true
Adapt the example to the installed version
The Symfony and Sonata examples remain tied to the versions indicated in the article. Symfony 4.x and Symfony 6.2 are no longer maintained: for a current project, use a maintained branch and the documentation that exactly matches your dependencies.
php bin/console about
composer show symfony/framework-bundle sonata-project/admin-bundle
Before resuming the code, check the signatures, services, routes, and templates concerned. Then add a functional test covering permissions, HTTP code, and the visible result.
Reference: maintained Symfony versions.