Version

Documentation » Admin

9. Annotations »

« 7. Events

8. Advanced configuration

8.1. Service Configuration

When you create a new Admin service you can configure its dependencies, the services which are injected by default are:

Dependencies Service Id
model_manager sonata.admin.manager.%manager-type%
form_contractor sonata.admin.builder.%manager-type%_form
show_builder sonata.admin.builder.%manager-type%_show
list_builder sonata.admin.builder.%manager-type%_list
datagrid_builder sonata.admin.builder.%manager-type%_datagrid
translator translator
configuration_pool sonata.admin.pool
router router
validator validator
security_handler sonata.admin.security.handler
menu_factory knp_menu.factory
route_builder sonata.admin.route.path_info | sonata.admin.route.path_info_slashes
label_translator_strategy sonata.admin.label.strategy.form_component

Note

%manager-type% is to be replaced by the manager type (orm, doctrine_mongodb...), and the default route_builder depends on it.

You have 2 ways of defining the dependencies inside services.xml:

  • With a tag attribute, less verbose:
  • XML
    <service id="app.admin.project" class="AppBundle\Admin\ProjectAdmin">
        <tag
            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"
            />
        <argument />
        <argument>AppBundle\Entity\Project</argument>
        <argument />
    </service>
    
  • YAML
    app.admin.project:
        class: AppBundle\Admin\ProjectAdmin
        tags:
            - { 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" }
        arguments:
            - ~
            - AppBundle\Entity\Project
            - ~
        public: true
    
  • With a method call, more verbose
  • XML
    <service id="app.admin.project" class="AppBundle\Admin\ProjectAdmin">
        <tag
            name="sonata.admin"
            manager_type="orm"
            group="Project"
            label="Project"
            />
        <argument />
        <argument>AppBundle\Entity\Project</argument>
        <argument />
    
        <call method="setLabelTranslatorStrategy">
            <argument type="service" id="sonata.admin.label.strategy.native" />
        </call>
    
        <call method="setRouteBuilder">
            <argument type="service" id="sonata.admin.route.path_info" />
        </call>
    </service>
    
  • YAML
    app.admin.project:
        class: AppBundle\Admin\ProjectAdmin
        tags:
            - { name: sonata.admin, manager_type: orm, group: "Project", label: "Project" }
        arguments:
            - ~
            - AppBundle\Entity\Project
            - ~
        calls:
            - [ setLabelTranslatorStrategy, [ "@sonata.admin.label.strategy.native" ]]
            - [ setRouteBuilder, [ "@sonata.admin.route.path_info" ]]
        public: true
    

If you want to modify the service that is going to be injected, add the following code to your application’s config file:

  • YAML
    # app/config/config.yml
    admins:
        sonata_admin:
            sonata.order.admin.order:   # id of the admin service this setting is for
                model_manager:          # dependency name, from the table above
                    sonata.order.admin.order.manager  # customised service id
    

8.1.1. Creating a custom RouteBuilder

To create your own RouteBuilder create the PHP class and register it as a service:

  • php Route Generator
<?php
namespace AppBundle\Route;

use Sonata\AdminBundle\Builder\RouteBuilderInterface;
use Sonata\AdminBundle\Admin\AdminInterface;
use Sonata\AdminBundle\Route\PathInfoBuilder;
use Sonata\AdminBundle\Route\RouteCollection;

class EntityRouterBuilder extends PathInfoBuilder implements RouteBuilderInterface
{
    /**
     * @param AdminInterface  $admin
     * @param RouteCollection $collection
     */
    public function build(AdminInterface $admin, RouteCollection $collection)
    {
        parent::build($admin, $collection);

        $collection->add('yourSubAction');

        // The create button will disappear, delete functionality will be disabled as well
        // No more changes needed!
        $collection->remove('create');
        $collection->remove('delete');
    }
}
  • xml service registration
  • XML
    <service id="app.admin.entity_route_builder" class="AppBundle\Route\EntityRouterBuilder">
        <argument type="service" id="sonata.admin.audit.manager" />
    </service>
    
  • YAML service registration
  • YAML
    services:
        app.admin.entity_route_builder:
            class: AppBundle\Route\EntityRouterBuilder
            arguments:
                - "@sonata.admin.audit.manager"
    

8.2. Inherited classes

You can manage inherited classes by injecting subclasses using the service configuration.

Lets consider a base class named Person and its subclasses Student and Teacher:

  • XML
    <service id="app.admin.person" class="AppBundle\Admin\PersonAdmin">
        <tag name="sonata.admin" manager_type="orm" group="admin" label="Person" />
        <argument/>
        <argument>AppBundle\Entity\Person</argument>
        <argument></argument>
        <call method="setSubClasses">
            <argument type="collection">
                <argument key="student">AppBundle\Entity\Student</argument>
                <argument key="teacher">AppBundle\Entity\Teacher</argument>
            </argument>
        </call>
    </service>
    

You will just need to change the way forms are configured in order to take into account these new subclasses:

<?php
// src/AppBundle/Admin/PersonAdmin.php

protected function configureFormFields(FormMapper $formMapper)
{
    $subject = $this->getSubject();

    $formMapper
        ->add('name')
    ;

    if ($subject instanceof Teacher) {
        $formMapper->add('course', 'text');
    }
    elseif ($subject instanceof Student) {
        $formMapper->add('year', 'integer');
    }
}

8.3. Tab Menu

8.3.1. ACL

Though the route linked by a menu may be protected the Tab Menu will not automatically check the ACl for you. The link will still appear unless you manually check it using the hasAccess method:

<?php

protected function configureTabMenu(MenuItemInterface $menu, $action, AdminInterface $childAdmin = null)
{
    // Link will always appear even if it is protected by ACL
    $menu->addChild($this->trans('Show'), array('uri' => $admin->generateUrl('show', array('id' => $id))));

    // Link will only appear if access to ACL protected URL is granted
    if ($this->hasAccess('edit')) {
        $menu->addChild($this->trans('Edit'), array('uri' => $admin->generateUrl('edit', array('id' => $id))));
    }
}

8.3.3. Translations

The translation parameters and domain can be customised by using the translation_domain and translation_parameters keys of the extra array of data associated with the item, respectively.

<?php
$menuItem->setExtras(array(
    'translation_parameters' => array('myparam' => 'myvalue'),
    'translation_domain' => 'My domain',
));

You can also set the translation domain on the menu root, and children will inherit it :

<?php
$menu->setExtra('translation_domain', 'My domain');

8.3.4. Filter parameters

You can add or override filter parameters to the Tab Menu:

<?php

use Knp\Menu\ItemInterface as MenuItemInterface;
use Sonata\AdminBundle\Admin\AbstractAdmin;
use Sonata\AdminBundle\Admin\AdminInterface;
use Sonata\CoreBundle\Form\Type\EqualType;

class DeliveryAdmin extends AbstractAdmin
{
    protected function configureTabMenu(MenuItemInterface $menu, $action, AdminInterface $childAdmin = null)
    {
        if (!$childAdmin && !in_array($action, array('edit', 'show', 'list'))) {
            return;
        }

        if ($action == 'list') {
            // Get current filter parameters
            $filterParameters = $this->getFilterParameters();

            // Add or override filter parameters
            $filterParameters['status'] = array(
                'type'  => EqualType::TYPE_IS_EQUAL, // => 1
                'value' => Delivery::STATUS_OPEN,
            );

            // Add filters to uri of tab
            $menu->addChild('List open deliveries', array('uri' => $this->generateUrl('list', array(
                'filter' => $filterParameters,
            ))));

            return;
        }
    }
}

The Delivery class is based on the sonata_type_translatable_choice example inside the Core’s documentation: http://sonata-project.org/bundles/core/master/doc/reference/form_types.html#sonata-type-translatable-choice

8.4. Actions Menu

You can add custom items to the actions menu for a specific action by overriding the following method:

public function configureActionButtons(AdminInterface $admin, $list, $action, $object)
{
    if (in_array($action, array('show', 'edit', 'acl')) && $object) {
        $buttonList['custom'] = array(
            'template' => 'AppBundle:Button:custom_button.html.twig',
        );
    }

    // Remove history action
    unset($buttonList['history']);

    return $buttonList;
}
Custom action buttons

8.5. Disable content stretching

You can disable html, body and sidebar elements stretching. These containers are forced to be full height by default. If you use custom layout or just don’t need such behavior, add no-stretch class to the <html> tag.

For example:

{# src/AppBundle/Resources/views/standard_layout.html.twig #}

{% block html_attributes %}class="no-js no-stretch"{% endblock %}

8.6. Custom Action Access Management

You can customize the access system inside the CRUDController by adding some entries inside the $accessMapping array in the linked Admin.

<?php
// src/AppBundle/Admin/PostAdmin.php

class CustomAdmin extends AbstractAdmin
{
    protected $accessMapping = array(
        'myCustomFoo' => 'EDIT',
        'myCustomBar' => array('EDIT', 'LIST'),
    );
}

<?php
// src/AppBundle/Controller/CustomCRUDController.php

class CustomCRUDController extends CRUDController
{
    public function myCustomFooAction()
    {
        $this->admin->checkAccess('myCustomFoo');
        // If you can't access to EDIT role for the linked admin, an AccessDeniedException will be thrown

        // ...
    }

    public function myCustomBarAction($object)
    {
        $this->admin->checkAccess('myCustomBar', $object);
        // If you can't access to EDIT AND LIST roles for the linked admin, an AccessDeniedException will be thrown

        // ...
    }

    // ...
}

You can also fully customize how you want to handle your access management by simply overriding checkAccess function

<?php
// src/AppBundle/Admin/CustomAdmin.php

class CustomAdmin extends AbstractAdmin
{
    public function checkAccess($action, $object = null)
    {
        $this->customAccessLogic();
    }

    // ...
}

Found a typo? Something is wrong in this documentation? Just fork and edit it!