Skip to content

Extending tabs

Many elements of the Back Office interface, such as System Information or Location View, are built using tabs.

Tabs in System Information

You can extend existing tab groups with new tabs, or create your own tab groups.

Adding a new tab group

New tab groups are created using the TabsComponent.

Register your tab group as a service in config/services.yaml:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
services:
    # ...
    app.my_tabs.custom_group:
        parent: EzSystems\EzPlatformAdminUi\Component\TabsComponent
        autowire: true
        autoconfigure: false
        arguments:
            $groupIdentifier: 'custom_group'
        tags:
            - { name: ezplatform.admin_ui.component, group: 'dashboard-blocks' }

The tab group must be tagged with ezplatform.admin_ui.component. The group tag indicates where the group will be rendered. For the list of possible rendering places, see Injecting custom components.

$groupIdentifier is the name that you point to when assigning a tab to this group. You can also provide the $template argument to use a custom template for rendering the group.

Adding a tab group with custom logic

To create a custom tab group with additional logic, you need to create it at the level of compiling the Symfony container, using a CompilerPass.

For example, in src/DependencyInjection/Compiler/CustomTabGroupPass.php:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
namespace App\DependencyInjection\Compiler;

use EzSystems\EzPlatformAdminUi\Tab\TabGroup;
use EzSystems\EzPlatformAdminUi\Tab\TabRegistry;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;

class CustomTabGroupPass implements CompilerPassInterface
{
    public function process(ContainerBuilder $container)
    {
        if (!$container->hasDefinition(TabRegistry::class)) {
            return;
        }

        $tabRegistry = $container->getDefinition(TabRegistry::class);
        $tabGroupDefinition = new Definition(
            TabGroup::class,  // or any class that extends TabGroup
            ['custom-tab-group']
        );
        $tabRegistry->addMethodCall('addTabGroup', [$tabGroupDefinition]);
    }
}

You also need to add the compiler pass to src/Kernel.php:

1
2
3
4
5
6
7
8
9
use App\DependencyInjection\Compiler\CustomTabGroupPass;

//

protected function configureContainer(ContainerBuilder $container, LoaderInterface $loader): void
{
    //
    $container->addCompilerPass(new CustomTabGroupPass());
}

Adding a new tab

Before you add a tab to a group you must create the tab's PHP class and define it as a Symfony service with the ezplatform.tab tag in config/services.yaml:

1
2
3
4
5
6
7
8
services:
    # ...
    App\Custom\Tab:
        parent: EzSystems\EzPlatformAdminUi\Tab\AbstractTab
        autowire: true
        autoconfigure: false
        tags:
            - { name: ezplatform.tab, group: dashboard-everyone }

This configuration also assigns the new tab to dashboard-everyone. You can also use here the name of your own custom tab group.

The tab class can look like this (in src/Custom/Tab.php):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
namespace App\Custom;

use EzSystems\EzPlatformAdminUi\Tab\AbstractTab;

class Tab extends AbstractTab
{
    public function getIdentifier(): string
    {
        return 'custom-tab';
    }

    public function getName(): string
    {
        return /** @Desc("Custom Tab") */
            $this->translator->trans('custom.tab.name', [], 'some_translation_domain');
    }

    public function renderView(array $parameters): string
    {
        // Do rendering here

        return $this->twig->render('my_tabs/custom.html.twig', [
            'foo' => 'Bar!',
        ]);
    }
}

Beyond the AbstractTab used in the example above you can use two specialized tab types:

  • AbstractControllerBasedTab enables you to embed the results of a controller action in the tab.
  • AbstractRouteBasedTab embeds the results of the selected routing, passing applicable parameters.

Custom Tab

Modifying tab display

You can order the tabs by making the tab implement OrderedTabInterface. The order will then depend on the numerical value returned by the getOrder method.

1
2
3
4
5
6
7
class Tab extends AbstractTab implements OrderedTabInterface
{
    public function getOrder(): int
    {
        return 100;
    }
}

The tabs will be displayed according to this value in ascending order.

Tip

It is good practice to reserve some distance between these values, for example to stagger them by step of 10. It may come useful if you later need to place something between the existing tabs.

You can also influence tab display (e.g. order tabs, remove or modify them, etc.) by using Event Listeners:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class TabEvents
{
    /**
     * Happens just before rendering a tab group.
     */
    const TAB_GROUP_PRE_RENDER = 'ezplatform.tab.group.pre_render';

    /**
     * Happens just before rendering a tab.
     */
    const TAB_PRE_RENDER = 'ezplatform.tab.pre_render';
}

As an example, see how OrderedTabInterface is implemented:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
<?php

namespace EzSystems\EzPlatformAdminUi\Tab\Event\Subscriber;

use EzSystems\EzPlatformAdminUi\Tab\Event\TabEvents;
use EzSystems\EzPlatformAdminUi\Tab\Event\TabGroupEvent;
use EzSystems\EzPlatformAdminUi\Tab\OrderedTabInterface;
use EzSystems\EzPlatformAdminUi\Tab\TabInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Reorders tabs according to their Order value (Tabs implementing OrderedTabInterface).
 * Tabs without order specified are pushed to the end of the group.
 *
 * @see OrderedTabInterface
 */
class OrderedTabSubscriber implements EventSubscriberInterface
{
    /**
     * @return array
     */
    public static function getSubscribedEvents(): array
    {
        return [
            TabEvents::TAB_GROUP_PRE_RENDER => ['onTabGroupPreRender'],
        ];
    }

    /**
     * @param TabGroupEvent $tabGroupEvent
     */
    public function onTabGroupPreRender(TabGroupEvent $tabGroupEvent)
    {
        $tabGroup = $tabGroupEvent->getData();
        $tabs = $tabGroup->getTabs();

        $tabs = $this->reorderTabs($tabs);

        $tabGroup->setTabs($tabs);
        $tabGroupEvent->setData($tabGroup);
    }

    /**
     * @param TabInterface[] $tabs
     *
     * @return array
     */
    private function reorderTabs($tabs): array
    {
        $orderedTabs = [];
        foreach ($tabs as $tab) {
            if ($tab instanceof OrderedTabInterface) {
                $orderedTabs[$tab->getIdentifier()] = $tab;
                unset($tabs[$tab->getIdentifier()]);
            }
        }

        uasort($orderedTabs, [$this, 'sortTabs']);

        return array_merge($orderedTabs, $tabs);
    }

    /**
     * @param OrderedTabInterface $tab1
     * @param OrderedTabInterface $tab2
     *
     * @return int
     */
    private function sortTabs(OrderedTabInterface $tab1, OrderedTabInterface $tab2): int
    {
        return $tab1->getOrder() <=> $tab2->getOrder();
    }
}