Frontend Adapters
Every entry in the adapter mapping pairs a frontend adapter with a backend adapter. The frontend adapter decides
which input controls the Tree Levels tab renders for that data type; the backend adapter resolves the value used to
build the tree.
The bundle ships four frontend adapters: SimpleValueAdapter, LocalizedFieldAdapter, RelationAdapter and
ObjectBrickAdapter.
Frontend adapters are registered in the frontend adapter registry, a service the bundle binds to the Studio UI
container under the id BackendPowerTools/AetConfiguration/FrontendAdapterRegistry. Your Studio UI bundle can add its
own adapter in three steps:
- Create a React component that renders the controls;
- Register the component in the frontend adapter registry;
- Add the adapter to the mapping.
Create the Component
An adapter is a plain React component. It renders additional Form.Items for one tree level and receives the tree
level context as props:
| Prop | Description |
|---|---|
classId | Id of the data-object class the tree is built for. |
classFieldName | Name of the class field selected for this tree level. |
classField | The selected class field as delivered by the backend, including its additionalData. |
adapterConfiguration | The adapter configuration currently stored for this tree level. |
Every control must live under the adapterConfiguration form path. Whatever the controls collect is stored as the
adapter configuration of the tree level and passed to the backend adapter's setConfiguration(). Values must be
scalars (string, number or boolean); the backend configuration schema rejects arrays and nested objects, so a
multi-select has to be stored as e.g. a comma-separated string.
import React from 'react'
import { useTranslation } from '@pimcore/studio-ui-bundle/app'
import { Form, Input } from '@pimcore/studio-ui-bundle/components'
export interface AetFrontendAdapterProps {
classId: string
classFieldName: string
classField?: { name: string, title: string, fieldType: string, adapterType: string, additionalData?: unknown }
adapterConfiguration?: Record<string, string | number | boolean | null | undefined>
}
export const FooAdapter = (props: AetFrontendAdapterProps): React.JSX.Element => {
const { t } = useTranslation()
return (
<>
<Form.Item
label={ t('aet-configuration.tree-level.icon-class') }
name={ ['adapterConfiguration', 'treeLevelIconClass'] }
>
<Input />
</Form.Item>
<Form.Item
label="Placeholder"
name={ ['adapterConfiguration', 'placeholder'] }
>
<Input />
</Form.Item>
</>
)
}
Register the Component
Register the adapter in the onInit hook of one of your bundle's Studio UI modules. The registry is bound and seeded
with the built-in adapters while the Backend Power Tools plugin initializes, which happens before any module's onInit
runs. Registering and overriding therefore behave the same regardless of the order in which the bundles' modules run.
import type React from 'react'
import { container, type AbstractModule } from '@pimcore/studio-ui-bundle'
import { FooAdapter, type AetFrontendAdapterProps } from './foo-adapter'
interface AetFrontendAdapterRegistry {
registerAdapter: (adapter: { type: string, component: React.ComponentType<AetFrontendAdapterProps> }) => void
overrideAdapter: (adapter: { type: string, component: React.ComponentType<AetFrontendAdapterProps> }) => boolean
hasAdapter: (type: string) => boolean
}
export const FooModule: AbstractModule = {
onInit: (): void => {
const registry = container.get<AetFrontendAdapterRegistry>('BackendPowerTools/AetConfiguration/FrontendAdapterRegistry')
registry.registerAdapter({
type: 'FooAdapter', //= reflects the "frontend" adapter in the config
component: FooAdapter
})
}
}
registerAdapter() refuses to register a type twice and reports the collision through the Studio error handler, so one
bundle cannot silently replace another bundle's adapter. To replace a registered adapter on purpose — for example to
take over one of the built-in adapters — call overrideAdapter() instead.
Add the Adapter to the Mapping
The last step is to add the adapter to the mapping in the config.yaml of your bundle. The string provided to
frontend has to match the type you registered.
pimcore_backend_power_tools:
pimcore_alternative_object_trees:
adapters:
input: #= the datatype of the corresponding class field
frontend: FooAdapter
backend: Pimcore\Bundle\BackendPowerToolsBundle\Adapter\AlternativeElementTree\DataObject\SimpleValueAdapter
If the frontend value names a type that no bundle registered, the tree level renders the class field selector only
and no adapter-specific controls.