원클릭으로
pimcore-studio-ui-bundle-structure
Organization and structure of Pimcore Studio bundles - modules, components, file layout
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Organization and structure of Pimcore Studio bundles - modules, components, file layout
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
| name | pimcore-studio-ui-bundle-structure |
| description | Organization and structure of Pimcore Studio bundles - modules, components, file layout |
| metadata | {"audience":"pimcore-developers","focus":"architecture"} |
This skill covers BUNDLE structure (outside studio-ui-bundle)
BEFORE writing any import, read CRITICAL-IMPORT-PATHS.md. All bundle imports use @pimcore/studio-ui-bundle/*.
Understanding the structure and organization of Pimcore Studio bundles:
Use this when:
{bundle-name}/ # e.g., personalization-bundle
│
├── assets/studio/ # Frontend code (TypeScript/React)
│ ├── js/
│ │ ├── src/
│ │ │ ├── modules/ # All Studio modules
│ │ │ │ ├── {module-name}/ # e.g., targeting-rules, personalization
│ │ │ │ │ ├── components/ # React components
│ │ │ │ │ │ ├── editor/
│ │ │ │ │ │ ├── detail/
│ │ │ │ │ │ └── list/
│ │ │ │ │ ├── hooks/ # Custom React hooks
│ │ │ │ │ ├── api/ # API utilities (if complex)
│ │ │ │ │ └── {module-name}-api.tsx # RTK Query endpoints
│ │ │ │ └── index.tsx # Re-exports all modules
│ │ │ └── index.tsx # Main entry point
│ │ └── tsconfig.json
│ ├── package.json # Frontend dependencies
│ └── webpack.config.js # Build configuration
│
├── public/studio/ # Build output (committed to Git)
│ └── build/
│ └── {hash}/ # Unique build hash
│ ├── remoteEntry.js # Module federation entry
│ └── *.js # Compiled bundles
│
├── src/ # PHP backend code
│ ├── Controller/ # API controllers
│ │ └── Studio/
│ │ └── {ModuleName}Controller.php
│ ├── EventSubscriber/ # Event subscribers
│ ├── Service/ # Business logic
│ └── DependencyInjection/
│
├── translations/ # Translation files
│ ├── studio.en.yaml # English (Studio UI)
│ └── admin.en.yaml # Legacy admin (if any)
│
├── config/ # Configuration
│ └── services.yaml
│
└── composer.json # PHP dependencies
modules/{module-name}/
├── components/ # React components
│ ├── editor/ # Edit/create views
│ │ ├── {module}-editor.tsx
│ │ └── tabs/ # If editor has tabs
│ ├── detail/ # Detail/view components
│ │ └── {module}-detail.tsx
│ ├── list/ # List/table views
│ │ └── {module}-list.tsx
│ └── shared/ # Shared components
│
├── hooks/ # Custom hooks
│ ├── use-{module}-helper.tsx
│ └── use-{module}-form.tsx
│
└── {module}-api.tsx # RTK Query API definition
modules/targeting-rules/
├── components/
│ ├── editor/
│ │ └── targeting-rule-editor.tsx # Main editor component
│ ├── list/
│ │ └── targeting-rules-list.tsx # List view
│ └── shared/
│ └── rule-status-badge.tsx # Shared UI component
│
├── hooks/
│ └── use-targeting-rules-helper.tsx # Helper hook
│
└── targeting-rules-api.tsx # API endpoints
// modules/{module-name}/{module-name}-api.tsx
import { api as baseApi } from '@Pimcore/modules/app/app-api'
// Type definitions
export interface EntityDetail {
id: number
settings: {
name: string
active: boolean
// ... other fields
}
}
export interface EntityListItem {
id: number
name: string
// ... abbreviated fields
}
// API endpoints
export const api = baseApi.injectEndpoints({
endpoints: (build) => ({
// List endpoint
getEntityList: build.query<EntityListItem[], void>({
query: () => ({
url: '/pimcore-bundle-{bundle}/{entity}/list',
}),
}),
// Detail endpoint
getEntityById: build.query<EntityDetail, { id: number }>({
query: ({ id }) => ({
url: `/pimcore-bundle-{bundle}/{entity}/${id}`,
}),
}),
// Update mutation
updateEntity: build.mutation<void, { id: number; body: any }>({
query: ({ id, body }) => ({
url: `/pimcore-bundle-{bundle}/{entity}/${id}`,
method: 'PUT',
body,
}),
}),
// Delete mutation
deleteEntity: build.mutation<void, { id: number }>({
query: ({ id }) => ({
url: `/pimcore-bundle-{bundle}/{entity}/${id}`,
method: 'DELETE',
}),
}),
}),
})
// Export hooks
export const {
useGetEntityListQuery,
useGetEntityByIdQuery,
useUpdateEntityMutation,
useDeleteEntityMutation,
} = api
// modules/targeting-rules/targeting-rules-api.tsx
export const api = baseApi.injectEndpoints({
endpoints: (build) => ({
bundlePersonalizationTargetingRuleGetById: build.query<
TargetingRuleDetail,
{ ruleId: number }
>({
query: ({ ruleId }) => ({
url: `/pimcore-bundle-personalization/targeting-rule/${ruleId}`,
}),
}),
bundlePersonalizationTargetingRuleUpdate: build.mutation<
void,
{ ruleId: number; body: UpdateBody }
>({
query: ({ ruleId, body }) => ({
url: `/pimcore-bundle-personalization/targeting-rule/${ruleId}`,
method: 'PUT',
body,
}),
}),
}),
})
export const {
useBundlePersonalizationTargetingRuleGetByIdQuery,
useBundlePersonalizationTargetingRuleUpdateMutation,
} = api
targeting-rule-editor.tsxTargetingRuleEditor// Editor component
export const TargetingRuleEditor: FC<Props> = ({ rule }) => {
// Edit existing or create new
}
// Detail component
export const TargetingRuleDetail: FC<Props> = ({ ruleId }) => {
// View-only display
}
// List component
export const TargetingRulesList: FC = () => {
// Table/grid of items
}
translations/studio.en.yaml
# Module name (top level)
personalization:
# Entity/feature (second level)
targeting-rules:
# Context (third level)
list:
title: 'Targeting Rules'
empty: 'No targeting rules found'
editor:
title: 'Edit Targeting Rule'
save: 'Save'
cancel: 'Cancel'
# Actions (third level)
create:
success: 'Targeting rule created'
error: 'Failed to create targeting rule'
update:
success: 'Saved successfully'
error: 'Failed to save targeting rule'
delete:
success: 'Targeting rule deleted'
error: 'Failed to delete targeting rule'
confirm: 'Are you sure you want to delete this rule?'
# Another entity
target-groups:
list:
title: 'Target Groups'
update:
success: 'Saved successfully'
// src/Controller/Studio/{Module}Controller.php
namespace Pimcore\Bundle\{BundleName}\Controller\Studio;
use Pimcore\Bundle\StudioBackendBundle\Controller\AbstractApiController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Annotation\Route;
#[Route('/pimcore-bundle-{bundle}/{entity}', name: 'pimcore_bundle_{bundle}_{entity}_')]
class EntityController extends AbstractApiController
{
#[Route('/{id}', name: 'get', methods: ['GET'])]
public function getById(int $id): JsonResponse
{
// Implementation
}
#[Route('/{id}', name: 'update', methods: ['PUT'])]
public function update(int $id): JsonResponse
{
// Implementation
}
}
// src/Service/{Module}Service.php
namespace Pimcore\Bundle\{BundleName}\Service;
class EntityService
{
public function getById(int $id): array
{
// Business logic
}
public function update(int $id, array $data): void
{
// Business logic
}
}
// assets/studio/js/src/index.tsx
export * from './modules/targeting-rules/targeting-rules-api'
export * from './modules/personalization/personalization-api'
// Export other modules
// assets/studio/js/src/modules/index.tsx
export * from './targeting-rules/targeting-rules-api'
export * from './personalization/personalization-api'
public/studio/build/{hash}/
remoteEntry.js: Module federation entry point (loaded by Studio)*.js: Compiled JavaScript bundles*.js.map: Source maps for debuggingtargeting-rules)assets/studio/js/src/modules/{module}/components/editor/, list/, detail/assets/studio/js/src/modules/{module}/{module}-api.tsxtranslations/studio.en.yamltargeting-rules)src/Controller/Studio/{Module}Controller.phpmodules/data-importer/
├── components/
│ ├── editor/
│ │ ├── data-importer-editor.tsx
│ │ └── tabs/
│ │ ├── general-tab.tsx
│ │ ├── mapping-tab.tsx
│ │ └── schedule-tab.tsx
│ └── list/
│ └── data-importer-list.tsx
modules/workflow/
├── components/
│ ├── editor/
│ │ ├── workflow-editor.tsx
│ │ └── forms/
│ │ ├── basic-info-form.tsx
│ │ ├── transitions-form.tsx
│ │ └── places-form.tsx
modules/reports/
├── components/
│ ├── dashboard/
│ │ └── reports-dashboard.tsx
│ ├── viewer/
│ │ └── report-viewer.tsx
│ └── builder/
│ └── report-builder.tsx
studio.en.yaml/pimcore-bundle-{name}/{entity}/{id}assets/studio/js/src/modules/{module-name}/{module-name}-api.tsxcomponents/translations/studio.en.yamlsrc/Controller/Studio/{Module}Controller.phpmodules/index.tsxnpm run check-types && npm run lint-fixnpm run dev (bundles) or npm run dev-app (SDK)targeting-rules not targetingRules)editor/, list/, detail/index.tsx files for re-exportsmodules/{module}/{module}-api.tsxtranslations/studio.en.yamlmodule.entity.action.resultFirst: Check dev server status
If dev server IS running:
If dev server NOT running:
npm run dev-appnpm run devpublic/studio/build/{hash}/Always safe to run:
npm run check-types
Fix common issues:
AI/Assistants: NEVER start or stop dev servers! They are managed by the user.
The user operates in ONE of two modes:
Mode 1: Dev Server Running (Development)
npm run check-types, npm run lint-fixnpm run build, npm run dev, npm run dev-appMode 2: Build Mode (Production/Testing)
ALWAYS ask the user first:
Is your dev server currently running?
- If YES → Run only npm run check-types and npm run lint-fix
- If NO → Proceed with build commands
Location: vendor/pimcore/studio-ui-bundle/assets/studio/
# Development build (when dev server NOT running)
npm run dev-app
# Type checking (safe to run anytime)
npm run check-types
# Linting with auto-fix (safe to run anytime)
npm run lint-fix
# Production build
npm run build
When to build studio-ui-bundle:
Location: {your-bundle}/assets/studio/
# Development build (when dev server NOT running)
npm run dev
# Type checking (safe to run anytime)
npm run check-types
# Linting with auto-fix (safe to run anytime)
npm run lint-fix
# Production build
npm run build
When developing a bundle that uses SDK changes:
Step 1: Check Dev Server Status
AI: "Is your dev server currently running?"
Step 2a: If Dev Server Running
# Only validate, don't build
cd {your-bundle}/assets/studio
npm run check-types
npm run lint-fix
Step 2b: If Dev Server NOT Running
# Build SDK changes first
cd vendor/pimcore/studio-ui-bundle/assets/studio
npm run dev-app
# Then build your bundle
cd ../../../../{your-bundle}/assets/studio
npm run dev
Users start dev servers manually in this order:
Terminal 1: SDK Server
cd vendor/pimcore/studio-ui-bundle/assets/studio
npm run dev-server-sdk # Runs on port 3030
Terminal 2: Bundle Server
cd {your-bundle}/assets/studio
npm run dev-server # Runs on port 3031
Why this order:
When dev servers are running:
These can run regardless of dev server status:
# Type checking
npm run check-types
# Linting with auto-fix
npm run lint-fix
# Both together
npm run check-types && npm run lint-fix
If dev server is running:
If dev server is NOT running:
public/studio/build/{hash}/ for outputRunning builds while dev server is active will:
Always ask first!
Use when creating a new color theme or token set for Pimcore Studio UI (a DynamicTypeThemeAbstract subclass registered in the ThemeRegistry), including choosing an extends chain, filling AntD color tokens, seeding the boot palette, and picking colors that meet WCAG AAA contrast
Building filters in Pimcore Studio UI - the declarative defineFilter framework for filter panels and sidebars (createFiltersStore, FiltersRenderer, useFilterQuery) and custom field-filter dynamic types for per-column listing filters (DynamicTypeFieldFilterAbstract, DynamicTypeFieldFilterRegistry). Use this whenever building, customizing, or extending any kind of filtering, filter panel, filter sidebar, or search/filter UI, or adding a new filter type to a listing or data view - even when the user only says "add filtering", "let users filter by X", "filter this list", or "build a filter sidebar".
Building listings in Pimcore Studio UI using the ListingBuilder decorator pattern - sorting, paging, filtering, inline editing, and custom decorators
UX and UI design conventions for Pimcore Studio - layout, spacing, action labels, writing style, and design principles for consistent extensions
Pimcore Studio migration and review checklist — 34-point verification checklist, anti-patterns to avoid, and reference base classes for validating Studio code completeness
Pimcore Studio PHP code style rules — strict types, class modifiers, final/readonly patterns, formatting (120 chars), named arguments, imports, PHPDoc, @throws documentation, and constructor promotion