Extending Widgets
Widgets are the main building blocks of the perspectives. They can be used to create unique views for the Pimcore Studio.
How to add a custom widget type
To add a custom widget to the Pimcore Studio Backend, you first need to extend the symfony configuration like the following:
Example
pimcore_studio_backend:
widget_types:
- my_custom_widget_id
How to add a custom widget configuration
The widget configuration utilizes the LocationAwareConfigRepository for storing the configuration. In the symfony tree the
storage location can be configured. The following values are possible:
symfony-config- write configs as Symfony Config as YAML files to/var/config/alternative_object_trees/custom_object_tree.yamlsettings-store- write configs to the SettingsStoredisabled- do not allow editing/writing configs at all
Details also see Pimcore Docs.
Example
pimcore_studio_backend:
config_location:
element_tree_widgets:
write_target:
type: 'symfony-config'
options:
directory: '/var/www/html/var/config/element_tree_widgets'
To add a custom widget configuration, the following steps need to be done:
- Create a new custom widget type.
- Create a new repository using
LocationAwareConfigRepositoryand implementing the mandatoryWidgetConfigRepositoryInterfaceand register the service with thepimcore.studio_backend.widget_repositorytag. - Create a new hydrator which implements the mandatory
WidgetConfigHydratorInterfaceand register the service with thepimcore.studio_backend.widget_hydratortag.
In some cases, there might be a custom configuration that already includes all necessary properties, and the data can be used in the widget directly.
There is a method called isWidgetTypeOnlyWrapper in the WidgetConfigRepositoryInterface which can be used to indicate that widget repository is only a wrapper used for loading of the original configuration.
Restrictions
There are a couple of restrictions and requirements for the widget configuration:
- Widget configuration name must be longer than 3 and shorter than 80 characters. Furthermore, only alphanumeric characters, underscores and spaces are allowed.
- Widget configuration ID is autogenerated by Pimcore Studio, based on the UUID generator.
- Every widget configuration should include at least the following properties:
id- unique identifier of the widget configurationname- name of the widget configurationicon- icon of the widget (can be nullable, in this case default Icon will be used)
- When updating a widget, all configuration properties must be present in the PUT request body.
Example Wrapper Widget Configuration Repository
<?php
declare(strict_types=1);
namespace App\Perspective\Widget\Repository;
use App\Custom\Configuration\Repository\ConfigurationRepositoryInterface;
use Exception;
use Pimcore\Bundle\StudioBackendBundle\Exception\Api\NotFoundException;
use Pimcore\Bundle\StudioBackendBundle\Exception\Api\NotWriteableException;
use Pimcore\Bundle\StudioBackendBundle\Perspective\Repository\WidgetConfigRepositoryInterface;
final readonly class CustomWidgetConfigRepository implements WidgetConfigRepositoryInterface
{
public function __construct(
// Data source of the actual configuration
private ConfigurationRepositoryInterface $configurationRepository,
) {
}
public function getSupportedWidgetType(): string
{
return 'my_custom_widget_type';
}
public function isWidgetTypeOnlyWrapper(): bool
{
return true;
}
/**
* @throws NotWriteableException
*/
public function createConfiguration(array $widgetData): string
{
throw new NotWriteableException(
'myType',
'Creating new my_custom_widget_type via widgets is not supported.'
);
}
/**
* @throws NotWriteableException
*/
public function updateConfiguration(array $widgetData): void
{
throw new NotWriteableException(
'aet',
'Updating new my_custom_widget_type via widgets is not supported.'
);
}
/**
* @throws NotFoundException|NotWriteableException
*/
public function getConfiguration(string $widgetId): array
{
$data = $this->configurationRepository->get($widgetId);
// Do any necessary transformation of the data to match the expected output format for your widget hydrator
if (!is_array($data) || $data[0] === null) {
return [];
}
return $data;
}
/**
* @throws NotFoundException|NotWriteableException
*/
public function listConfigurations(): array
{
// Do any necessary transformation of the data to match the expected output
// See Restrictions section for mandatory properties
return $this->configurationRepository->listConfigurations();
}
/**
* @throws NotWriteableException
*/
public function deleteConfiguration(
string $widgetId
): void {
throw new NotWriteableException(
'aet',
'Deleting new my_custom_widget_type via widgets is not supported.'
);
}
}
Example Widget Configuration Repository
For an example implementation please have a look at the Pimcore\Bundle\StudioBackendBundle\Perspective\Repository\ElementTreeWidgetConfigRepository file.
Example Widget Configuration Hydrator
<?php
declare(strict_types=1);
namespace App\Perspective\Widget\Hydrator;
use App\Perspective\Widget\Model\CustomWidgetConfig;
use Pimcore\Bundle\StudioBackendBundle\Perspective\Schema\WidgetConfig\WidgetConfigHydratorInterface;
final readonly class CustomWidgetConfigHydrator implements WidgetConfigHydratorInterface
{
public function getSupportedWidgetType(): string
{
return 'my_custom_widget_type';
}
public function hydrate(array $widgetData): CustomWidgetConfig
{
return new CustomWidgetConfig(
$widgetData['id'],
$widgetData['name'],
$widgetData['icon'],
$widgetData['customField'],
$widgetData['createdAt'],
$widgetData['isWriteable']
);
}
}
Please note that the Hydrator must return a class extending the Pimcore\Bundle\StudioBackendBundle\Perspective\Schema\WidgetConfig class.
Example Widget Configuration Schema
<?php
declare(strict_types=1);
namespace App\Perspective\Schema;
use Pimcore\Bundle\StudioBackendBundle\Perspective\Schema\WidgetConfig;
#[Schema(
title: 'Custom Widget',
required: [
'customField',
'createdAt',
'isWriteable',
],
type: 'object'
)]
final class MyCustomWidget extends WidgetConfig
{
public function __construct(
string $id,
string $name,
?ElementIcon $icon = null,
#[Property(description: 'Custom Field', type: 'string', example: 'Some Value')]
private readonly string $customField = 'Some Value',
#[Property(description: 'Created At timestamp', type: 'int', example: 1738917191)]
private readonly ?int $createdAt = null,
#[Property(description: 'Is Writeable', type: 'bool', example: false)]
private readonly bool $isWriteable = false,
) {
parent::__construct($id, $name, 'my_custom_widget_type', $icon);
}
public function getCustomField(): string
{
return $this->customField;
}
public function getCreatedAt(): ?int
{
return $this->createdAt;
}
public function isWriteable(): bool
{
return $this->isWriteable;
}
}
Repository and Hydrator will be automatically registered and used by the Pimcore Studio Backend service to handle the widget configuration by defined type.
Example Widget Configuration Yaml
pimcore_studio_backend:
element_tree_widgets:
1efe7ac9_a03a_6334_9e48_13f662882599:
id: 1efe7ac9_a03a_6334_9e48_13f662882599
name: 'My Object Tree Widget'
elementType: 'data-object' # data-object, asset or document types are supported
icon:
type: path
value: 'path/to/config-icon.svg'
rootFolder: '/path/to/root/folder'
showRoot: true
classes: ['CAR']
pql: null # PQL query to filter the tree items
pageSize: 50 # define custom page size for your tree
contextPermissions:
add: true
addFolder: true
changeChildrenSortBy: true
copy: true
cut: true
delete: true
lock: true
lockAndPropagate: true
paste: true
publish: true
reload: true
rename: true
searchAndMove: true
unlock: true
unlockAndPropagate: true
unpublish: true