batch_actions.rst 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. Batch actions
  2. =============
  3. Batch actions are actions triggered on a set of selected models (all of them or only a specific subset).
  4. Defining new actions
  5. --------------------
  6. You can easily add some custom batch action in the list view. By default the ``delete`` action
  7. allows you to remove several entries at once.
  8. Override the ``getBatchActions`` from your ``Admin`` class to define custom batch actions.
  9. For example, we define here a new ``merge`` action.
  10. .. code-block:: php
  11. <?php
  12. // In your Admin class
  13. public function getBatchActions()
  14. {
  15. // retrieve the default (currently only the delete action) actions
  16. $actions = parent::getBatchActions();
  17. // check user permissions
  18. if($this->hasRoute('edit') && $this->isGranted('EDIT') && $this->hasRoute('delete') && $this->isGranted('DELETE')){
  19. $actions['merge']=[
  20. 'label' => $this->trans('action_merge', array(), 'SonataAdminBundle'),
  21. 'ask_confirmation' => true // If true, a confirmation will be asked before performing the action
  22. ];
  23. }
  24. return $actions;
  25. }
  26. (Optional) Overriding the batch selection template
  27. --------------------------------------------------
  28. Obviously, a merge action requires two kind of selection : a set of source model to merge
  29. into one target model. By default, this bundle only enable the user to select some model,
  30. but there is only one selection kind. Thus you will need to override the ``list__batch.html.twig``
  31. template to display both a checkbox (source selection) and a radio (target selection) for each
  32. model row. See Symfony bundle overriding mechanism for further explanation.
  33. .. code-block:: html+jinja
  34. {# in Acme/ProjectBundle/Resources/views/CRUD/list__batch.html.twig #}
  35. {# See SonataAdminBundle:CRUD:list__batch.html.twig for the current default template #}
  36. <td class="sonata-ba-list-field sonata-ba-list-field-batch">
  37. <input type="checkbox" name="idx[]" value="{{ admin.id(object) }}" />
  38. {# the new radio #}
  39. <input type="radio" name="targetId" value="{{ admin.id(object) }}" />
  40. </td>
  41. And add this:
  42. .. code-block:: php
  43. <?php
  44. // In Acme/ProjectBundle/AcmeProjectBundle.php
  45. public function getParent()
  46. {
  47. return 'SonataAdminBundle';
  48. }
  49. (Optional) Overriding the default relevancy check function
  50. ----------------------------------------------------------
  51. By default, batch actions are not executed if no model was selected, and the user is notified of
  52. this lack of selection. If your custom batch action need some more complex logic to determine if
  53. an action can be performed or not, just define the ``batchActionMyActionIsRelevant`` method in
  54. your ``CRUDController`` class. This check is performed before any confirmation, to make sure there
  55. is actually something to confirm. This method may return three different values :
  56. - ``true``: The batch action is relevant and can be applied.
  57. - a string: The batch action is not relevant given the current request parameters (for example the ``target`` is missing for a ``merge`` action). The returned string is the message that inform the user why the action is aborted.
  58. - ``false``: Same as above, with the default "action aborted, no model selected" notification message.
  59. .. code-block:: php
  60. <?php
  61. // In Acme/Controller/CRUDController.php
  62. public function batchActionMergeIsRelevant(array $normalizedSelectedIds, $allEntitiesSelected)
  63. {
  64. // here you have access to all POST parameters, if you use some custom ones
  65. // POST parameters are kept even after the confirmation page.
  66. $parameterBag = $this->get('request')->request;
  67. // check that a target has been chosen
  68. if (!$parameterBag->has('targetId')) {
  69. return 'flash_batch_merge_no_target';
  70. }
  71. $normalizedTargetId = $parameterBag->get('targetId');
  72. // if all entities are selected, a merge can be done
  73. if ($allEntitiesSelected) {
  74. return true;
  75. }
  76. // filter out the target from the selected models
  77. $normalizedSelectedIds = array_filter($normalizedSelectedIds,
  78. function($normalizedSelectedId) use($normalizedTargetId){
  79. return $normalizedSelectedId !== $normalizedTargetId;
  80. }
  81. );
  82. // if at least one but not the target model is selected, a merge can be done.
  83. return count($normalizedSelectedIds) > 0;
  84. }
  85. Define the core action logic
  86. ----------------------------
  87. The method ``batchActionMyAction`` will be executed to achieve the core logic. The selected
  88. models are passed to the method through a query argument retrieving them. If for some reason
  89. it makes sense to perform your batch action without the default selection method (for example
  90. you defined another way, at template level, to select model at a lower granularity), the
  91. passed query is ``null``.
  92. .. code-block:: php
  93. <?php
  94. // In Acme/Controller/CRUDController.php
  95. public function batchActionMerge(ProxyQueryInterface $selectedModelQuery)
  96. {
  97. if ($this->admin->isGranted('EDIT') === false || $this->admin->isGranted('DELETE') === false)
  98. {
  99. throw new AccessDeniedException();
  100. }
  101. $request = $this->get('request');
  102. $modelManager = $this->admin->getModelManager();
  103. $target = $modelManager->find($this->admin->getClass(), $request->get('targetId'));
  104. if( $target === null){
  105. $this->addFlash('sonata_flash_info', 'flash_batch_merge_no_target');
  106. return new RedirectResponse($this->admin->generateUrl('list',$this->admin->getFilterParameters()));
  107. }
  108. $selectedModels = $selectedModelQuery->execute();
  109. // do the merge work here
  110. try {
  111. foreach ($selectedModels as $selectedModel) {
  112. $modelManager->delete($selectedModel);
  113. }
  114. $modelManager->update($selectedModel);
  115. } catch (\Exception $e) {
  116. $this->addFlash('sonata_flash_error', 'flash_batch_merge_error');
  117. return new RedirectResponse($this->admin->generateUrl('list',$this->admin->getFilterParameters()));
  118. }
  119. $this->addFlash('sonata_flash_success', 'flash_batch_merge_success');
  120. return new RedirectResponse($this->admin->generateUrl('list',$this->admin->getFilterParameters()));
  121. }