advanced_configuration.rst 14 KB

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