advanced_configuration.rst 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  1. Advanced configuration
  2. ======================
  3. Service Configuration
  4. ---------------------
  5. When you create a new Admin service you can configure its dependencies, the services which are injected by default are:
  6. ========================= =============================================
  7. Dependencies Service Id
  8. ========================= =============================================
  9. model_manager sonata.admin.manager.%manager-type%
  10. form_contractor sonata.admin.builder.%manager-type%_form
  11. show_builder sonata.admin.builder.%manager-type%_show
  12. list_builder sonata.admin.builder.%manager-type%_list
  13. datagrid_builder sonata.admin.builder.%manager-type%_datagrid
  14. translator translator
  15. configuration_pool sonata.admin.pool
  16. router router
  17. validator validator
  18. security_handler sonata.admin.security.handler
  19. menu_factory knp_menu.factory
  20. route_builder sonata.admin.route.path_info | sonata.admin.route.path_info_slashes
  21. label_translator_strategy sonata.admin.label.strategy.form_component
  22. ========================= =============================================
  23. .. note::
  24. %manager-type% is to be replaced by the manager type (orm, doctrine_mongodb...),
  25. and the default route_builder depends on it.
  26. You have 2 ways of defining the dependencies inside ``services.xml``:
  27. * With a tag attribute, less verbose:
  28. .. configuration-block::
  29. .. code-block:: xml
  30. <service id="app.admin.project" class="AppBundle\Admin\ProjectAdmin">
  31. <tag
  32. name="sonata.admin"
  33. manager_type="orm"
  34. group="Project"
  35. label="Project"
  36. label_translator_strategy="sonata.admin.label.strategy.native"
  37. route_builder="sonata.admin.route.path_info"
  38. />
  39. <argument />
  40. <argument>AppBundle\Entity\Project</argument>
  41. <argument />
  42. </service>
  43. .. configuration-block::
  44. .. code-block:: yaml
  45. app.admin.project:
  46. class: AppBundle\Admin\ProjectAdmin
  47. tags:
  48. - { name: sonata.admin, manager_type: orm, group: "Project", label: "Project", label_translator_strategy: "sonata.admin.label.strategy.native", route_builder: "sonata.admin.route.path_info" }
  49. arguments:
  50. - ~
  51. - AppBundle\Entity\Project
  52. - ~
  53. * With a method call, more verbose
  54. .. configuration-block::
  55. .. code-block:: xml
  56. <service id="app.admin.project" class="AppBundle\Admin\ProjectAdmin">
  57. <tag
  58. name="sonata.admin"
  59. manager_type="orm"
  60. group="Project"
  61. label="Project"
  62. />
  63. <argument />
  64. <argument>AppBundle\Entity\Project</argument>
  65. <argument />
  66. <call method="setLabelTranslatorStrategy">
  67. <argument type="service" id="sonata.admin.label.strategy.native" />
  68. </call>
  69. <call method="setRouteBuilder">
  70. <argument type="service" id="sonata.admin.route.path_info" />
  71. </call>
  72. </service>
  73. .. configuration-block::
  74. .. code-block:: yaml
  75. app.admin.project:
  76. class: AppBundle\Admin\ProjectAdmin
  77. tags:
  78. - { name: sonata.admin, manager_type: orm, group: "Project", label: "Project" }
  79. arguments:
  80. - ~
  81. - AppBundle\Entity\Project
  82. - ~
  83. calls:
  84. - [ setLabelTranslatorStrategy, [ "@sonata.admin.label.strategy.native" ]]
  85. - [ setRouteBuilder, [ "@sonata.admin.route.path_info" ]]
  86. If you want to modify the service that is going to be injected, add the following code to your
  87. application's config file:
  88. .. configuration-block::
  89. .. code-block:: yaml
  90. # app/config/config.yml
  91. admins:
  92. sonata_admin:
  93. sonata.order.admin.order: # id of the admin service this setting is for
  94. model_manager: # dependency name, from the table above
  95. sonata.order.admin.order.manager # customised service id
  96. Creating a custom RouteBuilder
  97. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  98. To create your own RouteBuilder create the PHP class and register it as a service:
  99. * php Route Generator
  100. .. code-block:: php
  101. <?php
  102. namespace AppBundle\Route;
  103. use Sonata\AdminBundle\Builder\RouteBuilderInterface;
  104. use Sonata\AdminBundle\Admin\AdminInterface;
  105. use Sonata\AdminBundle\Route\PathInfoBuilder;
  106. use Sonata\AdminBundle\Route\RouteCollection;
  107. class EntityRouterBuilder extends PathInfoBuilder implements RouteBuilderInterface
  108. {
  109. /**
  110. * @param AdminInterface $admin
  111. * @param RouteCollection $collection
  112. */
  113. public function build(AdminInterface $admin, RouteCollection $collection)
  114. {
  115. parent::build($admin, $collection);
  116. $collection->add('yourSubAction');
  117. // The create button will disappear, delete functionality will be disabled as well
  118. // No more changes needed!
  119. $collection->remove('create');
  120. $collection->remove('delete');
  121. }
  122. }
  123. * xml service registration
  124. .. configuration-block::
  125. .. code-block:: xml
  126. <service id="app.admin.entity_route_builder" class="AppBundle\Route\EntityRouterBuilder">
  127. <argument type="service" id="sonata.admin.audit.manager" />
  128. </service>
  129. * YAML service registration
  130. .. configuration-block::
  131. .. code-block:: yaml
  132. services:
  133. app.admin.entity_route_builder:
  134. class: AppBundle\Route\EntityRouterBuilder
  135. arguments:
  136. - "@sonata.admin.audit.manager"
  137. Inherited classes
  138. -----------------
  139. You can manage inherited classes by injecting subclasses using the service configuration.
  140. Lets consider a base class named `Person` and its subclasses `Student` and `Teacher`:
  141. .. configuration-block::
  142. .. code-block:: xml
  143. <service id="app.admin.person" class="AppBundle\Admin\PersonAdmin">
  144. <tag name="sonata.admin" manager_type="orm" group="admin" label="Person" />
  145. <argument/>
  146. <argument>AppBundle\Entity\Person</argument>
  147. <argument></argument>
  148. <call method="setSubClasses">
  149. <argument type="collection">
  150. <argument key="student">AppBundle\Entity\Student</argument>
  151. <argument key="teacher">AppBundle\Entity\Teacher</argument>
  152. </argument>
  153. </call>
  154. </service>
  155. You will just need to change the way forms are configured in order to take into account these new subclasses:
  156. .. code-block:: php
  157. <?php
  158. // src/AppBundle/Admin/PersonAdmin.php
  159. protected function configureFormFields(FormMapper $formMapper)
  160. {
  161. $subject = $this->getSubject();
  162. $formMapper
  163. ->add('name')
  164. ;
  165. if ($subject instanceof Teacher) {
  166. $formMapper->add('course', 'text');
  167. }
  168. elseif ($subject instanceof Student) {
  169. $formMapper->add('year', 'integer');
  170. }
  171. }
  172. Tab Menu
  173. --------
  174. ACL
  175. ^^^
  176. Though the route linked by a menu may be protected the Tab Menu will not automatically check the ACl for you.
  177. The link will still appear unless you manually check it using the `isGranted` method:
  178. .. code-block:: php
  179. <?php
  180. protected function configureTabMenu(MenuItemInterface $menu, $action, AdminInterface $childAdmin = null)
  181. {
  182. // Link will always appear even if it is protected by ACL
  183. $menu->addChild($this->trans('Show'), array('uri' => $admin->generateUrl('show', array('id' => $id))));
  184. // Link will only appear if access to ACL protected URL is granted
  185. if ($this->isGranted('EDIT')) {
  186. $menu->addChild($this->trans('Edit'), array('uri' => $admin->generateUrl('edit', array('id' => $id))));
  187. }
  188. }
  189. Dropdowns
  190. ^^^^^^^^^
  191. You can use dropdowns inside the Tab Menu by default. This can be achieved by using
  192. the `'dropdown' => true` attribute:
  193. .. code-block:: php
  194. <?php
  195. // src/AppBundle/Admin/PersonAdmin.php
  196. protected function configureTabMenu(MenuItemInterface $menu, $action, AdminInterface $childAdmin = null)
  197. {
  198. // other tab menu stuff ...
  199. $menu->addChild('comments', array('attributes' => array('dropdown' => true)));
  200. $menu['comments']->addChild('list', array('uri' => $admin->generateUrl('listComment', array('id' => $id))));
  201. $menu['comments']->addChild('create', array('uri' => $admin->generateUrl('addComment', array('id' => $id))));
  202. }
  203. If you want to use the Tab Menu in a different way, you can replace the Menu Template:
  204. .. configuration-block::
  205. .. code-block:: yaml
  206. # app/config/config.yml
  207. sonata_admin:
  208. templates:
  209. tab_menu_template: AppBundle:Admin:own_tab_menu_template.html.twig
  210. Translations
  211. ^^^^^^^^^^^^
  212. The translation parameters and domain can be customised by using the
  213. ``translation_domain`` and ``translation_parameters`` keys of the extra array
  214. of data associated with the item, respectively.
  215. .. code-block:: php
  216. <?php
  217. $menuItem->setExtras(array(
  218. 'translation_parameters' => array('myparam' => 'myvalue'),
  219. 'translation_domain' => 'My domain',
  220. ));
  221. You can also set the translation domain on the menu root, and children will
  222. inherit it :
  223. .. code-block:: php
  224. <?php
  225. $menu->setExtra('translation_domain', 'My domain');
  226. Filter parameters
  227. ^^^^^^^^^^^^^^^^^
  228. You can add or override filter parameters to the Tab Menu:
  229. .. code-block:: php
  230. <?php
  231. use Knp\Menu\ItemInterface as MenuItemInterface;
  232. use Sonata\AdminBundle\Admin\AbstractAdmin;
  233. use Sonata\AdminBundle\Admin\AdminInterface;
  234. use Sonata\CoreBundle\Form\Type\EqualType;
  235. class DeliveryAdmin extends AbstractAdmin
  236. {
  237. protected function configureTabMenu(MenuItemInterface $menu, $action, AdminInterface $childAdmin = null)
  238. {
  239. if (!$childAdmin && !in_array($action, array('edit', 'show', 'list'))) {
  240. return;
  241. }
  242. if ($action == 'list') {
  243. // Get current filter parameters
  244. $filterParameters = $this->getFilterParameters();
  245. // Add or override filter parameters
  246. $filterParameters['status'] = array(
  247. 'type' => EqualType::TYPE_IS_EQUAL, // => 1
  248. 'value' => Delivery::STATUS_OPEN,
  249. );
  250. // Add filters to uri of tab
  251. $menu->addChild('List open deliveries', array('uri' => $this->generateUrl('list', array(
  252. 'filter' => $filterParameters,
  253. ))));
  254. return;
  255. }
  256. }
  257. }
  258. The `Delivery` class is based on the `sonata_type_translatable_choice` example inside the Core's documentation:
  259. http://sonata-project.org/bundles/core/master/doc/reference/form_types.html#sonata-type-translatable-choice
  260. Actions Menu
  261. ------------
  262. You can add custom items to the actions menu for a specific action by overriding the following method:
  263. .. code-block:: php
  264. public function configureActionButtons(AdminInterface $admin, $list, $action, $object)
  265. {
  266. if (in_array($action, array('show', 'edit', 'acl')) && $object) {
  267. $list['custom'] = array(
  268. 'template' => 'AppBundle:Button:custom_button.html.twig',
  269. );
  270. }
  271. // Remove history action
  272. unset($list['history']);
  273. return $list;
  274. }
  275. .. figure:: ../images/custom_action_buttons.png
  276. :align: center
  277. :alt: Custom action buttons
  278. Disable content stretching
  279. --------------------------
  280. You can disable ``html``, ``body`` and ``sidebar`` elements stretching. These containers are forced
  281. to be full height by default. If you use custom layout or just don't need such behavior,
  282. add ``no-stretch`` class to the ``<html>`` tag.
  283. For example:
  284. .. code-block:: html+jinja
  285. {# src/AppBundle/Resources/views/standard_layout.html.twig #}
  286. {% block html_attributes %}class="no-js no-stretch"{% endblock %}
  287. Custom Action Access Management
  288. -------------------------------
  289. You can customize the access system inside the CRUDController by adding some entries inside the `$accessMapping` array in the linked Admin.
  290. .. code-block:: php
  291. <?php
  292. // src/AppBundle/Admin/PostAdmin.php
  293. class CustomAdmin extends AbstractAdmin
  294. {
  295. protected $accessMapping = array(
  296. 'myCustomFoo' => 'EDIT',
  297. 'myCustomBar' => array('EDIT', 'LIST'),
  298. );
  299. }
  300. <?php
  301. // src/AppBundle/Controller/CustomCRUDController.php
  302. class CustomCRUDController extends CRUDController
  303. {
  304. public function myCustomFooAction()
  305. {
  306. $this->admin->checkAccess('myCustomFoo');
  307. // If you can't access to EDIT role for the linked admin, an AccessDeniedException will be thrown
  308. // ...
  309. }
  310. public function myCustomBarAction($object)
  311. {
  312. $this->admin->checkAccess('myCustomBar', $object);
  313. // If you can't access to EDIT AND LIST roles for the linked admin, an AccessDeniedException will be thrown
  314. // ...
  315. }
  316. // ...
  317. }
  318. You can also fully customize how you want to handle your access management by simply overriding ``checkAccess`` function
  319. .. code-block:: php
  320. <?php
  321. // src/AppBundle/Admin/CustomAdmin.php
  322. class CustomAdmin extends AbstractAdmin
  323. {
  324. public function checkAccess($action, $object = null)
  325. {
  326. $this->customAccessLogic();
  327. }
  328. // ...
  329. }