recipe_custom_action.rst 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. Creating a Custom Admin Action
  2. ==============================
  3. This is a full working example of creating a custom list action for SonataAdmin.
  4. The example is based on an existing ``CarAdmin`` class in an ``AppBundle``.
  5. It is assumed you already have an admin service up and running.
  6. The recipe
  7. ----------
  8. SonataAdmin provides a very straight-forward way of adding your own custom actions.
  9. To do this we need to:
  10. - extend the ``SonataAdmin:CRUD`` Controller and tell our admin class to use it
  11. - create the custom action in our Controller
  12. - create a template to show the action in the list view
  13. - add the route and the new action in the Admin class
  14. Extending the Admin Controller
  15. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  16. First you need to create your own Controller extending the one from SonataAdmin
  17. .. code-block:: php
  18. <?php
  19. // src/AppBundle/Controller/CRUDController.php
  20. namespace AppBundle\Controller;
  21. use Sonata\AdminBundle\Controller\CRUDController as Controller;
  22. class CRUDController extends Controller
  23. {
  24. // ...
  25. }
  26. Admin classes by default use the ``SonataAdmin:CRUD`` controller, this is the third parameter
  27. of an admin service definition, you need to change it to your own.
  28. Register the Admin as a Service
  29. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  30. Either by using XML:
  31. .. code-block:: xml
  32. <!-- src/AppBundle/Resources/config/admin.xml -->
  33. <service id="app.admin.car" class="AppBundle\Admin\CarAdmin">
  34. <tag name="sonata.admin" manager_type="orm" group="Demo" label="Car" />
  35. <argument />
  36. <argument>AppBundle\Entity\Car</argument>
  37. <argument>AppBundle:CRUD</argument>
  38. </service>
  39. or by adding it to your ``admin.yml``:
  40. .. code-block:: yaml
  41. # src/AppBundle/Resources/config/admin.yml
  42. services:
  43. app.admin.car:
  44. class: AppBundle\Admin\CarAdmin
  45. tags:
  46. - { name: sonata.admin, manager_type: orm, group: Demo, label: Car }
  47. arguments:
  48. - null
  49. - AppBundle\Entity\Car
  50. - AppBundle:CRUD
  51. For more information about service configuration please refer to Step 3 of :doc:`../reference/getting_started`
  52. Create the custom action in your Controller
  53. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  54. Now it is time to actually create your custom action here, for this example I chose
  55. to implement a ``clone`` action.
  56. .. code-block:: php
  57. <?php
  58. // src/AppBundle/Controller/CRUDController.php
  59. namespace AppBundle\Controller;
  60. use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
  61. use Sonata\AdminBundle\Controller\CRUDController as Controller;
  62. use Symfony\Component\HttpFoundation\RedirectResponse;
  63. class CRUDController extends Controller
  64. {
  65. public function cloneAction()
  66. {
  67. $object = $this->admin->getSubject();
  68. if (!$object) {
  69. throw new NotFoundHttpException(sprintf('unable to find the object with id : %s', $id));
  70. }
  71. // Be careful, you may need to overload the __clone method of your object
  72. // to set its id to null !
  73. $clonedObject = clone $object;
  74. $clonedObject->setName($object->getName().' (Clone)');
  75. $this->admin->create($clonedObject);
  76. $this->addFlash('sonata_flash_success', 'Cloned successfully');
  77. return new RedirectResponse($this->admin->generateUrl('list'));
  78. // if you have a filtered list and want to keep your filters after the redirect
  79. // return new RedirectResponse($this->admin->generateUrl('list', $this->admin->getFilterParameters()));
  80. }
  81. }
  82. Here we first get the id of the object, see if it exists then clone it and insert the clone
  83. as a new object. Finally we set a flash message indicating success and redirect to the list view.
  84. If you want to add the current filter parameters to the redirect url you can add them to the `generateUrl` method:
  85. .. code-block:: php
  86. return new RedirectResponse($this->admin->generateUrl('list', array('filter' => $this->admin->getFilterParameters())));
  87. Using template in new controller
  88. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  89. If you want to render something here you can create new template anywhere, extend sonata layout
  90. and use `sonata_admin_content` block.
  91. .. code-block:: html+jinja
  92. {% extends 'SonataAdminBundle::standard_layout.html.twig' %}
  93. {% block sonata_admin_content %}
  94. Your content here
  95. {% endblock %}
  96. Create a template for the new action
  97. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  98. You need to tell SonataAdmin how to render your new action. You do that by
  99. creating a ``list__action_clone.html.twig`` in the namespace of your custom
  100. Admin Controller.
  101. .. code-block:: html+jinja
  102. {# src/AppBundle/Resources/views/CRUD/list__action_clone.html.twig #}
  103. <a class="btn btn-sm" href="{{ admin.generateObjectUrl('clone', object) }}">clone</a>
  104. Right now ``clone`` is not a known route, we define it in the next step.
  105. Bringing it all together
  106. ^^^^^^^^^^^^^^^^^^^^^^^^
  107. What is left now is actually adding your custom action to the admin class.
  108. You have to add the new route in ``configureRoutes``:
  109. .. code-block:: php
  110. // ...
  111. use Sonata\AdminBundle\Route\RouteCollection;
  112. protected function configureRoutes(RouteCollection $collection)
  113. {
  114. $collection->add('clone', $this->getRouterIdParameter().'/clone');
  115. }
  116. This gives us a route like ``../admin/app/car/1/clone``.
  117. You could also just write ``$collection->add('clone');`` to get a route like ``../admin/app/car/clone?id=1``
  118. Next we have to add the action in ``configureListFields`` specifying the template we created.
  119. .. code-block:: php
  120. protected function configureListFields(ListMapper $listMapper)
  121. {
  122. $listMapper
  123. // other fields...
  124. ->add('_action', 'actions', array(
  125. 'actions' => array(
  126. // ...
  127. 'clone' => array(
  128. 'template' => 'AppBundle:CRUD:list__action_clone.html.twig'
  129. )
  130. )
  131. ))
  132. ;
  133. }
  134. The full ``CarAdmin.php`` example looks like this:
  135. .. code-block:: php
  136. <?php
  137. // src/AppBundle/Admin/CarAdmin.php
  138. namespace AppBundle\Admin;
  139. use Sonata\AdminBundle\Admin\Admin;
  140. use Sonata\AdminBundle\Datagrid\DatagridMapper;
  141. use Sonata\AdminBundle\Datagrid\ListMapper;
  142. use Sonata\AdminBundle\Form\FormMapper;
  143. use Sonata\AdminBundle\Route\RouteCollection;
  144. use Sonata\AdminBundle\Show\ShowMapper;
  145. class CarAdmin extends Admin
  146. {
  147. protected function configureRoutes(RouteCollection $collection)
  148. {
  149. $collection->add('clone', $this->getRouterIdParameter().'/clone');
  150. }
  151. protected function configureDatagridFilters(DatagridMapper $datagridMapper)
  152. {
  153. // ...
  154. }
  155. protected function configureFormFields(FormMapper $formMapper)
  156. {
  157. // ...
  158. }
  159. protected function configureListFields(ListMapper $listMapper)
  160. {
  161. $listMapper
  162. ->addIdentifier('name')
  163. ->add('engine')
  164. ->add('rescueEngine')
  165. ->add('createdAt')
  166. ->add('_action', 'actions', array(
  167. 'actions' => array(
  168. 'show' => array(),
  169. 'edit' => array(),
  170. 'delete' => array(),
  171. 'clone' => array(
  172. 'template' => 'AppBundle:CRUD:list__action_clone.html.twig'
  173. )
  174. )
  175. ));
  176. }
  177. protected function configureShowFields(ShowMapper $showMapper)
  178. {
  179. // ...
  180. }
  181. }