Registering a Widget Type
After implementing the repository, hydrator, data resolver, and frontend class, wire them into the bundle through configuration and the Studio UI plugin system.
Backend Registration
1. Register the widget type identifier
Add the type string to pimcore_studio_dashboards.widget_types so it appears in the
add-widget dialog:
# config/packages/pimcore_studio_dashboards.yaml
pimcore_studio_dashboards:
widget_types:
- 'top_assets'
2. Add a config node for the widget storage (optional)
If your repository uses AbstractConfigRepository and LocationAwareConfigRepository,
add a configuration node for widget storage in your own bundle.
The Studio Dashboards extension wires storage parameters automatically only for built-in widget
types and custom_reports. For arbitrary custom nodes (for example top_assets_widgets),
you must provide the storage config and constructor arguments in your own extension/service
configuration.
The built-in widget nodes follow this pattern:
// In your DependencyInjection/Configuration.php
$node->children()
->arrayNode('top_assets_widgets')
->defaultValue([])
->useAttributeAsKey('id')
->arrayPrototype()
->children()
// widget-specific fields
->scalarNode('limit')->defaultValue(10)->end()
->end()
->end()
->end()
->end();
3. Tag service classes
The three service classes are picked up automatically by the tagged-iterator loaders
when decorated with the correct #[AutoconfigureTag] attribute:
| Class | Tag constant |
|---|---|
| Config repository | TaggedIteratorRepository::REPOSITORY_TAG (pimcore.studio_dashboards.widget_repository) |
| Config hydrator | TaggedIteratorHydrator::HYDRATOR_TAG (pimcore.studio_dashboards.widget_hydrator) |
| Data resolver | TaggedIteratorDataResolver::RESOLVER_TAG (pimcore.studio_dashboards.widget_data_resolver) |
With standard Symfony autowiring and autoconfigure: true, annotating the classes is sufficient:
use Pimcore\Bundle\StudioDashboardsBundle\Service\Loader\Widget\TaggedIteratorRepository;
use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag;
#[AutoconfigureTag(TaggedIteratorRepository::REPOSITORY_TAG)]
final class TopAssetsConfigRepository extends AbstractConfigRepository { ... }
use Pimcore\Bundle\StudioDashboardsBundle\Service\Loader\Widget\TaggedIteratorHydrator;
use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag;
#[AutoconfigureTag(TaggedIteratorHydrator::HYDRATOR_TAG)]
final class TopAssetsConfigHydrator implements ConfigHydratorInterface { ... }
use Pimcore\Bundle\StudioDashboardsBundle\Service\Loader\Widget\TaggedIteratorDataResolver;
use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag;
#[AutoconfigureTag(TaggedIteratorDataResolver::RESOLVER_TAG)]
final class TopAssetsResolver implements DataResolverInterface { ... }
Frontend Registration
Register the frontend class through your Studio UI bundle plugin.
1. Bind the service in the plugin's onInit
// src/plugins.ts
import { type IAbstractPlugin } from '@pimcore/studio-ui-bundle'
import { DynamicTypeWidgetTypeTopAssets } from './top-assets/dynamic-type-widget-type-top-assets'
export const TopAssetsPlugin: IAbstractPlugin = {
name: 'top-assets-plugin',
onInit: ({ container }): void => {
container
.bind('StudioDashboards/DynamicTypes/WidgetType/TopAssets')
.to(DynamicTypeWidgetTypeTopAssets)
.inSingletonScope()
},
onStartup: ({ moduleSystem }): void => {
moduleSystem.registerModule(TopAssetsExtension)
}
}
2. Register the dynamic type in the module's onInit
The widget type registry service ID is the plain string
'StudioDashboards/DynamicTypes/WidgetType/Registry'. Use it directly with the DI container -
no import from the dashboards bundle is needed.
// src/top-assets-extension.ts
import { container, type AbstractModule } from '@pimcore/studio-ui-bundle'
const DASHBOARDS_REGISTRY_ID = 'StudioDashboards/DynamicTypes/WidgetType/Registry'
const TOP_ASSETS_WIDGET_ID = 'StudioDashboards/DynamicTypes/WidgetType/TopAssets'
export const TopAssetsExtension: AbstractModule = {
onInit: (): void => {
const widgetTypeRegistry = container.get<any>(DASHBOARDS_REGISTRY_ID)
widgetTypeRegistry.registerDynamicType(
container.get(TOP_ASSETS_WIDGET_ID)
)
}
}
The Studio Dashboards Bundle exposes its module federation entry point under the name
pimcore_studio_dashboards_bundle.
Configure the dashboards bundle as a Module Federation remote in your rsbuild.config.ts
if your extension needs to consume the dashboards plugin entry point.
Do not import deep internal module paths from the remote (for example
pimcore_studio_dashboards_bundle/modules/...). The dashboards bundle exposes only its
root plugin entry (.) through module federation.
Remote configuration example:
// In rsbuild.config.ts remotes:
'pimcore_studio_dashboards_bundle': 'promise new Promise(resolve => { ... })',
Events
The bundle dispatches Symfony events before returning responses from its API controllers. Listen to them to enrich or modify the payload without overriding core services:
| Event class | Constant | Triggered when |
|---|---|---|
DashboardEvent | pre_response.dashboard | A single dashboard is returned |
SimpleDashboardEvent | pre_response.dashboard_simple | A dashboard summary is returned |
WidgetConfigEvent | pre_response.studio_dashboards.widget.config.get | A widget configuration is returned |
WidgetConfigConfigurationEvent | pre_response.studio_dashboards.widget.config.configuration | Widget config options are returned |
WidgetTypeEvent | pre_response.studio_dashboards.widget.type | Widget type metadata is returned |
LayoutOptionEvent | pre_response.dashboard.layout_option | Layout options are returned |
use Pimcore\Bundle\StudioDashboardsBundle\Event\WidgetTypeEvent;
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
#[AsEventListener(event: WidgetTypeEvent::EVENT_NAME)]
final class MyWidgetTypeListener
{
public function __invoke(WidgetTypeEvent $event): void
{
$widgetType = $event->getWidgetType();
// modify $widgetType as needed
}
}